envio 3.5.0-alpha.2 → 3.5.0-alpha.3
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 -6
- package/src/BatchProcessing.res +14 -0
- package/src/BatchProcessing.res.mjs +8 -0
- package/src/ChainState.res +10 -4
- package/src/ChainState.res.mjs +7 -6
- package/src/ChainState.resi +1 -1
- package/src/Config.res +2 -2
- package/src/Config.res.mjs +2 -2
- package/src/CrossChainState.res +33 -10
- package/src/CrossChainState.res.mjs +21 -6
- package/src/CrossChainState.resi +3 -0
- package/src/FetchState.res +230 -66
- package/src/FetchState.res.mjs +213 -67
- package/src/FinalizeBackfill.res +34 -0
- package/src/FinalizeBackfill.res.mjs +26 -0
- package/src/InMemoryTable.res +15 -15
- package/src/InMemoryTable.res.mjs +14 -14
- package/src/IndexerState.res +12 -0
- package/src/IndexerState.res.mjs +19 -0
- package/src/IndexerState.resi +3 -0
- package/src/LoadLayer.res +9 -1
- package/src/LoadLayer.res.mjs +1 -0
- package/src/Persistence.res +11 -0
- package/src/PgStorage.res +356 -35
- package/src/PgStorage.res.mjs +237 -24
- package/src/TestIndexer.res +3 -0
- package/src/TestIndexer.res.mjs +2 -0
- package/src/db/EntityFilter.res +0 -4
- package/src/db/EntityFilter.res.mjs +1 -8
- package/src/db/IndexRegistry.res +219 -0
- package/src/db/IndexRegistry.res.mjs +195 -0
- package/src/db/InternalTable.res +7 -0
- package/src/db/InternalTable.res.mjs +7 -0
- package/src/db/Table.res +13 -13
- package/src/db/Table.res.mjs +12 -12
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "envio",
|
|
3
|
-
"version": "3.5.0-alpha.
|
|
3
|
+
"version": "3.5.0-alpha.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
|
|
6
6
|
"bin": "./bin.mjs",
|
|
@@ -69,10 +69,10 @@
|
|
|
69
69
|
"tsx": "4.21.0"
|
|
70
70
|
},
|
|
71
71
|
"optionalDependencies": {
|
|
72
|
-
"envio-linux-x64": "3.5.0-alpha.
|
|
73
|
-
"envio-linux-x64-musl": "3.5.0-alpha.
|
|
74
|
-
"envio-linux-arm64": "3.5.0-alpha.
|
|
75
|
-
"envio-darwin-x64": "3.5.0-alpha.
|
|
76
|
-
"envio-darwin-arm64": "3.5.0-alpha.
|
|
72
|
+
"envio-linux-x64": "3.5.0-alpha.3",
|
|
73
|
+
"envio-linux-x64-musl": "3.5.0-alpha.3",
|
|
74
|
+
"envio-linux-arm64": "3.5.0-alpha.3",
|
|
75
|
+
"envio-darwin-x64": "3.5.0-alpha.3",
|
|
76
|
+
"envio-darwin-arm64": "3.5.0-alpha.3"
|
|
77
77
|
}
|
|
78
78
|
}
|
package/src/BatchProcessing.res
CHANGED
|
@@ -104,6 +104,13 @@ and processNextBatch = async (state: IndexerState.t, ~scheduleFetch): unit => {
|
|
|
104
104
|
// When resuming from persisted state, all events may already be processed.
|
|
105
105
|
if EventProcessing.allChainsEventsProcessedToEndblock(state->IndexerState.chainStates) {
|
|
106
106
|
Logging.info("All chains are caught up to end blocks.")
|
|
107
|
+
// A run that has nothing left to process still owes the schema its
|
|
108
|
+
// deferred indexes — otherwise a resume of a backfill that died right
|
|
109
|
+
// before finalizing would report ready without them.
|
|
110
|
+
state->IndexerState.markCaughtUp
|
|
111
|
+
if state->IndexerState.isFinalizingIndexes {
|
|
112
|
+
await FinalizeBackfill.run(state)
|
|
113
|
+
}
|
|
107
114
|
if !(state->IndexerState.keepProcessAlive) {
|
|
108
115
|
await ExitOnCaughtUp.run(state)
|
|
109
116
|
}
|
|
@@ -156,6 +163,13 @@ and processNextBatch = async (state: IndexerState.t, ~scheduleFetch): unit => {
|
|
|
156
163
|
state->IndexerState.clearRollback
|
|
157
164
|
state->IndexerState.applyBatchProgress(~batch)
|
|
158
165
|
|
|
166
|
+
// Backfilling → FinalizingIndexes → Ready. Awaiting here holds the
|
|
167
|
+
// processing loop for the whole finalize, which is what pauses
|
|
168
|
+
// processing while the indexes are built.
|
|
169
|
+
if state->IndexerState.isFinalizingIndexes {
|
|
170
|
+
await FinalizeBackfill.run(state)
|
|
171
|
+
}
|
|
172
|
+
|
|
159
173
|
if !isRealtimeBeforeUpdate && state->IndexerState.isRealtime {
|
|
160
174
|
// Catching up just flipped the chain to realtime, which changes the
|
|
161
175
|
// active source for waitForNewBlock (eg sync -> live). The waiter that
|
|
@@ -9,6 +9,7 @@ import * as ErrorHandling from "./ErrorHandling.res.mjs";
|
|
|
9
9
|
import * as InMemoryStore from "./InMemoryStore.res.mjs";
|
|
10
10
|
import * as ExitOnCaughtUp from "./ExitOnCaughtUp.res.mjs";
|
|
11
11
|
import * as EventProcessing from "./EventProcessing.res.mjs";
|
|
12
|
+
import * as FinalizeBackfill from "./FinalizeBackfill.res.mjs";
|
|
12
13
|
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
13
14
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
14
15
|
import * as SimulateDeadInputTracker from "./SimulateDeadInputTracker.res.mjs";
|
|
@@ -38,6 +39,10 @@ async function processNextBatch(state, scheduleFetch) {
|
|
|
38
39
|
}
|
|
39
40
|
if (EventProcessing.allChainsEventsProcessedToEndblock(IndexerState.chainStates(state))) {
|
|
40
41
|
Logging.info("All chains are caught up to end blocks.");
|
|
42
|
+
IndexerState.markCaughtUp(state);
|
|
43
|
+
if (IndexerState.isFinalizingIndexes(state)) {
|
|
44
|
+
await FinalizeBackfill.run(state);
|
|
45
|
+
}
|
|
41
46
|
if (!IndexerState.keepProcessAlive(state)) {
|
|
42
47
|
return await ExitOnCaughtUp.run(state);
|
|
43
48
|
} else {
|
|
@@ -64,6 +69,9 @@ async function processNextBatch(state, scheduleFetch) {
|
|
|
64
69
|
}
|
|
65
70
|
IndexerState.clearRollback(state);
|
|
66
71
|
IndexerState.applyBatchProgress(state, batch);
|
|
72
|
+
if (IndexerState.isFinalizingIndexes(state)) {
|
|
73
|
+
await FinalizeBackfill.run(state);
|
|
74
|
+
}
|
|
67
75
|
if (!isRealtimeBeforeUpdate && IndexerState.isRealtime(state)) {
|
|
68
76
|
IndexerState.invalidateInflight(state);
|
|
69
77
|
scheduleFetch();
|
package/src/ChainState.res
CHANGED
|
@@ -147,6 +147,7 @@ let makeInternal = (
|
|
|
147
147
|
~reorgCheckpoints: array<Internal.reorgCheckpoint>,
|
|
148
148
|
~maxReorgDepth,
|
|
149
149
|
~knownHeight=0,
|
|
150
|
+
~isResumed=false,
|
|
150
151
|
~reducedPollingInterval=?,
|
|
151
152
|
): t => {
|
|
152
153
|
// Handler binding + `where`-derived fetch state, and onBlock registrations,
|
|
@@ -203,6 +204,7 @@ let makeInternal = (
|
|
|
203
204
|
| Evm => Some(config.clientFilterAddressThreshold)
|
|
204
205
|
| Fuel | Svm => None
|
|
205
206
|
},
|
|
207
|
+
~isResumed,
|
|
206
208
|
)
|
|
207
209
|
|
|
208
210
|
let chainReorgCheckpoints = reorgCheckpoints->Array.filterMap(reorgCheckpoint => {
|
|
@@ -383,6 +385,7 @@ let makeFromDbState = (
|
|
|
383
385
|
~isInReorgThreshold,
|
|
384
386
|
~isRealtime,
|
|
385
387
|
~knownHeight=resumedChainState.sourceBlockNumber,
|
|
388
|
+
~isResumed=true,
|
|
386
389
|
~reducedPollingInterval?,
|
|
387
390
|
)
|
|
388
391
|
}
|
|
@@ -1082,11 +1085,14 @@ let applyBatchProgress = (cs: t, ~batch: Batch.t, ~blockTimestampName: string) =
|
|
|
1082
1085
|
}
|
|
1083
1086
|
|
|
1084
1087
|
// Mark the chain caught up to head/endblock. Called by CrossChainState only once
|
|
1085
|
-
// every chain in the indexer is caught up
|
|
1086
|
-
//
|
|
1087
|
-
|
|
1088
|
+
// every chain in the indexer is caught up and the deferred schema indexes are
|
|
1089
|
+
// committed, so no chain flips to ready while another is still backfilling or
|
|
1090
|
+
// while an index the schema promises is still missing. `readyAt` is the
|
|
1091
|
+
// timestamp already committed to `envio_chains.ready_at` in that same
|
|
1092
|
+
// transaction. Sticky: a chain stays ready once set.
|
|
1093
|
+
let markReady = (cs: t, ~readyAt) =>
|
|
1088
1094
|
if !(cs->isReady) {
|
|
1089
|
-
cs.timestampCaughtUpToHeadOrEndblock =
|
|
1095
|
+
cs.timestampCaughtUpToHeadOrEndblock = Some(readyAt)
|
|
1090
1096
|
}
|
|
1091
1097
|
|
|
1092
1098
|
// Roll a chain back to a reorg target. With a progress diff, restore fetch/
|
package/src/ChainState.res.mjs
CHANGED
|
@@ -89,9 +89,10 @@ function make(chainConfig, fetchState, onEventRegistrationsOpt, addressStore, so
|
|
|
89
89
|
};
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firstEventBlockOpt, progressBlockNumber, config, registrationsByChainId, logger, timestampCaughtUpToHeadOrEndblock, numEventsProcessed, isInReorgThreshold, isRealtime, reorgCheckpoints, maxReorgDepth, knownHeightOpt, reducedPollingInterval) {
|
|
92
|
+
function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firstEventBlockOpt, progressBlockNumber, config, registrationsByChainId, logger, timestampCaughtUpToHeadOrEndblock, numEventsProcessed, isInReorgThreshold, isRealtime, reorgCheckpoints, maxReorgDepth, knownHeightOpt, isResumedOpt, reducedPollingInterval) {
|
|
93
93
|
let firstEventBlock = firstEventBlockOpt !== undefined ? Primitive_option.valFromOption(firstEventBlockOpt) : undefined;
|
|
94
94
|
let knownHeight = knownHeightOpt !== undefined ? knownHeightOpt : 0;
|
|
95
|
+
let isResumed = isResumedOpt !== undefined ? isResumedOpt : false;
|
|
95
96
|
let match = Stdlib_Option.getOr(registrationsByChainId[chainConfig.id.toString()], {
|
|
96
97
|
onEventRegistrations: [],
|
|
97
98
|
onBlockRegistrations: []
|
|
@@ -116,7 +117,7 @@ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firs
|
|
|
116
117
|
tmp = undefined;
|
|
117
118
|
break;
|
|
118
119
|
}
|
|
119
|
-
let fetchState = FetchState.make(startBlock, endBlock, onEventRegistrations, addressStore, indexingAddresses, config.maxAddrInPartition, chainConfig.id, (config.batchSize << 1), knownHeight, progressBlockNumber, match.onBlockRegistrations, Primitive_int.max(!config.shouldRollbackOnReorg || isInReorgThreshold ? 0 : chainConfig.maxReorgDepth, chainConfig.blockLag), Primitive_option.some(firstEventBlock), Primitive_option.some(tmp));
|
|
120
|
+
let fetchState = FetchState.make(startBlock, endBlock, onEventRegistrations, addressStore, indexingAddresses, config.maxAddrInPartition, chainConfig.id, (config.batchSize << 1), knownHeight, progressBlockNumber, match.onBlockRegistrations, Primitive_int.max(!config.shouldRollbackOnReorg || isInReorgThreshold ? 0 : chainConfig.maxReorgDepth, chainConfig.blockLag), Primitive_option.some(firstEventBlock), Primitive_option.some(tmp), isResumed);
|
|
120
121
|
let chainReorgCheckpoints = Stdlib_Array.filterMap(reorgCheckpoints, reorgCheckpoint => {
|
|
121
122
|
if (reorgCheckpoint.chain_id === chainConfig.id) {
|
|
122
123
|
return reorgCheckpoint;
|
|
@@ -177,7 +178,7 @@ function makeFromConfig(chainConfig, config, registrationsByChainId, knownHeight
|
|
|
177
178
|
let logger = Logging.createChild({
|
|
178
179
|
chainId: chainConfig.id
|
|
179
180
|
});
|
|
180
|
-
return makeInternal(chainConfig, configAddresses(chainConfig), chainConfig.startBlock, chainConfig.endBlock, undefined, -1, config, registrationsByChainId, logger, undefined, 0, false, false, [], chainConfig.maxReorgDepth, knownHeight, undefined);
|
|
181
|
+
return makeInternal(chainConfig, configAddresses(chainConfig), chainConfig.startBlock, chainConfig.endBlock, undefined, -1, config, registrationsByChainId, logger, undefined, 0, false, false, [], chainConfig.maxReorgDepth, knownHeight, undefined, undefined);
|
|
181
182
|
}
|
|
182
183
|
|
|
183
184
|
function makeFromDbState(chainConfig, resumedChainState, reorgCheckpoints, isInReorgThreshold, isRealtime, config, registrationsByChainId, reducedPollingInterval) {
|
|
@@ -186,7 +187,7 @@ function makeFromDbState(chainConfig, resumedChainState, reorgCheckpoints, isInR
|
|
|
186
187
|
chainId: chainId
|
|
187
188
|
});
|
|
188
189
|
let progressBlockNumber = resumedChainState.progressBlockNumber >= 0 ? resumedChainState.progressBlockNumber : resumedChainState.startBlock - 1 | 0;
|
|
189
|
-
return makeInternal(chainConfig, resumedChainState.indexingAddresses, resumedChainState.startBlock, resumedChainState.endBlock, Primitive_option.some(resumedChainState.firstEventBlockNumber), progressBlockNumber, config, registrationsByChainId, logger, Env.updateSyncTimeOnRestart ? undefined : resumedChainState.timestampCaughtUpToHeadOrEndblock, resumedChainState.numEventsProcessed, isInReorgThreshold, isRealtime, reorgCheckpoints, resumedChainState.maxReorgDepth, resumedChainState.sourceBlockNumber, reducedPollingInterval);
|
|
190
|
+
return makeInternal(chainConfig, resumedChainState.indexingAddresses, resumedChainState.startBlock, resumedChainState.endBlock, Primitive_option.some(resumedChainState.firstEventBlockNumber), progressBlockNumber, config, registrationsByChainId, logger, Env.updateSyncTimeOnRestart ? undefined : resumedChainState.timestampCaughtUpToHeadOrEndblock, resumedChainState.numEventsProcessed, isInReorgThreshold, isRealtime, reorgCheckpoints, resumedChainState.maxReorgDepth, resumedChainState.sourceBlockNumber, true, reducedPollingInterval);
|
|
190
191
|
}
|
|
191
192
|
|
|
192
193
|
function logger(cs) {
|
|
@@ -757,9 +758,9 @@ function applyBatchProgress(cs, batch, blockTimestampName) {
|
|
|
757
758
|
}
|
|
758
759
|
}
|
|
759
760
|
|
|
760
|
-
function markReady(cs) {
|
|
761
|
+
function markReady(cs, readyAt) {
|
|
761
762
|
if (cs.timestampCaughtUpToHeadOrEndblock === undefined) {
|
|
762
|
-
cs.timestampCaughtUpToHeadOrEndblock =
|
|
763
|
+
cs.timestampCaughtUpToHeadOrEndblock = readyAt;
|
|
763
764
|
return;
|
|
764
765
|
}
|
|
765
766
|
}
|
package/src/ChainState.resi
CHANGED
|
@@ -137,7 +137,7 @@ let setEndBlockToFirstEvent: (t, ~blockNumber: int) => unit
|
|
|
137
137
|
let enterReorgThreshold: t => unit
|
|
138
138
|
let advanceAfterBatch: (t, ~batch: Batch.t, ~enteringReorgThreshold: bool) => unit
|
|
139
139
|
let applyBatchProgress: (t, ~batch: Batch.t, ~blockTimestampName: string) => unit
|
|
140
|
-
let markReady: t => unit
|
|
140
|
+
let markReady: (t, ~readyAt: Date.t) => unit
|
|
141
141
|
let rollback: (
|
|
142
142
|
t,
|
|
143
143
|
~newProgressBlockNumber: option<int>,
|
package/src/Config.res
CHANGED
|
@@ -455,7 +455,7 @@ let parseEntitiesFromJson = (
|
|
|
455
455
|
)
|
|
456
456
|
)
|
|
457
457
|
|
|
458
|
-
let
|
|
458
|
+
let compositeIndexes =
|
|
459
459
|
entityJson["compositeIndices"]
|
|
460
460
|
->Option.getOr([])
|
|
461
461
|
->Array.map(ci =>
|
|
@@ -470,7 +470,7 @@ let parseEntitiesFromJson = (
|
|
|
470
470
|
let table = Table.mkTable(
|
|
471
471
|
entityName,
|
|
472
472
|
~fields=Array.concat(fields, derivedFields),
|
|
473
|
-
~
|
|
473
|
+
~compositeIndexes,
|
|
474
474
|
~description=?entityJson["description"],
|
|
475
475
|
)
|
|
476
476
|
|
package/src/Config.res.mjs
CHANGED
|
@@ -331,11 +331,11 @@ function parseEntitiesFromJson(entitiesJson, enumConfigsByName, globalStorage) {
|
|
|
331
331
|
return Table.mkField(prop.name, match[0], match[1], undefined, match[3], match[2], prop.name === "id", match[4], prop.linkedEntity, prop.description, prop.postgresDbName, prop.clickhouseDbName);
|
|
332
332
|
});
|
|
333
333
|
let derivedFields = Stdlib_Option.getOr(entityJson.derivedFields, []).map(df => Table.mkDerivedFromField(df.fieldName, df.derivedFromEntity, df.derivedFromField, df.description));
|
|
334
|
-
let
|
|
334
|
+
let compositeIndexes = Stdlib_Option.getOr(entityJson.compositeIndices, []).map(ci => ci.map(f => ({
|
|
335
335
|
fieldName: f.fieldName,
|
|
336
336
|
direction: f.direction === "Asc" ? "Asc" : "Desc"
|
|
337
337
|
})));
|
|
338
|
-
let table = Table.mkTable(entityName,
|
|
338
|
+
let table = Table.mkTable(entityName, compositeIndexes, fields.concat(derivedFields), entityJson.description);
|
|
339
339
|
let getApiFieldName = prop => {
|
|
340
340
|
let match = prop.linkedEntity;
|
|
341
341
|
if (match !== undefined) {
|
package/src/CrossChainState.res
CHANGED
|
@@ -10,6 +10,10 @@ type t = {
|
|
|
10
10
|
chainIds: array<int>,
|
|
11
11
|
// True once every chain has caught up to head/endBlock. Monotonic during a run.
|
|
12
12
|
mutable isRealtime: bool,
|
|
13
|
+
// True once every chain has caught up and there's nothing left to process,
|
|
14
|
+
// but before the deferred schema indexes and `ready_at` are committed. The
|
|
15
|
+
// gap between this and `isRealtime` is the FinalizingIndexes phase.
|
|
16
|
+
mutable isCaughtUp: bool,
|
|
13
17
|
mutable isInReorgThreshold: bool,
|
|
14
18
|
// Indexer-wide fetch buffer pool (item count), shared across all chains.
|
|
15
19
|
targetBufferSize: int,
|
|
@@ -32,6 +36,7 @@ let make = (
|
|
|
32
36
|
chainStates,
|
|
33
37
|
chainIds: chainStates->Dict.valuesToArray->Array.map(cs => (cs->ChainState.chainConfig).id),
|
|
34
38
|
isRealtime,
|
|
39
|
+
isCaughtUp: isRealtime,
|
|
35
40
|
isInReorgThreshold,
|
|
36
41
|
targetBufferSize,
|
|
37
42
|
}
|
|
@@ -46,6 +51,7 @@ let getChainState = (crossChainState: t, chainId) =>
|
|
|
46
51
|
|
|
47
52
|
let chainStates = (crossChainState: t) => crossChainState.chainStates
|
|
48
53
|
let isRealtime = (crossChainState: t) => crossChainState.isRealtime
|
|
54
|
+
let isCaughtUp = (crossChainState: t) => crossChainState.isCaughtUp
|
|
49
55
|
let isInReorgThreshold = (crossChainState: t) => crossChainState.isInReorgThreshold
|
|
50
56
|
let targetBufferSize = (crossChainState: t) => crossChainState.targetBufferSize
|
|
51
57
|
|
|
@@ -129,10 +135,11 @@ let enterReorgThreshold = (crossChainState: t) => {
|
|
|
129
135
|
crossChainState.isInReorgThreshold = true
|
|
130
136
|
}
|
|
131
137
|
|
|
132
|
-
// Commit each progressed chain's batch progress, then
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
//
|
|
138
|
+
// Commit each progressed chain's batch progress, then record whether the whole
|
|
139
|
+
// indexer has caught up (every chain reached endblock or processed to head,
|
|
140
|
+
// with no processable events left). Catching up doesn't make the indexer ready:
|
|
141
|
+
// the deferred schema indexes still have to be created, which `markReady`
|
|
142
|
+
// below concludes once they're committed.
|
|
136
143
|
let applyBatchProgress = (crossChainState: t, ~batch: Batch.t, ~blockTimestampName: string) => {
|
|
137
144
|
let chainIds = crossChainState.chainIds
|
|
138
145
|
|
|
@@ -145,15 +152,14 @@ let applyBatchProgress = (crossChainState: t, ~batch: Batch.t, ~blockTimestampNa
|
|
|
145
152
|
}
|
|
146
153
|
}
|
|
147
154
|
|
|
148
|
-
|
|
155
|
+
crossChainState.isCaughtUp =
|
|
156
|
+
crossChainState.isCaughtUp || (crossChainState->nextItemIsNone && everyChainCaughtUp.contents)
|
|
149
157
|
|
|
158
|
+
// A run resumed with every chain already stamped ready needs no finalize —
|
|
159
|
+
// the indexes were committed together with those stamps.
|
|
150
160
|
let allChainsReady = ref(true)
|
|
151
161
|
for i in 0 to chainIds->Array.length - 1 {
|
|
152
|
-
|
|
153
|
-
if indexerCaughtUp {
|
|
154
|
-
cs->ChainState.markReady
|
|
155
|
-
}
|
|
156
|
-
if !(cs->ChainState.isReady) {
|
|
162
|
+
if !(crossChainState->getChainState(chainIds->Array.getUnsafe(i))->ChainState.isReady) {
|
|
157
163
|
allChainsReady := false
|
|
158
164
|
}
|
|
159
165
|
}
|
|
@@ -161,6 +167,23 @@ let applyBatchProgress = (crossChainState: t, ~batch: Batch.t, ~blockTimestampNa
|
|
|
161
167
|
crossChainState.isRealtime = crossChainState.isRealtime || allChainsReady.contents
|
|
162
168
|
}
|
|
163
169
|
|
|
170
|
+
// Enter the FinalizingIndexes phase without a batch. Used when the loop finds
|
|
171
|
+
// every chain already processed to its endblock — a resume that has nothing
|
|
172
|
+
// left to do still owes the schema its deferred indexes before it reports ready.
|
|
173
|
+
let markCaughtUp = (crossChainState: t) => crossChainState.isCaughtUp = true
|
|
174
|
+
|
|
175
|
+
// Concludes the FinalizingIndexes phase: stamps every chain with the `ready_at`
|
|
176
|
+
// already committed alongside the deferred schema indexes and switches the
|
|
177
|
+
// indexer to realtime.
|
|
178
|
+
let markReady = (crossChainState: t, ~readyAt) => {
|
|
179
|
+
for i in 0 to crossChainState.chainIds->Array.length - 1 {
|
|
180
|
+
crossChainState
|
|
181
|
+
->getChainState(crossChainState.chainIds->Array.getUnsafe(i))
|
|
182
|
+
->ChainState.markReady(~readyAt)
|
|
183
|
+
}
|
|
184
|
+
crossChainState.isRealtime = true
|
|
185
|
+
}
|
|
186
|
+
|
|
164
187
|
// --- Fetch control. ---
|
|
165
188
|
|
|
166
189
|
// Chains ordered furthest-behind first by fetch-frontier progress, so the
|
|
@@ -27,6 +27,7 @@ function make(chainStates, isInReorgThreshold, isRealtime, targetBufferSizeOpt)
|
|
|
27
27
|
chainStates: chainStates,
|
|
28
28
|
chainIds: Object.values(chainStates).map(cs => ChainState.chainConfig(cs).id),
|
|
29
29
|
isRealtime: isRealtime,
|
|
30
|
+
isCaughtUp: isRealtime,
|
|
30
31
|
isInReorgThreshold: isInReorgThreshold,
|
|
31
32
|
targetBufferSize: targetBufferSize
|
|
32
33
|
};
|
|
@@ -40,6 +41,10 @@ function isRealtime(crossChainState) {
|
|
|
40
41
|
return crossChainState.isRealtime;
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
function isCaughtUp(crossChainState) {
|
|
45
|
+
return crossChainState.isCaughtUp;
|
|
46
|
+
}
|
|
47
|
+
|
|
43
48
|
function isInReorgThreshold(crossChainState) {
|
|
44
49
|
return crossChainState.isInReorgThreshold;
|
|
45
50
|
}
|
|
@@ -104,20 +109,27 @@ function applyBatchProgress(crossChainState, batch, blockTimestampName) {
|
|
|
104
109
|
everyChainCaughtUp = false;
|
|
105
110
|
}
|
|
106
111
|
}
|
|
107
|
-
|
|
112
|
+
crossChainState.isCaughtUp = crossChainState.isCaughtUp || nextItemIsNone(crossChainState) && everyChainCaughtUp;
|
|
108
113
|
let allChainsReady = true;
|
|
109
114
|
for (let i$1 = 0, i_finish$1 = chainIds.length; i$1 < i_finish$1; ++i$1) {
|
|
110
|
-
|
|
111
|
-
if (indexerCaughtUp) {
|
|
112
|
-
ChainState.markReady(cs$1);
|
|
113
|
-
}
|
|
114
|
-
if (!ChainState.isReady(cs$1)) {
|
|
115
|
+
if (!ChainState.isReady(crossChainState.chainStates[chainIds[i$1]])) {
|
|
115
116
|
allChainsReady = false;
|
|
116
117
|
}
|
|
117
118
|
}
|
|
118
119
|
crossChainState.isRealtime = crossChainState.isRealtime || allChainsReady;
|
|
119
120
|
}
|
|
120
121
|
|
|
122
|
+
function markCaughtUp(crossChainState) {
|
|
123
|
+
crossChainState.isCaughtUp = true;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function markReady(crossChainState, readyAt) {
|
|
127
|
+
for (let i = 0, i_finish = crossChainState.chainIds.length; i < i_finish; ++i) {
|
|
128
|
+
ChainState.markReady(crossChainState.chainStates[crossChainState.chainIds[i]], readyAt);
|
|
129
|
+
}
|
|
130
|
+
crossChainState.isRealtime = true;
|
|
131
|
+
}
|
|
132
|
+
|
|
121
133
|
function priorityOrder(crossChainState) {
|
|
122
134
|
return Object.values(crossChainState.chainStates).toSorted((a, b) => Primitive_float.compare(ChainState.frontierProgress(a), ChainState.frontierProgress(b)));
|
|
123
135
|
}
|
|
@@ -222,6 +234,7 @@ export {
|
|
|
222
234
|
make,
|
|
223
235
|
chainStates,
|
|
224
236
|
isRealtime,
|
|
237
|
+
isCaughtUp,
|
|
225
238
|
isInReorgThreshold,
|
|
226
239
|
targetBufferSize,
|
|
227
240
|
nextItemIsNone,
|
|
@@ -229,6 +242,8 @@ export {
|
|
|
229
242
|
createBatch,
|
|
230
243
|
enterReorgThreshold,
|
|
231
244
|
applyBatchProgress,
|
|
245
|
+
markCaughtUp,
|
|
246
|
+
markReady,
|
|
232
247
|
priorityOrder,
|
|
233
248
|
checkAndFetch,
|
|
234
249
|
}
|
package/src/CrossChainState.resi
CHANGED
|
@@ -15,6 +15,7 @@ let make: (
|
|
|
15
15
|
// Accessors.
|
|
16
16
|
let chainStates: t => dict<ChainState.t>
|
|
17
17
|
let isRealtime: t => bool
|
|
18
|
+
let isCaughtUp: t => bool
|
|
18
19
|
let isInReorgThreshold: t => bool
|
|
19
20
|
let targetBufferSize: t => int
|
|
20
21
|
|
|
@@ -31,6 +32,8 @@ let createBatch: (
|
|
|
31
32
|
) => Batch.t
|
|
32
33
|
let enterReorgThreshold: t => unit
|
|
33
34
|
let applyBatchProgress: (t, ~batch: Batch.t, ~blockTimestampName: string) => unit
|
|
35
|
+
let markCaughtUp: t => unit
|
|
36
|
+
let markReady: (t, ~readyAt: Date.t) => unit
|
|
34
37
|
|
|
35
38
|
// Fetch control.
|
|
36
39
|
let priorityOrder: t => array<ChainState.t>
|