Skip to content

Commit c8cafef

Browse files
authored
feat(cli): parallel asset publishing (#19367)
Adds parallel asset publishing. In a 60-asset/60-Lambda test project, this change decreases the total hot-swap time from 57 seconds to 18 seconds. Fixes #19193 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 885d0c2 commit c8cafef

File tree

3 files changed

+63
-26
lines changed

3 files changed

+63
-26
lines changed

packages/aws-cdk/lib/util/asset-publishing.ts

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export async function publishAssets(manifest: cdk_assets.AssetManifest, sdk: Sdk
2121
aws: new PublishingAws(sdk, targetEnv),
2222
progressListener: new PublishingProgressListener(),
2323
throwOnError: false,
24+
publishInParallel: true,
2425
});
2526
await publisher.publish();
2627
if (publisher.hasFailures) {

packages/cdk-assets/lib/publishing.ts

+49-26
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ export interface AssetPublishingOptions {
2323
* @default true
2424
*/
2525
readonly throwOnError?: boolean;
26+
27+
/**
28+
* Whether to publish in parallel
29+
*
30+
* @default false
31+
*/
32+
readonly publishInParallel?: boolean;
2633
}
2734

2835
/**
@@ -56,44 +63,33 @@ export class AssetPublishing implements IPublishProgress {
5663
private readonly totalOperations: number;
5764
private completedOperations: number = 0;
5865
private aborted = false;
66+
private readonly handlerHost: IHandlerHost;
67+
private readonly publishInParallel: boolean;
5968

6069
constructor(private readonly manifest: AssetManifest, private readonly options: AssetPublishingOptions) {
6170
this.assets = manifest.entries;
6271
this.totalOperations = this.assets.length;
63-
}
72+
this.publishInParallel = options.publishInParallel ?? false;
6473

65-
/**
66-
* Publish all assets from the manifest
67-
*/
68-
public async publish(): Promise<void> {
6974
const self = this;
70-
71-
const handlerHost: IHandlerHost = {
75+
this.handlerHost = {
7276
aws: this.options.aws,
7377
get aborted() { return self.aborted; },
7478
emitMessage(t, m) { self.progressEvent(t, m); },
7579
};
80+
}
7681

77-
for (const asset of this.assets) {
78-
if (this.aborted) { break; }
79-
this.currentAsset = asset;
80-
81-
try {
82-
if (this.progressEvent(EventType.START, `Publishing ${asset.id}`)) { break; }
83-
84-
const handler = makeAssetHandler(this.manifest, asset, handlerHost);
85-
await handler.publish();
86-
87-
if (this.aborted) {
88-
throw new Error('Aborted');
82+
/**
83+
* Publish all assets from the manifest
84+
*/
85+
public async publish(): Promise<void> {
86+
if (this.publishInParallel) {
87+
await Promise.all(this.assets.map(async (asset) => this.publishAsset(asset)));
88+
} else {
89+
for (const asset of this.assets) {
90+
if (!await this.publishAsset(asset)) {
91+
break;
8992
}
90-
91-
this.completedOperations++;
92-
if (this.progressEvent(EventType.SUCCESS, `Published ${asset.id}`)) { break; }
93-
} catch (e) {
94-
this.failures.push({ asset, error: e });
95-
this.completedOperations++;
96-
if (this.progressEvent(EventType.FAIL, e.message)) { break; }
9793
}
9894
}
9995

@@ -102,6 +98,33 @@ export class AssetPublishing implements IPublishProgress {
10298
}
10399
}
104100

101+
/**
102+
* Publish an asset.
103+
* @param asset The asset to publish
104+
* @returns false when publishing should stop
105+
*/
106+
private async publishAsset(asset: IManifestEntry) {
107+
try {
108+
if (this.progressEvent(EventType.START, `Publishing ${asset.id}`)) { return false; }
109+
110+
const handler = makeAssetHandler(this.manifest, asset, this.handlerHost);
111+
await handler.publish();
112+
113+
if (this.aborted) {
114+
throw new Error('Aborted');
115+
}
116+
117+
this.completedOperations++;
118+
if (this.progressEvent(EventType.SUCCESS, `Published ${asset.id}`)) { return false; }
119+
} catch (e) {
120+
this.failures.push({ asset, error: e });
121+
this.completedOperations++;
122+
if (this.progressEvent(EventType.FAIL, e.message)) { return false; }
123+
}
124+
125+
return true;
126+
}
127+
105128
public get percentComplete() {
106129
if (this.totalOperations === 0) { return 100; }
107130
return Math.floor((this.completedOperations / this.totalOperations) * 100);

packages/cdk-assets/test/progress.test.ts

+13
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,19 @@ test('test listener', async () => {
5959
expect(allMessages).toContain('theAsset:theDestination2');
6060
});
6161

62+
test('test publishing in parallel', async () => {
63+
const progressListener = new FakeListener();
64+
65+
const pub = new AssetPublishing(AssetManifest.fromPath('/simple/cdk.out'), { aws, progressListener, publishInParallel: true });
66+
await pub.publish();
67+
68+
const allMessages = progressListener.messages.join('\n');
69+
70+
// Log mentions asset/destination ids
71+
expect(allMessages).toContain('theAsset:theDestination1');
72+
expect(allMessages).toContain('theAsset:theDestination2');
73+
});
74+
6275
test('test abort', async () => {
6376
const progressListener = new FakeListener(true);
6477

0 commit comments

Comments
 (0)