envio 3.2.0 → 3.3.0-alpha.0
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 +375 -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 +618 -0
- package/src/ChainState.res.mjs +461 -0
- package/src/ChainState.resi +85 -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 +269 -0
- package/src/CrossChainState.res.mjs +197 -0
- package/src/CrossChainState.resi +47 -0
- package/src/Ecosystem.res +58 -0
- package/src/Ecosystem.res.mjs +43 -1
- package/src/Env.res +7 -2
- package/src/Env.res.mjs +5 -2
- package/src/EventConfigBuilder.res +35 -0
- package/src/EventConfigBuilder.res.mjs +21 -0
- 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 +162 -113
- package/src/FetchState.res.mjs +181 -119
- package/src/HandlerLoader.res +6 -1
- package/src/HandlerLoader.res.mjs +13 -36
- package/src/InMemoryStore.res +36 -451
- package/src/InMemoryStore.res.mjs +20 -369
- package/src/IndexerLoop.res +44 -0
- package/src/IndexerLoop.res.mjs +46 -0
- package/src/IndexerState.res +544 -0
- package/src/IndexerState.res.mjs +547 -0
- package/src/IndexerState.resi +132 -0
- package/src/Internal.res +44 -7
- package/src/LoadLayer.res +20 -18
- package/src/LoadLayer.res.mjs +14 -12
- package/src/LoadLayer.resi +6 -3
- package/src/LogSelection.res +86 -17
- package/src/LogSelection.res.mjs +67 -14
- 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 +7 -8
- package/src/Prometheus.res.mjs +7 -4
- 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/TestIndexer.res +34 -27
- package/src/TestIndexer.res.mjs +4 -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 +7 -5
- package/src/sources/HyperSyncSource.res.mjs +3 -3
- package/src/sources/RpcSource.res +2 -2
- package/src/sources/RpcSource.res.mjs +1 -1
- package/src/sources/SourceManager.res +8 -18
- package/src/sources/SourceManager.res.mjs +8 -5
- package/src/sources/SourceManager.resi +4 -1
- 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
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
type contractConfig = {
|
|
1
|
+
type contractConfig = {startBlock: option<int>}
|
|
2
2
|
|
|
3
|
-
type indexingAddress =
|
|
4
|
-
...Internal.indexingAddress,
|
|
5
|
-
effectiveStartBlock: int,
|
|
6
|
-
}
|
|
3
|
+
type indexingAddress = Internal.indexingContract
|
|
7
4
|
|
|
8
5
|
let deriveEffectiveStartBlock = (~registrationBlock: int, ~contractStartBlock: option<int>) => {
|
|
9
6
|
Pervasives.max(Pervasives.max(registrationBlock, 0), contractStartBlock->Option.getOr(0))
|
|
@@ -499,9 +496,10 @@ type t = {
|
|
|
499
496
|
blockLag: int,
|
|
500
497
|
// Buffer of items ordered from earliest to latest
|
|
501
498
|
buffer: array<Internal.item>,
|
|
502
|
-
//
|
|
503
|
-
//
|
|
504
|
-
|
|
499
|
+
// Caps how far ahead onBlock items are pre-generated (set to 2x the batch
|
|
500
|
+
// size). Fetch depth is bounded separately by getNextQuery's bufferLimit, the
|
|
501
|
+
// chain's per-tick slice of the indexer-wide pool.
|
|
502
|
+
maxOnBlockBufferSize: int,
|
|
505
503
|
onBlockConfigs: array<Internal.onBlockConfig>,
|
|
506
504
|
knownHeight: int,
|
|
507
505
|
firstEventBlock: option<int>,
|
|
@@ -557,7 +555,7 @@ let numAddresses = fetchState => fetchState.indexingAddresses->Utils.Dict.size
|
|
|
557
555
|
|
|
558
556
|
// Appends Block items produced by the onBlock handlers for every block in
|
|
559
557
|
// (fromBlock, maxBlockNumber] into mutItems and returns the new
|
|
560
|
-
// latestOnBlockBlockNumber pointer.
|
|
558
|
+
// latestOnBlockBlockNumber pointer. maxOnBlockBufferSize bounds how many items
|
|
561
559
|
// are generated at once to prevent OOM.
|
|
562
560
|
let appendOnBlockItems = (
|
|
563
561
|
~mutItems: array<Internal.item>,
|
|
@@ -565,7 +563,7 @@ let appendOnBlockItems = (
|
|
|
565
563
|
~indexerStartBlock,
|
|
566
564
|
~fromBlock,
|
|
567
565
|
~maxBlockNumber,
|
|
568
|
-
~
|
|
566
|
+
~maxOnBlockBufferSize,
|
|
569
567
|
) => {
|
|
570
568
|
let newItemsCounter = ref(0)
|
|
571
569
|
let latestOnBlockBlockNumber = ref(fromBlock)
|
|
@@ -576,7 +574,7 @@ let appendOnBlockItems = (
|
|
|
576
574
|
while (
|
|
577
575
|
latestOnBlockBlockNumber.contents < maxBlockNumber &&
|
|
578
576
|
// Additional safeguard to prevent OOM
|
|
579
|
-
newItemsCounter.contents <=
|
|
577
|
+
newItemsCounter.contents <= maxOnBlockBufferSize
|
|
580
578
|
) {
|
|
581
579
|
let blockNumber = latestOnBlockBlockNumber.contents + 1
|
|
582
580
|
latestOnBlockBlockNumber := blockNumber
|
|
@@ -630,7 +628,7 @@ let updateInternal = (
|
|
|
630
628
|
| [] => knownHeight
|
|
631
629
|
| onBlockConfigs => {
|
|
632
630
|
// Calculate the max block number we are going to create items for
|
|
633
|
-
// Use
|
|
631
|
+
// Use maxOnBlockBufferSize to get the last target item in the buffer
|
|
634
632
|
//
|
|
635
633
|
// mutItems is not very reliable, since it might not be sorted,
|
|
636
634
|
// but the chances for it happen are very low and not critical
|
|
@@ -639,7 +637,7 @@ let updateInternal = (
|
|
|
639
637
|
let maxBlockNumber = switch switch mutItemsRef.contents {
|
|
640
638
|
| Some(mutItems) => mutItems
|
|
641
639
|
| None => fetchState.buffer
|
|
642
|
-
}->Array.get(fetchState.
|
|
640
|
+
}->Array.get(fetchState.maxOnBlockBufferSize - 1) {
|
|
643
641
|
| Some(item) => item->Internal.getItemBlockNumber
|
|
644
642
|
| None =>
|
|
645
643
|
switch optimizedPartitions->OptimizedPartitions.getLatestFullyFetchedBlock {
|
|
@@ -660,7 +658,7 @@ let updateInternal = (
|
|
|
660
658
|
~indexerStartBlock=fetchState.startBlock,
|
|
661
659
|
~fromBlock=fetchState.latestOnBlockBlockNumber,
|
|
662
660
|
~maxBlockNumber,
|
|
663
|
-
~
|
|
661
|
+
~maxOnBlockBufferSize=fetchState.maxOnBlockBufferSize,
|
|
664
662
|
)
|
|
665
663
|
}
|
|
666
664
|
}
|
|
@@ -672,7 +670,7 @@ let updateInternal = (
|
|
|
672
670
|
normalSelection: fetchState.normalSelection,
|
|
673
671
|
chainId: fetchState.chainId,
|
|
674
672
|
onBlockConfigs: fetchState.onBlockConfigs,
|
|
675
|
-
|
|
673
|
+
maxOnBlockBufferSize: fetchState.maxOnBlockBufferSize,
|
|
676
674
|
optimizedPartitions,
|
|
677
675
|
latestOnBlockBlockNumber,
|
|
678
676
|
indexingAddresses,
|
|
@@ -756,7 +754,6 @@ Returns OptimizedPartitions.t directly.
|
|
|
756
754
|
*/
|
|
757
755
|
let createPartitionsFromIndexingAddresses = (
|
|
758
756
|
~registeringContractsByContract: dict<dict<indexingAddress>>,
|
|
759
|
-
~contractConfigs: dict<contractConfig>,
|
|
760
757
|
~dynamicContracts: Utils.Set.t<string>,
|
|
761
758
|
~normalSelection: selection,
|
|
762
759
|
~maxAddrInPartition: int,
|
|
@@ -778,9 +775,6 @@ OptimizedPartitions.t => {
|
|
|
778
775
|
let addresses =
|
|
779
776
|
registeringContracts->Dict.keysToArray->(Utils.magic: array<string> => array<Address.t>)
|
|
780
777
|
|
|
781
|
-
// Can unsafely get it, because we already filtered out the contracts
|
|
782
|
-
// that don't have any events to fetch
|
|
783
|
-
let contractConfig = contractConfigs->Dict.getUnsafe(contractName)
|
|
784
778
|
let isDynamic = dynamicContracts->Utils.Set.has(contractName)
|
|
785
779
|
let partitions = isDynamic ? dynamicPartitions : nonDynamicPartitions
|
|
786
780
|
|
|
@@ -802,30 +796,23 @@ OptimizedPartitions.t => {
|
|
|
802
796
|
let maybeNextStartBlockKey =
|
|
803
797
|
ascKeys->Array.getUnsafe(idx + 1)->(Utils.magic: string => option<string>)
|
|
804
798
|
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
if shouldJoinCurrentStartBlock {
|
|
823
|
-
addressesRef :=
|
|
824
|
-
addressesRef.contents->Array.concat(byStartBlock->Dict.getUnsafe(nextStartBlockKey))
|
|
825
|
-
false
|
|
826
|
-
} else {
|
|
827
|
-
true
|
|
828
|
-
}
|
|
799
|
+
let shouldAllocateNewPartition = switch maybeNextStartBlockKey {
|
|
800
|
+
| None => true
|
|
801
|
+
| Some(nextStartBlockKey) => {
|
|
802
|
+
let nextStartBlock = nextStartBlockKey->Int.fromString->Option.getUnsafe
|
|
803
|
+
let shouldJoinCurrentStartBlock =
|
|
804
|
+
nextStartBlock - startBlockRef.contents < OptimizedPartitions.tooFarBlockRange
|
|
805
|
+
|
|
806
|
+
// Addresses with different start blocks within range share a partition;
|
|
807
|
+
// events before each address's effectiveStartBlock are dropped on the
|
|
808
|
+
// client side (EventRouter for the srcAddress, the event's
|
|
809
|
+
// clientAddressFilter for address-valued params).
|
|
810
|
+
if shouldJoinCurrentStartBlock {
|
|
811
|
+
addressesRef :=
|
|
812
|
+
addressesRef.contents->Array.concat(byStartBlock->Dict.getUnsafe(nextStartBlockKey))
|
|
813
|
+
false
|
|
814
|
+
} else {
|
|
815
|
+
true
|
|
829
816
|
}
|
|
830
817
|
}
|
|
831
818
|
}
|
|
@@ -907,15 +894,10 @@ OptimizedPartitions.t => {
|
|
|
907
894
|
}
|
|
908
895
|
}
|
|
909
896
|
|
|
910
|
-
let nextContractName =
|
|
911
|
-
nextP.addressesByContractName->Dict.keysToArray->Utils.Array.firstUnsafe
|
|
912
|
-
let hasFilterByAddresses = (
|
|
913
|
-
contractConfigs->Dict.getUnsafe(nextContractName)
|
|
914
|
-
).filterByAddresses
|
|
915
897
|
let isTooFar = currentPBlock + OptimizedPartitions.tooFarBlockRange < nextPBlock
|
|
916
898
|
|
|
917
|
-
if isTooFar
|
|
918
|
-
// Too far
|
|
899
|
+
if isTooFar {
|
|
900
|
+
// Too far: mergeBlock on current, merge addresses into next
|
|
919
901
|
mergedNonDynamic
|
|
920
902
|
->Array.push({
|
|
921
903
|
...currentP,
|
|
@@ -927,7 +909,7 @@ OptimizedPartitions.t => {
|
|
|
927
909
|
addressesByContractName: mergedAddresses,
|
|
928
910
|
}
|
|
929
911
|
} else {
|
|
930
|
-
// Close
|
|
912
|
+
// Close: push next's addresses into current
|
|
931
913
|
currentPRef := {
|
|
932
914
|
...currentP,
|
|
933
915
|
addressesByContractName: mergedAddresses,
|
|
@@ -968,7 +950,6 @@ let registerDynamicContracts = (
|
|
|
968
950
|
let indexingAddresses = fetchState.indexingAddresses
|
|
969
951
|
let registeringContractsByContract: dict<dict<indexingAddress>> = Dict.make()
|
|
970
952
|
let earliestRegisteringEventBlockNumber = ref(%raw(`Infinity`))
|
|
971
|
-
let hasDCWithFilterByAddresses = ref(false)
|
|
972
953
|
// Addresses registered for contracts without matching events. These are not
|
|
973
954
|
// added to partitions, but they are tracked on fetchState.indexingAddresses
|
|
974
955
|
// so that later conflicting registrations are detected, and are persisted
|
|
@@ -991,7 +972,7 @@ let registerDynamicContracts = (
|
|
|
991
972
|
let shouldRemove = ref(false)
|
|
992
973
|
|
|
993
974
|
switch fetchState.contractConfigs->Utils.Dict.dangerouslyGetNonOption(dc.contractName) {
|
|
994
|
-
| Some({
|
|
975
|
+
| Some({startBlock: contractStartBlock}) =>
|
|
995
976
|
let dcWithStartBlock: indexingAddress = {
|
|
996
977
|
address: dc.address,
|
|
997
978
|
contractName: dc.contractName,
|
|
@@ -1038,9 +1019,7 @@ let registerDynamicContracts = (
|
|
|
1038
1019
|
// FIXME: This unsafely relies on the asc order of the items
|
|
1039
1020
|
// which is 99% true, but there were cases when the source ordering was wrong
|
|
1040
1021
|
false
|
|
1041
|
-
| None =>
|
|
1042
|
-
hasDCWithFilterByAddresses := hasDCWithFilterByAddresses.contents || filterByAddresses
|
|
1043
|
-
true
|
|
1022
|
+
| None => true
|
|
1044
1023
|
}
|
|
1045
1024
|
if shouldUpdate {
|
|
1046
1025
|
earliestRegisteringEventBlockNumber :=
|
|
@@ -1218,7 +1197,6 @@ let registerDynamicContracts = (
|
|
|
1218
1197
|
|
|
1219
1198
|
let optimizedPartitions = createPartitionsFromIndexingAddresses(
|
|
1220
1199
|
~registeringContractsByContract,
|
|
1221
|
-
~contractConfigs=fetchState.contractConfigs,
|
|
1222
1200
|
~dynamicContracts=dynamicContractsRef.contents,
|
|
1223
1201
|
~normalSelection=fetchState.normalSelection,
|
|
1224
1202
|
~maxAddrInPartition=fetchState.optimizedPartitions.maxAddrInPartition,
|
|
@@ -1245,6 +1223,20 @@ let handleQueryResult = (
|
|
|
1245
1223
|
~latestFetchedBlock: blockNumberAndTimestamp,
|
|
1246
1224
|
~newItems,
|
|
1247
1225
|
): t => {
|
|
1226
|
+
// Drop events an address-param filter rejects. A merged partition may
|
|
1227
|
+
// over-fetch a wildcard event whose indexed address param references an
|
|
1228
|
+
// address registered after the log's block; `clientAddressFilter` is the
|
|
1229
|
+
// param-level analogue of EventRouter's srcAddress effectiveStartBlock check.
|
|
1230
|
+
let newItems = newItems->Array.filter(item =>
|
|
1231
|
+
switch item {
|
|
1232
|
+
| Internal.Event({eventConfig, payload, blockNumber}) =>
|
|
1233
|
+
switch eventConfig.clientAddressFilter {
|
|
1234
|
+
| Some(filter) => filter(payload, blockNumber, fetchState.indexingAddresses)
|
|
1235
|
+
| None => true
|
|
1236
|
+
}
|
|
1237
|
+
| _ => true
|
|
1238
|
+
}
|
|
1239
|
+
)
|
|
1248
1240
|
fetchState->updateInternal(
|
|
1249
1241
|
~optimizedPartitions=fetchState.optimizedPartitions->OptimizedPartitions.handleQueryResponse(
|
|
1250
1242
|
~query,
|
|
@@ -1328,26 +1320,30 @@ let pushQueriesForRange = (
|
|
|
1328
1320
|
| None => maxQueryBlockNumber
|
|
1329
1321
|
}
|
|
1330
1322
|
let chunkSize = Js.Math.ceil_int(chunkRange->Int.toFloat *. 1.8)
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1323
|
+
// Probe with two smaller chunks first so their responses come back
|
|
1324
|
+
// quickly and refresh the chunking heuristic, then three full-size chunks.
|
|
1325
|
+
let probeSize = Js.Math.ceil_int(chunkRange->Int.toFloat *. 0.9)
|
|
1326
|
+
let getChunkSize = chunkIdx => chunkIdx < 2 ? probeSize : chunkSize
|
|
1327
|
+
if rangeFromBlock + getChunkSize(0) + getChunkSize(1) - 1 <= maxBlock {
|
|
1328
|
+
let chunkFromBlock = ref(rangeFromBlock)
|
|
1329
|
+
let chunkIdx = ref(0)
|
|
1330
|
+
while (
|
|
1331
|
+
chunkIdx.contents < 5 &&
|
|
1332
|
+
chunkFromBlock.contents + getChunkSize(chunkIdx.contents) - 1 <= maxBlock
|
|
1333
|
+
) {
|
|
1334
|
+
let chunkToBlock = chunkFromBlock.contents + getChunkSize(chunkIdx.contents) - 1
|
|
1335
|
+
queries->Array.push({
|
|
1336
|
+
partitionId,
|
|
1337
|
+
fromBlock: chunkFromBlock.contents,
|
|
1338
|
+
toBlock: Some(chunkToBlock),
|
|
1339
|
+
isChunk: true,
|
|
1340
|
+
selection,
|
|
1341
|
+
addressesByContractName,
|
|
1342
|
+
indexingAddresses,
|
|
1343
|
+
})
|
|
1344
|
+
chunkFromBlock := chunkToBlock + 1
|
|
1345
|
+
chunkIdx := chunkIdx.contents + 1
|
|
1346
|
+
}
|
|
1351
1347
|
} else {
|
|
1352
1348
|
// Not enough room for 2 chunks, fall back to a single query
|
|
1353
1349
|
queries->Array.push({
|
|
@@ -1365,23 +1361,30 @@ let pushQueriesForRange = (
|
|
|
1365
1361
|
}
|
|
1366
1362
|
}
|
|
1367
1363
|
|
|
1364
|
+
// Most parallel in-flight chunk queries a single partition may have at once.
|
|
1365
|
+
let maxPendingChunksPerPartition = 10
|
|
1366
|
+
|
|
1368
1367
|
let getNextQuery = (
|
|
1369
1368
|
{
|
|
1370
1369
|
buffer,
|
|
1371
1370
|
optimizedPartitions,
|
|
1372
|
-
targetBufferSize,
|
|
1373
1371
|
indexingAddresses,
|
|
1374
1372
|
blockLag,
|
|
1375
1373
|
latestOnBlockBlockNumber,
|
|
1376
1374
|
knownHeight,
|
|
1377
1375
|
} as fetchState: t,
|
|
1378
1376
|
~concurrencyLimit,
|
|
1377
|
+
~bufferLimit,
|
|
1379
1378
|
) => {
|
|
1380
1379
|
let headBlockNumber = knownHeight - blockLag
|
|
1381
1380
|
if headBlockNumber <= 0 {
|
|
1382
1381
|
WaitingForNewBlock
|
|
1383
1382
|
} else if concurrencyLimit === 0 {
|
|
1384
1383
|
ReachedMaxConcurrency
|
|
1384
|
+
} else if bufferLimit <= 0 {
|
|
1385
|
+
// No room left in the shared buffer pool for this chain; wait for processing
|
|
1386
|
+
// to drain before fetching more.
|
|
1387
|
+
NothingToQuery
|
|
1385
1388
|
} else {
|
|
1386
1389
|
let isOnBlockBehindTheHead = latestOnBlockBlockNumber < headBlockNumber
|
|
1387
1390
|
let shouldWaitForNewBlock = ref(
|
|
@@ -1392,13 +1395,12 @@ let getNextQuery = (
|
|
|
1392
1395
|
!isOnBlockBehindTheHead,
|
|
1393
1396
|
)
|
|
1394
1397
|
|
|
1395
|
-
//
|
|
1396
|
-
//
|
|
1397
|
-
//
|
|
1398
|
-
//
|
|
1399
|
-
// it should be skipped until the buffer is consumed
|
|
1398
|
+
// Limit how far ahead we fetch to bufferLimit items (this chain's share of
|
|
1399
|
+
// the indexer-wide buffer pool) so processing always has buffer without
|
|
1400
|
+
// ballooning memory. A partition that fetched further is skipped until the
|
|
1401
|
+
// buffer drains.
|
|
1400
1402
|
let maxQueryBlockNumber = {
|
|
1401
|
-
switch buffer->Array.get(
|
|
1403
|
+
switch buffer->Array.get(bufferLimit - 1) {
|
|
1402
1404
|
| Some(item) =>
|
|
1403
1405
|
// Just in case check that we don't query beyond the current block
|
|
1404
1406
|
Pervasives.min(item->Internal.getItemBlockNumber, knownHeight)
|
|
@@ -1415,8 +1417,9 @@ let getNextQuery = (
|
|
|
1415
1417
|
let partitionId = optimizedPartitions.idsInAscOrder->Array.getUnsafe(idx)
|
|
1416
1418
|
let p = optimizedPartitions.entities->Dict.getUnsafe(partitionId)
|
|
1417
1419
|
|
|
1420
|
+
let pendingCount = p.mutPendingQueries->Array.length
|
|
1418
1421
|
let isBehindTheHead = p.latestFetchedBlock.blockNumber < headBlockNumber
|
|
1419
|
-
let hasPendingQueries =
|
|
1422
|
+
let hasPendingQueries = pendingCount > 0
|
|
1420
1423
|
|
|
1421
1424
|
if hasPendingQueries || isBehindTheHead {
|
|
1422
1425
|
// Even if there are some partitions waiting for the new block
|
|
@@ -1427,6 +1430,8 @@ let getNextQuery = (
|
|
|
1427
1430
|
shouldWaitForNewBlock := false
|
|
1428
1431
|
}
|
|
1429
1432
|
|
|
1433
|
+
let partitionQueriesStart = queries->Array.length
|
|
1434
|
+
|
|
1430
1435
|
// Compute queryEndBlock for this partition
|
|
1431
1436
|
let queryEndBlock = Utils.Math.minOptInt(fetchState.endBlock, p.mergeBlock)
|
|
1432
1437
|
let queryEndBlock = switch blockLag {
|
|
@@ -1494,6 +1499,22 @@ let getNextQuery = (
|
|
|
1494
1499
|
)
|
|
1495
1500
|
}
|
|
1496
1501
|
|
|
1502
|
+
// Cap parallel in-flight chunks per partition so a single partition can't
|
|
1503
|
+
// drain the whole concurrency budget (especially the high realtime one).
|
|
1504
|
+
// Keep the earliest new chunks; the furthest-ahead ones wait for the next
|
|
1505
|
+
// round once these resolve.
|
|
1506
|
+
let maxNewChunks = Pervasives.max(0, maxPendingChunksPerPartition - pendingCount)
|
|
1507
|
+
let generatedCount = queries->Array.length - partitionQueriesStart
|
|
1508
|
+
if generatedCount > maxNewChunks {
|
|
1509
|
+
queries
|
|
1510
|
+
->Array.splice(
|
|
1511
|
+
~start=partitionQueriesStart + maxNewChunks,
|
|
1512
|
+
~remove=generatedCount - maxNewChunks,
|
|
1513
|
+
~insert=[],
|
|
1514
|
+
)
|
|
1515
|
+
->ignore
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1497
1518
|
idxRef := idxRef.contents + 1
|
|
1498
1519
|
}
|
|
1499
1520
|
|
|
@@ -1556,7 +1577,7 @@ let make = (
|
|
|
1556
1577
|
~addresses: array<Internal.indexingAddress>,
|
|
1557
1578
|
~maxAddrInPartition,
|
|
1558
1579
|
~chainId,
|
|
1559
|
-
~
|
|
1580
|
+
~maxOnBlockBufferSize,
|
|
1560
1581
|
~knownHeight,
|
|
1561
1582
|
~progressBlockNumber=startBlock - 1,
|
|
1562
1583
|
~onBlockConfigs=[],
|
|
@@ -1572,15 +1593,14 @@ let make = (
|
|
|
1572
1593
|
let normalEventConfigs = []
|
|
1573
1594
|
let contractNamesWithNormalEvents = Utils.Set.make()
|
|
1574
1595
|
let indexingAddresses = Dict.make()
|
|
1575
|
-
let contractConfigs = Dict.make()
|
|
1596
|
+
let contractConfigs: dict<contractConfig> = Dict.make()
|
|
1576
1597
|
|
|
1577
1598
|
eventConfigs->Array.forEach(ec => {
|
|
1578
1599
|
switch contractConfigs->Utils.Dict.dangerouslyGetNonOption(ec.contractName) {
|
|
1579
|
-
| Some({
|
|
1600
|
+
| Some({startBlock}) =>
|
|
1580
1601
|
contractConfigs->Dict.set(
|
|
1581
1602
|
ec.contractName,
|
|
1582
1603
|
{
|
|
1583
|
-
filterByAddresses: filterByAddresses || ec.filterByAddresses,
|
|
1584
1604
|
startBlock: switch (startBlock, ec.startBlock) {
|
|
1585
1605
|
| (Some(a), Some(b)) => Some(Pervasives.min(a, b))
|
|
1586
1606
|
| (Some(_) as s, None) | (None, Some(_) as s) => s
|
|
@@ -1592,7 +1612,6 @@ let make = (
|
|
|
1592
1612
|
contractConfigs->Dict.set(
|
|
1593
1613
|
ec.contractName,
|
|
1594
1614
|
{
|
|
1595
|
-
filterByAddresses: ec.filterByAddresses,
|
|
1596
1615
|
startBlock: ec.startBlock,
|
|
1597
1616
|
},
|
|
1598
1617
|
)
|
|
@@ -1672,7 +1691,6 @@ let make = (
|
|
|
1672
1691
|
|
|
1673
1692
|
let optimizedPartitions = createPartitionsFromIndexingAddresses(
|
|
1674
1693
|
~registeringContractsByContract,
|
|
1675
|
-
~contractConfigs,
|
|
1676
1694
|
~dynamicContracts,
|
|
1677
1695
|
~normalSelection,
|
|
1678
1696
|
~maxAddrInPartition,
|
|
@@ -1708,7 +1726,7 @@ let make = (
|
|
|
1708
1726
|
~indexerStartBlock=startBlock,
|
|
1709
1727
|
~fromBlock=progressBlockNumber,
|
|
1710
1728
|
~maxBlockNumber,
|
|
1711
|
-
~
|
|
1729
|
+
~maxOnBlockBufferSize,
|
|
1712
1730
|
)
|
|
1713
1731
|
} else {
|
|
1714
1732
|
progressBlockNumber
|
|
@@ -1725,7 +1743,7 @@ let make = (
|
|
|
1725
1743
|
indexingAddresses,
|
|
1726
1744
|
blockLag,
|
|
1727
1745
|
onBlockConfigs,
|
|
1728
|
-
|
|
1746
|
+
maxOnBlockBufferSize,
|
|
1729
1747
|
knownHeight,
|
|
1730
1748
|
buffer,
|
|
1731
1749
|
firstEventBlock,
|
|
@@ -1749,6 +1767,25 @@ let make = (
|
|
|
1749
1767
|
|
|
1750
1768
|
let bufferSize = ({buffer}: t) => buffer->Array.length
|
|
1751
1769
|
|
|
1770
|
+
// Number of buffered items at or below the ready frontier (processable now,
|
|
1771
|
+
// i.e. not stuck behind a gap from a lagging partition or out-of-order chunk).
|
|
1772
|
+
// The buffer is kept sorted, so binary-search the frontier in O(log n).
|
|
1773
|
+
let bufferReadyCount = (fetchState: t) => {
|
|
1774
|
+
let frontier = fetchState->bufferBlockNumber
|
|
1775
|
+
let buffer = fetchState.buffer
|
|
1776
|
+
let lo = ref(0)
|
|
1777
|
+
let hi = ref(buffer->Array.length)
|
|
1778
|
+
while lo.contents < hi.contents {
|
|
1779
|
+
let mid = (lo.contents + hi.contents) / 2
|
|
1780
|
+
if buffer->Array.getUnsafe(mid)->Internal.getItemBlockNumber <= frontier {
|
|
1781
|
+
lo := mid + 1
|
|
1782
|
+
} else {
|
|
1783
|
+
hi := mid
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
lo.contents
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1752
1789
|
let rollbackPendingQueries = (mutPendingQueries: array<pendingQuery>, ~targetBlockNumber) => {
|
|
1753
1790
|
// - Remove queries where fromBlock > target
|
|
1754
1791
|
// - Cap fetchedBlock at target where fetchedBlock > target
|
|
@@ -1875,7 +1912,6 @@ let rollback = (fetchState: t, ~targetBlockNumber) => {
|
|
|
1875
1912
|
// Step 3: Recreate partitions from deleted partition addresses
|
|
1876
1913
|
let optimizedPartitions = createPartitionsFromIndexingAddresses(
|
|
1877
1914
|
~registeringContractsByContract,
|
|
1878
|
-
~contractConfigs=fetchState.contractConfigs,
|
|
1879
1915
|
~dynamicContracts=fetchState.optimizedPartitions.dynamicContracts,
|
|
1880
1916
|
~normalSelection=fetchState.normalSelection,
|
|
1881
1917
|
~maxAddrInPartition=fetchState.optimizedPartitions.maxAddrInPartition,
|
|
@@ -1948,6 +1984,18 @@ let isActivelyIndexing = ({endBlock} as fetchState: t) => {
|
|
|
1948
1984
|
}
|
|
1949
1985
|
}
|
|
1950
1986
|
|
|
1987
|
+
// True once the fetch frontier has reached the (lagged) head or endBlock,
|
|
1988
|
+
// regardless of whether the buffer has been consumed yet. Unlike
|
|
1989
|
+
// isReadyToEnterReorgThreshold, fetched-but-unprocessed items still count.
|
|
1990
|
+
let isFetchingAtHead = ({endBlock, blockLag, knownHeight} as fetchState: t) => {
|
|
1991
|
+
let bufferBlockNumber = fetchState->bufferBlockNumber
|
|
1992
|
+
knownHeight !== 0 &&
|
|
1993
|
+
switch endBlock {
|
|
1994
|
+
| Some(endBlock) if bufferBlockNumber >= endBlock => true
|
|
1995
|
+
| _ => bufferBlockNumber >= knownHeight - blockLag
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1951
1999
|
let isReadyToEnterReorgThreshold = ({endBlock, blockLag, buffer, knownHeight} as fetchState: t) => {
|
|
1952
2000
|
let bufferBlockNumber = fetchState->bufferBlockNumber
|
|
1953
2001
|
knownHeight !== 0 &&
|
|
@@ -1958,7 +2006,26 @@ let isReadyToEnterReorgThreshold = ({endBlock, blockLag, buffer, knownHeight} as
|
|
|
1958
2006
|
buffer->Utils.Array.isEmpty
|
|
1959
2007
|
}
|
|
1960
2008
|
|
|
1961
|
-
|
|
2009
|
+
// Lower progress percentage = further behind = higher priority. Shared by the
|
|
2010
|
+
// batch ordering and the cross-chain fetch priority.
|
|
2011
|
+
let getProgressPercentage = (fetchState: t) => {
|
|
2012
|
+
switch fetchState.firstEventBlock {
|
|
2013
|
+
| None => 0.
|
|
2014
|
+
| Some(firstEventBlock) =>
|
|
2015
|
+
let totalRange = fetchState.knownHeight - firstEventBlock
|
|
2016
|
+
if totalRange <= 0 {
|
|
2017
|
+
0.
|
|
2018
|
+
} else {
|
|
2019
|
+
let progress = switch fetchState.buffer->Array.get(0) {
|
|
2020
|
+
| Some(item) => item->Internal.getItemBlockNumber - firstEventBlock
|
|
2021
|
+
| None => fetchState->bufferBlockNumber - firstEventBlock
|
|
2022
|
+
}
|
|
2023
|
+
progress->Int.toFloat /. totalRange->Int.toFloat
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
|
|
2028
|
+
let sortForBatch = {
|
|
1962
2029
|
let hasFullBatch = ({buffer} as fetchState: t, ~batchSizeTarget) => {
|
|
1963
2030
|
switch buffer->Array.get(batchSizeTarget - 1) {
|
|
1964
2031
|
| Some(item) => item->Internal.getItemBlockNumber <= fetchState->bufferBlockNumber
|
|
@@ -1966,24 +2033,6 @@ let sortForUnorderedBatch = {
|
|
|
1966
2033
|
}
|
|
1967
2034
|
}
|
|
1968
2035
|
|
|
1969
|
-
// Lower progress percentage = further behind = higher priority
|
|
1970
|
-
let getProgressPercentage = (fetchState: t) => {
|
|
1971
|
-
switch fetchState.firstEventBlock {
|
|
1972
|
-
| None => 0.
|
|
1973
|
-
| Some(firstEventBlock) =>
|
|
1974
|
-
let totalRange = fetchState.knownHeight - firstEventBlock
|
|
1975
|
-
if totalRange <= 0 {
|
|
1976
|
-
0.
|
|
1977
|
-
} else {
|
|
1978
|
-
let progress = switch fetchState.buffer->Array.get(0) {
|
|
1979
|
-
| Some(item) => item->Internal.getItemBlockNumber - firstEventBlock
|
|
1980
|
-
| None => fetchState->bufferBlockNumber - firstEventBlock
|
|
1981
|
-
}
|
|
1982
|
-
progress->Int.toFloat /. totalRange->Int.toFloat
|
|
1983
|
-
}
|
|
1984
|
-
}
|
|
1985
|
-
}
|
|
1986
|
-
|
|
1987
2036
|
(fetchStates: array<t>, ~batchSizeTarget: int) => {
|
|
1988
2037
|
let copied = fetchStates->Array.copy
|
|
1989
2038
|
copied->Array.sort((a: t, b: t) => {
|