From b61463527ff53525ef7c00ecb4445df4866569f9 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Fri, 2 Jun 2023 10:29:03 +0200 Subject: [PATCH] chore(cli): print detected cycle if work graph stalls To help with diagnosing #25806, if the work graph can't make any progress anymore because of a dependency cycle, print the cycle that was found instead of all the remaining nodes. --- .../aws-custom-resource/sdk-api-metadata.json | 11 ------- packages/aws-cdk/lib/util/work-graph.ts | 29 +++++++++++++++++-- packages/aws-cdk/test/work-graph.test.ts | 7 +++-- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/packages/aws-cdk-lib/custom-resources/lib/aws-custom-resource/sdk-api-metadata.json b/packages/aws-cdk-lib/custom-resources/lib/aws-custom-resource/sdk-api-metadata.json index 83d53e4eb03bc..c1f19ce849b8e 100644 --- a/packages/aws-cdk-lib/custom-resources/lib/aws-custom-resource/sdk-api-metadata.json +++ b/packages/aws-cdk-lib/custom-resources/lib/aws-custom-resource/sdk-api-metadata.json @@ -1284,16 +1284,5 @@ }, "internetmonitor": { "name": "InternetMonitor" - }, - "ivsrealtime": { - "prefix": "ivs-realtime", - "name": "IVSRealTime" - }, - "vpclattice": { - "prefix": "vpc-lattice", - "name": "VPCLattice" - }, - "osis": { - "name": "OSIS" } } \ No newline at end of file diff --git a/packages/aws-cdk/lib/util/work-graph.ts b/packages/aws-cdk/lib/util/work-graph.ts index c51a9a2b3f4e8..d35eaf8b9d166 100644 --- a/packages/aws-cdk/lib/util/work-graph.ts +++ b/packages/aws-cdk/lib/util/work-graph.ts @@ -272,7 +272,7 @@ export class WorkGraph { this.readyPool.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0)); if (this.readyPool.length === 0 && activeCount === 0 && pendingCount > 0) { - throw new Error(`Unable to make progress anymore among: ${this}`); + throw new Error(`Unable to make progress anymore, dependency cycle between remaining artifacts: ${this.findCycle().join(' -> ')}`); } } @@ -283,6 +283,31 @@ export class WorkGraph { } } } + + /** + * Find cycles in a graph + * + * Not the fastest, but effective and should be rare + */ + private findCycle(): string[] { + const self = this; + for (const nodeId of Object.keys(this.nodes)) { + const cycle = recurse(nodeId, [nodeId]); + if (cycle) { return cycle; } + } + return ['No cycle found!']; + + function recurse(nodeId: string, path: string[]): string[] | undefined { + for (const dep of self.nodes[nodeId].dependencies ?? []) { + if (dep === path[0]) { return [...path, dep]; } + + const cycle = recurse(dep, [...path, dep]); + if (cycle) { return cycle; } + } + + return undefined; + } + } } export interface WorkGraphActions { @@ -297,4 +322,4 @@ function sum(xs: number[]) { ret += x; } return ret; -} \ No newline at end of file +} diff --git a/packages/aws-cdk/test/work-graph.test.ts b/packages/aws-cdk/test/work-graph.test.ts index 5b7a2d1235ec8..8486d667e991f 100644 --- a/packages/aws-cdk/test/work-graph.test.ts +++ b/packages/aws-cdk/test/work-graph.test.ts @@ -328,6 +328,7 @@ describe('WorkGraph', () => { toDeploy: createArtifacts([ { id: 'A', type: 'stack', stackDependencies: ['A'] }, ]), + expectedError: 'A -> A', }, { scenario: 'A -> B, B -> A', @@ -335,6 +336,7 @@ describe('WorkGraph', () => { { id: 'A', type: 'stack', stackDependencies: ['B'] }, { id: 'B', type: 'stack', stackDependencies: ['A'] }, ]), + expectedError: 'A -> B -> A', }, { scenario: 'A, B -> C, C -> D, D -> B', @@ -344,12 +346,13 @@ describe('WorkGraph', () => { { id: 'C', type: 'stack', stackDependencies: ['D'] }, { id: 'D', type: 'stack', stackDependencies: ['B'] }, ]), + expectedError: 'B -> C -> D -> B', }, - ])('Failure - Graph Circular Dependencies - $scenario', async ({ toDeploy }) => { + ])('Failure - Graph Circular Dependencies - $scenario', async ({ toDeploy, expectedError }) => { const graph = new WorkGraph(); addTestArtifactsToGraph(toDeploy, graph); - await expect(graph.doParallel(1, callbacks)).rejects.toThrowError(/Unable to make progress anymore/); + await expect(graph.doParallel(1, callbacks)).rejects.toThrowError(new RegExp(`Unable to make progress.*${expectedError}`)); }); });