envio 3.11.0 → 3.12.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/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/ChainState.res +72 -19
- package/src/ChainState.res.mjs +50 -12
- package/src/ChainState.resi +5 -0
- package/src/Config.res +58 -37
- package/src/Config.res.mjs +63 -53
- package/src/CrossChainState.res +49 -52
- 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 +14 -0
- package/src/FetchState.res.mjs +14 -0
- 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/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 +11 -4
- package/src/LoadLayer.res.mjs +8 -5
- 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 +194 -118
- package/src/PgStorage.res.mjs +132 -72
- 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 +5 -5
- package/src/TestIndexer.res.mjs +7 -6
- package/src/UserContext.res +7 -4
- package/src/UserContext.res.mjs +5 -2
- package/src/Utils.res +9 -0
- package/src/Utils.res.mjs +9 -0
- 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/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/sources/BlockStore.res +3 -0
- package/src/sources/EvmHyperSyncSource.res +3 -1
- package/src/sources/EvmHyperSyncSource.res.mjs +2 -1
- package/src/sources/HyperSync.res +2 -0
- package/src/sources/HyperSync.res.mjs +1 -0
- package/src/sources/HyperSync.resi +1 -0
- package/src/sources/HyperSyncClient.res +3 -0
- package/src/sources/RequestStat.res +8 -1
- package/src/sources/Source.res +4 -3
- package/src/sources/SourceManager.res +30 -10
- package/src/sources/SourceManager.res.mjs +24 -7
- package/src/sources/SourceManager.resi +2 -0
- 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/IndexerState.res
CHANGED
|
@@ -58,12 +58,12 @@ type t = {
|
|
|
58
58
|
mutable entities: EntityTables.t,
|
|
59
59
|
effectState: EffectState.t,
|
|
60
60
|
mutable rollback: option<Persistence.rollback>,
|
|
61
|
-
// Last checkpoint persisted to the db.
|
|
62
|
-
mutable
|
|
63
|
-
// Processing frontier; runs ahead of
|
|
64
|
-
mutable
|
|
61
|
+
// Last checkpoint each chain has persisted to the db.
|
|
62
|
+
mutable committedFrontier: Frontier.t,
|
|
63
|
+
// Processing frontier; runs ahead of committedFrontier while writes lag.
|
|
64
|
+
mutable processedFrontier: Frontier.t,
|
|
65
65
|
// Processed but unwritten. The cycle drains them, splitting each write at a
|
|
66
|
-
// change in
|
|
66
|
+
// change in the history policy so it never mixes history-saving modes.
|
|
67
67
|
mutable processedBatches: array<Batch.t>,
|
|
68
68
|
// Count of processed batches; version-independent progress counter.
|
|
69
69
|
mutable processedBatchesCount: int,
|
|
@@ -139,10 +139,9 @@ let make = (
|
|
|
139
139
|
~config: Config.t,
|
|
140
140
|
~persistence: Persistence.t,
|
|
141
141
|
~chainStates: dict<ChainState.t>,
|
|
142
|
-
~isInReorgThreshold: bool,
|
|
143
142
|
~isRealtime: bool,
|
|
144
143
|
~targetBufferSize=CrossChainState.calculateTargetBufferSize(),
|
|
145
|
-
~
|
|
144
|
+
~committedFrontier=Frontier.empty(),
|
|
146
145
|
~isDevelopmentMode=false,
|
|
147
146
|
~shouldUseTui=false,
|
|
148
147
|
~exitAfterFirstEventBlock=false,
|
|
@@ -169,8 +168,8 @@ let make = (
|
|
|
169
168
|
entities: EntityTables.make(persistence.allEntities->EntityTables.crossChain),
|
|
170
169
|
effectState: EffectState.make(),
|
|
171
170
|
rollback: None,
|
|
172
|
-
|
|
173
|
-
|
|
171
|
+
committedFrontier,
|
|
172
|
+
processedFrontier: committedFrontier->Frontier.copy,
|
|
174
173
|
processedBatches: [],
|
|
175
174
|
processedBatchesCount: 0,
|
|
176
175
|
writeFiber: None,
|
|
@@ -181,12 +180,7 @@ let make = (
|
|
|
181
180
|
chainMetaDirty: false,
|
|
182
181
|
chainMetaThrottler,
|
|
183
182
|
isProcessing: false,
|
|
184
|
-
crossChainState: CrossChainState.make(
|
|
185
|
-
~chainStates,
|
|
186
|
-
~isInReorgThreshold,
|
|
187
|
-
~isRealtime,
|
|
188
|
-
~targetBufferSize,
|
|
189
|
-
),
|
|
183
|
+
crossChainState: CrossChainState.make(~chainStates, ~isRealtime, ~targetBufferSize),
|
|
190
184
|
indexerStartTime: Date.make(),
|
|
191
185
|
indexerStartTimeRef: Performance.now(),
|
|
192
186
|
rollbackState: NoRollback,
|
|
@@ -278,10 +272,9 @@ let makeFromDbState = (
|
|
|
278
272
|
~config,
|
|
279
273
|
~persistence,
|
|
280
274
|
~chainStates,
|
|
281
|
-
~isInReorgThreshold,
|
|
282
275
|
~isRealtime,
|
|
283
276
|
~targetBufferSize,
|
|
284
|
-
~
|
|
277
|
+
~committedFrontier=initialState.checkpointFrontier,
|
|
285
278
|
~isDevelopmentMode,
|
|
286
279
|
~shouldUseTui,
|
|
287
280
|
~exitAfterFirstEventBlock,
|
|
@@ -357,19 +350,15 @@ let getChainState = (state: t, ~chainId: ChainId.t): ChainState.t =>
|
|
|
357
350
|
|
|
358
351
|
let getSafeCheckpointIdByChain = (state: t) =>
|
|
359
352
|
state.crossChainState->CrossChainState.getSafeCheckpointIdByChain(
|
|
360
|
-
~
|
|
353
|
+
~sequence=state.config.checkpointSequence,
|
|
354
|
+
~committedFrontier=state.committedFrontier,
|
|
361
355
|
)
|
|
362
356
|
|
|
363
|
-
let createBatch = (
|
|
364
|
-
state: t,
|
|
365
|
-
~processedCheckpointId,
|
|
366
|
-
~batchSizeTarget: int,
|
|
367
|
-
~isRollback: bool,
|
|
368
|
-
): Batch.t =>
|
|
357
|
+
let createBatch = (state: t, ~batchSizeTarget: int): Batch.t =>
|
|
369
358
|
state.crossChainState->CrossChainState.createBatch(
|
|
370
|
-
~
|
|
359
|
+
~config=state.config,
|
|
360
|
+
~frontier=state.processedFrontier,
|
|
371
361
|
~batchSizeTarget,
|
|
372
|
-
~isRollback,
|
|
373
362
|
)
|
|
374
363
|
|
|
375
364
|
let enterReorgThreshold = (state: t) => state.crossChainState->CrossChainState.enterReorgThreshold
|
|
@@ -483,8 +472,13 @@ let eachEntityTable = (
|
|
|
483
472
|
)
|
|
484
473
|
}
|
|
485
474
|
let effectState = (state: t) => state.effectState
|
|
486
|
-
let
|
|
487
|
-
let
|
|
475
|
+
let committedFrontier = (state: t) => state.committedFrontier
|
|
476
|
+
let processedFrontier = (state: t) => state.processedFrontier
|
|
477
|
+
|
|
478
|
+
// The committed id a scope's in-memory rows compare against.
|
|
479
|
+
let committedCheckpointIdFor = (state: t, ~scope) =>
|
|
480
|
+
state.config.checkpointSequence->CheckpointSequence.forScope(state.committedFrontier, ~scope)
|
|
481
|
+
|
|
488
482
|
let processedBatches = (state: t) => state.processedBatches
|
|
489
483
|
let processedBatchesCount = (state: t) => state.processedBatchesCount
|
|
490
484
|
let writeFiber = (state: t) => state.writeFiber
|
|
@@ -495,11 +489,13 @@ let chainMetaThrottler = (state: t) => state.chainMetaThrottler
|
|
|
495
489
|
let crossChainState = (state: t) => state.crossChainState
|
|
496
490
|
let chainStates = (state: t) => state.crossChainState->CrossChainState.chainStates
|
|
497
491
|
let isInReorgThreshold = (state: t) => state.crossChainState->CrossChainState.isInReorgThreshold
|
|
492
|
+
let shouldSaveHistory = (state: t) => state.crossChainState->CrossChainState.shouldSaveHistory
|
|
498
493
|
let isRealtime = (state: t) => state.crossChainState->CrossChainState.isRealtime
|
|
499
494
|
|
|
500
495
|
// The indexer runs Backfilling → FinalizingIndexes → Ready. This is true only
|
|
501
|
-
// in the middle phase:
|
|
502
|
-
// indexes and `ready_at` haven't been committed
|
|
496
|
+
// in the middle phase: the chains this process drives have caught up, but the
|
|
497
|
+
// indexes the schema promises them and their `ready_at` haven't been committed
|
|
498
|
+
// yet.
|
|
503
499
|
let isFinalizingIndexes = (state: t) =>
|
|
504
500
|
state.crossChainState->CrossChainState.isCaughtUp &&
|
|
505
501
|
!(state.crossChainState->CrossChainState.isRealtime)
|
|
@@ -538,6 +534,8 @@ let toMetrics = (state: t): Metrics.t => {
|
|
|
538
534
|
method: s.method,
|
|
539
535
|
count: s.count,
|
|
540
536
|
seconds: s.seconds,
|
|
537
|
+
responseBlocks: s.responseBlocks,
|
|
538
|
+
emptyResponseCount: s.emptyResponseCount,
|
|
541
539
|
})
|
|
542
540
|
)
|
|
543
541
|
sourceManager
|
|
@@ -758,18 +756,18 @@ let recordRollbackSuccess = (state: t, ~timeSeconds, ~rollbackedProcessedEvents)
|
|
|
758
756
|
// Queue a processed batch for writing and advance the processing frontier.
|
|
759
757
|
let queueProcessedBatch = (state: t, ~batch: Batch.t) => {
|
|
760
758
|
state.processedBatches->Array.push(batch)->ignore
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
759
|
+
state.processedFrontier = Frontier.mergeMax(
|
|
760
|
+
state.processedFrontier,
|
|
761
|
+
batch->Batch.checkpointFrontier,
|
|
762
|
+
)
|
|
765
763
|
}
|
|
766
764
|
|
|
767
|
-
// Take the leading run of queued batches sharing
|
|
765
|
+
// Take the leading run of queued batches sharing a history policy as one merged
|
|
768
766
|
// batch, leaving the rest queued for the next write. Caller guarantees the queue
|
|
769
767
|
// is non-empty.
|
|
770
768
|
let drainBatchRun = (state: t): Batch.t => {
|
|
771
769
|
let all = state.processedBatches
|
|
772
|
-
let
|
|
770
|
+
let history = (all->Array.getUnsafe(0)).history
|
|
773
771
|
|
|
774
772
|
let rest = []
|
|
775
773
|
let progressedChainsById = Dict.make()
|
|
@@ -779,11 +777,12 @@ let drainBatchRun = (state: t): Batch.t => {
|
|
|
779
777
|
let checkpointChainIds = []
|
|
780
778
|
let checkpointBlockNumbers = []
|
|
781
779
|
let checkpointBlockHashes = []
|
|
780
|
+
let checkpointItemsCount = []
|
|
782
781
|
let checkpointEventsProcessed = []
|
|
783
782
|
let registeredAddresses = []
|
|
784
783
|
all->Array.forEach(batch => {
|
|
785
784
|
// Once one batch lands in rest, all later ones follow it, preserving order.
|
|
786
|
-
if rest->Utils.Array.isEmpty && batch.
|
|
785
|
+
if rest->Utils.Array.isEmpty && batch.history == history {
|
|
787
786
|
batch.progressedChainsById->Utils.Dict.forEachWithKey((chainAfterBatch, key) =>
|
|
788
787
|
progressedChainsById->Dict.set(key, chainAfterBatch)
|
|
789
788
|
)
|
|
@@ -793,6 +792,7 @@ let drainBatchRun = (state: t): Batch.t => {
|
|
|
793
792
|
checkpointChainIds->Array.pushMany(batch.checkpointChainIds)
|
|
794
793
|
checkpointBlockNumbers->Array.pushMany(batch.checkpointBlockNumbers)
|
|
795
794
|
checkpointBlockHashes->Array.pushMany(batch.checkpointBlockHashes)
|
|
795
|
+
checkpointItemsCount->Array.pushMany(batch.checkpointItemsCount)
|
|
796
796
|
checkpointEventsProcessed->Array.pushMany(batch.checkpointEventsProcessed)
|
|
797
797
|
registeredAddresses->Array.pushMany(batch.registeredAddresses)
|
|
798
798
|
} else {
|
|
@@ -805,11 +805,12 @@ let drainBatchRun = (state: t): Batch.t => {
|
|
|
805
805
|
totalBatchSize: totalBatchSize.contents,
|
|
806
806
|
items,
|
|
807
807
|
progressedChainsById,
|
|
808
|
-
|
|
808
|
+
history,
|
|
809
809
|
checkpointIds,
|
|
810
810
|
checkpointChainIds,
|
|
811
811
|
checkpointBlockNumbers,
|
|
812
812
|
checkpointBlockHashes,
|
|
813
|
+
checkpointItemsCount,
|
|
813
814
|
checkpointEventsProcessed,
|
|
814
815
|
registeredAddresses,
|
|
815
816
|
}
|
|
@@ -829,18 +830,30 @@ let takeRollback = (state: t): option<Persistence.rollback> => {
|
|
|
829
830
|
// committed. A failed write keeps them, and re-inserting a row the database
|
|
830
831
|
// already has is a no-op. Rows staged while the write was in flight belong to
|
|
831
832
|
// later checkpoints — ids only ever grow — so this can't drop one unwritten.
|
|
832
|
-
let markCommitted = (state: t, ~
|
|
833
|
-
state.
|
|
833
|
+
let markCommitted = (state: t, ~writtenFrontier) => {
|
|
834
|
+
state.committedFrontier = Frontier.mergeMax(state.committedFrontier, writtenFrontier)
|
|
834
835
|
}
|
|
835
836
|
|
|
836
|
-
// Reset the in-memory tables and arm the rollback diff that the next write
|
|
837
|
+
// Reset the in-memory tables and arm the rollback diff that the next write
|
|
838
|
+
// commits. The diff ids start from the committed frontier — a rollback that
|
|
839
|
+
// supersedes an unwritten one takes over its ids along with its rows. A sibling
|
|
840
|
+
// the rollback leaves alone gets no diff row: burning an id on it would leave a
|
|
841
|
+
// hole in its sequence.
|
|
837
842
|
let beginRollbackDiff = (
|
|
838
843
|
state: t,
|
|
839
|
-
~
|
|
840
|
-
~floors,
|
|
844
|
+
~floors: RollbackFloors.t,
|
|
841
845
|
~progressedChains: array<InternalTable.Chains.progressedChain>,
|
|
842
846
|
~rolledBackAddresses,
|
|
843
|
-
) => {
|
|
847
|
+
): Frontier.t => {
|
|
848
|
+
let diffFrontier = {
|
|
849
|
+
let cursor =
|
|
850
|
+
state.config.checkpointSequence->CheckpointSequence.cursor(~frontier=state.committedFrontier)
|
|
851
|
+
floors.checkpointBounds.byChain
|
|
852
|
+
->Frontier.chainIds
|
|
853
|
+
->Array.map(chainId => (chainId, cursor->CheckpointSequence.next(~chainId)))
|
|
854
|
+
->Frontier.fromEntries
|
|
855
|
+
}
|
|
856
|
+
state.processedFrontier = Frontier.mergeMax(state.processedFrontier, diffFrontier)
|
|
844
857
|
let perChainEntities = state.allEntities->EntityTables.perChain
|
|
845
858
|
state.entities = EntityTables.make(state.allEntities->EntityTables.crossChain)
|
|
846
859
|
state->chainStates->Utils.Dict.forEach(cs => cs->ChainState.resetEntities(~perChainEntities))
|
|
@@ -867,11 +880,19 @@ let beginRollbackDiff = (
|
|
|
867
880
|
| None => progressedChains
|
|
868
881
|
}
|
|
869
882
|
state.rollback = Some({
|
|
870
|
-
|
|
883
|
+
diffFrontier,
|
|
884
|
+
diffCheckpoints: diffFrontier
|
|
885
|
+
->Frontier.entries
|
|
886
|
+
->Array.map(((chainId, checkpointId)): InternalTable.Checkpoints.diffCheckpoint => {
|
|
887
|
+
chainId,
|
|
888
|
+
checkpointId,
|
|
889
|
+
blockNumber: state->getChainState(~chainId)->ChainState.committedProgressBlockNumber,
|
|
890
|
+
}),
|
|
871
891
|
floors,
|
|
872
892
|
progressedChains,
|
|
873
893
|
rolledBackAddresses,
|
|
874
894
|
})
|
|
895
|
+
diffFrontier
|
|
875
896
|
}
|
|
876
897
|
|
|
877
898
|
// Stop the write loop and surface the failure; the error itself goes to onError.
|
package/src/IndexerState.res.mjs
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as Env from "./Env.res.mjs";
|
|
4
|
+
import * as Batch from "./Batch.res.mjs";
|
|
4
5
|
import * as Utils from "./Utils.res.mjs";
|
|
5
6
|
import * as Config from "./Config.res.mjs";
|
|
6
7
|
import * as ChainId from "./ChainId.res.mjs";
|
|
7
8
|
import * as Logging from "./Logging.res.mjs";
|
|
8
9
|
import * as ChainMap from "./ChainMap.res.mjs";
|
|
9
|
-
import * as
|
|
10
|
+
import * as Frontier from "./db/Frontier.res.mjs";
|
|
10
11
|
import * as Throttler from "./Throttler.res.mjs";
|
|
11
12
|
import * as ChainState from "./ChainState.res.mjs";
|
|
12
13
|
import * as EffectState from "./EffectState.res.mjs";
|
|
@@ -20,11 +21,12 @@ import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
|
20
21
|
import * as CrossChainState from "./CrossChainState.res.mjs";
|
|
21
22
|
import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
|
|
22
23
|
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
24
|
+
import * as CheckpointSequence from "./db/CheckpointSequence.res.mjs";
|
|
23
25
|
import * as SimulateDeadInputTracker from "./SimulateDeadInputTracker.res.mjs";
|
|
24
26
|
|
|
25
|
-
function make(config, persistence, chainStates,
|
|
27
|
+
function make(config, persistence, chainStates, isRealtime, targetBufferSizeOpt, committedFrontierOpt, isDevelopmentModeOpt, shouldUseTuiOpt, exitAfterFirstEventBlockOpt, onError, onExit) {
|
|
26
28
|
let targetBufferSize = targetBufferSizeOpt !== undefined ? targetBufferSizeOpt : CrossChainState.calculateTargetBufferSize();
|
|
27
|
-
let
|
|
29
|
+
let committedFrontier = committedFrontierOpt !== undefined ? committedFrontierOpt : Frontier.empty();
|
|
28
30
|
let isDevelopmentMode = isDevelopmentModeOpt !== undefined ? isDevelopmentModeOpt : false;
|
|
29
31
|
let shouldUseTui = shouldUseTuiOpt !== undefined ? shouldUseTuiOpt : false;
|
|
30
32
|
let exitAfterFirstEventBlock = exitAfterFirstEventBlockOpt !== undefined ? exitAfterFirstEventBlockOpt : false;
|
|
@@ -40,8 +42,8 @@ function make(config, persistence, chainStates, isInReorgThreshold, isRealtime,
|
|
|
40
42
|
entities: EntityTables.make(EntityTables.crossChain(persistence.allEntities)),
|
|
41
43
|
effectState: EffectState.make(),
|
|
42
44
|
rollback: undefined,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
+
committedFrontier: committedFrontier,
|
|
46
|
+
processedFrontier: Frontier.copy(committedFrontier),
|
|
45
47
|
processedBatches: [],
|
|
46
48
|
processedBatchesCount: 0,
|
|
47
49
|
writeFiber: undefined,
|
|
@@ -52,7 +54,7 @@ function make(config, persistence, chainStates, isInReorgThreshold, isRealtime,
|
|
|
52
54
|
chainMetaDirty: false,
|
|
53
55
|
chainMetaThrottler: chainMetaThrottler,
|
|
54
56
|
isProcessing: false,
|
|
55
|
-
crossChainState: CrossChainState.make(chainStates,
|
|
57
|
+
crossChainState: CrossChainState.make(chainStates, isRealtime, targetBufferSize),
|
|
56
58
|
rollbackState: "NoRollback",
|
|
57
59
|
indexerStartTime: new Date(),
|
|
58
60
|
indexerStartTimeRef: Performance.now(),
|
|
@@ -102,7 +104,7 @@ function makeFromDbState(config, persistence, initialState, registrationsByChain
|
|
|
102
104
|
let chainConfig = ChainMap.get(config.chainMap, chainId);
|
|
103
105
|
chainStates[resumedChainState.id] = ChainState.makeFromDbState(chainConfig, resumedChainState, initialState.reorgCheckpoints, isInReorgThreshold, isRealtime, config, initialState.contractMapping, registrationsByChainId, reducedPollingInterval);
|
|
104
106
|
});
|
|
105
|
-
let state = make(config, persistence, chainStates,
|
|
107
|
+
let state = make(config, persistence, chainStates, isRealtime, targetBufferSize, initialState.checkpointFrontier, isDevelopmentMode, shouldUseTui, exitAfterFirstEventBlock, onError, onExit);
|
|
106
108
|
CrossChainState.markCaughtUpOnResume(state.crossChainState);
|
|
107
109
|
Utils.Dict.forEach(initialState.cache, param => {
|
|
108
110
|
let count = param.count;
|
|
@@ -160,11 +162,11 @@ function getChainState(state, chainId) {
|
|
|
160
162
|
}
|
|
161
163
|
|
|
162
164
|
function getSafeCheckpointIdByChain(state) {
|
|
163
|
-
return CrossChainState.getSafeCheckpointIdByChain(state.crossChainState, state.
|
|
165
|
+
return CrossChainState.getSafeCheckpointIdByChain(state.crossChainState, state.config.checkpointSequence, state.committedFrontier);
|
|
164
166
|
}
|
|
165
167
|
|
|
166
|
-
function createBatch(state,
|
|
167
|
-
return CrossChainState.createBatch(state.crossChainState,
|
|
168
|
+
function createBatch(state, batchSizeTarget) {
|
|
169
|
+
return CrossChainState.createBatch(state.crossChainState, state.config, state.processedFrontier, batchSizeTarget);
|
|
168
170
|
}
|
|
169
171
|
|
|
170
172
|
function enterReorgThreshold(state) {
|
|
@@ -280,12 +282,16 @@ function effectState(state) {
|
|
|
280
282
|
return state.effectState;
|
|
281
283
|
}
|
|
282
284
|
|
|
283
|
-
function
|
|
284
|
-
return state.
|
|
285
|
+
function committedFrontier(state) {
|
|
286
|
+
return state.committedFrontier;
|
|
285
287
|
}
|
|
286
288
|
|
|
287
|
-
function
|
|
288
|
-
return state.
|
|
289
|
+
function processedFrontier(state) {
|
|
290
|
+
return state.processedFrontier;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function committedCheckpointIdFor(state, scope) {
|
|
294
|
+
return CheckpointSequence.forScope(state.config.checkpointSequence, state.committedFrontier, scope);
|
|
289
295
|
}
|
|
290
296
|
|
|
291
297
|
function processedBatches(state) {
|
|
@@ -328,6 +334,10 @@ function isInReorgThreshold(state) {
|
|
|
328
334
|
return CrossChainState.isInReorgThreshold(state.crossChainState);
|
|
329
335
|
}
|
|
330
336
|
|
|
337
|
+
function shouldSaveHistory(state) {
|
|
338
|
+
return CrossChainState.shouldSaveHistory(state.crossChainState);
|
|
339
|
+
}
|
|
340
|
+
|
|
331
341
|
function isRealtime(state) {
|
|
332
342
|
return CrossChainState.isRealtime(state.crossChainState);
|
|
333
343
|
}
|
|
@@ -401,7 +411,9 @@ function toMetrics(state) {
|
|
|
401
411
|
chainId: s.chainId,
|
|
402
412
|
method: s.method,
|
|
403
413
|
count: s.count,
|
|
404
|
-
seconds: s.seconds
|
|
414
|
+
seconds: s.seconds,
|
|
415
|
+
responseBlocks: s.responseBlocks,
|
|
416
|
+
emptyResponseCount: s.emptyResponseCount
|
|
405
417
|
});
|
|
406
418
|
});
|
|
407
419
|
SourceManager.getSourceHeightSamples(sourceManager).forEach(s => {
|
|
@@ -614,16 +626,12 @@ function recordRollbackSuccess(state, timeSeconds, rollbackedProcessedEvents) {
|
|
|
614
626
|
|
|
615
627
|
function queueProcessedBatch(state, batch) {
|
|
616
628
|
state.processedBatches.push(batch);
|
|
617
|
-
|
|
618
|
-
if (checkpointId !== undefined) {
|
|
619
|
-
state.processedCheckpointId = checkpointId;
|
|
620
|
-
return;
|
|
621
|
-
}
|
|
629
|
+
state.processedFrontier = Frontier.mergeMax(state.processedFrontier, Batch.checkpointFrontier(batch));
|
|
622
630
|
}
|
|
623
631
|
|
|
624
632
|
function drainBatchRun(state) {
|
|
625
633
|
let all = state.processedBatches;
|
|
626
|
-
let
|
|
634
|
+
let history = all[0].history;
|
|
627
635
|
let rest = [];
|
|
628
636
|
let progressedChainsById = {};
|
|
629
637
|
let totalBatchSize = {
|
|
@@ -634,10 +642,11 @@ function drainBatchRun(state) {
|
|
|
634
642
|
let checkpointChainIds = [];
|
|
635
643
|
let checkpointBlockNumbers = [];
|
|
636
644
|
let checkpointBlockHashes = [];
|
|
645
|
+
let checkpointItemsCount = [];
|
|
637
646
|
let checkpointEventsProcessed = [];
|
|
638
647
|
let registeredAddresses = [];
|
|
639
648
|
all.forEach(batch => {
|
|
640
|
-
if (Utils.$$Array.isEmpty(rest) && batch.
|
|
649
|
+
if (Utils.$$Array.isEmpty(rest) && Primitive_object.equal(batch.history, history)) {
|
|
641
650
|
Utils.Dict.forEachWithKey(batch.progressedChainsById, (chainAfterBatch, key) => {
|
|
642
651
|
progressedChainsById[key] = chainAfterBatch;
|
|
643
652
|
});
|
|
@@ -647,6 +656,7 @@ function drainBatchRun(state) {
|
|
|
647
656
|
checkpointChainIds.push(...batch.checkpointChainIds);
|
|
648
657
|
checkpointBlockNumbers.push(...batch.checkpointBlockNumbers);
|
|
649
658
|
checkpointBlockHashes.push(...batch.checkpointBlockHashes);
|
|
659
|
+
checkpointItemsCount.push(...batch.checkpointItemsCount);
|
|
650
660
|
checkpointEventsProcessed.push(...batch.checkpointEventsProcessed);
|
|
651
661
|
registeredAddresses.push(...batch.registeredAddresses);
|
|
652
662
|
} else {
|
|
@@ -658,11 +668,12 @@ function drainBatchRun(state) {
|
|
|
658
668
|
totalBatchSize: totalBatchSize.contents,
|
|
659
669
|
items: items,
|
|
660
670
|
progressedChainsById: progressedChainsById,
|
|
661
|
-
|
|
671
|
+
history: history,
|
|
662
672
|
checkpointIds: checkpointIds,
|
|
663
673
|
checkpointChainIds: checkpointChainIds,
|
|
664
674
|
checkpointBlockNumbers: checkpointBlockNumbers,
|
|
665
675
|
checkpointBlockHashes: checkpointBlockHashes,
|
|
676
|
+
checkpointItemsCount: checkpointItemsCount,
|
|
666
677
|
checkpointEventsProcessed: checkpointEventsProcessed,
|
|
667
678
|
registeredAddresses: registeredAddresses
|
|
668
679
|
};
|
|
@@ -678,11 +689,17 @@ function takeRollback(state) {
|
|
|
678
689
|
return rollback;
|
|
679
690
|
}
|
|
680
691
|
|
|
681
|
-
function markCommitted(state,
|
|
682
|
-
state.
|
|
692
|
+
function markCommitted(state, writtenFrontier) {
|
|
693
|
+
state.committedFrontier = Frontier.mergeMax(state.committedFrontier, writtenFrontier);
|
|
683
694
|
}
|
|
684
695
|
|
|
685
|
-
function beginRollbackDiff(state,
|
|
696
|
+
function beginRollbackDiff(state, floors, progressedChains, rolledBackAddresses) {
|
|
697
|
+
let cursor = CheckpointSequence.cursor(state.config.checkpointSequence, state.committedFrontier);
|
|
698
|
+
let diffFrontier = Frontier.fromEntries(Frontier.chainIds(floors.checkpointBounds.byChain).map(chainId => [
|
|
699
|
+
chainId,
|
|
700
|
+
CheckpointSequence.next(cursor, chainId)
|
|
701
|
+
]));
|
|
702
|
+
state.processedFrontier = Frontier.mergeMax(state.processedFrontier, diffFrontier);
|
|
686
703
|
let perChainEntities = EntityTables.perChain(state.allEntities);
|
|
687
704
|
state.entities = EntityTables.make(EntityTables.crossChain(state.allEntities));
|
|
688
705
|
Utils.Dict.forEach(CrossChainState.chainStates(state.crossChainState), cs => ChainState.resetEntities(cs, perChainEntities));
|
|
@@ -704,11 +721,20 @@ function beginRollbackDiff(state, diffCheckpointId, floors, progressedChains, ro
|
|
|
704
721
|
progressedChains$1 = progressedChains;
|
|
705
722
|
}
|
|
706
723
|
state.rollback = {
|
|
707
|
-
|
|
724
|
+
diffFrontier: diffFrontier,
|
|
725
|
+
diffCheckpoints: Frontier.entries(diffFrontier).map(param => {
|
|
726
|
+
let chainId = param[0];
|
|
727
|
+
return {
|
|
728
|
+
chainId: chainId,
|
|
729
|
+
checkpointId: param[1],
|
|
730
|
+
blockNumber: ChainState.committedProgressBlockNumber(getChainState(state, chainId))
|
|
731
|
+
};
|
|
732
|
+
}),
|
|
708
733
|
floors: floors,
|
|
709
734
|
rolledBackAddresses: rolledBackAddresses$1,
|
|
710
735
|
progressedChains: progressedChains$1
|
|
711
736
|
};
|
|
737
|
+
return diffFrontier;
|
|
712
738
|
}
|
|
713
739
|
|
|
714
740
|
function recordWriteFailure(state, exn) {
|
|
@@ -801,8 +827,9 @@ export {
|
|
|
801
827
|
entities,
|
|
802
828
|
eachEntityTable,
|
|
803
829
|
effectState,
|
|
804
|
-
|
|
805
|
-
|
|
830
|
+
committedFrontier,
|
|
831
|
+
processedFrontier,
|
|
832
|
+
committedCheckpointIdFor,
|
|
806
833
|
processedBatches,
|
|
807
834
|
processedBatchesCount,
|
|
808
835
|
writeFiber,
|
|
@@ -813,6 +840,7 @@ export {
|
|
|
813
840
|
crossChainState,
|
|
814
841
|
chainStates,
|
|
815
842
|
isInReorgThreshold,
|
|
843
|
+
shouldSaveHistory,
|
|
816
844
|
isRealtime,
|
|
817
845
|
isFinalizingIndexes,
|
|
818
846
|
markCaughtUpIfSettled,
|
package/src/IndexerState.resi
CHANGED
|
@@ -14,10 +14,9 @@ let make: (
|
|
|
14
14
|
~config: Config.t,
|
|
15
15
|
~persistence: Persistence.t,
|
|
16
16
|
~chainStates: dict<ChainState.t>,
|
|
17
|
-
~isInReorgThreshold: bool,
|
|
18
17
|
~isRealtime: bool,
|
|
19
18
|
~targetBufferSize: int=?,
|
|
20
|
-
~
|
|
19
|
+
~committedFrontier: Frontier.t=?,
|
|
21
20
|
~isDevelopmentMode: bool=?,
|
|
22
21
|
~shouldUseTui: bool=?,
|
|
23
22
|
~exitAfterFirstEventBlock: bool=?,
|
|
@@ -60,12 +59,7 @@ let beginProcessing: t => unit
|
|
|
60
59
|
let endProcessing: t => unit
|
|
61
60
|
let markProcessingStalledOnFetch: t => unit
|
|
62
61
|
let recordProcessedBatch: t => unit
|
|
63
|
-
let createBatch: (
|
|
64
|
-
t,
|
|
65
|
-
~processedCheckpointId: Internal.checkpointId,
|
|
66
|
-
~batchSizeTarget: int,
|
|
67
|
-
~isRollback: bool,
|
|
68
|
-
) => Batch.t
|
|
62
|
+
let createBatch: (t, ~batchSizeTarget: int) => Batch.t
|
|
69
63
|
let getSafeCheckpointIdByChain: t => array<(ChainId.t, option<Internal.checkpointId>)>
|
|
70
64
|
|
|
71
65
|
// Accessors.
|
|
@@ -82,8 +76,9 @@ let eachEntityTable: (
|
|
|
82
76
|
) => unit,
|
|
83
77
|
) => unit
|
|
84
78
|
let effectState: t => EffectState.t
|
|
85
|
-
let
|
|
86
|
-
let
|
|
79
|
+
let committedFrontier: t => Frontier.t
|
|
80
|
+
let processedFrontier: t => Frontier.t
|
|
81
|
+
let committedCheckpointIdFor: (t, ~scope: Internal.chainScope) => Internal.checkpointId
|
|
87
82
|
let processedBatches: t => array<Batch.t>
|
|
88
83
|
let processedBatchesCount: t => int
|
|
89
84
|
let writeFiber: t => option<promise<unit>>
|
|
@@ -94,6 +89,7 @@ let chainMetaThrottler: t => Throttler.t
|
|
|
94
89
|
let crossChainState: t => CrossChainState.t
|
|
95
90
|
let chainStates: t => dict<ChainState.t>
|
|
96
91
|
let isInReorgThreshold: t => bool
|
|
92
|
+
let shouldSaveHistory: t => dict<bool>
|
|
97
93
|
let isRealtime: t => bool
|
|
98
94
|
let isFinalizingIndexes: t => bool
|
|
99
95
|
let markCaughtUpIfSettled: t => unit
|
|
@@ -137,14 +133,13 @@ let queueProcessedBatch: (t, ~batch: Batch.t) => unit
|
|
|
137
133
|
let drainBatchRun: t => Batch.t
|
|
138
134
|
let pendingRollback: t => option<Persistence.rollback>
|
|
139
135
|
let takeRollback: t => option<Persistence.rollback>
|
|
140
|
-
let markCommitted: (t, ~
|
|
136
|
+
let markCommitted: (t, ~writtenFrontier: Frontier.t) => unit
|
|
141
137
|
let beginRollbackDiff: (
|
|
142
138
|
t,
|
|
143
|
-
~diffCheckpointId: Internal.checkpointId,
|
|
144
139
|
~floors: RollbackFloors.t,
|
|
145
140
|
~progressedChains: array<InternalTable.Chains.progressedChain>,
|
|
146
141
|
~rolledBackAddresses: array<AddressRows.key>,
|
|
147
|
-
) =>
|
|
142
|
+
) => Frontier.t
|
|
148
143
|
let recordWriteFailure: (t, exn) => unit
|
|
149
144
|
let beginWriteFiber: (t, promise<unit>) => unit
|
|
150
145
|
let endWriteFiber: t => unit
|
package/src/Internal.res
CHANGED
|
@@ -854,8 +854,10 @@ type effectArgs = {
|
|
|
854
854
|
input: effectInput,
|
|
855
855
|
context: effectContext,
|
|
856
856
|
cacheKey: string,
|
|
857
|
-
// The processing checkpoint that referenced this effect
|
|
858
|
-
// in-memory cache entry so it's evicted once
|
|
857
|
+
// The processing checkpoint that referenced this effect, on the chain whose
|
|
858
|
+
// handler ran it; stamped on the in-memory cache entry so it's evicted once
|
|
859
|
+
// that chain commits the checkpoint.
|
|
860
|
+
chainId: ChainId.t,
|
|
859
861
|
checkpointId: bigint,
|
|
860
862
|
}
|
|
861
863
|
type effectCacheItem = {id: string, output: effectOutput}
|
package/src/LoadLayer.res
CHANGED
|
@@ -69,9 +69,10 @@ let loadById = (
|
|
|
69
69
|
let entity = dbEntities->Array.getUnsafe(idx)
|
|
70
70
|
entitiesMap->Dict.set(entity.id, entity)
|
|
71
71
|
}
|
|
72
|
+
let committedCheckpointId = indexerState->IndexerState.committedCheckpointIdFor(~scope)
|
|
72
73
|
idsToLoad->Array.forEach(entityId => {
|
|
73
74
|
inMemTable->InMemoryTable.Entity.initValue(
|
|
74
|
-
~committedCheckpointId
|
|
75
|
+
~committedCheckpointId,
|
|
75
76
|
~key=entityId,
|
|
76
77
|
~entity=entitiesMap->Utils.Dict.dangerouslyGetNonOption(entityId),
|
|
77
78
|
)
|
|
@@ -109,6 +110,7 @@ let callEffect = (
|
|
|
109
110
|
effect.handler(arg)
|
|
110
111
|
->Promise.thenResolve(output => {
|
|
111
112
|
inMemTable->InMemoryStore.setEffectOutput(
|
|
113
|
+
~chainId=arg.chainId,
|
|
112
114
|
~checkpointId=arg.checkpointId,
|
|
113
115
|
~cacheKey=arg.cacheKey,
|
|
114
116
|
~output,
|
|
@@ -294,7 +296,11 @@ let loadEffect = (
|
|
|
294
296
|
try {
|
|
295
297
|
let output = dbEntity.output->S.parseOrThrow(outputSchema)
|
|
296
298
|
idsFromCache->Utils.Set.add(dbEntity.id)->ignore
|
|
297
|
-
inMemTable->InMemoryStore.initEffectOutputFromDb(
|
|
299
|
+
inMemTable->InMemoryStore.initEffectOutputFromDb(
|
|
300
|
+
~chainId=item->Internal.getItemChainId,
|
|
301
|
+
~cacheKey=dbEntity.id,
|
|
302
|
+
~output,
|
|
303
|
+
)
|
|
298
304
|
} catch {
|
|
299
305
|
| S.Raised(error) =>
|
|
300
306
|
inMemTable->EffectState.recordInvalidation
|
|
@@ -379,7 +385,7 @@ let loadByFilter = (
|
|
|
379
385
|
// Inside the load timing: waiting on the build is time the handler spends
|
|
380
386
|
// waiting for this operation, and it's the only thing that explains an
|
|
381
387
|
// occasional very slow getWhere.
|
|
382
|
-
await storage.ensureQueryIndexes(~
|
|
388
|
+
await storage.ensureQueryIndexes(~entityConfig, ~scope, ~filters)
|
|
383
389
|
|
|
384
390
|
// Loading a superset of rows via a merged query is safe: every loaded
|
|
385
391
|
// entity is matched against all registered indexes, not only the
|
|
@@ -397,9 +403,10 @@ let loadByFilter = (
|
|
|
397
403
|
)
|
|
398
404
|
)->(Utils.magic: array<unknown> => array<Internal.entity>)
|
|
399
405
|
|
|
406
|
+
let committedCheckpointId = indexerState->IndexerState.committedCheckpointIdFor(~scope)
|
|
400
407
|
entities->Array.forEach(entity => {
|
|
401
408
|
inMemTable->InMemoryTable.Entity.initValue(
|
|
402
|
-
~committedCheckpointId
|
|
409
|
+
~committedCheckpointId,
|
|
403
410
|
~key=entity.id,
|
|
404
411
|
~entity=Some(entity),
|
|
405
412
|
)
|
package/src/LoadLayer.res.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import * as Table from "./db/Table.res.mjs";
|
|
|
4
4
|
import * as Utils from "./Utils.res.mjs";
|
|
5
5
|
import * as ChainId from "./ChainId.res.mjs";
|
|
6
6
|
import * as Logging from "./Logging.res.mjs";
|
|
7
|
+
import * as Internal from "./Internal.res.mjs";
|
|
7
8
|
import * as Ecosystem from "./Ecosystem.res.mjs";
|
|
8
9
|
import * as EffectState from "./EffectState.res.mjs";
|
|
9
10
|
import * as LoadManager from "./LoadManager.res.mjs";
|
|
@@ -71,7 +72,8 @@ function loadById(loadManager, persistence, entityConfig, scope, indexerState, s
|
|
|
71
72
|
let entity = dbEntities[idx];
|
|
72
73
|
entitiesMap[entity.id] = entity;
|
|
73
74
|
}
|
|
74
|
-
|
|
75
|
+
let committedCheckpointId = IndexerState.committedCheckpointIdFor(indexerState, scope);
|
|
76
|
+
idsToLoad.forEach(entityId => InMemoryTable.Entity.initValue(inMemTable, committedCheckpointId, entityId, entitiesMap[entityId]));
|
|
75
77
|
return IndexerState.endStorageLoad(indexerState, timerRef, storage.name, key, idsToLoad.length, dbEntities.length);
|
|
76
78
|
};
|
|
77
79
|
return LoadManager.call(loadManager, entityId, key, load, LoadManager.noopHasher, shouldGroup, hash => hash in inMemTable.latestEntityChangeById, InMemoryTable.Entity.getUnsafe(inMemTable));
|
|
@@ -79,7 +81,7 @@ function loadById(loadManager, persistence, entityConfig, scope, indexerState, s
|
|
|
79
81
|
|
|
80
82
|
function callEffect(effect, arg, inMemTable, timerRef, onError) {
|
|
81
83
|
EffectState.startCall(inMemTable.stats, timerRef);
|
|
82
|
-
return effect.handler(arg).then(output => InMemoryStore.setEffectOutput(inMemTable, arg.checkpointId, arg.cacheKey, output, arg.context.cache)).catch(exn => onError(arg.cacheKey, exn)).finally(() => EffectState.endCall(inMemTable.stats, timerRef));
|
|
84
|
+
return effect.handler(arg).then(output => InMemoryStore.setEffectOutput(inMemTable, arg.chainId, arg.checkpointId, arg.cacheKey, output, arg.context.cache)).catch(exn => onError(arg.cacheKey, exn)).finally(() => EffectState.endCall(inMemTable.stats, timerRef));
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
function executeWithRateLimit(effect, effectArgs, inMemTable, onError, isFromQueue) {
|
|
@@ -173,7 +175,7 @@ function loadEffect(loadManager, persistence, effect, effectArgs, scope, indexer
|
|
|
173
175
|
try {
|
|
174
176
|
let output = S$RescriptSchema.parseOrThrow(dbEntity.output, outputSchema);
|
|
175
177
|
idsFromCache.add(dbEntity.id);
|
|
176
|
-
return InMemoryStore.initEffectOutputFromDb(inMemTable, dbEntity.id, output);
|
|
178
|
+
return InMemoryStore.initEffectOutputFromDb(inMemTable, Internal.getItemChainId(item), dbEntity.id, output);
|
|
177
179
|
} catch (raw_error) {
|
|
178
180
|
let error = Primitive_exceptions.internalToException(raw_error);
|
|
179
181
|
if (error.RE_EXN_ID === S$RescriptSchema.Raised) {
|
|
@@ -218,12 +220,13 @@ function loadByFilter(loadManager, persistence, entityConfig, scope, indexerStat
|
|
|
218
220
|
contents: 0
|
|
219
221
|
};
|
|
220
222
|
filters.forEach(filter => InMemoryTable.Entity.addEmptyIndex(inMemTable, filter));
|
|
221
|
-
await storage.ensureQueryIndexes(entityConfig
|
|
223
|
+
await storage.ensureQueryIndexes(entityConfig, scope, filters);
|
|
222
224
|
let queries = EntityFilter.merge(filters);
|
|
223
225
|
await Promise.all(queries.map(async filter => {
|
|
224
226
|
try {
|
|
225
227
|
let entities = await storage.loadOrThrow(scopeFilter(filter, entityConfig.table, scope), entityConfig.table);
|
|
226
|
-
|
|
228
|
+
let committedCheckpointId = IndexerState.committedCheckpointIdFor(indexerState, scope);
|
|
229
|
+
entities.forEach(entity => InMemoryTable.Entity.initValue(inMemTable, committedCheckpointId, entity.id, entity));
|
|
227
230
|
size.contents = size.contents + entities.length | 0;
|
|
228
231
|
return;
|
|
229
232
|
} catch (raw_exn) {
|