envio 3.2.0 → 3.3.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) 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 +375 -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 +618 -0
  11. package/src/ChainState.res.mjs +461 -0
  12. package/src/ChainState.resi +85 -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 +269 -0
  19. package/src/CrossChainState.res.mjs +197 -0
  20. package/src/CrossChainState.resi +47 -0
  21. package/src/Ecosystem.res +58 -0
  22. package/src/Ecosystem.res.mjs +43 -1
  23. package/src/Env.res +7 -2
  24. package/src/Env.res.mjs +5 -2
  25. package/src/EventConfigBuilder.res +35 -0
  26. package/src/EventConfigBuilder.res.mjs +21 -0
  27. package/src/EventProcessing.res +60 -46
  28. package/src/EventProcessing.res.mjs +33 -32
  29. package/src/EventUtils.res +0 -4
  30. package/src/EventUtils.res.mjs +0 -4
  31. package/src/ExitOnCaughtUp.res +10 -0
  32. package/src/ExitOnCaughtUp.res.mjs +22 -0
  33. package/src/FetchState.res +162 -113
  34. package/src/FetchState.res.mjs +181 -119
  35. package/src/HandlerLoader.res +6 -1
  36. package/src/HandlerLoader.res.mjs +13 -36
  37. package/src/InMemoryStore.res +36 -451
  38. package/src/InMemoryStore.res.mjs +20 -369
  39. package/src/IndexerLoop.res +44 -0
  40. package/src/IndexerLoop.res.mjs +46 -0
  41. package/src/IndexerState.res +544 -0
  42. package/src/IndexerState.res.mjs +547 -0
  43. package/src/IndexerState.resi +132 -0
  44. package/src/Internal.res +44 -7
  45. package/src/LoadLayer.res +20 -18
  46. package/src/LoadLayer.res.mjs +14 -12
  47. package/src/LoadLayer.resi +6 -3
  48. package/src/LogSelection.res +86 -17
  49. package/src/LogSelection.res.mjs +67 -14
  50. package/src/Logging.res +11 -44
  51. package/src/Logging.res.mjs +10 -42
  52. package/src/Main.res +54 -79
  53. package/src/Main.res.mjs +44 -56
  54. package/src/PgStorage.res +8 -71
  55. package/src/PgStorage.res.mjs +3 -47
  56. package/src/Prometheus.res +7 -8
  57. package/src/Prometheus.res.mjs +7 -4
  58. package/src/PruneStaleHistory.res +45 -0
  59. package/src/PruneStaleHistory.res.mjs +46 -0
  60. package/src/RawEvent.res +73 -0
  61. package/src/RawEvent.res.mjs +51 -0
  62. package/src/Rollback.res +204 -0
  63. package/src/Rollback.res.mjs +118 -0
  64. package/src/SimulateItems.res +12 -10
  65. package/src/SimulateItems.res.mjs +1 -1
  66. package/src/TestIndexer.res +34 -27
  67. package/src/TestIndexer.res.mjs +4 -1
  68. package/src/UserContext.res +26 -114
  69. package/src/UserContext.res.mjs +15 -78
  70. package/src/Writing.res +242 -0
  71. package/src/Writing.res.mjs +215 -0
  72. package/src/db/InternalTable.res +14 -27
  73. package/src/sources/Evm.res +40 -1
  74. package/src/sources/Evm.res.mjs +94 -84
  75. package/src/sources/Fuel.res +40 -1
  76. package/src/sources/Fuel.res.mjs +36 -26
  77. package/src/sources/HyperFuel.res +41 -120
  78. package/src/sources/HyperFuel.res.mjs +42 -80
  79. package/src/sources/HyperFuel.resi +1 -24
  80. package/src/sources/HyperFuelClient.res +16 -297
  81. package/src/sources/HyperFuelClient.res.mjs +5 -4
  82. package/src/sources/HyperFuelSource.res +33 -8
  83. package/src/sources/HyperFuelSource.res.mjs +56 -10
  84. package/src/sources/HyperSyncSource.res +7 -5
  85. package/src/sources/HyperSyncSource.res.mjs +3 -3
  86. package/src/sources/RpcSource.res +2 -2
  87. package/src/sources/RpcSource.res.mjs +1 -1
  88. package/src/sources/SourceManager.res +8 -18
  89. package/src/sources/SourceManager.res.mjs +8 -5
  90. package/src/sources/SourceManager.resi +4 -1
  91. package/src/sources/Svm.res +18 -1
  92. package/src/sources/Svm.res.mjs +30 -22
  93. package/src/sources/SvmHyperSyncSource.res +1 -1
  94. package/src/sources/SvmHyperSyncSource.res.mjs +1 -1
  95. package/src/tui/Tui.res +38 -36
  96. package/src/tui/Tui.res.mjs +51 -51
  97. package/src/ChainFetcher.res +0 -519
  98. package/src/ChainFetcher.res.mjs +0 -314
  99. package/src/ChainManager.res +0 -358
  100. package/src/ChainManager.res.mjs +0 -291
  101. package/src/Ctx.res +0 -6
  102. package/src/Ctx.res.mjs +0 -2
  103. package/src/GlobalState.res +0 -1056
  104. package/src/GlobalState.res.mjs +0 -1086
  105. package/src/GlobalStateManager.res +0 -57
  106. package/src/GlobalStateManager.res.mjs +0 -68
  107. package/src/GlobalStateManager.resi +0 -7
  108. package/src/Ports.res +0 -5
  109. package/src/Ports.res.mjs +0 -9
  110. package/src/adapters/MarkBatchProcessedAdapter.res +0 -5
  111. package/src/adapters/MarkBatchProcessedAdapter.res.mjs +0 -14
  112. package/src/sources/EnvioApiClient.res +0 -15
  113. package/src/sources/EnvioApiClient.res.mjs +0 -24
