envio 3.6.1 → 3.7.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 (80) hide show
  1. package/index.d.ts +197 -9
  2. package/package.json +6 -6
  3. package/src/Batch.res +64 -25
  4. package/src/Batch.res.mjs +69 -46
  5. package/src/ChainFetching.res +13 -17
  6. package/src/ChainFetching.res.mjs +9 -14
  7. package/src/ChainState.res +160 -119
  8. package/src/ChainState.res.mjs +75 -46
  9. package/src/ChainState.resi +15 -12
  10. package/src/Config.res +9 -2
  11. package/src/Config.res.mjs +4 -4
  12. package/src/Core.res +5 -0
  13. package/src/Ecosystem.res +0 -1
  14. package/src/Envio.res +16 -3
  15. package/src/EventConfigBuilder.res +155 -40
  16. package/src/EventConfigBuilder.res.mjs +76 -24
  17. package/src/EventProcessing.res +4 -8
  18. package/src/EventProcessing.res.mjs +4 -4
  19. package/src/EventUtils.res +3 -47
  20. package/src/FetchState.res +124 -104
  21. package/src/FetchState.res.mjs +116 -100
  22. package/src/HandlerRegister.res +108 -2
  23. package/src/HandlerRegister.res.mjs +59 -10
  24. package/src/HandlerRegister.resi +4 -0
  25. package/src/Internal.res +84 -24
  26. package/src/Internal.res.mjs +33 -2
  27. package/src/LazyLoader.res +12 -11
  28. package/src/LazyLoader.res.mjs +13 -7
  29. package/src/Main.res +22 -9
  30. package/src/Main.res.mjs +18 -5
  31. package/src/MemoryStorage.res +724 -0
  32. package/src/MemoryStorage.res.mjs +601 -0
  33. package/src/PgStorage.res +18 -7
  34. package/src/PgStorage.res.mjs +14 -11
  35. package/src/ReorgDetection.res +0 -222
  36. package/src/ReorgDetection.res.mjs +0 -163
  37. package/src/Rollback.res +14 -15
  38. package/src/Rollback.res.mjs +4 -5
  39. package/src/SimulateItems.res +2 -2
  40. package/src/Utils.res +3 -0
  41. package/src/bindings/ClickHouse.res +16 -11
  42. package/src/bindings/ClickHouse.res.mjs +10 -10
  43. package/src/bindings/Vitest.res +1 -1
  44. package/src/sources/BlockStore.res +115 -5
  45. package/src/sources/BlockStore.res.mjs +25 -0
  46. package/src/sources/Evm.res +0 -1
  47. package/src/sources/Evm.res.mjs +0 -1
  48. package/src/sources/EvmHyperSyncSource.res +19 -84
  49. package/src/sources/EvmHyperSyncSource.res.mjs +24 -60
  50. package/src/sources/Fuel.res +24 -4
  51. package/src/sources/Fuel.res.mjs +23 -3
  52. package/src/sources/FuelHyperSync.res +8 -3
  53. package/src/sources/FuelHyperSync.res.mjs +5 -4
  54. package/src/sources/FuelHyperSync.resi +3 -1
  55. package/src/sources/FuelHyperSyncClient.res +7 -10
  56. package/src/sources/FuelHyperSyncSource.res +18 -45
  57. package/src/sources/FuelHyperSyncSource.res.mjs +18 -35
  58. package/src/sources/HyperSync.res +49 -229
  59. package/src/sources/HyperSync.res.mjs +74 -191
  60. package/src/sources/HyperSync.resi +11 -35
  61. package/src/sources/HyperSyncClient.res +9 -79
  62. package/src/sources/HyperSyncClient.res.mjs +2 -6
  63. package/src/sources/RequestStat.res +5 -0
  64. package/src/sources/RequestStat.res.mjs +2 -0
  65. package/src/sources/RpcSource.res +149 -43
  66. package/src/sources/RpcSource.res.mjs +104 -41
  67. package/src/sources/SimulateSource.res +8 -5
  68. package/src/sources/SimulateSource.res.mjs +6 -6
  69. package/src/sources/Source.res +89 -16
  70. package/src/sources/Source.res.mjs +50 -1
  71. package/src/sources/SourceManager.res +255 -50
  72. package/src/sources/SourceManager.res.mjs +149 -55
  73. package/src/sources/SourceManager.resi +2 -6
  74. package/src/sources/Svm.res +0 -7
  75. package/src/sources/Svm.res.mjs +0 -8
  76. package/src/sources/SvmHyperSyncClient.res +21 -16
  77. package/src/sources/SvmHyperSyncClient.res.mjs +3 -4
  78. package/src/sources/SvmHyperSyncSource.res +25 -117
  79. package/src/sources/SvmHyperSyncSource.res.mjs +8 -121
  80. package/svm.schema.json +3 -2
