envio 3.11.0 → 3.12.1

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 (109) hide show
  1. package/index.d.ts +3 -0
  2. package/package.json +6 -6
  3. package/src/Batch.res +111 -78
  4. package/src/Batch.res.mjs +53 -46
  5. package/src/BatchProcessing.res +1 -11
  6. package/src/BatchProcessing.res.mjs +1 -4
  7. package/src/ChainFetching.res +7 -3
  8. package/src/ChainFetching.res.mjs +1 -1
  9. package/src/ChainState.res +82 -20
  10. package/src/ChainState.res.mjs +51 -13
  11. package/src/ChainState.resi +6 -1
  12. package/src/Config.res +58 -37
  13. package/src/Config.res.mjs +63 -53
  14. package/src/CrossChainState.res +50 -53
  15. package/src/CrossChainState.res.mjs +25 -23
  16. package/src/CrossChainState.resi +6 -13
  17. package/src/EffectState.res +13 -4
  18. package/src/EffectState.resi +7 -1
  19. package/src/EventProcessing.res +6 -6
  20. package/src/EventProcessing.res.mjs +6 -6
  21. package/src/FetchState.res +65 -46
  22. package/src/FetchState.res.mjs +68 -36
  23. package/src/FinalizeBackfill.res +16 -12
  24. package/src/FinalizeBackfill.res.mjs +3 -3
  25. package/src/HistoryPolicy.res +46 -0
  26. package/src/HistoryPolicy.res.mjs +50 -0
  27. package/src/InMemoryStore.res +30 -32
  28. package/src/InMemoryStore.res.mjs +33 -28
  29. package/src/InMemoryTable.res +155 -67
  30. package/src/InMemoryTable.res.mjs +151 -60
  31. package/src/IndexerState.res +66 -45
  32. package/src/IndexerState.res.mjs +57 -29
  33. package/src/IndexerState.resi +8 -13
  34. package/src/Internal.res +4 -2
  35. package/src/LoadLayer.res +68 -38
  36. package/src/LoadLayer.res.mjs +50 -64
  37. package/src/LoadLayer.resi +1 -1
  38. package/src/Main.res +25 -24
  39. package/src/Main.res.mjs +24 -25
  40. package/src/Metrics.res +49 -1
  41. package/src/Metrics.res.mjs +26 -3
  42. package/src/Persistence.res +55 -16
  43. package/src/Persistence.res.mjs +18 -3
  44. package/src/PgStorage.res +286 -180
  45. package/src/PgStorage.res.mjs +209 -122
  46. package/src/PruneStaleHistory.res +9 -15
  47. package/src/PruneStaleHistory.res.mjs +11 -20
  48. package/src/Rollback.res +9 -14
  49. package/src/Rollback.res.mjs +5 -3
  50. package/src/Sink.res +7 -5
  51. package/src/Sink.res.mjs +4 -4
  52. package/src/Staging.res +298 -0
  53. package/src/Staging.res.mjs +257 -0
  54. package/src/TestIndexer.res +14 -30
  55. package/src/TestIndexer.res.mjs +11 -9
  56. package/src/UserContext.res +20 -36
  57. package/src/UserContext.res.mjs +6 -9
  58. package/src/Utils.res +10 -4
  59. package/src/Utils.res.mjs +16 -16
  60. package/src/Writing.res +34 -20
  61. package/src/Writing.res.mjs +15 -20
  62. package/src/bindings/ClickHouse.res +41 -34
  63. package/src/bindings/ClickHouse.res.mjs +36 -30
  64. package/src/bindings/ClickHouseSink.res +45 -186
  65. package/src/bindings/ClickHouseSink.res.mjs +21 -174
  66. package/src/db/CheckpointSequence.res +109 -0
  67. package/src/db/CheckpointSequence.res.mjs +99 -0
  68. package/src/db/EntityFilter.res +487 -275
  69. package/src/db/EntityFilter.res.mjs +557 -309
  70. package/src/db/EntityHistory.res +6 -5
  71. package/src/db/EntityHistory.res.mjs +5 -5
  72. package/src/db/Frontier.res +86 -0
  73. package/src/db/Frontier.res.mjs +126 -0
  74. package/src/db/InternalTable.res +145 -27
  75. package/src/db/InternalTable.res.mjs +115 -29
  76. package/src/db/RollbackFloors.res +34 -41
  77. package/src/db/RollbackFloors.res.mjs +27 -71
  78. package/src/db/Table.res +21 -6
  79. package/src/db/Table.res.mjs +13 -4
  80. package/src/sources/BlockStore.res +8 -0
  81. package/src/sources/EvmHyperSyncSource.res +5 -1
  82. package/src/sources/EvmHyperSyncSource.res.mjs +4 -3
  83. package/src/sources/FuelHyperSyncSource.res +1 -0
  84. package/src/sources/FuelHyperSyncSource.res.mjs +1 -1
  85. package/src/sources/HyperSync.res +6 -0
  86. package/src/sources/HyperSync.res.mjs +5 -2
  87. package/src/sources/HyperSync.resi +2 -0
  88. package/src/sources/HyperSyncClient.res +6 -0
  89. package/src/sources/HyperSyncSSE.res +1 -1
  90. package/src/sources/HyperSyncSSE.res.mjs +4 -10
  91. package/src/sources/RequestStat.res +8 -1
  92. package/src/sources/RpcSource.res +1 -0
  93. package/src/sources/RpcSource.res.mjs +1 -1
  94. package/src/sources/SimulateSource.res +1 -0
  95. package/src/sources/SimulateSource.res.mjs +1 -1
  96. package/src/sources/Source.res +11 -3
  97. package/src/sources/SourceManager.res +34 -13
  98. package/src/sources/SourceManager.res.mjs +26 -9
  99. package/src/sources/SourceManager.resi +2 -0
  100. package/src/sources/SvmHyperSyncClient.res +5 -0
  101. package/src/sources/SvmHyperSyncSource.res +3 -0
  102. package/src/sources/SvmHyperSyncSource.res.mjs +4 -2
  103. package/src/tui/Tui.res +56 -143
  104. package/src/tui/Tui.res.mjs +70 -166
  105. package/src/tui/components/TuiData.res +61 -16
  106. package/src/tui/components/TuiData.res.mjs +53 -15
  107. package/svm.schema.json +0 -7
  108. package/src/db/CheckpointBounds.res +0 -53
  109. package/src/db/CheckpointBounds.res.mjs +0 -44
