envio 3.11.0 → 3.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/index.d.ts +3 -0
  2. package/package.json +6 -6
  3. package/src/Batch.res +111 -78
  4. package/src/Batch.res.mjs +53 -46
  5. package/src/BatchProcessing.res +1 -11
  6. package/src/BatchProcessing.res.mjs +1 -4
  7. package/src/ChainFetching.res +7 -3
  8. package/src/ChainFetching.res.mjs +1 -1
  9. package/src/ChainState.res +82 -20
  10. package/src/ChainState.res.mjs +51 -13
  11. package/src/ChainState.resi +6 -1
  12. package/src/Config.res +58 -37
  13. package/src/Config.res.mjs +63 -53
  14. package/src/CrossChainState.res +50 -53
  15. package/src/CrossChainState.res.mjs +25 -23
  16. package/src/CrossChainState.resi +6 -13
  17. package/src/EffectState.res +13 -4
  18. package/src/EffectState.resi +7 -1
  19. package/src/EventProcessing.res +6 -6
  20. package/src/EventProcessing.res.mjs +6 -6
  21. package/src/FetchState.res +65 -46
  22. package/src/FetchState.res.mjs +68 -36
  23. package/src/FinalizeBackfill.res +16 -12
  24. package/src/FinalizeBackfill.res.mjs +3 -3
  25. package/src/HistoryPolicy.res +46 -0
  26. package/src/HistoryPolicy.res.mjs +50 -0
  27. package/src/InMemoryStore.res +30 -32
  28. package/src/InMemoryStore.res.mjs +33 -28
  29. package/src/InMemoryTable.res +155 -67
  30. package/src/InMemoryTable.res.mjs +151 -60
  31. package/src/IndexerState.res +66 -45
  32. package/src/IndexerState.res.mjs +57 -29
  33. package/src/IndexerState.resi +8 -13
  34. package/src/Internal.res +4 -2
  35. package/src/LoadLayer.res +68 -38
  36. package/src/LoadLayer.res.mjs +50 -64
  37. package/src/LoadLayer.resi +1 -1
  38. package/src/Main.res +25 -24
  39. package/src/Main.res.mjs +24 -25
  40. package/src/Metrics.res +49 -1
  41. package/src/Metrics.res.mjs +26 -3
  42. package/src/Persistence.res +55 -16
  43. package/src/Persistence.res.mjs +18 -3
  44. package/src/PgStorage.res +286 -180
  45. package/src/PgStorage.res.mjs +209 -122
  46. package/src/PruneStaleHistory.res +9 -15
  47. package/src/PruneStaleHistory.res.mjs +11 -20
  48. package/src/Rollback.res +9 -14
  49. package/src/Rollback.res.mjs +5 -3
  50. package/src/Sink.res +7 -5
  51. package/src/Sink.res.mjs +4 -4
  52. package/src/Staging.res +298 -0
  53. package/src/Staging.res.mjs +257 -0
  54. package/src/TestIndexer.res +14 -30
  55. package/src/TestIndexer.res.mjs +11 -9
  56. package/src/UserContext.res +20 -36
  57. package/src/UserContext.res.mjs +6 -9
  58. package/src/Utils.res +10 -4
  59. package/src/Utils.res.mjs +16 -16
  60. package/src/Writing.res +34 -20
  61. package/src/Writing.res.mjs +15 -20
  62. package/src/bindings/ClickHouse.res +41 -34
  63. package/src/bindings/ClickHouse.res.mjs +36 -30
  64. package/src/bindings/ClickHouseSink.res +45 -186
  65. package/src/bindings/ClickHouseSink.res.mjs +21 -174
  66. package/src/db/CheckpointSequence.res +109 -0
  67. package/src/db/CheckpointSequence.res.mjs +99 -0
  68. package/src/db/EntityFilter.res +487 -275
  69. package/src/db/EntityFilter.res.mjs +557 -309
  70. package/src/db/EntityHistory.res +6 -5
  71. package/src/db/EntityHistory.res.mjs +5 -5
  72. package/src/db/Frontier.res +86 -0
  73. package/src/db/Frontier.res.mjs +126 -0
  74. package/src/db/InternalTable.res +145 -27
  75. package/src/db/InternalTable.res.mjs +115 -29
  76. package/src/db/RollbackFloors.res +34 -41
  77. package/src/db/RollbackFloors.res.mjs +27 -71
  78. package/src/db/Table.res +21 -6
  79. package/src/db/Table.res.mjs +13 -4
  80. package/src/sources/BlockStore.res +8 -0
  81. package/src/sources/EvmHyperSyncSource.res +5 -1
  82. package/src/sources/EvmHyperSyncSource.res.mjs +4 -3
  83. package/src/sources/FuelHyperSyncSource.res +1 -0
  84. package/src/sources/FuelHyperSyncSource.res.mjs +1 -1
  85. package/src/sources/HyperSync.res +6 -0
  86. package/src/sources/HyperSync.res.mjs +5 -2
  87. package/src/sources/HyperSync.resi +2 -0
  88. package/src/sources/HyperSyncClient.res +6 -0
  89. package/src/sources/HyperSyncSSE.res +1 -1
  90. package/src/sources/HyperSyncSSE.res.mjs +4 -10
  91. package/src/sources/RequestStat.res +8 -1
  92. package/src/sources/RpcSource.res +1 -0
  93. package/src/sources/RpcSource.res.mjs +1 -1
  94. package/src/sources/SimulateSource.res +1 -0
  95. package/src/sources/SimulateSource.res.mjs +1 -1
  96. package/src/sources/Source.res +11 -3
  97. package/src/sources/SourceManager.res +34 -13
  98. package/src/sources/SourceManager.res.mjs +26 -9
  99. package/src/sources/SourceManager.resi +2 -0
  100. package/src/sources/SvmHyperSyncClient.res +5 -0
  101. package/src/sources/SvmHyperSyncSource.res +3 -0
  102. package/src/sources/SvmHyperSyncSource.res.mjs +4 -2
  103. package/src/tui/Tui.res +56 -143
  104. package/src/tui/Tui.res.mjs +70 -166
  105. package/src/tui/components/TuiData.res +61 -16
  106. package/src/tui/components/TuiData.res.mjs +53 -15
  107. package/svm.schema.json +0 -7
  108. package/src/db/CheckpointBounds.res +0 -53
  109. package/src/db/CheckpointBounds.res.mjs +0 -44
