diff --git a/.bumpy/faster-large-schema-resolve.md b/.bumpy/faster-large-schema-resolve.md new file mode 100644 index 000000000..888e1ff27 --- /dev/null +++ b/.bumpy/faster-large-schema-resolve.md @@ -0,0 +1,5 @@ +--- +"varlock": patch +--- + +Resolving a large schema no longer slows down quadratically with the number of items (3,000 items: about 245 ms to 50 ms) diff --git a/packages/varlock/src/env-graph/lib/env-graph.ts b/packages/varlock/src/env-graph/lib/env-graph.ts index c6a89a42d..c6360b1a5 100644 --- a/packages/varlock/src/env-graph/lib/env-graph.ts +++ b/packages/varlock/src/env-graph/lib/env-graph.ts @@ -933,18 +933,23 @@ export class EnvGraph { // - false = not yet started // - items are removed when completed const itemsToResolveStatus = _.fromPairs(keysToResolve.map((key) => [key, false])); + // kept as a counter, because counting the object's keys after every item is O(n^2) + let itemsRemaining = Object.keys(itemsToResolveStatus).length; // code is a bit awkward here because we are resolving items in parallel // and need to continue resolving dependent items as each finishes const deferred = new Promise((resolve, reject) => { const markItemCompleted = (itemKey: string) => { - delete itemsToResolveStatus[itemKey]; + if (itemKey in itemsToResolveStatus) { + delete itemsToResolveStatus[itemKey]; + itemsRemaining--; + } if (reverseAdjList[itemKey]) { // eslint-disable-next-line no-use-before-define reverseAdjList[itemKey].forEach((depKey) => startResolveItem(depKey)); } - if (_.keys(itemsToResolveStatus).length === 0) resolve(); + if (itemsRemaining === 0) resolve(); }; const resolveItem = async (itemKey: string) => {