envio 3.0.0-alpha.21 → 3.0.0-alpha.22

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 (219) hide show
  1. package/bin.mjs +2 -48
  2. package/evm.schema.json +67 -0
  3. package/fuel.schema.json +67 -0
  4. package/index.d.ts +822 -38
  5. package/index.js +5 -3
  6. package/package.json +10 -8
  7. package/rescript.json +5 -9
  8. package/src/Address.res +4 -5
  9. package/src/Address.res.mjs +9 -12
  10. package/src/Api.res +15 -0
  11. package/src/Api.res.mjs +20 -0
  12. package/src/Batch.res +32 -34
  13. package/src/Batch.res.mjs +172 -187
  14. package/src/Bin.res +89 -0
  15. package/src/Bin.res.mjs +97 -0
  16. package/src/ChainFetcher.res +33 -57
  17. package/src/ChainFetcher.res.mjs +197 -227
  18. package/src/ChainManager.res +6 -14
  19. package/src/ChainManager.res.mjs +74 -85
  20. package/src/ChainMap.res +14 -16
  21. package/src/ChainMap.res.mjs +38 -38
  22. package/src/Config.res +193 -135
  23. package/src/Config.res.mjs +566 -592
  24. package/src/Core.res +182 -0
  25. package/src/Core.res.mjs +207 -0
  26. package/src/Ecosystem.res +25 -4
  27. package/src/Ecosystem.res.mjs +12 -13
  28. package/src/Env.res +20 -13
  29. package/src/Env.res.mjs +124 -113
  30. package/src/EnvSafe.res +269 -0
  31. package/src/EnvSafe.res.mjs +296 -0
  32. package/src/EnvSafe.resi +18 -0
  33. package/src/Envio.res +37 -26
  34. package/src/Envio.res.mjs +59 -60
  35. package/src/ErrorHandling.res +2 -2
  36. package/src/ErrorHandling.res.mjs +15 -15
  37. package/src/EventConfigBuilder.res +219 -81
  38. package/src/EventConfigBuilder.res.mjs +259 -202
  39. package/src/EventProcessing.res +27 -38
  40. package/src/EventProcessing.res.mjs +165 -183
  41. package/src/EventUtils.res +11 -11
  42. package/src/EventUtils.res.mjs +21 -22
  43. package/src/EvmTypes.res +0 -1
  44. package/src/EvmTypes.res.mjs +5 -5
  45. package/src/FetchState.res +360 -256
  46. package/src/FetchState.res.mjs +958 -914
  47. package/src/GlobalState.res +365 -351
  48. package/src/GlobalState.res.mjs +958 -992
  49. package/src/GlobalStateManager.res +1 -2
  50. package/src/GlobalStateManager.res.mjs +36 -44
  51. package/src/HandlerLoader.res +107 -23
  52. package/src/HandlerLoader.res.mjs +128 -38
  53. package/src/HandlerRegister.res +127 -103
  54. package/src/HandlerRegister.res.mjs +164 -164
  55. package/src/HandlerRegister.resi +12 -4
  56. package/src/Hasura.res +35 -22
  57. package/src/Hasura.res.mjs +158 -167
  58. package/src/InMemoryStore.res +20 -27
  59. package/src/InMemoryStore.res.mjs +64 -80
  60. package/src/InMemoryTable.res +34 -39
  61. package/src/InMemoryTable.res.mjs +165 -170
  62. package/src/Internal.res +52 -33
  63. package/src/Internal.res.mjs +84 -81
  64. package/src/LazyLoader.res.mjs +55 -61
  65. package/src/LoadLayer.res +77 -78
  66. package/src/LoadLayer.res.mjs +160 -189
  67. package/src/LoadManager.res +16 -21
  68. package/src/LoadManager.res.mjs +79 -84
  69. package/src/LogSelection.res +236 -68
  70. package/src/LogSelection.res.mjs +211 -141
  71. package/src/Logging.res +13 -9
  72. package/src/Logging.res.mjs +130 -143
  73. package/src/Main.res +428 -51
  74. package/src/Main.res.mjs +528 -271
  75. package/src/Persistence.res +77 -84
  76. package/src/Persistence.res.mjs +131 -132
  77. package/src/PgStorage.res +291 -167
  78. package/src/PgStorage.res.mjs +797 -817
  79. package/src/Prometheus.res +50 -58
  80. package/src/Prometheus.res.mjs +345 -373
  81. package/src/ReorgDetection.res +22 -24
  82. package/src/ReorgDetection.res.mjs +100 -106
  83. package/src/SafeCheckpointTracking.res +7 -7
  84. package/src/SafeCheckpointTracking.res.mjs +40 -43
  85. package/src/SimulateItems.res +41 -49
  86. package/src/SimulateItems.res.mjs +257 -272
  87. package/src/Sink.res +2 -2
  88. package/src/Sink.res.mjs +22 -26
  89. package/src/TableIndices.res +1 -2
  90. package/src/TableIndices.res.mjs +42 -48
  91. package/src/TestIndexer.res +196 -189
  92. package/src/TestIndexer.res.mjs +536 -536
  93. package/src/TestIndexerProxyStorage.res +15 -16
  94. package/src/TestIndexerProxyStorage.res.mjs +98 -122
  95. package/src/TestIndexerWorker.res +4 -0
  96. package/src/TestIndexerWorker.res.mjs +7 -0
  97. package/src/Throttler.res +3 -3
  98. package/src/Throttler.res.mjs +23 -24
  99. package/src/Time.res +1 -1
  100. package/src/Time.res.mjs +18 -21
  101. package/src/TopicFilter.res +3 -3
  102. package/src/TopicFilter.res.mjs +29 -30
  103. package/src/UserContext.res +93 -54
  104. package/src/UserContext.res.mjs +197 -182
  105. package/src/Utils.res +141 -86
  106. package/src/Utils.res.mjs +334 -295
  107. package/src/bindings/BigDecimal.res +0 -2
  108. package/src/bindings/BigDecimal.res.mjs +19 -23
  109. package/src/bindings/ClickHouse.res +28 -27
  110. package/src/bindings/ClickHouse.res.mjs +243 -240
  111. package/src/bindings/DateFns.res +11 -11
  112. package/src/bindings/DateFns.res.mjs +7 -7
  113. package/src/bindings/EventSource.res.mjs +2 -2
  114. package/src/bindings/Express.res +2 -5
  115. package/src/bindings/Hrtime.res +2 -2
  116. package/src/bindings/Hrtime.res.mjs +30 -32
  117. package/src/bindings/Lodash.res.mjs +1 -1
  118. package/src/bindings/NodeJs.res +14 -9
  119. package/src/bindings/NodeJs.res.mjs +20 -20
  120. package/src/bindings/Pino.res +8 -10
  121. package/src/bindings/Pino.res.mjs +40 -43
  122. package/src/bindings/Postgres.res +2 -5
  123. package/src/bindings/Postgres.res.mjs +9 -9
  124. package/src/bindings/PromClient.res +17 -2
  125. package/src/bindings/PromClient.res.mjs +30 -7
  126. package/src/bindings/SDSL.res.mjs +2 -2
  127. package/src/bindings/Viem.res +4 -4
  128. package/src/bindings/Viem.res.mjs +20 -22
  129. package/src/bindings/Vitest.res +1 -1
  130. package/src/bindings/Vitest.res.mjs +2 -2
  131. package/src/bindings/WebSocket.res +1 -1
  132. package/src/db/EntityHistory.res +9 -3
  133. package/src/db/EntityHistory.res.mjs +84 -59
  134. package/src/db/InternalTable.res +62 -60
  135. package/src/db/InternalTable.res.mjs +271 -203
  136. package/src/db/Schema.res +1 -2
  137. package/src/db/Schema.res.mjs +28 -32
  138. package/src/db/Table.res +28 -27
  139. package/src/db/Table.res.mjs +276 -292
  140. package/src/sources/EventRouter.res +21 -16
  141. package/src/sources/EventRouter.res.mjs +55 -57
  142. package/src/sources/Evm.res +17 -1
  143. package/src/sources/Evm.res.mjs +16 -8
  144. package/src/sources/EvmChain.res +15 -17
  145. package/src/sources/EvmChain.res.mjs +40 -42
  146. package/src/sources/Fuel.res +14 -1
  147. package/src/sources/Fuel.res.mjs +16 -8
  148. package/src/sources/FuelSDK.res +1 -1
  149. package/src/sources/FuelSDK.res.mjs +6 -8
  150. package/src/sources/HyperFuel.res +8 -10
  151. package/src/sources/HyperFuel.res.mjs +113 -123
  152. package/src/sources/HyperFuelClient.res.mjs +6 -7
  153. package/src/sources/HyperFuelSource.res +19 -20
  154. package/src/sources/HyperFuelSource.res.mjs +339 -356
  155. package/src/sources/HyperSync.res +11 -13
  156. package/src/sources/HyperSync.res.mjs +206 -220
  157. package/src/sources/HyperSyncClient.res +5 -7
  158. package/src/sources/HyperSyncClient.res.mjs +70 -75
  159. package/src/sources/HyperSyncHeightStream.res +8 -9
  160. package/src/sources/HyperSyncHeightStream.res.mjs +78 -86
  161. package/src/sources/HyperSyncJsonApi.res +18 -15
  162. package/src/sources/HyperSyncJsonApi.res.mjs +201 -231
  163. package/src/sources/HyperSyncSource.res +17 -21
  164. package/src/sources/HyperSyncSource.res.mjs +268 -290
  165. package/src/sources/Rpc.res +5 -5
  166. package/src/sources/Rpc.res.mjs +168 -192
  167. package/src/sources/RpcSource.res +166 -167
  168. package/src/sources/RpcSource.res.mjs +972 -1046
  169. package/src/sources/RpcWebSocketHeightStream.res +10 -11
  170. package/src/sources/RpcWebSocketHeightStream.res.mjs +131 -145
  171. package/src/sources/SimulateSource.res +1 -1
  172. package/src/sources/SimulateSource.res.mjs +35 -38
  173. package/src/sources/Source.res +1 -1
  174. package/src/sources/Source.res.mjs +3 -3
  175. package/src/sources/SourceManager.res +39 -20
  176. package/src/sources/SourceManager.res.mjs +340 -371
  177. package/src/sources/SourceManager.resi +2 -1
  178. package/src/sources/Svm.res +12 -5
  179. package/src/sources/Svm.res.mjs +44 -41
  180. package/src/tui/Tui.res +23 -12
  181. package/src/tui/Tui.res.mjs +292 -290
  182. package/src/tui/bindings/Ink.res +2 -4
  183. package/src/tui/bindings/Ink.res.mjs +35 -41
  184. package/src/tui/components/BufferedProgressBar.res +7 -7
  185. package/src/tui/components/BufferedProgressBar.res.mjs +46 -46
  186. package/src/tui/components/CustomHooks.res +1 -2
  187. package/src/tui/components/CustomHooks.res.mjs +102 -122
  188. package/src/tui/components/Messages.res +1 -2
  189. package/src/tui/components/Messages.res.mjs +38 -42
  190. package/src/tui/components/SyncETA.res +10 -11
  191. package/src/tui/components/SyncETA.res.mjs +178 -196
  192. package/src/tui/components/TuiData.res +1 -1
  193. package/src/tui/components/TuiData.res.mjs +7 -6
  194. package/src/vendored/Rest.res +52 -66
  195. package/src/vendored/Rest.res.mjs +324 -364
  196. package/svm.schema.json +67 -0
  197. package/src/Address.gen.ts +0 -8
  198. package/src/Config.gen.ts +0 -19
  199. package/src/Envio.gen.ts +0 -55
  200. package/src/EvmTypes.gen.ts +0 -6
  201. package/src/InMemoryStore.gen.ts +0 -6
  202. package/src/Internal.gen.ts +0 -64
  203. package/src/PgStorage.gen.ts +0 -10
  204. package/src/PgStorage.res.d.mts +0 -5
  205. package/src/Types.ts +0 -56
  206. package/src/bindings/BigDecimal.gen.ts +0 -14
  207. package/src/bindings/BigDecimal.res.d.mts +0 -5
  208. package/src/bindings/BigInt.gen.ts +0 -10
  209. package/src/bindings/BigInt.res +0 -70
  210. package/src/bindings/BigInt.res.d.mts +0 -5
  211. package/src/bindings/BigInt.res.mjs +0 -154
  212. package/src/bindings/Ethers.res.d.mts +0 -5
  213. package/src/bindings/Pino.gen.ts +0 -17
  214. package/src/bindings/Postgres.gen.ts +0 -8
  215. package/src/bindings/Postgres.res.d.mts +0 -5
  216. package/src/bindings/Promise.res +0 -67
  217. package/src/bindings/Promise.res.mjs +0 -26
  218. package/src/db/InternalTable.gen.ts +0 -36
  219. package/src/sources/HyperSyncClient.gen.ts +0 -19
