envio 3.0.2 → 3.1.0-rc.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 (91) hide show
  1. package/README.md +0 -1
  2. package/evm.schema.json +15 -8
  3. package/fuel.schema.json +19 -12
  4. package/index.d.ts +0 -2
  5. package/package.json +6 -7
  6. package/rescript.json +1 -1
  7. package/src/Batch.res +4 -214
  8. package/src/Batch.res.mjs +6 -165
  9. package/src/ChainFetcher.res +4 -5
  10. package/src/ChainFetcher.res.mjs +6 -7
  11. package/src/ChainManager.res +10 -9
  12. package/src/ChainManager.res.mjs +6 -10
  13. package/src/Config.res +9 -25
  14. package/src/Config.res.mjs +17 -27
  15. package/src/Core.res +7 -0
  16. package/src/Ctx.res +1 -0
  17. package/src/Env.res +0 -1
  18. package/src/Env.res.mjs +0 -3
  19. package/src/EventConfigBuilder.res +13 -123
  20. package/src/EventConfigBuilder.res.mjs +6 -73
  21. package/src/EventProcessing.res +5 -29
  22. package/src/EventProcessing.res.mjs +11 -20
  23. package/src/EventUtils.res +0 -27
  24. package/src/EventUtils.res.mjs +0 -24
  25. package/src/FetchState.res +1 -11
  26. package/src/FetchState.res.mjs +2 -16
  27. package/src/GlobalState.res +23 -37
  28. package/src/GlobalState.res.mjs +10 -38
  29. package/src/HandlerLoader.res +6 -5
  30. package/src/HandlerLoader.res.mjs +27 -9
  31. package/src/HandlerRegister.res +1 -12
  32. package/src/HandlerRegister.res.mjs +1 -6
  33. package/src/HandlerRegister.resi +1 -1
  34. package/src/Hasura.res +96 -32
  35. package/src/Hasura.res.mjs +93 -38
  36. package/src/InMemoryStore.res +181 -45
  37. package/src/InMemoryStore.res.mjs +143 -40
  38. package/src/InMemoryTable.res +147 -247
  39. package/src/InMemoryTable.res.mjs +131 -230
  40. package/src/Internal.res +10 -34
  41. package/src/Internal.res.mjs +9 -3
  42. package/src/LoadLayer.res +5 -5
  43. package/src/LoadLayer.res.mjs +5 -5
  44. package/src/Main.res +4 -6
  45. package/src/Main.res.mjs +26 -15
  46. package/src/Persistence.res +7 -132
  47. package/src/Persistence.res.mjs +1 -102
  48. package/src/PgStorage.res +57 -40
  49. package/src/PgStorage.res.mjs +60 -34
  50. package/src/ReorgDetection.res +35 -58
  51. package/src/ReorgDetection.res.mjs +21 -29
  52. package/src/SimulateItems.res.mjs +21 -3
  53. package/src/Sink.res +2 -2
  54. package/src/Sink.res.mjs +1 -1
  55. package/src/TableIndices.res +9 -2
  56. package/src/TableIndices.res.mjs +7 -1
  57. package/src/TestIndexer.res +53 -60
  58. package/src/TestIndexer.res.mjs +77 -63
  59. package/src/TestIndexerProxyStorage.res +4 -14
  60. package/src/TestIndexerProxyStorage.res.mjs +1 -5
  61. package/src/UserContext.res +2 -4
  62. package/src/UserContext.res.mjs +4 -5
  63. package/src/Utils.res +0 -2
  64. package/src/Utils.res.mjs +0 -3
  65. package/src/bindings/ClickHouse.res +45 -38
  66. package/src/bindings/ClickHouse.res.mjs +16 -17
  67. package/src/bindings/Vitest.res +3 -0
  68. package/src/db/InternalTable.res +59 -18
  69. package/src/db/InternalTable.res.mjs +82 -51
  70. package/src/db/Table.res +9 -2
  71. package/src/db/Table.res.mjs +10 -7
  72. package/src/sources/EvmChain.res +32 -9
  73. package/src/sources/EvmChain.res.mjs +31 -4
  74. package/src/sources/HyperFuelSource.res +14 -57
  75. package/src/sources/HyperFuelSource.res.mjs +18 -38
  76. package/src/sources/HyperSync.res +36 -101
  77. package/src/sources/HyperSync.res.mjs +42 -96
  78. package/src/sources/HyperSync.resi +4 -22
  79. package/src/sources/HyperSyncClient.res +67 -245
  80. package/src/sources/HyperSyncClient.res.mjs +47 -46
  81. package/src/sources/HyperSyncSource.res +76 -147
  82. package/src/sources/HyperSyncSource.res.mjs +61 -114
  83. package/src/sources/RpcSource.res +43 -22
  84. package/src/sources/RpcSource.res.mjs +50 -35
  85. package/src/sources/SimulateSource.res +1 -7
  86. package/src/sources/SimulateSource.res.mjs +1 -7
  87. package/src/sources/Source.res +8 -1
  88. package/src/sources/SourceManager.res +9 -0
  89. package/src/sources/SourceManager.res.mjs +10 -0
  90. package/src/sources/SourceManager.resi +2 -0
  91. package/svm.schema.json +11 -4
