envio 3.2.1 → 3.3.0-alpha.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 (106) hide show
  1. package/package.json +6 -7
  2. package/src/Batch.res +12 -15
  3. package/src/Batch.res.mjs +4 -5
  4. package/src/BatchProcessing.res +199 -0
  5. package/src/BatchProcessing.res.mjs +125 -0
  6. package/src/ChainFetching.res +373 -0
  7. package/src/ChainFetching.res.mjs +206 -0
  8. package/src/ChainMetadata.res +27 -0
  9. package/src/ChainMetadata.res.mjs +27 -0
  10. package/src/ChainState.res +643 -0
  11. package/src/ChainState.res.mjs +476 -0
  12. package/src/ChainState.resi +87 -0
  13. package/src/Config.res +13 -4
  14. package/src/Config.res.mjs +8 -3
  15. package/src/ContractRegisterContext.res +106 -0
  16. package/src/ContractRegisterContext.res.mjs +76 -0
  17. package/src/Core.res +3 -0
  18. package/src/CrossChainState.res +294 -0
  19. package/src/CrossChainState.res.mjs +221 -0
  20. package/src/CrossChainState.resi +39 -0
  21. package/src/Ecosystem.res +58 -0
  22. package/src/Ecosystem.res.mjs +43 -1
  23. package/src/Env.res +0 -2
  24. package/src/Env.res.mjs +0 -3
  25. package/src/EventConfigBuilder.res +2 -2
  26. package/src/EventProcessing.res +60 -46
  27. package/src/EventProcessing.res.mjs +33 -32
  28. package/src/EventUtils.res +0 -4
  29. package/src/EventUtils.res.mjs +0 -4
  30. package/src/ExitOnCaughtUp.res +10 -0
  31. package/src/ExitOnCaughtUp.res.mjs +22 -0
  32. package/src/FetchState.res +205 -73
  33. package/src/FetchState.res.mjs +242 -103
  34. package/src/InMemoryStore.res +36 -451
  35. package/src/InMemoryStore.res.mjs +20 -369
  36. package/src/IndexerLoop.res +43 -0
  37. package/src/IndexerLoop.res.mjs +46 -0
  38. package/src/IndexerState.res +536 -0
  39. package/src/IndexerState.res.mjs +543 -0
  40. package/src/IndexerState.resi +128 -0
  41. package/src/Internal.res +29 -8
  42. package/src/LoadLayer.res +20 -18
  43. package/src/LoadLayer.res.mjs +14 -12
  44. package/src/LoadLayer.resi +6 -3
  45. package/src/Logging.res +11 -44
  46. package/src/Logging.res.mjs +10 -42
  47. package/src/Main.res +54 -79
  48. package/src/Main.res.mjs +44 -56
  49. package/src/PgStorage.res +8 -71
  50. package/src/PgStorage.res.mjs +3 -47
  51. package/src/Prometheus.res +1 -13
  52. package/src/Prometheus.res.mjs +84 -96
  53. package/src/PruneStaleHistory.res +45 -0
  54. package/src/PruneStaleHistory.res.mjs +46 -0
  55. package/src/RawEvent.res +73 -0
  56. package/src/RawEvent.res.mjs +51 -0
  57. package/src/Rollback.res +204 -0
  58. package/src/Rollback.res.mjs +118 -0
  59. package/src/SimulateItems.res +12 -10
  60. package/src/SimulateItems.res.mjs +1 -1
  61. package/src/UserContext.res +26 -114
  62. package/src/UserContext.res.mjs +15 -78
  63. package/src/Writing.res +242 -0
  64. package/src/Writing.res.mjs +215 -0
  65. package/src/db/InternalTable.res +14 -27
  66. package/src/sources/Evm.res +40 -1
  67. package/src/sources/Evm.res.mjs +94 -84
  68. package/src/sources/Fuel.res +40 -1
  69. package/src/sources/Fuel.res.mjs +36 -26
  70. package/src/sources/HyperFuel.res +41 -120
  71. package/src/sources/HyperFuel.res.mjs +42 -80
  72. package/src/sources/HyperFuel.resi +1 -24
  73. package/src/sources/HyperFuelClient.res +16 -297
  74. package/src/sources/HyperFuelClient.res.mjs +5 -4
  75. package/src/sources/HyperFuelSource.res +33 -8
  76. package/src/sources/HyperFuelSource.res.mjs +56 -10
  77. package/src/sources/HyperSyncSource.res +5 -3
  78. package/src/sources/HyperSyncSource.res.mjs +1 -1
  79. package/src/sources/RpcSource.res +2 -2
  80. package/src/sources/RpcSource.res.mjs +1 -1
  81. package/src/sources/SourceManager.res +14 -23
  82. package/src/sources/SourceManager.res.mjs +27 -29
  83. package/src/sources/SourceManager.resi +4 -2
  84. package/src/sources/Svm.res +18 -1
  85. package/src/sources/Svm.res.mjs +30 -22
  86. package/src/sources/SvmHyperSyncSource.res +1 -1
  87. package/src/sources/SvmHyperSyncSource.res.mjs +1 -1
  88. package/src/tui/Tui.res +38 -36
  89. package/src/tui/Tui.res.mjs +51 -51
  90. package/src/ChainFetcher.res +0 -519
  91. package/src/ChainFetcher.res.mjs +0 -314
  92. package/src/ChainManager.res +0 -358
  93. package/src/ChainManager.res.mjs +0 -291
  94. package/src/Ctx.res +0 -6
  95. package/src/Ctx.res.mjs +0 -2
  96. package/src/GlobalState.res +0 -1056
  97. package/src/GlobalState.res.mjs +0 -1086
  98. package/src/GlobalStateManager.res +0 -57
  99. package/src/GlobalStateManager.res.mjs +0 -68
  100. package/src/GlobalStateManager.resi +0 -7
  101. package/src/Ports.res +0 -5
  102. package/src/Ports.res.mjs +0 -9
  103. package/src/adapters/MarkBatchProcessedAdapter.res +0 -5
  104. package/src/adapters/MarkBatchProcessedAdapter.res.mjs +0 -14
  105. package/src/sources/EnvioApiClient.res +0 -15
  106. package/src/sources/EnvioApiClient.res.mjs +0 -24