@@ -1,5 +1,3 @@
1
- open Belt
2
-
3
1
  type chain = ChainMap.Chain.t
4
2
  type rollbackState =
5
3
  | NoRollback
@@ -45,7 +43,7 @@ type t = {
45
43
  processedBatches: int,
46
44
  currentlyProcessingBatch: bool,
47
45
  rollbackState: rollbackState,
48
- indexerStartTime: Js.Date.t,
46
+ indexerStartTime: Date.t,
49
47
  writeThrottlers: WriteThrottlers.t,
50
48
  loadManager: LoadManager.t,
51
49
  keepProcessAlive: bool,
@@ -67,7 +65,7 @@ let make = (
67
65
  currentlyProcessingBatch: false,
68
66
  processedBatches: 0,
69
67
  chainManager,
70
- indexerStartTime: Js.Date.make(),
68
+ indexerStartTime: Date.make(),
71
69
  rollbackState: NoRollback,
72
70
  writeThrottlers: WriteThrottlers.make(),
73
71
  loadManager: LoadManager.make(),
@@ -153,25 +151,25 @@ let updateChainMetadataTable = (
153
151
  ~persistence: Persistence.t,
154
152
  ~throttler: Throttler.t,
155
153
  ) => {
156
- let chainsData: dict<InternalTable.Chains.metaFields> = Js.Dict.empty()
154
+ let chainsData: dict<InternalTable.Chains.metaFields> = Dict.make()
157
155
 
158
156
  cm.chainFetchers
159
157
  ->ChainMap.values
160
158
  ->Belt.Array.forEach(cf => {
161
- chainsData->Js.Dict.set(
159
+ chainsData->Dict.set(
162
160
  cf.chainConfig.id->Belt.Int.toString,
163
161
  {
164
- firstEventBlockNumber: cf.fetchState.firstEventBlock->Js.Null.fromOption,
162
+ firstEventBlockNumber: cf.fetchState.firstEventBlock->Null.fromOption,
165
163
  isHyperSync: (cf.sourceManager->SourceManager.getActiveSource).poweredByHyperSync,
166
164
  latestFetchedBlockNumber: cf.fetchState->FetchState.bufferBlockNumber,
167
- timestampCaughtUpToHeadOrEndblock: cf.timestampCaughtUpToHeadOrEndblock->Js.Null.fromOption,
165
+ timestampCaughtUpToHeadOrEndblock: cf.timestampCaughtUpToHeadOrEndblock->Null.fromOption,
168
166
  },
169
167
  )
170
168
  })
171
169
 
172
170
  //Don't await this set, it can happen in its own time
173
171
  throttler->Throttler.schedule(() =>
174
- persistence.storage.setChainMeta(chainsData)->Promise.ignoreValue
172
+ persistence.storage.setChainMeta(chainsData)->Utils.Promise.ignoreValue
175
173
  )
176
174
  }
177
175
 
@@ -213,7 +211,7 @@ let updateProgressedChains = (chainManager: ChainManager.t, ~batch: Batch.t, ~ct
213
211
  switch batch->Batch.findLastEventItem(~chainId=chain->ChainMap.Chain.toChainId) {
214
212
  | Some(eventItem) => {
215
213
  let blockTimestamp = eventItem.event.block->ctx.config.ecosystem.getTimestamp
216
- let currentTimeMs = Js.Date.now()->Float.toInt
214
+ let currentTimeMs = Date.now()->Float.toInt
217
215
  let blockTimestampMs = blockTimestamp * 1000
218
216
  let latencyMs = currentTimeMs - blockTimestampMs
219
217
 
@@ -278,7 +276,7 @@ let updateProgressedChains = (chainManager: ChainManager.t, ~batch: Batch.t, ~ct
278
276
  let cf = if cf->ChainFetcher.hasProcessedToEndblock {
279
277
  // in the case this is already set, don't reset and instead propagate the existing value
280
278
  let timestampCaughtUpToHeadOrEndblock =
281
- cf->ChainFetcher.isReady ? cf.timestampCaughtUpToHeadOrEndblock : Js.Date.make()->Some
279
+ cf->ChainFetcher.isReady ? cf.timestampCaughtUpToHeadOrEndblock : Date.make()->Some
282
280
  {
283
281
  ...cf,
284
282
  timestampCaughtUpToHeadOrEndblock,
@@ -292,7 +290,7 @@ let updateProgressedChains = (chainManager: ChainManager.t, ~batch: Batch.t, ~ct
292
290
  if nextQueueItemIsNone && allChainsAtHead {
293
291
  {
294
292
  ...cf,
295
- timestampCaughtUpToHeadOrEndblock: Js.Date.make()->Some,
293
+ timestampCaughtUpToHeadOrEndblock: Date.make()->Some,
296
294
  }
297
295
  } else {
298
296
  //CASE2 -> Only calculate if case1 fails
@@ -303,7 +301,7 @@ let updateProgressedChains = (chainManager: ChainManager.t, ~batch: Batch.t, ~ct
303
301
  if hasNoMoreEventsToProcess {
304
302
  {
305
303
  ...cf,
306
- timestampCaughtUpToHeadOrEndblock: Js.Date.make()->Some,
304
+ timestampCaughtUpToHeadOrEndblock: Date.make()->Some,
307
305
  }
308
306
  } else {
309
307
  //Default to just returning cf
@@ -554,7 +552,6 @@ let processPartitionQueryResponse = async (
554
552
  | _ =>
555
553
  await ChainFetcher.runContractRegistersOrThrow(
556
554
  ~itemsWithContractRegister,
557
- ~chain,
558
555
  ~config=state.ctx.config,
559
556
  )
560
557
  }
@@ -818,403 +815,420 @@ let checkAndFetchForChain = (
818
815
  //required args
819
816
  ~state,
820
817
  ~dispatchAction,
821
- ) => async chain => {
822
- let chainFetcher = state.chainManager.chainFetchers->ChainMap.get(chain)
823
- if !isPreparingRollback(state) {
824
- let {fetchState} = chainFetcher
825
-
826
- await chainFetcher.sourceManager->SourceManager.fetchNext(
827
- ~fetchState,
828
- ~waitForNewBlock=(~knownHeight) =>
829
- chainFetcher.sourceManager->waitForNewBlock(
830
- ~knownHeight,
831
- ~isLive=chainFetcher->ChainFetcher.isReady,
832
- ),
833
- ~onNewBlock=(~knownHeight) => dispatchAction(FinishWaitingForNewBlock({chain, knownHeight})),
834
- ~executeQuery=async query => {
835
- try {
836
- let response =
837
- await chainFetcher.sourceManager->executeQuery(
818
+ ) =>
819
+ async chain => {
820
+ let chainFetcher = state.chainManager.chainFetchers->ChainMap.get(chain)
821
+ if !isPreparingRollback(state) {
822
+ let {fetchState} = chainFetcher
823
+
824
+ // Reduce polling to 60s when this chain is caught up but waiting for other chains
825
+ let reducedPolling = if state.ctx.config.shouldRollbackOnReorg {
826
+ !state.chainManager.isInReorgThreshold &&
827
+ fetchState->FetchState.isReadyToEnterReorgThreshold
828
+ } else {
829
+ chainFetcher->ChainFetcher.isReady &&
830
+ state.chainManager.chainFetchers
831
+ ->ChainMap.values
832
+ ->Array.some(cf => !(cf->ChainFetcher.isReady))
833
+ }
834
+
835
+ await chainFetcher.sourceManager->SourceManager.fetchNext(
836
+ ~fetchState,
837
+ ~waitForNewBlock=(~knownHeight) =>
838
+ chainFetcher.sourceManager->waitForNewBlock(
839
+ ~knownHeight,
840
+ ~isLive=chainFetcher->ChainFetcher.isReady,
841
+ ~reducedPolling,
842
+ ),
843
+ ~onNewBlock=(~knownHeight) =>
844
+ dispatchAction(FinishWaitingForNewBlock({chain, knownHeight})),
845
+ ~executeQuery=async query => {
846
+ try {
847
+ let response = await chainFetcher.sourceManager->executeQuery(
838
848
  ~query,
839
849
  ~knownHeight=fetchState.knownHeight,
840
850
  ~isLive=chainFetcher->ChainFetcher.isReady,
841
851
  )
842
- dispatchAction(ValidatePartitionQueryResponse({chain, response, query}))
843
- } catch {
844
- | exn => dispatchAction(ErrorExit(exn->ErrorHandling.make))
845
- }
846
- },
847
- ~stateId=state.id,
848
- )
852
+ dispatchAction(ValidatePartitionQueryResponse({chain, response, query}))
853
+ } catch {
854
+ | exn => dispatchAction(ErrorExit(exn->ErrorHandling.make))
855
+ }
856
+ },
857
+ ~stateId=state.id,
858
+ )
859
+ }
849
860
  }
850
- }
851
861
 
852
862
  let injectedTaskReducer = (
853
863
  //Used for dependency injection for tests
854
864
  ~waitForNewBlock,
855
865
  ~executeQuery,
856
866
  ~getLastKnownValidBlock,
857
- ) => async (
858
- //required args
859
- state: t,
860
- task: task,
861
- ~dispatchAction,
862
- ) => {
863
- switch task {
864
- | ProcessPartitionQueryResponse(partitionQueryResponse) =>
865
- state->processPartitionQueryResponse(partitionQueryResponse, ~dispatchAction)->Promise.done
866
- | PruneStaleEntityHistory =>
867
- let runPrune = async () => {
868
- switch state.chainManager->ChainManager.getSafeCheckpointId {
869
- | None => ()
870
- | Some(safeCheckpointId) =>
871
- await state.ctx.persistence.storage.pruneStaleCheckpoints(~safeCheckpointId)
872
-
873
- for idx in 0 to state.ctx.persistence.allEntities->Array.length - 1 {
874
- if idx !== 0 {
875
- // Add some delay between entities
876
- // To unblock the pg client if it's needed for something else
877
- await Utils.delay(1000)
878
- }
879
- let entityConfig = state.ctx.persistence.allEntities->Array.getUnsafe(idx)
880
- let timeRef = Hrtime.makeTimer()
881
- try {
882
- let () = await state.ctx.persistence.storage.pruneStaleEntityHistory(
867
+ ) =>
868
+ async (
869
+ //required args
870
+ state: t,
871
+ task: task,
872
+ ~dispatchAction,
873
+ ) => {
874
+ switch task {
875
+ | ProcessPartitionQueryResponse(partitionQueryResponse) =>
876
+ state->processPartitionQueryResponse(partitionQueryResponse, ~dispatchAction)->Promise.ignore
877
+ | PruneStaleEntityHistory =>
878
+ let runPrune = async () => {
879
+ switch state.chainManager->ChainManager.getSafeCheckpointId {
880
+ | None => ()
881
+ | Some(safeCheckpointId) =>
882
+ await state.ctx.persistence.storage.pruneStaleCheckpoints(~safeCheckpointId)
883
+
884
+ for idx in 0 to state.ctx.persistence.allEntities->Array.length - 1 {
885
+ if idx !== 0 {
886
+ // Add some delay between entities
887
+ // To unblock the pg client if it's needed for something else
888
+ await Utils.delay(1000)
889
+ }
890
+ let entityConfig = state.ctx.persistence.allEntities->Array.getUnsafe(idx)
891
+ let timeRef = Hrtime.makeTimer()
892
+ try {
893
+ let () = await state.ctx.persistence.storage.pruneStaleEntityHistory(
894
+ ~entityName=entityConfig.name,
895
+ ~entityIndex=entityConfig.index,
896
+ ~safeCheckpointId,
897
+ )
898
+ } catch {
899
+ | exn =>
900
+ exn->ErrorHandling.mkLogAndRaise(
901
+ ~msg=`Failed to prune stale entity history`,
902
+ ~logger=Logging.createChild(
903
+ ~params={
904
+ "entityName": entityConfig.name,
905
+ "safeCheckpointId": safeCheckpointId,
906
+ },
907
+ ),
908
+ )
909
+ }
910
+ Prometheus.RollbackHistoryPrune.increment(
911
+ ~timeSeconds=Hrtime.timeSince(timeRef)->Hrtime.toSecondsFloat,
883
912
  ~entityName=entityConfig.name,
884
- ~entityIndex=entityConfig.index,
885
- ~safeCheckpointId,
886
- )
887
- } catch {
888
- | exn =>
889
- exn->ErrorHandling.mkLogAndRaise(
890
- ~msg=`Failed to prune stale entity history`,
891
- ~logger=Logging.createChild(
892
- ~params={
893
- "entityName": entityConfig.name,
894
- "safeCheckpointId": safeCheckpointId,
895
- },
896
- ),
897
913
  )
898
914
  }
899
- Prometheus.RollbackHistoryPrune.increment(
900
- ~timeSeconds=Hrtime.timeSince(timeRef)->Hrtime.toSecondsFloat,
901
- ~entityName=entityConfig.name,
902
- )
903
915
  }
904
916
  }
905
- }
906
- state.writeThrottlers.pruneStaleEntityHistory->Throttler.schedule(runPrune)
907
-
908
- | UpdateChainMetaDataAndCheckForExit(shouldExit) =>
909
- let {chainManager, writeThrottlers} = state
910
- switch shouldExit {
911
- | ExitWithSuccess =>
912
- updateChainMetadataTable(
913
- chainManager,
914
- ~throttler=writeThrottlers.chainMetaData,
915
- ~persistence=state.ctx.persistence,
917
+ state.writeThrottlers.pruneStaleEntityHistory->Throttler.schedule(runPrune)
918
+
919
+ | UpdateChainMetaDataAndCheckForExit(shouldExit) =>
920
+ let {chainManager, writeThrottlers} = state
921
+ switch shouldExit {
922
+ | ExitWithSuccess =>
923
+ updateChainMetadataTable(
924
+ chainManager,
925
+ ~throttler=writeThrottlers.chainMetaData,
926
+ ~persistence=state.ctx.persistence,
927
+ )
928
+ dispatchAction(SuccessExit)
929
+ | ExitWithError(msg) =>
930
+ dispatchAction(ErrorExit(ErrorHandling.make(JsError.throwWithMessage(msg))))
931
+ | NoExit =>
932
+ updateChainMetadataTable(
933
+ chainManager,
934
+ ~throttler=writeThrottlers.chainMetaData,
935
+ ~persistence=state.ctx.persistence,
936
+ )->ignore
937
+ }
938
+ | NextQuery(chainCheck) =>
939
+ let fetchForChain = checkAndFetchForChain(
940
+ ~waitForNewBlock,
941
+ ~executeQuery,
942
+ ~state,
943
+ ~dispatchAction,
916
944
  )
917
- dispatchAction(SuccessExit)
918
- | ExitWithError(msg) => dispatchAction(ErrorExit(ErrorHandling.make(Js.Exn.raiseError(msg))))
919
- | NoExit =>
920
- updateChainMetadataTable(
921
- chainManager,
922
- ~throttler=writeThrottlers.chainMetaData,
923
- ~persistence=state.ctx.persistence,
924
- )->ignore
925
- }
926
- | NextQuery(chainCheck) =>
927
- let fetchForChain = checkAndFetchForChain(
928
- ~waitForNewBlock,
929
- ~executeQuery,
930
- ~state,
931
- ~dispatchAction,
932
- )
933
945
 
934
- switch chainCheck {
935
- | Chain(chain) => await chain->fetchForChain
936
- | CheckAllChains =>
937
- //Mapping from the states chainManager so we can construct tests that don't use
938
- //all chains
939
- let _ =
940
- await state.chainManager.chainFetchers
946
+ switch chainCheck {
947
+ | Chain(chain) => await chain->fetchForChain
948
+ | CheckAllChains =>
949
+ //Mapping from the states chainManager so we can construct tests that don't use
950
+ //all chains
951
+ let _ = await state.chainManager.chainFetchers
941
952
  ->ChainMap.keys
942
953
  ->Array.map(fetchForChain(_))
943
954
  ->Promise.all
944
- }
945
- | ProcessEventBatch =>
946
- if !state.currentlyProcessingBatch && !isPreparingRollback(state) {
947
- //In the case of a rollback, use the provided in memory store
948
- //With rolled back values
949
- let rollbackInMemStore = switch state.rollbackState {
950
- | RollbackReady({diffInMemoryStore}) => Some(diffInMemoryStore)
951
- | _ => None
952
955
  }
956
+ | ProcessEventBatch =>
957
+ if !state.currentlyProcessingBatch && !isPreparingRollback(state) {
958
+ //In the case of a rollback, use the provided in memory store
959
+ //With rolled back values
960
+ let rollbackInMemStore = switch state.rollbackState {
961
+ | RollbackReady({diffInMemoryStore}) => Some(diffInMemoryStore)
962
+ | _ => None
963
+ }
953
964
 
954
- let batch =
955
- state.chainManager->ChainManager.createBatch(
956
- ~batchSizeTarget=state.ctx.config.batchSize,
957
- ~isRollback=rollbackInMemStore !== None,
958
- )
965
+ let batch =
966
+ state.chainManager->ChainManager.createBatch(
967
+ ~batchSizeTarget=state.ctx.config.batchSize,
968
+ ~isRollback=rollbackInMemStore !== None,
969
+ )
959
970
 
960
- let progressedChainsById = batch.progressedChainsById
971
+ let progressedChainsById = batch.progressedChainsById
961
972
 
962
- let isInReorgThreshold = state.chainManager.isInReorgThreshold
963
- let shouldSaveHistory = state.ctx.config->Config.shouldSaveHistory(~isInReorgThreshold)
973
+ let isInReorgThreshold = state.chainManager.isInReorgThreshold
974
+ let shouldSaveHistory = state.ctx.config->Config.shouldSaveHistory(~isInReorgThreshold)
964
975
 
965
- let isBelowReorgThreshold =
966
- !state.chainManager.isInReorgThreshold && state.ctx.config.shouldRollbackOnReorg
967
- let shouldEnterReorgThreshold =
968
- isBelowReorgThreshold &&
969
- state.chainManager.chainFetchers
970
- ->ChainMap.values
971
- ->Array.every(chainFetcher => {
972
- let fetchState = switch progressedChainsById->Utils.Dict.dangerouslyGetByIntNonOption(
973
- chainFetcher.fetchState.chainId,
974
- ) {
975
- | Some(chainAfterBatch) => chainAfterBatch.fetchState
976
- | None => chainFetcher.fetchState
977
- }
978
- fetchState->FetchState.isReadyToEnterReorgThreshold
979
- })
976
+ let isBelowReorgThreshold =
977
+ !state.chainManager.isInReorgThreshold && state.ctx.config.shouldRollbackOnReorg
978
+ let shouldEnterReorgThreshold =
979
+ isBelowReorgThreshold &&
980
+ state.chainManager.chainFetchers
981
+ ->ChainMap.values
982
+ ->Array.every(chainFetcher => {
983
+ let fetchState = switch progressedChainsById->Utils.Dict.dangerouslyGetByIntNonOption(
984
+ chainFetcher.fetchState.chainId,
985
+ ) {
986
+ | Some(chainAfterBatch) => chainAfterBatch.fetchState
987
+ | None => chainFetcher.fetchState
988
+ }
989
+ fetchState->FetchState.isReadyToEnterReorgThreshold
990
+ })
980
991
 
981
- if shouldEnterReorgThreshold {
982
- dispatchAction(EnterReorgThreshold)
983
- }
992
+ if shouldEnterReorgThreshold {
993
+ dispatchAction(EnterReorgThreshold)
994
+ }
984
995
 
985
- if progressedChainsById->Utils.Dict.isEmpty {
986
- // When resuming from persisted state, all events may already be processed.
987
- // Log the same completion message and handle exit just like EventBatchProcessed does.
988
- if EventProcessing.allChainsEventsProcessedToEndblock(state.chainManager.chainFetchers) {
989
- Logging.info("All chains are caught up to end blocks.")
990
- if !state.keepProcessAlive {
991
- updateChainMetadataTable(
992
- state.chainManager,
993
- ~persistence=state.ctx.persistence,
994
- ~throttler=state.writeThrottlers.chainMetaData,
995
- )
996
- dispatchAction(SuccessExit)
996
+ if progressedChainsById->Utils.Dict.isEmpty {
997
+ // When resuming from persisted state, all events may already be processed.
998
+ // Log the same completion message and handle exit just like EventBatchProcessed does.
999
+ if EventProcessing.allChainsEventsProcessedToEndblock(state.chainManager.chainFetchers) {
1000
+ Logging.info("All chains are caught up to end blocks.")
1001
+ if !state.keepProcessAlive {
1002
+ updateChainMetadataTable(
1003
+ state.chainManager,
1004
+ ~persistence=state.ctx.persistence,
1005
+ ~throttler=state.writeThrottlers.chainMetaData,
1006
+ )
1007
+ dispatchAction(SuccessExit)
1008
+ }
997
1009
  }
998
- }
999
- } else {
1000
- dispatchAction(StartProcessingBatch)
1001
- dispatchAction(UpdateQueues({progressedChainsById, shouldEnterReorgThreshold}))
1010
+ } else {
1011
+ dispatchAction(StartProcessingBatch)
1012
+ dispatchAction(UpdateQueues({progressedChainsById, shouldEnterReorgThreshold}))
1002
1013
 
1003
- let inMemoryStore =
1004
- rollbackInMemStore->Option.getWithDefault(
1005
- InMemoryStore.make(~entities=state.ctx.persistence.allEntities),
1006
- )
1014
+ let inMemoryStore =
1015
+ rollbackInMemStore->Option.getOr(
1016
+ InMemoryStore.make(~entities=state.ctx.persistence.allEntities),
1017
+ )
1007
1018
 
1008
- inMemoryStore->InMemoryStore.setBatchDcs(~batch, ~shouldSaveHistory)
1019
+ inMemoryStore->InMemoryStore.setBatchDcs(~batch, ~shouldSaveHistory)
1009
1020
 
1010
- switch await EventProcessing.processEventBatch(
1011
- ~batch,
1012
- ~inMemoryStore,
1013
- ~isInReorgThreshold,
1014
- ~loadManager=state.loadManager,
1015
- ~ctx=state.ctx,
1016
- ~chainFetchers=state.chainManager.chainFetchers,
1017
- ) {
1018
- | exception exn =>
1019
- //All casese should be handled/caught before this with better user messaging.
1020
- //This is just a safety in case something unexpected happens
1021
- let errHandler =
1022
- exn->ErrorHandling.make(~msg="A top level unexpected error occurred during processing")
1023
- dispatchAction(ErrorExit(errHandler))
1024
- | res =>
1025
- switch res {
1026
- | Ok() => dispatchAction(EventBatchProcessed({batch: batch}))
1027
- | Error(errHandler) => dispatchAction(ErrorExit(errHandler))
1021
+ switch await EventProcessing.processEventBatch(
1022
+ ~batch,
1023
+ ~inMemoryStore,
1024
+ ~isInReorgThreshold,
1025
+ ~loadManager=state.loadManager,
1026
+ ~ctx=state.ctx,
1027
+ ~chainFetchers=state.chainManager.chainFetchers,
1028
+ ) {
1029
+ | exception exn =>
1030
+ //All casese should be handled/caught before this with better user messaging.
1031
+ //This is just a safety in case something unexpected happens
1032
+ let errHandler =
1033
+ exn->ErrorHandling.make(
1034
+ ~msg="A top level unexpected error occurred during processing",
1035
+ )
1036
+ dispatchAction(ErrorExit(errHandler))
1037
+ | res =>
1038
+ switch res {
1039
+ | Ok() => dispatchAction(EventBatchProcessed({batch: batch}))
1040
+ | Error(errHandler) => dispatchAction(ErrorExit(errHandler))
1041
+ }
1028
1042
  }
1029
1043
  }
1030
1044
  }
1031
- }
1032
- | Rollback =>
1033
- //If it isn't processing a batch currently continue with rollback otherwise wait for current batch to finish processing
1034
- switch state {
1035
- | {rollbackState: NoRollback | RollbackReady(_)} =>
1036
- Js.Exn.raiseError("Internal error: Rollback initiated with invalid state")
1037
- | {rollbackState: ReorgDetected({chain, blockNumber: reorgBlockNumber})} => {
1038
- let chainFetcher = state.chainManager.chainFetchers->ChainMap.get(chain)
1039
-
1040
- dispatchAction(StartFindingReorgDepth)
1041
- let rollbackTargetBlockNumber =
1042
- await chainFetcher->getLastKnownValidBlock(~reorgBlockNumber)
1043
-
1044
- dispatchAction(FindReorgDepth({chain, rollbackTargetBlockNumber}))
1045
- }
1046
- // We can come to this case when event batch finished processing
1047
- // while we are still finding the reorg depth
1048
- // Do nothing here, just wait for reorg depth to be found
1049
- | {rollbackState: FindingReorgDepth} => ()
1050
- | {rollbackState: FoundReorgDepth(_), currentlyProcessingBatch: true} =>
1051
- Logging.info("Waiting for batch to finish processing before executing rollback")
1052
- | {rollbackState: FoundReorgDepth({chain: reorgChain, rollbackTargetBlockNumber})} =>
1053
- let startTime = Hrtime.makeTimer()
1054
-
1055
- let chainFetcher = state.chainManager.chainFetchers->ChainMap.get(reorgChain)
1056
-
1057
- let logger = Logging.createChildFrom(
1058
- ~logger=chainFetcher.logger,
1059
- ~params={
1060
- "action": "Rollback",
1061
- "reorgChain": reorgChain,
1062
- "targetBlockNumber": rollbackTargetBlockNumber,
1063
- },
1064
- )
1065
- logger->Logging.childInfo("Started rollback on reorg")
1066
- Prometheus.RollbackTargetBlockNumber.set(
1067
- ~blockNumber=rollbackTargetBlockNumber,
1068
- ~chain=reorgChain,
1069
- )
1070
-
1071
- let reorgChainId = reorgChain->ChainMap.Chain.toChainId
1045
+ | Rollback =>
1046
+ //If it isn't processing a batch currently continue with rollback otherwise wait for current batch to finish processing
1047
+ switch state {
1048
+ | {rollbackState: NoRollback | RollbackReady(_)} =>
1049
+ JsError.throwWithMessage("Internal error: Rollback initiated with invalid state")
1050
+ | {rollbackState: ReorgDetected({chain, blockNumber: reorgBlockNumber})} => {
1051
+ let chainFetcher = state.chainManager.chainFetchers->ChainMap.get(chain)
1052
+
1053
+ dispatchAction(StartFindingReorgDepth)
1054
+ let rollbackTargetBlockNumber = await chainFetcher->getLastKnownValidBlock(
1055
+ ~reorgBlockNumber,
1056
+ )
1072
1057
 
1073
- let rollbackTargetCheckpointId = {
1074
- switch await state.ctx.persistence.storage.getRollbackTargetCheckpoint(
1075
- ~reorgChainId,
1076
- ~lastKnownValidBlockNumber=rollbackTargetBlockNumber,
1077
- ) {
1078
- | Some(checkpointId) => checkpointId
1079
- | None => 0n
1058
+ dispatchAction(FindReorgDepth({chain, rollbackTargetBlockNumber}))
1080
1059
  }
1081
- }
1060
+ // We can come to this case when event batch finished processing
1061
+ // while we are still finding the reorg depth
1062
+ // Do nothing here, just wait for reorg depth to be found
1063
+ | {rollbackState: FindingReorgDepth} => ()
1064
+ | {rollbackState: FoundReorgDepth(_), currentlyProcessingBatch: true} =>
1065
+ Logging.info("Waiting for batch to finish processing before executing rollback")
1066
+ | {rollbackState: FoundReorgDepth({chain: reorgChain, rollbackTargetBlockNumber})} =>
1067
+ let startTime = Hrtime.makeTimer()
1068
+
1069
+ let chainFetcher = state.chainManager.chainFetchers->ChainMap.get(reorgChain)
1070
+
1071
+ let logger = Logging.createChildFrom(
1072
+ ~logger=chainFetcher.logger,
1073
+ ~params={
1074
+ "action": "Rollback",
1075
+ "reorgChain": reorgChain,
1076
+ "targetBlockNumber": rollbackTargetBlockNumber,
1077
+ },
1078
+ )
1079
+ logger->Logging.childInfo("Started rollback on reorg")
1080
+ Prometheus.RollbackTargetBlockNumber.set(
1081
+ ~blockNumber=rollbackTargetBlockNumber,
1082
+ ~chain=reorgChain,
1083
+ )
1082
1084
 
1083
- let eventsProcessedDiffByChain = Js.Dict.empty()
1084
- let newProgressBlockNumberPerChain = Js.Dict.empty()
1085
- let rollbackedProcessedEvents = ref(0.)
1085
+ let reorgChainId = reorgChain->ChainMap.Chain.toChainId
1086
1086
 
1087
- {
1088
- let rollbackProgressDiff = await state.ctx.persistence.storage.getRollbackProgressDiff(
1089
- ~rollbackTargetCheckpointId,
1090
- )
1091
- for idx in 0 to rollbackProgressDiff->Js.Array2.length - 1 {
1092
- let diff = rollbackProgressDiff->Js.Array2.unsafe_get(idx)
1093
- eventsProcessedDiffByChain->Utils.Dict.setByInt(
1094
- diff["chain_id"],
1095
- {
1096
- let eventsProcessedDiff =
1097
- Float.fromString(diff["events_processed_diff"])->Option.getExn
1098
- rollbackedProcessedEvents := rollbackedProcessedEvents.contents +. eventsProcessedDiff
1099
- eventsProcessedDiff
1100
- },
1101
- )
1102
- newProgressBlockNumberPerChain->Utils.Dict.setByInt(
1103
- diff["chain_id"],
1104
- if rollbackTargetCheckpointId === 0n && diff["chain_id"] === reorgChainId {
1105
- Pervasives.min(diff["new_progress_block_number"], rollbackTargetBlockNumber)
1106
- } else {
1107
- diff["new_progress_block_number"]
1108
- },
1109
- )
1087
+ let rollbackTargetCheckpointId = {
1088
+ switch await state.ctx.persistence.storage.getRollbackTargetCheckpoint(
1089
+ ~reorgChainId,
1090
+ ~lastKnownValidBlockNumber=rollbackTargetBlockNumber,
1091
+ ) {
1092
+ | Some(checkpointId) => checkpointId
1093
+ | None => 0n
1094
+ }
1110
1095
  }
1111
- }
1112
1096
 
1113
- let chainFetchers = state.chainManager.chainFetchers->ChainMap.mapWithKey((chain, cf) => {
1114
- switch newProgressBlockNumberPerChain->Utils.Dict.dangerouslyGetByIntNonOption(
1115
- chain->ChainMap.Chain.toChainId,
1116
- ) {
1117
- | Some(newProgressBlockNumber) =>
1118
- let fetchState =
1119
- cf.fetchState->FetchState.rollback(~targetBlockNumber=newProgressBlockNumber)
1120
- let newTotalEventsProcessed =
1121
- cf.numEventsProcessed -.
1122
- eventsProcessedDiffByChain
1123
- ->Utils.Dict.dangerouslyGetByIntNonOption(chain->ChainMap.Chain.toChainId)
1124
- ->Option.getUnsafe
1125
-
1126
- if cf.committedProgressBlockNumber !== newProgressBlockNumber {
1127
- Prometheus.ProgressBlockNumber.set(
1128
- ~blockNumber=newProgressBlockNumber,
1129
- ~chainId=chain->ChainMap.Chain.toChainId,
1097
+ let eventsProcessedDiffByChain = Dict.make()
1098
+ let newProgressBlockNumberPerChain = Dict.make()
1099
+ let rollbackedProcessedEvents = ref(0.)
1100
+
1101
+ {
1102
+ let rollbackProgressDiff = await state.ctx.persistence.storage.getRollbackProgressDiff(
1103
+ ~rollbackTargetCheckpointId,
1104
+ )
1105
+ for idx in 0 to rollbackProgressDiff->Array.length - 1 {
1106
+ let diff = rollbackProgressDiff->Array.getUnsafe(idx)
1107
+ eventsProcessedDiffByChain->Utils.Dict.setByInt(
1108
+ diff["chain_id"],
1109
+ {
1110
+ let eventsProcessedDiff =
1111
+ Float.fromString(diff["events_processed_diff"])->Option.getOrThrow
1112
+ rollbackedProcessedEvents :=
1113
+ rollbackedProcessedEvents.contents +. eventsProcessedDiff
1114
+ eventsProcessedDiff
1115
+ },
1130
1116
  )
1131
- }
1132
- if cf.numEventsProcessed !== newTotalEventsProcessed {
1133
- Prometheus.ProgressEventsCount.set(
1134
- ~processedCount=newTotalEventsProcessed,
1135
- ~chainId=chain->ChainMap.Chain.toChainId,
1117
+ newProgressBlockNumberPerChain->Utils.Dict.setByInt(
1118
+ diff["chain_id"],
1119
+ if rollbackTargetCheckpointId === 0n && diff["chain_id"] === reorgChainId {
1120
+ Pervasives.min(diff["new_progress_block_number"], rollbackTargetBlockNumber)
1121
+ } else {
1122
+ diff["new_progress_block_number"]
1123
+ },
1136
1124
  )
1137
1125
  }
1126
+ }
1138
1127
 
1139
- {
1140
- ...cf,
1141
- reorgDetection: chain == reorgChain
1142
- ? cf.reorgDetection->ReorgDetection.rollbackToValidBlockNumber(
1143
- ~blockNumber=rollbackTargetBlockNumber,
1144
- )
1145
- : cf.reorgDetection,
1146
- safeCheckpointTracking: switch cf.safeCheckpointTracking {
1147
- | Some(safeCheckpointTracking) =>
1148
- Some(
1149
- safeCheckpointTracking->SafeCheckpointTracking.rollback(
1150
- ~targetBlockNumber=newProgressBlockNumber,
1151
- ),
1128
+ let chainFetchers = state.chainManager.chainFetchers->ChainMap.mapWithKey((chain, cf) => {
1129
+ switch newProgressBlockNumberPerChain->Utils.Dict.dangerouslyGetByIntNonOption(
1130
+ chain->ChainMap.Chain.toChainId,
1131
+ ) {
1132
+ | Some(newProgressBlockNumber) =>
1133
+ let fetchState =
1134
+ cf.fetchState->FetchState.rollback(~targetBlockNumber=newProgressBlockNumber)
1135
+ let newTotalEventsProcessed =
1136
+ cf.numEventsProcessed -.
1137
+ eventsProcessedDiffByChain
1138
+ ->Utils.Dict.dangerouslyGetByIntNonOption(chain->ChainMap.Chain.toChainId)
1139
+ ->Option.getUnsafe
1140
+
1141
+ if cf.committedProgressBlockNumber !== newProgressBlockNumber {
1142
+ Prometheus.ProgressBlockNumber.set(
1143
+ ~blockNumber=newProgressBlockNumber,
1144
+ ~chainId=chain->ChainMap.Chain.toChainId,
1152
1145
  )
1153
- | None => None
1154
- },
1155
- fetchState,
1156
- committedProgressBlockNumber: newProgressBlockNumber,
1157
- numEventsProcessed: newTotalEventsProcessed,
1158
- }
1146
+ }
1147
+ if cf.numEventsProcessed !== newTotalEventsProcessed {
1148
+ Prometheus.ProgressEventsCount.set(
1149
+ ~processedCount=newTotalEventsProcessed,
1150
+ ~chainId=chain->ChainMap.Chain.toChainId,
1151
+ )
1152
+ }
1159
1153
 
1160
- | None =>
1161
- // Even without a progress diff entry, the reorg chain must have its
1162
- // reorgDetection and fetchState rolled back. Otherwise the stale block hash
1163
- // stays in dataByBlockNumber and the same reorg is re-detected on the next
1164
- // fetch, causing an infinite reorg→rollback loop.
1165
- if chain == reorgChain {
1166
1154
  {
1167
1155
  ...cf,
1168
- reorgDetection: cf.reorgDetection->ReorgDetection.rollbackToValidBlockNumber(
1169
- ~blockNumber=rollbackTargetBlockNumber,
1170
- ),
1171
- fetchState: cf.fetchState->FetchState.rollback(
1172
- ~targetBlockNumber=rollbackTargetBlockNumber,
1173
- ),
1156
+ reorgDetection: chain == reorgChain
1157
+ ? cf.reorgDetection->ReorgDetection.rollbackToValidBlockNumber(
1158
+ ~blockNumber=rollbackTargetBlockNumber,
1159
+ )
1160
+ : cf.reorgDetection,
1161
+ safeCheckpointTracking: switch cf.safeCheckpointTracking {
1162
+ | Some(safeCheckpointTracking) =>
1163
+ Some(
1164
+ safeCheckpointTracking->SafeCheckpointTracking.rollback(
1165
+ ~targetBlockNumber=newProgressBlockNumber,
1166
+ ),
1167
+ )
1168
+ | None => None
1169
+ },
1170
+ fetchState,
1171
+ committedProgressBlockNumber: newProgressBlockNumber,
1172
+ numEventsProcessed: newTotalEventsProcessed,
1173
+ }
1174
+
1175
+ | None =>
1176
+ // Even without a progress diff entry, the reorg chain must have its
1177
+ // reorgDetection and fetchState rolled back. Otherwise the stale block hash
1178
+ // stays in dataByBlockNumber and the same reorg is re-detected on the next
1179
+ // fetch, causing an infinite reorg→rollback loop.
1180
+ if chain == reorgChain {
1181
+ {
1182
+ ...cf,
1183
+ reorgDetection: cf.reorgDetection->ReorgDetection.rollbackToValidBlockNumber(
1184
+ ~blockNumber=rollbackTargetBlockNumber,
1185
+ ),
1186
+ fetchState: cf.fetchState->FetchState.rollback(
1187
+ ~targetBlockNumber=rollbackTargetBlockNumber,
1188
+ ),
1189
+ }
1190
+ } else {
1191
+ cf
1174
1192
  }
1175
- } else {
1176
- cf
1177
1193
  }
1178
- }
1179
- })
1194
+ })
1180
1195
 
1181
- // Construct in Memory store with rollback diff
1182
- let diff =
1183
- await state.ctx.persistence->Persistence.prepareRollbackDiff(
1196
+ // Construct in Memory store with rollback diff
1197
+ let diff = await state.ctx.persistence->Persistence.prepareRollbackDiff(
1184
1198
  ~rollbackTargetCheckpointId,
1185
1199
  ~rollbackDiffCheckpointId=state.chainManager.committedCheckpointId->BigInt.add(1n),
1186
1200
  )
1187
1201
 
1188
- let chainManager = {
1189
- ...state.chainManager,
1190
- chainFetchers,
1191
- }
1202
+ let chainManager = {
1203
+ ...state.chainManager,
1204
+ chainFetchers,
1205
+ }
1192
1206
 
1193
- logger->Logging.childTrace({
1194
- "msg": "Finished rollback on reorg",
1195
- "entityChanges": {
1196
- "deleted": diff["deletedEntities"],
1197
- "upserted": diff["setEntities"],
1198
- },
1199
- "rollbackedEvents": rollbackedProcessedEvents.contents,
1200
- "beforeCheckpointId": state.chainManager.committedCheckpointId,
1201
- "targetCheckpointId": rollbackTargetCheckpointId,
1202
- })
1203
- Prometheus.RollbackSuccess.increment(
1204
- ~timeSeconds=Hrtime.timeSince(startTime)->Hrtime.toSecondsFloat,
1205
- ~rollbackedProcessedEvents=rollbackedProcessedEvents.contents,
1206
- )
1207
+ logger->Logging.childTrace({
1208
+ "msg": "Finished rollback on reorg",
1209
+ "entityChanges": {
1210
+ "deleted": diff["deletedEntities"],
1211
+ "upserted": diff["setEntities"],
1212
+ },
1213
+ "rollbackedEvents": rollbackedProcessedEvents.contents,
1214
+ "beforeCheckpointId": state.chainManager.committedCheckpointId,
1215
+ "targetCheckpointId": rollbackTargetCheckpointId,
1216
+ })
1217
+ Prometheus.RollbackSuccess.increment(
1218
+ ~timeSeconds=Hrtime.timeSince(startTime)->Hrtime.toSecondsFloat,
1219
+ ~rollbackedProcessedEvents=rollbackedProcessedEvents.contents,
1220
+ )
1207
1221
 
1208
- dispatchAction(
1209
- SetRollbackState({
1210
- diffInMemoryStore: diff["inMemStore"],
1211
- rollbackedChainManager: chainManager,
1212
- eventsProcessedDiffByChain,
1213
- }),
1214
- )
1222
+ dispatchAction(
1223
+ SetRollbackState({
1224
+ diffInMemoryStore: diff["inMemStore"],
1225
+ rollbackedChainManager: chainManager,
1226
+ eventsProcessedDiffByChain,
1227
+ }),
1228
+ )
1229
+ }
1215
1230
  }
1216
1231
  }
1217
- }
1218
1232
 
1219
1233
  let taskReducer = injectedTaskReducer(
1220
1234
  ~waitForNewBlock=SourceManager.waitForNewBlock,