envio 2.32.2 → 3.0.0-alpha-main-clickhouse-sink

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.
@@ -147,114 +147,77 @@ function initValue(inMemTable, key, entity, allowOverWriteEntityOpt) {
147
147
  return ;
148
148
  }
149
149
  var entityIndices = new Set();
150
- var initialStoreRow;
151
150
  if (entity !== undefined) {
152
- var entity$1 = Caml_option.valFromOption(entity);
153
- updateIndices(inMemTable, entity$1, entityIndices);
154
- initialStoreRow = {
155
- TAG: "InitialReadFromDb",
156
- _0: {
157
- TAG: "AlreadySet",
158
- _0: entity$1
159
- }
160
- };
161
- } else {
162
- initialStoreRow = {
163
- TAG: "InitialReadFromDb",
164
- _0: "NotSet"
165
- };
151
+ updateIndices(inMemTable, Caml_option.valFromOption(entity), entityIndices);
166
152
  }
167
153
  inMemTable.table.dict[inMemTable.table.hash(key)] = {
168
- entityRow: initialStoreRow,
154
+ latest: entity,
155
+ status: "Loaded",
169
156
  entityIndices: entityIndices
170
157
  };
171
158
  }
172
159
 
173
- function set$1(inMemTable, entityUpdate, shouldSaveHistory, containsRollbackDiffChangeOpt) {
160
+ function set$1(inMemTable, change, shouldSaveHistory, containsRollbackDiffChangeOpt) {
174
161
  var containsRollbackDiffChange = containsRollbackDiffChangeOpt !== undefined ? containsRollbackDiffChangeOpt : false;
175
- var match = get(inMemTable.table, entityUpdate.entityId);
176
- var match$1;
162
+ var latest;
163
+ latest = change.type === "SET" ? Caml_option.some(change.entity) : undefined;
164
+ var match = get(inMemTable.table, change.entityId);
165
+ var updatedEntityRecord;
177
166
  if (match !== undefined) {
178
- var previous_values = match.entityRow;
179
- if (previous_values.TAG === "Updated") {
180
- var previous_values$1 = previous_values._0;
181
- var entityRow = {
182
- TAG: "Updated",
183
- _0: {
184
- latest: entityUpdate,
185
- history: shouldSaveHistory ? (
186
- previous_values$1.latest.checkpointId === entityUpdate.checkpointId ? Utils.$$Array.setIndexImmutable(previous_values$1.history, previous_values$1.history.length - 1 | 0, entityUpdate) : Belt_Array.concatMany([
187
- previous_values$1.history,
188
- [entityUpdate]
189
- ])
190
- ) : previous_values$1.history,
191
- containsRollbackDiffChange: previous_values$1.containsRollbackDiffChange
192
- }
193
- };
194
- match$1 = {
195
- entityRow: entityRow,
167
+ var previous_values = match.status;
168
+ if (typeof previous_values !== "object") {
169
+ updatedEntityRecord = {
170
+ latest: latest,
171
+ status: {
172
+ latestChange: change,
173
+ history: shouldSaveHistory ? [change] : Utils.$$Array.immutableEmpty,
174
+ containsRollbackDiffChange: containsRollbackDiffChange
175
+ },
196
176
  entityIndices: match.entityIndices
197
177
  };
198
178
  } else {
199
- match$1 = {
200
- entityRow: {
201
- TAG: "Updated",
202
- _0: {
203
- latest: entityUpdate,
204
- history: shouldSaveHistory ? [entityUpdate] : [],
205
- containsRollbackDiffChange: containsRollbackDiffChange
206
- }
207
- },
179
+ var newStatus = {
180
+ latestChange: change,
181
+ history: shouldSaveHistory ? (
182
+ previous_values.latestChange.checkpointId === change.checkpointId ? Utils.$$Array.setIndexImmutable(previous_values.history, previous_values.history.length - 1 | 0, change) : Belt_Array.concatMany([
183
+ previous_values.history,
184
+ [change]
185
+ ])
186
+ ) : previous_values.history,
187
+ containsRollbackDiffChange: previous_values.containsRollbackDiffChange
188
+ };
189
+ updatedEntityRecord = {
190
+ latest: latest,
191
+ status: newStatus,
208
192
  entityIndices: match.entityIndices
209
193
  };
210
194
  }
211
195
  } else {
212
- match$1 = {
213
- entityRow: {
214
- TAG: "Updated",
215
- _0: {
216
- latest: entityUpdate,
217
- history: shouldSaveHistory ? [entityUpdate] : [],
218
- containsRollbackDiffChange: containsRollbackDiffChange
219
- }
196
+ updatedEntityRecord = {
197
+ latest: latest,
198
+ status: {
199
+ latestChange: change,
200
+ history: shouldSaveHistory ? [change] : Utils.$$Array.immutableEmpty,
201
+ containsRollbackDiffChange: containsRollbackDiffChange
220
202
  },
221
203
  entityIndices: new Set()
222
204
  };
223
205
  }
224
- var entityIndices = match$1.entityIndices;
225
- var entity = entityUpdate.entityUpdateAction;
226
- if (typeof entity !== "object") {
227
- deleteEntityFromIndices(inMemTable, entityUpdate.entityId, entityIndices);
206
+ if (change.type === "SET") {
207
+ updateIndices(inMemTable, change.entity, updatedEntityRecord.entityIndices);
228
208
  } else {
229
- updateIndices(inMemTable, entity._0, entityIndices);
209
+ deleteEntityFromIndices(inMemTable, change.entityId, updatedEntityRecord.entityIndices);
230
210
  }
231
- set(inMemTable.table, entityUpdate.entityId, {
232
- entityRow: match$1.entityRow,
233
- entityIndices: entityIndices
234
- });
211
+ set(inMemTable.table, change.entityId, updatedEntityRecord);
235
212
  }
236
213
 
237
214
  function rowToEntity(row) {
238
- var match = row.entityRow;
239
- if (match.TAG === "Updated") {
240
- var entity = match._0.latest.entityUpdateAction;
241
- if (typeof entity !== "object") {
242
- return ;
243
- } else {
244
- return Caml_option.some(entity._0);
245
- }
246
- }
247
- var entity$1 = match._0;
248
- if (typeof entity$1 !== "object") {
249
- return ;
250
- } else {
251
- return Caml_option.some(entity$1._0);
252
- }
215
+ return row.latest;
253
216
  }
254
217
 
255
218
  function getUnsafe(inMemTable) {
256
219
  return function (key) {
257
- return rowToEntity(inMemTable.table.dict[key]);
220
+ return inMemTable.table.dict[key].latest;
258
221
  };
259
222
  }
260
223
 
@@ -295,7 +258,7 @@ function addEmptyIndex(inMemTable, index) {
295
258
  var fieldName = TableIndices.Index.getFieldName(index);
296
259
  var relatedEntityIds = new Set();
297
260
  Belt_Array.forEach(Js_dict.values(inMemTable.table.dict), (function (row) {
298
- var entity = rowToEntity(row);
261
+ var entity = row.latest;
299
262
  if (entity === undefined) {
300
263
  return ;
301
264
  }
@@ -340,9 +303,14 @@ function addIdToIndex(inMemTable, index, entityId) {
340
303
  }
341
304
  }
342
305
 
343
- function rows(inMemTable) {
344
- return Belt_Array.map(Js_dict.values(inMemTable.table.dict), (function (v) {
345
- return v.entityRow;
306
+ function updates(inMemTable) {
307
+ return Belt_Array.keepMapU(Js_dict.values(inMemTable.table.dict), (function (v) {
308
+ var update = v.status;
309
+ if (typeof update !== "object") {
310
+ return ;
311
+ } else {
312
+ return update;
313
+ }
346
314
  }));
347
315
  }
348
316
 
@@ -384,7 +352,7 @@ var Entity = {
384
352
  getUnsafeOnIndex: getUnsafeOnIndex,
385
353
  addEmptyIndex: addEmptyIndex,
386
354
  addIdToIndex: addIdToIndex,
387
- rows: rows,
355
+ updates: updates,
388
356
  values: values$1,
389
357
  clone: clone$1
390
358
  };
@@ -53,8 +53,6 @@ export type genericHandlerWithLoader<loader,handler,eventFilters> = {
53
53
  readonly preRegisterDynamicContracts?: boolean
54
54
  };
55
55
 
56
- export type blockEvent = { readonly number: number; readonly chainId: number };
57
-
58
56
  export type eventOptions<eventFilters> = {
59
57
  readonly wildcard?: boolean;
60
58
  readonly eventFilters?: eventFilters;
package/src/Internal.res CHANGED
@@ -169,11 +169,8 @@ type eventItem = private {
169
169
  event: event,
170
170
  }
171
171
 
172
- @genType
173
- type blockEvent = {
174
- number: int,
175
- chainId: int,
176
- }
172
+ // Opaque type to support both EVM and Fuel platforms
173
+ type blockEvent
177
174
 
178
175
  type onBlockArgs = {
179
176
  block: blockEvent,
@@ -249,36 +246,22 @@ let fuelTransferParamsSchema = S.schema(s => {
249
246
  })
250
247
 
251
248
  type entity = private {id: string}
249
+ type clickHouseSetUpdatesCache = {
250
+ tableName: string,
251
+ convertOrThrow: Change.t<entity> => Js.Json.t,
252
+ }
252
253
  type genericEntityConfig<'entity> = {
253
254
  name: string,
254
255
  index: int,
255
256
  schema: S.t<'entity>,
256
257
  rowsSchema: S.t<array<'entity>>,
257
258
  table: Table.table,
258
- entityHistory: EntityHistory.t<'entity>,
259
+ mutable clickHouseSetUpdatesCache?: clickHouseSetUpdatesCache,
260
+ mutable pgEntityHistoryCache?: EntityHistory.pgEntityHistory<'entity>,
259
261
  }
260
262
  type entityConfig = genericEntityConfig<entity>
261
263
  external fromGenericEntityConfig: genericEntityConfig<'entity> => entityConfig = "%identity"
262
264
 
263
- type enum
264
- type enumConfig<'enum> = {
265
- name: string,
266
- variants: array<'enum>,
267
- schema: S.t<'enum>,
268
- default: 'enum,
269
- }
270
- external fromGenericEnumConfig: enumConfig<'enum> => enumConfig<enum> = "%identity"
271
-
272
- let makeEnumConfig = (~name, ~variants) => {
273
- name,
274
- variants,
275
- schema: S.enum(variants),
276
- default: switch variants->Belt.Array.get(0) {
277
- | Some(v) => v
278
- | None => Js.Exn.raiseError("No variants defined for enum " ++ name)
279
- },
280
- }
281
-
282
265
  type effectInput
283
266
  type effectOutput
284
267
  type effectContext = private {mutable cache: bool}
@@ -322,8 +305,8 @@ let makeCacheTable = (~effectName) => {
322
305
  Table.mkTable(
323
306
  cacheTablePrefix ++ effectName,
324
307
  ~fields=[
325
- Table.mkField("id", Text, ~fieldSchema=S.string, ~isPrimaryKey=true),
326
- Table.mkField("output", JsonB, ~fieldSchema=cacheOutputSchema, ~isNullable=true),
308
+ Table.mkField("id", String, ~fieldSchema=S.string, ~isPrimaryKey=true),
309
+ Table.mkField("output", Json, ~fieldSchema=cacheOutputSchema, ~isNullable=true),
327
310
  ],
328
311
  )
329
312
  }
@@ -331,9 +314,11 @@ let makeCacheTable = (~effectName) => {
331
314
  @genType.import(("./Types.ts", "Invalid"))
332
315
  type noEventFilters
333
316
 
317
+ type checkpointId = float
318
+
334
319
  type reorgCheckpoint = {
335
320
  @as("id")
336
- checkpointId: int,
321
+ checkpointId: float,
337
322
  @as("chain_id")
338
323
  chainId: int,
339
324
  @as("block_number")
@@ -342,13 +327,9 @@ type reorgCheckpoint = {
342
327
  blockHash: string,
343
328
  }
344
329
 
345
- type entityValueAtStartOfBatch<'entityType> =
346
- | NotSet // The entity isn't in the DB yet
347
- | AlreadySet('entityType)
348
-
349
- type updatedValue<'entityType> = {
350
- latest: EntityHistory.entityUpdate<'entityType>,
351
- history: array<EntityHistory.entityUpdate<'entityType>>,
330
+ type inMemoryStoreEntityUpdate<'entity> = {
331
+ latestChange: Change.t<'entity>,
332
+ history: array<Change.t<'entity>>,
352
333
  // In the event of a rollback, some entity updates may have been
353
334
  // been affected by a rollback diff. If there was no rollback diff
354
335
  // this will always be false.
@@ -359,6 +340,7 @@ type updatedValue<'entityType> = {
359
340
  containsRollbackDiffChange: bool,
360
341
  }
361
342
 
362
- type inMemoryStoreRowEntity<'entityType> =
363
- | Updated(updatedValue<'entityType>)
364
- | InitialReadFromDb(entityValueAtStartOfBatch<'entityType>) // This means there is no change from the db.
343
+ @unboxed
344
+ type inMemoryStoreEntityStatus<'entity> =
345
+ | Updated(inMemoryStoreEntityUpdate<'entity>)
346
+ | Loaded // This means there is no change from the db.
@@ -3,10 +3,7 @@
3
3
 
4
4
  var Table = require("./db/Table.res.js");
5
5
  var $$BigInt = require("./bindings/BigInt.res.js");
6
- var Js_exn = require("rescript/lib/js/js_exn.js");
7
6
  var Address = require("./Address.res.js");
8
- var Belt_Array = require("rescript/lib/js/belt_Array.js");
9
- var Caml_option = require("rescript/lib/js/caml_option.js");
10
7
  var S$RescriptSchema = require("rescript-schema/src/S.res.js");
11
8
 
12
9
  var fuelSupplyParamsSchema = S$RescriptSchema.schema(function (s) {
@@ -24,16 +21,6 @@ var fuelTransferParamsSchema = S$RescriptSchema.schema(function (s) {
24
21
  };
25
22
  });
26
23
 
27
- function makeEnumConfig(name, variants) {
28
- var v = Belt_Array.get(variants, 0);
29
- return {
30
- name: name,
31
- variants: variants,
32
- schema: S$RescriptSchema.$$enum(variants),
33
- default: v !== undefined ? Caml_option.valFromOption(v) : Js_exn.raiseError("No variants defined for enum " + name)
34
- };
35
- }
36
-
37
24
  var cacheTablePrefix = "envio_effect_";
38
25
 
39
26
  var cacheOutputSchema = S$RescriptSchema.json(false);
@@ -47,14 +34,13 @@ var effectCacheItemRowsSchema = S$RescriptSchema.array(S$RescriptSchema.schema(f
47
34
 
48
35
  function makeCacheTable(effectName) {
49
36
  return Table.mkTable(cacheTablePrefix + effectName, undefined, [
50
- Table.mkField("id", "TEXT", S$RescriptSchema.string, undefined, undefined, undefined, true, undefined, undefined),
51
- Table.mkField("output", "JSONB", cacheOutputSchema, undefined, undefined, true, undefined, undefined, undefined)
37
+ Table.mkField("id", "String", S$RescriptSchema.string, undefined, undefined, undefined, true, undefined, undefined),
38
+ Table.mkField("output", "Json", cacheOutputSchema, undefined, undefined, true, undefined, undefined, undefined)
52
39
  ]);
53
40
  }
54
41
 
55
42
  exports.fuelSupplyParamsSchema = fuelSupplyParamsSchema;
56
43
  exports.fuelTransferParamsSchema = fuelTransferParamsSchema;
57
- exports.makeEnumConfig = makeEnumConfig;
58
44
  exports.cacheTablePrefix = cacheTablePrefix;
59
45
  exports.cacheOutputSchema = cacheOutputSchema;
60
46
  exports.effectCacheItemRowsSchema = effectCacheItemRowsSchema;
@@ -29,13 +29,24 @@ type initialState = {
29
29
  cleanRun: bool,
30
30
  cache: dict<effectCacheRecord>,
31
31
  chains: array<initialChainState>,
32
- checkpointId: int,
32
+ checkpointId: Internal.checkpointId,
33
33
  // Needed to keep reorg detection logic between restarts
34
34
  reorgCheckpoints: array<Internal.reorgCheckpoint>,
35
35
  }
36
36
 
37
37
  type operator = [#">" | #"=" | #"<"]
38
38
 
39
+ type updatedEffectCache = {
40
+ effect: Internal.effect,
41
+ items: array<Internal.effectCacheItem>,
42
+ shouldInitialize: bool,
43
+ }
44
+
45
+ type updatedEntity = {
46
+ entityConfig: Internal.entityConfig,
47
+ updates: array<Internal.inMemoryStoreEntityUpdate<Internal.entity>>,
48
+ }
49
+
39
50
  type storage = {
40
51
  // Should return true if we already have persisted data
41
52
  // and we can skip initialization
@@ -45,7 +56,7 @@ type storage = {
45
56
  initialize: (
46
57
  ~chainConfigs: array<Config.chain>=?,
47
58
  ~entities: array<Internal.entityConfig>=?,
48
- ~enums: array<Internal.enumConfig<Internal.enum>>=?,
59
+ ~enums: array<Table.enumConfig<Table.enum>>=?,
49
60
  ) => promise<initialState>,
50
61
  resumeInitialState: unit => promise<initialState>,
51
62
  @raises("StorageError")
@@ -77,10 +88,53 @@ type storage = {
77
88
  ) => promise<unit>,
78
89
  // This is to download cache from the database to .envio/cache
79
90
  dumpEffectCache: unit => promise<unit>,
91
+ // Execute raw SQL query
92
+ executeUnsafe: string => promise<unknown>,
93
+ // Check if entity history has rows
94
+ hasEntityHistoryRows: unit => promise<bool>,
95
+ // Update chain metadata
96
+ setChainMeta: dict<InternalTable.Chains.metaFields> => promise<unknown>,
97
+ // Prune old checkpoints
98
+ pruneStaleCheckpoints: (~safeCheckpointId: Internal.checkpointId) => promise<unit>,
99
+ // Prune stale entity history
100
+ pruneStaleEntityHistory: (
101
+ ~entityName: string,
102
+ ~entityIndex: int,
103
+ ~safeCheckpointId: Internal.checkpointId,
104
+ ) => promise<unit>,
105
+ // Get rollback target checkpoint
106
+ getRollbackTargetCheckpoint: (
107
+ ~reorgChainId: int,
108
+ ~lastKnownValidBlockNumber: int,
109
+ ) => promise<array<{"id": Internal.checkpointId}>>,
110
+ // Get rollback progress diff
111
+ getRollbackProgressDiff: (
112
+ ~rollbackTargetCheckpointId: Internal.checkpointId,
113
+ ) => promise<
114
+ array<{
115
+ "chain_id": int,
116
+ "events_processed_diff": string,
117
+ "new_progress_block_number": int,
118
+ }>,
119
+ >,
120
+ // Get rollback data for entity
121
+ getRollbackData: (
122
+ ~entityConfig: Internal.entityConfig,
123
+ ~rollbackTargetCheckpointId: Internal.checkpointId,
124
+ ) => promise<(array<{"id": string}>, array<unknown>)>,
125
+ // Write batch to storage
126
+ writeBatch: (
127
+ ~batch: Batch.t,
128
+ ~rawEvents: array<InternalTable.RawEvents.t>,
129
+ ~rollbackTargetCheckpointId: option<Internal.checkpointId>,
130
+ ~isInReorgThreshold: bool,
131
+ ~config: Config.t,
132
+ ~allEntities: array<Internal.entityConfig>,
133
+ ~updatedEffectsCache: array<updatedEffectCache>,
134
+ ~updatedEntities: array<updatedEntity>,
135
+ ) => promise<unit>,
80
136
  }
81
137
 
82
- exception StorageError({message: string, reason: exn})
83
-
84
138
  type storageStatus =
85
139
  | Unknown
86
140
  | Initializing(promise<unit>)
@@ -89,38 +143,28 @@ type storageStatus =
89
143
  type t = {
90
144
  userEntities: array<Internal.entityConfig>,
91
145
  allEntities: array<Internal.entityConfig>,
92
- allEnums: array<Internal.enumConfig<Internal.enum>>,
146
+ allEnums: array<Table.enumConfig<Table.enum>>,
93
147
  mutable storageStatus: storageStatus,
94
148
  mutable storage: storage,
95
- // FIXME: This is temporary to move it library
96
- // Should be a part of the storage interface and db agnostic
97
- mutable sql: Postgres.sql,
98
149
  }
99
150
 
100
- let entityHistoryActionEnumConfig: Internal.enumConfig<EntityHistory.RowAction.t> = {
101
- name: EntityHistory.RowAction.name,
102
- variants: EntityHistory.RowAction.variants,
103
- schema: EntityHistory.RowAction.schema,
104
- default: SET,
105
- }
151
+ exception StorageError({message: string, reason: exn})
106
152
 
107
153
  let make = (
108
154
  ~userEntities,
109
155
  // TODO: Should only pass userEnums and create internal config in runtime
110
156
  ~allEnums,
111
157
  ~storage,
112
- ~sql,
113
158
  ) => {
114
159
  let allEntities = userEntities->Js.Array2.concat([InternalTable.DynamicContractRegistry.config])
115
160
  let allEnums =
116
- allEnums->Js.Array2.concat([entityHistoryActionEnumConfig->Internal.fromGenericEnumConfig])
161
+ allEnums->Js.Array2.concat([EntityHistory.RowAction.config->Table.fromGenericEnumConfig])
117
162
  {
118
163
  userEntities,
119
164
  allEntities,
120
165
  allEnums,
121
166
  storageStatus: Unknown,
122
167
  storage,
123
- sql,
124
168
  }
125
169
  }
126
170
 
@@ -197,32 +241,140 @@ let getInitializedState = persistence => {
197
241
  }
198
242
  }
199
243
 
200
- let setEffectCacheOrThrow = async (
244
+ let writeBatch = (
201
245
  persistence,
202
- ~effect: Internal.effect,
203
- ~items,
204
- ~invalidationsCount,
205
- ) => {
246
+ ~batch,
247
+ ~config,
248
+ ~inMemoryStore: InMemoryStore.t,
249
+ ~isInReorgThreshold,
250
+ ) =>
206
251
  switch persistence.storageStatus {
207
252
  | Unknown
208
253
  | Initializing(_) =>
209
254
  Js.Exn.raiseError(`Failed to access the indexer storage. The Persistence layer is not initialized.`)
210
- | Ready({cache}) => {
211
- let storage = persistence.storage
212
- let effectName = effect.name
213
- let effectCacheRecord = switch cache->Utils.Dict.dangerouslyGetNonOption(effectName) {
214
- | Some(c) => c
215
- | None => {
216
- let c = {effectName, count: 0}
217
- cache->Js.Dict.set(effectName, c)
218
- c
219
- }
255
+ | Ready({cache}) =>
256
+ let updatedEntities = persistence.allEntities->Belt.Array.keepMapU(entityConfig => {
257
+ let updates =
258
+ inMemoryStore
259
+ ->InMemoryStore.getInMemTable(~entityConfig)
260
+ ->InMemoryTable.Entity.updates
261
+ if updates->Utils.Array.isEmpty {
262
+ None
263
+ } else {
264
+ Some({entityConfig, updates})
220
265
  }
221
- let initialize = effectCacheRecord.count === 0
222
- await storage.setEffectCacheOrThrow(~effect, ~items, ~initialize)
223
- effectCacheRecord.count =
224
- effectCacheRecord.count + items->Js.Array2.length - invalidationsCount
225
- Prometheus.EffectCacheCount.set(~count=effectCacheRecord.count, ~effectName)
226
- }
266
+ })
267
+ persistence.storage.writeBatch(
268
+ ~batch,
269
+ ~rawEvents=inMemoryStore.rawEvents->InMemoryTable.values,
270
+ ~rollbackTargetCheckpointId=inMemoryStore.rollbackTargetCheckpointId,
271
+ ~isInReorgThreshold,
272
+ ~config,
273
+ ~allEntities=persistence.allEntities,
274
+ ~updatedEntities,
275
+ ~updatedEffectsCache={
276
+ inMemoryStore.effects
277
+ ->Js.Dict.keys
278
+ ->Belt.Array.keepMapU(effectName => {
279
+ let inMemTable = inMemoryStore.effects->Js.Dict.unsafeGet(effectName)
280
+ let {idsToStore, dict, effect, invalidationsCount} = inMemTable
281
+ switch idsToStore {
282
+ | [] => None
283
+ | ids => {
284
+ let items = Belt.Array.makeUninitializedUnsafe(ids->Belt.Array.length)
285
+ ids->Belt.Array.forEachWithIndex((index, id) => {
286
+ items->Js.Array2.unsafe_set(
287
+ index,
288
+ (
289
+ {
290
+ id,
291
+ output: dict->Js.Dict.unsafeGet(id),
292
+ }: Internal.effectCacheItem
293
+ ),
294
+ )
295
+ })
296
+ Some({
297
+ let effectName = effect.name
298
+ let effectCacheRecord = switch cache->Utils.Dict.dangerouslyGetNonOption(
299
+ effectName,
300
+ ) {
301
+ | Some(c) => c
302
+ | None => {
303
+ let c = {effectName, count: 0}
304
+ cache->Js.Dict.set(effectName, c)
305
+ c
306
+ }
307
+ }
308
+ let shouldInitialize = effectCacheRecord.count === 0
309
+ effectCacheRecord.count =
310
+ effectCacheRecord.count + items->Js.Array2.length - invalidationsCount
311
+ Prometheus.EffectCacheCount.set(~count=effectCacheRecord.count, ~effectName)
312
+ {effect, items, shouldInitialize}
313
+ })
314
+ }
315
+ }
316
+ })
317
+ },
318
+ )
319
+ }
320
+
321
+ let prepareRollbackDiff = async (
322
+ persistence: t,
323
+ ~rollbackTargetCheckpointId,
324
+ ~rollbackDiffCheckpointId,
325
+ ) => {
326
+ let inMemStore = InMemoryStore.make(
327
+ ~entities=persistence.allEntities,
328
+ ~rollbackTargetCheckpointId,
329
+ )
330
+
331
+ let deletedEntities = Js.Dict.empty()
332
+ let setEntities = Js.Dict.empty()
333
+
334
+ let _ =
335
+ await persistence.allEntities
336
+ ->Belt.Array.map(async entityConfig => {
337
+ let entityTable = inMemStore->InMemoryStore.getInMemTable(~entityConfig)
338
+
339
+ let (removedIdsResult, restoredEntitiesResult) = await persistence.storage.getRollbackData(
340
+ ~entityConfig,
341
+ ~rollbackTargetCheckpointId,
342
+ )
343
+
344
+ // Process removed IDs
345
+ removedIdsResult->Js.Array2.forEach(data => {
346
+ deletedEntities->Utils.Dict.push(entityConfig.name, data["id"])
347
+ entityTable->InMemoryTable.Entity.set(
348
+ Delete({
349
+ entityId: data["id"],
350
+ checkpointId: rollbackDiffCheckpointId,
351
+ }),
352
+ ~shouldSaveHistory=false,
353
+ ~containsRollbackDiffChange=true,
354
+ )
355
+ })
356
+
357
+ let restoredEntities = restoredEntitiesResult->S.parseOrThrow(entityConfig.rowsSchema)
358
+
359
+ // Process restored entities
360
+ restoredEntities->Belt.Array.forEach((entity: Internal.entity) => {
361
+ setEntities->Utils.Dict.push(entityConfig.name, entity.id)
362
+ entityTable->InMemoryTable.Entity.set(
363
+ Set({
364
+ entityId: entity.id,
365
+ checkpointId: rollbackDiffCheckpointId,
366
+ entity,
367
+ }),
368
+ ~shouldSaveHistory=false,
369
+ ~containsRollbackDiffChange=true,
370
+ )
371
+ })
372
+ })
373
+ ->Promise.all
374
+
375
+ {
376
+ "inMemStore": inMemStore,
377
+ "deletedEntities": deletedEntities,
378
+ "setEntities": setEntities,
227
379
  }
228
380
  }