package/index.d.ts CHANGED
@@ -1216,6 +1216,9 @@ export type SvmAllTransactionFields = {
1216
1216
  readonly err: string | undefined;
1217
1217
  readonly fee: bigint;
1218
1218
  readonly computeUnitsConsumed: bigint | undefined;
1219
+ /** Every account the transaction resolves to: the static keys, then the
1220
+ * address lookup tables' writable then readonly addresses. A transaction's
1221
+ * account indexes address this list. */
1219
1222
  readonly accountKeys: readonly string[];
1220
1223
  readonly recentBlockhash: string;
1221
1224
  readonly version: string | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "envio",
3
- "version": "3.11.0",
3
+ "version": "3.12.1",
4
4
  "type": "module",
5
5
  "description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
6
6
  "bin": "./bin.mjs",
@@ -68,10 +68,10 @@
68
68
  "tsx": "4.21.0"
69
69
  },
70
70
  "optionalDependencies": {
71
- "envio-linux-x64": "3.11.0",
72
- "envio-linux-x64-musl": "3.11.0",
73
- "envio-linux-arm64": "3.11.0",
74
- "envio-darwin-x64": "3.11.0",
75
- "envio-darwin-arm64": "3.11.0"
71
+ "envio-linux-x64": "3.12.1",
72
+ "envio-linux-x64-musl": "3.12.1",
73
+ "envio-linux-arm64": "3.12.1",
74
+ "envio-darwin-x64": "3.12.1",
75
+ "envio-darwin-arm64": "3.12.1"
76
76
  }
77
77
  }