package/src/Main.res CHANGED
@@ -24,7 +24,6 @@ type state =
24
24
  chains: array<chainData>,
25
25
  indexerStartTime: Date.t,
26
26
  isPreRegisteringDynamicContracts: bool,
27
- isUnorderedMultichainMode: bool,
28
27
  rollbackOnReorg: bool,
29
28
  })
30
29
 
@@ -51,7 +50,6 @@ let stateSchema = S.union([
51
50
  indexerStartTime: s.matches(S.datetime(S.string)),
52
51
  // Keep the field, since Dev Console expects it to be present
53
52
  isPreRegisteringDynamicContracts: false,
54
- isUnorderedMultichainMode: s.matches(S.bool),
55
53
  rollbackOnReorg: s.matches(S.bool),
56
54
  })),
57
55
  ])
@@ -692,6 +690,10 @@ let start = async (
692
690
  Ctx.registrations,
693
691
  config,
694
692
  persistence,
693
+ inMemoryStore: InMemoryStore.make(
694
+ ~entities=persistence.allEntities,
695
+ ~committedCheckpointId=(persistence->Persistence.getInitializedState).checkpointId,
696
+ ),
695
697
  }
696
698
 
697
699
  let envioVersion = Utils.EnvioPackage.value.version
@@ -744,10 +746,6 @@ let start = async (
744
746
  indexerStartTime: state.indexerStartTime,
745
747
  isPreRegisteringDynamicContracts: false,
746
748
  rollbackOnReorg: ctx.config.shouldRollbackOnReorg,
747
- isUnorderedMultichainMode: switch ctx.config.multichain {
748
- | Unordered => true
749
- | Ordered => false
750
- },
751
749
  })
752
750
  }
753
751
  }
package/src/Main.res.mjs CHANGED
@@ -19,6 +19,7 @@ import Yargs from "yargs/yargs";
19
19
  import * as ChainFetcher from "./ChainFetcher.res.mjs";
20
20
  import * as ChainManager from "./ChainManager.res.mjs";
21
21
  import * as HandlerLoader from "./HandlerLoader.res.mjs";
22
+ import * as InMemoryStore from "./InMemoryStore.res.mjs";
22
23
  import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
23
24
  import * as SourceManager from "./sources/SourceManager.res.mjs";
