envio 3.2.1 → 3.3.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -7
- package/src/Batch.res +12 -15
- package/src/Batch.res.mjs +4 -5
- package/src/BatchProcessing.res +199 -0
- package/src/BatchProcessing.res.mjs +125 -0
- package/src/ChainFetching.res +373 -0
- package/src/ChainFetching.res.mjs +206 -0
- package/src/ChainMetadata.res +27 -0
- package/src/ChainMetadata.res.mjs +27 -0
- package/src/ChainState.res +643 -0
- package/src/ChainState.res.mjs +476 -0
- package/src/ChainState.resi +87 -0
- package/src/Config.res +13 -4
- package/src/Config.res.mjs +8 -3
- package/src/ContractRegisterContext.res +106 -0
- package/src/ContractRegisterContext.res.mjs +76 -0
- package/src/Core.res +3 -0
- package/src/CrossChainState.res +294 -0
- package/src/CrossChainState.res.mjs +221 -0
- package/src/CrossChainState.resi +39 -0
- package/src/Ecosystem.res +58 -0
- package/src/Ecosystem.res.mjs +43 -1
- package/src/Env.res +0 -2
- package/src/Env.res.mjs +0 -3
- package/src/EventConfigBuilder.res +2 -2
- package/src/EventProcessing.res +60 -46
- package/src/EventProcessing.res.mjs +33 -32
- package/src/EventUtils.res +0 -4
- package/src/EventUtils.res.mjs +0 -4
- package/src/ExitOnCaughtUp.res +10 -0
- package/src/ExitOnCaughtUp.res.mjs +22 -0
- package/src/FetchState.res +205 -73
- package/src/FetchState.res.mjs +242 -103
- package/src/InMemoryStore.res +36 -451
- package/src/InMemoryStore.res.mjs +20 -369
- package/src/IndexerLoop.res +43 -0
- package/src/IndexerLoop.res.mjs +46 -0
- package/src/IndexerState.res +536 -0
- package/src/IndexerState.res.mjs +543 -0
- package/src/IndexerState.resi +128 -0
- package/src/Internal.res +29 -8
- package/src/LoadLayer.res +20 -18
- package/src/LoadLayer.res.mjs +14 -12
- package/src/LoadLayer.resi +6 -3
- package/src/Logging.res +11 -44
- package/src/Logging.res.mjs +10 -42
- package/src/Main.res +54 -79
- package/src/Main.res.mjs +44 -56
- package/src/PgStorage.res +8 -71
- package/src/PgStorage.res.mjs +3 -47
- package/src/Prometheus.res +1 -13
- package/src/Prometheus.res.mjs +84 -96
- package/src/PruneStaleHistory.res +45 -0
- package/src/PruneStaleHistory.res.mjs +46 -0
- package/src/RawEvent.res +73 -0
- package/src/RawEvent.res.mjs +51 -0
- package/src/Rollback.res +204 -0
- package/src/Rollback.res.mjs +118 -0
- package/src/SimulateItems.res +12 -10
- package/src/SimulateItems.res.mjs +1 -1
- package/src/UserContext.res +26 -114
- package/src/UserContext.res.mjs +15 -78
- package/src/Writing.res +242 -0
- package/src/Writing.res.mjs +215 -0
- package/src/db/InternalTable.res +14 -27
- package/src/sources/Evm.res +40 -1
- package/src/sources/Evm.res.mjs +94 -84
- package/src/sources/Fuel.res +40 -1
- package/src/sources/Fuel.res.mjs +36 -26
- package/src/sources/HyperFuel.res +41 -120
- package/src/sources/HyperFuel.res.mjs +42 -80
- package/src/sources/HyperFuel.resi +1 -24
- package/src/sources/HyperFuelClient.res +16 -297
- package/src/sources/HyperFuelClient.res.mjs +5 -4
- package/src/sources/HyperFuelSource.res +33 -8
- package/src/sources/HyperFuelSource.res.mjs +56 -10
- package/src/sources/HyperSyncSource.res +5 -3
- package/src/sources/HyperSyncSource.res.mjs +1 -1
- package/src/sources/RpcSource.res +2 -2
- package/src/sources/RpcSource.res.mjs +1 -1
- package/src/sources/SourceManager.res +14 -23
- package/src/sources/SourceManager.res.mjs +27 -29
- package/src/sources/SourceManager.resi +4 -2
- package/src/sources/Svm.res +18 -1
- package/src/sources/Svm.res.mjs +30 -22
- package/src/sources/SvmHyperSyncSource.res +1 -1
- package/src/sources/SvmHyperSyncSource.res.mjs +1 -1
- package/src/tui/Tui.res +38 -36
- package/src/tui/Tui.res.mjs +51 -51
- package/src/ChainFetcher.res +0 -519
- package/src/ChainFetcher.res.mjs +0 -314
- package/src/ChainManager.res +0 -358
- package/src/ChainManager.res.mjs +0 -291
- package/src/Ctx.res +0 -6
- package/src/Ctx.res.mjs +0 -2
- package/src/GlobalState.res +0 -1056
- package/src/GlobalState.res.mjs +0 -1086
- package/src/GlobalStateManager.res +0 -57
- package/src/GlobalStateManager.res.mjs +0 -68
- package/src/GlobalStateManager.resi +0 -7
- package/src/Ports.res +0 -5
- package/src/Ports.res.mjs +0 -9
- package/src/adapters/MarkBatchProcessedAdapter.res +0 -5
- package/src/adapters/MarkBatchProcessedAdapter.res.mjs +0 -14
- package/src/sources/EnvioApiClient.res +0 -15
- package/src/sources/EnvioApiClient.res.mjs +0 -24
package/src/FetchState.res
CHANGED
|
@@ -19,6 +19,9 @@ type pendingQuery = {
|
|
|
19
19
|
fromBlock: int,
|
|
20
20
|
toBlock: option<int>,
|
|
21
21
|
isChunk: bool,
|
|
22
|
+
// Estimated items this in-flight query will add, carried from the query so the
|
|
23
|
+
// shared buffer budget can account for what's already being fetched.
|
|
24
|
+
estResponseSize: float,
|
|
22
25
|
// Stores latestFetchedBlock when query completes. Only needed to persist
|
|
23
26
|
// timestamp while earlier queries are still pending before updating
|
|
24
27
|
// the partition's latestFetchedBlock.
|
|
@@ -47,6 +50,10 @@ type partition = {
|
|
|
47
50
|
// Track last 3 successful query ranges for chunking heuristic (0 means no data)
|
|
48
51
|
prevQueryRange: int,
|
|
49
52
|
prevPrevQueryRange: int,
|
|
53
|
+
// Item count of the response that set prevQueryRange. Paired with it to derive
|
|
54
|
+
// the partition's event density (prevRangeSize / prevQueryRange) for sizing
|
|
55
|
+
// queries against the buffer budget.
|
|
56
|
+
prevRangeSize: int,
|
|
50
57
|
// Tracks the latestFetchedBlock.blockNumber of the most recent response
|
|
51
58
|
// that updated prevQueryRange. Prevents degradation of the chunking heuristic
|
|
52
59
|
// when parallel query responses arrive out of order.
|
|
@@ -58,11 +65,39 @@ type query = {
|
|
|
58
65
|
fromBlock: int,
|
|
59
66
|
toBlock: option<int>,
|
|
60
67
|
isChunk: bool,
|
|
68
|
+
// Estimated number of items the query will return, from the partition's
|
|
69
|
+
// density and the query's block range. Used to admit queries against the
|
|
70
|
+
// shared buffer budget.
|
|
71
|
+
estResponseSize: float,
|
|
72
|
+
// Owning chain and the chain progress % at the query's fromBlock. Set by the
|
|
73
|
+
// cross-chain scheduler so candidate queries can be pooled and ordered
|
|
74
|
+
// (furthest-behind first) without allocating a side tuple per query.
|
|
75
|
+
mutable chainId: int,
|
|
76
|
+
mutable progress: float,
|
|
61
77
|
selection: selection,
|
|
62
78
|
addressesByContractName: dict<array<Address.t>>,
|
|
63
79
|
indexingAddresses: dict<indexingAddress>,
|
|
64
80
|
}
|
|
65
81
|
|
|
82
|
+
// Default estimate for a query whose partition hasn't responded yet, so the
|
|
83
|
+
// shared budget still accounts for unknown queries instead of treating them as
|
|
84
|
+
// free.
|
|
85
|
+
let defaultEstResponseSize = 10_000.
|
|
86
|
+
|
|
87
|
+
// Estimated items a query will return, from the partition's event density
|
|
88
|
+
// (items/block derived from its last response) and the query's block range.
|
|
89
|
+
// toBlock None is the open-ended tail, capped at maxQueryBlockNumber. A partition
|
|
90
|
+
// that responded with no items has density 0, so its queries cost 0 — correct,
|
|
91
|
+
// they don't fill the buffer. Only a partition that has never responded
|
|
92
|
+
// (prevQueryRange 0) has no signal, so it falls back to defaultEstResponseSize.
|
|
93
|
+
let calculateEstResponseSize = (p: partition, ~fromBlock, ~toBlock, ~maxQueryBlockNumber) =>
|
|
94
|
+
if p.prevQueryRange > 0 {
|
|
95
|
+
let density = p.prevRangeSize->Int.toFloat /. p.prevQueryRange->Int.toFloat
|
|
96
|
+
(toBlock->Option.getOr(maxQueryBlockNumber) - fromBlock + 1)->Int.toFloat *. density
|
|
97
|
+
} else {
|
|
98
|
+
defaultEstResponseSize
|
|
99
|
+
}
|
|
100
|
+
|
|
66
101
|
// Calculate the chunk range from history using min-of-last-3-ranges heuristic
|
|
67
102
|
let getMinHistoryRange = (p: partition) => {
|
|
68
103
|
switch (p.prevQueryRange, p.prevPrevQueryRange) {
|
|
@@ -160,6 +195,7 @@ module OptimizedPartitions = {
|
|
|
160
195
|
mutPendingQueries: [],
|
|
161
196
|
prevQueryRange: minRange,
|
|
162
197
|
prevPrevQueryRange: minRange,
|
|
198
|
+
prevRangeSize: 0,
|
|
163
199
|
latestBlockRangeUpdateBlock: 0,
|
|
164
200
|
}
|
|
165
201
|
}
|
|
@@ -397,6 +433,7 @@ module OptimizedPartitions = {
|
|
|
397
433
|
optimizedPartitions: t,
|
|
398
434
|
~query,
|
|
399
435
|
~knownHeight,
|
|
436
|
+
~itemsCount,
|
|
400
437
|
~latestFetchedBlock: blockNumberAndTimestamp,
|
|
401
438
|
) => {
|
|
402
439
|
let p = optimizedPartitions->getOrThrow(~partitionId=query.partitionId)
|
|
@@ -428,6 +465,7 @@ module OptimizedPartitions = {
|
|
|
428
465
|
}
|
|
429
466
|
let updatedPrevQueryRange = shouldUpdateBlockRange ? blockRange : p.prevQueryRange
|
|
430
467
|
let updatedPrevPrevQueryRange = shouldUpdateBlockRange ? p.prevQueryRange : p.prevPrevQueryRange
|
|
468
|
+
let updatedPrevRangeSize = shouldUpdateBlockRange ? itemsCount : p.prevRangeSize
|
|
431
469
|
|
|
432
470
|
// Process fetched queries from front of queue for main partition
|
|
433
471
|
let updatedLatestFetchedBlock = consumeFetchedQueries(
|
|
@@ -449,6 +487,7 @@ module OptimizedPartitions = {
|
|
|
449
487
|
latestFetchedBlock: updatedLatestFetchedBlock,
|
|
450
488
|
prevQueryRange: updatedPrevQueryRange,
|
|
451
489
|
prevPrevQueryRange: updatedPrevPrevQueryRange,
|
|
490
|
+
prevRangeSize: updatedPrevRangeSize,
|
|
452
491
|
latestBlockRangeUpdateBlock: shouldUpdateBlockRange
|
|
453
492
|
? latestFetchedBlock.blockNumber
|
|
454
493
|
: p.latestBlockRangeUpdateBlock,
|
|
@@ -496,9 +535,10 @@ type t = {
|
|
|
496
535
|
blockLag: int,
|
|
497
536
|
// Buffer of items ordered from earliest to latest
|
|
498
537
|
buffer: array<Internal.item>,
|
|
499
|
-
//
|
|
500
|
-
//
|
|
501
|
-
|
|
538
|
+
// Caps how far ahead onBlock items are pre-generated (set to 2x the batch
|
|
539
|
+
// size). Fetch depth is bounded separately by getNextQuery's itemBudget, the
|
|
540
|
+
// chain's per-tick slice of the indexer-wide pool.
|
|
541
|
+
maxOnBlockBufferSize: int,
|
|
502
542
|
onBlockConfigs: array<Internal.onBlockConfig>,
|
|
503
543
|
knownHeight: int,
|
|
504
544
|
firstEventBlock: option<int>,
|
|
@@ -554,7 +594,7 @@ let numAddresses = fetchState => fetchState.indexingAddresses->Utils.Dict.size
|
|
|
554
594
|
|
|
555
595
|
// Appends Block items produced by the onBlock handlers for every block in
|
|
556
596
|
// (fromBlock, maxBlockNumber] into mutItems and returns the new
|
|
557
|
-
// latestOnBlockBlockNumber pointer.
|
|
597
|
+
// latestOnBlockBlockNumber pointer. maxOnBlockBufferSize bounds how many items
|
|
558
598
|
// are generated at once to prevent OOM.
|
|
559
599
|
let appendOnBlockItems = (
|
|
560
600
|
~mutItems: array<Internal.item>,
|
|
@@ -562,7 +602,7 @@ let appendOnBlockItems = (
|
|
|
562
602
|
~indexerStartBlock,
|
|
563
603
|
~fromBlock,
|
|
564
604
|
~maxBlockNumber,
|
|
565
|
-
~
|
|
605
|
+
~maxOnBlockBufferSize,
|
|
566
606
|
) => {
|
|
567
607
|
let newItemsCounter = ref(0)
|
|
568
608
|
let latestOnBlockBlockNumber = ref(fromBlock)
|
|
@@ -573,7 +613,7 @@ let appendOnBlockItems = (
|
|
|
573
613
|
while (
|
|
574
614
|
latestOnBlockBlockNumber.contents < maxBlockNumber &&
|
|
575
615
|
// Additional safeguard to prevent OOM
|
|
576
|
-
newItemsCounter.contents <=
|
|
616
|
+
newItemsCounter.contents <= maxOnBlockBufferSize
|
|
577
617
|
) {
|
|
578
618
|
let blockNumber = latestOnBlockBlockNumber.contents + 1
|
|
579
619
|
latestOnBlockBlockNumber := blockNumber
|
|
@@ -627,7 +667,7 @@ let updateInternal = (
|
|
|
627
667
|
| [] => knownHeight
|
|
628
668
|
| onBlockConfigs => {
|
|
629
669
|
// Calculate the max block number we are going to create items for
|
|
630
|
-
// Use
|
|
670
|
+
// Use maxOnBlockBufferSize to get the last target item in the buffer
|
|
631
671
|
//
|
|
632
672
|
// mutItems is not very reliable, since it might not be sorted,
|
|
633
673
|
// but the chances for it happen are very low and not critical
|
|
@@ -636,7 +676,7 @@ let updateInternal = (
|
|
|
636
676
|
let maxBlockNumber = switch switch mutItemsRef.contents {
|
|
637
677
|
| Some(mutItems) => mutItems
|
|
638
678
|
| None => fetchState.buffer
|
|
639
|
-
}->Array.get(fetchState.
|
|
679
|
+
}->Array.get(fetchState.maxOnBlockBufferSize - 1) {
|
|
640
680
|
| Some(item) => item->Internal.getItemBlockNumber
|
|
641
681
|
| None =>
|
|
642
682
|
switch optimizedPartitions->OptimizedPartitions.getLatestFullyFetchedBlock {
|
|
@@ -657,7 +697,7 @@ let updateInternal = (
|
|
|
657
697
|
~indexerStartBlock=fetchState.startBlock,
|
|
658
698
|
~fromBlock=fetchState.latestOnBlockBlockNumber,
|
|
659
699
|
~maxBlockNumber,
|
|
660
|
-
~
|
|
700
|
+
~maxOnBlockBufferSize=fetchState.maxOnBlockBufferSize,
|
|
661
701
|
)
|
|
662
702
|
}
|
|
663
703
|
}
|
|
@@ -669,7 +709,7 @@ let updateInternal = (
|
|
|
669
709
|
normalSelection: fetchState.normalSelection,
|
|
670
710
|
chainId: fetchState.chainId,
|
|
671
711
|
onBlockConfigs: fetchState.onBlockConfigs,
|
|
672
|
-
|
|
712
|
+
maxOnBlockBufferSize: fetchState.maxOnBlockBufferSize,
|
|
673
713
|
optimizedPartitions,
|
|
674
714
|
latestOnBlockBlockNumber,
|
|
675
715
|
indexingAddresses,
|
|
@@ -837,6 +877,7 @@ OptimizedPartitions.t => {
|
|
|
837
877
|
mutPendingQueries: [],
|
|
838
878
|
prevQueryRange: 0,
|
|
839
879
|
prevPrevQueryRange: 0,
|
|
880
|
+
prevRangeSize: 0,
|
|
840
881
|
latestBlockRangeUpdateBlock: 0,
|
|
841
882
|
})
|
|
842
883
|
nextPartitionIndexRef := nextPartitionIndexRef.contents + 1
|
|
@@ -1178,6 +1219,7 @@ let registerDynamicContracts = (
|
|
|
1178
1219
|
mutPendingQueries: p.mutPendingQueries,
|
|
1179
1220
|
prevQueryRange: p.prevQueryRange,
|
|
1180
1221
|
prevPrevQueryRange: p.prevPrevQueryRange,
|
|
1222
|
+
prevRangeSize: p.prevRangeSize,
|
|
1181
1223
|
latestBlockRangeUpdateBlock: p.latestBlockRangeUpdateBlock,
|
|
1182
1224
|
})
|
|
1183
1225
|
}
|
|
@@ -1228,9 +1270,9 @@ let handleQueryResult = (
|
|
|
1228
1270
|
// param-level analogue of EventRouter's srcAddress effectiveStartBlock check.
|
|
1229
1271
|
let newItems = newItems->Array.filter(item =>
|
|
1230
1272
|
switch item {
|
|
1231
|
-
| Internal.Event({eventConfig,
|
|
1273
|
+
| Internal.Event({eventConfig, payload, blockNumber}) =>
|
|
1232
1274
|
switch eventConfig.clientAddressFilter {
|
|
1233
|
-
| Some(filter) => filter(
|
|
1275
|
+
| Some(filter) => filter(payload, blockNumber, fetchState.indexingAddresses)
|
|
1234
1276
|
| None => true
|
|
1235
1277
|
}
|
|
1236
1278
|
| _ => true
|
|
@@ -1240,6 +1282,7 @@ let handleQueryResult = (
|
|
|
1240
1282
|
~optimizedPartitions=fetchState.optimizedPartitions->OptimizedPartitions.handleQueryResponse(
|
|
1241
1283
|
~query,
|
|
1242
1284
|
~knownHeight=fetchState.knownHeight,
|
|
1285
|
+
~itemsCount=newItems->Array.length,
|
|
1243
1286
|
~latestFetchedBlock,
|
|
1244
1287
|
),
|
|
1245
1288
|
~mutItems=?{
|
|
@@ -1252,7 +1295,6 @@ let handleQueryResult = (
|
|
|
1252
1295
|
}
|
|
1253
1296
|
|
|
1254
1297
|
type nextQuery =
|
|
1255
|
-
| ReachedMaxConcurrency
|
|
1256
1298
|
| WaitingForNewBlock
|
|
1257
1299
|
| NothingToQuery
|
|
1258
1300
|
| Ready(array<query>)
|
|
@@ -1266,6 +1308,7 @@ let startFetchingQueries = ({optimizedPartitions}: t, ~queries: array<query>) =>
|
|
|
1266
1308
|
fromBlock: q.fromBlock,
|
|
1267
1309
|
toBlock: q.toBlock,
|
|
1268
1310
|
isChunk: q.isChunk,
|
|
1311
|
+
estResponseSize: q.estResponseSize,
|
|
1269
1312
|
fetchedBlock: None,
|
|
1270
1313
|
}
|
|
1271
1314
|
|
|
@@ -1294,6 +1337,7 @@ let pushQueriesForRange = (
|
|
|
1294
1337
|
~rangeEndBlock: option<int>,
|
|
1295
1338
|
~maxQueryBlockNumber: int,
|
|
1296
1339
|
~maybeChunkRange: option<int>,
|
|
1340
|
+
~partition: partition,
|
|
1297
1341
|
~selection: selection,
|
|
1298
1342
|
~addressesByContractName: dict<array<Address.t>>,
|
|
1299
1343
|
~indexingAddresses: dict<indexingAddress>,
|
|
@@ -1310,6 +1354,14 @@ let pushQueriesForRange = (
|
|
|
1310
1354
|
toBlock: rangeEndBlock,
|
|
1311
1355
|
selection,
|
|
1312
1356
|
isChunk: false,
|
|
1357
|
+
estResponseSize: calculateEstResponseSize(
|
|
1358
|
+
partition,
|
|
1359
|
+
~fromBlock=rangeFromBlock,
|
|
1360
|
+
~toBlock=rangeEndBlock,
|
|
1361
|
+
~maxQueryBlockNumber,
|
|
1362
|
+
),
|
|
1363
|
+
chainId: 0,
|
|
1364
|
+
progress: 0.,
|
|
1313
1365
|
addressesByContractName,
|
|
1314
1366
|
indexingAddresses,
|
|
1315
1367
|
})
|
|
@@ -1319,26 +1371,38 @@ let pushQueriesForRange = (
|
|
|
1319
1371
|
| None => maxQueryBlockNumber
|
|
1320
1372
|
}
|
|
1321
1373
|
let chunkSize = Js.Math.ceil_int(chunkRange->Int.toFloat *. 1.8)
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1374
|
+
// Probe with two smaller chunks first so their responses come back
|
|
1375
|
+
// quickly and refresh the chunking heuristic, then three full-size chunks.
|
|
1376
|
+
let probeSize = Js.Math.ceil_int(chunkRange->Int.toFloat *. 0.9)
|
|
1377
|
+
let getChunkSize = chunkIdx => chunkIdx < 2 ? probeSize : chunkSize
|
|
1378
|
+
if rangeFromBlock + getChunkSize(0) + getChunkSize(1) - 1 <= maxBlock {
|
|
1379
|
+
let chunkFromBlock = ref(rangeFromBlock)
|
|
1380
|
+
let chunkIdx = ref(0)
|
|
1381
|
+
while (
|
|
1382
|
+
chunkIdx.contents < 5 &&
|
|
1383
|
+
chunkFromBlock.contents + getChunkSize(chunkIdx.contents) - 1 <= maxBlock
|
|
1384
|
+
) {
|
|
1385
|
+
let chunkToBlock = chunkFromBlock.contents + getChunkSize(chunkIdx.contents) - 1
|
|
1386
|
+
queries->Array.push({
|
|
1387
|
+
partitionId,
|
|
1388
|
+
fromBlock: chunkFromBlock.contents,
|
|
1389
|
+
toBlock: Some(chunkToBlock),
|
|
1390
|
+
isChunk: true,
|
|
1391
|
+
selection,
|
|
1392
|
+
estResponseSize: calculateEstResponseSize(
|
|
1393
|
+
partition,
|
|
1394
|
+
~fromBlock=chunkFromBlock.contents,
|
|
1395
|
+
~toBlock=Some(chunkToBlock),
|
|
1396
|
+
~maxQueryBlockNumber,
|
|
1397
|
+
),
|
|
1398
|
+
chainId: 0,
|
|
1399
|
+
progress: 0.,
|
|
1400
|
+
addressesByContractName,
|
|
1401
|
+
indexingAddresses,
|
|
1402
|
+
})
|
|
1403
|
+
chunkFromBlock := chunkToBlock + 1
|
|
1404
|
+
chunkIdx := chunkIdx.contents + 1
|
|
1405
|
+
}
|
|
1342
1406
|
} else {
|
|
1343
1407
|
// Not enough room for 2 chunks, fall back to a single query
|
|
1344
1408
|
queries->Array.push({
|
|
@@ -1347,6 +1411,14 @@ let pushQueriesForRange = (
|
|
|
1347
1411
|
toBlock: rangeEndBlock,
|
|
1348
1412
|
selection,
|
|
1349
1413
|
isChunk: rangeEndBlock !== None,
|
|
1414
|
+
estResponseSize: calculateEstResponseSize(
|
|
1415
|
+
partition,
|
|
1416
|
+
~fromBlock=rangeFromBlock,
|
|
1417
|
+
~toBlock=rangeEndBlock,
|
|
1418
|
+
~maxQueryBlockNumber,
|
|
1419
|
+
),
|
|
1420
|
+
chainId: 0,
|
|
1421
|
+
progress: 0.,
|
|
1350
1422
|
addressesByContractName,
|
|
1351
1423
|
indexingAddresses,
|
|
1352
1424
|
})
|
|
@@ -1356,23 +1428,28 @@ let pushQueriesForRange = (
|
|
|
1356
1428
|
}
|
|
1357
1429
|
}
|
|
1358
1430
|
|
|
1431
|
+
// Most parallel in-flight chunk queries a single partition may have at once.
|
|
1432
|
+
let maxPendingChunksPerPartition = 10
|
|
1433
|
+
|
|
1359
1434
|
let getNextQuery = (
|
|
1360
1435
|
{
|
|
1361
1436
|
buffer,
|
|
1362
1437
|
optimizedPartitions,
|
|
1363
|
-
targetBufferSize,
|
|
1364
1438
|
indexingAddresses,
|
|
1365
1439
|
blockLag,
|
|
1366
1440
|
latestOnBlockBlockNumber,
|
|
1367
1441
|
knownHeight,
|
|
1368
1442
|
} as fetchState: t,
|
|
1369
|
-
~
|
|
1443
|
+
~budget,
|
|
1444
|
+
~chainPendingBudget,
|
|
1370
1445
|
) => {
|
|
1371
1446
|
let headBlockNumber = knownHeight - blockLag
|
|
1372
1447
|
if headBlockNumber <= 0 {
|
|
1373
1448
|
WaitingForNewBlock
|
|
1374
|
-
} else if
|
|
1375
|
-
|
|
1449
|
+
} else if budget <= 0 {
|
|
1450
|
+
// No room left in the shared buffer pool for this chain; wait for processing
|
|
1451
|
+
// to drain before fetching more.
|
|
1452
|
+
NothingToQuery
|
|
1376
1453
|
} else {
|
|
1377
1454
|
let isOnBlockBehindTheHead = latestOnBlockBlockNumber < headBlockNumber
|
|
1378
1455
|
let shouldWaitForNewBlock = ref(
|
|
@@ -1383,13 +1460,11 @@ let getNextQuery = (
|
|
|
1383
1460
|
!isOnBlockBehindTheHead,
|
|
1384
1461
|
)
|
|
1385
1462
|
|
|
1386
|
-
//
|
|
1387
|
-
//
|
|
1388
|
-
//
|
|
1389
|
-
// If a partition fetched further
|
|
1390
|
-
// it should be skipped until the buffer is consumed
|
|
1463
|
+
// Limit how far ahead we fetch to budget items (plus what's already in
|
|
1464
|
+
// flight) so processing always has buffer without ballooning memory. A
|
|
1465
|
+
// partition that fetched further is skipped until the buffer drains.
|
|
1391
1466
|
let maxQueryBlockNumber = {
|
|
1392
|
-
switch buffer->Array.get(
|
|
1467
|
+
switch buffer->Array.get(budget + chainPendingBudget->Float.toInt - 1) {
|
|
1393
1468
|
| Some(item) =>
|
|
1394
1469
|
// Just in case check that we don't query beyond the current block
|
|
1395
1470
|
Pervasives.min(item->Internal.getItemBlockNumber, knownHeight)
|
|
@@ -1406,8 +1481,9 @@ let getNextQuery = (
|
|
|
1406
1481
|
let partitionId = optimizedPartitions.idsInAscOrder->Array.getUnsafe(idx)
|
|
1407
1482
|
let p = optimizedPartitions.entities->Dict.getUnsafe(partitionId)
|
|
1408
1483
|
|
|
1484
|
+
let pendingCount = p.mutPendingQueries->Array.length
|
|
1409
1485
|
let isBehindTheHead = p.latestFetchedBlock.blockNumber < headBlockNumber
|
|
1410
|
-
let hasPendingQueries =
|
|
1486
|
+
let hasPendingQueries = pendingCount > 0
|
|
1411
1487
|
|
|
1412
1488
|
if hasPendingQueries || isBehindTheHead {
|
|
1413
1489
|
// Even if there are some partitions waiting for the new block
|
|
@@ -1418,6 +1494,8 @@ let getNextQuery = (
|
|
|
1418
1494
|
shouldWaitForNewBlock := false
|
|
1419
1495
|
}
|
|
1420
1496
|
|
|
1497
|
+
let partitionQueriesStart = queries->Array.length
|
|
1498
|
+
|
|
1421
1499
|
// Compute queryEndBlock for this partition
|
|
1422
1500
|
let queryEndBlock = Utils.Math.minOptInt(fetchState.endBlock, p.mergeBlock)
|
|
1423
1501
|
let queryEndBlock = switch blockLag {
|
|
@@ -1455,6 +1533,7 @@ let getNextQuery = (
|
|
|
1455
1533
|
~rangeEndBlock=Utils.Math.minOptInt(Some(pq.fromBlock - 1), queryEndBlock),
|
|
1456
1534
|
~maxQueryBlockNumber,
|
|
1457
1535
|
~maybeChunkRange,
|
|
1536
|
+
~partition=p,
|
|
1458
1537
|
~selection=p.selection,
|
|
1459
1538
|
~addressesByContractName=p.addressesByContractName,
|
|
1460
1539
|
~indexingAddresses,
|
|
@@ -1479,12 +1558,28 @@ let getNextQuery = (
|
|
|
1479
1558
|
~rangeEndBlock=queryEndBlock,
|
|
1480
1559
|
~maxQueryBlockNumber,
|
|
1481
1560
|
~maybeChunkRange,
|
|
1561
|
+
~partition=p,
|
|
1482
1562
|
~selection=p.selection,
|
|
1483
1563
|
~addressesByContractName=p.addressesByContractName,
|
|
1484
1564
|
~indexingAddresses,
|
|
1485
1565
|
)
|
|
1486
1566
|
}
|
|
1487
1567
|
|
|
1568
|
+
// Cap parallel in-flight chunks per partition so a single partition can't
|
|
1569
|
+
// monopolize fetching. Keep the earliest new chunks; the furthest-ahead
|
|
1570
|
+
// ones wait for the next round once these resolve.
|
|
1571
|
+
let maxNewChunks = Pervasives.max(0, maxPendingChunksPerPartition - pendingCount)
|
|
1572
|
+
let generatedCount = queries->Array.length - partitionQueriesStart
|
|
1573
|
+
if generatedCount > maxNewChunks {
|
|
1574
|
+
queries
|
|
1575
|
+
->Array.splice(
|
|
1576
|
+
~start=partitionQueriesStart + maxNewChunks,
|
|
1577
|
+
~remove=generatedCount - maxNewChunks,
|
|
1578
|
+
~insert=[],
|
|
1579
|
+
)
|
|
1580
|
+
->ignore
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1488
1583
|
idxRef := idxRef.contents + 1
|
|
1489
1584
|
}
|
|
1490
1585
|
|
|
@@ -1495,13 +1590,6 @@ let getNextQuery = (
|
|
|
1495
1590
|
NothingToQuery
|
|
1496
1591
|
}
|
|
1497
1592
|
} else {
|
|
1498
|
-
// Enforce concurrency limit: sort by fromBlock and take the first concurrencyLimit
|
|
1499
|
-
let queries = if queries->Array.length > concurrencyLimit {
|
|
1500
|
-
queries->Array.sort((a, b) => Int.compare(a.fromBlock, b.fromBlock))
|
|
1501
|
-
queries->Array.slice(~start=0, ~end=concurrencyLimit)
|
|
1502
|
-
} else {
|
|
1503
|
-
queries
|
|
1504
|
-
}
|
|
1505
1593
|
Ready(queries)
|
|
1506
1594
|
}
|
|
1507
1595
|
}
|
|
@@ -1547,7 +1635,7 @@ let make = (
|
|
|
1547
1635
|
~addresses: array<Internal.indexingAddress>,
|
|
1548
1636
|
~maxAddrInPartition,
|
|
1549
1637
|
~chainId,
|
|
1550
|
-
~
|
|
1638
|
+
~maxOnBlockBufferSize,
|
|
1551
1639
|
~knownHeight,
|
|
1552
1640
|
~progressBlockNumber=startBlock - 1,
|
|
1553
1641
|
~onBlockConfigs=[],
|
|
@@ -1611,6 +1699,7 @@ let make = (
|
|
|
1611
1699
|
mutPendingQueries: [],
|
|
1612
1700
|
prevQueryRange: 0,
|
|
1613
1701
|
prevPrevQueryRange: 0,
|
|
1702
|
+
prevRangeSize: 0,
|
|
1614
1703
|
latestBlockRangeUpdateBlock: 0,
|
|
1615
1704
|
})
|
|
1616
1705
|
}
|
|
@@ -1696,7 +1785,7 @@ let make = (
|
|
|
1696
1785
|
~indexerStartBlock=startBlock,
|
|
1697
1786
|
~fromBlock=progressBlockNumber,
|
|
1698
1787
|
~maxBlockNumber,
|
|
1699
|
-
~
|
|
1788
|
+
~maxOnBlockBufferSize,
|
|
1700
1789
|
)
|
|
1701
1790
|
} else {
|
|
1702
1791
|
progressBlockNumber
|
|
@@ -1713,7 +1802,7 @@ let make = (
|
|
|
1713
1802
|
indexingAddresses,
|
|
1714
1803
|
blockLag,
|
|
1715
1804
|
onBlockConfigs,
|
|
1716
|
-
|
|
1805
|
+
maxOnBlockBufferSize,
|
|
1717
1806
|
knownHeight,
|
|
1718
1807
|
buffer,
|
|
1719
1808
|
firstEventBlock,
|
|
@@ -1737,6 +1826,25 @@ let make = (
|
|
|
1737
1826
|
|
|
1738
1827
|
let bufferSize = ({buffer}: t) => buffer->Array.length
|
|
1739
1828
|
|
|
1829
|
+
// Number of buffered items at or below the ready frontier (processable now,
|
|
1830
|
+
// i.e. not stuck behind a gap from a lagging partition or out-of-order chunk).
|
|
1831
|
+
// The buffer is kept sorted, so binary-search the frontier in O(log n).
|
|
1832
|
+
let bufferReadyCount = (fetchState: t) => {
|
|
1833
|
+
let frontier = fetchState->bufferBlockNumber
|
|
1834
|
+
let buffer = fetchState.buffer
|
|
1835
|
+
let lo = ref(0)
|
|
1836
|
+
let hi = ref(buffer->Array.length)
|
|
1837
|
+
while lo.contents < hi.contents {
|
|
1838
|
+
let mid = (lo.contents + hi.contents) / 2
|
|
1839
|
+
if buffer->Array.getUnsafe(mid)->Internal.getItemBlockNumber <= frontier {
|
|
1840
|
+
lo := mid + 1
|
|
1841
|
+
} else {
|
|
1842
|
+
hi := mid
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
lo.contents
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1740
1848
|
let rollbackPendingQueries = (mutPendingQueries: array<pendingQuery>, ~targetBlockNumber) => {
|
|
1741
1849
|
// - Remove queries where fromBlock > target
|
|
1742
1850
|
// - Cap fetchedBlock at target where fetchedBlock > target
|
|
@@ -1935,6 +2043,18 @@ let isActivelyIndexing = ({endBlock} as fetchState: t) => {
|
|
|
1935
2043
|
}
|
|
1936
2044
|
}
|
|
1937
2045
|
|
|
2046
|
+
// True once the fetch frontier has reached the (lagged) head or endBlock,
|
|
2047
|
+
// regardless of whether the buffer has been consumed yet. Unlike
|
|
2048
|
+
// isReadyToEnterReorgThreshold, fetched-but-unprocessed items still count.
|
|
2049
|
+
let isFetchingAtHead = ({endBlock, blockLag, knownHeight} as fetchState: t) => {
|
|
2050
|
+
let bufferBlockNumber = fetchState->bufferBlockNumber
|
|
2051
|
+
knownHeight !== 0 &&
|
|
2052
|
+
switch endBlock {
|
|
2053
|
+
| Some(endBlock) if bufferBlockNumber >= endBlock => true
|
|
2054
|
+
| _ => bufferBlockNumber >= knownHeight - blockLag
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
|
|
1938
2058
|
let isReadyToEnterReorgThreshold = ({endBlock, blockLag, buffer, knownHeight} as fetchState: t) => {
|
|
1939
2059
|
let bufferBlockNumber = fetchState->bufferBlockNumber
|
|
1940
2060
|
knownHeight !== 0 &&
|
|
@@ -1945,7 +2065,37 @@ let isReadyToEnterReorgThreshold = ({endBlock, blockLag, buffer, knownHeight} as
|
|
|
1945
2065
|
buffer->Utils.Array.isEmpty
|
|
1946
2066
|
}
|
|
1947
2067
|
|
|
1948
|
-
|
|
2068
|
+
// Lower progress percentage = further behind = higher priority. Shared by the
|
|
2069
|
+
// batch ordering and the cross-chain fetch priority.
|
|
2070
|
+
let getProgressPercentage = (fetchState: t) => {
|
|
2071
|
+
switch fetchState.firstEventBlock {
|
|
2072
|
+
| None => 0.
|
|
2073
|
+
| Some(firstEventBlock) =>
|
|
2074
|
+
let totalRange = fetchState.knownHeight - firstEventBlock
|
|
2075
|
+
if totalRange <= 0 {
|
|
2076
|
+
0.
|
|
2077
|
+
} else {
|
|
2078
|
+
let progress = switch fetchState.buffer->Array.get(0) {
|
|
2079
|
+
| Some(item) => item->Internal.getItemBlockNumber - firstEventBlock
|
|
2080
|
+
| None => fetchState->bufferBlockNumber - firstEventBlock
|
|
2081
|
+
}
|
|
2082
|
+
progress->Int.toFloat /. totalRange->Int.toFloat
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
// Progress a specific block sits at along the chain, used to order queries from
|
|
2088
|
+
// different chains: a query starting further back (lower %) is fetched first.
|
|
2089
|
+
let getProgressPercentageAt = (fetchState: t, ~blockNumber) => {
|
|
2090
|
+
switch fetchState.firstEventBlock {
|
|
2091
|
+
| None => 0.
|
|
2092
|
+
| Some(firstEventBlock) =>
|
|
2093
|
+
let totalRange = fetchState.knownHeight - firstEventBlock
|
|
2094
|
+
totalRange <= 0 ? 0. : (blockNumber - firstEventBlock)->Int.toFloat /. totalRange->Int.toFloat
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
|
|
2098
|
+
let sortForBatch = {
|
|
1949
2099
|
let hasFullBatch = ({buffer} as fetchState: t, ~batchSizeTarget) => {
|
|
1950
2100
|
switch buffer->Array.get(batchSizeTarget - 1) {
|
|
1951
2101
|
| Some(item) => item->Internal.getItemBlockNumber <= fetchState->bufferBlockNumber
|
|
@@ -1953,24 +2103,6 @@ let sortForUnorderedBatch = {
|
|
|
1953
2103
|
}
|
|
1954
2104
|
}
|
|
1955
2105
|
|
|
1956
|
-
// Lower progress percentage = further behind = higher priority
|
|
1957
|
-
let getProgressPercentage = (fetchState: t) => {
|
|
1958
|
-
switch fetchState.firstEventBlock {
|
|
1959
|
-
| None => 0.
|
|
1960
|
-
| Some(firstEventBlock) =>
|
|
1961
|
-
let totalRange = fetchState.knownHeight - firstEventBlock
|
|
1962
|
-
if totalRange <= 0 {
|
|
1963
|
-
0.
|
|
1964
|
-
} else {
|
|
1965
|
-
let progress = switch fetchState.buffer->Array.get(0) {
|
|
1966
|
-
| Some(item) => item->Internal.getItemBlockNumber - firstEventBlock
|
|
1967
|
-
| None => fetchState->bufferBlockNumber - firstEventBlock
|
|
1968
|
-
}
|
|
1969
|
-
progress->Int.toFloat /. totalRange->Int.toFloat
|
|
1970
|
-
}
|
|
1971
|
-
}
|
|
1972
|
-
}
|
|
1973
|
-
|
|
1974
2106
|
(fetchStates: array<t>, ~batchSizeTarget: int) => {
|
|
1975
2107
|
let copied = fetchStates->Array.copy
|
|
1976
2108
|
copied->Array.sort((a: t, b: t) => {
|