@@ -0,0 +1,601 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Table from "./db/Table.res.mjs";
4
+ import * as Utils from "./Utils.res.mjs";
5
+ import * as Config from "./Config.res.mjs";
6
+ import * as ChainId from "./ChainId.res.mjs";
7
+ import * as EntityId from "./EntityId.res.mjs";
8
+ import * as Internal from "./Internal.res.mjs";
9
+ import * as Stdlib_Null from "@rescript/runtime/lib/es6/Stdlib_Null.js";
10
+ import * as EntityFilter from "./db/EntityFilter.res.mjs";
11
+ import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
12
+ import * as InternalTable from "./db/InternalTable.res.mjs";
13
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
14
+ import * as Primitive_float from "@rescript/runtime/lib/es6/Primitive_float.js";
15
+ import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
16
+ import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
17
+ import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
18
+
19
+ function make() {
20
+ return {
21
+ entities: {},
22
+ entityConfigs: {},
23
+ history: {},
24
+ checkpoints: [],
25
+ chains: {},
26
+ cache: {},
27
+ effectCache: {},
28
+ envioInfo: undefined,
29
+ isInitialized: false
30
+ };
31
+ }
32
+
33
+ function rowKey(scope, entityId) {
34
+ if (scope === "crossChain") {
35
+ return EntityId.toKey(entityId);
36
+ } else {
37
+ return ChainId.toString(scope) + `|` + EntityId.toKey(entityId);
38
+ }
39
+ }
40
+
41
+ let historyKey = rowKey;
42
+
43
+ function readChainId(entity, field) {
44
+ return entity[field.fieldName];
45
+ }
46
+
47
+ function copyEntity(entity) {
48
+ return Utils.Dict.shallowCopy(entity);
49
+ }
50
+
51
+ function getEntityDict(state, name) {
52
+ let dict = state.entities[name];
53
+ if (dict !== undefined) {
54
+ return dict;
55
+ }
56
+ let dict$1 = {};
57
+ state.entities[name] = dict$1;
58
+ return dict$1;
59
+ }
60
+
61
+ function getHistory(state, name) {
62
+ let rows = state.history[name];
63
+ if (rows !== undefined) {
64
+ return rows;
65
+ }
66
+ let rows$1 = [];
67
+ state.history[name] = rows$1;
68
+ return rows$1;
69
+ }
70
+
71
+ function registerEntities(state, entities) {
72
+ entities.forEach(entityConfig => {
73
+ state.entityConfigs[entityConfig.name] = entityConfig;
74
+ state.entityConfigs[entityConfig.table.tableName] = entityConfig;
75
+ getEntityDict(state, entityConfig.name);
76
+ });
77
+ }
78
+
79
+ function seedIndexingAddresses(state, chainConfigs) {
80
+ let dict = getEntityDict(state, Config.EnvioAddresses.name);
81
+ chainConfigs.forEach(chainConfig => {
82
+ chainConfig.contracts.forEach(contract => {
83
+ contract.addresses.forEach(address => {
84
+ let entity_id = Config.EnvioAddresses.makeId(chainConfig.id, address);
85
+ let entity_chain_id = chainConfig.id;
86
+ let entity_contract_name = contract.name;
87
+ let entity = {
88
+ id: entity_id,
89
+ chain_id: entity_chain_id,
90
+ registration_block: -1,
91
+ registration_log_index: -1,
92
+ contract_name: entity_contract_name
93
+ };
94
+ dict[entity_id] = entity;
95
+ });
96
+ });
97
+ });
98
+ }
99
+
100
+ function toIndexingAddress(dc) {
101
+ return {
102
+ address: Config.EnvioAddresses.getAddress(dc),
103
+ contractName: dc.contract_name,
104
+ registrationBlock: dc.registration_block
105
+ };
106
+ }
107
+
108
+ function getIndexingAddressesByChain(state) {
109
+ let byChain = {};
110
+ let dcDict = state.entities[Config.EnvioAddresses.name];
111
+ if (dcDict !== undefined) {
112
+ Object.values(dcDict).forEach(entity => {
113
+ let chainIdStr = ChainId.toString(entity.chain_id);
114
+ let arr = byChain[chainIdStr];
115
+ let contracts;
116
+ if (arr !== undefined) {
117
+ contracts = arr;
118
+ } else {
119
+ let arr$1 = [];
120
+ byChain[chainIdStr] = arr$1;
121
+ contracts = arr$1;
122
+ }
123
+ contracts.push(toIndexingAddress(entity));
124
+ });
125
+ }
126
+ return byChain;
127
+ }
128
+
129
+ function toInitialChainStates(state) {
130
+ let addressesByChain = getIndexingAddressesByChain(state);
131
+ return Object.values(state.chains).map(chain => ({
132
+ id: chain.id,
133
+ startBlock: chain.startBlock,
134
+ endBlock: chain.endBlock,
135
+ maxReorgDepth: chain.maxReorgDepth,
136
+ progressBlockNumber: chain.progressBlockNumber,
137
+ numEventsProcessed: chain.numEventsProcessed,
138
+ firstEventBlockNumber: chain.firstEventBlockNumber,
139
+ timestampCaughtUpToHeadOrEndblock: chain.timestampCaughtUpToHeadOrEndblock,
140
+ indexingAddresses: Stdlib_Option.getOr(addressesByChain[ChainId.toString(chain.id)], []),
141
+ sourceBlockNumber: chain.sourceBlockNumber
142
+ }));
143
+ }
144
+
145
+ function committedCheckpointId(state) {
146
+ return Stdlib_Array.reduce(state.checkpoints, InternalTable.Checkpoints.initialCheckpointId, (max, cp) => {
147
+ if (cp.id > max) {
148
+ return cp.id;
149
+ } else {
150
+ return max;
151
+ }
152
+ });
153
+ }
154
+
155
+ function reorgCheckpoints(state) {
156
+ return Stdlib_Array.filterMap(state.checkpoints, cp => {
157
+ let match = cp.blockHash;
158
+ let match$1 = state.chains[ChainId.toString(cp.chainId)];
159
+ if (match === undefined) {
160
+ return;
161
+ }
162
+ if (match$1 === undefined) {
163
+ return;
164
+ }
165
+ let safeBlock = match$1.sourceBlockNumber - match$1.maxReorgDepth | 0;
166
+ if (match$1.maxReorgDepth > 0 && match$1.progressBlockNumber > safeBlock && cp.blockNumber >= safeBlock) {
167
+ return {
168
+ id: cp.id,
169
+ chain_id: cp.chainId,
170
+ block_number: cp.blockNumber,
171
+ block_hash: match
172
+ };
173
+ }
174
+ });
175
+ }
176
+
177
+ function toInitialState(state, cleanRun) {
178
+ return {
179
+ cleanRun: cleanRun,
180
+ cache: state.cache,
181
+ chains: toInitialChainStates(state),
182
+ checkpointId: committedCheckpointId(state),
183
+ reorgCheckpoints: reorgCheckpoints(state),
184
+ envioInfo: state.envioInfo
185
+ };
186
+ }
187
+
188
+ function handleLoad(state, tableName, filter) {
189
+ let entityConfig = state.entityConfigs[tableName];
190
+ if (entityConfig === undefined) {
191
+ return [];
192
+ }
193
+ let entityDict = Stdlib_Option.getOr(state.entities[entityConfig.name], {});
194
+ let matched = Object.values(entityDict).filter(entity => EntityFilter.matches(filter, entity));
195
+ let field = Table.getChainIdField(entityConfig.table);
196
+ if (field !== undefined) {
197
+ return matched.map(entity => {
198
+ let copy = Utils.Dict.shallowCopy(entity);
199
+ Utils.Dict.deleteInPlace(copy, field.fieldName);
200
+ return copy;
201
+ });
202
+ } else {
203
+ return matched;
204
+ }
205
+ }
206
+
207
+ function backfillHistory(state, entityConfig, scope, entityId, rows) {
208
+ let key = rowKey(scope, entityId);
209
+ if (rows.some(row => rowKey(row.scope, row.entityId) === key)) {
210
+ return;
211
+ }
212
+ let entity = Stdlib_Option.flatMap(state.entities[entityConfig.name], dict => dict[rowKey(scope, entityId)]);
213
+ if (entity !== undefined) {
214
+ rows.push({
215
+ entityId: entityId,
216
+ scope: scope,
217
+ checkpointId: InternalTable.Checkpoints.initialCheckpointId,
218
+ action: "SET",
219
+ entity: entity
220
+ });
221
+ return;
222
+ }
223
+ }
224
+
225
+ function applyRollback(state, targetCheckpointId) {
226
+ state.checkpoints = state.checkpoints.filter(cp => cp.id <= targetCheckpointId);
227
+ Object.entries(state.history).forEach(param => {
228
+ state.history[param[0]] = param[1].filter(row => row.checkpointId <= targetCheckpointId);
229
+ });
230
+ }
231
+
232
+ function writeBatch(state, batch, rollback, isInReorgThreshold, config, updatedEntities, updatedEffectsCache, chainMetaData) {
233
+ let shouldSaveHistory = Config.shouldSaveHistory(config, isInReorgThreshold);
234
+ if (rollback !== undefined) {
235
+ applyRollback(state, rollback.targetCheckpointId);
236
+ }
237
+ updatedEntities.forEach(param => {
238
+ let scope = param.scope;
239
+ let entityConfig = param.entityConfig;
240
+ let entityDict = getEntityDict(state, entityConfig.name);
241
+ let historyRows = getHistory(state, entityConfig.name);
242
+ let chainIdField = Table.getChainIdField(entityConfig.table);
243
+ param.changes.forEach(change => {
244
+ let entityId = change.entityId;
245
+ if (shouldSaveHistory) {
246
+ backfillHistory(state, entityConfig, scope, entityId, historyRows);
247
+ }
248
+ if (change.type === "SET") {
249
+ let entity = change.entity;
250
+ let match = Internal.chainScopeChainId(scope);
251
+ let storedEntity = chainIdField !== undefined && match !== undefined ? Internal.stampChainId(entity, chainIdField.fieldName, Primitive_option.valFromOption(match)) : entity;
252
+ entityDict[rowKey(scope, entityId)] = storedEntity;
253
+ if (shouldSaveHistory) {
254
+ historyRows.push({
255
+ entityId: entityId,
256
+ scope: scope,
257
+ checkpointId: change.checkpointId,
258
+ action: "SET",
259
+ entity: storedEntity
260
+ });
261
+ return;
262
+ } else {
263
+ return;
264
+ }
265
+ }
266
+ Utils.Dict.deleteInPlace(entityDict, rowKey(scope, entityId));
267
+ if (shouldSaveHistory) {
268
+ historyRows.push({
269
+ entityId: entityId,
270
+ scope: scope,
271
+ checkpointId: change.checkpointId,
272
+ action: "DELETE",
273
+ entity: undefined
274
+ });
275
+ return;
276
+ }
277
+ });
278
+ });
279
+ Object.values(batch.progressedChainsById).forEach(chainAfterBatch => {
280
+ let key = ChainId.toString(chainAfterBatch.fetchState.chainId);
281
+ let chain = state.chains[key];
282
+ if (chain !== undefined) {
283
+ chain.progressBlockNumber = chainAfterBatch.progressBlockNumber;
284
+ chain.sourceBlockNumber = chainAfterBatch.sourceBlockNumber;
285
+ chain.numEventsProcessed = chainAfterBatch.totalEventsProcessed;
286
+ return;
287
+ }
288
+ });
289
+ if (chainMetaData !== undefined) {
290
+ Object.entries(chainMetaData).forEach(param => {
291
+ let meta = param[1];
292
+ let chain = state.chains[param[0]];
293
+ if (chain !== undefined) {
294
+ chain.firstEventBlockNumber = Primitive_option.fromNull(meta.first_event_block);
295
+ chain.latestFetchedBlockNumber = meta.buffer_block;
296
+ chain.timestampCaughtUpToHeadOrEndblock = Primitive_option.fromNull(meta.ready_at);
297
+ return;
298
+ }
299
+ });
300
+ }
301
+ if (shouldSaveHistory) {
302
+ for (let i = 0, i_finish = batch.checkpointIds.length; i < i_finish; ++i) {
303
+ state.checkpoints.push({
304
+ id: batch.checkpointIds[i],
305
+ chainId: batch.checkpointChainIds[i],
306
+ blockNumber: batch.checkpointBlockNumbers[i],
307
+ blockHash: Primitive_option.fromNull(batch.checkpointBlockHashes[i]),
308
+ eventsProcessed: batch.checkpointEventsProcessed[i]
309
+ });
310
+ }
311
+ }
312
+ updatedEffectsCache.forEach(param => {
313
+ let table = param.table;
314
+ let dict = state.effectCache[table.tableName];
315
+ let cacheDict;
316
+ if (dict !== undefined) {
317
+ cacheDict = dict;
318
+ } else {
319
+ let dict$1 = {};
320
+ state.effectCache[table.tableName] = dict$1;
321
+ cacheDict = dict$1;
322
+ }
323
+ param.items.forEach(item => {
324
+ cacheDict[item.id] = item;
325
+ });
326
+ let record = state.cache[table.tableName];
327
+ if (record !== undefined) {
328
+ record.count = Object.keys(cacheDict).length;
329
+ return;
330
+ }
331
+ });
332
+ }
333
+
334
+ function getRollbackData(state, entityConfig, rollbackTargetCheckpointId) {
335
+ let rows = Stdlib_Option.getOr(state.history[entityConfig.name], []);
336
+ let changedKeys = {};
337
+ rows.forEach(row => {
338
+ if (row.checkpointId > rollbackTargetCheckpointId) {
339
+ changedKeys[rowKey(row.scope, row.entityId)] = row;
340
+ return;
341
+ }
342
+ });
343
+ let removals = [];
344
+ let restored = [];
345
+ Object.entries(changedKeys).forEach(param => {
346
+ let changedRow = param[1];
347
+ let key = param[0];
348
+ let preTarget = Stdlib_Array.reduce(rows, undefined, (latest, row) => {
349
+ if (rowKey(row.scope, row.entityId) === key && row.checkpointId <= rollbackTargetCheckpointId && !(latest !== undefined && latest.checkpointId >= row.checkpointId)) {
350
+ return row;
351
+ } else {
352
+ return latest;
353
+ }
354
+ });
355
+ if (preTarget !== undefined) {
356
+ if (preTarget.action === "SET") {
357
+ let entity = preTarget.entity;
358
+ if (entity !== undefined) {
359
+ restored.push(entity);
360
+ return;
361
+ } else {
362
+ return;
363
+ }
364
+ }
365
+ removals.push({
366
+ entityId: preTarget.entityId,
367
+ scope: preTarget.scope
368
+ });
369
+ return;
370
+ }
371
+ removals.push({
372
+ entityId: changedRow.entityId,
373
+ scope: changedRow.scope
374
+ });
375
+ });
376
+ return [
377
+ removals,
378
+ restored
379
+ ];
380
+ }
381
+
382
+ function getRollbackProgressDiff(state, rollbackTargetCheckpointId) {
383
+ let byChain = {};
384
+ state.checkpoints.forEach(cp => {
385
+ if (cp.id <= rollbackTargetCheckpointId) {
386
+ return;
387
+ }
388
+ let key = ChainId.toString(cp.chainId);
389
+ let acc = byChain[key];
390
+ if (acc !== undefined) {
391
+ acc.eventsProcessed = acc.eventsProcessed + cp.eventsProcessed | 0;
392
+ acc.newProgressBlockNumber = Math.min(acc.newProgressBlockNumber, cp.blockNumber - 1 | 0);
393
+ } else {
394
+ byChain[key] = {
395
+ chainId: cp.chainId,
396
+ eventsProcessed: cp.eventsProcessed,
397
+ newProgressBlockNumber: cp.blockNumber - 1 | 0
398
+ };
399
+ }
400
+ });
401
+ return Object.values(byChain).map(acc => ({
402
+ chain_id: acc.chainId,
403
+ events_processed_diff: acc.eventsProcessed.toString(),
404
+ new_progress_block_number: acc.newProgressBlockNumber
405
+ }));
406
+ }
407
+
408
+ function toStorage(state, config) {
409
+ return {
410
+ name: "memory",
411
+ isInitialized: async () => state.isInitialized,
412
+ initialize: async (chainConfigsOpt, entitiesOpt, $staropt$star, envioInfo) => {
413
+ let chainConfigs = chainConfigsOpt !== undefined ? chainConfigsOpt : [];
414
+ let entities = entitiesOpt !== undefined ? entitiesOpt : [];
415
+ $staropt$star !== undefined;
416
+ registerEntities(state, entities);
417
+ seedIndexingAddresses(state, chainConfigs);
418
+ chainConfigs.forEach(chainConfig => {
419
+ state.chains[ChainId.toString(chainConfig.id)] = {
420
+ id: chainConfig.id,
421
+ startBlock: chainConfig.startBlock,
422
+ endBlock: chainConfig.endBlock,
423
+ maxReorgDepth: chainConfig.maxReorgDepth,
424
+ progressBlockNumber: -1,
425
+ sourceBlockNumber: 0,
426
+ numEventsProcessed: 0,
427
+ firstEventBlockNumber: undefined,
428
+ latestFetchedBlockNumber: 0,
429
+ timestampCaughtUpToHeadOrEndblock: undefined
430
+ };
431
+ });
432
+ state.envioInfo = envioInfo;
433
+ state.isInitialized = true;
434
+ return toInitialState(state, true);
435
+ },
436
+ resumeInitialState: async () => toInitialState(state, false),
437
+ loadOrThrow: async (filter, table) => handleLoad(state, table.tableName, filter),
438
+ ensureQueryIndexes: async (param, param$1) => {},
439
+ ensureSchemaIndexes: async param => {},
440
+ finalizeBackfill: async (param, chainIds, readyAt) => {
441
+ chainIds.forEach(chainId => {
442
+ let chain = state.chains[ChainId.toString(chainId)];
443
+ if (chain !== undefined) {
444
+ chain.timestampCaughtUpToHeadOrEndblock = readyAt;
445
+ return;
446
+ }
447
+ });
448
+ },
449
+ dumpEffectCache: async () => {},
450
+ reset: async () => {
451
+ let clear = dict => {
452
+ Object.keys(dict).forEach(key => Utils.Dict.deleteInPlace(dict, key));
453
+ };
454
+ clear(state.entities);
455
+ clear(state.history);
456
+ clear(state.chains);
457
+ clear(state.effectCache);
458
+ clear(state.cache);
459
+ state.checkpoints = [];
460
+ state.envioInfo = undefined;
461
+ state.isInitialized = false;
462
+ },
463
+ setChainMeta: async chainsData => {
464
+ Object.entries(chainsData).forEach(param => {
465
+ let meta = param[1];
466
+ let chain = state.chains[param[0]];
467
+ if (chain !== undefined) {
468
+ chain.firstEventBlockNumber = Primitive_option.fromNull(meta.first_event_block);
469
+ chain.latestFetchedBlockNumber = meta.buffer_block;
470
+ chain.timestampCaughtUpToHeadOrEndblock = Primitive_option.fromNull(meta.ready_at);
471
+ return;
472
+ }
473
+ });
474
+ return undefined;
475
+ },
476
+ pruneStaleCheckpoints: async safeCheckpointId => {
477
+ state.checkpoints = state.checkpoints.filter(cp => cp.id >= safeCheckpointId);
478
+ },
479
+ pruneStaleEntityHistory: async (entityName, param, param$1, safeCheckpointId) => {
480
+ let rows = state.history[entityName];
481
+ if (rows === undefined) {
482
+ return;
483
+ }
484
+ let newestBelow = {};
485
+ rows.forEach(row => {
486
+ if (row.checkpointId >= safeCheckpointId) {
487
+ return;
488
+ }
489
+ let key = rowKey(row.scope, row.entityId);
490
+ let current = newestBelow[key];
491
+ if (current !== undefined && current.checkpointId >= row.checkpointId) {
492
+ return;
493
+ } else {
494
+ newestBelow[key] = row;
495
+ return;
496
+ }
497
+ });
498
+ state.history[entityName] = rows.filter(row => {
499
+ let key = rowKey(row.scope, row.entityId);
500
+ if (row.checkpointId >= safeCheckpointId) {
501
+ return true;
502
+ }
503
+ let kept = newestBelow[key];
504
+ if (kept !== undefined) {
505
+ return kept === row;
506
+ } else {
507
+ return false;
508
+ }
509
+ });
510
+ },
511
+ getRollbackTargetCheckpoint: async (reorgChainId, lastKnownValidBlockNumber) => Stdlib_Array.reduce(state.checkpoints, undefined, (target, cp) => {
512
+ if (Primitive_object.equal(cp.chainId, reorgChainId) && cp.blockNumber <= lastKnownValidBlockNumber && !(target !== undefined && target >= cp.id)) {
513
+ return cp.id;
514
+ } else {
515
+ return target;
516
+ }
517
+ }),
518
+ getRollbackProgressDiff: async rollbackTargetCheckpointId => getRollbackProgressDiff(state, rollbackTargetCheckpointId),
519
+ getRollbackData: async (entityConfig, rollbackTargetCheckpointId) => getRollbackData(state, entityConfig, rollbackTargetCheckpointId),
520
+ writeBatch: async (batch, rollback, isInReorgThreshold, param, param$1, updatedEffectsCache, updatedEntities, chainMetaData, param$2) => writeBatch(state, batch, rollback, isInReorgThreshold, config, updatedEntities, updatedEffectsCache, chainMetaData),
521
+ close: async () => {}
522
+ };
523
+ }
524
+
525
+ function currentRows(state, entityConfig) {
526
+ return Object.values(Stdlib_Option.getOr(state.entities[entityConfig.name], {}));
527
+ }
528
+
529
+ function historyChanges(state, entityConfig) {
530
+ return Stdlib_Option.getOr(state.history[entityConfig.name], []).map(row => {
531
+ let match = row.action;
532
+ if (match === "SET") {
533
+ return {
534
+ type: "SET",
535
+ entityId: row.entityId,
536
+ entity: Stdlib_Option.getOr(row.entity, {}),
537
+ checkpointId: row.checkpointId
538
+ };
539
+ } else {
540
+ return {
541
+ type: "DELETE",
542
+ entityId: row.entityId,
543
+ checkpointId: row.checkpointId
544
+ };
545
+ }
546
+ }).toSorted((a, b) => {
547
+ let order = Primitive_string.compare(EntityId.toKey(a.entityId), EntityId.toKey(b.entityId));
548
+ if (order !== 0) {
549
+ return order;
550
+ } else {
551
+ return Primitive_float.compare(Number(a.checkpointId), Number(b.checkpointId));
552
+ }
553
+ });
554
+ }
555
+
556
+ function checkpointRows(state) {
557
+ return state.checkpoints.map(cp => ({
558
+ id: cp.id,
559
+ chain_id: cp.chainId,
560
+ block_number: cp.blockNumber,
561
+ block_hash: Stdlib_Null.fromOption(cp.blockHash),
562
+ events_processed: cp.eventsProcessed
563
+ }));
564
+ }
565
+
566
+ function effectCacheRows(state, tableName) {
567
+ return Object.values(Stdlib_Option.getOr(state.effectCache[tableName], {})).map(item => ({
568
+ id: item.id,
569
+ output: item.output
570
+ }));
571
+ }
572
+
573
+ export {
574
+ make,
575
+ rowKey,
576
+ historyKey,
577
+ readChainId,
578
+ copyEntity,
579
+ getEntityDict,
580
+ getHistory,
581
+ registerEntities,
582
+ seedIndexingAddresses,
583
+ toIndexingAddress,
584
+ getIndexingAddressesByChain,
585
+ toInitialChainStates,
586
+ committedCheckpointId,
587
+ reorgCheckpoints,
588
+ toInitialState,
589
+ handleLoad,
590
+ backfillHistory,
591
+ applyRollback,
592
+ writeBatch,
593
+ getRollbackData,
594
+ getRollbackProgressDiff,
595
+ toStorage,
596
+ currentRows,
597
+ historyChanges,
598
+ checkpointRows,
599
+ effectCacheRows,
600
+ }
601
+ /* Table Not a pure module */
package/src/PgStorage.res CHANGED
@@ -630,14 +630,19 @@ let classifyWriteError = (~specificError: ref<option<exn>>, ~table: Table.table,
630
630
  }
