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.
- package/index.d.ts +1 -0
- package/package.json +6 -5
- package/src/Batch.res +4 -4
- package/src/Change.res +9 -0
- package/src/Change.res.js +2 -0
- package/src/Config.res +5 -5
- package/src/Config.res.js +3 -1
- package/src/Envio.gen.ts +3 -3
- package/src/Envio.res +14 -3
- package/src/EventRegister.res +3 -11
- package/src/EventRegister.res.js +4 -8
- package/src/EventRegister.resi +1 -1
- package/src/InMemoryStore.res +7 -15
- package/src/InMemoryStore.res.js +2 -4
- package/src/InMemoryTable.res +50 -35
- package/src/InMemoryTable.res.js +52 -84
- package/src/Internal.gen.ts +0 -2
- package/src/Internal.res +20 -38
- package/src/Internal.res.js +2 -16
- package/src/Persistence.res +190 -38
- package/src/Persistence.res.js +92 -39
- package/src/PgStorage.res +700 -14
- package/src/PgStorage.res.js +431 -19
- package/src/Platform.res +141 -0
- package/src/Platform.res.js +170 -0
- package/src/Prometheus.res +41 -0
- package/src/Prometheus.res.js +45 -0
- package/src/SafeCheckpointTracking.res +5 -4
- package/src/Sink.res +47 -0
- package/src/Sink.res.js +36 -0
- package/src/Utils.res +2 -0
- package/src/Utils.res.js +3 -0
- package/src/bindings/ClickHouse.res +387 -0
- package/src/bindings/ClickHouse.res.js +274 -0
- package/src/bindings/Postgres.res +15 -0
- package/src/db/EntityHistory.res +33 -156
- package/src/db/EntityHistory.res.js +40 -115
- package/src/db/InternalTable.res +56 -55
- package/src/db/InternalTable.res.js +49 -52
- package/src/db/Table.res +86 -22
- package/src/db/Table.res.js +77 -10
package/src/InMemoryTable.res.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
154
|
+
latest: entity,
|
|
155
|
+
status: "Loaded",
|
|
169
156
|
entityIndices: entityIndices
|
|
170
157
|
};
|
|
171
158
|
}
|
|
172
159
|
|
|
173
|
-
function set$1(inMemTable,
|
|
160
|
+
function set$1(inMemTable, change, shouldSaveHistory, containsRollbackDiffChangeOpt) {
|
|
174
161
|
var containsRollbackDiffChange = containsRollbackDiffChangeOpt !== undefined ? containsRollbackDiffChangeOpt : false;
|
|
175
|
-
var
|
|
176
|
-
|
|
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.
|
|
179
|
-
if (previous_values
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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
|
-
|
|
225
|
-
|
|
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
|
-
|
|
209
|
+
deleteEntityFromIndices(inMemTable, change.entityId, updatedEntityRecord.entityIndices);
|
|
230
210
|
}
|
|
231
|
-
set(inMemTable.table,
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
|
344
|
-
return Belt_Array.
|
|
345
|
-
|
|
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
|
-
|
|
355
|
+
updates: updates,
|
|
388
356
|
values: values$1,
|
|
389
357
|
clone: clone$1
|
|
390
358
|
};
|
package/src/Internal.gen.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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",
|
|
326
|
-
Table.mkField("output",
|
|
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:
|
|
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
|
|
346
|
-
|
|
347
|
-
|
|
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
|
-
|
|
363
|
-
|
|
364
|
-
|
|
|
343
|
+
@unboxed
|
|
344
|
+
type inMemoryStoreEntityStatus<'entity> =
|
|
345
|
+
| Updated(inMemoryStoreEntityUpdate<'entity>)
|
|
346
|
+
| Loaded // This means there is no change from the db.
|
package/src/Internal.res.js
CHANGED
|
@@ -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", "
|
|
51
|
-
Table.mkField("output", "
|
|
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;
|
package/src/Persistence.res
CHANGED
|
@@ -29,13 +29,24 @@ type initialState = {
|
|
|
29
29
|
cleanRun: bool,
|
|
30
30
|
cache: dict<effectCacheRecord>,
|
|
31
31
|
chains: array<initialChainState>,
|
|
32
|
-
checkpointId:
|
|
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<
|
|
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<
|
|
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
|
-
|
|
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([
|
|
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
|
|
244
|
+
let writeBatch = (
|
|
201
245
|
persistence,
|
|
202
|
-
~
|
|
203
|
-
~
|
|
204
|
-
~
|
|
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
|
-
|
|
212
|
-
let
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
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
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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
|
}
|