envio 3.0.0-alpha.2 → 3.0.0-alpha.20

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 (175) hide show
  1. package/README.md +164 -30
  2. package/bin.mjs +49 -0
  3. package/evm.schema.json +79 -169
  4. package/fuel.schema.json +50 -21
  5. package/index.d.ts +497 -1
  6. package/index.js +4 -0
  7. package/package.json +42 -31
  8. package/rescript.json +4 -1
  9. package/src/Batch.res +11 -8
  10. package/src/Batch.res.mjs +11 -9
  11. package/src/ChainFetcher.res +531 -0
  12. package/src/ChainFetcher.res.mjs +339 -0
  13. package/src/ChainManager.res +190 -0
  14. package/src/ChainManager.res.mjs +166 -0
  15. package/src/Change.res +3 -3
  16. package/src/Config.gen.ts +19 -0
  17. package/src/Config.res +737 -22
  18. package/src/Config.res.mjs +703 -26
  19. package/src/{Indexer.res → Ctx.res} +1 -1
  20. package/src/Ecosystem.res +9 -124
  21. package/src/Ecosystem.res.mjs +19 -160
  22. package/src/Env.res +30 -74
  23. package/src/Env.res.mjs +25 -87
  24. package/src/Envio.gen.ts +3 -1
  25. package/src/Envio.res +20 -9
  26. package/src/EventProcessing.res +469 -0
  27. package/src/EventProcessing.res.mjs +337 -0
  28. package/src/EvmTypes.gen.ts +6 -0
  29. package/src/EvmTypes.res +1 -0
  30. package/src/FetchState.res +1256 -639
  31. package/src/FetchState.res.mjs +1135 -612
  32. package/src/GlobalState.res +1190 -0
  33. package/src/GlobalState.res.mjs +1183 -0
  34. package/src/GlobalStateManager.res +68 -0
  35. package/src/GlobalStateManager.res.mjs +75 -0
  36. package/src/GlobalStateManager.resi +7 -0
  37. package/src/HandlerLoader.res +89 -0
  38. package/src/HandlerLoader.res.mjs +79 -0
  39. package/src/HandlerRegister.res +357 -0
  40. package/src/HandlerRegister.res.mjs +299 -0
  41. package/src/{EventRegister.resi → HandlerRegister.resi} +13 -13
  42. package/src/Hasura.res +111 -175
  43. package/src/Hasura.res.mjs +88 -150
  44. package/src/InMemoryStore.res +1 -1
  45. package/src/InMemoryStore.res.mjs +3 -3
  46. package/src/InMemoryTable.res +1 -1
  47. package/src/InMemoryTable.res.mjs +1 -1
  48. package/src/Internal.gen.ts +4 -0
  49. package/src/Internal.res +230 -12
  50. package/src/Internal.res.mjs +115 -1
  51. package/src/LoadLayer.res +444 -0
  52. package/src/LoadLayer.res.mjs +296 -0
  53. package/src/LoadLayer.resi +32 -0
  54. package/src/LogSelection.res +33 -27
  55. package/src/LogSelection.res.mjs +6 -0
  56. package/src/Logging.res +21 -7
  57. package/src/Logging.res.mjs +16 -8
  58. package/src/Main.res +377 -0
  59. package/src/Main.res.mjs +339 -0
  60. package/src/Persistence.res +7 -21
  61. package/src/Persistence.res.mjs +3 -3
  62. package/src/PgStorage.gen.ts +10 -0
  63. package/src/PgStorage.res +116 -69
  64. package/src/PgStorage.res.d.mts +5 -0
  65. package/src/PgStorage.res.mjs +93 -50
  66. package/src/Prometheus.res +294 -224
  67. package/src/Prometheus.res.mjs +353 -340
  68. package/src/ReorgDetection.res +6 -10
  69. package/src/ReorgDetection.res.mjs +6 -6
  70. package/src/SafeCheckpointTracking.res +4 -4
  71. package/src/SafeCheckpointTracking.res.mjs +2 -2
  72. package/src/Sink.res +4 -2
  73. package/src/Sink.res.mjs +2 -1
  74. package/src/TableIndices.res +0 -1
  75. package/src/TestIndexer.res +692 -0
  76. package/src/TestIndexer.res.mjs +527 -0
  77. package/src/TestIndexerProxyStorage.res +205 -0
  78. package/src/TestIndexerProxyStorage.res.mjs +151 -0
  79. package/src/TopicFilter.res +1 -1
  80. package/src/Types.ts +1 -1
  81. package/src/UserContext.res +424 -0
  82. package/src/UserContext.res.mjs +279 -0
  83. package/src/Utils.res +97 -26
  84. package/src/Utils.res.mjs +91 -44
  85. package/src/bindings/BigInt.res +10 -0
  86. package/src/bindings/BigInt.res.mjs +15 -0
  87. package/src/bindings/ClickHouse.res +120 -23
  88. package/src/bindings/ClickHouse.res.mjs +118 -28
  89. package/src/bindings/DateFns.res +74 -0
  90. package/src/bindings/DateFns.res.mjs +22 -0
  91. package/src/bindings/EventSource.res +8 -1
  92. package/src/bindings/EventSource.res.mjs +8 -1
  93. package/src/bindings/Express.res +1 -0
  94. package/src/bindings/Hrtime.res +14 -1
  95. package/src/bindings/Hrtime.res.mjs +22 -2
  96. package/src/bindings/Hrtime.resi +4 -0
  97. package/src/bindings/Lodash.res +0 -1
  98. package/src/bindings/NodeJs.res +49 -3
  99. package/src/bindings/NodeJs.res.mjs +11 -3
  100. package/src/bindings/Pino.res +24 -10
  101. package/src/bindings/Pino.res.mjs +14 -8
  102. package/src/bindings/Postgres.gen.ts +8 -0
  103. package/src/bindings/Postgres.res +5 -1
  104. package/src/bindings/Postgres.res.d.mts +5 -0
  105. package/src/bindings/PromClient.res +0 -10
  106. package/src/bindings/PromClient.res.mjs +0 -3
  107. package/src/bindings/Vitest.res +142 -0
  108. package/src/bindings/Vitest.res.mjs +9 -0
  109. package/src/bindings/WebSocket.res +27 -0
  110. package/src/bindings/WebSocket.res.mjs +2 -0
  111. package/src/bindings/Yargs.res +8 -0
  112. package/src/bindings/Yargs.res.mjs +2 -0
  113. package/src/db/EntityHistory.res +7 -7
  114. package/src/db/EntityHistory.res.mjs +9 -9
  115. package/src/db/InternalTable.res +59 -111
  116. package/src/db/InternalTable.res.mjs +73 -104
  117. package/src/db/Table.res +27 -8
  118. package/src/db/Table.res.mjs +25 -14
  119. package/src/sources/Evm.res +84 -0
  120. package/src/sources/Evm.res.mjs +105 -0
  121. package/src/sources/EvmChain.res +94 -0
  122. package/src/sources/EvmChain.res.mjs +60 -0
  123. package/src/sources/Fuel.res +19 -34
  124. package/src/sources/Fuel.res.mjs +34 -16
  125. package/src/sources/FuelSDK.res +38 -0
  126. package/src/sources/FuelSDK.res.mjs +29 -0
  127. package/src/sources/HyperFuel.res +2 -2
  128. package/src/sources/HyperFuel.resi +1 -1
  129. package/src/sources/HyperFuelClient.res +2 -2
  130. package/src/sources/HyperFuelSource.res +33 -13
  131. package/src/sources/HyperFuelSource.res.mjs +24 -16
  132. package/src/sources/HyperSync.res +36 -6
  133. package/src/sources/HyperSync.res.mjs +9 -7
  134. package/src/sources/HyperSync.resi +4 -0
  135. package/src/sources/HyperSyncClient.res +1 -1
  136. package/src/sources/HyperSyncHeightStream.res +47 -116
  137. package/src/sources/HyperSyncHeightStream.res.mjs +46 -73
  138. package/src/sources/HyperSyncSource.res +118 -139
  139. package/src/sources/HyperSyncSource.res.mjs +104 -121
  140. package/src/sources/Rpc.res +86 -14
  141. package/src/sources/Rpc.res.mjs +101 -9
  142. package/src/sources/RpcSource.res +621 -364
  143. package/src/sources/RpcSource.res.mjs +843 -410
  144. package/src/sources/RpcWebSocketHeightStream.res +181 -0
  145. package/src/sources/RpcWebSocketHeightStream.res.mjs +196 -0
  146. package/src/sources/Source.res +7 -5
  147. package/src/sources/SourceManager.res +325 -225
  148. package/src/sources/SourceManager.res.mjs +314 -171
  149. package/src/sources/SourceManager.resi +17 -6
  150. package/src/sources/Svm.res +81 -0
  151. package/src/sources/Svm.res.mjs +90 -0
  152. package/src/tui/Tui.res +247 -0
  153. package/src/tui/Tui.res.mjs +337 -0
  154. package/src/tui/bindings/Ink.res +371 -0
  155. package/src/tui/bindings/Ink.res.mjs +72 -0
  156. package/src/tui/bindings/Style.res +123 -0
  157. package/src/tui/bindings/Style.res.mjs +2 -0
  158. package/src/tui/components/BufferedProgressBar.res +40 -0
  159. package/src/tui/components/BufferedProgressBar.res.mjs +57 -0
  160. package/src/tui/components/CustomHooks.res +122 -0
  161. package/src/tui/components/CustomHooks.res.mjs +179 -0
  162. package/src/tui/components/Messages.res +41 -0
  163. package/src/tui/components/Messages.res.mjs +75 -0
  164. package/src/tui/components/SyncETA.res +174 -0
  165. package/src/tui/components/SyncETA.res.mjs +263 -0
  166. package/src/tui/components/TuiData.res +47 -0
  167. package/src/tui/components/TuiData.res.mjs +34 -0
  168. package/svm.schema.json +112 -0
  169. package/bin.js +0 -48
  170. package/src/EventRegister.res +0 -241
  171. package/src/EventRegister.res.mjs +0 -240
  172. package/src/bindings/Ethers.gen.ts +0 -14
  173. package/src/bindings/Ethers.res +0 -204
  174. package/src/bindings/Ethers.res.mjs +0 -130
  175. /package/src/{Indexer.res.mjs → Ctx.res.mjs} +0 -0
