Skip to content

Commit ceb0361

Browse files
committed
perf: optimize allocation rules evaluation from O(n²) to O(n)
Replace repeated Array.filter() and Array.find() operations with a Map-based lookup system for deployment rules. Changes: - Add preprocessRules() function to create a Map for O(1) rule lookups - Update isDeploymentWorthAllocatingTowards() to use preprocessed rules - Update evaluateDeployments() to preprocess rules once before iteration - Update matchingRuleExists() in AllocationManager to use optimized lookup This eliminates quadratic complexity when evaluating thousands of deployments against hundreds of rules. Based on: #1147 Original author: madumas Original commit: 2618f23
1 parent 6b4fa9e commit ceb0361

2 files changed

Lines changed: 45 additions & 12 deletions

File tree

packages/indexer-common/src/indexer-management/allocations.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
IndexingStatus,
2727
isActionFailure,
2828
isDeploymentWorthAllocatingTowards,
29+
preprocessRules,
2930
Network,
3031
ReallocateAllocationResult,
3132
SubgraphIdentifierType,
@@ -1791,8 +1792,14 @@ export class AllocationManager {
17911792
`SHOULD BE UNREACHABLE: No matching subgraphDeployment (${subgraphDeploymentID.ipfsHash}) found on the network`,
17921793
)
17931794
}
1794-
return isDeploymentWorthAllocatingTowards(logger, subgraphDeployment, indexingRules)
1795-
.toAllocate
1795+
// Use preprocessed rules for O(1) lookup
1796+
const { deploymentRulesMap, globalRule } = preprocessRules(indexingRules)
1797+
return isDeploymentWorthAllocatingTowards(
1798+
logger,
1799+
subgraphDeployment,
1800+
deploymentRulesMap,
1801+
globalRule,
1802+
).toAllocate
17961803
}
17971804

17981805
// Calculates the balance (GRT delta) of a single Action.

packages/indexer-common/src/subgraphs.ts

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,30 @@ interface RuleMatch {
159159
activationCriteria: ActivationCriteria
160160
}
161161

162+
export interface PreprocessedRules {
163+
deploymentRulesMap: { [key: string]: IndexingRuleAttributes }
164+
globalRule: IndexingRuleAttributes | undefined
165+
}
166+
167+
// Preprocess rules into a Map for O(1) lookups instead of O(n) Array.filter().find()
168+
// This optimizes performance from O(n²) to O(n) when evaluating many deployments
169+
export function preprocessRules(rules: IndexingRuleAttributes[]): PreprocessedRules {
170+
const deploymentRulesMap: { [key: string]: IndexingRuleAttributes } = {}
171+
let globalRule: IndexingRuleAttributes | undefined = undefined
172+
173+
for (let i = 0; i < rules.length; i++) {
174+
const rule = rules[i]
175+
if (rule.identifier === INDEXING_RULE_GLOBAL) {
176+
globalRule = rule
177+
} else if (rule.identifierType === SubgraphIdentifierType.DEPLOYMENT) {
178+
const key = new SubgraphDeploymentID(rule.identifier).bytes32
179+
deploymentRulesMap[key] = rule
180+
}
181+
}
182+
183+
return { deploymentRulesMap, globalRule }
184+
}
185+
162186
export class AllocationDecision {
163187
declare deployment: SubgraphDeploymentID
164188
declare toAllocate: boolean
@@ -192,24 +216,26 @@ export function evaluateDeployments(
192216
networkDeployments: SubgraphDeployment[],
193217
rules: IndexingRuleAttributes[],
194218
): AllocationDecision[] {
219+
// Preprocess rules once for O(1) lookups instead of O(n) per deployment
220+
const { deploymentRulesMap, globalRule } = preprocessRules(rules)
195221
return networkDeployments.map((deployment) =>
196-
isDeploymentWorthAllocatingTowards(logger, deployment, rules),
222+
isDeploymentWorthAllocatingTowards(
223+
logger,
224+
deployment,
225+
deploymentRulesMap,
226+
globalRule,
227+
),
197228
)
198229
}
199230

200231
export function isDeploymentWorthAllocatingTowards(
201232
logger: Logger,
202233
deployment: SubgraphDeployment,
203-
rules: IndexingRuleAttributes[],
234+
deploymentRulesMap: { [key: string]: IndexingRuleAttributes },
235+
globalRule: IndexingRuleAttributes | undefined,
204236
): AllocationDecision {
205-
const globalRule = rules.find((rule) => rule.identifier === INDEXING_RULE_GLOBAL)
206-
const deploymentRule =
207-
rules
208-
.filter((rule) => rule.identifierType == SubgraphIdentifierType.DEPLOYMENT)
209-
.find(
210-
(rule) =>
211-
new SubgraphDeploymentID(rule.identifier).bytes32 === deployment.id.bytes32,
212-
) || globalRule
237+
// O(1) lookup using preprocessed rules map
238+
const deploymentRule = deploymentRulesMap[deployment.id.bytes32] || globalRule
213239

214240
logger.trace('Evaluating whether subgraphDeployment is worth allocating towards', {
215241
deployment,

0 commit comments

Comments
 (0)