@@ -0,0 +1,375 @@
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
+ ~concurrencyLimit,
312
+ ~bufferLimit,
313
+ ~stateId,
314
+ ~scheduleFetch,
315
+ ~scheduleProcessing,
316
+ ~scheduleRollback,
317
+ ) => {
318
+ let chainState = state->IndexerState.getChainState(~chain)
319
+ if !(state->IndexerState.isResolvingReorg) && !(state->IndexerState.isStopped) {
320
+ let fetchState = chainState->ChainState.fetchState
321
+ let isRealtime = state->IndexerState.isRealtime
322
+ let sourceManager = chainState->ChainState.sourceManager
323
+
324
+ // Only affects the WaitingForNewBlock branch of fetchNext, where
325
+ // there's nothing to fetch. During backfill any such chain is idle.
326
+ let reducedPolling = !isRealtime
327
+
328
+ // Owns its error boundary: launch doesn't catch, so any failure here (the
329
+ // query, response handling, or fetchNext itself) must stop the indexer.
330
+ try {
331
+ await sourceManager->SourceManager.fetchNext(
332
+ ~fetchState,
333
+ ~waitForNewBlock=(~knownHeight) =>
334
+ sourceManager->SourceManager.waitForNewBlock(~knownHeight, ~isRealtime, ~reducedPolling),
335
+ ~onNewBlock=(~knownHeight) =>
336
+ finishWaitingForNewBlock(
337
+ state,
338
+ ~chain,
339
+ ~knownHeight,
340
+ ~stateId,
341
+ ~scheduleFetch,
342
+ ~scheduleProcessing,
343
+ ),
344
+ ~executeQuery=async query => {
345
+ // Caught here (not just by the outer try) so the query promise never
346
+ // rejects: fetchNext spins a side-chain off it that would otherwise
347
+ // become an unhandled rejection.
348
+ try {
349
+ let response = await sourceManager->SourceManager.executeQuery(
350
+ ~query,
351
+ ~knownHeight=fetchState.knownHeight,
352
+ ~isRealtime,
353
+ )
354
+ await onQueryResponse(
355
+ state,
356
+ {chain, response, query},
357
+ ~stateId,
358
+ ~scheduleFetch,
359
+ ~scheduleProcessing,
360
+ ~scheduleRollback,
361
+ )
362
+ } catch {
363
+ | exn => IndexerState.errorExit(state, exn->ErrorHandling.make)
364
+ }
365
+ },
366
+ ~concurrencyLimit,
367
+ ~bufferLimit,
368
+ ~stateId,
369
+ )
370
+ } catch {
371
+ | exn =>
372
+ IndexerState.errorExit(state, exn->ErrorHandling.make(~msg=IndexerState.unexpectedErrorMsg))
373
+ }
374
+ }
375
+ }
@@ -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, concurrencyLimit, bufferLimit, 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.fetchNext(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), concurrencyLimit, bufferLimit, 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 */