envio 3.11.0 → 3.12.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/index.d.ts +3 -0
- package/package.json +6 -6
- package/src/Batch.res +111 -78
- package/src/Batch.res.mjs +53 -46
- package/src/BatchProcessing.res +1 -11
- package/src/BatchProcessing.res.mjs +1 -4
- package/src/ChainFetching.res +7 -3
- package/src/ChainFetching.res.mjs +1 -1
- package/src/ChainState.res +82 -20
- package/src/ChainState.res.mjs +51 -13
- package/src/ChainState.resi +6 -1
- package/src/Config.res +58 -37
- package/src/Config.res.mjs +63 -53
- package/src/CrossChainState.res +50 -53
- package/src/CrossChainState.res.mjs +25 -23
- package/src/CrossChainState.resi +6 -13
- package/src/EffectState.res +13 -4
- package/src/EffectState.resi +7 -1
- package/src/EventProcessing.res +6 -6
- package/src/EventProcessing.res.mjs +6 -6
- package/src/FetchState.res +65 -46
- package/src/FetchState.res.mjs +68 -36
- package/src/FinalizeBackfill.res +16 -12
- package/src/FinalizeBackfill.res.mjs +3 -3
- package/src/HistoryPolicy.res +46 -0
- package/src/HistoryPolicy.res.mjs +50 -0
- package/src/InMemoryStore.res +30 -32
- package/src/InMemoryStore.res.mjs +33 -28
- package/src/InMemoryTable.res +155 -67
- package/src/InMemoryTable.res.mjs +151 -60
- package/src/IndexerState.res +66 -45
- package/src/IndexerState.res.mjs +57 -29
- package/src/IndexerState.resi +8 -13
- package/src/Internal.res +4 -2
- package/src/LoadLayer.res +68 -38
- package/src/LoadLayer.res.mjs +50 -64
- package/src/LoadLayer.resi +1 -1
- package/src/Main.res +25 -24
- package/src/Main.res.mjs +24 -25
- package/src/Metrics.res +49 -1
- package/src/Metrics.res.mjs +26 -3
- package/src/Persistence.res +55 -16
- package/src/Persistence.res.mjs +18 -3
- package/src/PgStorage.res +286 -180
- package/src/PgStorage.res.mjs +209 -122
- package/src/PruneStaleHistory.res +9 -15
- package/src/PruneStaleHistory.res.mjs +11 -20
- package/src/Rollback.res +9 -14
- package/src/Rollback.res.mjs +5 -3
- package/src/Sink.res +7 -5
- package/src/Sink.res.mjs +4 -4
- package/src/Staging.res +298 -0
- package/src/Staging.res.mjs +257 -0
- package/src/TestIndexer.res +14 -30
- package/src/TestIndexer.res.mjs +11 -9
- package/src/UserContext.res +20 -36
- package/src/UserContext.res.mjs +6 -9
- package/src/Utils.res +10 -4
- package/src/Utils.res.mjs +16 -16
- package/src/Writing.res +34 -20
- package/src/Writing.res.mjs +15 -20
- package/src/bindings/ClickHouse.res +41 -34
- package/src/bindings/ClickHouse.res.mjs +36 -30
- package/src/bindings/ClickHouseSink.res +45 -186
- package/src/bindings/ClickHouseSink.res.mjs +21 -174
- package/src/db/CheckpointSequence.res +109 -0
- package/src/db/CheckpointSequence.res.mjs +99 -0
- package/src/db/EntityFilter.res +487 -275
- package/src/db/EntityFilter.res.mjs +557 -309
- package/src/db/EntityHistory.res +6 -5
- package/src/db/EntityHistory.res.mjs +5 -5
- package/src/db/Frontier.res +86 -0
- package/src/db/Frontier.res.mjs +126 -0
- package/src/db/InternalTable.res +145 -27
- package/src/db/InternalTable.res.mjs +115 -29
- package/src/db/RollbackFloors.res +34 -41
- package/src/db/RollbackFloors.res.mjs +27 -71
- package/src/db/Table.res +21 -6
- package/src/db/Table.res.mjs +13 -4
- package/src/sources/BlockStore.res +8 -0
- package/src/sources/EvmHyperSyncSource.res +5 -1
- package/src/sources/EvmHyperSyncSource.res.mjs +4 -3
- package/src/sources/FuelHyperSyncSource.res +1 -0
- package/src/sources/FuelHyperSyncSource.res.mjs +1 -1
- package/src/sources/HyperSync.res +6 -0
- package/src/sources/HyperSync.res.mjs +5 -2
- package/src/sources/HyperSync.resi +2 -0
- package/src/sources/HyperSyncClient.res +6 -0
- package/src/sources/HyperSyncSSE.res +1 -1
- package/src/sources/HyperSyncSSE.res.mjs +4 -10
- package/src/sources/RequestStat.res +8 -1
- package/src/sources/RpcSource.res +1 -0
- package/src/sources/RpcSource.res.mjs +1 -1
- package/src/sources/SimulateSource.res +1 -0
- package/src/sources/SimulateSource.res.mjs +1 -1
- package/src/sources/Source.res +11 -3
- package/src/sources/SourceManager.res +34 -13
- package/src/sources/SourceManager.res.mjs +26 -9
- package/src/sources/SourceManager.resi +2 -0
- package/src/sources/SvmHyperSyncClient.res +5 -0
- package/src/sources/SvmHyperSyncSource.res +3 -0
- package/src/sources/SvmHyperSyncSource.res.mjs +4 -2
- package/src/tui/Tui.res +56 -143
- package/src/tui/Tui.res.mjs +70 -166
- package/src/tui/components/TuiData.res +61 -16
- package/src/tui/components/TuiData.res.mjs +53 -15
- package/svm.schema.json +0 -7
- package/src/db/CheckpointBounds.res +0 -53
- package/src/db/CheckpointBounds.res.mjs +0 -44
package/src/BatchProcessing.res
CHANGED
|
@@ -69,17 +69,7 @@ and processNextBatch = async (state: IndexerState.t, ~scheduleFetch): unit => {
|
|
|
69
69
|
let isInReorgThresholdBeforeUpdate = state->IndexerState.isInReorgThreshold
|
|
70
70
|
let isRealtimeBeforeUpdate = state->IndexerState.isRealtime
|
|
71
71
|
|
|
72
|
-
let
|
|
73
|
-
| RollbackReady(_) => true
|
|
74
|
-
| _ => false
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
let batch =
|
|
78
|
-
state->IndexerState.createBatch(
|
|
79
|
-
~processedCheckpointId=state->IndexerState.processedCheckpointId,
|
|
80
|
-
~batchSizeTarget=(state->IndexerState.config).batchSize,
|
|
81
|
-
~isRollback=isRollbackBatch,
|
|
82
|
-
)
|
|
72
|
+
let batch = state->IndexerState.createBatch(~batchSizeTarget=(state->IndexerState.config).batchSize)
|
|
83
73
|
|
|
84
74
|
let progressedChainsById = batch.progressedChainsById
|
|
85
75
|
|
|
@@ -23,10 +23,7 @@ function yieldTick() {
|
|
|
23
23
|
async function processNextBatch(state, scheduleFetch) {
|
|
24
24
|
let isInReorgThresholdBeforeUpdate = IndexerState.isInReorgThreshold(state);
|
|
25
25
|
let isRealtimeBeforeUpdate = IndexerState.isRealtime(state);
|
|
26
|
-
let
|
|
27
|
-
let isRollbackBatch;
|
|
28
|
-
isRollbackBatch = typeof match !== "object" ? false : match.TAG === "RollbackReady";
|
|
29
|
-
let batch = IndexerState.createBatch(state, IndexerState.processedCheckpointId(state), IndexerState.config(state).batchSize, isRollbackBatch);
|
|
26
|
+
let batch = IndexerState.createBatch(state, IndexerState.config(state).batchSize);
|
|
30
27
|
let progressedChainsById = batch.progressedChainsById;
|
|
31
28
|
let isBelowReorgThreshold = !isInReorgThresholdBeforeUpdate && IndexerState.config(state).shouldRollbackOnReorg;
|
|
32
29
|
let shouldEnterReorgThreshold = isBelowReorgThreshold && Object.values(IndexerState.chainStates(state)).every(cs => ChainState.isReadyToEnterReorgThresholdAfterBatch(cs, batch));
|
package/src/ChainFetching.res
CHANGED
|
@@ -326,9 +326,13 @@ let fetchChain = async (
|
|
|
326
326
|
let isRealtime = state->IndexerState.isRealtime
|
|
327
327
|
let sourceManager = chainState->ChainState.sourceManager
|
|
328
328
|
|
|
329
|
-
// Only affects the WaitingForNewBlock branch of dispatch, where
|
|
330
|
-
//
|
|
331
|
-
|
|
329
|
+
// Only affects the WaitingForNewBlock branch of dispatch, where there's
|
|
330
|
+
// nothing to fetch. The line is whether any height is known at all, not
|
|
331
|
+
// whether the head is fetchable yet: a chain held back by its lag is still
|
|
332
|
+
// tracking a head it has, and asking faster would buy nothing. A chain with
|
|
333
|
+
// no height has nothing to show for itself until the first one lands, so it
|
|
334
|
+
// polls at the source's own cadence and earns the normal stall window.
|
|
335
|
+
let reducedPolling = !isRealtime && chainState->ChainState.knownHeight > 0
|
|
332
336
|
|
|
333
337
|
// Owns its error boundary: launch doesn't catch, so any failure here (the
|
|
334
338
|
// query, response handling, or dispatch itself) must stop the indexer.
|
|
@@ -180,7 +180,7 @@ async function fetchChain(state, chainId, action, stateId, scheduleFetch, schedu
|
|
|
180
180
|
}
|
|
181
181
|
let isRealtime = IndexerState.isRealtime(state);
|
|
182
182
|
let sourceManager = ChainState.sourceManager(chainState);
|
|
183
|
-
let reducedPolling = !isRealtime;
|
|
183
|
+
let reducedPolling = !isRealtime && ChainState.knownHeight(chainState) > 0;
|
|
184
184
|
try {
|
|
185
185
|
return await ChainState.dispatch(chainState, async query => {
|
|
186
186
|
try {
|
package/src/ChainState.res
CHANGED
|
@@ -41,6 +41,8 @@ type t = {
|
|
|
41
41
|
// detected reorg either rolls back or is only logged.
|
|
42
42
|
shouldRollbackOnReorg: bool,
|
|
43
43
|
maxReorgDepth: int,
|
|
44
|
+
// One-way: past it, everything the chain writes can still be rolled back.
|
|
45
|
+
mutable isInReorgThreshold: bool,
|
|
44
46
|
// Holds this chain's transactions (kept in Rust) keyed by (blockNumber,
|
|
45
47
|
// transactionIndex). Fetch responses merge their page in; entries are pruned
|
|
46
48
|
// as the chain progresses and dropped above the target on rollback.
|
|
@@ -60,6 +62,9 @@ type t = {
|
|
|
60
62
|
mutable reorgDetectedBlock: option<int>,
|
|
61
63
|
mutable rollbackTargetBlock: option<int>,
|
|
62
64
|
mutable progressLatencyMs: option<int>,
|
|
65
|
+
// Timestamp of the committed progress block itself. See
|
|
66
|
+
// `Batch.chainAfterBatch.progressBlockTime`.
|
|
67
|
+
mutable committedProgressBlockTime: option<int>,
|
|
63
68
|
}
|
|
64
69
|
|
|
65
70
|
// The chain's config-declared addresses as storage rows: what a clean run
|
|
@@ -123,9 +128,11 @@ let make = (
|
|
|
123
128
|
~addressStore: AddressStore.t,
|
|
124
129
|
~sourceManager: SourceManager.t,
|
|
125
130
|
~committedProgressBlockNumber: int,
|
|
131
|
+
~committedProgressBlockTime=None,
|
|
126
132
|
~safeCheckpointTracking=None,
|
|
127
133
|
~shouldRollbackOnReorg,
|
|
128
134
|
~maxReorgDepth,
|
|
135
|
+
~isInReorgThreshold=false,
|
|
129
136
|
~numEventsProcessed=0.,
|
|
130
137
|
~timestampCaughtUpToHeadOrEndblock=None,
|
|
131
138
|
~isProgressAtHead=false,
|
|
@@ -155,6 +162,7 @@ let make = (
|
|
|
155
162
|
safeCheckpointTracking,
|
|
156
163
|
shouldRollbackOnReorg,
|
|
157
164
|
maxReorgDepth,
|
|
165
|
+
isInReorgThreshold,
|
|
158
166
|
transactionStore,
|
|
159
167
|
blockStore,
|
|
160
168
|
reorgThresholdReadyTolerance,
|
|
@@ -167,6 +175,7 @@ let make = (
|
|
|
167
175
|
reorgDetectedBlock: None,
|
|
168
176
|
rollbackTargetBlock: None,
|
|
169
177
|
progressLatencyMs: None,
|
|
178
|
+
committedProgressBlockTime,
|
|
170
179
|
}
|
|
171
180
|
}
|
|
172
181
|
|
|
@@ -180,6 +189,7 @@ let makeInternal = (
|
|
|
180
189
|
~endBlock,
|
|
181
190
|
~firstEventBlock=None,
|
|
182
191
|
~progressBlockNumber,
|
|
192
|
+
~committedProgressBlockTime=None,
|
|
183
193
|
~config: Config.t,
|
|
184
194
|
~registrationsByChainId: HandlerRegister.registrationsByChainId,
|
|
185
195
|
~logger,
|
|
@@ -308,12 +318,14 @@ let makeInternal = (
|
|
|
308
318
|
~sourceManager=SourceManager.make(~sources, ~isRealtime, ~reducedPollingInterval?),
|
|
309
319
|
~shouldRollbackOnReorg=config.shouldRollbackOnReorg,
|
|
310
320
|
~maxReorgDepth,
|
|
321
|
+
~isInReorgThreshold,
|
|
311
322
|
~safeCheckpointTracking=SafeCheckpointTracking.make(
|
|
312
323
|
~maxReorgDepth,
|
|
313
324
|
~shouldRollbackOnReorg=config.shouldRollbackOnReorg,
|
|
314
325
|
~chainReorgCheckpoints,
|
|
315
326
|
),
|
|
316
327
|
~committedProgressBlockNumber=progressBlockNumber,
|
|
328
|
+
~committedProgressBlockTime,
|
|
317
329
|
~perChainEntities=config.userEntities->EntityTables.perChain,
|
|
318
330
|
~timestampCaughtUpToHeadOrEndblock,
|
|
319
331
|
~numEventsProcessed,
|
|
@@ -363,6 +375,10 @@ let makeFromDbState = (
|
|
|
363
375
|
~maxReorgDepth=resumedChainState.maxReorgDepth,
|
|
364
376
|
~firstEventBlock=resumedChainState.firstEventBlockNumber,
|
|
365
377
|
~progressBlockNumber,
|
|
378
|
+
// A block's time never changes, so the stored one stays true for the block
|
|
379
|
+
// progress is at - and keeps the metric reporting from the first scrape
|
|
380
|
+
// after a restart, rather than only once a batch commits.
|
|
381
|
+
~committedProgressBlockTime=resumedChainState.progressBlockTime,
|
|
366
382
|
~timestampCaughtUpToHeadOrEndblock=resumedChainState.timestampCaughtUpToHeadOrEndblock,
|
|
367
383
|
~numEventsProcessed=resumedChainState.numEventsProcessed,
|
|
368
384
|
~logger,
|
|
@@ -402,6 +418,7 @@ let getLatestValidScannedBlock = (cs: t, ~blockStore: BlockStore.t, ~blockNumber
|
|
|
402
418
|
let safeCheckpointTracking = (cs: t) => cs.safeCheckpointTracking
|
|
403
419
|
let isProgressAtHead = (cs: t) => cs.isProgressAtHead
|
|
404
420
|
let committedProgressBlockNumber = (cs: t) => cs.committedProgressBlockNumber
|
|
421
|
+
let committedProgressBlockTime = (cs: t) => cs.committedProgressBlockTime
|
|
405
422
|
let numEventsProcessed = (cs: t) => cs.numEventsProcessed
|
|
406
423
|
let pendingBudget = (cs: t) => cs.pendingBudget
|
|
407
424
|
let timestampCaughtUpToHeadOrEndblock = (cs: t) => cs.timestampCaughtUpToHeadOrEndblock
|
|
@@ -640,15 +657,21 @@ let hasProcessedToEndblock = (cs: t) => {
|
|
|
640
657
|
// head has run away from it while the indexer was down.
|
|
641
658
|
let isDurablyCaughtUp = (cs: t) => {
|
|
642
659
|
let {committedProgressBlockNumber, fetchState} = cs
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
660
|
+
let atEndBlock =
|
|
661
|
+
fetchState.endBlock->Option.mapOr(false, endBlock => committedProgressBlockNumber >= endBlock)
|
|
662
|
+
// Either one, like `isFetchingAtHead`: an `end_block` above the head is never
|
|
663
|
+
// reached, and testing only for it would leave such a chain reading as behind
|
|
664
|
+
// however long it sits at the head.
|
|
665
|
+
//
|
|
666
|
+
// The configured lag, not the fetch state's: pre-threshold that one also
|
|
667
|
+
// carries maxReorgDepth, which would read a chain a whole reorg depth behind
|
|
668
|
+
// the head as caught up. Clamped at zero: the -1 a run that has processed
|
|
669
|
+
// nothing carries would otherwise clear a chain younger than its own lag.
|
|
670
|
+
let atHead =
|
|
649
671
|
fetchState.knownHeight > 0 &&
|
|
650
|
-
committedProgressBlockNumber >=
|
|
651
|
-
|
|
672
|
+
committedProgressBlockNumber >=
|
|
673
|
+
Pervasives.max(0, fetchState.knownHeight - cs.chainConfig.blockLag)
|
|
674
|
+
atEndBlock || atHead
|
|
652
675
|
}
|
|
653
676
|
|
|
654
677
|
let getHighestBlockBelowThreshold = (cs: t): int => {
|
|
@@ -929,8 +952,18 @@ let setEndBlockToFirstEvent = (cs: t, ~blockNumber) =>
|
|
|
929
952
|
}
|
|
930
953
|
|
|
931
954
|
// Shrink the fetch buffer by the configured blockLag on entering the reorg threshold.
|
|
932
|
-
let enterReorgThreshold = (cs: t) =>
|
|
955
|
+
let enterReorgThreshold = (cs: t) => {
|
|
956
|
+
cs.isInReorgThreshold = true
|
|
933
957
|
cs.fetchState = cs.fetchState->FetchState.updateInternal(~blockLag=cs.chainConfig.blockLag)
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
let isInReorgThreshold = (cs: t) => cs.isInReorgThreshold
|
|
961
|
+
|
|
962
|
+
// Whether the chain's writes need history: only what a rollback could still
|
|
963
|
+
// reach. A chain with no reorg depth is never rolled back, however far its
|
|
964
|
+
// progress has run.
|
|
965
|
+
let shouldSaveHistory = (cs: t) =>
|
|
966
|
+
cs.shouldRollbackOnReorg && cs.maxReorgDepth > 0 && cs.isInReorgThreshold
|
|
934
967
|
|
|
935
968
|
// Snapshot the chain's metadata fields for staging into the chains table.
|
|
936
969
|
let toChainMetadata = (cs: t): InternalTable.Chains.metaFields => {
|
|
@@ -964,6 +997,7 @@ let toMetrics = (cs: t): Metrics.chainMetrics => {
|
|
|
964
997
|
sourceBlockNumber: cs.fetchState.knownHeight,
|
|
965
998
|
progressBlockNumber: cs.committedProgressBlockNumber,
|
|
966
999
|
progressLatencyMs: cs.progressLatencyMs,
|
|
1000
|
+
progressBlockTime: cs.committedProgressBlockTime,
|
|
967
1001
|
concurrency: cs.sourceManager->SourceManager.inFlightCount,
|
|
968
1002
|
partitionsCount: cs.fetchState->FetchState.partitionsCount,
|
|
969
1003
|
bufferSize: cs.fetchState->FetchState.bufferSize,
|
|
@@ -979,12 +1013,15 @@ let toMetrics = (cs: t): Metrics.chainMetrics => {
|
|
|
979
1013
|
reorgCount: cs.reorgCount,
|
|
980
1014
|
reorgDetectedBlock: cs.reorgDetectedBlock,
|
|
981
1015
|
rollbackTargetBlock: cs.rollbackTargetBlock,
|
|
1016
|
+
rateLimitTimeMs: cs.sourceManager->SourceManager.getRateLimitTimeMs,
|
|
1017
|
+
rateLimitResetInMs: cs.sourceManager->SourceManager.getRateLimitResetInMs,
|
|
982
1018
|
}
|
|
983
1019
|
|
|
984
|
-
// Snapshot the inputs a batch build needs from this chain
|
|
985
|
-
//
|
|
986
|
-
//
|
|
987
|
-
|
|
1020
|
+
// Snapshot the inputs a batch build needs from this chain. The scanned
|
|
1021
|
+
// in-threshold block hashes are copied up front because the build reuses the
|
|
1022
|
+
// whole set; the block time comes as a lookup instead, since the key it needs -
|
|
1023
|
+
// the batch's own progress block - isn't known until the build computes it.
|
|
1024
|
+
let toChainBeforeBatch = (cs: t, ~isRealtime): Batch.chainBeforeBatch => {
|
|
988
1025
|
let {blockNumbers, hashes} =
|
|
989
1026
|
cs.blockStore->BlockStore.getHashes(
|
|
990
1027
|
~fromBlock=Pervasives.max(cs.fetchState.knownHeight - cs.maxReorgDepth, 0),
|
|
@@ -1000,6 +1037,13 @@ let toChainBeforeBatch = (cs: t): Batch.chainBeforeBatch => {
|
|
|
1000
1037
|
totalEventsProcessed: cs.numEventsProcessed,
|
|
1001
1038
|
sourceBlockNumber: cs.fetchState.knownHeight,
|
|
1002
1039
|
scannedHashes: {blockNumbers, hashByBlockNumber},
|
|
1040
|
+
// A slot that produced no block only counts as skipped where the query
|
|
1041
|
+
// covered every slot in its range, which is what a chain at the head asks
|
|
1042
|
+
// for.
|
|
1043
|
+
blockTimeAt: blockNumber =>
|
|
1044
|
+
cs.blockStore
|
|
1045
|
+
->BlockStore.getTimestamp(blockNumber, ~allowSkippedSlot=isRealtime)
|
|
1046
|
+
->Null.toOption,
|
|
1003
1047
|
shouldRollbackOnReorg: cs.shouldRollbackOnReorg,
|
|
1004
1048
|
chainConfig: cs.chainConfig,
|
|
1005
1049
|
}
|
|
@@ -1099,6 +1143,7 @@ let applyBatchProgress = (cs: t, ~batch: Batch.t, ~blockTimestampName: string) =
|
|
|
1099
1143
|
}
|
|
1100
1144
|
|
|
1101
1145
|
cs.committedProgressBlockNumber = chainAfterBatch.progressBlockNumber
|
|
1146
|
+
cs.committedProgressBlockTime = chainAfterBatch.progressBlockTime
|
|
1102
1147
|
|
|
1103
1148
|
// Normally already set by advanceAfterBatch at batch creation; catch up
|
|
1104
1149
|
// here for paths that commit progress without it.
|
|
@@ -1135,16 +1180,33 @@ let applyBatchProgress = (cs: t, ~batch: Batch.t, ~blockTimestampName: string) =
|
|
|
1135
1180
|
}
|
|
1136
1181
|
|
|
1137
1182
|
// Mark the chain caught up to head/endblock. Called by CrossChainState only once
|
|
1138
|
-
// every chain
|
|
1139
|
-
// committed, so no chain flips to ready while
|
|
1140
|
-
//
|
|
1141
|
-
//
|
|
1142
|
-
//
|
|
1183
|
+
// every chain this process drives is caught up and the indexes the schema
|
|
1184
|
+
// promises them are committed, so no chain flips to ready while an index it
|
|
1185
|
+
// promised is still missing. `readyAt` is the timestamp already committed to
|
|
1186
|
+
// `envio_chains.ready_at` in that same transaction. Sticky: a chain stays ready
|
|
1187
|
+
// once set.
|
|
1143
1188
|
let markReady = (cs: t, ~readyAt) =>
|
|
1144
1189
|
if !(cs->isReady) {
|
|
1145
1190
|
cs.timestampCaughtUpToHeadOrEndblock = Some(readyAt)
|
|
1146
1191
|
}
|
|
1147
1192
|
|
|
1193
|
+
// Rewind progress to a reorg target. Moving it means the timestamp now belongs
|
|
1194
|
+
// to an orphaned block, so it's re-read for the block progress actually lands
|
|
1195
|
+
// on - normally `None`, since the store keeps only hashes below the last
|
|
1196
|
+
// committed progress, until the re-fetch commits a new progress block. A target
|
|
1197
|
+
// at or above the progress block leaves both alone: that block was never
|
|
1198
|
+
// orphaned, and re-reading its long-pruned header would drop a valid timestamp.
|
|
1199
|
+
let rollbackCommittedProgress = (cs: t, blockNumber) =>
|
|
1200
|
+
if blockNumber !== cs.committedProgressBlockNumber {
|
|
1201
|
+
cs.committedProgressBlockNumber = blockNumber
|
|
1202
|
+
// Exact block only: the rolled-back region is about to be refetched, and
|
|
1203
|
+
// the next batch re-establishes the time either way.
|
|
1204
|
+
cs.committedProgressBlockTime =
|
|
1205
|
+
cs.blockStore
|
|
1206
|
+
->BlockStore.getTimestamp(blockNumber, ~allowSkippedSlot=false)
|
|
1207
|
+
->Null.toOption
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1148
1210
|
type progressDiff = {blockNumber: int, eventsProcessed: float}
|
|
1149
1211
|
|
|
1150
1212
|
type rolledBackTo =
|
|
@@ -1192,7 +1254,7 @@ let rollback = (cs: t, ~rolledBackTo: rolledBackTo): array<AddressRows.key> => {
|
|
|
1192
1254
|
let rolledBackAddresses = rollbackTo(newProgressBlockNumber)
|
|
1193
1255
|
cs.transactionStore->TransactionStore.rollback(newProgressBlockNumber)
|
|
1194
1256
|
cs.blockStore->BlockStore.rollback(newProgressBlockNumber)
|
|
1195
|
-
cs
|
|
1257
|
+
cs->rollbackCommittedProgress(newProgressBlockNumber)
|
|
1196
1258
|
cs.processingBlockNumber = newProgressBlockNumber
|
|
1197
1259
|
cs.numEventsProcessed = newTotalEventsProcessed
|
|
1198
1260
|
rolledBackAddresses
|
|
@@ -1202,7 +1264,7 @@ let rollback = (cs: t, ~rolledBackTo: rolledBackTo): array<AddressRows.key> => {
|
|
|
1202
1264
|
let rolledBackAddresses = rollbackTo(forkBlock)
|
|
1203
1265
|
cs.transactionStore->TransactionStore.rollback(forkBlock)
|
|
1204
1266
|
cs.blockStore->BlockStore.rollback(forkBlock)
|
|
1205
|
-
cs
|
|
1267
|
+
cs->rollbackCommittedProgress(Pervasives.min(cs.committedProgressBlockNumber, forkBlock))
|
|
1206
1268
|
cs.processingBlockNumber = Pervasives.min(cs.processingBlockNumber, forkBlock)
|
|
1207
1269
|
rolledBackAddresses
|
|
1208
1270
|
| Untouched => []
|
package/src/ChainState.res.mjs
CHANGED
|
@@ -55,9 +55,11 @@ function validateOnEventRegistrations(chainId, registrations) {
|
|
|
55
55
|
});
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
function make(chainConfig, fetchState, onEventRegistrationsOpt, addressStore, sourceManager, committedProgressBlockNumber, safeCheckpointTrackingOpt, shouldRollbackOnReorg, maxReorgDepth, numEventsProcessedOpt, timestampCaughtUpToHeadOrEndblockOpt, isProgressAtHeadOpt, transactionStoreOpt, chainDensityOpt, blockStoreOpt, reorgThresholdReadyToleranceOpt, perChainEntitiesOpt, logger) {
|
|
58
|
+
function make(chainConfig, fetchState, onEventRegistrationsOpt, addressStore, sourceManager, committedProgressBlockNumber, committedProgressBlockTimeOpt, safeCheckpointTrackingOpt, shouldRollbackOnReorg, maxReorgDepth, isInReorgThresholdOpt, numEventsProcessedOpt, timestampCaughtUpToHeadOrEndblockOpt, isProgressAtHeadOpt, transactionStoreOpt, chainDensityOpt, blockStoreOpt, reorgThresholdReadyToleranceOpt, perChainEntitiesOpt, logger) {
|
|
59
59
|
let onEventRegistrations = onEventRegistrationsOpt !== undefined ? onEventRegistrationsOpt : [];
|
|
60
|
+
let committedProgressBlockTime = committedProgressBlockTimeOpt !== undefined ? Primitive_option.valFromOption(committedProgressBlockTimeOpt) : undefined;
|
|
60
61
|
let safeCheckpointTracking = safeCheckpointTrackingOpt !== undefined ? Primitive_option.valFromOption(safeCheckpointTrackingOpt) : undefined;
|
|
62
|
+
let isInReorgThreshold = isInReorgThresholdOpt !== undefined ? isInReorgThresholdOpt : false;
|
|
61
63
|
let numEventsProcessed = numEventsProcessedOpt !== undefined ? numEventsProcessedOpt : 0;
|
|
62
64
|
let timestampCaughtUpToHeadOrEndblock = timestampCaughtUpToHeadOrEndblockOpt !== undefined ? Primitive_option.valFromOption(timestampCaughtUpToHeadOrEndblockOpt) : undefined;
|
|
63
65
|
let isProgressAtHead = isProgressAtHeadOpt !== undefined ? isProgressAtHeadOpt : false;
|
|
@@ -85,6 +87,7 @@ function make(chainConfig, fetchState, onEventRegistrationsOpt, addressStore, so
|
|
|
85
87
|
safeCheckpointTracking: safeCheckpointTracking,
|
|
86
88
|
shouldRollbackOnReorg: shouldRollbackOnReorg,
|
|
87
89
|
maxReorgDepth: maxReorgDepth,
|
|
90
|
+
isInReorgThreshold: isInReorgThreshold,
|
|
88
91
|
transactionStore: transactionStore,
|
|
89
92
|
blockStore: blockStore,
|
|
90
93
|
reorgThresholdReadyTolerance: reorgThresholdReadyTolerance,
|
|
@@ -96,7 +99,8 @@ function make(chainConfig, fetchState, onEventRegistrationsOpt, addressStore, so
|
|
|
96
99
|
reorgCount: 0,
|
|
97
100
|
reorgDetectedBlock: undefined,
|
|
98
101
|
rollbackTargetBlock: undefined,
|
|
99
|
-
progressLatencyMs: undefined
|
|
102
|
+
progressLatencyMs: undefined,
|
|
103
|
+
committedProgressBlockTime: committedProgressBlockTime
|
|
100
104
|
};
|
|
101
105
|
}
|
|
102
106
|
|
|
@@ -110,12 +114,14 @@ function makeFromDbState(chainConfig, resumedChainState, reorgCheckpoints, isInR
|
|
|
110
114
|
let startBlock = resumedChainState.startBlock;
|
|
111
115
|
let endBlock = resumedChainState.endBlock;
|
|
112
116
|
let firstEventBlockOpt = Primitive_option.some(resumedChainState.firstEventBlockNumber);
|
|
117
|
+
let committedProgressBlockTimeOpt = Primitive_option.some(resumedChainState.progressBlockTime);
|
|
113
118
|
let timestampCaughtUpToHeadOrEndblock = resumedChainState.timestampCaughtUpToHeadOrEndblock;
|
|
114
119
|
let numEventsProcessed = resumedChainState.numEventsProcessed;
|
|
115
120
|
let maxReorgDepth = resumedChainState.maxReorgDepth;
|
|
116
121
|
let knownHeightOpt = resumedChainState.sourceBlockNumber;
|
|
117
122
|
let isResumedOpt = true;
|
|
118
123
|
let firstEventBlock = firstEventBlockOpt !== undefined ? Primitive_option.valFromOption(firstEventBlockOpt) : undefined;
|
|
124
|
+
let committedProgressBlockTime = committedProgressBlockTimeOpt !== undefined ? Primitive_option.valFromOption(committedProgressBlockTimeOpt) : undefined;
|
|
119
125
|
let knownHeight = knownHeightOpt !== undefined ? knownHeightOpt : 0;
|
|
120
126
|
let isResumed = isResumedOpt !== undefined ? isResumedOpt : false;
|
|
121
127
|
let match = Stdlib_Option.getOr(registrationsByChainId[ChainId.toString(chainConfig.id)], {
|
|
@@ -159,7 +165,7 @@ function makeFromDbState(chainConfig, resumedChainState, reorgCheckpoints, isInR
|
|
|
159
165
|
}
|
|
160
166
|
let firstEventBlock$1 = fetchState.firstEventBlock;
|
|
161
167
|
let chainDensity = firstEventBlock$1 !== undefined && progressBlockNumber > firstEventBlock$1 && numEventsProcessed > 0 ? numEventsProcessed / (progressBlockNumber - firstEventBlock$1 | 0) : undefined;
|
|
162
|
-
return make(chainConfig, fetchState, onEventRegistrations, addressStore, SourceManager.make(sources, isRealtime, undefined, undefined, undefined, reducedPollingInterval, undefined, undefined), progressBlockNumber, Primitive_option.some(SafeCheckpointTracking.make(maxReorgDepth, config.shouldRollbackOnReorg, chainReorgCheckpoints)), config.shouldRollbackOnReorg, maxReorgDepth, numEventsProcessed, Primitive_option.some(timestampCaughtUpToHeadOrEndblock), undefined, Primitive_option.some(TransactionStore.make(config.ecosystem.name, !lowercaseAddresses)), Primitive_option.some(chainDensity), Primitive_option.some(blockStore), config.reorgThresholdReadyTolerance, EntityTables.perChain(config.userEntities), logger);
|
|
168
|
+
return make(chainConfig, fetchState, onEventRegistrations, addressStore, SourceManager.make(sources, isRealtime, undefined, undefined, undefined, reducedPollingInterval, undefined, undefined), progressBlockNumber, Primitive_option.some(committedProgressBlockTime), Primitive_option.some(SafeCheckpointTracking.make(maxReorgDepth, config.shouldRollbackOnReorg, chainReorgCheckpoints)), config.shouldRollbackOnReorg, maxReorgDepth, isInReorgThreshold, numEventsProcessed, Primitive_option.some(timestampCaughtUpToHeadOrEndblock), undefined, Primitive_option.some(TransactionStore.make(config.ecosystem.name, !lowercaseAddresses)), Primitive_option.some(chainDensity), Primitive_option.some(blockStore), config.reorgThresholdReadyTolerance, EntityTables.perChain(config.userEntities), logger);
|
|
163
169
|
}
|
|
164
170
|
|
|
165
171
|
function logger(cs) {
|
|
@@ -210,6 +216,10 @@ function committedProgressBlockNumber(cs) {
|
|
|
210
216
|
return cs.committedProgressBlockNumber;
|
|
211
217
|
}
|
|
212
218
|
|
|
219
|
+
function committedProgressBlockTime(cs) {
|
|
220
|
+
return cs.committedProgressBlockTime;
|
|
221
|
+
}
|
|
222
|
+
|
|
213
223
|
function numEventsProcessed(cs) {
|
|
214
224
|
return cs.numEventsProcessed;
|
|
215
225
|
}
|
|
@@ -402,13 +412,12 @@ function hasProcessedToEndblock(cs) {
|
|
|
402
412
|
function isDurablyCaughtUp(cs) {
|
|
403
413
|
let fetchState = cs.fetchState;
|
|
404
414
|
let committedProgressBlockNumber = cs.committedProgressBlockNumber;
|
|
405
|
-
let
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
return committedProgressBlockNumber >= (fetchState.knownHeight - cs.chainConfig.blockLag | 0);
|
|
415
|
+
let atEndBlock = Stdlib_Option.mapOr(fetchState.endBlock, false, endBlock => committedProgressBlockNumber >= endBlock);
|
|
416
|
+
let atHead = fetchState.knownHeight > 0 && committedProgressBlockNumber >= Primitive_int.max(0, fetchState.knownHeight - cs.chainConfig.blockLag | 0);
|
|
417
|
+
if (atEndBlock) {
|
|
418
|
+
return true;
|
|
410
419
|
} else {
|
|
411
|
-
return
|
|
420
|
+
return atHead;
|
|
412
421
|
}
|
|
413
422
|
}
|
|
414
423
|
|
|
@@ -641,9 +650,22 @@ function setEndBlockToFirstEvent(cs, blockNumber) {
|
|
|
641
650
|
}
|
|
642
651
|
|
|
643
652
|
function enterReorgThreshold(cs) {
|
|
653
|
+
cs.isInReorgThreshold = true;
|
|
644
654
|
cs.fetchState = FetchState.updateInternal(cs.fetchState, undefined, undefined, undefined, cs.chainConfig.blockLag, undefined);
|
|
645
655
|
}
|
|
646
656
|
|
|
657
|
+
function isInReorgThreshold(cs) {
|
|
658
|
+
return cs.isInReorgThreshold;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
function shouldSaveHistory(cs) {
|
|
662
|
+
if (cs.shouldRollbackOnReorg && cs.maxReorgDepth > 0) {
|
|
663
|
+
return cs.isInReorgThreshold;
|
|
664
|
+
} else {
|
|
665
|
+
return false;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
647
669
|
function toChainMetadata(cs) {
|
|
648
670
|
return {
|
|
649
671
|
first_event_block: Stdlib_Null.fromOption(cs.fetchState.firstEventBlock),
|
|
@@ -675,6 +697,7 @@ function toMetrics(cs) {
|
|
|
675
697
|
sourceBlockNumber: cs.fetchState.knownHeight,
|
|
676
698
|
progressBlockNumber: cs.committedProgressBlockNumber,
|
|
677
699
|
progressLatencyMs: cs.progressLatencyMs,
|
|
700
|
+
progressBlockTime: cs.committedProgressBlockTime,
|
|
678
701
|
concurrency: SourceManager.inFlightCount(cs.sourceManager),
|
|
679
702
|
partitionsCount: FetchState.partitionsCount(cs.fetchState),
|
|
680
703
|
bufferSize: FetchState.bufferSize(cs.fetchState),
|
|
@@ -689,11 +712,13 @@ function toMetrics(cs) {
|
|
|
689
712
|
blockRangeFetchedBlocks: cs.blockRangeFetchedBlocks,
|
|
690
713
|
reorgCount: cs.reorgCount,
|
|
691
714
|
reorgDetectedBlock: cs.reorgDetectedBlock,
|
|
692
|
-
rollbackTargetBlock: cs.rollbackTargetBlock
|
|
715
|
+
rollbackTargetBlock: cs.rollbackTargetBlock,
|
|
716
|
+
rateLimitTimeMs: SourceManager.getRateLimitTimeMs(cs.sourceManager),
|
|
717
|
+
rateLimitResetInMs: SourceManager.getRateLimitResetInMs(cs.sourceManager)
|
|
693
718
|
};
|
|
694
719
|
}
|
|
695
720
|
|
|
696
|
-
function toChainBeforeBatch(cs) {
|
|
721
|
+
function toChainBeforeBatch(cs, isRealtime) {
|
|
697
722
|
let match = cs.blockStore.getHashes(Primitive_int.max(cs.fetchState.knownHeight - cs.maxReorgDepth | 0, 0), cs.fetchState.knownHeight + 1 | 0);
|
|
698
723
|
let hashes = match.hashes;
|
|
699
724
|
let blockNumbers = match.blockNumbers;
|
|
@@ -707,6 +732,7 @@ function toChainBeforeBatch(cs) {
|
|
|
707
732
|
blockNumbers: blockNumbers,
|
|
708
733
|
hashByBlockNumber: hashByBlockNumber
|
|
709
734
|
},
|
|
735
|
+
blockTimeAt: blockNumber => Primitive_option.fromNull(cs.blockStore.getTimestamp(blockNumber, isRealtime)),
|
|
710
736
|
shouldRollbackOnReorg: cs.shouldRollbackOnReorg,
|
|
711
737
|
progressBlockNumber: cs.committedProgressBlockNumber,
|
|
712
738
|
sourceBlockNumber: cs.fetchState.knownHeight,
|
|
@@ -786,6 +812,7 @@ function applyBatchProgress(cs, batch, blockTimestampName) {
|
|
|
786
812
|
}
|
|
787
813
|
}
|
|
788
814
|
cs.committedProgressBlockNumber = chainAfterBatch.progressBlockNumber;
|
|
815
|
+
cs.committedProgressBlockTime = chainAfterBatch.progressBlockTime;
|
|
789
816
|
cs.processingBlockNumber = Primitive_int.max(cs.processingBlockNumber, chainAfterBatch.progressBlockNumber);
|
|
790
817
|
cs.numEventsProcessed = chainAfterBatch.totalEventsProcessed;
|
|
791
818
|
cs.transactionStore.prune(chainAfterBatch.progressBlockNumber);
|
|
@@ -805,6 +832,14 @@ function markReady(cs, readyAt) {
|
|
|
805
832
|
}
|
|
806
833
|
}
|
|
807
834
|
|
|
835
|
+
function rollbackCommittedProgress(cs, blockNumber) {
|
|
836
|
+
if (blockNumber !== cs.committedProgressBlockNumber) {
|
|
837
|
+
cs.committedProgressBlockNumber = blockNumber;
|
|
838
|
+
cs.committedProgressBlockTime = Primitive_option.fromNull(cs.blockStore.getTimestamp(blockNumber, false));
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
808
843
|
function rollback(cs, rolledBackTo) {
|
|
809
844
|
let rollbackTo = targetBlockNumber => {
|
|
810
845
|
let match = FetchState.rollback(cs.fetchState, cs.addressStore, targetBlockNumber);
|
|
@@ -829,7 +864,7 @@ function rollback(cs, rolledBackTo) {
|
|
|
829
864
|
let rolledBackAddresses = rollbackTo(newProgressBlockNumber);
|
|
830
865
|
cs.transactionStore.rollback(newProgressBlockNumber);
|
|
831
866
|
cs.blockStore.rollback(newProgressBlockNumber);
|
|
832
|
-
cs
|
|
867
|
+
rollbackCommittedProgress(cs, newProgressBlockNumber);
|
|
833
868
|
cs.processingBlockNumber = newProgressBlockNumber;
|
|
834
869
|
cs.numEventsProcessed = newTotalEventsProcessed;
|
|
835
870
|
return rolledBackAddresses;
|
|
@@ -838,7 +873,7 @@ function rollback(cs, rolledBackTo) {
|
|
|
838
873
|
let rolledBackAddresses$1 = rollbackTo(forkBlock);
|
|
839
874
|
cs.transactionStore.rollback(forkBlock);
|
|
840
875
|
cs.blockStore.rollback(forkBlock);
|
|
841
|
-
cs
|
|
876
|
+
rollbackCommittedProgress(cs, Primitive_int.min(cs.committedProgressBlockNumber, forkBlock));
|
|
842
877
|
cs.processingBlockNumber = Primitive_int.min(cs.processingBlockNumber, forkBlock);
|
|
843
878
|
return rolledBackAddresses$1;
|
|
844
879
|
}
|
|
@@ -860,6 +895,7 @@ export {
|
|
|
860
895
|
safeCheckpointTracking,
|
|
861
896
|
isProgressAtHead,
|
|
862
897
|
committedProgressBlockNumber,
|
|
898
|
+
committedProgressBlockTime,
|
|
863
899
|
numEventsProcessed,
|
|
864
900
|
pendingBudget,
|
|
865
901
|
startFetchingQueries,
|
|
@@ -902,6 +938,8 @@ export {
|
|
|
902
938
|
updateKnownHeight,
|
|
903
939
|
setEndBlockToFirstEvent,
|
|
904
940
|
enterReorgThreshold,
|
|
941
|
+
isInReorgThreshold,
|
|
942
|
+
shouldSaveHistory,
|
|
905
943
|
advanceAfterBatch,
|
|
906
944
|
applyBatchProgress,
|
|
907
945
|
markReady,
|
package/src/ChainState.resi
CHANGED
|
@@ -23,9 +23,11 @@ let make: (
|
|
|
23
23
|
~addressStore: AddressStore.t,
|
|
24
24
|
~sourceManager: SourceManager.t,
|
|
25
25
|
~committedProgressBlockNumber: int,
|
|
26
|
+
~committedProgressBlockTime: option<int>=?,
|
|
26
27
|
~safeCheckpointTracking: option<SafeCheckpointTracking.t>=?,
|
|
27
28
|
~shouldRollbackOnReorg: bool,
|
|
28
29
|
~maxReorgDepth: int,
|
|
30
|
+
~isInReorgThreshold: bool=?,
|
|
29
31
|
~numEventsProcessed: float=?,
|
|
30
32
|
~timestampCaughtUpToHeadOrEndblock: option<Date.t>=?,
|
|
31
33
|
~isProgressAtHead: bool=?,
|
|
@@ -67,6 +69,7 @@ let getLatestValidScannedBlock: (
|
|
|
67
69
|
let safeCheckpointTracking: t => option<SafeCheckpointTracking.t>
|
|
68
70
|
let isProgressAtHead: t => bool
|
|
69
71
|
let committedProgressBlockNumber: t => int
|
|
72
|
+
let committedProgressBlockTime: t => option<int>
|
|
70
73
|
let numEventsProcessed: t => float
|
|
71
74
|
let pendingBudget: t => float
|
|
72
75
|
let startFetchingQueries: (t, ~queries: array<FetchState.query>) => unit
|
|
@@ -116,7 +119,7 @@ let dispatch: (
|
|
|
116
119
|
// Views.
|
|
117
120
|
let toMetrics: t => Metrics.chainMetrics
|
|
118
121
|
let toChainMetadata: t => InternalTable.Chains.metaFields
|
|
119
|
-
let toChainBeforeBatch: t => Batch.chainBeforeBatch
|
|
122
|
+
let toChainBeforeBatch: (t, ~isRealtime: bool) => Batch.chainBeforeBatch
|
|
120
123
|
let isReadyToEnterReorgThresholdAfterBatch: (t, ~batch: Batch.t) => bool
|
|
121
124
|
|
|
122
125
|
// Derived (pure).
|
|
@@ -154,6 +157,8 @@ let resetPendingQueries: t => unit
|
|
|
154
157
|
let updateKnownHeight: (t, ~knownHeight: int) => unit
|
|
155
158
|
let setEndBlockToFirstEvent: (t, ~blockNumber: int) => unit
|
|
156
159
|
let enterReorgThreshold: t => unit
|
|
160
|
+
let isInReorgThreshold: t => bool
|
|
161
|
+
let shouldSaveHistory: t => bool
|
|
157
162
|
let advanceAfterBatch: (t, ~batch: Batch.t, ~enteringReorgThreshold: bool) => unit
|
|
158
163
|
let applyBatchProgress: (t, ~batch: Batch.t, ~blockTimestampName: string) => unit
|
|
159
164
|
let markReady: (t, ~readyAt: Date.t) => unit
|