631
631
  }
632
632
 
633
- // WeakMap for caching table batch set queries
634
- let setQueryCache = Utils.WeakMap.make()
633
+ // Batch set queries, cached per table. The query text bakes in the schema and
634
+ // the chain-id mode, so the cache belongs to the storage instance those came
635
+ // from — `make` creates one and threads it down. A process-wide cache would
636
+ // hand a second storage the first one's schema.
637
+ let makeSetQueryCache = () => Utils.WeakMap.make()
638
+
635
639
  let setOrThrow = async (
636
640
  sql,
637
641
  ~items,
638
642
  ~table: Table.table,
639
643
  ~itemSchema,
640
644
  ~pgSchema,
645
+ ~setQueryCache,
641
646
  ~chainIdMode: ChainId.mode=Int32,
642
647
  ) => {
643
648
  if items->Array.length === 0 {
@@ -953,6 +958,7 @@ let rec writeBatch = async (
953
958
  ~config: Config.t,
954
959
  ~allEntities: array<Internal.entityConfig>,
955
960
  ~setEffectCacheOrThrow,
961
+ ~setQueryCache,
956
962
  ~updatedEffectsCache,
957
963
  ~updatedEntities: array<Persistence.updatedEntity>,
958
964
  ~sinkPromise: option<promise<option<exn>>>,
@@ -973,11 +979,8 @@ let rec writeBatch = async (
973
979
  let rows = batch.items->Array.filterMap(item =>
974
980
  switch item {
975
981
  | Internal.Event(_) =>
976
- let coordinate = `${item
977
- ->Internal.getItemChainId
978
- ->ChainId.toString}-${item
979
- ->Internal.getItemBlockNumber
980
- ->Int.toString}-${item->Internal.getItemLogIndex->Int.toString}`
982
+ let eventItem = item->Internal.castUnsafeEventItem
983
+ let coordinate = `${eventItem.chainId->ChainId.toString}-${eventItem.blockNumber->Int.toString}-${eventItem.logIndex->Int.toString}`
981
984
  if seenLogCoordinates->Utils.Set.has(coordinate) {
982
985
  None
983
986
  } else {
@@ -1006,6 +1009,7 @@ let rec writeBatch = async (
1006
1009
  ~itemSchema=InternalTable.RawEvents.schema,
1007
1010
  ~pgSchema,
1008
1011
  ~chainIdMode,
1012
+ ~setQueryCache,
1009
1013
  )
1010
1014
  }, ~items=rawEvents)
1011
1015
  } catch {
@@ -1161,6 +1165,7 @@ let rec writeBatch = async (
1161
1165
  ~table=entityHistory.table,
1162
1166
  ~pgSchema,
1163
1167
  ~chainIdMode,
1168
+ ~setQueryCache,
1164
1169
  ),
1165
1170
  )
1166
1171
  ->ignore
@@ -1178,6 +1183,7 @@ let rec writeBatch = async (
1178
1183
  ~itemSchema=entityConfig->getRowSchema,
1179
1184
  ~pgSchema,
1180
1185
  ~chainIdMode,
1186
+ ~setQueryCache,
1181
1187
  ),
1182
1188
  )
1183
1189
  }
@@ -1340,6 +1346,7 @@ let rec writeBatch = async (
1340
1346
  ~escapeTables,
1341
1347
  ~batch,
1342
1348
  ~pgSchema,
1349
+ ~setQueryCache,
1343
1350
  ~rollback,
1344
1351
  ~isInReorgThreshold,
1345
1352
  ~config,
@@ -1483,6 +1490,7 @@ let make = (
1483
1490
  let storageName = "postgres"
1484
1491
 
1485
1492
  let indexManager = IndexManager.make()
1493
+ let setQueryCache = makeSetQueryCache()
1486
1494
 
1487
1495
  let loadCatalogRows = (sql, ~indexName=?) =>
1488
1496
  sql
@@ -2064,6 +2072,8 @@ SELECT id, chain_id, -1, -1, contract_name FROM unnest($1::text[],$2::${addrChai
2064
2072
  ~table,
2065
2073
  ~itemSchema=itemSchema->S.toUnknown,
2066
2074
  ~pgSchema,
2075
+ ~setQueryCache,
2076
+ ~chainIdMode,
2067
2077
  )
2068
2078
  }
2069
2079
 
@@ -2347,6 +2357,7 @@ SELECT id, chain_id, -1, -1, contract_name FROM unnest($1::text[],$2::${addrChai
2347
2357
  sql,
2348
2358
  ~batch,
2349
2359
  ~pgSchema,
2360
+ ~setQueryCache,
2350
2361
  ~rollback,
2351
2362
  ~isInReorgThreshold,
2352
2363
  ~config,