package/src/Envio.gen.ts CHANGED
@@ -13,7 +13,9 @@ import type {S_t as RescriptSchema_S_t} from 'rescript-schema/RescriptSchema.gen
13
13
 
14
14
  export type blockEvent = { readonly number: number };
15
15
 
16
- export type fuelBlockEvent = { readonly height: number; readonly chainId: number };
16
+ export type fuelBlockEvent = { readonly height: number };
17
+
18
+ export type svmOnBlockArgs<context> = { readonly slot: number; readonly context: context };
17
19
 
18
20
  export type onBlockArgs<block,context> = { readonly block: block; readonly context: context };
19
21
 
package/src/Envio.res CHANGED
@@ -2,18 +2,14 @@
2
2
  // Should be an entry point after we get rid of the generated project.
3
3
  // Don't forget to keep index.d.ts in sync with this file.
4
4
 
5
- // EVM block event with 'number' field for backward compatibility
6
5
  @genType
7
- type blockEvent = {
8
- number: int,
9
- }
6
+ type blockEvent = {number: int}
10
7
 
11
- // Fuel block event with 'height' field
12
8
  @genType
13
- type fuelBlockEvent = {
14
- height: int,
15
- chainId: int,
16
- }
9
+ type fuelBlockEvent = {height: int}
10
+
11
+ @genType
12
+ type svmOnBlockArgs<'context> = {slot: int, context: 'context}
17
13
 
