envio 3.2.1 → 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 (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 +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 +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 +124 -62
  33. package/src/FetchState.res.mjs +159 -93
  34. package/src/InMemoryStore.res +36 -451
  35. package/src/InMemoryStore.res.mjs +20 -369
  36. package/src/IndexerLoop.res +44 -0
  37. package/src/IndexerLoop.res.mjs +46 -0
  38. package/src/IndexerState.res +544 -0
  39. package/src/IndexerState.res.mjs +547 -0
  40. package/src/IndexerState.resi +132 -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 +7 -8
  52. package/src/Prometheus.res.mjs +7 -4
  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 +8 -18
  82. package/src/sources/SourceManager.res.mjs +8 -5
  83. package/src/sources/SourceManager.resi +4 -1
  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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "envio",
3
- "version": "3.2.1",
3
+ "version": "3.3.0-alpha.0",
4
4
  "type": "module",
5
5
  "description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
6
6
  "bin": "./bin.mjs",
@@ -42,7 +42,6 @@
42
42
  "dependencies": {
43
43
  "@clickhouse/client": "1.17.0",
44
44
  "@elastic/ecs-pino-format": "1.4.0",
45
- "@envio-dev/hyperfuel-client": "1.2.2",
46
45
  "@fuel-ts/crypto": "0.96.1",
47
46
  "@fuel-ts/errors": "0.96.1",
48
47
  "@fuel-ts/hasher": "0.96.1",
@@ -70,10 +69,10 @@
70
69
  "tsx": "4.21.0"
71
70
  },
72
71
  "optionalDependencies": {
73
- "envio-linux-x64": "3.2.1",
74
- "envio-linux-x64-musl": "3.2.1",
75
- "envio-linux-arm64": "3.2.1",
76
- "envio-darwin-x64": "3.2.1",
77
- "envio-darwin-arm64": "3.2.1"
72
+ "envio-linux-x64": "3.3.0-alpha.0",
73
+ "envio-linux-x64-musl": "3.3.0-alpha.0",
74
+ "envio-linux-arm64": "3.3.0-alpha.0",
75
+ "envio-darwin-x64": "3.3.0-alpha.0",
76
+ "envio-darwin-arm64": "3.3.0-alpha.0"
78
77
  }
79
78
  }
package/src/Batch.res CHANGED
@@ -34,10 +34,8 @@ type t = {
34
34
  checkpointEventsProcessed: array<int>,
35
35
  }
36
36
 