24
25
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
@@ -58,7 +59,6 @@ let stateSchema = S$RescriptSchema.union([
58
59
  chains: s.m(S$RescriptSchema.array(chainDataSchema)),
59
60
  indexerStartTime: s.m(S$RescriptSchema.datetime(S$RescriptSchema.string, undefined)),
60
61
  isPreRegisteringDynamicContracts: false,
61
- isUnorderedMultichainMode: s.m(S$RescriptSchema.bool),
62
62
  rollbackOnReorg: s.m(S$RescriptSchema.bool)
63
63
  }))
64
64
  ]);
@@ -501,19 +501,34 @@ async function start(persistence, resetOpt, isTestOpt, exitAfterFirstEventBlockO
501
501
  let match = await HandlerLoader.registerAllHandlers(configWithoutRegistrations);
502
502
  let registrations = match[1];
503
503
  let config = match[0];
504
- let config$1;
505
- if (isTest) {
506
- let newrecord = {...config};
507
- newrecord.shouldRollbackOnReorg = false;
508
- config$1 = newrecord;
509
- } else {
510
- config$1 = config;
511
- }
504
+ let config$1 = isTest ? ({
505
+ name: config.name,
506
+ description: config.description,
507
+ handlers: config.handlers,
508
+ contractHandlers: config.contractHandlers,
509
+ shouldRollbackOnReorg: false,
510
+ shouldSaveFullHistory: config.shouldSaveFullHistory,
511
+ storage: config.storage,
512
+ chainMap: config.chainMap,
513
+ defaultChain: config.defaultChain,
514
+ ecosystem: config.ecosystem,
515
+ enableRawEvents: config.enableRawEvents,
516
+ maxAddrInPartition: config.maxAddrInPartition,
517
+ batchSize: config.batchSize,
518
+ lowercaseAddresses: config.lowercaseAddresses,
519
+ isDev: config.isDev,
520
+ userEntitiesByName: config.userEntitiesByName,
521
+ userEntities: config.userEntities,
522
+ allEntities: config.allEntities,
523
+ allEnums: config.allEnums
524
+ }) : config;
512
525
  let config$2 = patchConfig !== undefined ? patchConfig(config$1, registrations) : config$1;
526
+ let ctx_inMemoryStore = InMemoryStore.make(persistence$1.allEntities, Persistence.getInitializedState(persistence$1).checkpointId);
513
527
  let ctx = {
514
528
  registrations: registrations,
515
529
  config: config$2,
516
- persistence: persistence$1
530
+ persistence: persistence$1,
531
+ inMemoryStore: ctx_inMemoryStore
517
532
  };
518
533
  let envioVersion = Utils.EnvioPackage.value.version;
519
534
  Prometheus.Info.set(envioVersion);
@@ -546,21 +561,17 @@ async function start(persistence, resetOpt, isTestOpt, exitAfterFirstEventBlockO
546
561
  numAddresses: FetchState.numAddresses(cf.fetchState)
547
562
  };
548
563
  });
549
- let match = config$2.multichain;
550
- let tmp;
551
- tmp = match !== "ordered";
552
564
  return {
553
565
  status: "active",
554
566
  envioVersion: envioVersion,
555
567
  chains: chains,
556
568
  indexerStartTime: state.indexerStartTime,
557
569
  isPreRegisteringDynamicContracts: false,
558
- isUnorderedMultichainMode: tmp,
559
570
  rollbackOnReorg: config$2.shouldRollbackOnReorg
560
571
  };
561
572
  }, ctx, isDevelopmentMode);
562
573
  }
563
- let chainManager = ChainManager.makeFromDbState(Persistence.getInitializedState(persistence$1), config$2, registrations);
574
+ let chainManager = ChainManager.makeFromDbState(Persistence.getInitializedState(persistence$1), config$2, registrations, undefined);
564
575
  let globalState = GlobalState.make(ctx, chainManager, isDevelopmentMode, shouldUseTui, exitAfterFirstEventBlock);
565
576
  let gsManager = GlobalStateManager.make(globalState, undefined);
