From b7e0362496e7b335ea57773d3f9aefdb90ee851a Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 11 Aug 2023 14:46:34 +0200 Subject: [PATCH] backend-app-api: some docs for DependencyGraph Signed-off-by: Patrik Oldsberg --- .../src/lib/DependencyGraph.ts | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/backend-app-api/src/lib/DependencyGraph.ts b/packages/backend-app-api/src/lib/DependencyGraph.ts index cafdbccc00..8fd679f555 100644 --- a/packages/backend-app-api/src/lib/DependencyGraph.ts +++ b/packages/backend-app-api/src/lib/DependencyGraph.ts @@ -37,7 +37,10 @@ class Node { ) {} } -/** @internal */ +/** + * Internal helper to help validate and traverse a dependency graph. + * @internal + */ export class DependencyGraph { static fromMap( nodes: Record, 'value'>>, @@ -75,6 +78,7 @@ export class DependencyGraph { } } + // Find all nodes that consume dependencies that are not provided by any other node findUnsatisfiedDeps(): Array<{ value: T; unsatisfied: string[] }> { const unsatisfiedDependencies = []; for (const node of this.#nodes.values()) { @@ -88,6 +92,8 @@ export class DependencyGraph { return unsatisfiedDependencies; } + // Detect circular dependencies within the graph, returning the path of nodes that + // form a cycle, with the same node as the first and last element of the array. detectCircularDependency(): T[] | undefined { for (const startNode of this.#nodes) { const visited = new Set>(); @@ -120,6 +126,15 @@ export class DependencyGraph { return undefined; } + /** + * Traverses the dependency graph in topological order, calling the provided + * function for each node and waiting for it to resolve. + * + * The nodes are traversed in parallel, but in such a way that no node is + * visited before all of its dependencies. + * + * Dependencies of nodes that are not produced by any other nodes will be ignored. + */ async parallelTopologicalTraversal( fn: (value: T) => Promise, ): Promise { @@ -128,8 +143,9 @@ export class DependencyGraph { const waiting = new Set(this.#nodes.values()); const visited = new Set>(); const results = new Array(); - let inFlight = 0; + let inFlight = 0; // Keep track of how many callbacks are in flight, so that we know if we got stuck + // Find all nodes that have no dependencies that have not already been produced by visited nodes async function processMoreNodes() { if (waiting.size === 0) { return; @@ -161,6 +177,7 @@ export class DependencyGraph { await Promise.all(nodesToProcess.map(processNode)); } + // Process an individual node, and then add its produced dependencies to the set of available products async function processNode(node: Node) { visited.add(node); inFlight += 1;