@@ -0,0 +1,373 @@
1
+ // Fetch orchestration and query-response handling. Re-enters the loop only
2
+ // through the injected schedule* effects; everything else points at IndexerState
3
+ // (state + transitions) and leaf effect modules.
4
+
5
+ type partitionQueryResponse = {
6
+ chain: IndexerState.chain,
7
+ response: Source.blockRangeFetchResponse,
8
+ query: FetchState.query,
9
+ }
10
+
11
+ let runContractRegistersOrThrow = async (
12
+ ~itemsWithContractRegister: array<Internal.item>,
13
+ ~config: Config.t,
14
+ ) => {
15
+ let itemsWithDcs = []
16
+
17
+ let onRegister = (~item: Internal.item, ~contractAddress, ~contractName) => {
18
+ let eventItem = item->Internal.castUnsafeEventItem
19
+ let {blockNumber} = eventItem
20
+
21
+ let dc: Internal.indexingAddress = {
22
+ address: contractAddress,
23
+ contractName,
24
+ registrationBlock: blockNumber,
25
+ }
26
+
27
+ switch item->Internal.getItemDcs {
28
+ | None => {
29
+ item->Internal.setItemDcs([dc])
30
+ itemsWithDcs->Array.push(item)
31
+ }
32
+ | Some(dcs) => dcs->Array.push(dc)
33
+ }
34
+ }
35
+
36
+ let promises = []
37
+ for idx in 0 to itemsWithContractRegister->Array.length - 1 {
38
+ let item = itemsWithContractRegister->Array.getUnsafe(idx)
39
+ let eventItem = item->Internal.castUnsafeEventItem
40
+ let contractRegister = switch eventItem {
41
+ | {eventConfig: {contractRegister: Some(contractRegister)}} => contractRegister
42
+ | {eventConfig: {contractRegister: None, name: eventName}} =>
43
+ // Unexpected case, since we should pass only events with contract register to this function
44
+ JsError.throwWithMessage("Contract register is not set for event " ++ eventName)
45
+ }
46
+
47
+ let errorMessage = "Event contractRegister failed, please fix the error to keep the indexer running smoothly"
48
+
49
+ // Catch sync and async errors
50
+ try {
51
+ let params: ContractRegisterContext.contractRegisterParams = {
52
+ item,
53
+ onRegister,
54
+ config,
55
+ isResolved: false,
56
+ }
57
+ let result = contractRegister(ContractRegisterContext.getContractRegisterArgs(params))
58
+
59
+ // Even though `contractRegister` always returns a promise,
60
+ // in the ReScript type, but it might return a non-promise value for TS API.
61
+ if result->Utils.Promise.isCatchable {
62
+ promises->Array.push(
63
+ result
64
+ ->Promise.thenResolve(r => {
65
+ params.isResolved = true
66
+ r
67
+ })
68
+ ->Promise.catch(exn => {
69
+ params.isResolved = true
70
+ exn->ErrorHandling.mkLogAndRaise(
71
+ ~msg=errorMessage,
72
+ ~logger=Ecosystem.getItemLogger(item, ~ecosystem=config.ecosystem),
73
+ )
74
+ }),
75
+ )
76
+ } else {
77
+ params.isResolved = true
78
+ }
79
+ } catch {
80
+ | exn =>
81
+ exn->ErrorHandling.mkLogAndRaise(
82
+ ~msg=errorMessage,
83
+ ~logger=Ecosystem.getItemLogger(item, ~ecosystem=config.ecosystem),
84
+ )
85
+ }
86
+ }
87
+
88
+ if promises->Utils.Array.notEmpty {
89
+ let _ = await Promise.all(promises)
90
+ }
91
+
92
+ itemsWithDcs
93
+ }
94
+
95
+ let rec onQueryResponse = async (
96
+ state: IndexerState.t,
97
+ {chain, response, query}: partitionQueryResponse,
98
+ ~stateId,
99
+ ~scheduleFetch,
100
+ ~scheduleProcessing,
101
+ ~scheduleRollback,
102
+ ) =>
103
+ if state->IndexerState.isStale(~stateId) {
104
+ ()
105
+ } else {
106
+ let chainState = state->IndexerState.getChainState(~chain)
107
+ let {
108
+ parsedQueueItems,
109
+ latestFetchedBlockNumber,
110
+ latestFetchedBlockTimestamp,
111
+ stats,
112
+ knownHeight,
113
+ blockHashes,
114
+ fromBlockQueried,
115
+ } = response
116
+
117
+ if knownHeight > (chainState->ChainState.fetchState).knownHeight {
118
+ Prometheus.SourceHeight.set(
119
+ ~blockNumber=knownHeight,
120
+ ~chainId=(chainState->ChainState.chainConfig).id,
121
+ // The knownHeight from response won't necessarily
122
+ // belong to the currently active source.
123
+ // But for simplicity, assume it does.
124
+ ~sourceName=(chainState->ChainState.sourceManager->SourceManager.getActiveSource).name,
125
+ )
126
+ }
127
+
128
+ Prometheus.FetchingBlockRange.increment(
129
+ ~chainId=chain->ChainMap.Chain.toChainId,
130
+ ~totalTimeElapsed=stats.totalTimeElapsed,
131
+ ~parsingTimeElapsed=stats.parsingTimeElapsed->Option.getOr(0.),
132
+ ~numEvents=parsedQueueItems->Array.length,
133
+ ~blockRangeSize=latestFetchedBlockNumber - fromBlockQueried + 1,
134
+ )
135
+
136
+ let reorgResult = chainState->ChainState.registerReorgGuard(~blockHashes, ~knownHeight)
137
+
138
+ let rollbackWithReorgDetectedBlockNumber = switch reorgResult {
139
+ | ReorgDetected(reorgDetected) => {
140
+ chainState
141
+ ->ChainState.logger
142
+ ->Logging.childInfo(
143
+ reorgDetected->ReorgDetection.reorgDetectedToLogParams(
144
+ ~shouldRollbackOnReorg=(state->IndexerState.config).shouldRollbackOnReorg,
145
+ ),
146
+ )
147
+ Prometheus.ReorgCount.increment(~chain)
148
+ Prometheus.ReorgDetectionBlockNumber.set(
149
+ ~blockNumber=reorgDetected.scannedBlock.blockNumber,
150
+ ~chain,
151
+ )
152
+ if (state->IndexerState.config).shouldRollbackOnReorg {
153
+ Some(reorgDetected.scannedBlock.blockNumber)
154
+ } else {
155
+ None
156
+ }
157
+ }
158
+ | NoReorg => None
159
+ }
160
+
161
+ switch rollbackWithReorgDetectedBlockNumber {
162
+ | Some(reorgDetectedBlockNumber) =>
163
+ // Prepare every chain for the rollback: restore each events-processed
164
+ // counter (the previous, uncommitted rollback subtracted from all chains,
165
+ // so undo that before the new rollback subtracts again) and drop pending
166
+ // queries requested against the about-to-be-invalidated chain state.
167
+ let eventsProcessedDiffByChain = switch state->IndexerState.rollbackState {
168
+ | RollbackReady({eventsProcessedDiffByChain}) => Some(eventsProcessedDiffByChain)
169
+ | _ => None
170
+ }
171
+ state
172
+ ->IndexerState.chainStates
173
+ ->Utils.Dict.forEach(cs =>
174
+ cs->ChainState.prepareReorg(
175
+ ~eventsProcessedDiff=switch eventsProcessedDiffByChain {
176
+ | Some(byChain) =>
177
+ byChain->Utils.Dict.dangerouslyGetByIntNonOption((cs->ChainState.chainConfig).id)
178
+ | None => None
179
+ },
180
+ )
181
+ )
182
+ state->IndexerState.beginReorg(~chain, ~blockNumber=reorgDetectedBlockNumber)
183
+ // Advances synchronously to FindingReorgDepth, so a concurrent rollback
184
+ // kick (eg from the processing loop quiescing) collapses into this one.
185
+ scheduleRollback()
186
+ | None =>
187
+ let itemsWithContractRegister = []
188
+ let newItems = []
189
+ for idx in 0 to parsedQueueItems->Array.length - 1 {
190
+ let item = parsedQueueItems->Array.getUnsafe(idx)
191
+ let eventItem = item->Internal.castUnsafeEventItem
192
+ if eventItem.eventConfig.contractRegister !== None {
193
+ itemsWithContractRegister->Array.push(item)
194
+ }
195
+ // TODO: Don't really need to keep it in the queue
196
+ // when there's no handler (besides raw_events, processed counter, and dcsToStore consuming)
197
+ newItems->Array.push(item)
198
+ }
199
+
200
+ // Re-check staleness: contract registration is async, so the chain state
201
+ // may have rolled back by the time we apply the fetched items.
202
+ let proceed = (~newItemsWithDcs) =>
203
+ if !(state->IndexerState.isStale(~stateId)) {
204
+ applyQueryResponse(
205
+ state,
206
+ ~chain,
207
+ ~newItems,
208
+ ~newItemsWithDcs,
209
+ ~knownHeight,
210
+ ~latestFetchedBlock={
211
+ FetchState.blockNumber: latestFetchedBlockNumber,
212
+ blockTimestamp: latestFetchedBlockTimestamp,
213
+ },
214
+ ~query,
215
+ )
216
+ ChainMetadata.stage(state)
217
+ scheduleFetch()
218
+ scheduleProcessing()
219
+ }
220
+
221
+ switch itemsWithContractRegister {
222
+ | [] => proceed(~newItemsWithDcs=[])
223
+ | _ =>
224
+ switch await runContractRegistersOrThrow(
225
+ ~itemsWithContractRegister,
226
+ ~config=state->IndexerState.config,
227
+ ) {
228
+ | exception exn => IndexerState.errorExit(state, exn->ErrorHandling.make)
229
+ | newItemsWithDcs => proceed(~newItemsWithDcs)
230
+ }
231
+ }
232
+ }
233
+ }
234
+
235
+ and applyQueryResponse = (
236
+ state: IndexerState.t,
237
+ ~chain,
238
+ ~newItems,
239
+ ~newItemsWithDcs,
240
+ ~knownHeight,
241
+ ~latestFetchedBlock,
242
+ ~query,
243
+ ) => {
244
+ let chainState = state->IndexerState.getChainState(~chain)
245
+ let wasFetchingAtHead = chainState->ChainState.isFetchingAtHead
246
+
247
+ chainState->ChainState.handleQueryResult(
248
+ ~query,
249
+ ~latestFetchedBlock,
250
+ ~newItems,
251
+ ~newItemsWithDcs,
252
+ ~knownHeight,
253
+ )
254
+
255
+ // In auto-exit mode, set endBlock to the first event's block when events arrive.
256
+ if state->IndexerState.exitAfterFirstEventBlock && newItems->Array.length > 0 {
257
+ chainState->ChainState.setEndBlockToFirstEvent(
258
+ ~blockNumber=newItems->Array.getUnsafe(0)->Internal.getItemBlockNumber,
259
+ )
260
+ }
261
+
262
+ // Log the backfill→head transition once: this response brought the fetch
263
+ // frontier to the head. Gated on !isReady so realtime re-catch-ups (a new
264
+ // block arrives, gets fetched) don't spam the log after the chain is synced.
265
+ if (
266
+ !wasFetchingAtHead &&
267
+ !(chainState->ChainState.isReady) &&
268
+ chainState->ChainState.isFetchingAtHead
269
+ ) {
270
+ chainState->ChainState.logger->Logging.childInfo("All events have been fetched")
271
+ }
272
+ }
273
+
274
+ let finishWaitingForNewBlock = (
275
+ state: IndexerState.t,
276
+ ~chain,
277
+ ~knownHeight,
278
+ ~stateId,
279
+ ~scheduleFetch,
280
+ ~scheduleProcessing,
281
+ ) =>
282
+ if state->IndexerState.isStale(~stateId) {
283
+ ()
284
+ } else {
285
+ let chainState = state->IndexerState.getChainState(~chain)
286
+ chainState->ChainState.updateKnownHeight(~knownHeight)
287
+
288
+ let isBelowReorgThreshold =
289
+ !(state->IndexerState.isInReorgThreshold) &&
290
+ (state->IndexerState.config).shouldRollbackOnReorg
291
+ let shouldEnterReorgThreshold =
292
+ isBelowReorgThreshold &&
293
+ state
294
+ ->IndexerState.chainStates
295
+ ->Dict.valuesToArray
296
+ ->Array.every(cs => {
297
+ cs->ChainState.fetchState->FetchState.isReadyToEnterReorgThreshold
298
+ })
299
+
300
+ // Kick processing in case there are block handlers to run.
301
+ if shouldEnterReorgThreshold {
302
+ IndexerState.enterReorgThreshold(state)
303
+ }
304
+ scheduleFetch()
305
+ scheduleProcessing()
306
+ }
307
+
308
+ let fetchChain = async (
309
+ state: IndexerState.t,
310
+ chain,
311
+ ~action,
312
+ ~stateId,
313
+ ~scheduleFetch,
314
+ ~scheduleProcessing,
315
+ ~scheduleRollback,
316
+ ) => {
317
+ let chainState = state->IndexerState.getChainState(~chain)
318
+ if !(state->IndexerState.isResolvingReorg) && !(state->IndexerState.isStopped) {
319
+ let fetchState = chainState->ChainState.fetchState
320
+ let isRealtime = state->IndexerState.isRealtime
321
+ let sourceManager = chainState->ChainState.sourceManager
322
+
323
+ // Only affects the WaitingForNewBlock branch of dispatch, where
324
+ // there's nothing to fetch. During backfill any such chain is idle.
325
+ let reducedPolling = !isRealtime
326
+
327
+ // Owns its error boundary: launch doesn't catch, so any failure here (the
328
+ // query, response handling, or dispatch itself) must stop the indexer.
329
+ try {
330
+ await sourceManager->SourceManager.dispatch(
331
+ ~fetchState,
332
+ ~waitForNewBlock=(~knownHeight) =>
333
+ sourceManager->SourceManager.waitForNewBlock(~knownHeight, ~isRealtime, ~reducedPolling),
334
+ ~onNewBlock=(~knownHeight) =>
335
+ finishWaitingForNewBlock(
336
+ state,
337
+ ~chain,
338
+ ~knownHeight,
339
+ ~stateId,
340
+ ~scheduleFetch,
341
+ ~scheduleProcessing,
342
+ ),
343
+ ~executeQuery=async query => {
344
+ // Caught here (not just by the outer try) so the query promise never
345
+ // rejects: dispatch spins a side-chain off it that would otherwise
346
+ // become an unhandled rejection.
347
+ try {
348
+ let response = await sourceManager->SourceManager.executeQuery(
349
+ ~query,
350
+ ~knownHeight=fetchState.knownHeight,
351
+ ~isRealtime,
352
+ )
353
+ await onQueryResponse(
354
+ state,
355
+ {chain, response, query},
356
+ ~stateId,
357
+ ~scheduleFetch,
358
+ ~scheduleProcessing,
359
+ ~scheduleRollback,
360
+ )
361
+ } catch {
362
+ | exn => IndexerState.errorExit(state, exn->ErrorHandling.make)
363
+ }
364
+ },
365
+ ~action,
366
+ ~stateId,
367
+ )
368
+ } catch {
369
+ | exn =>
370
+ IndexerState.errorExit(state, exn->ErrorHandling.make(~msg=IndexerState.unexpectedErrorMsg))
371
+ }
372
+ }
373
+ }
@@ -0,0 +1,206 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Utils from "./Utils.res.mjs";
4
+ import * as Logging from "./Logging.res.mjs";
5
+ import * as Ecosystem from "./Ecosystem.res.mjs";
6
+ import * as ChainState from "./ChainState.res.mjs";
7
+ import * as FetchState from "./FetchState.res.mjs";
8
+ import * as Prometheus from "./Prometheus.res.mjs";
9
+ import * as IndexerState from "./IndexerState.res.mjs";
10
+ import * as ChainMetadata from "./ChainMetadata.res.mjs";
11
+ import * as ErrorHandling from "./ErrorHandling.res.mjs";
12
+ import * as SourceManager from "./sources/SourceManager.res.mjs";
13
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
14
+ import * as ReorgDetection from "./ReorgDetection.res.mjs";
15
+ import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
16
+ import * as Stdlib_Promise from "@rescript/runtime/lib/es6/Stdlib_Promise.js";
17
+ import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
18
+ import * as ContractRegisterContext from "./ContractRegisterContext.res.mjs";
19
+
20
+ async function runContractRegistersOrThrow(itemsWithContractRegister, config) {
21
+ let itemsWithDcs = [];
22
+ let onRegister = (item, contractAddress, contractName) => {
23
+ let dc_registrationBlock = item.blockNumber;
24
+ let dc = {
25
+ address: contractAddress,
26
+ contractName: contractName,
27
+ registrationBlock: dc_registrationBlock
28
+ };
29
+ let dcs = item.dcs;
30
+ if (dcs !== undefined) {
31
+ dcs.push(dc);
32
+ } else {
33
+ item.dcs = [dc];
34
+ itemsWithDcs.push(item);
35
+ }
36
+ };
37
+ let promises = [];
38
+ for (let idx = 0, idx_finish = itemsWithContractRegister.length; idx < idx_finish; ++idx) {
39
+ let item = itemsWithContractRegister[idx];
40
+ let match = item.eventConfig;
41
+ let contractRegister = match.contractRegister;
42
+ let contractRegister$1 = contractRegister !== undefined ? contractRegister : Stdlib_JsError.throwWithMessage("Contract register is not set for event " + match.name);
43
+ let errorMessage = "Event contractRegister failed, please fix the error to keep the indexer running smoothly";
44
+ try {
45
+ let params = {
46
+ item: item,
47
+ onRegister: onRegister,
48
+ config: config,
49
+ isResolved: false
50
+ };
51
+ let result = contractRegister$1(ContractRegisterContext.getContractRegisterArgs(params));
52
+ if (Utils.$$Promise.isCatchable(result)) {
53
+ promises.push(Stdlib_Promise.$$catch(result.then(r => {
54
+ params.isResolved = true;
55
+
56
+ }), exn => {
57
+ params.isResolved = true;
58
+ return ErrorHandling.mkLogAndRaise(Ecosystem.getItemLogger(item, config.ecosystem), errorMessage, exn);
59
+ }));
60
+ } else {
61
+ params.isResolved = true;
62
+ }
63
+ } catch (raw_exn) {
64
+ let exn = Primitive_exceptions.internalToException(raw_exn);
65
+ ErrorHandling.mkLogAndRaise(Ecosystem.getItemLogger(item, config.ecosystem), errorMessage, exn);
66
+ }
67
+ }
68
+ if (Utils.$$Array.notEmpty(promises)) {
69
+ await Promise.all(promises);
70
+ }
71
+ return itemsWithDcs;
72
+ }
73
+
74
+ function applyQueryResponse(state, chain, newItems, newItemsWithDcs, knownHeight, latestFetchedBlock, query) {
75
+ let chainState = IndexerState.getChainState(state, chain);
76
+ let wasFetchingAtHead = ChainState.isFetchingAtHead(chainState);
77
+ ChainState.handleQueryResult(chainState, query, newItems, newItemsWithDcs, latestFetchedBlock, knownHeight);
78
+ if (IndexerState.exitAfterFirstEventBlock(state) && newItems.length !== 0) {
79
+ ChainState.setEndBlockToFirstEvent(chainState, newItems[0].blockNumber);
80
+ }
81
+ if (!wasFetchingAtHead && !ChainState.isReady(chainState) && ChainState.isFetchingAtHead(chainState)) {
82
+ return Logging.childInfo(ChainState.logger(chainState), "All events have been fetched");
83
+ }
84
+ }
85
+
86
+ async function onQueryResponse(state, param, stateId, scheduleFetch, scheduleProcessing, scheduleRollback) {
87
+ let query = param.query;
88
+ let response = param.response;
89
+ let chain = param.chain;
90
+ if (IndexerState.isStale(state, stateId)) {
91
+ return;
92
+ }
93
+ let chainState = IndexerState.getChainState(state, chain);
94
+ let stats = response.stats;
95
+ let latestFetchedBlockTimestamp = response.latestFetchedBlockTimestamp;
96
+ let latestFetchedBlockNumber = response.latestFetchedBlockNumber;
97
+ let parsedQueueItems = response.parsedQueueItems;
98
+ let knownHeight = response.knownHeight;
99
+ if (knownHeight > ChainState.fetchState(chainState).knownHeight) {
100
+ Prometheus.SourceHeight.set(SourceManager.getActiveSource(ChainState.sourceManager(chainState)).name, ChainState.chainConfig(chainState).id, knownHeight);
101
+ }
102
+ Prometheus.FetchingBlockRange.increment(chain, stats["total time elapsed (s)"], Stdlib_Option.getOr(stats["parsing time (s)"], 0), parsedQueueItems.length, (latestFetchedBlockNumber - response.fromBlockQueried | 0) + 1 | 0);
103
+ let reorgResult = ChainState.registerReorgGuard(chainState, response.blockHashes, knownHeight);
104
+ let rollbackWithReorgDetectedBlockNumber;
105
+ if (typeof reorgResult !== "object") {
106
+ rollbackWithReorgDetectedBlockNumber = undefined;
107
+ } else {
108
+ let reorgDetected = reorgResult._0;
109
+ Logging.childInfo(ChainState.logger(chainState), ReorgDetection.reorgDetectedToLogParams(reorgDetected, IndexerState.config(state).shouldRollbackOnReorg));
110
+ Prometheus.ReorgCount.increment(chain);
111
+ Prometheus.ReorgDetectionBlockNumber.set(reorgDetected.scannedBlock.blockNumber, chain);
112
+ rollbackWithReorgDetectedBlockNumber = IndexerState.config(state).shouldRollbackOnReorg ? reorgDetected.scannedBlock.blockNumber : undefined;
113
+ }
114
+ if (rollbackWithReorgDetectedBlockNumber !== undefined) {
115
+ let match = IndexerState.rollbackState(state);
116
+ let eventsProcessedDiffByChain;
117
+ eventsProcessedDiffByChain = typeof match !== "object" || match.TAG !== "RollbackReady" ? undefined : match.eventsProcessedDiffByChain;
118
+ Utils.Dict.forEach(IndexerState.chainStates(state), cs => ChainState.prepareReorg(cs, eventsProcessedDiffByChain !== undefined ? eventsProcessedDiffByChain[ChainState.chainConfig(cs).id] : undefined));
119
+ IndexerState.beginReorg(state, chain, rollbackWithReorgDetectedBlockNumber);
120
+ return scheduleRollback();
121
+ }
122
+ let itemsWithContractRegister = [];
123
+ let newItems = [];
124
+ for (let idx = 0, idx_finish = parsedQueueItems.length; idx < idx_finish; ++idx) {
125
+ let item = parsedQueueItems[idx];
126
+ if (item.eventConfig.contractRegister !== undefined) {
127
+ itemsWithContractRegister.push(item);
128
+ }
129
+ newItems.push(item);
130
+ }
131
+ let proceed = newItemsWithDcs => {
132
+ if (!IndexerState.isStale(state, stateId)) {
133
+ applyQueryResponse(state, chain, newItems, newItemsWithDcs, knownHeight, {
134
+ blockNumber: latestFetchedBlockNumber,
135
+ blockTimestamp: latestFetchedBlockTimestamp
136
+ }, query);
137
+ ChainMetadata.stage(state);
138
+ scheduleFetch();
139
+ return scheduleProcessing();
140
+ }
141
+ };
142
+ if (itemsWithContractRegister.length === 0) {
143
+ return proceed([]);
144
+ }
145
+ let newItemsWithDcs;
146
+ try {
147
+ newItemsWithDcs = await runContractRegistersOrThrow(itemsWithContractRegister, IndexerState.config(state));
148
+ } catch (raw_exn) {
149
+ let exn = Primitive_exceptions.internalToException(raw_exn);
150
+ return IndexerState.errorExit(state, ErrorHandling.make(exn, undefined, undefined));
151
+ }
152
+ return proceed(newItemsWithDcs);
153
+ }
154
+
155
+ function finishWaitingForNewBlock(state, chain, knownHeight, stateId, scheduleFetch, scheduleProcessing) {
156
+ if (IndexerState.isStale(state, stateId)) {
157
+ return;
158
+ }
159
+ let chainState = IndexerState.getChainState(state, chain);
160
+ ChainState.updateKnownHeight(chainState, knownHeight);
161
+ let isBelowReorgThreshold = !IndexerState.isInReorgThreshold(state) && IndexerState.config(state).shouldRollbackOnReorg;
162
+ let shouldEnterReorgThreshold = isBelowReorgThreshold && Object.values(IndexerState.chainStates(state)).every(cs => FetchState.isReadyToEnterReorgThreshold(ChainState.fetchState(cs)));
163
+ if (shouldEnterReorgThreshold) {
164
+ IndexerState.enterReorgThreshold(state);
165
+ }
166
+ scheduleFetch();
167
+ scheduleProcessing();
168
+ }
169
+
170
+ async function fetchChain(state, chain, action, stateId, scheduleFetch, scheduleProcessing, scheduleRollback) {
171
+ let chainState = IndexerState.getChainState(state, chain);
172
+ if (IndexerState.isResolvingReorg(state) || IndexerState.isStopped(state)) {
173
+ return;
174
+ }
175
+ let fetchState = ChainState.fetchState(chainState);
176
+ let isRealtime = IndexerState.isRealtime(state);
177
+ let sourceManager = ChainState.sourceManager(chainState);
178
+ let reducedPolling = !isRealtime;
179
+ try {
180
+ return await SourceManager.dispatch(sourceManager, fetchState, async query => {
181
+ try {
182
+ let response = await SourceManager.executeQuery(sourceManager, query, fetchState.knownHeight, isRealtime);
183
+ return await onQueryResponse(state, {
184
+ chain: chain,
185
+ response: response,
186
+ query: query
187
+ }, stateId, scheduleFetch, scheduleProcessing, scheduleRollback);
188
+ } catch (raw_exn) {
189
+ let exn = Primitive_exceptions.internalToException(raw_exn);
190
+ return IndexerState.errorExit(state, ErrorHandling.make(exn, undefined, undefined));
191
+ }
192
+ }, knownHeight => SourceManager.waitForNewBlock(sourceManager, knownHeight, isRealtime, reducedPolling), knownHeight => finishWaitingForNewBlock(state, chain, knownHeight, stateId, scheduleFetch, scheduleProcessing), action, stateId);
193
+ } catch (raw_exn) {
194
+ let exn = Primitive_exceptions.internalToException(raw_exn);
195
+ return IndexerState.errorExit(state, ErrorHandling.make(exn, undefined, IndexerState.unexpectedErrorMsg));
196
+ }
197
+ }
198
+
199
+ export {
200
+ runContractRegistersOrThrow,
201
+ onQueryResponse,
202
+ applyQueryResponse,
203
+ finishWaitingForNewBlock,
204
+ fetchChain,
205
+ }
206
+ /* Utils Not a pure module */
@@ -0,0 +1,27 @@
1
+ // Stage the per-chain metadata into the in-memory store. It's staged, not
2
+ // written: the next batch write folds the staged diff in.
3
+ let stage = (state: IndexerState.t) => {
4
+ let chainsData: dict<InternalTable.Chains.metaFields> = Dict.make()
5
+
6
+ state
7
+ ->IndexerState.chainStates
8
+ ->Dict.valuesToArray
9
+ ->Array.forEach(cs => {
10
+ let fetchState = cs->ChainState.fetchState
11
+ chainsData->Dict.set(
12
+ (cs->ChainState.chainConfig).id->Int.toString,
13
+ {
14
+ firstEventBlockNumber: fetchState.firstEventBlock->Null.fromOption,
15
+ isHyperSync: (
16
+ cs->ChainState.sourceManager->SourceManager.getActiveSource
17
+ ).poweredByHyperSync,
18
+ latestFetchedBlockNumber: fetchState->FetchState.bufferBlockNumber,
19
+ timestampCaughtUpToHeadOrEndblock: cs
20
+ ->ChainState.timestampCaughtUpToHeadOrEndblock
21
+ ->Null.fromOption,
22
+ },
23
+ )
24
+ })
25
+
26
+ state->Writing.setChainMeta(chainsData)
27
+ }
@@ -0,0 +1,27 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Writing from "./Writing.res.mjs";
4
+ import * as ChainState from "./ChainState.res.mjs";
5
+ import * as FetchState from "./FetchState.res.mjs";
6
+ import * as Stdlib_Null from "@rescript/runtime/lib/es6/Stdlib_Null.js";
7
+ import * as IndexerState from "./IndexerState.res.mjs";
8
+ import * as SourceManager from "./sources/SourceManager.res.mjs";
9
+
10
+ function stage(state) {
11
+ let chainsData = {};
12
+ Object.values(IndexerState.chainStates(state)).forEach(cs => {
13
+ let fetchState = ChainState.fetchState(cs);
14
+ chainsData[ChainState.chainConfig(cs).id.toString()] = {
15
+ first_event_block: Stdlib_Null.fromOption(fetchState.firstEventBlock),
16
+ buffer_block: FetchState.bufferBlockNumber(fetchState),
17
+ ready_at: Stdlib_Null.fromOption(ChainState.timestampCaughtUpToHeadOrEndblock(cs)),
18
+ _is_hyper_sync: SourceManager.getActiveSource(ChainState.sourceManager(cs)).poweredByHyperSync
19
+ };
20
+ });
21
+ Writing.setChainMeta(state, chainsData);
22
+ }
23
+
24
+ export {
25
+ stage,
26
+ }
27
+ /* Writing Not a pure module */