package/src/Writing.res CHANGED
@@ -42,12 +42,10 @@ let snapshotEffects = (state: IndexerState.t, ~cache): array<Persistence.updated
42
42
  switch idsToStore {
43
43
  | [] => ()
44
44
  | ids =>
45
- let items = ids->Array.filterMap((id): option<Internal.effectCacheItem> =>
46
- switch dict->Dict.getUnsafe(id) {
47
- | Set({entity: output}) => Some({id, output})
48
- | Delete(_) => None
49
- }
50
- )
45
+ let items = ids->Array.map((id): Internal.effectCacheItem => {
46
+ id,
47
+ output: (dict->Dict.getUnsafe(id)).output,
48
+ })
51
49
  let effectName = effect.name
52
50
  let tableName = table.tableName
53
51
  let effectCacheRecord = switch cache->Utils.Dict.dangerouslyGetNonOption(tableName) {
@@ -101,22 +99,34 @@ let runOneWrite = async (state: IndexerState.t) => {
101
99
  | None => ()
102
100
  }
103
101
  | _ =>
104
- let committedCheckpointId = state->IndexerState.committedCheckpointId
105
102
  let batch = state->IndexerState.drainBatchRun
106
- // The run's last checkpoint; entity changes above it stay queued for the next write.
107
- let upToCheckpointId = switch batch.checkpointIds->Utils.Array.last {
108
- | Some(checkpointId) => checkpointId
109
- | None => committedCheckpointId
110
- }
111
103
 
112
104
  let rollback = state->IndexerState.takeRollback
113
105
 
106
+ // Entity changes above it stay queued for the next write.
107
+ let writtenFrontier = Persistence.writtenFrontier(~batch, ~rollback)
108
+
114
109
  let updatedEntities = []
115
110
  state->IndexerState.eachEntityTable((~entityConfig, ~scope, ~table) => {
116
111
  let changes =
117
- table->InMemoryTable.Entity.snapshotChanges(~committedCheckpointId, ~upToCheckpointId)
112
+ table->InMemoryTable.Entity.snapshotChanges(
113
+ ~committedCheckpointId=state->IndexerState.committedCheckpointIdFor(~scope),
114
+ ~upToCheckpointId=config.checkpointSequence->CheckpointSequence.forScope(
115
+ writtenFrontier,
116
+ ~scope,
117
+ ),
118
+ )
118
119
  if changes->Utils.Array.notEmpty {
119
- updatedEntities->Array.push(({entityConfig, scope, changes}: Persistence.updatedEntity))
120
+ updatedEntities->Array.push(
121
+ (
122
+ {
123
+ entityConfig,
124
+ scope,
125
+ changes,
126
+ shouldSaveHistory: batch.history->HistoryPolicy.forScope(~scope),
127
+ }: Persistence.updatedEntity
128
+ ),
129
+ )
120
130
  }
121
131
  })
122
132
  let updatedEffectsCache = snapshotEffects(state, ~cache)
@@ -142,7 +152,6 @@ let runOneWrite = async (state: IndexerState.t) => {
142
152
  persistence.storage.writeBatch(
143
153
  ~batch,
144
154
  ~rollback,
145
- ~isInReorgThreshold=batch.isInReorgThreshold,
146
155
  ~config,
147
156
  ~allEntities=persistence.allEntities,
148
157
  ~updatedEntities,
@@ -155,7 +164,7 @@ let runOneWrite = async (state: IndexerState.t) => {
155
164
  PruneStaleHistory.runConcurrent(state, ~targets=pruneTargets),
156
165
  ))
157
166
 
158
- state->IndexerState.markCommitted(~upToCheckpointId)
167
+ state->IndexerState.markCommitted(~writtenFrontier)
159
168
 
160
169
  switch rollback {
161
170
  | Some({progressedChains}) if RollbackCommit.callbacks->Utils.Array.notEmpty =>
@@ -218,14 +227,19 @@ let commitBatch = (state: IndexerState.t, ~batch: Batch.t) => {
218
227
  // Drops committed entity and effect entries across all tables. With
219
228
  // keepLoadedFromDb, entries seeded from a db read are spared.
220
229
  let dropCommitted = (state: IndexerState.t, ~keepLoadedFromDb) => {
221
- let committedCheckpointId = state->IndexerState.committedCheckpointId
222
- state->IndexerState.eachEntityTable((~entityConfig as _, ~scope as _, ~table) =>
223
- table->InMemoryTable.Entity.dropCommittedChanges(~committedCheckpointId, ~keepLoadedFromDb)
230
+ state->IndexerState.eachEntityTable((~entityConfig as _, ~scope, ~table) =>
231
+ table->InMemoryTable.Entity.dropCommittedChanges(
232
+ ~committedCheckpointId=state->IndexerState.committedCheckpointIdFor(~scope),
233
+ ~keepLoadedFromDb,
234
+ )
224
235
  )
225
236
  state
226
237
  ->IndexerState.effectState
227
238
  ->EffectState.forEach(inMemTable =>
228
- inMemTable->InMemoryStore.dropCommittedEffects(~committedCheckpointId, ~keepLoadedFromDb)
239
+ inMemTable->InMemoryStore.dropCommittedEffects(
240
+ ~committedFrontier=state->IndexerState.committedFrontier,
241
+ ~keepLoadedFromDb,
242
+ )
229
243
  )
230
244
  }
231
245
 
@@ -5,14 +5,16 @@ import * as Utils from "./Utils.res.mjs";
5
5
  import * as Throttler from "./Throttler.res.mjs";
6
6
  import * as EffectState from "./EffectState.res.mjs";
7
7
  import * as Performance from "./bindings/Performance.res.mjs";
8
+ import * as Persistence from "./Persistence.res.mjs";
8
9
  import * as IndexerState from "./IndexerState.res.mjs";
9
- import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
10
+ import * as HistoryPolicy from "./HistoryPolicy.res.mjs";
10
11
  import * as InMemoryStore from "./InMemoryStore.res.mjs";
11
12
  import * as InMemoryTable from "./InMemoryTable.res.mjs";
12
13
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
13
14
  import * as RollbackCommit from "./RollbackCommit.res.mjs";
14
15
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
15
16
  import * as PruneStaleHistory from "./PruneStaleHistory.res.mjs";
17
+ import * as CheckpointSequence from "./db/CheckpointSequence.res.mjs";
16
18
  import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
17
19
 
18
20
  function getChangesCount(state) {
@@ -44,15 +46,10 @@ function snapshotEffects(state, cache) {
44
46
  let table = inMemTable.table;
45
47
  let effect = inMemTable.effect;
46
48
  if (idsToStore.length !== 0) {
47
- let items = Stdlib_Array.filterMap(idsToStore, id => {
48
- let match = dict[id];
49
- if (match.type === "SET") {
50
- return {
51
- id: id,
52
- output: match.entity
53
- };
54
- }
55
- });
49
+ let items = idsToStore.map(id => ({
50
+ id: id,
51
+ output: dict[id].output
52
+ }));
56
53
  let effectName = effect.name;
57
54
  let tableName = table.tableName;
58
55
  let c = cache[tableName];
@@ -100,19 +97,18 @@ async function runOneWrite(state) {
100
97
  return;
101
98
  }
102
99
  }
103
- let committedCheckpointId = IndexerState.committedCheckpointId(state);
104
100
  let batch = IndexerState.drainBatchRun(state);
105
- let checkpointId = Utils.$$Array.last(batch.checkpointIds);
106
- let upToCheckpointId = checkpointId !== undefined ? checkpointId : committedCheckpointId;
107
101
  let rollback = IndexerState.takeRollback(state);
102
+ let writtenFrontier = Persistence.writtenFrontier(batch, rollback);
108
103
  let updatedEntities = [];
109
104
  IndexerState.eachEntityTable(state, (entityConfig, scope, table) => {
110
- let changes = InMemoryTable.Entity.snapshotChanges(table, committedCheckpointId, upToCheckpointId);
105
+ let changes = InMemoryTable.Entity.snapshotChanges(table, IndexerState.committedCheckpointIdFor(state, scope), CheckpointSequence.forScope(config.checkpointSequence, writtenFrontier, scope));
111
106
  if (Utils.$$Array.notEmpty(changes)) {
112
107
  updatedEntities.push({
113
108
  entityConfig: entityConfig,
114
109
  scope: scope,
115
- changes: changes
110
+ changes: changes,
111
+ shouldSaveHistory: HistoryPolicy.forScope(batch.history, scope)
116
112
  });
117
113
  return;
118
114
  }
@@ -125,10 +121,10 @@ async function runOneWrite(state) {
125
121
  });
126
122
  let pruneTargets = PruneStaleHistory.select(state, writtenEntityNames, Stdlib_Option.isSome(rollback));
127
123
  await Promise.all([
128
- persistence.storage.writeBatch(batch, rollback, batch.isInReorgThreshold, config, persistence.allEntities, updatedEffectsCache, updatedEntities, registeredAddresses, chainMetaData, (storage, timeSeconds) => IndexerState.recordStorageWrite(state, storage, timeSeconds)),
124
+ persistence.storage.writeBatch(batch, rollback, config, persistence.allEntities, updatedEffectsCache, updatedEntities, registeredAddresses, chainMetaData, (storage, timeSeconds) => IndexerState.recordStorageWrite(state, storage, timeSeconds)),
129
125
  PruneStaleHistory.runConcurrent(state, pruneTargets)
130
126
  ]);
131
- IndexerState.markCommitted(state, upToCheckpointId);
127
+ IndexerState.markCommitted(state, writtenFrontier);
132
128
  if (rollback !== undefined && Utils.$$Array.notEmpty(RollbackCommit.callbacks)) {
133
129
  await RollbackCommit.fire(rollback.progressedChains);
134
130
  }
@@ -179,9 +175,8 @@ function commitBatch(state, batch) {
179
175
  }
180
176
 
181
177
  function dropCommitted(state, keepLoadedFromDb) {
182
- let committedCheckpointId = IndexerState.committedCheckpointId(state);
183
- IndexerState.eachEntityTable(state, (param, param$1, table) => InMemoryTable.Entity.dropCommittedChanges(table, committedCheckpointId, keepLoadedFromDb));
184
- EffectState.forEach(IndexerState.effectState(state), inMemTable => InMemoryStore.dropCommittedEffects(inMemTable, committedCheckpointId, keepLoadedFromDb));
178
+ IndexerState.eachEntityTable(state, (param, scope, table) => InMemoryTable.Entity.dropCommittedChanges(table, IndexerState.committedCheckpointIdFor(state, scope), keepLoadedFromDb));
179
+ EffectState.forEach(IndexerState.effectState(state), inMemTable => InMemoryStore.dropCommittedEffects(inMemTable, IndexerState.committedFrontier(state), keepLoadedFromDb));
185
180
  }
186
181
 
187
182
  async function awaitCapacityLoop(state) {
@@ -90,6 +90,7 @@ let makeColumnSpec = (
90
90
  type checkpointColumn = {
91
91
  spec: ClickHouseSink.columnSpec,
92
92
  valuesOf: Batch.t => array<unknown>,
93
+ diffValuesOf: array<InternalTable.Checkpoints.diffCheckpoint> => array<unknown>,
93
94
  }
94
95
 
95
96
  // Registration and staging both read this one list, so the column a batch's
@@ -98,12 +99,14 @@ let checkpointColumns = InternalTable.Checkpoints.columns->Array.filterMap(({
98
99
  field,
99
100
  clickHouseFieldType,
100
101
  valuesOf,
102
+ diffValuesOf,
101
103
  }) =>
102
104
  switch field {
103
105
  | Table.Field({fieldName, isNullable}) =>
104
106
  Some({
105
107
  spec: makeColumnSpec(~name=fieldName, ~fieldType=clickHouseFieldType, ~isNullable),
106
108
  valuesOf,
109
+ diffValuesOf,
107
110
  })
108
111
  | DerivedFrom(_) => None
109
112
  }
@@ -259,25 +262,18 @@ let makeRegistry = () => {
259
262
  )
260
263
 
261
264
  %%private(
262
- let fillBuilders = (
263
- ~table: ClickHouseSink.table,
264
- ~converters,
265
- ~changes: array<Change.t<Internal.entity>>,
266
- ) => {
267
- let rows = changes->Array.length
268
- let builders = table.columns->Array.map(ClickHouseSink.makeBuilder(_, ~rows))
269
- let columns = builders->Array.length
270
- for row in 0 to rows - 1 {
265
+ let fillStage = (stage, ~converters, ~changes: array<Change.t<Internal.entity>>) => {
266
+ let columns = stage->Staging.columnCount
267
+ for row in 0 to changes->Array.length - 1 {
271
268
  let change = changes->Array.getUnsafe(row)
272
269
  let (cells, write) = switch change {
273
- | Change.Set(_) => (converters.setCells, ClickHouseSink.writeValue)
274
- | Delete(_) => (converters.deleteCells, ClickHouseSink.writeDeletedValue)
270
+ | Change.Set(_) => (converters.setCells, Staging.writeValue)
271
+ | Delete(_) => (converters.deleteCells, Staging.writeDeletedValue)
275
272
  }
276
273
  for column in 0 to columns - 1 {
277
- builders->Array.getUnsafe(column)->write(~row, (cells->Array.getUnsafe(column))(change))
274
+ stage->write(~column, ~row, (cells->Array.getUnsafe(column))(change))
278
275
  }
279
276
  }
280
- builders
281
277
  }
282
278
  )
283
279
 
@@ -306,31 +302,33 @@ let checkpointsTable = (sink, ~registry) =>
306
302
  table
307
303
  }
308
304
 
309
- let stageBuilders = (sink, ~table: ClickHouseSink.table, ~builders, ~rows) =>
310
- sink->ClickHouseSink.stage(
311
- ~table=table.handle,
312
- ~rows,
313
- ~columns=builders->Array.map(ClickHouseSink.builderPayload),
314
- )
315
-
316
- let stageCheckpointsOrThrow = (sink, ~registry, ~batch: Batch.t) => {
317
- let rows = batch.checkpointIds->Array.length
305
+ let stageCheckpointsOrThrow = (
306
+ sink,
307
+ ~registry,
308
+ ~batch: Batch.t,
309
+ ~diffCheckpoints: array<InternalTable.Checkpoints.diffCheckpoint>,
310
+ ) => {
311
+ let rows = batch.checkpointIds->Array.length + diffCheckpoints->Array.length
318
312
  if rows === 0 {
319
313
  Null.null
320
314
  } else {
321
315
  let table = sink->checkpointsTable(~registry)
316
+ let stage =
317
+ sink->ClickHouseSink.arena->Staging.begin(~table=table.handle, ~rows, ~columns=table.columns)
322
318
  try {
323
319
  // The table was registered from `checkpointColumns`, in this order.
324
- let builders = table.columns->Array.map(ClickHouseSink.makeBuilder(_, ~rows))
325
- builders->Array.forEachWithIndex((builder, index) => {
326
- let columnValues = (checkpointColumns->Array.getUnsafe(index)).valuesOf(batch)
320
+ table.columns->Array.forEachWithIndex((_, column) => {
321
+ let source = checkpointColumns->Array.getUnsafe(column)
322
+ let columnValues =
323
+ source.valuesOf(batch)->Array.concat(source.diffValuesOf(diffCheckpoints))
327
324
  for row in 0 to rows - 1 {
328
- builder->ClickHouseSink.writeValue(~row, columnValues->Array.getUnsafe(row))
325
+ stage->Staging.writeValue(~column, ~row, columnValues->Array.getUnsafe(row))
329
326
  }
330
327
  })
331
- Null.make(sink->stageBuilders(~table, ~builders, ~rows))
328
+ Null.make(stage->Staging.commit)
332
329
  } catch {
333
330
  | exn =>
331
+ stage->Staging.abort
334
332
  throw(
335
333
  Persistence.StorageError({
336
334
  message: `Failed to convert checkpoints for ClickHouse table "${table.name}"`,
@@ -355,6 +353,8 @@ let stageUpdatesOrThrow = (
355
353
  let table = sink->entityTable(~registry, ~entityConfig)
356
354
  let tableName = table.name
357
355
  let cacheKey = `${entityConfig.name}|${scope->Internal.chainScopeToString}`
356
+ let stage =
357
+ sink->ClickHouseSink.arena->Staging.begin(~table=table.handle, ~rows, ~columns=table.columns)
358
358
  try {
359
359
  let converters = switch registry.converters->Utils.Dict.dangerouslyGetNonOption(cacheKey) {
360
360
  | Some(cached) => cached
@@ -363,10 +363,11 @@ let stageUpdatesOrThrow = (
363
363
  registry.converters->Dict.set(cacheKey, cached)
364
364
  cached
365
365
  }
366
- let builders = fillBuilders(~table, ~converters, ~changes)
367
- Some(sink->stageBuilders(~table, ~builders, ~rows))
366
+ stage->fillStage(~converters, ~changes)
367
+ Some(stage->Staging.commit)
368
368
  } catch {
369
369
  | exn =>
370
+ stage->Staging.abort
370
371
  throw(
371
372
  Persistence.StorageError({
372
373
  message: `Failed to convert items for ClickHouse table "${tableName}"`,
@@ -415,20 +416,26 @@ let initialize = async (sink, ~entities: array<Internal.entityConfig>) => {
415
416
 
416
417
  let resume = async (
417
418
  sink,
418
- ~checkpointId: Internal.checkpointId,
419
+ ~sequence: CheckpointSequence.t,
420
+ ~frontier: Frontier.t,
419
421
  ~chains: array<Persistence.initialChainState>,
420
422
  ~entities: array<Internal.entityConfig>,
421
423
  ) => {
422
424
  let chainProgress = chains->Array.map(chain => {
423
425
  ClickHouseSink.chainId: chain.id->ChainId.toString,
424
426
  progressBlockNumber: chain.progressBlockNumber,
427
+ committedCheckpointId: frontier->Frontier.get(chain.id)->BigInt.toString,
425
428
  })
426
429
  try await sink->ClickHouseSink.resume({
427
- checkpointId: checkpointId->BigInt.toString,
430
+ perChain: switch sequence {
431
+ | PerChain => true
432
+ | SharedAcrossChains => false
433
+ },
428
434
  chainProgress,
429
- historyTables: entities->Array.map(entityConfig => entitySpec(~entityConfig).historyTable),
430
- replicated: Env.ClickHouse.replicated(),
431
- databaseEngine: ?Env.ClickHouse.databaseEngine(),
435
+ historyTables: entities->Array.map(entityConfig => {
436
+ let spec = entitySpec(~entityConfig)
437
+ {ClickHouseSink.name: spec.historyTable, chainIdColumn: ?spec.chainIdColumn}
438
+ }),
432
439
  }) catch {
433
440
  | exn => {
434
441
  Logging.errorWithExn(exn, "Failed to resume ClickHouse storage")
@@ -5,6 +5,8 @@ import * as Table from "../db/Table.res.mjs";
5
5
  import * as Utils from "../Utils.res.mjs";
6
6
  import * as ChainId from "../ChainId.res.mjs";
7
7
  import * as Logging from "../Logging.res.mjs";
8
+ import * as Staging from "../Staging.res.mjs";
9
+ import * as Frontier from "../db/Frontier.res.mjs";
8
10
  import * as Internal from "../Internal.res.mjs";
9
11
  import * as Persistence from "../Persistence.res.mjs";
10
12
  import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
@@ -176,7 +178,8 @@ let checkpointColumns = Stdlib_Array.filterMap(InternalTable.Checkpoints.columns
176
178
  let match = field._0;
177
179
  return {
178
180
  spec: makeColumnSpec(match.fieldName, undefined, param.clickHouseFieldType, match.isNullable, undefined),
179
- valuesOf: param.valuesOf
181
+ valuesOf: param.valuesOf,
182
+ diffValuesOf: param.diffValuesOf
180
183
  };
181
184
  });
182
185
 
@@ -303,27 +306,24 @@ function makeConverters(entityConfig, scope, table) {
303
306
  };
304
307
  }
305
308
 
306
- function fillBuilders(table, converters, changes) {
307
- let rows = changes.length;
308
- let builders = table.columns.map(__x => ClickHouseSink.makeBuilder(__x, rows));
309
- let columns = builders.length;
310
- for (let row = 0; row < rows; ++row) {
309
+ function fillStage(stage, converters, changes) {
310
+ let columns = Staging.columnCount(stage);
311
+ for (let row = 0, row_finish = changes.length; row < row_finish; ++row) {
311
312
  let change = changes[row];
312
313
  let match;
313
314
  match = change.type === "SET" ? [
314
315
  converters.setCells,
315
- ClickHouseSink.writeValue
316
+ Staging.writeValue
316
317
  ] : [
317
318
  converters.deleteCells,
318
- ClickHouseSink.writeDeletedValue
319
+ Staging.writeDeletedValue
319
320
  ];
320
321
  let write = match[1];
321
322
  let cells = match[0];
322
323
  for (let column = 0; column < columns; ++column) {
323
- write(builders[column], row, cells[column](change));
324
+ write(stage, column, row, cells[column](change));
324
325
  }
325
326
  }
326
- return builders;
327
327
  }
328
328
 
329
329
  function entityTable(sink, registry, entityConfig) {
@@ -347,27 +347,25 @@ function checkpointsTable(sink, registry) {
347
347
  return table$1;
348
348
  }
349
349
 
350
- function stageBuilders(sink, table, builders, rows) {
351
- return sink.stage(table.handle, rows, builders.map(ClickHouseSink.builderPayload));
352
- }
353
-
354
- function stageCheckpointsOrThrow(sink, registry, batch) {
355
- let rows = batch.checkpointIds.length;
350
+ function stageCheckpointsOrThrow(sink, registry, batch, diffCheckpoints) {
351
+ let rows = batch.checkpointIds.length + diffCheckpoints.length | 0;
356
352
  if (rows === 0) {
357
353
  return null;
358
354
  }
359
355
  let table = checkpointsTable(sink, registry);
356
+ let stage = Staging.begin(ClickHouseSink.arena(sink), table.handle, rows, table.columns);
360
357
  try {
361
- let builders = table.columns.map(__x => ClickHouseSink.makeBuilder(__x, rows));
362
- builders.forEach((builder, index) => {
363
- let columnValues = checkpointColumns[index].valuesOf(batch);
358
+ table.columns.forEach((param, column) => {
359
+ let source = checkpointColumns[column];
360
+ let columnValues = source.valuesOf(batch).concat(source.diffValuesOf(diffCheckpoints));
364
361
  for (let row = 0; row < rows; ++row) {
365
- ClickHouseSink.writeValue(builder, row, columnValues[row]);
362
+ Staging.writeValue(stage, column, row, columnValues[row]);
366
363
  }
367
364
  });
368
- return stageBuilders(sink, table, builders, rows);
365
+ return Staging.commit(stage);
369
366
  } catch (raw_exn) {
370
367
  let exn = Primitive_exceptions.internalToException(raw_exn);
368
+ Staging.abort(stage);
371
369
  throw {
372
370
  RE_EXN_ID: Persistence.StorageError,
373
371
  message: `Failed to convert checkpoints for ClickHouse table "` + table.name + `"`,
@@ -385,6 +383,7 @@ function stageUpdatesOrThrow(sink, registry, changes, entityConfig, scope) {
385
383
  let table = entityTable(sink, registry, entityConfig);
386
384
  let tableName = table.name;
387
385
  let cacheKey = entityConfig.name + `|` + Internal.chainScopeToString(scope);
386
+ let stage = Staging.begin(ClickHouseSink.arena(sink), table.handle, rows, table.columns);
388
387
  try {
389
388
  let cached = registry.converters[cacheKey];
390
389
  let converters;
@@ -395,10 +394,11 @@ function stageUpdatesOrThrow(sink, registry, changes, entityConfig, scope) {
395
394
  registry.converters[cacheKey] = cached$1;
396
395
  converters = cached$1;
397
396
  }
398
- let builders = fillBuilders(table, converters, changes);
399
- return stageBuilders(sink, table, builders, rows);
397
+ fillStage(stage, converters, changes);
398
+ return Staging.commit(stage);
400
399
  } catch (raw_exn) {
401
400
  let exn = Primitive_exceptions.internalToException(raw_exn);
401
+ Staging.abort(stage);
402
402
  throw {
403
403
  RE_EXN_ID: Persistence.StorageError,
404
404
  message: `Failed to convert items for ClickHouse table "` + tableName + `"`,
@@ -443,18 +443,25 @@ async function initialize(sink, entities) {
443
443
  }
444
444
  }
445
445
 
446
- async function resume(sink, checkpointId, chains, entities) {
446
+ async function resume(sink, sequence, frontier, chains, entities) {
447
447
  let chainProgress = chains.map(chain => ({
448
448
  chainId: ChainId.toString(chain.id),
449
- progressBlockNumber: chain.progressBlockNumber
449
+ progressBlockNumber: chain.progressBlockNumber,
450
+ committedCheckpointId: Frontier.get(frontier, chain.id).toString()
450
451
  }));
451
452
  try {
453
+ let tmp;
454
+ tmp = sequence !== "SharedAcrossChains";
452
455
  return await sink.resume({
453
- checkpointId: checkpointId.toString(),
456
+ perChain: tmp,
454
457
  chainProgress: chainProgress,
455
- historyTables: entities.map(entityConfig => entitySpec(entityConfig).historyTable),
456
- replicated: Env.ClickHouse.replicated(),
457
- databaseEngine: Env.ClickHouse.databaseEngine()
458
+ historyTables: entities.map(entityConfig => {
459
+ let spec = entitySpec(entityConfig);
460
+ return {
461
+ name: spec.historyTable,
462
+ chainIdColumn: spec.chainIdColumn
463
+ };
464
+ })
458
465
  });
459
466
  } catch (raw_exn) {
460
467
  let exn = Primitive_exceptions.internalToException(raw_exn);
@@ -480,7 +487,6 @@ export {
480
487
  makeRegistry,
481
488
  entityTable,
482
489
  checkpointsTable,
483
- stageBuilders,
484
490
  stageCheckpointsOrThrow,
485
491
  stageUpdatesOrThrow,
486
492
  writeStagedOrThrow,