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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "envio",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0-alpha.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
|
|
6
6
|
"bin": "./bin.mjs",
|
|
@@ -42,7 +42,6 @@
|
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@clickhouse/client": "1.17.0",
|
|
44
44
|
"@elastic/ecs-pino-format": "1.4.0",
|
|
45
|
-
"@envio-dev/hyperfuel-client": "1.2.2",
|
|
46
45
|
"@fuel-ts/crypto": "0.96.1",
|
|
47
46
|
"@fuel-ts/errors": "0.96.1",
|
|
48
47
|
"@fuel-ts/hasher": "0.96.1",
|
|
@@ -70,10 +69,10 @@
|
|
|
70
69
|
"tsx": "4.21.0"
|
|
71
70
|
},
|
|
72
71
|
"optionalDependencies": {
|
|
73
|
-
"envio-linux-x64": "3.
|
|
74
|
-
"envio-linux-x64-musl": "3.
|
|
75
|
-
"envio-linux-arm64": "3.
|
|
76
|
-
"envio-darwin-x64": "3.
|
|
77
|
-
"envio-darwin-arm64": "3.
|
|
72
|
+
"envio-linux-x64": "3.3.0-alpha.0",
|
|
73
|
+
"envio-linux-x64-musl": "3.3.0-alpha.0",
|
|
74
|
+
"envio-linux-arm64": "3.3.0-alpha.0",
|
|
75
|
+
"envio-darwin-x64": "3.3.0-alpha.0",
|
|
76
|
+
"envio-darwin-arm64": "3.3.0-alpha.0"
|
|
78
77
|
}
|
|
79
78
|
}
|
package/src/Batch.res
CHANGED
|
@@ -34,10 +34,8 @@ type t = {
|
|
|
34
34
|
checkpointEventsProcessed: array<int>,
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
let hasReadyItem = (fetchStates:
|
|
38
|
-
fetchStates
|
|
39
|
-
->ChainMap.values
|
|
40
|
-
->Array.some(fetchState => {
|
|
37
|
+
let hasReadyItem = (fetchStates: array<FetchState.t>) => {
|
|
38
|
+
fetchStates->Array.some(fetchState => {
|
|
41
39
|
fetchState->FetchState.isActivelyIndexing && fetchState->FetchState.hasReadyItem
|
|
42
40
|
})
|
|
43
41
|
}
|
|
@@ -72,7 +70,7 @@ let getProgressedChainsById = {
|
|
|
72
70
|
}
|
|
73
71
|
|
|
74
72
|
(
|
|
75
|
-
~chainsBeforeBatch:
|
|
73
|
+
~chainsBeforeBatch: dict<chainBeforeBatch>,
|
|
76
74
|
~batchSizePerChain: dict<int>,
|
|
77
75
|
~progressBlockNumberPerChain: dict<int>,
|
|
78
76
|
) => {
|
|
@@ -83,7 +81,7 @@ let getProgressedChainsById = {
|
|
|
83
81
|
// - Accumulate registered dynamic contracts to store in the db
|
|
84
82
|
// - Trigger onBlock pointer update
|
|
85
83
|
chainsBeforeBatch
|
|
86
|
-
->
|
|
84
|
+
->Dict.valuesToArray
|
|
87
85
|
->Array.forEach(chainBeforeBatch => {
|
|
88
86
|
let fetchState = chainBeforeBatch.fetchState
|
|
89
87
|
|
|
@@ -166,15 +164,15 @@ let addReorgCheckpoints = (
|
|
|
166
164
|
|
|
167
165
|
let prepareBatch = (
|
|
168
166
|
~checkpointIdBeforeBatch,
|
|
169
|
-
~chainsBeforeBatch:
|
|
167
|
+
~chainsBeforeBatch: dict<chainBeforeBatch>,
|
|
170
168
|
~batchSizeTarget,
|
|
171
169
|
~isInReorgThreshold,
|
|
172
170
|
) => {
|
|
173
171
|
let preparedFetchStates =
|
|
174
172
|
chainsBeforeBatch
|
|
175
|
-
->
|
|
173
|
+
->Dict.valuesToArray
|
|
176
174
|
->Array.map(chainBeforeBatch => chainBeforeBatch.fetchState)
|
|
177
|
-
->FetchState.
|
|
175
|
+
->FetchState.sortForBatch(~batchSizeTarget)
|
|
178
176
|
|
|
179
177
|
let chainIdx = ref(0)
|
|
180
178
|
let preparedNumber = preparedFetchStates->Array.length
|
|
@@ -202,7 +200,9 @@ let prepareBatch = (
|
|
|
202
200
|
~fromItem=0,
|
|
203
201
|
)
|
|
204
202
|
let chainBeforeBatch =
|
|
205
|
-
chainsBeforeBatch
|
|
203
|
+
chainsBeforeBatch
|
|
204
|
+
->Utils.Dict.dangerouslyGetByIntNonOption(fetchState.chainId)
|
|
205
|
+
->Option.getUnsafe
|
|
206
206
|
|
|
207
207
|
let prevBlockNumber = ref(chainBeforeBatch.progressBlockNumber)
|
|
208
208
|
if chainBatchSize > 0 {
|
|
@@ -243,10 +243,7 @@ let prepareBatch = (
|
|
|
243
243
|
} else {
|
|
244
244
|
let lastIndex = checkpointEventsProcessed->Array.length - 1
|
|
245
245
|
checkpointEventsProcessed
|
|
246
|
-
->Array.setUnsafe(
|
|
247
|
-
lastIndex,
|
|
248
|
-
checkpointEventsProcessed->Array.getUnsafe(lastIndex) + 1,
|
|
249
|
-
)
|
|
246
|
+
->Array.setUnsafe(lastIndex, checkpointEventsProcessed->Array.getUnsafe(lastIndex) + 1)
|
|
250
247
|
->ignore
|
|
251
248
|
}
|
|
252
249
|
|
|
@@ -301,7 +298,7 @@ let prepareBatch = (
|
|
|
301
298
|
|
|
302
299
|
let make = (
|
|
303
300
|
~checkpointIdBeforeBatch,
|
|
304
|
-
~chainsBeforeBatch:
|
|
301
|
+
~chainsBeforeBatch: dict<chainBeforeBatch>,
|
|
305
302
|
~batchSizeTarget,
|
|
306
303
|
~isInReorgThreshold,
|
|
307
304
|
) => {
|
package/src/Batch.res.mjs
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as Utils from "./Utils.res.mjs";
|
|
4
|
-
import * as ChainMap from "./ChainMap.res.mjs";
|
|
5
4
|
import * as FetchState from "./FetchState.res.mjs";
|
|
6
5
|
import * as ReorgDetection from "./ReorgDetection.res.mjs";
|
|
7
6
|
|
|
8
7
|
function hasReadyItem(fetchStates) {
|
|
9
|
-
return
|
|
8
|
+
return fetchStates.some(fetchState => {
|
|
10
9
|
if (FetchState.isActivelyIndexing(fetchState)) {
|
|
11
10
|
return FetchState.hasReadyItem(fetchState);
|
|
12
11
|
} else {
|
|
@@ -30,7 +29,7 @@ function getChainAfterBatchIfProgressed(chainBeforeBatch, progressBlockNumberAft
|
|
|
30
29
|
|
|
31
30
|
function getProgressedChainsById(chainsBeforeBatch, batchSizePerChain, progressBlockNumberPerChain) {
|
|
32
31
|
let progressedChainsById = {};
|
|
33
|
-
|
|
32
|
+
Object.values(chainsBeforeBatch).forEach(chainBeforeBatch => {
|
|
34
33
|
let fetchState = chainBeforeBatch.fetchState;
|
|
35
34
|
let progressBlockNumber = progressBlockNumberPerChain[fetchState.chainId.toString()];
|
|
36
35
|
let progressBlockNumberAfterBatch = progressBlockNumber !== undefined ? progressBlockNumber : chainBeforeBatch.progressBlockNumber;
|
|
@@ -71,7 +70,7 @@ function addReorgCheckpoints(prevCheckpointId, reorgDetection, fromBlockExclusiv
|
|
|
71
70
|
}
|
|
72
71
|
|
|
73
72
|
function prepareBatch(checkpointIdBeforeBatch, chainsBeforeBatch, batchSizeTarget, isInReorgThreshold) {
|
|
74
|
-
let preparedFetchStates = FetchState.
|
|
73
|
+
let preparedFetchStates = FetchState.sortForBatch(Object.values(chainsBeforeBatch).map(chainBeforeBatch => chainBeforeBatch.fetchState), batchSizeTarget);
|
|
75
74
|
let chainIdx = 0;
|
|
76
75
|
let preparedNumber = preparedFetchStates.length;
|
|
77
76
|
let totalBatchSize = 0;
|
|
@@ -87,7 +86,7 @@ function prepareBatch(checkpointIdBeforeBatch, chainsBeforeBatch, batchSizeTarge
|
|
|
87
86
|
while (totalBatchSize < batchSizeTarget && chainIdx < preparedNumber) {
|
|
88
87
|
let fetchState = preparedFetchStates[chainIdx];
|
|
89
88
|
let chainBatchSize = FetchState.getReadyItemsCount(fetchState, batchSizeTarget - totalBatchSize, 0);
|
|
90
|
-
let chainBeforeBatch =
|
|
89
|
+
let chainBeforeBatch = chainsBeforeBatch[fetchState.chainId];
|
|
91
90
|
let prevBlockNumber = chainBeforeBatch.progressBlockNumber;
|
|
92
91
|
if (chainBatchSize > 0) {
|
|
93
92
|
for (let idx = 0, idx_finish = chainBatchSize - 1; idx <= idx_finish; ++idx) {
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
// The single batch-processing loop. Re-enters fetch/rollback through the
|
|
2
|
+
// injected schedule* effects.
|
|
3
|
+
|
|
4
|
+
// Yield to the end of the current tick so fetch responses that resolved this
|
|
5
|
+
// tick land before the processing loop builds its first batch (coalescing).
|
|
6
|
+
@inline
|
|
7
|
+
let yieldTick = () => Promise.make((resolve, _) => NodeJs.setImmediate(() => resolve()))
|
|
8
|
+
|
|
9
|
+
// The single processing loop. Runs batches back-to-back while there's work and
|
|
10
|
+
// no reorg is being resolved; exits when idle so producers (fetch, rollback) can
|
|
11
|
+
// re-kick it. `state.isProcessing` guarantees one instance. The loop
|
|
12
|
+
// decides whether to keep going by inspecting state after each batch, rather than
|
|
13
|
+
// from a return value of processNextBatch.
|
|
14
|
+
let rec startProcessing = async (state: IndexerState.t, ~scheduleFetch, ~scheduleRollback) => {
|
|
15
|
+
if !(state->IndexerState.isProcessing) && !(state->IndexerState.isStopped) {
|
|
16
|
+
state->IndexerState.beginProcessing
|
|
17
|
+
// FIXME: Needed only for test determinism. The mocks resolve several fetch
|
|
18
|
+
// responses synchronously in one tick; this yield lets them all land before
|
|
19
|
+
// the first createBatch so they coalesce into one batch (matching the old
|
|
20
|
+
// setImmediate model). In production responses arrive on separate network
|
|
21
|
+
// ticks and never co-arrive, so this never coalesces anything real — remove
|
|
22
|
+
// it later to avoid an unnecessary setImmediate per processing burst.
|
|
23
|
+
await yieldTick()
|
|
24
|
+
// Seeded true so the first batch always runs (it handles the caught-up exit
|
|
25
|
+
// even when there's nothing to process, eg resuming fully-synced state).
|
|
26
|
+
// Keep looping only while the last batch actually processed something: an
|
|
27
|
+
// empty/idle batch records nothing, so the counter stays put and the loop
|
|
28
|
+
// exits, leaving producers (fetch, rollback) to re-kick it.
|
|
29
|
+
let hasMoreWork = ref(true)
|
|
30
|
+
while (
|
|
31
|
+
hasMoreWork.contents &&
|
|
32
|
+
!(state->IndexerState.isStopped) &&
|
|
33
|
+
!(state->IndexerState.isResolvingReorg)
|
|
34
|
+
) {
|
|
35
|
+
let processedBatchesBefore = state->IndexerState.processedBatchesCount
|
|
36
|
+
switch await processNextBatch(state, ~scheduleFetch) {
|
|
37
|
+
| exception exn =>
|
|
38
|
+
IndexerState.errorExit(state, exn->ErrorHandling.make(~msg=IndexerState.unexpectedErrorMsg))
|
|
39
|
+
| () => hasMoreWork := state->IndexerState.processedBatchesCount > processedBatchesBefore
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
state->IndexerState.endProcessing
|
|
43
|
+
|
|
44
|
+
// A reorg detected mid-batch parks the rollback until the loop is idle.
|
|
45
|
+
// Hand off now that no batch is in flight.
|
|
46
|
+
if state->IndexerState.isResolvingReorg {
|
|
47
|
+
scheduleRollback()
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
and processNextBatch = async (state: IndexerState.t, ~scheduleFetch): unit => {
|
|
53
|
+
// The reorg-threshold and queue updates below advance the chain states for the
|
|
54
|
+
// next round, but the current batch is created and processed against the chain
|
|
55
|
+
// states as they are now.
|
|
56
|
+
let isInReorgThresholdBeforeUpdate = state->IndexerState.isInReorgThreshold
|
|
57
|
+
let isRealtimeBeforeUpdate = state->IndexerState.isRealtime
|
|
58
|
+
|
|
59
|
+
let isRollbackBatch = switch state->IndexerState.rollbackState {
|
|
60
|
+
| RollbackReady(_) => true
|
|
61
|
+
| _ => false
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let batch =
|
|
65
|
+
state->IndexerState.createBatch(
|
|
66
|
+
~processedCheckpointId=state->IndexerState.processedCheckpointId,
|
|
67
|
+
~batchSizeTarget=(state->IndexerState.config).batchSize,
|
|
68
|
+
~isRollback=isRollbackBatch,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
let progressedChainsById = batch.progressedChainsById
|
|
72
|
+
|
|
73
|
+
let isBelowReorgThreshold =
|
|
74
|
+
!isInReorgThresholdBeforeUpdate && (state->IndexerState.config).shouldRollbackOnReorg
|
|
75
|
+
let shouldEnterReorgThreshold =
|
|
76
|
+
isBelowReorgThreshold &&
|
|
77
|
+
state
|
|
78
|
+
->IndexerState.chainStates
|
|
79
|
+
->Dict.valuesToArray
|
|
80
|
+
->Array.every(cs => {
|
|
81
|
+
let fetchState = switch progressedChainsById->Utils.Dict.dangerouslyGetByIntNonOption(
|
|
82
|
+
(cs->ChainState.fetchState).chainId,
|
|
83
|
+
) {
|
|
84
|
+
| Some(chainAfterBatch) => chainAfterBatch.fetchState
|
|
85
|
+
| None => cs->ChainState.fetchState
|
|
86
|
+
}
|
|
87
|
+
fetchState->FetchState.isReadyToEnterReorgThreshold
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
if shouldEnterReorgThreshold {
|
|
91
|
+
IndexerState.enterReorgThreshold(state)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if progressedChainsById->Utils.Dict.isEmpty {
|
|
95
|
+
if shouldEnterReorgThreshold {
|
|
96
|
+
scheduleFetch()
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// When resuming from persisted state, all events may already be processed.
|
|
100
|
+
if EventProcessing.allChainsEventsProcessedToEndblock(state->IndexerState.chainStates) {
|
|
101
|
+
Logging.info("All chains are caught up to end blocks.")
|
|
102
|
+
if !(state->IndexerState.keepProcessAlive) {
|
|
103
|
+
await ExitOnCaughtUp.run(state)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
// The batch was created from pre-threshold fetch states, so advanceAfterBatch
|
|
108
|
+
// applies blockLag when crossing the threshold; enterReorgThreshold already
|
|
109
|
+
// covered the non-progressed chain states.
|
|
110
|
+
state
|
|
111
|
+
->IndexerState.chainStates
|
|
112
|
+
->Utils.Dict.forEach(cs =>
|
|
113
|
+
cs->ChainState.advanceAfterBatch(~batch, ~enteringReorgThreshold=shouldEnterReorgThreshold)
|
|
114
|
+
)
|
|
115
|
+
// Kick the next fetch round before awaiting the batch. A response that
|
|
116
|
+
// lands mid-batch commits only fetch-frontier fields (buffer, knownHeight),
|
|
117
|
+
// while applyBatchProgress below commits only progress fields, so the two
|
|
118
|
+
// concurrent writes are disjoint and neither clobbers the other.
|
|
119
|
+
scheduleFetch()
|
|
120
|
+
|
|
121
|
+
state->InMemoryStore.setBatchDcs(~batch)
|
|
122
|
+
|
|
123
|
+
// An exception here propagates to startProcessing's catch, the single error
|
|
124
|
+
// boundary for the loop. The Error case is not an exception, so it's handled
|
|
125
|
+
// here to preserve the handler's user-facing message.
|
|
126
|
+
switch await EventProcessing.processEventBatch(
|
|
127
|
+
~batch,
|
|
128
|
+
~indexerState=state,
|
|
129
|
+
~loadManager=state->IndexerState.loadManager,
|
|
130
|
+
~persistence=state->IndexerState.persistence,
|
|
131
|
+
~config=state->IndexerState.config,
|
|
132
|
+
~chainStates=state->IndexerState.chainStates,
|
|
133
|
+
) {
|
|
134
|
+
| Error(errHandler) => IndexerState.errorExit(state, errHandler)
|
|
135
|
+
| Ok() =>
|
|
136
|
+
state->IndexerState.recordProcessedBatch
|
|
137
|
+
|
|
138
|
+
if state->IndexerState.isResolvingReorg {
|
|
139
|
+
// A reorg landed while this batch was processing. Apply its progress so
|
|
140
|
+
// the rollback diff is computed against up-to-date chain progress, but
|
|
141
|
+
// don't reset rollback state or evaluate exit. The loop exit hands off
|
|
142
|
+
// to rollback.
|
|
143
|
+
state->IndexerState.applyBatchProgress(~batch)
|
|
144
|
+
} else {
|
|
145
|
+
// Can safely reset rollback state, since overwrite is not possible.
|
|
146
|
+
state->IndexerState.clearRollback
|
|
147
|
+
state->IndexerState.applyBatchProgress(~batch)
|
|
148
|
+
|
|
149
|
+
if !isRealtimeBeforeUpdate && state->IndexerState.isRealtime {
|
|
150
|
+
// Catching up just flipped the chain to realtime, which changes the
|
|
151
|
+
// active source for waitForNewBlock (eg sync -> live). The waiter that
|
|
152
|
+
// parked during backfill is bound to the old source; bump the epoch to
|
|
153
|
+
// invalidate it and kick a fresh fetch that parks on the realtime source.
|
|
154
|
+
state->IndexerState.invalidateInflight
|
|
155
|
+
scheduleFetch()
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
let allCaughtUp = EventProcessing.allChainsEventsProcessedToEndblock(
|
|
159
|
+
state->IndexerState.chainStates,
|
|
160
|
+
)
|
|
161
|
+
if allCaughtUp {
|
|
162
|
+
Logging.info("All chains are caught up to end blocks.")
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if allCaughtUp && !(state->IndexerState.keepProcessAlive) {
|
|
166
|
+
await ExitOnCaughtUp.run(state)
|
|
167
|
+
} else if (
|
|
168
|
+
// In auto-exit mode, error if all chains reached head with no events found
|
|
169
|
+
!allCaughtUp &&
|
|
170
|
+
state->IndexerState.exitAfterFirstEventBlock &&
|
|
171
|
+
state
|
|
172
|
+
->IndexerState.chainStates
|
|
173
|
+
->Dict.valuesToArray
|
|
174
|
+
->Array.every(cs =>
|
|
175
|
+
cs->ChainState.isProgressAtHead && (cs->ChainState.fetchState).endBlock->Option.isNone
|
|
176
|
+
)
|
|
177
|
+
) {
|
|
178
|
+
IndexerState.errorExit(
|
|
179
|
+
state,
|
|
180
|
+
ErrorHandling.make(
|
|
181
|
+
Utils.Error.make(
|
|
182
|
+
"No events found between startBlock and chain head. Cannot auto-detect endBlock.",
|
|
183
|
+
),
|
|
184
|
+
),
|
|
185
|
+
)
|
|
186
|
+
} else {
|
|
187
|
+
ChainMetadata.stage(state)
|
|
188
|
+
if (
|
|
189
|
+
state
|
|
190
|
+
->IndexerState.config
|
|
191
|
+
->Config.shouldPruneHistory(~isInReorgThreshold=state->IndexerState.isInReorgThreshold)
|
|
192
|
+
) {
|
|
193
|
+
state->PruneStaleHistory.schedule
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Utils from "./Utils.res.mjs";
|
|
4
|
+
import * as Config from "./Config.res.mjs";
|
|
5
|
+
import * as Logging from "./Logging.res.mjs";
|
|
6
|
+
import * as ChainState from "./ChainState.res.mjs";
|
|
7
|
+
import * as FetchState from "./FetchState.res.mjs";
|
|
8
|
+
import * as IndexerState from "./IndexerState.res.mjs";
|
|
9
|
+
import * as ChainMetadata from "./ChainMetadata.res.mjs";
|
|
10
|
+
import * as ErrorHandling from "./ErrorHandling.res.mjs";
|
|
11
|
+
import * as InMemoryStore from "./InMemoryStore.res.mjs";
|
|
12
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
13
|
+
import * as ExitOnCaughtUp from "./ExitOnCaughtUp.res.mjs";
|
|
14
|
+
import * as EventProcessing from "./EventProcessing.res.mjs";
|
|
15
|
+
import * as PruneStaleHistory from "./PruneStaleHistory.res.mjs";
|
|
16
|
+
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
17
|
+
|
|
18
|
+
function yieldTick() {
|
|
19
|
+
return new Promise((resolve, param) => {
|
|
20
|
+
setImmediate(() => resolve());
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function processNextBatch(state, scheduleFetch) {
|
|
25
|
+
let isInReorgThresholdBeforeUpdate = IndexerState.isInReorgThreshold(state);
|
|
26
|
+
let isRealtimeBeforeUpdate = IndexerState.isRealtime(state);
|
|
27
|
+
let match = IndexerState.rollbackState(state);
|
|
28
|
+
let isRollbackBatch;
|
|
29
|
+
isRollbackBatch = typeof match !== "object" ? false : match.TAG === "RollbackReady";
|
|
30
|
+
let batch = IndexerState.createBatch(state, IndexerState.processedCheckpointId(state), IndexerState.config(state).batchSize, isRollbackBatch);
|
|
31
|
+
let progressedChainsById = batch.progressedChainsById;
|
|
32
|
+
let isBelowReorgThreshold = !isInReorgThresholdBeforeUpdate && IndexerState.config(state).shouldRollbackOnReorg;
|
|
33
|
+
let shouldEnterReorgThreshold = isBelowReorgThreshold && Object.values(IndexerState.chainStates(state)).every(cs => {
|
|
34
|
+
let chainAfterBatch = progressedChainsById[ChainState.fetchState(cs).chainId];
|
|
35
|
+
return FetchState.isReadyToEnterReorgThreshold(chainAfterBatch !== undefined ? chainAfterBatch.fetchState : ChainState.fetchState(cs));
|
|
36
|
+
});
|
|
37
|
+
if (shouldEnterReorgThreshold) {
|
|
38
|
+
IndexerState.enterReorgThreshold(state);
|
|
39
|
+
}
|
|
40
|
+
if (Utils.Dict.isEmpty(progressedChainsById)) {
|
|
41
|
+
if (shouldEnterReorgThreshold) {
|
|
42
|
+
scheduleFetch();
|
|
43
|
+
}
|
|
44
|
+
if (EventProcessing.allChainsEventsProcessedToEndblock(IndexerState.chainStates(state))) {
|
|
45
|
+
Logging.info("All chains are caught up to end blocks.");
|
|
46
|
+
if (!IndexerState.keepProcessAlive(state)) {
|
|
47
|
+
return await ExitOnCaughtUp.run(state);
|
|
48
|
+
} else {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
Utils.Dict.forEach(IndexerState.chainStates(state), cs => ChainState.advanceAfterBatch(cs, batch, shouldEnterReorgThreshold));
|
|
56
|
+
scheduleFetch();
|
|
57
|
+
InMemoryStore.setBatchDcs(state, batch);
|
|
58
|
+
let errHandler = await EventProcessing.processEventBatch(batch, state, IndexerState.loadManager(state), IndexerState.persistence(state), IndexerState.config(state), IndexerState.chainStates(state));
|
|
59
|
+
if (errHandler.TAG !== "Ok") {
|
|
60
|
+
return IndexerState.errorExit(state, errHandler._0);
|
|
61
|
+
}
|
|
62
|
+
IndexerState.recordProcessedBatch(state);
|
|
63
|
+
if (IndexerState.isResolvingReorg(state)) {
|
|
64
|
+
return IndexerState.applyBatchProgress(state, batch);
|
|
65
|
+
}
|
|
66
|
+
IndexerState.clearRollback(state);
|
|
67
|
+
IndexerState.applyBatchProgress(state, batch);
|
|
68
|
+
if (!isRealtimeBeforeUpdate && IndexerState.isRealtime(state)) {
|
|
69
|
+
IndexerState.invalidateInflight(state);
|
|
70
|
+
scheduleFetch();
|
|
71
|
+
}
|
|
72
|
+
let allCaughtUp = EventProcessing.allChainsEventsProcessedToEndblock(IndexerState.chainStates(state));
|
|
73
|
+
if (allCaughtUp) {
|
|
74
|
+
Logging.info("All chains are caught up to end blocks.");
|
|
75
|
+
}
|
|
76
|
+
if (allCaughtUp && !IndexerState.keepProcessAlive(state)) {
|
|
77
|
+
return await ExitOnCaughtUp.run(state);
|
|
78
|
+
} else if (!allCaughtUp && IndexerState.exitAfterFirstEventBlock(state) && Object.values(IndexerState.chainStates(state)).every(cs => ChainState.isProgressAtHead(cs) ? Stdlib_Option.isNone(ChainState.fetchState(cs).endBlock) : false)) {
|
|
79
|
+
return IndexerState.errorExit(state, ErrorHandling.make(new Error("No events found between startBlock and chain head. Cannot auto-detect endBlock."), undefined, undefined));
|
|
80
|
+
} else {
|
|
81
|
+
ChainMetadata.stage(state);
|
|
82
|
+
if (Config.shouldPruneHistory(IndexerState.config(state), IndexerState.isInReorgThreshold(state))) {
|
|
83
|
+
return PruneStaleHistory.schedule(state);
|
|
84
|
+
} else {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function startProcessing(state, scheduleFetch, scheduleRollback) {
|
|
91
|
+
if (IndexerState.isProcessing(state) || IndexerState.isStopped(state)) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
IndexerState.beginProcessing(state);
|
|
95
|
+
await new Promise((resolve, param) => {
|
|
96
|
+
setImmediate(() => resolve());
|
|
97
|
+
});
|
|
98
|
+
let hasMoreWork = true;
|
|
99
|
+
while (hasMoreWork && !IndexerState.isStopped(state) && !IndexerState.isResolvingReorg(state)) {
|
|
100
|
+
let processedBatchesBefore = IndexerState.processedBatchesCount(state);
|
|
101
|
+
let exit = 0;
|
|
102
|
+
let val;
|
|
103
|
+
try {
|
|
104
|
+
val = await processNextBatch(state, scheduleFetch);
|
|
105
|
+
exit = 1;
|
|
106
|
+
} catch (raw_exn) {
|
|
107
|
+
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
108
|
+
IndexerState.errorExit(state, ErrorHandling.make(exn, undefined, IndexerState.unexpectedErrorMsg));
|
|
109
|
+
}
|
|
110
|
+
if (exit === 1) {
|
|
111
|
+
hasMoreWork = IndexerState.processedBatchesCount(state) > processedBatchesBefore;
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
IndexerState.endProcessing(state);
|
|
115
|
+
if (IndexerState.isResolvingReorg(state)) {
|
|
116
|
+
return scheduleRollback();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export {
|
|
121
|
+
yieldTick,
|
|
122
|
+
startProcessing,
|
|
123
|
+
processNextBatch,
|
|
124
|
+
}
|
|
125
|
+
/* Utils Not a pure module */
|