Skip to content

Commit 2618f23

Browse files
committed
perf: optimize allocation rules evaluation from O(n²) to O(n)
Replace repeated Array.filter() + Array.find() with Map-based lookup for deployment rules. - Add preprocessRules() function to create Map for O(1) rule lookups - Update isDeploymentWorthAllocatingTowards() to use pre-processed rules Map - Update evaluateDeployments() to preprocess rules once instead of per deployment - Update AllocationManager.matchingRuleExists() to use optimized lookup Performance improvement: - Before: O(n²) - filter + find for each deployment evaluation - After: O(n) - single preprocessing + O(1) lookups per deployment - Significant speedup when evaluating many deployments against many rules Fixes performance bottleneck in allocation rule evaluation where processing thousands of deployments with hundreds of rules resulted in quadratic complexity.
1 parent bea354e commit 2618f23

File tree

2 files changed

+54
-13
lines changed

2 files changed

+54
-13
lines changed

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import {
4747
encodeCollectIndexingRewardsData,
4848
encodePOIMetadata,
4949
} from '@graphprotocol/toolshed'
50+
import { preprocessRules } from '../subgraphs'
5051

5152
import {
5253
BigNumberish,
@@ -1791,8 +1792,15 @@ 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+
const { deploymentRulesMap, globalRule } = preprocessRules(indexingRules)
1796+
1797+
return isDeploymentWorthAllocatingTowards(
1798+
logger,
1799+
subgraphDeployment,
1800+
indexingRules,
1801+
deploymentRulesMap,
1802+
globalRule,
1803+
).toAllocate
17961804
}
17971805

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

packages/indexer-common/src/subgraphs.ts

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -187,29 +187,62 @@ export class AllocationDecision {
187187
}
188188
}
189189

190+
export interface PreprocessedRules {
191+
deploymentRulesMap: { [key: string]: IndexingRuleAttributes }
192+
globalRule: IndexingRuleAttributes | undefined
193+
}
194+
195+
export function preprocessRules(rules: IndexingRuleAttributes[]): PreprocessedRules {
196+
let globalRule: IndexingRuleAttributes | undefined
197+
const deploymentRulesMap: { [key: string]: IndexingRuleAttributes } = {}
198+
199+
for (let i = 0; i < rules.length; i++) {
200+
const rule = rules[i]
201+
if (rule.identifier === INDEXING_RULE_GLOBAL) {
202+
globalRule = rule
203+
} else if (rule.identifierType === SubgraphIdentifierType.DEPLOYMENT) {
204+
// Use bytes32 as key to match the original logic
205+
const deploymentId = new SubgraphDeploymentID(rule.identifier)
206+
deploymentRulesMap[deploymentId.bytes32] = rule
207+
}
208+
}
209+
210+
return { deploymentRulesMap, globalRule }
211+
}
212+
190213
export function evaluateDeployments(
191214
logger: Logger,
192215
networkDeployments: SubgraphDeployment[],
193216
rules: IndexingRuleAttributes[],
194217
): AllocationDecision[] {
195-
return networkDeployments.map((deployment) =>
196-
isDeploymentWorthAllocatingTowards(logger, deployment, rules),
197-
)
218+
const { deploymentRulesMap, globalRule } = preprocessRules(rules)
219+
const results: AllocationDecision[] = []
220+
221+
for (let i = 0; i < networkDeployments.length; i++) {
222+
const deployment = networkDeployments[i]
223+
results.push(
224+
isDeploymentWorthAllocatingTowards(
225+
logger,
226+
deployment,
227+
rules,
228+
deploymentRulesMap,
229+
globalRule,
230+
),
231+
)
232+
}
233+
234+
return results
198235
}
199236

200237
export function isDeploymentWorthAllocatingTowards(
201238
logger: Logger,
202239
deployment: SubgraphDeployment,
203240
rules: IndexingRuleAttributes[],
241+
deploymentRulesMap: { [key: string]: IndexingRuleAttributes },
242+
globalRule: IndexingRuleAttributes | undefined,
204243
): 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
244+
// Use the pre-processed object for O(1) lookup using bytes32 to match original logic
245+
const deploymentRule = deploymentRulesMap[deployment.id.bytes32] || globalRule
213246

214247
logger.trace('Evaluating whether subgraphDeployment is worth allocating towards', {
215248
deployment,

0 commit comments

Comments
 (0)