package/src/Batch.res CHANGED
@@ -4,6 +4,12 @@ open Utils.UnsafeIntOperators
4
4
  type chainAfterBatch = {
5
5
  batchSize: int,
6
6
  progressBlockNumber: int,
7
+ // Timestamp of `progressBlockNumber` itself, never of a lower block, so it
8
+ // measures how far behind chain time the committed progress is. `None` when
9
+ // the source didn't return that block's header - during backfill it only
10
+ // returns blocks carrying items, and an SVM slot may have produced no block
11
+ // at all.
12
+ progressBlockTime: option<int>,
7
13
  sourceBlockNumber: int,
8
14
  totalEventsProcessed: float,
9
15
  fetchState: FetchState.t,
@@ -23,6 +29,10 @@ type reorgHashSnapshot = {
23
29
  type chainBeforeBatch = {
24
30
  fetchState: FetchState.t,
25
31
  scannedHashes: reorgHashSnapshot,
32
+ // The progress block isn't known until the build computes it, so its
33
+ // timestamp can't be snapshotted with the rest. Reads the chain's store,
34
+ // which the synchronous build can't see mutate.
35
+ blockTimeAt: int => option<int>,
26
36
  shouldRollbackOnReorg: bool,
27
37
  progressBlockNumber: int,
28
38
  sourceBlockNumber: int,
@@ -34,24 +44,32 @@ type t = {
34
44
  totalBatchSize: int,
35
45
  items: array<Internal.item>,
36
46
  progressedChainsById: dict<chainAfterBatch>,
37
- // Processed inside the reorg threshold. Drives whether history is saved, so
38
- // writes never merge across a change in this value.
39
- isInReorgThreshold: bool,
47
+ // Whether the batch's rows get history. Writes never merge across a change in
48
+ // it, so a single write can't mix the two.
49
+ history: HistoryPolicy.t,
40
50
  // Unnest-like checkpoint fields:
41
51
  checkpointIds: array<bigint>,
42
52
  checkpointChainIds: array<ChainId.t>,
43
53
  checkpointBlockNumbers: array<int>,
44
54
  checkpointBlockHashes: array<Null.t<string>>,
55
+ // Items the checkpoint carries, which is what indexes `items`.
56
+ checkpointItemsCount: array<int>,
57
+ // Logs the checkpoint carries: one log routed to several registrations is one
58
+ // event, however many items it made.
45
59
  checkpointEventsProcessed: array<int>,
46
60
  registeredAddresses: array<AddressRows.staged>,
47
61
  }
48
62
 
63
+ // What a chain contributed to the batch: items taken off its buffer, and the
64
+ // logs behind them.
65
+ type chainBatchCounts = {size: int, eventsProcessed: int}
66
+
49
67
  let getProgressedChainsById = {
50
68
  let getChainAfterBatchIfProgressed = (
51
69
  ~chainBeforeBatch: chainBeforeBatch,
52
70
  ~progressBlockNumberAfterBatch,
53
71
  ~fetchStateAfterBatch,
54
- ~batchSize,
72
+ ~counts: chainBatchCounts,
55
73
  ) => {
56
74
  // The check is sufficient, since we guarantee to include a full block in a batch
57
75
  // Also, this might be true even if batchSize is 0,
@@ -60,10 +78,12 @@ let getProgressedChainsById = {
60
78
  Some(
61
79
  (
62
80
  {
63
- batchSize,
81
+ batchSize: counts.size,
64
82
  progressBlockNumber: progressBlockNumberAfterBatch,
83
+ progressBlockTime: chainBeforeBatch.blockTimeAt(progressBlockNumberAfterBatch),
65
84
  sourceBlockNumber: chainBeforeBatch.sourceBlockNumber,
66
- totalEventsProcessed: chainBeforeBatch.totalEventsProcessed +. batchSize->Int.toFloat,
85
+ totalEventsProcessed: chainBeforeBatch.totalEventsProcessed +.
86
+ counts.eventsProcessed->Int.toFloat,
67
87
  fetchState: fetchStateAfterBatch,
68
88
  isProgressAtHeadWhenBatchCreated: progressBlockNumberAfterBatch >=
69
89
  chainBeforeBatch.sourceBlockNumber - chainBeforeBatch.chainConfig.blockLag,
@@ -77,7 +97,7 @@ let getProgressedChainsById = {
77
97
 
78
98
  (
79
99
  ~chainsBeforeBatch: dict<chainBeforeBatch>,
80
- ~batchSizePerChain: dict<int>,
100
+ ~countsPerChain: dict<chainBatchCounts>,
81
101
  ~progressBlockNumberPerChain: dict<int>,
82
102
  ) => {
83
103
  let progressedChainsById = Dict.make()
@@ -98,14 +118,14 @@ let getProgressedChainsById = {
98
118
  | None => chainBeforeBatch.progressBlockNumber
99
119
  }
100
120
 
101
- switch switch batchSizePerChain->Utils.Dict.dangerouslyGetNonOption(
121
+ switch switch countsPerChain->Utils.Dict.dangerouslyGetNonOption(
102
122
  fetchState.chainId->ChainId.toString,
103
123
  ) {
104
- | Some(batchSize) =>
105
- let leftItems = fetchState.buffer->Array.slice(~start=batchSize)
124
+ | Some(counts) =>
125
+ let leftItems = fetchState.buffer->Array.slice(~start=counts.size)
106
126
  getChainAfterBatchIfProgressed(
107
127
  ~chainBeforeBatch,
108
- ~batchSize,
128
+ ~counts,
109
129
  ~fetchStateAfterBatch=fetchState->FetchState.updateInternal(~mutItems=leftItems),
110
130
  ~progressBlockNumberAfterBatch,
111
131
  )
@@ -113,7 +133,7 @@ let getProgressedChainsById = {
113
133
  | None =>
114
134
  getChainAfterBatchIfProgressed(
115
135
  ~chainBeforeBatch,
116
- ~batchSize=0,
136
+ ~counts={size: 0, eventsProcessed: 0},
117
137
  ~fetchStateAfterBatch=chainBeforeBatch.fetchState,
118
138
  ~progressBlockNumberAfterBatch,
119
139
  )
@@ -146,7 +166,7 @@ let seekFirstAbove = (blockNumbers: array<int>, blockNumber) => {
146
166
 
147
167
  @inline
148
168
  let addReorgCheckpoints = (
149
- ~prevCheckpointId,
169
+ ~cursor,
150
170
  ~scannedHashes: reorgHashSnapshot,
151
171
  ~shouldRollbackOnReorg,
152
172
  ~fromBlockExclusive,
@@ -156,10 +176,10 @@ let addReorgCheckpoints = (
156
176
  ~mutCheckpointChainIds,
157
177
  ~mutCheckpointBlockNumbers,
158
178
  ~mutCheckpointBlockHashes,
179
+ ~mutCheckpointItemsCount,
159
180
  ~mutCheckpointEventsProcessed,
160
181
  ) => {
161
182
  if shouldRollbackOnReorg {
162
- let prevCheckpointId = ref(prevCheckpointId)
163
183
  // The snapshot already holds only in-threshold scanned hashes, ascending,
164
184
  // so seeking to the gap's lower bound gives the gap checkpoints without
165
185
  // rescanning the whole snapshot for every gap in the batch.
@@ -172,37 +192,30 @@ let addReorgCheckpoints = (
172
192
  scannedHashes.hashByBlockNumber
173
193
  ->Utils.Dict.dangerouslyGetByIntNonOption(blockNumber)
174
194
  ->Option.getUnsafe
175
- let checkpointId = prevCheckpointId.contents->BigInt.add(1n)
176
- prevCheckpointId := checkpointId
195
+ let checkpointId = cursor->CheckpointSequence.next(~chainId)
177
196
 
178
197
  mutCheckpointIds->Array.push(checkpointId)
179
198
  mutCheckpointChainIds->Array.push(chainId)
180
199
  mutCheckpointBlockNumbers->Array.push(blockNumber)
181
200
  mutCheckpointBlockHashes->Array.push(Null.Value(hash))
201
+ mutCheckpointItemsCount->Array.push(0)
182
202
  mutCheckpointEventsProcessed->Array.push(0)
183
203
 
184
204
  idx := idx.contents + 1
185
205
  }
186
- prevCheckpointId.contents
187
- } else {
188
- prevCheckpointId
189
206
  }
190
207
  }
191
208
 
192
- // Checkpoint ids are handed out in ascending order as each chain's items are
193
- // walked in block order, so within a chain a higher id always means a later
194
- // block. Rollback preserves that: it deletes the chain's ids above its target
195
- // before any higher one is allocated, so the ids the re-indexed blocks get are
196
- // above everything the chain still holds. An isolated rollback leans on the
197
- // invariant — it deletes by `chain_id = c AND id > target`, which is only the
198
- // chain's stale suffix if ids and blocks agree on order within the chain. Ids
199
- // interleave freely *across* chains, which is exactly why that predicate can't
200
- // be the id bound alone.
201
- let prepareBatch = (
202
- ~checkpointIdBeforeBatch,
209
+ // Within a chain, ids ascend with block order, and rollback preserves that by
210
+ // deleting the chain's ids above its target before any higher one is allocated.
211
+ // Every rollback deletes by `id > target`, which is only the stale suffix while
212
+ // ids and blocks agree on order within each chain.
213
+ let make = (
214
+ ~sequence: CheckpointSequence.t,
215
+ ~frontier: Frontier.t,
203
216
  ~chainsBeforeBatch: dict<chainBeforeBatch>,
204
217
  ~batchSizeTarget,
205
- ~isInReorgThreshold,
218
+ ~history,
206
219
  ) => {
207
220
  let preparedFetchStates =
208
221
  chainsBeforeBatch
@@ -214,8 +227,8 @@ let prepareBatch = (
214
227
  let preparedNumber = preparedFetchStates->Array.length
215
228
  let totalBatchSize = ref(0)
216
229
 
217
- let prevCheckpointId = ref(checkpointIdBeforeBatch)
218
- let mutBatchSizePerChain = Dict.make()
230
+ let cursor = sequence->CheckpointSequence.cursor(~frontier)
231
+ let mutCountsPerChain = Dict.make()
219
232
  let mutProgressBlockNumberPerChain = Dict.make()
220
233
 
221
234
  let items = []
@@ -223,6 +236,7 @@ let prepareBatch = (
223
236
  let checkpointChainIds = []
224
237
  let checkpointBlockNumbers = []
225
238
  let checkpointBlockHashes = []
239
+ let checkpointItemsCount = []
226
240
  let checkpointEventsProcessed = []
227
241
 
228
242
  // Accumulate items for all actively indexing chains
@@ -241,29 +255,38 @@ let prepareBatch = (
241
255
  ->Option.getUnsafe
242
256
 
243
257
  let prevBlockNumber = ref(chainBeforeBatch.progressBlockNumber)
258
+ let chainEventsProcessed = ref(0)
244
259
  if chainBatchSize > 0 {
245
260
  for idx in 0 to chainBatchSize - 1 {
246
261
  let item = fetchState.buffer->Array.getUnsafe(idx)
247
262
  let blockNumber = item->Internal.getItemBlockNumber
263
+ // The buffer is sorted, so a log's items are consecutive: the first of
264
+ // them is the only one that counts as an event.
265
+ let isNewEvent =
266
+ idx === 0 ||
267
+ !(fetchState.buffer->Array.getUnsafe(idx - 1)->FetchState.isSameLog(item))
268
+ if isNewEvent {
269
+ chainEventsProcessed := chainEventsProcessed.contents + 1
270
+ }
248
271
 
249
272
  // Every new block we should create a new checkpoint
250
273
  if blockNumber !== prevBlockNumber.contents {
251
- prevCheckpointId :=
252
- addReorgCheckpoints(
253
- ~chainId=fetchState.chainId,
254
- ~scannedHashes=chainBeforeBatch.scannedHashes,
255
- ~shouldRollbackOnReorg=chainBeforeBatch.shouldRollbackOnReorg,
256
- ~prevCheckpointId=prevCheckpointId.contents,
257
- ~fromBlockExclusive=prevBlockNumber.contents,
258
- ~toBlockExclusive=blockNumber,
259
- ~mutCheckpointIds=checkpointIds,
260
- ~mutCheckpointChainIds=checkpointChainIds,
261
- ~mutCheckpointBlockNumbers=checkpointBlockNumbers,
262
- ~mutCheckpointBlockHashes=checkpointBlockHashes,
263
- ~mutCheckpointEventsProcessed=checkpointEventsProcessed,
264
- )
265
-
266
- let checkpointId = prevCheckpointId.contents->BigInt.add(1n)
274
+ addReorgCheckpoints(
275
+ ~chainId=fetchState.chainId,
276
+ ~scannedHashes=chainBeforeBatch.scannedHashes,
277
+ ~shouldRollbackOnReorg=chainBeforeBatch.shouldRollbackOnReorg,
278
+ ~cursor,
279
+ ~fromBlockExclusive=prevBlockNumber.contents,
280
+ ~toBlockExclusive=blockNumber,
281
+ ~mutCheckpointIds=checkpointIds,
282
+ ~mutCheckpointChainIds=checkpointChainIds,
283
+ ~mutCheckpointBlockNumbers=checkpointBlockNumbers,
284
+ ~mutCheckpointBlockHashes=checkpointBlockHashes,
285
+ ~mutCheckpointItemsCount=checkpointItemsCount,
286
+ ~mutCheckpointEventsProcessed=checkpointEventsProcessed,
287
+ )
288
+
289
+ let checkpointId = cursor->CheckpointSequence.next(~chainId=fetchState.chainId)
267
290
 
268
291
  checkpointIds->Array.push(checkpointId)->ignore
269
292
  checkpointChainIds->Array.push(fetchState.chainId)->ignore
@@ -278,41 +301,49 @@ let prepareBatch = (
278
301
  },
279
302
  )
280
303
  ->ignore
304
+ checkpointItemsCount->Array.push(1)->ignore
281
305
  checkpointEventsProcessed->Array.push(1)->ignore
282
306
 
283
307
  prevBlockNumber := blockNumber
284
- prevCheckpointId := checkpointId
285
308
  } else {
286
- let lastIndex = checkpointEventsProcessed->Array.length - 1
287
- checkpointEventsProcessed
288
- ->Array.setUnsafe(lastIndex, checkpointEventsProcessed->Array.getUnsafe(lastIndex) + 1)
309
+ let lastIndex = checkpointItemsCount->Array.length - 1
310
+ checkpointItemsCount
311
+ ->Array.setUnsafe(lastIndex, checkpointItemsCount->Array.getUnsafe(lastIndex) + 1)
289
312
  ->ignore
313
+ if isNewEvent {
314
+ checkpointEventsProcessed
315
+ ->Array.setUnsafe(lastIndex, checkpointEventsProcessed->Array.getUnsafe(lastIndex) + 1)
316
+ ->ignore
317
+ }
290
318
  }
291
319
 
292
320
  items->Array.push(item)->ignore
293
321
  }
294
322
 
295
323
  totalBatchSize := totalBatchSize.contents + chainBatchSize
296
- mutBatchSizePerChain->ChainId.Dict.set(fetchState.chainId, chainBatchSize)
324
+ mutCountsPerChain->ChainId.Dict.set(
325
+ fetchState.chainId,
326
+ {size: chainBatchSize, eventsProcessed: chainEventsProcessed.contents},
327
+ )
297
328
  }
298
329
 
299
330
  let progressBlockNumberAfterBatch =
300
331
  fetchState->FetchState.getProgressBlockNumberAt(~index=chainBatchSize)
301
332
 
302
- prevCheckpointId :=
303
- addReorgCheckpoints(
304
- ~chainId=fetchState.chainId,
305
- ~scannedHashes=chainBeforeBatch.scannedHashes,
306
- ~shouldRollbackOnReorg=chainBeforeBatch.shouldRollbackOnReorg,
307
- ~prevCheckpointId=prevCheckpointId.contents,
308
- ~fromBlockExclusive=prevBlockNumber.contents,
309
- ~toBlockExclusive=progressBlockNumberAfterBatch + 1, // Make it inclusive
310
- ~mutCheckpointIds=checkpointIds,
311
- ~mutCheckpointChainIds=checkpointChainIds,
312
- ~mutCheckpointBlockNumbers=checkpointBlockNumbers,
313
- ~mutCheckpointBlockHashes=checkpointBlockHashes,
314
- ~mutCheckpointEventsProcessed=checkpointEventsProcessed,
315
- )
333
+ addReorgCheckpoints(
334
+ ~chainId=fetchState.chainId,
335
+ ~scannedHashes=chainBeforeBatch.scannedHashes,
336
+ ~shouldRollbackOnReorg=chainBeforeBatch.shouldRollbackOnReorg,
337
+ ~cursor,
338
+ ~fromBlockExclusive=prevBlockNumber.contents,
339
+ ~toBlockExclusive=progressBlockNumberAfterBatch + 1, // Make it inclusive
340
+ ~mutCheckpointIds=checkpointIds,
341
+ ~mutCheckpointChainIds=checkpointChainIds,
342
+ ~mutCheckpointBlockNumbers=checkpointBlockNumbers,
343
+ ~mutCheckpointBlockHashes=checkpointBlockHashes,
344
+ ~mutCheckpointItemsCount=checkpointItemsCount,
345
+ ~mutCheckpointEventsProcessed=checkpointEventsProcessed,
346
+ )
316
347
 
317
348
  mutProgressBlockNumberPerChain->ChainId.Dict.set(
318
349
  fetchState.chainId,
@@ -327,26 +358,28 @@ let prepareBatch = (
327
358
  items,
328
359
  progressedChainsById: getProgressedChainsById(
329
360
  ~chainsBeforeBatch,
330
- ~batchSizePerChain=mutBatchSizePerChain,
361
+ ~countsPerChain=mutCountsPerChain,
331
362
  ~progressBlockNumberPerChain=mutProgressBlockNumberPerChain,
332
363
  ),
333
- isInReorgThreshold,
364
+ history,
334
365
  checkpointIds,
335
366
  checkpointChainIds,
336
367
  checkpointBlockNumbers,
337
368
  checkpointBlockHashes,
369
+ checkpointItemsCount,
338
370
  checkpointEventsProcessed,
339
371
  registeredAddresses: [],
340
372
  }
341
373
  }
342
374
 
343
- let make = (
344
- ~checkpointIdBeforeBatch,
345
- ~chainsBeforeBatch: dict<chainBeforeBatch>,
346
- ~batchSizeTarget,
347
- ~isInReorgThreshold,
348
- ) => {
349
- prepareBatch(~checkpointIdBeforeBatch, ~chainsBeforeBatch, ~batchSizeTarget, ~isInReorgThreshold)
375
+ // Where the batch leaves each chain it handed ids to. Ids ascend within a
376
+ // chain, so the last one seen per chain is its highest.
377
+ let checkpointFrontier = (batch: t): Frontier.t => {
378
+ let frontier = Frontier.empty()
379
+ batch.checkpointChainIds->Array.forEachWithIndex((chainId, index) =>
380
+ frontier->Frontier.set(chainId, batch.checkpointIds->Array.getUnsafe(index))
381
+ )
382
+ frontier
350
383
  }
351
384
 
352
385
  let findFirstEventBlockNumber = (batch: t, ~chainId) => {
@@ -357,7 +390,7 @@ let findFirstEventBlockNumber = (batch: t, ~chainId) => {
357
390
  let checkpointChainId = batch.checkpointChainIds->Array.getUnsafe(idx.contents)
358
391
  if (
359
392
  checkpointChainId === chainId &&
360
- batch.checkpointEventsProcessed->Array.getUnsafe(idx.contents) > 0
393
+ batch.checkpointItemsCount->Array.getUnsafe(idx.contents) > 0
361
394
  ) {
362
395
  result := Some(batch.checkpointBlockNumbers->Array.getUnsafe(idx.contents))
363
396
  } else {
package/src/Batch.res.mjs CHANGED
@@ -1,34 +1,40 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
3
  import * as ChainId from "./ChainId.res.mjs";
4
+ import * as Frontier from "./db/Frontier.res.mjs";
4
5
  import * as FetchState from "./FetchState.res.mjs";
6
+ import * as CheckpointSequence from "./db/CheckpointSequence.res.mjs";
5
7
 
6
- function getChainAfterBatchIfProgressed(chainBeforeBatch, progressBlockNumberAfterBatch, fetchStateAfterBatch, batchSize) {
8
+ function getChainAfterBatchIfProgressed(chainBeforeBatch, progressBlockNumberAfterBatch, fetchStateAfterBatch, counts) {
7
9
  if (chainBeforeBatch.progressBlockNumber < progressBlockNumberAfterBatch) {
8
10
  return {
9
- batchSize: batchSize,
11
+ batchSize: counts.size,
10
12
  progressBlockNumber: progressBlockNumberAfterBatch,
13
+ progressBlockTime: chainBeforeBatch.blockTimeAt(progressBlockNumberAfterBatch),
11
14
  sourceBlockNumber: chainBeforeBatch.sourceBlockNumber,
12
- totalEventsProcessed: chainBeforeBatch.totalEventsProcessed + batchSize,
15
+ totalEventsProcessed: chainBeforeBatch.totalEventsProcessed + counts.eventsProcessed,
13
16
  fetchState: fetchStateAfterBatch,
14
17
  isProgressAtHeadWhenBatchCreated: progressBlockNumberAfterBatch >= chainBeforeBatch.sourceBlockNumber - chainBeforeBatch.chainConfig.blockLag
15
18
  };
16
19
  }
17
20
  }
18
21
 
19
- function getProgressedChainsById(chainsBeforeBatch, batchSizePerChain, progressBlockNumberPerChain) {
22
+ function getProgressedChainsById(chainsBeforeBatch, countsPerChain, progressBlockNumberPerChain) {
20
23
  let progressedChainsById = {};
21
24
  Object.values(chainsBeforeBatch).forEach(chainBeforeBatch => {
22
25
  let fetchState = chainBeforeBatch.fetchState;
23
26
  let progressBlockNumber = progressBlockNumberPerChain[ChainId.toString(fetchState.chainId)];
24
27
  let progressBlockNumberAfterBatch = progressBlockNumber !== undefined ? progressBlockNumber : chainBeforeBatch.progressBlockNumber;
25
- let batchSize = batchSizePerChain[ChainId.toString(fetchState.chainId)];
28
+ let counts = countsPerChain[ChainId.toString(fetchState.chainId)];
26
29
  let progressedChain;
27
- if (batchSize !== undefined) {
28
- let leftItems = fetchState.buffer.slice(batchSize);
29
- progressedChain = getChainAfterBatchIfProgressed(chainBeforeBatch, progressBlockNumberAfterBatch, FetchState.updateInternal(fetchState, undefined, leftItems, undefined, undefined, undefined), batchSize);
30
+ if (counts !== undefined) {
31
+ let leftItems = fetchState.buffer.slice(counts.size);
32
+ progressedChain = getChainAfterBatchIfProgressed(chainBeforeBatch, progressBlockNumberAfterBatch, FetchState.updateInternal(fetchState, undefined, leftItems, undefined, undefined, undefined), counts);
30
33
  } else {
31
- progressedChain = getChainAfterBatchIfProgressed(chainBeforeBatch, progressBlockNumberAfterBatch, chainBeforeBatch.fetchState, 0);
34
+ progressedChain = getChainAfterBatchIfProgressed(chainBeforeBatch, progressBlockNumberAfterBatch, chainBeforeBatch.fetchState, {
35
+ size: 0,
36
+ eventsProcessed: 0
37
+ });
32
38
  }
33
39
  if (progressedChain !== undefined) {
34
40
  progressedChainsById[chainBeforeBatch.fetchState.chainId] = progressedChain;
@@ -52,145 +58,146 @@ function seekFirstAbove(blockNumbers, blockNumber) {
52
58
  return low;
53
59
  }
54
60
 
55
- function addReorgCheckpoints(prevCheckpointId, scannedHashes, shouldRollbackOnReorg, fromBlockExclusive, toBlockExclusive, chainId, mutCheckpointIds, mutCheckpointChainIds, mutCheckpointBlockNumbers, mutCheckpointBlockHashes, mutCheckpointEventsProcessed) {
61
+ function addReorgCheckpoints(cursor, scannedHashes, shouldRollbackOnReorg, fromBlockExclusive, toBlockExclusive, chainId, mutCheckpointIds, mutCheckpointChainIds, mutCheckpointBlockNumbers, mutCheckpointBlockHashes, mutCheckpointItemsCount, mutCheckpointEventsProcessed) {
56
62
  if (!shouldRollbackOnReorg) {
57
- return prevCheckpointId;
63
+ return;
58
64
  }
59
- let prevCheckpointId$1 = prevCheckpointId;
60
65
  let blockNumbers = scannedHashes.blockNumbers;
61
66
  let length = blockNumbers.length;
62
67
  let idx = seekFirstAbove(blockNumbers, fromBlockExclusive);
63
68
  while (idx < length && blockNumbers[idx] < toBlockExclusive) {
64
69
  let blockNumber = blockNumbers[idx];
65
70
  let hash = scannedHashes.hashByBlockNumber[blockNumber];
66
- let checkpointId = prevCheckpointId$1 + 1n;
67
- prevCheckpointId$1 = checkpointId;
71
+ let checkpointId = CheckpointSequence.next(cursor, chainId);
68
72
  mutCheckpointIds.push(checkpointId);
69
73
  mutCheckpointChainIds.push(chainId);
70
74
  mutCheckpointBlockNumbers.push(blockNumber);
71
75
  mutCheckpointBlockHashes.push(hash);
76
+ mutCheckpointItemsCount.push(0);
72
77
  mutCheckpointEventsProcessed.push(0);
73
78
  idx = idx + 1;
74
79
  };
75
- return prevCheckpointId$1;
76
80
  }
77
81
 
78
- function prepareBatch(checkpointIdBeforeBatch, chainsBeforeBatch, batchSizeTarget, isInReorgThreshold) {
82
+ function make(sequence, frontier, chainsBeforeBatch, batchSizeTarget, history) {
79
83
  let preparedFetchStates = FetchState.sortForBatch(Object.values(chainsBeforeBatch).map(chainBeforeBatch => chainBeforeBatch.fetchState), batchSizeTarget);
80
84
  let chainIdx = 0;
81
85
  let preparedNumber = preparedFetchStates.length;
82
86
  let totalBatchSize = 0;
83
- let prevCheckpointId = checkpointIdBeforeBatch;
84
- let mutBatchSizePerChain = {};
87
+ let cursor = CheckpointSequence.cursor(sequence, frontier);
88
+ let mutCountsPerChain = {};
85
89
  let mutProgressBlockNumberPerChain = {};
86
90
  let items = [];
87
91
  let checkpointIds = [];
88
92
  let checkpointChainIds = [];
89
93
  let checkpointBlockNumbers = [];
90
94
  let checkpointBlockHashes = [];
95
+ let checkpointItemsCount = [];
91
96
  let checkpointEventsProcessed = [];
92
97
  while (totalBatchSize < batchSizeTarget && chainIdx < preparedNumber) {
93
98
  let fetchState = preparedFetchStates[chainIdx];
94
99
  let chainBatchSize = FetchState.getReadyItemsCount(fetchState, batchSizeTarget - totalBatchSize, 0);
95
100
  let chainBeforeBatch = chainsBeforeBatch[fetchState.chainId];
96
101
  let prevBlockNumber = chainBeforeBatch.progressBlockNumber;
102
+ let chainEventsProcessed = 0;
97
103
  if (chainBatchSize > 0) {
98
104
  for (let idx = 0, idx_finish = chainBatchSize - 1; idx <= idx_finish; ++idx) {
99
105
  let item = fetchState.buffer[idx];
100
106
  let blockNumber = item.blockNumber;
107
+ let isNewEvent = idx === 0 || !FetchState.isSameLog(fetchState.buffer[idx - 1], item);
108
+ if (isNewEvent) {
109
+ chainEventsProcessed = chainEventsProcessed + 1;
110
+ }
101
111
  if (blockNumber !== prevBlockNumber) {
102
112
  let chainId = fetchState.chainId;
103
113
  let fromBlockExclusive = prevBlockNumber;
104
114
  let scannedHashes = chainBeforeBatch.scannedHashes;
105
- let prevCheckpointId$1 = prevCheckpointId;
106
- let tmp;
107
115
  if (chainBeforeBatch.shouldRollbackOnReorg) {
108
- let prevCheckpointId$2 = prevCheckpointId$1;
109
116
  let blockNumbers = scannedHashes.blockNumbers;
110
117
  let length = blockNumbers.length;
111
118
  let idx$1 = seekFirstAbove(blockNumbers, fromBlockExclusive);
112
119
  while (idx$1 < length && blockNumbers[idx$1] < blockNumber) {
113
120
  let blockNumber$1 = blockNumbers[idx$1];
114
121
  let hash = scannedHashes.hashByBlockNumber[blockNumber$1];
115
- let checkpointId = prevCheckpointId$2 + 1n;
116
- prevCheckpointId$2 = checkpointId;
122
+ let checkpointId = CheckpointSequence.next(cursor, chainId);
117
123
  checkpointIds.push(checkpointId);
118
124
  checkpointChainIds.push(chainId);
119
125
  checkpointBlockNumbers.push(blockNumber$1);
120
126
  checkpointBlockHashes.push(hash);
127
+ checkpointItemsCount.push(0);
121
128
  checkpointEventsProcessed.push(0);
122
129
  idx$1 = idx$1 + 1;
123
130
  };
124
- tmp = prevCheckpointId$2;
125
- } else {
126
- tmp = prevCheckpointId$1;
127
131
  }
128
- prevCheckpointId = tmp;
129
- let checkpointId$1 = prevCheckpointId + 1n;
132
+ let checkpointId$1 = CheckpointSequence.next(cursor, fetchState.chainId);
130
133
  checkpointIds.push(checkpointId$1);
131
134
  checkpointChainIds.push(fetchState.chainId);
132
135
  checkpointBlockNumbers.push(blockNumber);
133
136
  let hash$1 = chainBeforeBatch.scannedHashes.hashByBlockNumber[blockNumber];
134
137
  checkpointBlockHashes.push(hash$1 !== undefined ? hash$1 : null);
138
+ checkpointItemsCount.push(1);
135
139
  checkpointEventsProcessed.push(1);
136
140
  prevBlockNumber = blockNumber;
137
- prevCheckpointId = checkpointId$1;
138
141
  } else {
139
- let lastIndex = checkpointEventsProcessed.length - 1;
140
- checkpointEventsProcessed[lastIndex] = checkpointEventsProcessed[lastIndex] + 1;
142
+ let lastIndex = checkpointItemsCount.length - 1;
143
+ checkpointItemsCount[lastIndex] = checkpointItemsCount[lastIndex] + 1;
144
+ if (isNewEvent) {
145
+ checkpointEventsProcessed[lastIndex] = checkpointEventsProcessed[lastIndex] + 1;
146
+ }
141
147
  }
142
148
  items.push(item);
143
149
  }
144
150
  totalBatchSize = totalBatchSize + chainBatchSize;
145
- mutBatchSizePerChain[fetchState.chainId] = chainBatchSize;
151
+ mutCountsPerChain[fetchState.chainId] = {
152
+ size: chainBatchSize,
153
+ eventsProcessed: chainEventsProcessed
154
+ };
146
155
  }
147
156
  let progressBlockNumberAfterBatch = FetchState.getProgressBlockNumberAt(fetchState, chainBatchSize);
148
157
  let chainId$1 = fetchState.chainId;
149
158
  let toBlockExclusive = progressBlockNumberAfterBatch + 1;
150
159
  let fromBlockExclusive$1 = prevBlockNumber;
151
160
  let scannedHashes$1 = chainBeforeBatch.scannedHashes;
152
- let prevCheckpointId$3 = prevCheckpointId;
153
- let tmp$1;
154
161
  if (chainBeforeBatch.shouldRollbackOnReorg) {
155
- let prevCheckpointId$4 = prevCheckpointId$3;
156
162
  let blockNumbers$1 = scannedHashes$1.blockNumbers;
157
163
  let length$1 = blockNumbers$1.length;
158
164
  let idx$2 = seekFirstAbove(blockNumbers$1, fromBlockExclusive$1);
159
165
  while (idx$2 < length$1 && blockNumbers$1[idx$2] < toBlockExclusive) {
160
166
  let blockNumber$2 = blockNumbers$1[idx$2];
161
167
  let hash$2 = scannedHashes$1.hashByBlockNumber[blockNumber$2];
162
- let checkpointId$2 = prevCheckpointId$4 + 1n;
163
- prevCheckpointId$4 = checkpointId$2;
168
+ let checkpointId$2 = CheckpointSequence.next(cursor, chainId$1);
164
169
  checkpointIds.push(checkpointId$2);
165
170
  checkpointChainIds.push(chainId$1);
166
171
  checkpointBlockNumbers.push(blockNumber$2);
167
172
  checkpointBlockHashes.push(hash$2);
173
+ checkpointItemsCount.push(0);
168
174
  checkpointEventsProcessed.push(0);
169
175
  idx$2 = idx$2 + 1;
170
176
  };
171
- tmp$1 = prevCheckpointId$4;
172
- } else {
173
- tmp$1 = prevCheckpointId$3;
174
177
  }
175
- prevCheckpointId = tmp$1;
176
178
  mutProgressBlockNumberPerChain[fetchState.chainId] = progressBlockNumberAfterBatch;
177
179
  chainIdx = chainIdx + 1;
178
180
  };
179
181
  return {
180
182
  totalBatchSize: totalBatchSize,
181
183
  items: items,
182
- progressedChainsById: getProgressedChainsById(chainsBeforeBatch, mutBatchSizePerChain, mutProgressBlockNumberPerChain),
183
- isInReorgThreshold: isInReorgThreshold,
184
+ progressedChainsById: getProgressedChainsById(chainsBeforeBatch, mutCountsPerChain, mutProgressBlockNumberPerChain),
185
+ history: history,
184
186
  checkpointIds: checkpointIds,
185
187
  checkpointChainIds: checkpointChainIds,
186
188
  checkpointBlockNumbers: checkpointBlockNumbers,
187
189
  checkpointBlockHashes: checkpointBlockHashes,
190
+ checkpointItemsCount: checkpointItemsCount,
188
191
  checkpointEventsProcessed: checkpointEventsProcessed,
189
192
  registeredAddresses: []
190
193
  };
191
194
  }
192
195
 
193
- let make = prepareBatch;
196
+ function checkpointFrontier(batch) {
197
+ let frontier = Frontier.empty();
198
+ batch.checkpointChainIds.forEach((chainId, index) => Frontier.set(frontier, chainId, batch.checkpointIds[index]));
199
+ return frontier;
200
+ }
194
201
 
195
202
  function findFirstEventBlockNumber(batch, chainId) {
196
203
  let idx = 0;
@@ -198,7 +205,7 @@ function findFirstEventBlockNumber(batch, chainId) {
198
205
  let checkpointsLength = batch.checkpointIds.length;
199
206
  while (idx < checkpointsLength && result === undefined) {
200
207
  let checkpointChainId = batch.checkpointChainIds[idx];
201
- if (checkpointChainId === chainId && batch.checkpointEventsProcessed[idx] > 0) {
208
+ if (checkpointChainId === chainId && batch.checkpointItemsCount[idx] > 0) {
202
209
  result = batch.checkpointBlockNumbers[idx];
203
210
  } else {
204
211
  idx = idx + 1;
@@ -225,8 +232,8 @@ export {
225
232
  getProgressedChainsById,
226
233
  seekFirstAbove,
227
234
  addReorgCheckpoints,
228
- prepareBatch,
229
235
  make,
236
+ checkpointFrontier,
230
237
  findFirstEventBlockNumber,
231
238
  findLastEventItem,
232
239
  }