37
- let hasReadyItem = (fetchStates: ChainMap.t<FetchState.t>) => {
38
- fetchStates
39
- ->ChainMap.values
40
- ->Array.some(fetchState => {
37
+ let hasReadyItem = (fetchStates: array<FetchState.t>) => {
38
+ fetchStates->Array.some(fetchState => {
41
39
  fetchState->FetchState.isActivelyIndexing && fetchState->FetchState.hasReadyItem
42
40
  })
43
41
  }
@@ -72,7 +70,7 @@ let getProgressedChainsById = {
72
70
  }
73
71
 
74
72
  (
75
- ~chainsBeforeBatch: ChainMap.t<chainBeforeBatch>,
73
+ ~chainsBeforeBatch: dict<chainBeforeBatch>,
76
74
  ~batchSizePerChain: dict<int>,
77
75
  ~progressBlockNumberPerChain: dict<int>,
78
76
  ) => {
@@ -83,7 +81,7 @@ let getProgressedChainsById = {
83
81
  // - Accumulate registered dynamic contracts to store in the db
84
82
  // - Trigger onBlock pointer update
85
83
  chainsBeforeBatch
86
- ->ChainMap.values
84
+ ->Dict.valuesToArray
87
85
  ->Array.forEach(chainBeforeBatch => {
88
86
  let fetchState = chainBeforeBatch.fetchState
89
87
 
@@ -166,15 +164,15 @@ let addReorgCheckpoints = (
166
164
 
167
165
  let prepareBatch = (
168
166
  ~checkpointIdBeforeBatch,
169
- ~chainsBeforeBatch: ChainMap.t<chainBeforeBatch>,
167
+ ~chainsBeforeBatch: dict<chainBeforeBatch>,
170
168
  ~batchSizeTarget,
171
169
  ~isInReorgThreshold,
172
170
  ) => {
173
171
  let preparedFetchStates =
174
172
  chainsBeforeBatch
175
- ->ChainMap.values
173
+ ->Dict.valuesToArray
176
174
  ->Array.map(chainBeforeBatch => chainBeforeBatch.fetchState)
177
- ->FetchState.sortForUnorderedBatch(~batchSizeTarget)
175
+ ->FetchState.sortForBatch(~batchSizeTarget)
178
176
 
179
177
  let chainIdx = ref(0)
180
178
  let preparedNumber = preparedFetchStates->Array.length
@@ -202,7 +200,9 @@ let prepareBatch = (
202
200
  ~fromItem=0,
203
201
  )
204
202
  let chainBeforeBatch =
205
- chainsBeforeBatch->ChainMap.get(ChainMap.Chain.makeUnsafe(~chainId=fetchState.chainId))
203
+ chainsBeforeBatch
204
+ ->Utils.Dict.dangerouslyGetByIntNonOption(fetchState.chainId)
205
+ ->Option.getUnsafe
206
206
 
207
207
  let prevBlockNumber = ref(chainBeforeBatch.progressBlockNumber)
208
208
  if chainBatchSize > 0 {
@@ -243,10 +243,7 @@ let prepareBatch = (
243
243
  } else {
244
244
  let lastIndex = checkpointEventsProcessed->Array.length - 1
245
245
  checkpointEventsProcessed
246
- ->Array.setUnsafe(
247
- lastIndex,
248
- checkpointEventsProcessed->Array.getUnsafe(lastIndex) + 1,
249
- )
246
+ ->Array.setUnsafe(lastIndex, checkpointEventsProcessed->Array.getUnsafe(lastIndex) + 1)
250
247
  ->ignore
251
248
  }
252
249
 
@@ -301,7 +298,7 @@ let prepareBatch = (
301
298
 
302
299
  let make = (
303
300
  ~checkpointIdBeforeBatch,
304
- ~chainsBeforeBatch: ChainMap.t<chainBeforeBatch>,
301
+ ~chainsBeforeBatch: dict<chainBeforeBatch>,
305
302
  ~batchSizeTarget,
306
303
  ~isInReorgThreshold,
307
304
  ) => {
package/src/Batch.res.mjs CHANGED
@@ -1,12 +1,11 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
3
  import * as Utils from "./Utils.res.mjs";
4
- import * as ChainMap from "./ChainMap.res.mjs";
5
4
  import * as FetchState from "./FetchState.res.mjs";
6
5
  import * as ReorgDetection from "./ReorgDetection.res.mjs";
7
6
 
8
7
  function hasReadyItem(fetchStates) {
9
- return ChainMap.values(fetchStates).some(fetchState => {
8
+ return fetchStates.some(fetchState => {
10
9
  if (FetchState.isActivelyIndexing(fetchState)) {
11
10
  return FetchState.hasReadyItem(fetchState);
12
11
  } else {
@@ -30,7 +29,7 @@ function getChainAfterBatchIfProgressed(chainBeforeBatch, progressBlockNumberAft
30
29
 
31
30
  function getProgressedChainsById(chainsBeforeBatch, batchSizePerChain, progressBlockNumberPerChain) {
32
31
  let progressedChainsById = {};
33
- ChainMap.values(chainsBeforeBatch).forEach(chainBeforeBatch => {
32
+ Object.values(chainsBeforeBatch).forEach(chainBeforeBatch => {
34
33
  let fetchState = chainBeforeBatch.fetchState;
35
34
  let progressBlockNumber = progressBlockNumberPerChain[fetchState.chainId.toString()];
36
35
  let progressBlockNumberAfterBatch = progressBlockNumber !== undefined ? progressBlockNumber : chainBeforeBatch.progressBlockNumber;
@@ -71,7 +70,7 @@ function addReorgCheckpoints(prevCheckpointId, reorgDetection, fromBlockExclusiv
71
70
  }
72
71
 
73
72
  function prepareBatch(checkpointIdBeforeBatch, chainsBeforeBatch, batchSizeTarget, isInReorgThreshold) {
74
- let preparedFetchStates = FetchState.sortForUnorderedBatch(ChainMap.values(chainsBeforeBatch).map(chainBeforeBatch => chainBeforeBatch.fetchState), batchSizeTarget);
73
+ let preparedFetchStates = FetchState.sortForBatch(Object.values(chainsBeforeBatch).map(chainBeforeBatch => chainBeforeBatch.fetchState), batchSizeTarget);
75
74
  let chainIdx = 0;
76
75
  let preparedNumber = preparedFetchStates.length;
77
76
  let totalBatchSize = 0;
@@ -87,7 +86,7 @@ function prepareBatch(checkpointIdBeforeBatch, chainsBeforeBatch, batchSizeTarge
87
86
  while (totalBatchSize < batchSizeTarget && chainIdx < preparedNumber) {
88
87
  let fetchState = preparedFetchStates[chainIdx];
89
88
  let chainBatchSize = FetchState.getReadyItemsCount(fetchState, batchSizeTarget - totalBatchSize, 0);
90
- let chainBeforeBatch = ChainMap.get(chainsBeforeBatch, ChainMap.Chain.makeUnsafe(fetchState.chainId));
89
+ let chainBeforeBatch = chainsBeforeBatch[fetchState.chainId];
91
90
  let prevBlockNumber = chainBeforeBatch.progressBlockNumber;
92
91
  if (chainBatchSize > 0) {
93
92
  for (let idx = 0, idx_finish = chainBatchSize - 1; idx <= idx_finish; ++idx) {
@@ -0,0 +1,199 @@
1
+ // The single batch-processing loop. Re-enters fetch/rollback through the
2
+ // injected schedule* effects.
3
+
4
+ // Yield to the end of the current tick so fetch responses that resolved this
5
+ // tick land before the processing loop builds its first batch (coalescing).
6
+ @inline
7
+ let yieldTick = () => Promise.make((resolve, _) => NodeJs.setImmediate(() => resolve()))
8
+
9
+ // The single processing loop. Runs batches back-to-back while there's work and
10
+ // no reorg is being resolved; exits when idle so producers (fetch, rollback) can
11
+ // re-kick it. `state.isProcessing` guarantees one instance. The loop
12
+ // decides whether to keep going by inspecting state after each batch, rather than
13
+ // from a return value of processNextBatch.
14
+ let rec startProcessing = async (state: IndexerState.t, ~scheduleFetch, ~scheduleRollback) => {
15
+ if !(state->IndexerState.isProcessing) && !(state->IndexerState.isStopped) {
16
+ state->IndexerState.beginProcessing
17
+ // FIXME: Needed only for test determinism. The mocks resolve several fetch
18
+ // responses synchronously in one tick; this yield lets them all land before
19
+ // the first createBatch so they coalesce into one batch (matching the old
20
+ // setImmediate model). In production responses arrive on separate network
21
+ // ticks and never co-arrive, so this never coalesces anything real — remove
22
+ // it later to avoid an unnecessary setImmediate per processing burst.
23
+ await yieldTick()
24
+ // Seeded true so the first batch always runs (it handles the caught-up exit
25
+ // even when there's nothing to process, eg resuming fully-synced state).
26
+ // Keep looping only while the last batch actually processed something: an
27
+ // empty/idle batch records nothing, so the counter stays put and the loop
28
+ // exits, leaving producers (fetch, rollback) to re-kick it.
29
+ let hasMoreWork = ref(true)
30
+ while (
31
+ hasMoreWork.contents &&
32
+ !(state->IndexerState.isStopped) &&
33
+ !(state->IndexerState.isResolvingReorg)
34
+ ) {
35
+ let processedBatchesBefore = state->IndexerState.processedBatchesCount
36
+ switch await processNextBatch(state, ~scheduleFetch) {
37
+ | exception exn =>
38
+ IndexerState.errorExit(state, exn->ErrorHandling.make(~msg=IndexerState.unexpectedErrorMsg))
39
+ | () => hasMoreWork := state->IndexerState.processedBatchesCount > processedBatchesBefore
40
+ }
41
+ }
42
+ state->IndexerState.endProcessing
43
+
44
+ // A reorg detected mid-batch parks the rollback until the loop is idle.
45
+ // Hand off now that no batch is in flight.
46
+ if state->IndexerState.isResolvingReorg {
47
+ scheduleRollback()
48
+ }
49
+ }
50
+ }
51
+
52
+ and processNextBatch = async (state: IndexerState.t, ~scheduleFetch): unit => {
53
+ // The reorg-threshold and queue updates below advance the chain states for the
54
+ // next round, but the current batch is created and processed against the chain
55
+ // states as they are now.
56
+ let isInReorgThresholdBeforeUpdate = state->IndexerState.isInReorgThreshold
57
+ let isRealtimeBeforeUpdate = state->IndexerState.isRealtime
58
+
59
+ let isRollbackBatch = switch state->IndexerState.rollbackState {
60
+ | RollbackReady(_) => true
61
+ | _ => false
62
+ }
63
+
64
+ let batch =
65
+ state->IndexerState.createBatch(
66
+ ~processedCheckpointId=state->IndexerState.processedCheckpointId,
67
+ ~batchSizeTarget=(state->IndexerState.config).batchSize,
68
+ ~isRollback=isRollbackBatch,
69
+ )
70
+
71
+ let progressedChainsById = batch.progressedChainsById
72
+
73
+ let isBelowReorgThreshold =
74
+ !isInReorgThresholdBeforeUpdate && (state->IndexerState.config).shouldRollbackOnReorg
75
+ let shouldEnterReorgThreshold =
76
+ isBelowReorgThreshold &&
77
+ state
78
+ ->IndexerState.chainStates
79
+ ->Dict.valuesToArray
80
+ ->Array.every(cs => {
81
+ let fetchState = switch progressedChainsById->Utils.Dict.dangerouslyGetByIntNonOption(
82
+ (cs->ChainState.fetchState).chainId,
83
+ ) {
84
+ | Some(chainAfterBatch) => chainAfterBatch.fetchState
85
+ | None => cs->ChainState.fetchState
86
+ }
87
+ fetchState->FetchState.isReadyToEnterReorgThreshold
88
+ })
89
+
90
+ if shouldEnterReorgThreshold {
91
+ IndexerState.enterReorgThreshold(state)
92
+ }
93
+
94
+ if progressedChainsById->Utils.Dict.isEmpty {
95
+ if shouldEnterReorgThreshold {
96
+ scheduleFetch()
97
+ }
98
+
99
+ // When resuming from persisted state, all events may already be processed.
100
+ if EventProcessing.allChainsEventsProcessedToEndblock(state->IndexerState.chainStates) {
101
+ Logging.info("All chains are caught up to end blocks.")
102
+ if !(state->IndexerState.keepProcessAlive) {
103
+ await ExitOnCaughtUp.run(state)
104
+ }
105
+ }
106
+ } else {
107
+ // The batch was created from pre-threshold fetch states, so advanceAfterBatch
108
+ // applies blockLag when crossing the threshold; enterReorgThreshold already
109
+ // covered the non-progressed chain states.
110
+ state
111
+ ->IndexerState.chainStates
112
+ ->Utils.Dict.forEach(cs =>
113
+ cs->ChainState.advanceAfterBatch(~batch, ~enteringReorgThreshold=shouldEnterReorgThreshold)
114
+ )
115
+ // Kick the next fetch round before awaiting the batch. A response that
116
+ // lands mid-batch commits only fetch-frontier fields (buffer, knownHeight),
117
+ // while applyBatchProgress below commits only progress fields, so the two
118
+ // concurrent writes are disjoint and neither clobbers the other.
119
+ scheduleFetch()
120
+
121
+ state->InMemoryStore.setBatchDcs(~batch)
122
+
123
+ // An exception here propagates to startProcessing's catch, the single error
124
+ // boundary for the loop. The Error case is not an exception, so it's handled
125
+ // here to preserve the handler's user-facing message.
126
+ switch await EventProcessing.processEventBatch(
127
+ ~batch,
128
+ ~indexerState=state,
129
+ ~loadManager=state->IndexerState.loadManager,
130
+ ~persistence=state->IndexerState.persistence,
131
+ ~config=state->IndexerState.config,
132
+ ~chainStates=state->IndexerState.chainStates,
133
+ ) {
134
+ | Error(errHandler) => IndexerState.errorExit(state, errHandler)
135
+ | Ok() =>
136
+ state->IndexerState.recordProcessedBatch
137
+
138
+ if state->IndexerState.isResolvingReorg {
139
+ // A reorg landed while this batch was processing. Apply its progress so
140
+ // the rollback diff is computed against up-to-date chain progress, but
141
+ // don't reset rollback state or evaluate exit. The loop exit hands off
142
+ // to rollback.
143
+ state->IndexerState.applyBatchProgress(~batch)
144
+ } else {
145
+ // Can safely reset rollback state, since overwrite is not possible.
146
+ state->IndexerState.clearRollback
147
+ state->IndexerState.applyBatchProgress(~batch)
148
+
149
+ if !isRealtimeBeforeUpdate && state->IndexerState.isRealtime {
150
+ // Catching up just flipped the chain to realtime, which changes the
151
+ // active source for waitForNewBlock (eg sync -> live). The waiter that
152
+ // parked during backfill is bound to the old source; bump the epoch to
153
+ // invalidate it and kick a fresh fetch that parks on the realtime source.
154
+ state->IndexerState.invalidateInflight
155
+ scheduleFetch()
156
+ }
157
+
158
+ let allCaughtUp = EventProcessing.allChainsEventsProcessedToEndblock(
159
+ state->IndexerState.chainStates,
160
+ )
161
+ if allCaughtUp {
162
+ Logging.info("All chains are caught up to end blocks.")
163
+ }
164
+
165
+ if allCaughtUp && !(state->IndexerState.keepProcessAlive) {
166
+ await ExitOnCaughtUp.run(state)
167
+ } else if (
168
+ // In auto-exit mode, error if all chains reached head with no events found
169
+ !allCaughtUp &&
170
+ state->IndexerState.exitAfterFirstEventBlock &&
171
+ state
172
+ ->IndexerState.chainStates
173
+ ->Dict.valuesToArray
174
+ ->Array.every(cs =>
175
+ cs->ChainState.isProgressAtHead && (cs->ChainState.fetchState).endBlock->Option.isNone
176
+ )
177
+ ) {
178
+ IndexerState.errorExit(
179
+ state,
180
+ ErrorHandling.make(
181
+ Utils.Error.make(
182
+ "No events found between startBlock and chain head. Cannot auto-detect endBlock.",
183
+ ),
184
+ ),
185
+ )
186
+ } else {
187
+ ChainMetadata.stage(state)
188
+ if (
189
+ state
190
+ ->IndexerState.config
191
+ ->Config.shouldPruneHistory(~isInReorgThreshold=state->IndexerState.isInReorgThreshold)
192
+ ) {
193
+ state->PruneStaleHistory.schedule
194
+ }
195
+ }
196
+ }
197
+ }
198
+ }
199
+ }
@@ -0,0 +1,125 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Utils from "./Utils.res.mjs";
4
+ import * as Config from "./Config.res.mjs";
5
+ import * as Logging from "./Logging.res.mjs";
6
+ import * as ChainState from "./ChainState.res.mjs";
7
+ import * as FetchState from "./FetchState.res.mjs";
8
+ import * as IndexerState from "./IndexerState.res.mjs";
9
+ import * as ChainMetadata from "./ChainMetadata.res.mjs";
10
+ import * as ErrorHandling from "./ErrorHandling.res.mjs";
11
+ import * as InMemoryStore from "./InMemoryStore.res.mjs";
12
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
13
+ import * as ExitOnCaughtUp from "./ExitOnCaughtUp.res.mjs";
14
+ import * as EventProcessing from "./EventProcessing.res.mjs";
15
+ import * as PruneStaleHistory from "./PruneStaleHistory.res.mjs";
16
+ import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
17
+
18
+ function yieldTick() {
19
+ return new Promise((resolve, param) => {
20
+ setImmediate(() => resolve());
21
+ });
22
+ }
23
+
24
+ async function processNextBatch(state, scheduleFetch) {
25
+ let isInReorgThresholdBeforeUpdate = IndexerState.isInReorgThreshold(state);
26
+ let isRealtimeBeforeUpdate = IndexerState.isRealtime(state);
27
+ let match = IndexerState.rollbackState(state);
28
+ let isRollbackBatch;
29
+ isRollbackBatch = typeof match !== "object" ? false : match.TAG === "RollbackReady";
30
+ let batch = IndexerState.createBatch(state, IndexerState.processedCheckpointId(state), IndexerState.config(state).batchSize, isRollbackBatch);
31
+ let progressedChainsById = batch.progressedChainsById;
32
+ let isBelowReorgThreshold = !isInReorgThresholdBeforeUpdate && IndexerState.config(state).shouldRollbackOnReorg;
33
+ let shouldEnterReorgThreshold = isBelowReorgThreshold && Object.values(IndexerState.chainStates(state)).every(cs => {
34
+ let chainAfterBatch = progressedChainsById[ChainState.fetchState(cs).chainId];
35
+ return FetchState.isReadyToEnterReorgThreshold(chainAfterBatch !== undefined ? chainAfterBatch.fetchState : ChainState.fetchState(cs));
36
+ });
37
+ if (shouldEnterReorgThreshold) {
38
+ IndexerState.enterReorgThreshold(state);
39
+ }
40
+ if (Utils.Dict.isEmpty(progressedChainsById)) {
41
+ if (shouldEnterReorgThreshold) {
42
+ scheduleFetch();
43
+ }
44
+ if (EventProcessing.allChainsEventsProcessedToEndblock(IndexerState.chainStates(state))) {
45
+ Logging.info("All chains are caught up to end blocks.");
46
+ if (!IndexerState.keepProcessAlive(state)) {
47
+ return await ExitOnCaughtUp.run(state);
48
+ } else {
49
+ return;
50
+ }
51
+ } else {
52
+ return;
53
+ }
54
+ }
55
+ Utils.Dict.forEach(IndexerState.chainStates(state), cs => ChainState.advanceAfterBatch(cs, batch, shouldEnterReorgThreshold));
56
+ scheduleFetch();
57
+ InMemoryStore.setBatchDcs(state, batch);
58
+ let errHandler = await EventProcessing.processEventBatch(batch, state, IndexerState.loadManager(state), IndexerState.persistence(state), IndexerState.config(state), IndexerState.chainStates(state));
59
+ if (errHandler.TAG !== "Ok") {
60
+ return IndexerState.errorExit(state, errHandler._0);
61
+ }
62
+ IndexerState.recordProcessedBatch(state);
63
+ if (IndexerState.isResolvingReorg(state)) {
64
+ return IndexerState.applyBatchProgress(state, batch);
65
+ }
66
+ IndexerState.clearRollback(state);
67
+ IndexerState.applyBatchProgress(state, batch);
68
+ if (!isRealtimeBeforeUpdate && IndexerState.isRealtime(state)) {
69
+ IndexerState.invalidateInflight(state);
70
+ scheduleFetch();
71
+ }
72
+ let allCaughtUp = EventProcessing.allChainsEventsProcessedToEndblock(IndexerState.chainStates(state));
73
+ if (allCaughtUp) {
74
+ Logging.info("All chains are caught up to end blocks.");
75
+ }
76
+ if (allCaughtUp && !IndexerState.keepProcessAlive(state)) {
77
+ return await ExitOnCaughtUp.run(state);
78
+ } else if (!allCaughtUp && IndexerState.exitAfterFirstEventBlock(state) && Object.values(IndexerState.chainStates(state)).every(cs => ChainState.isProgressAtHead(cs) ? Stdlib_Option.isNone(ChainState.fetchState(cs).endBlock) : false)) {
79
+ return IndexerState.errorExit(state, ErrorHandling.make(new Error("No events found between startBlock and chain head. Cannot auto-detect endBlock."), undefined, undefined));
80
+ } else {
81
+ ChainMetadata.stage(state);
82
+ if (Config.shouldPruneHistory(IndexerState.config(state), IndexerState.isInReorgThreshold(state))) {
83
+ return PruneStaleHistory.schedule(state);
84
+ } else {
85
+ return;
86
+ }
87
+ }
88
+ }
89
+
90
+ async function startProcessing(state, scheduleFetch, scheduleRollback) {
91
+ if (IndexerState.isProcessing(state) || IndexerState.isStopped(state)) {
92
+ return;
93
+ }
94
+ IndexerState.beginProcessing(state);
95
+ await new Promise((resolve, param) => {
96
+ setImmediate(() => resolve());
97
+ });
98
+ let hasMoreWork = true;
99
+ while (hasMoreWork && !IndexerState.isStopped(state) && !IndexerState.isResolvingReorg(state)) {
100
+ let processedBatchesBefore = IndexerState.processedBatchesCount(state);
101
+ let exit = 0;
102
+ let val;
103
+ try {
104
+ val = await processNextBatch(state, scheduleFetch);
105
+ exit = 1;
106
+ } catch (raw_exn) {
107
+ let exn = Primitive_exceptions.internalToException(raw_exn);
108
+ IndexerState.errorExit(state, ErrorHandling.make(exn, undefined, IndexerState.unexpectedErrorMsg));
109
+ }
110
+ if (exit === 1) {
111
+ hasMoreWork = IndexerState.processedBatchesCount(state) > processedBatchesBefore;
112
+ }
113
+ };
114
+ IndexerState.endProcessing(state);
115
+ if (IndexerState.isResolvingReorg(state)) {
116
+ return scheduleRollback();
117
+ }
118
+ }
119
+
120
+ export {
121
+ yieldTick,
122
+ startProcessing,
123
+ processNextBatch,
124
+ }
125
+ /* Utils Not a pure module */