18
14
  @genType
19
15
  type onBlockArgs<'block, 'context> = {
@@ -30,6 +26,21 @@ type onBlockOptions<'chain> = {
30
26
  endBlock?: int,
31
27
  }
32
28
 
29
+ type whereOperator<'fieldType> = {
30
+ /** Matches entities where the field equals the given value. */
31
+ _eq?: 'fieldType,
32
+ /** Matches entities where the field is strictly greater than the given value. */
33
+ _gt?: 'fieldType,
34
+ /** Matches entities where the field is strictly less than the given value. */
35
+ _lt?: 'fieldType,
36
+ /** Matches entities where the field is greater than or equal to the given value. */
37
+ _gte?: 'fieldType,
38
+ /** Matches entities where the field is less than or equal to the given value. */
39
+ _lte?: 'fieldType,
40
+ /** Matches entities where the field equals any of the given values. */
41
+ _in?: array<'fieldType>,
42
+ }
43
+
33
44
  @genType.import(("./Types.ts", "Logger"))
34
45
  type logger = {
35
46
  debug: 'params. (string, ~params: {..} as 'params=?) => unit,
@@ -0,0 +1,469 @@
1
+ open Belt
2
+
3
+ let allChainsEventsProcessedToEndblock = (chainFetchers: ChainMap.t<ChainFetcher.t>) => {
4
+ chainFetchers
5
+ ->ChainMap.values
6
+ ->Array.every(cf => cf->ChainFetcher.hasProcessedToEndblock)
7
+ }
8
+
9
+ let computeChainsState = (chainFetchers: ChainMap.t<ChainFetcher.t>): Internal.chains => {
10
+ let chains = Js.Dict.empty()
11
+
12
+ chainFetchers
13
+ ->ChainMap.entries
14
+ ->Array.forEach(((chain, chainFetcher)) => {
15
+ let chainId = chain->ChainMap.Chain.toChainId->Int.toString
16
+ let isLive = chainFetcher->ChainFetcher.isReady
17
+
18
+ chains->Js.Dict.set(
19
+ chainId,
20
+ {
21
+ Internal.id: chain->ChainMap.Chain.toChainId,
22
+ isLive,
23
+ },
24
+ )
25
+ })
26
+
27
+ chains
28
+ }
29
+
30
+ let convertFieldsToJson = (fields: option<dict<unknown>>) => {
31
+ switch fields {
32
+ | None => %raw(`{}`)
33
+ | Some(fields) => {
34
+ let keys = fields->Js.Dict.keys
35
+ let new = Js.Dict.empty()
36
+ for i in 0 to keys->Js.Array2.length - 1 {
37
+ let key = keys->Js.Array2.unsafe_get(i)
38
+ let value = fields->Js.Dict.unsafeGet(key)
39
+ // Skip `undefined` values and convert bigint fields to string
40
+ // There are not fields with nested bigints, so this is safe
41
+ new->Js.Dict.set(
42
+ key,
43
+ Js.typeof(value) === "bigint"
44
+ ? value
45
+ ->(Utils.magic: unknown => bigint)
46
+ ->BigInt.toString
47
+ ->(Utils.magic: string => unknown)
48
+ : value,
49
+ )
50
+ }
51
+ new->(Utils.magic: dict<unknown> => Js.Json.t)
52
+ }
53
+ }
54
+ }
55
+
56
+ let addItemToRawEvents = (
57
+ eventItem: Internal.eventItem,
58
+ ~inMemoryStore: InMemoryStore.t,
59
+ ~config: Config.t,
60
+ ) => {
61
+ let {event, eventConfig, chain, blockNumber, timestamp: blockTimestamp} = eventItem
62
+ let {block, transaction, params, logIndex, srcAddress} = event
63
+ let chainId = chain->ChainMap.Chain.toChainId
64
+ let eventId = EventUtils.packEventIndex(~logIndex, ~blockNumber)
65
+ let blockFields =
66
+ block
67
+ ->(Utils.magic: Internal.eventBlock => option<dict<unknown>>)
68
+ ->convertFieldsToJson
69
+ let transactionFields =
70
+ transaction
71
+ ->(Utils.magic: Internal.eventTransaction => option<dict<unknown>>)
72
+ ->convertFieldsToJson
73
+
74
+ blockFields->config.ecosystem.cleanUpRawEventFieldsInPlace
75
+
76
+ // Serialize to unknown, because serializing to Js.Json.t fails for Bytes Fuel type, since it has unknown schema
77
+ let params =
78
+ params
79
+ ->S.reverseConvertOrThrow(eventConfig.paramsRawEventSchema)
80
+ ->(Utils.magic: unknown => Js.Json.t)
81
+ let params = if params === %raw(`null`) {
82
+ // Should probably make the params field nullable
83
+ // But this is currently needed to make events
84
+ // with empty params work
85
+ %raw(`"null"`)
86
+ } else {
87
+ params
88
+ }
89
+
90
+ let rawEvent: InternalTable.RawEvents.t = {
91
+ chainId,
92
+ eventId,
93
+ eventName: eventConfig.name,
94
+ contractName: eventConfig.contractName,
95
+ blockNumber,
96
+ logIndex,
97
+ srcAddress,
98
+ blockHash: block->config.ecosystem.getId,
99
+ blockTimestamp,
100
+ blockFields,
101
+ transactionFields,
102
+ params,
103
+ }
104
+
105
+ let eventIdStr = eventId->BigInt.toString
106
+
107
+ inMemoryStore.rawEvents->InMemoryTable.set({chainId, eventId: eventIdStr}, rawEvent)
108
+ }
109
+
110
+ exception ProcessingError({message: string, exn: exn, item: Internal.item})
111
+
112
+ let runEventHandlerOrThrow = async (
113
+ item: Internal.item,
114
+ ~checkpointId,
115
+ ~handler,
116
+ ~inMemoryStore,
117
+ ~loadManager,
118
+ ~persistence,
119
+ ~shouldSaveHistory,
120
+ ~chains: Internal.chains,
121
+ ~config: Config.t,
122
+ ) => {
123
+ let eventItem = item->Internal.castUnsafeEventItem
124
+
125
+ //Include the load in time before handler
126
+ let timeBeforeHandler = Hrtime.makeTimer()
127
+
128
+ try {
129
+ let contextParams: UserContext.contextParams = {
130
+ item,
131
+ checkpointId,
132
+ inMemoryStore,
133
+ loadManager,
134
+ persistence,
135
+ shouldSaveHistory,
136
+ isPreload: false,
137
+ chains,
138
+ config,
139
+ isResolved: false,
140
+ }
141
+ await handler(
142
+ (
143
+ {
144
+ event: eventItem.event,
145
+ context: UserContext.getHandlerContext(contextParams),
146
+ }: Internal.handlerArgs
147
+ ),
148
+ )
149
+ contextParams.isResolved = true
150
+ } catch {
151
+ | exn =>
152
+ raise(
153
+ ProcessingError({
154
+ message: "Unexpected error in the event handler. Please handle the error to keep the indexer running smoothly.",
155
+ item,
156
+ exn,
157
+ }),
158
+ )
159
+ }
160
+ let handlerDuration = timeBeforeHandler->Hrtime.timeSince->Hrtime.toSecondsFloat
161
+ Prometheus.ProcessingHandler.increment(
162
+ ~contract=eventItem.eventConfig.contractName,
163
+ ~event=eventItem.eventConfig.name,
164
+ ~duration=handlerDuration,
165
+ )
166
+ }
167
+
168
+ let runHandlerOrThrow = async (
169
+ item: Internal.item,
170
+ ~checkpointId,
171
+ ~inMemoryStore,
172
+ ~loadManager,
173
+ ~ctx: Ctx.t,
174
+ ~shouldSaveHistory,
175
+ ~chains: Internal.chains,
176
+ ) => {
177
+ switch item {
178
+ | Block({onBlockConfig: {handler}, blockNumber}) =>
179
+ try {
180
+ let contextParams: UserContext.contextParams = {
181
+ item,
182
+ inMemoryStore,
183
+ loadManager,
184
+ persistence: ctx.persistence,
185
+ shouldSaveHistory,
186
+ checkpointId,
187
+ isPreload: false,
188
+ chains,
189
+ config: ctx.config,
190
+ isResolved: false,
191
+ }
192
+ await handler(
193
+ Ecosystem.makeOnBlockArgs(
194
+ ~blockNumber,
195
+ ~ecosystem=ctx.config.ecosystem,
196
+ ~context=UserContext.getHandlerContext(contextParams),
197
+ ),
198
+ )
199
+ contextParams.isResolved = true
200
+ } catch {
201
+ | exn =>
202
+ raise(
203
+ ProcessingError({
204
+ message: "Unexpected error in the block handler. Please handle the error to keep the indexer running smoothly.",
205
+ item,
206
+ exn,
207
+ }),
208
+ )
209
+ }
210
+ | Event({eventConfig}) => {
211
+ switch eventConfig.handler {
212
+ | Some(handler) =>
213
+ await item->runEventHandlerOrThrow(
214
+ ~handler,
215
+ ~checkpointId,
216
+ ~inMemoryStore,
217
+ ~loadManager,
218
+ ~persistence=ctx.persistence,
219
+ ~shouldSaveHistory,
220
+ ~chains,
221
+ ~config=ctx.config,
222
+ )
223
+ | None => ()
224
+ }
225
+
226
+ if ctx.config.enableRawEvents {
227
+ item
228
+ ->Internal.castUnsafeEventItem
229
+ ->addItemToRawEvents(~inMemoryStore, ~config=ctx.config)
230
+ }
231
+ }
232
+ }
233
+ }
234
+
235
+ let preloadBatchOrThrow = async (
236
+ batch: Batch.t,
237
+ ~loadManager,
238
+ ~persistence,
239
+ ~config: Config.t,
240
+ ~inMemoryStore,
241
+ ~chains: Internal.chains,
242
+ ) => {
243
+ // On the first run of loaders, we don't care about the result,
244
+ // whether it's an error or a return type.
245
+ // We'll rerun the loader again right before the handler run,
246
+ // to avoid having a stale data returned from the loader.
247
+
248
+ let promises = []
249
+ let itemIdx = ref(0)
250
+
251
+ for checkpointIdx in 0 to batch.checkpointIds->Array.length - 1 {
252
+ let checkpointId = batch.checkpointIds->Js.Array2.unsafe_get(checkpointIdx)
253
+ let checkpointEventsProcessed =
254
+ batch.checkpointEventsProcessed->Js.Array2.unsafe_get(checkpointIdx)
255
+
256
+ for idx in 0 to checkpointEventsProcessed - 1 {
257
+ let item = batch.items->Js.Array2.unsafe_get(itemIdx.contents + idx)
258
+ switch item {
259
+ | Event({eventConfig: {handler, contractName, name: eventName}, event}) =>
260
+ switch handler {
261
+ | None => ()
262
+ | Some(handler) =>
263
+ try {
264
+ let timerRef = Prometheus.PreloadHandler.startOperation(
265
+ ~contract=contractName,
266
+ ~event=eventName,
267
+ )
268
+ promises->Array.push(
269
+ handler({
270
+ event,
271
+ context: UserContext.getHandlerContext({
272
+ item,
273
+ inMemoryStore,
274
+ loadManager,
275
+ persistence,
276
+ checkpointId,
277
+ isPreload: true,
278
+ shouldSaveHistory: false,
279
+ chains,
280
+ isResolved: false,
281
+ config,
282
+ }),
283
+ })
284
+ ->Promise.thenResolve(_ => {
285
+ timerRef->Prometheus.PreloadHandler.endOperation(
286
+ ~contract=contractName,
287
+ ~event=eventName,
288
+ )
289
+ })
290
+ ->Promise.silentCatch,
291
+ // Must have Promise.catch as well as normal catch,
292
+ // because if user throws an error before await in the handler,
293
+ // it won't create a rejected promise
294
+ )
295
+ } catch {
296
+ | _ => ()
297
+ }
298
+ }
299
+ | Block({onBlockConfig: {handler}, blockNumber}) =>
300
+ try {
301
+ promises->Array.push(
302
+ handler({
303
+ Ecosystem.makeOnBlockArgs(
304
+ ~blockNumber,
305
+ ~ecosystem=config.ecosystem,
306
+ ~context=UserContext.getHandlerContext({
307
+ item,
308
+ inMemoryStore,
309
+ loadManager,
310
+ persistence,
311
+ checkpointId,
312
+ isPreload: true,
313
+ shouldSaveHistory: false,
314
+ chains,
315
+ isResolved: false,
316
+ config,
317
+ }),
318
+ )
319
+ })->Promise.silentCatch,
320
+ )
321
+ } catch {
322
+ | _ => ()
323
+ }
324
+ }
325
+ }
326
+
327
+ itemIdx := itemIdx.contents + checkpointEventsProcessed
328
+ }
329
+
330
+ let _ = await Promise.all(promises)
331
+ }
332
+
333
+ let runBatchHandlersOrThrow = async (
334
+ batch: Batch.t,
335
+ ~inMemoryStore,
336
+ ~loadManager,
337
+ ~ctx,
338
+ ~shouldSaveHistory,
339
+ ~chains: Internal.chains,
340
+ ) => {
341
+ let itemIdx = ref(0)
342
+
343
+ for checkpointIdx in 0 to batch.checkpointIds->Array.length - 1 {
344
+ let checkpointId = batch.checkpointIds->Js.Array2.unsafe_get(checkpointIdx)
345
+ let checkpointEventsProcessed =
346
+ batch.checkpointEventsProcessed->Js.Array2.unsafe_get(checkpointIdx)
347
+
348
+ for idx in 0 to checkpointEventsProcessed - 1 {
349
+ let item = batch.items->Js.Array2.unsafe_get(itemIdx.contents + idx)
350
+
351
+ await runHandlerOrThrow(
352
+ item,
353
+ ~checkpointId,
354
+ ~inMemoryStore,
355
+ ~loadManager,
356
+ ~ctx,
357
+ ~shouldSaveHistory,
358
+ ~chains,
359
+ )
360
+ }
361
+ itemIdx := itemIdx.contents + checkpointEventsProcessed
362
+ }
363
+ }
364
+
365
+ let registerProcessEventBatchMetrics = (
366
+ ~logger,
367
+ ~loadDuration,
368
+ ~handlerDuration,
369
+ ~dbWriteDuration,
370
+ ) => {
371
+ logger->Logging.childTrace({
372
+ "msg": "Finished processing batch",
373
+ "loader_time_elapsed": loadDuration,
374
+ "handlers_time_elapsed": handlerDuration,
375
+ "write_time_elapsed": dbWriteDuration,
376
+ })
377
+
378
+ Prometheus.ProcessingBatch.registerMetrics(~loadDuration, ~handlerDuration, ~dbWriteDuration)
379
+ }
380
+
381
+ type logPartitionInfo = {
382
+ batchSize: int,
383
+ firstItemTimestamp: option<int>,
384
+ firstItemBlockNumber?: int,
385
+ lastItemBlockNumber?: int,
386
+ }
387
+
388
+ let processEventBatch = async (
389
+ ~batch: Batch.t,
390
+ ~inMemoryStore: InMemoryStore.t,
391
+ ~isInReorgThreshold,
392
+ ~loadManager,
393
+ ~ctx: Ctx.t,
394
+ ~chainFetchers: ChainMap.t<ChainFetcher.t>,
395
+ ) => {
396
+ let totalBatchSize = batch.totalBatchSize
397
+ // Compute chains state for this batch
398
+ let chains: Internal.chains = chainFetchers->computeChainsState
399
+
400
+ let logger = Logging.getLogger()
401
+ logger->Logging.childTrace({
402
+ "msg": "Started processing batch",
403
+ "totalBatchSize": totalBatchSize,
404
+ "chains": batch.progressedChainsById->Utils.Dict.mapValues(chainAfterBatch => {
405
+ {
406
+ "batchSize": chainAfterBatch.batchSize,
407
+ "progress": chainAfterBatch.progressBlockNumber,
408
+ }
409
+ }),
410
+ })
411
+
412
+ try {
413
+ let timeRef = Hrtime.makeTimer()
414
+
415
+ if batch.items->Utils.Array.notEmpty {
416
+ await batch->preloadBatchOrThrow(
417
+ ~loadManager,
418
+ ~persistence=ctx.persistence,
419
+ ~inMemoryStore,
420
+ ~chains,
421
+ ~config=ctx.config,
422
+ )
423
+ }
424
+
425
+ let elapsedTimeAfterLoaders = timeRef->Hrtime.timeSince->Hrtime.toSecondsFloat
426
+
427
+ if batch.items->Utils.Array.notEmpty {
428
+ await batch->runBatchHandlersOrThrow(
429
+ ~inMemoryStore,
430
+ ~loadManager,
431
+ ~ctx,
432
+ ~shouldSaveHistory=ctx.config->Config.shouldSaveHistory(~isInReorgThreshold),
433
+ ~chains,
434
+ )
435
+ }
436
+
437
+ let elapsedTimeAfterProcessing = timeRef->Hrtime.timeSince->Hrtime.toSecondsFloat
438
+
439
+ try {
440
+ await ctx.persistence->Persistence.writeBatch(
441
+ ~batch,
442
+ ~config=ctx.config,
443
+ ~inMemoryStore,
444
+ ~isInReorgThreshold,
445
+ )
446
+
447
+ let elapsedTimeAfterDbWrite = timeRef->Hrtime.timeSince->Hrtime.toSecondsFloat
448
+ let loaderDuration = elapsedTimeAfterLoaders
449
+ let handlerDuration = elapsedTimeAfterProcessing -. loaderDuration
450
+ let dbWriteDuration = elapsedTimeAfterDbWrite -. elapsedTimeAfterProcessing
451
+ registerProcessEventBatchMetrics(
452
+ ~logger,
453
+ ~loadDuration=loaderDuration,
454
+ ~handlerDuration,
455
+ ~dbWriteDuration,
456
+ )
457
+ Ok()
458
+ } catch {
459
+ | Persistence.StorageError({message, reason}) =>
460
+ reason->ErrorHandling.make(~msg=message, ~logger)->Error
461
+ | exn => exn->ErrorHandling.make(~msg="Failed writing batch to database", ~logger)->Error
462
+ }
463
+ } catch {
464
+ | ProcessingError({message, exn, item}) =>
465
+ exn
466
+ ->ErrorHandling.make(~msg=message, ~logger=item->Logging.getItemLogger)
467
+ ->Error
468
+ }
469
+ }