envio 3.13.0-rc.0 → 3.13.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "envio",
3
- "version": "3.13.0-rc.0",
3
+ "version": "3.13.0",
4
4
  "type": "module",
5
5
  "description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
6
6
  "bin": "./bin.mjs",
@@ -66,10 +66,10 @@
66
66
  "tsx": "4.21.0"
67
67
  },
68
68
  "optionalDependencies": {
69
- "envio-linux-x64": "3.13.0-rc.0",
70
- "envio-linux-x64-musl": "3.13.0-rc.0",
71
- "envio-linux-arm64": "3.13.0-rc.0",
72
- "envio-darwin-x64": "3.13.0-rc.0",
73
- "envio-darwin-arm64": "3.13.0-rc.0"
69
+ "envio-linux-x64": "3.13.0",
70
+ "envio-linux-x64-musl": "3.13.0",
71
+ "envio-linux-arm64": "3.13.0",
72
+ "envio-darwin-x64": "3.13.0",
73
+ "envio-darwin-arm64": "3.13.0"
74
74
  }
75
75
  }
@@ -159,8 +159,6 @@ and processNextBatch = async (state: IndexerState.t, ~scheduleFetch): unit => {
159
159
  // Can safely reset rollback state, since overwrite is not possible.
160
160
  state->IndexerState.clearRollback
161
161
  state->IndexerState.applyBatchProgress(~batch)
162
- // Before the finalize below, so a chain says where it finished ahead of
163
- // the process saying what it does about that.
164
162
  state->IndexerState.reportFinished
165
163
 
166
164
  // Backfilling → FinalizingIndexes → Ready. Awaiting here holds the
@@ -426,7 +426,6 @@ let getLatestValidScannedBlock = (cs: t, ~blockStore: BlockStore.t, ~blockNumber
426
426
  ->BlockStore.latestValidBlockFromStore(blockStore, blockNumbers)
427
427
  ->Null.toOption
428
428
  let safeCheckpointTracking = (cs: t) => cs.safeCheckpointTracking
429
- let isProgressAtHead = (cs: t) => cs.isProgressAtHead
430
429
  let committedProgressBlockNumber = (cs: t) => cs.committedProgressBlockNumber
431
430
  let committedProgressBlockTime = (cs: t) => cs.committedProgressBlockTime
432
431
  let numEventsProcessed = (cs: t) => cs.numEventsProcessed
@@ -660,30 +659,6 @@ let hasProcessedToEndblock = (cs: t) => {
660
659
  }
661
660
  }
662
661
 
663
- // Where this chain has finished indexing, the first time it gets there.
664
- // `EndBlock` is terminal: the chain indexed everything it was configured to.
665
- // `Backfill` is the rest of the history, up to the point where blocks can
666
- // still be reorged, which is as far as a chain indexes before the indexer
667
- // crosses into them.
668
- type finished = EndBlock(int) | Backfill(int)
669
-
670
- let takeFinished = (cs: t) =>
671
- if cs.reportedFinished {
672
- None
673
- } else {
674
- switch (cs.fetchState.endBlock, cs->hasProcessedToEndblock, cs.isProgressAtHead) {
675
- | (Some(endBlock), true, _) => {
676
- cs.reportedFinished = true
677
- Some(EndBlock(endBlock))
678
- }
679
- | (_, _, true) => {
680
- cs.reportedFinished = true
681
- Some(Backfill(cs.committedProgressBlockNumber))
682
- }
683
- | _ => None
684
- }
685
- }
686
-
687
662
  // Caught up as judged by persisted values alone: progress reached the endBlock,
688
663
  // or the head the previous run had already observed (less the lag that holds the
689
664
  // tip back). Unlike `isFetchingAtHead` this doesn't move when a fresh height
@@ -708,6 +683,45 @@ let isDurablyCaughtUp = (cs: t) => {
708
683
  atEndBlock || atHead
709
684
  }
710
685
 
686
+ // This chain has indexed everything there was to index: it reached its end
687
+ // block, or its progress reached the head. The single reading of that, for
688
+ // everything that turns on it — what the chain reports, and whether the whole
689
+ // process may finalize.
690
+ //
691
+ // Both spellings, because neither covers the other: `isProgressAtHead` latches
692
+ // the moment a batch's progress met the head known when it was created, which a
693
+ // head that has since moved on would read as behind; `isDurablyCaughtUp` judges
694
+ // the head as it stands, which is all a run resumed at the head has — it has no
695
+ // batch to latch anything.
696
+ let hasCaughtUp = (cs: t) => cs.isProgressAtHead || cs->isDurablyCaughtUp
697
+
698
+ // Where this chain has finished indexing, the first time it gets there.
699
+ // `EndBlock` is terminal: the chain indexed everything it was configured to.
700
+ // `Backfill` is the rest of the history, up to the point where blocks can
701
+ // still be reorged, which is as far as a chain indexes before the indexer
702
+ // crosses into them.
703
+ type finished = EndBlock(int) | Backfill(int)
704
+
705
+ let takeFinished = (cs: t) =>
706
+ if cs.reportedFinished {
707
+ None
708
+ } else {
709
+ switch (cs.fetchState.endBlock, cs->hasProcessedToEndblock, cs->hasCaughtUp) {
710
+ | (Some(endBlock), true, _) => {
711
+ cs.reportedFinished = true
712
+ Some(EndBlock(endBlock))
713
+ }
714
+ | (_, _, true) => {
715
+ cs.reportedFinished = true
716
+ // Finishing a backfill happens in a run, and a chain resumed with its
717
+ // `ready_at` already committed did none in this one. Being at the end
718
+ // block, above, stays true across runs and is said on every one.
719
+ cs->isReady ? None : Some(Backfill(cs.committedProgressBlockNumber))
720
+ }
721
+ | _ => None
722
+ }
723
+ }
724
+
711
725
  let getHighestBlockBelowThreshold = (cs: t): int => {
712
726
  let highestBlockBelowThreshold = cs.fetchState.knownHeight - cs.maxReorgDepth
713
727
  highestBlockBelowThreshold < 0 ? 0 : highestBlockBelowThreshold
@@ -220,10 +220,6 @@ function safeCheckpointTracking(cs) {
220
220
  return cs.safeCheckpointTracking;
221
221
  }
222
222
 
223
- function isProgressAtHead(cs) {
224
- return cs.isProgressAtHead;
225
- }
226
-
227
223
  function committedProgressBlockNumber(cs) {
228
224
  return cs.committedProgressBlockNumber;
229
225
  }
@@ -421,13 +417,29 @@ function hasProcessedToEndblock(cs) {
421
417
  }
422
418
  }
423
419
 
420
+ function hasCaughtUp(cs) {
421
+ if (cs.isProgressAtHead) {
422
+ return true;
423
+ } else {
424
+ let fetchState = cs.fetchState;
425
+ let committedProgressBlockNumber = cs.committedProgressBlockNumber;
426
+ let atEndBlock = Stdlib_Option.mapOr(fetchState.endBlock, false, endBlock => committedProgressBlockNumber >= endBlock);
427
+ let atHead = fetchState.knownHeight > 0 && committedProgressBlockNumber >= Primitive_int.max(0, fetchState.knownHeight - cs.chainConfig.blockLag | 0);
428
+ if (atEndBlock) {
429
+ return true;
430
+ } else {
431
+ return atHead;
432
+ }
433
+ }
434
+ }
435
+
424
436
  function takeFinished(cs) {
425
437
  if (cs.reportedFinished) {
426
438
  return;
427
439
  }
428
440
  let match = cs.fetchState.endBlock;
429
441
  let match$1 = hasProcessedToEndblock(cs);
430
- let match$2 = cs.isProgressAtHead;
442
+ let match$2 = hasCaughtUp(cs);
431
443
  if (match !== undefined && match$1) {
432
444
  cs.reportedFinished = true;
433
445
  return {
@@ -437,22 +449,14 @@ function takeFinished(cs) {
437
449
  }
438
450
  if (match$2) {
439
451
  cs.reportedFinished = true;
440
- return {
441
- TAG: "Backfill",
442
- _0: cs.committedProgressBlockNumber
443
- };
444
- }
445
- }
446
-
447
- function isDurablyCaughtUp(cs) {
448
- let fetchState = cs.fetchState;
449
- let committedProgressBlockNumber = cs.committedProgressBlockNumber;
450
- let atEndBlock = Stdlib_Option.mapOr(fetchState.endBlock, false, endBlock => committedProgressBlockNumber >= endBlock);
451
- let atHead = fetchState.knownHeight > 0 && committedProgressBlockNumber >= Primitive_int.max(0, fetchState.knownHeight - cs.chainConfig.blockLag | 0);
452
- if (atEndBlock) {
453
- return true;
454
- } else {
455
- return atHead;
452
+ if (cs.timestampCaughtUpToHeadOrEndblock !== undefined) {
453
+ return;
454
+ } else {
455
+ return {
456
+ TAG: "Backfill",
457
+ _0: cs.committedProgressBlockNumber
458
+ };
459
+ }
456
460
  }
457
461
  }
458
462
 
@@ -946,7 +950,6 @@ export {
946
950
  getReorgThresholdBlockNumbersBelow,
947
951
  getLatestValidScannedBlock,
948
952
  safeCheckpointTracking,
949
- isProgressAtHead,
950
953
  committedProgressBlockNumber,
951
954
  committedProgressBlockTime,
952
955
  numEventsProcessed,
@@ -980,7 +983,7 @@ export {
980
983
  reorgThresholdLiftsCeiling,
981
984
  reorgThresholdEntryMessage,
982
985
  hasProcessedToEndblock,
983
- isDurablyCaughtUp,
986
+ hasCaughtUp,
984
987
  getHighestBlockBelowThreshold,
985
988
  isActivelyIndexing,
986
989
  isReady,
@@ -70,7 +70,6 @@ let getLatestValidScannedBlock: (
70
70
  ~blockNumbers: array<int>,
71
71
  ) => option<int>
72
72
  let safeCheckpointTracking: t => option<SafeCheckpointTracking.t>
73
- let isProgressAtHead: t => bool
74
73
  let committedProgressBlockNumber: t => int
75
74
  let committedProgressBlockTime: t => option<int>
76
75
  let numEventsProcessed: t => float
@@ -132,7 +131,7 @@ let takeFinished: t => option<finished>
132
131
  let reorgThresholdLiftsCeiling: t => bool
133
132
  let reorgThresholdEntryMessage: string
134
133
  let hasProcessedToEndblock: t => bool
135
- let isDurablyCaughtUp: t => bool
134
+ let hasCaughtUp: t => bool
136
135
  let getHighestBlockBelowThreshold: t => int
137
136
  let isActivelyIndexing: t => bool
138
137
  let isReady: t => bool
@@ -199,7 +199,7 @@ let applyBatchProgress = (crossChainState: t, ~batch: Batch.t, ~blockTimestampNa
199
199
  for i in 0 to chainIds->Array.length - 1 {
200
200
  let cs = crossChainState->getChainState(chainIds->Array.getUnsafe(i))
201
201
  cs->ChainState.applyBatchProgress(~batch, ~blockTimestampName)
202
- if !(cs->ChainState.hasProcessedToEndblock || cs->ChainState.isProgressAtHead) {
202
+ if !(cs->ChainState.hasCaughtUp) {
203
203
  everyChainCaughtUp := false
204
204
  }
205
205
  }
@@ -254,7 +254,7 @@ let markCaughtUpOnResume = (crossChainState: t) => {
254
254
  let everyChainCaughtUp = ref(crossChainState.chainIds->Array.length > 0)
255
255
  for i in 0 to crossChainState.chainIds->Array.length - 1 {
256
256
  let cs = crossChainState->getChainState(crossChainState.chainIds->Array.getUnsafe(i))
257
- if !(cs->ChainState.isDurablyCaughtUp) {
257
+ if !(cs->ChainState.hasCaughtUp) {
258
258
  everyChainCaughtUp := false
259
259
  }
260
260
  }
@@ -284,11 +284,19 @@ let markReady = (crossChainState: t, ~readyAt) => {
284
284
  crossChainState.isRealtime = true
285
285
  }
286
286
 
287
+ // What a caught-up chain is waiting on before its process can finalize: the run
288
+ // holding the process back, or another chain it drives still behind. Read when
289
+ // the chain speaks, so "waiting" is said only while it is true.
290
+ let isWaitingOnOthers = (crossChainState: t, cs: ChainState.t) =>
291
+ crossChainState.holdRealtime ||
292
+ crossChainState.chainStates
293
+ ->Dict.valuesToArray
294
+ ->Array.some(other => other !== cs && !(other->ChainState.hasCaughtUp))
295
+
287
296
  // Each chain that has just finished indexing, said once, by the chain it is
288
297
  // about — so a chain that finishes early says so then, rather than when the
289
298
  // last chain in its process catches up.
290
- let reportFinished = (crossChainState: t) => {
291
- let waitingOnOthers = crossChainState.holdRealtime || crossChainState.chainIds->Array.length > 1
299
+ let reportFinished = (crossChainState: t) =>
292
300
  crossChainState.chainStates
293
301
  ->Dict.valuesToArray
294
302
  ->Array.forEach(cs =>
@@ -301,7 +309,7 @@ let reportFinished = (crossChainState: t) => {
301
309
  cs
302
310
  ->ChainState.logger
303
311
  ->Logging.childInfo({
304
- "msg": waitingOnOthers
312
+ "msg": crossChainState->isWaitingOnOthers(cs)
305
313
  ? "Finished backfill. Waiting for the other chains."
306
314
  : "Finished backfill.",
307
315
  "block": block,
@@ -309,7 +317,6 @@ let reportFinished = (crossChainState: t) => {
309
317
  | None => ()
310
318
  }
311
319
  )
312
- }
313
320
 
314
321
  // --- Fetch control. ---
315
322
 
@@ -141,7 +141,7 @@ function applyBatchProgress(crossChainState, batch, blockTimestampName) {
141
141
  for (let i = 0, i_finish = chainIds.length; i < i_finish; ++i) {
142
142
  let cs = crossChainState.chainStates[chainIds[i]];
143
143
  ChainState.applyBatchProgress(cs, batch, blockTimestampName);
144
- if (!(ChainState.hasProcessedToEndblock(cs) || ChainState.isProgressAtHead(cs))) {
144
+ if (!ChainState.hasCaughtUp(cs)) {
145
145
  everyChainCaughtUp = false;
146
146
  }
147
147
  }
@@ -169,7 +169,7 @@ function markCaughtUpOnResume(crossChainState) {
169
169
  let everyChainCaughtUp = crossChainState.chainIds.length !== 0;
170
170
  for (let i = 0, i_finish = crossChainState.chainIds.length; i < i_finish; ++i) {
171
171
  let cs = crossChainState.chainStates[crossChainState.chainIds[i]];
172
- if (!ChainState.isDurablyCaughtUp(cs)) {
172
+ if (!ChainState.hasCaughtUp(cs)) {
173
173
  everyChainCaughtUp = false;
174
174
  }
175
175
  }
@@ -191,8 +191,21 @@ function markReady(crossChainState, readyAt) {
191
191
  crossChainState.isRealtime = true;
192
192
  }
193
193
 
194
+ function isWaitingOnOthers(crossChainState, cs) {
195
+ if (crossChainState.holdRealtime) {
196
+ return true;
197
+ } else {
198
+ return Object.values(crossChainState.chainStates).some(other => {
199
+ if (other !== cs) {
200
+ return !ChainState.hasCaughtUp(other);
201
+ } else {
202
+ return false;
203
+ }
204
+ });
205
+ }
206
+ }
207
+
194
208
  function reportFinished(crossChainState) {
195
- let waitingOnOthers = crossChainState.holdRealtime || crossChainState.chainIds.length > 1;
196
209
  Object.values(crossChainState.chainStates).forEach(cs => {
197
210
  let match = ChainState.takeFinished(cs);
198
211
  if (match !== undefined) {
@@ -203,7 +216,7 @@ function reportFinished(crossChainState) {
203
216
  });
204
217
  } else {
205
218
  return Logging.childInfo(ChainState.logger(cs), {
206
- msg: waitingOnOthers ? "Finished backfill. Waiting for the other chains." : "Finished backfill.",
219
+ msg: isWaitingOnOthers(crossChainState, cs) ? "Finished backfill. Waiting for the other chains." : "Finished backfill.",
207
220
  block: match._0
208
221
  });
209
222
  }
@@ -11,15 +11,25 @@
11
11
  // `envio start --chain` process is indexing and never waits on one.
12
12
 
13
13
  let runOnce = async (state: IndexerState.t) => {
14
+ let chainIds = state->IndexerState.crossChainState->CrossChainState.chainIds
15
+
16
+ // Whatever brought the process here — a batch, a tick that progressed
17
+ // nothing, a supervisor's release — its chains say where they finished
18
+ // before it says what it does about that. Said here rather than left to each
19
+ // caller to order: a chain that has already spoken says nothing again.
20
+ state->IndexerState.reportFinished
21
+
14
22
  // Said by the process rather than by each of its chains: the indexes are one
15
23
  // build over the tables, and the pause is the whole process's. A chain has
16
24
  // already said it caught up, and says it is ready once this commits. The
17
25
  // chains are named because the pause is theirs, and a split run has a process
18
- // saying this for each part of it.
19
- Logging.info({
20
- "msg": "Building database indexes. Indexing is paused until they are ready, which can take a while on a large database.",
21
- "chainIds": state->IndexerState.crossChainState->CrossChainState.chainIds,
22
- })
26
+ // saying this for each part of it — one of them, for a process driving a
27
+ // single chain.
28
+ let msg = "Building database indexes. Indexing is paused until they are ready, which can take a while on a large database."
29
+ switch chainIds {
30
+ | [chainId] => Logging.info({"msg": msg, "chainId": chainId})
31
+ | chainIds => Logging.info({"msg": msg, "chainIds": chainIds})
32
+ }
23
33
 
24
34
  await Writing.flush(state)
25
35
 
@@ -30,11 +40,7 @@ let runOnce = async (state: IndexerState.t) => {
30
40
  let storage = persistence->Persistence.getInitializedStorageOrThrow
31
41
  let readyAt = Date.make()
32
42
 
33
- await storage.finalizeBackfill(
34
- ~entities=persistence.allEntities,
35
- ~chainIds=state->IndexerState.crossChainState->CrossChainState.chainIds,
36
- ~readyAt,
37
- )
43
+ await storage.finalizeBackfill(~entities=persistence.allEntities, ~chainIds, ~readyAt)
38
44
 
39
45
  // Only after the commit: in-memory readiness must never run ahead of the
40
46
  // `ready_at` a restart would read back.
@@ -7,10 +7,21 @@ import * as IndexerState from "./IndexerState.res.mjs";
7
7
  import * as CrossChainState from "./CrossChainState.res.mjs";
8
8
 
9
9
  async function runOnce(state) {
10
- Logging.info({
11
- msg: "Building database indexes. Indexing is paused until they are ready, which can take a while on a large database.",
12
- chainIds: CrossChainState.chainIds(IndexerState.crossChainState(state))
13
- });
10
+ let chainIds = CrossChainState.chainIds(IndexerState.crossChainState(state));
11
+ IndexerState.reportFinished(state);
12
+ let msg = "Building database indexes. Indexing is paused until they are ready, which can take a while on a large database.";
13
+ if (chainIds.length !== 1) {
14
+ Logging.info({
15
+ msg: msg,
16
+ chainIds: chainIds
17
+ });
18
+ } else {
19
+ let chainId = chainIds[0];
20
+ Logging.info({
21
+ msg: msg,
22
+ chainId: chainId
23
+ });
24
+ }
14
25
  await Writing.flush(state);
15
26
  if (IndexerState.hasFailedWrite(state)) {
16
27
  return;
@@ -18,7 +29,7 @@ async function runOnce(state) {
18
29
  let persistence = IndexerState.persistence(state);
19
30
  let storage = Persistence.getInitializedStorageOrThrow(persistence);
20
31
  let readyAt = new Date();
21
- await storage.finalizeBackfill(persistence.allEntities, CrossChainState.chainIds(IndexerState.crossChainState(state)), readyAt);
32
+ await storage.finalizeBackfill(persistence.allEntities, chainIds, readyAt);
22
33
  return IndexerState.markReady(state, readyAt);
23
34
  }
24
35
 
@@ -254,13 +254,16 @@ let makeInitialState = (
254
254
  addressRowsByChain
255
255
  ->Utils.Dict.dangerouslyGetNonOption(chainIdStr)
256
256
  ->Option.getOr(AddressRows.emptySeedRows())
257
+ // The pinned startBlock resumes the chain rather than moving its start, so
258
+ // contract start blocks and onBlock intervals stay what config.yaml says.
259
+ // https://github.com/enviodev/hyperindex/issues/1656
257
260
  {
258
261
  Persistence.id: chain,
259
- startBlock: processChainConfig.startBlock,
262
+ startBlock: config.chainMap->ChainMap.get(chain)->Config.startBlockOrZero,
260
263
  endBlock: processChainConfig.endBlock,
261
264
  sourceBlockNumber: processChainConfig.endBlock->Option.getOr(0),
262
265
  maxReorgDepth: 0, // No reorg support in test indexer
263
- progressBlockNumber: -1,
266
+ progressBlockNumber: processChainConfig.startBlock - 1,
264
267
  progressBlockTime: None,
265
268
  numEventsProcessed: 0.,
266
269
  firstEventBlockNumber: None,
@@ -180,10 +180,10 @@ function makeInitialState(config, processConfigChains, addressRowsByChain, contr
180
180
  let addressRows = Stdlib_Option.getOr(addressRowsByChain[chainIdStr], AddressRows.emptySeedRows());
181
181
  return {
182
182
  id: chain,
183
- startBlock: processChainConfig.startBlock,
183
+ startBlock: Config.startBlockOrZero(ChainMap.get(config.chainMap, chain)),
184
184
  endBlock: processChainConfig.endBlock,
185
185
  maxReorgDepth: 0,
186
- progressBlockNumber: -1,
186
+ progressBlockNumber: processChainConfig.startBlock - 1 | 0,
187
187
  progressBlockTime: undefined,
188
188
  numEventsProcessed: 0,
189
189
  firstEventBlockNumber: undefined,