566
577
  if (shouldUseTui) {
@@ -48,9 +48,14 @@ type updatedEffectCache = {
48
48
  shouldInitialize: bool,
49
49
  }
50
50
 
51
+ type rollback = {
52
+ targetCheckpointId: Internal.checkpointId,
53
+ diffCheckpointId: Internal.checkpointId,
54
+ }
55
+
51
56
  type updatedEntity = {
52
57
  entityConfig: Internal.entityConfig,
53
- updates: array<Internal.inMemoryStoreEntityUpdate<Internal.entity>>,
58
+ changes: array<Change.t<Internal.entity>>,
54
59
  }
55
60
 
56
61
  type storage = {
@@ -122,7 +127,7 @@ type storage = {
122
127
  writeBatch: (
123
128
  ~batch: Batch.t,
124
129
  ~rawEvents: array<InternalTable.RawEvents.t>,
125
- ~rollbackTargetCheckpointId: option<Internal.checkpointId>,
130
+ ~rollback: option<rollback>,
126
131
  ~isInReorgThreshold: bool,
127
132
  ~config: Config.t,
128
133
  ~allEntities: array<Internal.entityConfig>,
@@ -263,133 +268,3 @@ let getInitializedState = persistence => {
263
268
  | Ready(initialState) => initialState
264
269
  }
265
270
  }
266
-
267
- let writeBatch = (
268
- persistence,
269
- ~batch,
270
- ~config,
271
- ~inMemoryStore: InMemoryStore.t,
272
- ~isInReorgThreshold,
273
- ) =>
274
- switch persistence.storageStatus {
275
- | Unknown
276
- | Initializing(_) =>
277
- JsError.throwWithMessage(`Failed to access the indexer storage. The Persistence layer is not initialized.`)
278
- | Ready({cache}) =>
279
- let updatedEntities = persistence.allEntities->Belt.Array.keepMap(entityConfig => {
280
- let updates =
281
- inMemoryStore
282
- ->InMemoryStore.getInMemTable(~entityConfig)
283
- ->InMemoryTable.Entity.updates
284
- if updates->Utils.Array.isEmpty {
285
- None
286
- } else {
287
- Some({entityConfig, updates})
288
- }
289
- })
290
- persistence.storage.writeBatch(
291
- ~batch,
292
- ~rawEvents=inMemoryStore.rawEvents->InMemoryTable.values,
293
- ~rollbackTargetCheckpointId=inMemoryStore.rollbackTargetCheckpointId,
294
- ~isInReorgThreshold,
295
- ~config,
296
- ~allEntities=persistence.allEntities,
297
- ~updatedEntities,
298
- ~updatedEffectsCache={
299
- let acc = []
300
- inMemoryStore.effects->Utils.Dict.forEach(inMemTable => {
301
- let {idsToStore, dict, effect, invalidationsCount} = inMemTable
302
- switch idsToStore {
303
- | [] => ()
304
- | ids =>
305
- let items = Belt.Array.makeUninitializedUnsafe(ids->Belt.Array.length)
306
- ids->Belt.Array.forEachWithIndex((index, id) => {
307
- items->Array.setUnsafe(
308
- index,
309
- (
310
- {
311
- id,
312
- output: dict->Dict.getUnsafe(id),
313
- }: Internal.effectCacheItem
314
- ),
315
- )
316
- })
317
- let effectName = effect.name
318
- let effectCacheRecord = switch cache->Utils.Dict.dangerouslyGetNonOption(effectName) {
319
- | Some(c) => c
320
- | None =>
321
- let c = {effectName, count: 0}
322
- cache->Dict.set(effectName, c)
323
- c
324
- }
325
- let shouldInitialize = effectCacheRecord.count === 0
326
- effectCacheRecord.count =
327
- effectCacheRecord.count + items->Array.length - invalidationsCount
328
- Prometheus.EffectCacheCount.set(~count=effectCacheRecord.count, ~effectName)
329
- acc->Array.push({effect, items, shouldInitialize})->ignore
330
- }
331
- })
332
- acc
333
- },
334
- )
335
- }
336
-
337
- let prepareRollbackDiff = async (
338
- persistence: t,
339
- ~rollbackTargetCheckpointId,
340
- ~rollbackDiffCheckpointId,
341
- ) => {
342
- let inMemStore = InMemoryStore.make(
343
- ~entities=persistence.allEntities,
344
- ~rollbackTargetCheckpointId,
345
- )
346
-
347
- let deletedEntities = Dict.make()
348
- let setEntities = Dict.make()
349
-
350
- let _ = await persistence.allEntities
351
- ->Belt.Array.map(async entityConfig => {
352
- let entityTable = inMemStore->InMemoryStore.getInMemTable(~entityConfig)
353
-
354
- let (removedIdsResult, restoredEntitiesResult) = await persistence.storage.getRollbackData(
355
- ~entityConfig,
356
- ~rollbackTargetCheckpointId,
357
- )
358
-
359
- // Process removed IDs
360
- removedIdsResult->Array.forEach(data => {
361
- deletedEntities->Utils.Dict.push(entityConfig.name, data["id"])
362
- entityTable->InMemoryTable.Entity.set(
363
- Delete({
364
- entityId: data["id"],
365
- checkpointId: rollbackDiffCheckpointId,
366
- }),
367
- ~shouldSaveHistory=false,
368
- ~containsRollbackDiffChange=true,
369
- )
370
- })
371
-
372
- let restoredEntities = restoredEntitiesResult->S.parseOrThrow(entityConfig.rowsSchema)
373
-
374
- // Process restored entities
375
- restoredEntities->Belt.Array.forEach((entity: Internal.entity) => {
376
- setEntities->Utils.Dict.push(entityConfig.name, entity.id)
377
- entityTable->InMemoryTable.Entity.set(
378
- Set({
379
- entityId: entity.id,
380
- checkpointId: rollbackDiffCheckpointId,
381
- entity,
382
- }),
383
- ~shouldSaveHistory=false,
384
- ~containsRollbackDiffChange=true,
385
- )
386
- })
387
- })
388
- ->Promise.all
389
-
390
- {
391
- "inMemStore": inMemStore,
392
- "deletedEntities": deletedEntities,
393
- "setEntities": setEntities,
394
- }
395
- }
@@ -1,16 +1,10 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
- import * as Utils from "./Utils.res.mjs";
4
3
  import * as Config from "./Config.res.mjs";
5
4
  import * as Logging from "./Logging.res.mjs";
6
- import * as Belt_Array from "@rescript/runtime/lib/es6/Belt_Array.js";
7
- import * as Prometheus from "./Prometheus.res.mjs";
8
5
  import * as EntityHistory from "./db/EntityHistory.res.mjs";
9
6
  import * as ErrorHandling from "./ErrorHandling.res.mjs";
10
- import * as InMemoryStore from "./InMemoryStore.res.mjs";
11
- import * as InMemoryTable from "./InMemoryTable.res.mjs";
12
7
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
13
- import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
14
8
  import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
15
9
 
16
10
  let StorageError = /* @__PURE__ */Primitive_exceptions.create("Persistence.StorageError");
@@ -122,106 +116,11 @@ function getInitializedState(persistence) {
122
116
  }
123
117
  }
124
118
 
125
- function writeBatch(persistence, batch, config, inMemoryStore, isInReorgThreshold) {
126
- let match = persistence.storageStatus;
127
- if (typeof match !== "object") {
128
- return Stdlib_JsError.throwWithMessage(`Failed to access the indexer storage. The Persistence layer is not initialized.`);
129
- }
130
- if (match.TAG === "Initializing") {
131
- return Stdlib_JsError.throwWithMessage(`Failed to access the indexer storage. The Persistence layer is not initialized.`);
132
- }
133
- let cache = match._0.cache;
134
- let updatedEntities = Belt_Array.keepMap(persistence.allEntities, entityConfig => {
135
- let updates = InMemoryTable.Entity.updates(InMemoryStore.getInMemTable(inMemoryStore, entityConfig));
136
- if (Utils.$$Array.isEmpty(updates)) {
137
- return;
138
- } else {
139
- return {
140
- entityConfig: entityConfig,
141
- updates: updates
142
- };
143
- }
144
- });
145
- let acc = [];
146
- return persistence.storage.writeBatch(batch, InMemoryTable.values(inMemoryStore.rawEvents), inMemoryStore.rollbackTargetCheckpointId, isInReorgThreshold, config, persistence.allEntities, (Utils.Dict.forEach(inMemoryStore.effects, inMemTable => {
147
- let idsToStore = inMemTable.idsToStore;
148
- let invalidationsCount = inMemTable.invalidationsCount;
149
- let effect = inMemTable.effect;
150
- let dict = inMemTable.dict;
151
- if (idsToStore.length === 0) {
152
- return;
153
- }
154
- let items = new Array(idsToStore.length);
155
- Belt_Array.forEachWithIndex(idsToStore, (index, id) => {
156
- items[index] = {
157
- id: id,
158
- output: dict[id]
159
- };
160
- });
161
- let effectName = effect.name;
162
- let c = cache[effectName];
163
- let effectCacheRecord;
164
- if (c !== undefined) {
165
- effectCacheRecord = c;
166
- } else {
167
- let c$1 = {
168
- effectName: effectName,
169
- count: 0
170
- };
171
- cache[effectName] = c$1;
172
- effectCacheRecord = c$1;
173
- }
174
- let shouldInitialize = effectCacheRecord.count === 0;
175
- effectCacheRecord.count = (effectCacheRecord.count + items.length | 0) - invalidationsCount | 0;
176
- Prometheus.EffectCacheCount.set(effectCacheRecord.count, effectName);
177
- acc.push({
178
- effect: effect,
179
- items: items,
180
- shouldInitialize: shouldInitialize
181
- });
182
- }), acc), updatedEntities);
183
- }
184
-
185
- async function prepareRollbackDiff(persistence, rollbackTargetCheckpointId, rollbackDiffCheckpointId) {
186
- let inMemStore = InMemoryStore.make(persistence.allEntities, rollbackTargetCheckpointId);
187
- let deletedEntities = {};
188
- let setEntities = {};
189
- await Promise.all(Belt_Array.map(persistence.allEntities, async entityConfig => {
190
- let entityTable = InMemoryStore.getInMemTable(inMemStore, entityConfig);
191
- let match = await persistence.storage.getRollbackData(entityConfig, rollbackTargetCheckpointId);
192
- match[0].forEach(data => {
193
- Utils.Dict.push(deletedEntities, entityConfig.name, data.id);
194
- InMemoryTable.Entity.set(entityTable, {
195
- type: "DELETE",
196
- entityId: data.id,
197
- checkpointId: rollbackDiffCheckpointId
198
- }, false, true);
199
- });
200
- let restoredEntities = S$RescriptSchema.parseOrThrow(match[1], entityConfig.rowsSchema);
201
- return Belt_Array.forEach(restoredEntities, entity => {
202
- Utils.Dict.push(setEntities, entityConfig.name, entity.id);
203
- InMemoryTable.Entity.set(entityTable, {
204
- type: "SET",
205
- entityId: entity.id,
206
- entity: entity,
207
- checkpointId: rollbackDiffCheckpointId
208
- }, false, true);
209
- });
210
- }));
211
- return {
212
- inMemStore: inMemStore,
213
- deletedEntities: deletedEntities,
214
- setEntities: setEntities
215
- };
216
- }
217
-
218
119
  export {
219
120
  StorageError,
220
121
  make,
221
122
  init,
222
123
  getInitializedStorageOrThrow,
223
124
  getInitializedState,
224
- writeBatch,
225
- prepareRollbackDiff,
226
125
  }
227
- /* Utils Not a pure module */
126
+ /* Config Not a pure module */
package/src/PgStorage.res CHANGED
@@ -717,7 +717,7 @@ let rec writeBatch = async (
717
717
  ~batch: Batch.t,
718
718
  ~rawEvents,
719
719
  ~pgSchema,
720
- ~rollbackTargetCheckpointId,
720
+ ~rollback: option<Persistence.rollback>,
721
721
  ~isInReorgThreshold,
722
722
  ~config: Config.t,
723
723
  ~allEntities: array<Internal.entityConfig>,
@@ -745,14 +745,55 @@ let rec writeBatch = async (
745
745
  ~items=rawEvents,
746
746
  )
747
747
 
748
- let setEntities = updatedEntities->Belt.Array.map(({entityConfig, updates}) => {
748
+ let setEntities = updatedEntities->Belt.Array.map(({entityConfig, changes}) => {
749
749
  let entitiesToSet = []
750
750
  let idsToDelete = []
751
751
 
752
- updates->Array.forEach(row => {
753
- switch row {
754
- | {latestChange: Set({entity})} => entitiesToSet->Belt.Array.push(entity)
755
- | {latestChange: Delete({entityId})} => idsToDelete->Belt.Array.push(entityId)
752
+ // The rollback-diff change is written to the entity table only, never the
753
+ // history table; when present it is an id's oldest change.
754
+ let diffCheckpointId = rollback->Option.map(r => r.diffCheckpointId)
755
+
756
+ // History batches, populated only when saving history.
757
+ let batchSetUpdates = []
758
+ let batchDeleteEntityIds = []
759
+ let batchDeleteCheckpointIds = []
760
+ let idsWithDiff = Utils.Set.make()
761
+
762
+ // Single pass over the change log: track each id's latest change (the last
763
+ // one seen) and, when saving history, fan every non-diff change out to the
764
+ // history-table batches.
765
+ let latestChangeById = Dict.make()
766
+ let orderedIds = []
767
+ changes->Belt.Array.forEach(change => {
768
+ let entityId = change->Change.getEntityId
769
+ if latestChangeById->Utils.Dict.dangerouslyGetNonOption(entityId)->Option.isNone {
770
+ orderedIds->Belt.Array.push(entityId)
771
+ }
772
+ latestChangeById->Dict.set(entityId, change)
773
+ if shouldSaveHistory {
774
+ if Some(change->Change.getCheckpointId) === diffCheckpointId {
775
+ idsWithDiff->Utils.Set.add(entityId)->ignore
776
+ } else {
777
+ switch change {
778
+ | Delete({entityId, checkpointId}) =>
779
+ batchDeleteEntityIds->Belt.Array.push(entityId)->ignore
780
+ batchDeleteCheckpointIds->Belt.Array.push(checkpointId)->ignore
781
+ | Set(_) => batchSetUpdates->Belt.Array.push(change)->ignore
782
+ }
783
+ }
784
+ }
785
+ })
786
+
787
+ let backfillHistoryIds = Utils.Set.make()
788
+ orderedIds->Belt.Array.forEach(entityId => {
789
+ switch latestChangeById->Dict.getUnsafe(entityId) {
790
+ | Set({entity}) => entitiesToSet->Belt.Array.push(entity)
791
+ | Delete({entityId}) => idsToDelete->Belt.Array.push(entityId)
792
+ }
793
+
794
+ // An id needs a history backfill iff none of its changes is the diff.
795
+ if shouldSaveHistory && !(idsWithDiff->Utils.Set.has(entityId)) {
796
+ backfillHistoryIds->Utils.Set.add(entityId)->ignore
756
797
  }
757
798
  })
758
799
 
@@ -766,34 +807,6 @@ let rec writeBatch = async (
766
807
  let promises = []
767
808
 
768
809
  if shouldSaveHistory {
769
- let backfillHistoryIds = Utils.Set.make()
770
- let batchSetUpdates = []
771
- // Use unnest approach
772
- let batchDeleteCheckpointIds = []
773
- let batchDeleteEntityIds = []
774
-
775
- updates->Array.forEach(update => {
776
- switch update {
777
- | {history, containsRollbackDiffChange} =>
778
- history->Array.forEach(
779
- (change: Change.t<'a>) => {
780
- if !containsRollbackDiffChange {
781
- backfillHistoryIds->Utils.Set.add(change->Change.getEntityId)->ignore
782
- }
783
- switch change {
784
- | Delete({entityId}) => {
785
- batchDeleteEntityIds->Belt.Array.push(entityId)->ignore
786
- batchDeleteCheckpointIds
787
- ->Belt.Array.push(change->Change.getCheckpointId)
788
- ->ignore
789
- }
790
- | Set(_) => batchSetUpdates->Array.push(change)->ignore
791
- }
792
- },
793
- )
794
- }
795
- })
796
-
797
810
  if backfillHistoryIds->Utils.Set.size !== 0 {
798
811
  // This must run before updating entity or entity history tables
799
812
  await EntityHistory.backfillHistory(
@@ -913,8 +926,8 @@ let rec writeBatch = async (
913
926
  //In the event of a rollback, rollback all meta tables based on the given
914
927
  //valid event identifier, where all rows created after this eventIdentifier should
915
928
  //be deleted
916
- let rollbackTables = switch rollbackTargetCheckpointId {
917
- | Some(rollbackTargetCheckpointId) =>
929
+ let rollbackTables = switch rollback {
930
+ | Some({targetCheckpointId: rollbackTargetCheckpointId}) =>
918
931
  Some(
919
932
  sql => {
920
933
  let promises = allEntities->Array.map(entityConfig => {
@@ -1026,7 +1039,7 @@ let rec writeBatch = async (
1026
1039
  ~rawEvents,
1027
1040
  ~batch,
1028
1041
  ~pgSchema,
1029
- ~rollbackTargetCheckpointId,
1042
+ ~rollback,
1030
1043
  ~isInReorgThreshold,
1031
1044
  ~config,
1032
1045
  ~setEffectCacheOrThrow,
@@ -1619,7 +1632,7 @@ SELECT id, chain_id, -1, -1, contract_name FROM unnest($1::text[],$2::int[],$3::
1619
1632
  let writeBatchMethod = async (
1620
1633
  ~batch,
1621
1634
  ~rawEvents,
1622
- ~rollbackTargetCheckpointId,
1635
+ ~rollback,
1623
1636
  ~isInReorgThreshold,
1624
1637
  ~config,
1625
1638
  ~allEntities,
@@ -1665,7 +1678,7 @@ SELECT id, chain_id, -1, -1, contract_name FROM unnest($1::text[],$2::int[],$3::
1665
1678
  ~batch,
1666
1679
  ~rawEvents,
1667
1680
  ~pgSchema,
1668
- ~rollbackTargetCheckpointId,
1681
+ ~rollback,
1669
1682
  ~isInReorgThreshold,
1670
1683
  ~config,
1671
1684
  ~allEntities,
@@ -1789,7 +1802,11 @@ let makeStorageFromEnv = (
1789
1802
  secret: Env.Hasura.secret,
1790
1803
  },
1791
1804
  ~pgSchema,
1792
- ~tableNames,
1805
+ ~tableConfigs=tableNames->Array.map(tableName => {
1806
+ Hasura.tableName,
1807
+ description: None,
1808
+ columnDescriptions: dict{},
1809
+ }),
1793
1810
  )->Promise.catch(err => {
1794
1811
  Logging.errorWithExn(
1795
1812
  err->Utils.prettifyExn,