envio 3.15.0 → 3.16.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 (99) hide show
  1. package/README.md +40 -34
  2. package/evm.schema.json +11 -2
  3. package/fuel.schema.json +10 -1
  4. package/index.d.ts +6 -0
  5. package/licenses/README.md +1 -1
  6. package/package.json +8 -8
  7. package/src/Batch.res.mjs +5 -5
  8. package/src/BatchProcessing.res +1 -0
  9. package/src/BatchProcessing.res.mjs +1 -0
  10. package/src/ChainState.res +48 -7
  11. package/src/ChainState.res.mjs +27 -11
  12. package/src/Clock.res +57 -0
  13. package/src/Clock.res.mjs +72 -0
  14. package/src/Config.res +10 -3
  15. package/src/Config.res.mjs +4 -2
  16. package/src/Core.res +4 -40
  17. package/src/EffectState.res +10 -2
  18. package/src/EffectState.res.mjs +8 -4
  19. package/src/EffectState.resi +3 -1
  20. package/src/EntityId.res +2 -7
  21. package/src/EntityId.res.mjs +1 -8
  22. package/src/EntityTables.res +4 -1
  23. package/src/EntityTables.res.mjs +1 -1
  24. package/src/FetchState.res +157 -69
  25. package/src/FetchState.res.mjs +144 -49
  26. package/src/FinalizeBackfill.res +2 -4
  27. package/src/FinalizeBackfill.res.mjs +3 -4
  28. package/src/InMemoryTable.res +17 -16
  29. package/src/InMemoryTable.res.mjs +14 -15
  30. package/src/IndexerState.res +28 -4
  31. package/src/IndexerState.res.mjs +19 -6
  32. package/src/IndexerState.resi +4 -0
  33. package/src/Internal.res +0 -49
  34. package/src/Internal.res.mjs +1 -80
  35. package/src/LoadLayer.res +14 -8
  36. package/src/LoadLayer.res.mjs +8 -6
  37. package/src/Main.res.mjs +1 -1
  38. package/src/Persistence.res +3 -23
  39. package/src/Persistence.res.mjs +3 -3
  40. package/src/Rollback.res +0 -1
  41. package/src/Rollback.res.mjs +1 -1
  42. package/src/SafeCheckpointTracking.res +4 -4
  43. package/src/SafeCheckpointTracking.res.mjs +1 -1
  44. package/src/StagedRows.res +106 -0
  45. package/src/StagedRows.res.mjs +102 -0
  46. package/src/Staging.res +32 -14
  47. package/src/Staging.res.mjs +17 -20
  48. package/src/Storage.res +277 -948
  49. package/src/Storage.res.mjs +215 -707
  50. package/src/Supervisor.res +75 -56
  51. package/src/Supervisor.res.mjs +55 -26
  52. package/src/TestIndexer.res +22 -27
  53. package/src/TestIndexer.res.mjs +20 -17
  54. package/src/Throttler.res +15 -8
  55. package/src/Throttler.res.mjs +9 -6
  56. package/src/Throttler.resi +1 -1
  57. package/src/Utils.res +0 -6
  58. package/src/Utils.res.mjs +0 -5
  59. package/src/Writing.res +0 -1
  60. package/src/Writing.res.mjs +1 -1
  61. package/src/bindings/ClickHouse.res +0 -322
  62. package/src/bindings/ClickHouse.res.mjs +0 -338
  63. package/src/bindings/StorageClient.res +107 -174
  64. package/src/bindings/StorageClient.res.mjs +19 -8
  65. package/src/bindings/Vitest.res +0 -26
  66. package/src/bindings/Vitest.res.mjs +0 -3
  67. package/src/db/Table.res +24 -150
  68. package/src/db/Table.res.mjs +38 -272
  69. package/src/sources/ChainSources.res +2 -0
  70. package/src/sources/ChainSources.res.mjs +2 -2
  71. package/src/sources/EvmChain.res +3 -0
  72. package/src/sources/EvmChain.res.mjs +4 -2
  73. package/src/sources/EvmHyperSyncSource.res +2 -2
  74. package/src/sources/EvmHyperSyncSource.res.mjs +1 -1
  75. package/src/sources/EvmRpcClient.res +13 -3
  76. package/src/sources/EvmRpcClient.res.mjs +4 -3
  77. package/src/sources/EvmRpcWs.res +4 -3
  78. package/src/sources/EvmRpcWs.res.mjs +2 -2
  79. package/src/sources/HeightFeed.res +31 -10
  80. package/src/sources/HeightFeed.res.mjs +10 -6
  81. package/src/sources/HeightStream.res +32 -17
  82. package/src/sources/HeightStream.res.mjs +10 -10
  83. package/src/sources/HyperSyncSSE.res +4 -6
  84. package/src/sources/HyperSyncSSE.res.mjs +2 -2
  85. package/src/sources/RpcSource.res +10 -1
  86. package/src/sources/RpcSource.res.mjs +4 -3
  87. package/src/sources/Source.res +3 -0
  88. package/src/sources/SourceManager.res +84 -77
  89. package/src/sources/SourceManager.res.mjs +17 -20
  90. package/src/sources/SourceManager.resi +3 -4
  91. package/src/sources/StartBlockResolver.res +1 -0
  92. package/src/sources/StartBlockResolver.res.mjs +3 -2
  93. package/src/sources/SvmHyperSyncSource.res +2 -2
  94. package/src/sources/SvmHyperSyncSource.res.mjs +1 -1
  95. package/svm.schema.json +11 -2
  96. package/src/PgWriting.res +0 -36
  97. package/src/PgWriting.res.mjs +0 -34
  98. package/src/db/IndexDefinition.res +0 -47
  99. package/src/db/IndexDefinition.res.mjs +0 -67
@@ -1,6 +1,5 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
- import * as Fs from "fs";
4
3
  import * as Env from "./Env.res.mjs";
5
4
  import * as Sql from "./db/Sql.res.mjs";
6
5
  import * as Path from "path";
@@ -12,16 +11,16 @@ import * as Logger from "./Logger.res.mjs";
12
11
  import * as Schema from "./db/Schema.res.mjs";
13
12
  import * as ChainId from "./ChainId.res.mjs";
14
13
  import * as Staging from "./Staging.res.mjs";
15
- import * as EntityId from "./EntityId.res.mjs";
16
14
  import * as Frontier from "./db/Frontier.res.mjs";
17
15
  import * as Internal from "./Internal.res.mjs";
18
- import * as PgWriting from "./PgWriting.res.mjs";
19
16
  import * as ChainState from "./ChainState.res.mjs";
20
17
  import * as ClickHouse from "./bindings/ClickHouse.res.mjs";
21
18
  import * as LoadFilter from "./db/LoadFilter.res.mjs";
19
+ import * as StagedRows from "./StagedRows.res.mjs";
22
20
  import * as AddressRows from "./AddressRows.res.mjs";
23
21
  import * as Performance from "./bindings/Performance.res.mjs";
24
22
  import * as Persistence from "./Persistence.res.mjs";
23
+ import * as Stdlib_Null from "@rescript/runtime/lib/es6/Stdlib_Null.js";
25
24
  import * as EntityFilter from "./db/EntityFilter.res.mjs";
26
25
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
27
26
  import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
@@ -30,21 +29,12 @@ import * as HistoryPolicy from "./HistoryPolicy.res.mjs";
30
29
  import * as InternalTable from "./db/InternalTable.res.mjs";
31
30
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
32
31
  import * as StorageClient from "./bindings/StorageClient.res.mjs";
33
- import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
34
32
  import * as Stdlib_Promise from "@rescript/runtime/lib/es6/Stdlib_Promise.js";
35
33
  import * as ContractMapping from "./ContractMapping.res.mjs";
36
- import * as IndexDefinition from "./db/IndexDefinition.res.mjs";
37
34
  import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
38
35
  import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
39
- import * as CheckpointSequence from "./db/CheckpointSequence.res.mjs";
40
36
  import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
41
37
 
42
- function formatSeconds(seconds) {
43
- return (Math.round(seconds * 100) / 100).toString();
44
- }
45
-
46
- let slowOnLargeDatabaseNotice = "This can take a long time on a large database.";
47
-
48
38
  function storeName(store) {
49
39
  if (store === "Postgres") {
50
40
  return "postgres";
@@ -55,83 +45,11 @@ function storeName(store) {
55
45
 
56
46
  let postgresName = "postgres";
57
47
 
58
- function logIndexEvent(logger, pgSchema, event) {
59
- switch (event.kind) {
60
- case "Invalid" :
61
- return Logger.warn(logger, `Ignoring invalid PostgreSQL indexes in schema "` + pgSchema + `". They can't serve queries, so the indexer builds its own alongside them.`, {
62
- storage: postgresName,
63
- indexes: event.names
64
- });
65
- case "Planned" :
66
- let rebuilt = event.rebuilt;
67
- let missing = event.missing;
68
- if (Utils.$$Array.notEmpty(rebuilt)) {
69
- Logger.warn(logger, `PostgreSQL reports ` + rebuilt.length.toString() + ` of the indexer's own indexes as invalid, so they can't serve queries. Rebuilding them.`, {
70
- storage: postgresName,
71
- indexes: rebuilt
72
- });
73
- }
74
- if (missing.length !== 0) {
75
- return Logger.info(logger, `Creating the ` + missing.length.toString() + ` remaining schema indexes before the indexer reports ready. Writes are paused until they are committed. ` + slowOnLargeDatabaseNotice, {
76
- storage: postgresName,
77
- indexes: missing
78
- });
79
- } else {
80
- return Logger.info(logger, `All ` + event.declared.toString() + ` schema indexes are already in place. Marking the indexer ready.`, {
81
- storage: postgresName
82
- });
83
- }
84
- case "Building" :
85
- if (event.purpose === "Query") {
86
- return Logger.info(logger, (
87
- event.isRebuild ? "Rebuilding unusable index" : "Creating index"
88
- ) + ` "` + event.name + `" to serve a getWhere query on "` + event.tableName + `". Writes to the table are paused until it completes. ` + slowOnLargeDatabaseNotice, {
89
- storage: postgresName
90
- });
91
- } else {
92
- return Logger.info(logger, (
93
- event.isRebuild ? "Rebuilding unusable index" : "Creating missing index"
94
- ) + ` "` + event.name + `" the schema promises but the database no longer has. Writes to the table are paused until it completes. ` + slowOnLargeDatabaseNotice, {
95
- storage: postgresName
96
- });
97
- }
98
- case "Built" :
99
- let tmp;
100
- tmp = event.purpose === "Query" ? " Resuming indexing." : "";
101
- return Logger.info(logger, `Index "` + event.name + `" is ready after ` + formatSeconds(event.seconds) + `s.` + tmp, {
102
- storage: postgresName
103
- });
104
- case "Failed" :
105
- if (event.purpose === "Query") {
106
- return Logger.warn(logger, `Failed to create an index on "` + event.tableName + `"(` + event.columns.map(column => `"` + column + `"`).join(", ") + `) for a getWhere query. The query runs without it.`, {
107
- storage: postgresName,
108
- err: event.error,
109
- code: event.code
110
- });
111
- } else {
112
- return Logger.warn(logger, `Failed to restore the schema index "` + event.name + `". Queries relying on it run unindexed until the next restart.`, {
113
- storage: postgresName,
114
- err: event.error,
115
- code: event.code
116
- });
117
- }
118
- case "ResyncFailed" :
119
- return Logger.trace(logger, `Could not re-read the index "` + event.name + `" after a failed build. The next attempt reads it again.`, {
120
- storage: postgresName,
121
- err: event.error,
122
- code: event.code
123
- });
124
- case "Committed" :
125
- return Logger.info(logger, `Committed ` + event.count.toString() + ` schema indexes and the ready timestamp in ` + formatSeconds(event.seconds) + `s.`, {
126
- storage: postgresName
127
- });
128
- }
129
- }
130
-
131
- function postgresOptions(pgSchemaOpt, isHasuraEnabledOpt, maxConnectionsOpt) {
48
+ function postgresOptions(pgSchemaOpt, isHasuraEnabledOpt, maxConnectionsOpt, isolatedOpt) {
132
49
  let pgSchema = pgSchemaOpt !== undefined ? pgSchemaOpt : Env.Db.publicSchema;
133
50
  let isHasuraEnabled = isHasuraEnabledOpt !== undefined ? isHasuraEnabledOpt : false;
134
51
  let maxConnections = maxConnectionsOpt !== undefined ? maxConnectionsOpt : Env.Db.maxConnections;
52
+ let isolated = isolatedOpt !== undefined ? isolatedOpt : false;
135
53
  return {
136
54
  host: Env.Db.host,
137
55
  port: Env.Db.port,
@@ -142,288 +60,23 @@ function postgresOptions(pgSchemaOpt, isHasuraEnabledOpt, maxConnectionsOpt) {
142
60
  maxConnections: maxConnections,
143
61
  applicationName: `hyperindex/` + Utils.EnvioPackage.value.version,
144
62
  pgSchema: pgSchema,
145
- isHasuraEnabled: isHasuraEnabled
63
+ isHasuraEnabled: isHasuraEnabled,
64
+ isolated: isolated
146
65
  };
147
66
  }
148
67
 
149
- function makeClient(logger, chainIdModeOpt, postgres, clickhouse) {
150
- let chainIdMode = chainIdModeOpt !== undefined ? chainIdModeOpt : "int32";
68
+ function makeClient(logger, config, postgres, clickhouse) {
151
69
  return StorageClient.make({
152
- chainIdMode: chainIdMode,
153
70
  postgres: postgres,
154
71
  clickhouse: clickhouse
155
- }, event => logIndexEvent(logger, postgres.pgSchema, event), message => Logger.warn(logger, message, {
156
- storage: "clickhouse"
157
- }));
158
- }
159
-
160
- function partitionTableName(entityConfig, chainId) {
161
- let chainIdStr = ChainId.toString(chainId);
162
- return Table.fitPgTableName(entityConfig.table.tableName + `$` + chainIdStr, `$` + entityConfig.index.toString() + `$` + chainIdStr);
163
- }
164
-
165
- function indexTableNames(entityConfig, partitionChainIds) {
166
- let match = Table.getChainIdField(entityConfig.table);
167
- if (match !== undefined) {
168
- if (partitionChainIds !== undefined) {
169
- return partitionChainIds.map(chainId => partitionTableName(entityConfig, chainId));
170
- } else {
171
- return [entityConfig.table.tableName];
172
- }
173
- } else {
174
- return [entityConfig.table.tableName];
175
- }
176
- }
177
-
178
- function getSchemaIndexes(entities, partitionChainIds) {
179
- let derivedSchema = Schema.make(entities.map(e => e.table));
180
- let all = [];
181
- entities.forEach(param => {
182
- let table = param.table;
183
- Table.getSingleIndexes(table).forEach(column => {
184
- all.push(IndexDefinition.single(table.tableName, column));
185
- });
186
- Table.getCompositeIndexes(table).forEach(indexFields => {
187
- all.push(IndexDefinition.fromIndexFields(table.tableName, indexFields));
188
- });
189
- });
190
- entities.forEach(param => {
191
- Table.getDerivedFromFields(param.table).forEach(derivedFromField => {
192
- let column = Utils.unwrapResultExn(Schema.getDerivedFromPgFieldName(derivedSchema, derivedFromField));
193
- all.push(IndexDefinition.single(derivedFromField.derivedFromEntity, column));
194
- });
195
- });
196
- let seen = new Set();
197
- let entityByTableName = {};
198
- entities.forEach(entityConfig => {
199
- entityByTableName[entityConfig.table.tableName] = entityConfig;
200
- });
201
- return all.filter(definition => {
202
- let identity = IndexDefinition.describe(definition);
203
- if (seen.has(identity)) {
204
- return false;
205
- } else {
206
- seen.add(identity);
207
- return true;
208
- }
209
- }).flatMap(definition => indexTableNames(Stdlib_Option.getOrThrow(entityByTableName[definition.tableName], undefined), partitionChainIds).map(tableName => ({
210
- tableName: tableName,
211
- columns: definition.columns,
212
- method: definition.method
213
- })));
214
- }
215
-
216
- function pgColumnInput(field) {
217
- let match = Table.pgFieldTypeParts(field.fieldType);
218
- return {
219
- name: Table.getPgDbFieldName(field),
220
- fieldType: match[0],
221
- isArray: field.isArray,
222
- isNullable: field.isNullable,
223
- isPrimaryKey: field.isPrimaryKey,
224
- defaultValue: field.defaultValue,
225
- precision: match[1],
226
- scale: match[2],
227
- enumName: match[3]
228
- };
229
- }
230
-
231
- let rowSchemaCache = new WeakMap();
232
-
233
- function getRowSchema(entityConfig) {
234
- let cached = rowSchemaCache.get(entityConfig);
235
- if (cached !== undefined) {
236
- return Primitive_option.valFromOption(cached);
237
- }
238
- let chainIdField = Table.getChainIdField(entityConfig.table);
239
- let schema = chainIdField !== undefined ? S$RescriptSchema.schema(s => {
240
- let dict = {};
241
- let match = entityConfig.schema.t;
242
- let exit = 0;
243
- if (typeof match !== "object" || match.TAG !== "object") {
244
- exit = 1;
245
- } else {
246
- match.items.forEach(param => {
247
- dict[param.location] = s.m(param.schema);
248
- });
249
- }
250
- if (exit === 1) {
251
- Stdlib_JsError.throwWithMessage(`Unexpected non-object schema for entity "` + entityConfig.name + `".`);
252
- }
253
- dict[chainIdField.fieldName] = s.m(ChainId.schema);
254
- return dict;
255
- }) : entityConfig.schema;
256
- rowSchemaCache.set(entityConfig, schema);
257
- return schema;
258
- }
259
-
260
- function register(client, table, itemSchema, postgresOpt, clickhouse, appendOnlyOpt, history) {
261
- let postgres = postgresOpt !== undefined ? postgresOpt : true;
262
- let appendOnly = appendOnlyOpt !== undefined ? appendOnlyOpt : false;
263
- let fields = Table.schemaOrderedFields(table, itemSchema);
264
- let chainIdField = Table.getChainIdField(table);
265
- let registered = client.registerTable({
266
- name: table.tableName,
267
- postgres: postgres ? ({
268
- columns: Table.getFields(table).map(pgColumnInput),
269
- writeColumns: fields.map(Table.getPgDbFieldName),
270
- partitionByColumn: Stdlib_Option.map(chainIdField, Table.getPgDbFieldName),
271
- appendOnly: appendOnly,
272
- historyTable: history,
273
- chainIdColumn: history !== undefined ? Stdlib_Option.map(chainIdField, Table.getPgDbFieldName) : undefined
274
- }) : undefined,
275
- clickhouse: clickhouse
276
- });
277
- return {
278
- handle: registered.handle,
279
- home: registered.home,
280
- table: table,
281
- fields: fields,
282
- postgres: Stdlib_Option.map(registered.postgres, param => ({
283
- staged: Stdlib_Option.map(param.kinds, kinds => PgWriting.columns(fields, kinds.map(Staging.kindOfOrdinal)))
284
- })),
285
- clickhouse: Stdlib_Option.map(registered.clickhouse, __x => ClickHouse.makeTable(registered.handle, Stdlib_Option.getOr(history, table.tableName), __x))
286
- };
287
- }
288
-
289
- function registerEntity(client, entityConfig) {
290
- return register(client, entityConfig.table, getRowSchema(entityConfig), entityConfig.storage.postgres, entityConfig.storage.clickhouse ? ClickHouse.entityTable(entityConfig) : undefined, undefined, EntityHistory.historyTableName(entityConfig.name, entityConfig.index));
291
- }
292
-
293
- function renderDocument(value, isNullable) {
294
- if (value == null) {
295
- if (value === null && !isNullable) {
296
- return "null";
297
- } else {
298
- return value;
299
- }
300
- } else {
301
- return Sql.stringifyDocument(value);
302
- }
303
- }
304
-
305
- let arenaConverters = new WeakMap();
306
-
307
- let cellConverters = new WeakMap();
308
-
309
- function converterOf(registered, forArena, itemSchema) {
310
- let converters = forArena ? arenaConverters : cellConverters;
311
- let convert = converters.get(itemSchema);
312
- if (convert !== undefined) {
313
- return convert;
314
- }
315
- let documents = Stdlib_Array.filterMapWithIndex(registered.fields, (field, index) => {
316
- if (field.fieldType === "Json") {
317
- return [
318
- index,
319
- field
320
- ];
321
- }
322
- });
323
- let schema = forArena ? Table.toDbSchema(registered.table, itemSchema) : itemSchema;
324
- let convert$1 = S$RescriptSchema.compile(S$RescriptSchema.preprocess(S$RescriptSchema.unnest(schema), param => ({
325
- s: columns => {
326
- documents.forEach(param => {
327
- let field = param[1];
328
- let values = columns[param[0]];
329
- for (let row = 0, row_finish = values.length; row < row_finish; ++row) {
330
- let value = values[row];
331
- values[row] = !(value == null) && field.isArray ? value.map(document => renderDocument(document, true)) : renderDocument(value, field.isNullable);
332
- }
333
- });
334
- return columns;
335
- }
336
- })), "Output", "Input", "Sync", false);
337
- converters.set(itemSchema, convert$1);
338
- return convert$1;
339
- }
340
-
341
- function refuseNonFinite(values, fields) {
342
- fields.forEach((field, index) => {
343
- if (field.fieldType !== "Number") {
344
- return;
345
- }
346
- let column = Table.getPgDbFieldName(field);
347
- let check = value => {
348
- if (value == null) {
349
- return;
350
- }
351
- Staging.finiteOrThrow(value, column);
352
- };
353
- values[index].forEach(value => {
354
- if (!field.isArray) {
355
- return check(value);
356
- }
357
- if (value == null) {
358
- return;
359
- }
360
- value.forEach(check);
361
- });
362
- });
363
- }
364
-
365
- function stagedFor(staged, store, stage) {
366
- let started = Performance.now();
367
- let result = stage();
368
- let name = storeName(store);
369
- staged.seconds[name] = Stdlib_Option.getOr(staged.seconds[name], 0) + Performance.secondsSince(started);
370
- return result;
371
- }
372
-
373
- function rowsOrThrow(client, registered, itemSchema, items, staged) {
374
- return stagedFor(staged, "Postgres", () => {
375
- let rows = items.length;
376
- try {
377
- let match = registered.postgres;
378
- if (match === undefined) {
379
- return Stdlib_JsError.throwWithMessage(`Postgres doesn't keep "` + registered.table.tableName + `"`);
380
- }
381
- let columns = match.staged;
382
- if (columns !== undefined) {
383
- let values = converterOf(registered, true, itemSchema)(items);
384
- let handle = PgWriting.stage(StorageClient.arena(client, "Postgres"), registered.handle, columns, values, rows);
385
- staged.handles.push(handle);
386
- return {
387
- staged: handle,
388
- rows: rows
389
- };
390
- }
391
- let values$1 = converterOf(registered, false, itemSchema)(items);
392
- refuseNonFinite(values$1, registered.fields);
393
- return {
394
- cells: Sql.params(values$1.flat(1)),
395
- rows: rows
396
- };
397
- } catch (raw_exn) {
398
- let exn = Primitive_exceptions.internalToException(raw_exn);
399
- if (exn.RE_EXN_ID === S$RescriptSchema.Raised) {
400
- throw {
401
- RE_EXN_ID: Persistence.StorageError,
402
- message: `Failed to convert items for table "` + registered.table.tableName + `"`,
403
- reason: exn,
404
- Error: new Error()
405
- };
406
- }
407
- throw {
408
- RE_EXN_ID: Persistence.StorageError,
409
- message: `Failed to insert items into table "` + registered.table.tableName + `"`,
410
- reason: Utils.prettifyExn(exn),
411
- Error: new Error()
412
- };
413
- }
414
- });
415
- }
416
-
417
- function renderIds(table, ids) {
418
- return Table.encodeIdsToJson(table, ids).map(Sql.render);
72
+ }, Stdlib_Option.map(config, config => JSON.stringify(config.envioInfo)), logger);
419
73
  }
420
74
 
421
75
  function bounds(param) {
422
- let match = Frontier.unnestParams(param.byChain);
76
+ let byChain = param.byChain;
423
77
  return {
424
- sequence: param.sequence,
425
- chainIds: match[0],
426
- checkpointIds: match[1]
78
+ chainIds: Frontier.chainIds(byChain),
79
+ checkpointIds: Frontier.chainIds(byChain).map(chainId => Frontier.get(byChain, chainId))
427
80
  };
428
81
  }
429
82
 
@@ -474,149 +127,99 @@ function storageErrorOf(exn) {
474
127
  }
475
128
  }
476
129
 
477
- let rollbackRowStateSchema = Utils.$$WeakMap.memoize(table => S$RescriptSchema.object(s => [
478
- s.f(Table.idFieldName, Table.getIdSchema(table)),
479
- s.f(EntityHistory.changeFieldName, EntityHistory.RowAction.schema)
480
- ]));
481
-
482
130
  let rollbackChainIdSchema = Utils.$$WeakMap.memoize(table => Stdlib_Option.map(Table.getChainIdField(table), field => S$RescriptSchema.object(s => s.f(Table.getPgDbFieldName(field), ChainId.schema))));
483
131
 
484
132
  let rollbackRemovedIdSchema = Utils.$$WeakMap.memoize(table => S$RescriptSchema.object(s => s.f(Table.idFieldName, Table.getIdSchema(table))));
485
133
 
486
- function make(client, logger, entitiesOpt, cacheDir, ecosystem, isolatedOpt, onInitialize) {
487
- let entities = entitiesOpt !== undefined ? entitiesOpt : [];
488
- let isolated = isolatedOpt !== undefined ? isolatedOpt : false;
489
- let cacheDirPath = cacheDir !== undefined ? Primitive_option.valFromOption(cacheDir) : Path.resolve(".envio", "cache");
490
- let registry = {};
491
- let registered = (table, register) => {
492
- let registered$1 = registry[table.tableName];
493
- if (registered$1 !== undefined) {
494
- return registered$1;
495
- }
496
- let registered$2 = register();
497
- registry[table.tableName] = registered$2;
498
- return registered$2;
499
- };
500
- let entityTable = entityConfig => registered(entityConfig.table, () => registerEntity(client, entityConfig));
501
- let ownTable = (table, itemSchema, appendOnly) => registered(table, () => register(client, table, itemSchema, undefined, undefined, appendOnly, undefined));
502
- let rawEventsTable = () => ownTable(InternalTable.RawEvents.table, InternalTable.RawEvents.schema, true);
503
- entities.forEach(entityConfig => {
504
- entityTable(entityConfig);
134
+ let changeColumns = [
135
+ {
136
+ name: EntityHistory.checkpointIdFieldName,
137
+ kind: 2,
138
+ isNullable: false
139
+ },
140
+ {
141
+ name: EntityHistory.changeFieldName,
142
+ kind: 0,
143
+ isNullable: false
144
+ }
145
+ ];
146
+
147
+ function stageOrThrow(arena, columns, rows, fill) {
148
+ let stage = Staging.begin(arena, rows, columns);
149
+ try {
150
+ fill(stage);
151
+ return Staging.commit(stage);
152
+ } catch (exn) {
153
+ Staging.abort(stage);
154
+ throw exn;
155
+ }
156
+ }
157
+
158
+ function stageRowsOrThrow(arena, writers, items) {
159
+ return stageOrThrow(arena, StagedRows.columnsOf(writers), items.length, stage => {
160
+ items.forEach((item, row) => {
161
+ writers.forEach((param, column) => StagedRows.writeField(stage, writers, column, row, item[param.field.name]));
162
+ });
505
163
  });
506
- let tableOf = table => {
507
- let registered = registry[table.tableName];
508
- if (registered !== undefined) {
509
- return registered;
510
- } else if (Stdlib_Option.isSome(Internal.EffectCache.fromTableName(table.tableName))) {
511
- return ownTable(table, Internal.cacheItemSchema, undefined);
512
- } else {
513
- return Stdlib_JsError.throwWithMessage(`The storage has no table "` + table.tableName + `".`);
514
- }
515
- };
516
- let partitions = (entities, chainIds) => entities.filter(entityConfig => {
517
- if (entityConfig.storage.postgres) {
518
- return Stdlib_Option.isSome(Table.getChainIdField(entityConfig.table));
519
- } else {
520
- return false;
521
- }
522
- }).flatMap(entityConfig => chainIds.map(chainId => ({
523
- table: entityTable(entityConfig).handle,
524
- chainId: chainId,
525
- name: partitionTableName(entityConfig, chainId)
526
- })));
527
- let holding = () => client.holding();
528
- let scanCacheDir = async () => {
529
- let topEntries;
530
- try {
531
- topEntries = await Fs.promises.readdir(cacheDirPath);
532
- } catch (exn) {
533
- topEntries = [];
534
- }
535
- let result = [];
536
- await Promise.all(topEntries.map(async entry => {
537
- let entryPath = Path.join(cacheDirPath, entry);
538
- let isDir = (await Fs.promises.stat(entryPath)).isDirectory();
539
- if (isDir) {
540
- let subEntries = await Fs.promises.readdir(entryPath);
541
- let tsvs = subEntries.filter(sub => sub.endsWith(".tsv"));
542
- let chainId = Internal.EffectCache.parseChainId(entry);
543
- if (chainId === undefined) {
544
- if (Utils.$$Array.notEmpty(tsvs)) {
545
- return Stdlib_JsError.throwWithMessage(`Invalid effect cache directory ".envio/cache/` + entry + `". Chain cache directories must be named by a numeric chain id (e.g. "1"). Found cache files: ` + tsvs.join(", ") + `.`);
164
+ }
165
+
166
+ function stageChangesOrThrow(arena, writers, changes) {
167
+ let fields = writers.length;
168
+ return stageOrThrow(arena, StagedRows.columnsOf(writers).concat(changeColumns), changes.length, stage => {
169
+ changes.forEach((change, row) => {
170
+ let entityId = change.entityId;
171
+ if (change.type === "SET") {
172
+ let entity = change.entity;
173
+ writers.forEach((param, column) => {
174
+ let field = param.field;
175
+ StagedRows.writeField(stage, writers, column, row, field.name === Table.idFieldName ? entityId : entity[field.name]);
176
+ });
177
+ } else {
178
+ writers.forEach((param, column) => {
179
+ if (param.field.name === Table.idFieldName) {
180
+ return StagedRows.writeField(stage, writers, column, row, entityId);
546
181
  } else {
547
- return;
182
+ return Staging.writeDeletedValue(stage, column, row, undefined);
548
183
  }
549
- }
550
- let chainId$1 = Primitive_option.valFromOption(chainId);
551
- tsvs.forEach(sub => {
552
- let effectName = sub.slice(0, -4);
553
- let table = Internal.makeCacheTable(effectName, chainId$1);
554
- result.push([
555
- table,
556
- Path.join(entryPath, sub)
557
- ]);
558
184
  });
559
- return;
560
185
  }
561
- if (!entry.endsWith(".tsv")) {
562
- return;
563
- }
564
- let effectName = entry.slice(0, -4);
565
- let table = Internal.makeCacheTable(effectName, "crossChain");
566
- result.push([
567
- table,
568
- entryPath
569
- ]);
570
- }));
571
- return result;
572
- };
186
+ Staging.writeValue(stage, fields, row, change.checkpointId);
187
+ let tmp;
188
+ tmp = change.type === "SET" ? 0 : 1;
189
+ Staging.writeValue(stage, fields + 1 | 0, row, tmp);
190
+ });
191
+ });
192
+ }
193
+
194
+ function make(client, cacheDir, ecosystem, onInitialize) {
195
+ let cacheDirPath = cacheDir !== undefined ? Primitive_option.valFromOption(cacheDir) : Path.resolve(".envio", "cache");
196
+ let layouts = client.layouts();
197
+ let entityIndexByName = {};
198
+ layouts.entities.forEach((layout, index) => {
199
+ entityIndexByName[layout.name] = index;
200
+ });
201
+ let entityWriters = layouts.entities.map(layout => StagedRows.writersOf(layout.fields));
202
+ let rawEventWriters = StagedRows.writersOf(layouts.rawEvents);
203
+ let effectCacheWriters = StagedRows.writersOf(layouts.effectCache);
204
+ let holding = () => client.holding();
573
205
  let restoreEffectCache = async withUpload => {
574
- if (withUpload) {
575
- let entries = await scanCacheDir();
576
- if (entries.length !== 0) {
577
- try {
578
- await client.uploadEffectCache(entries.map(param => ({
579
- table: ownTable(param[0], Internal.cacheItemSchema, undefined).handle,
580
- path: param[1]
581
- })));
582
- Logger.info(logger, "Successfully uploaded cache.", undefined);
583
- } catch (raw_exn) {
584
- let exn = Primitive_exceptions.internalToException(raw_exn);
585
- Logger.errorWithExn(logger, "Failed to upload cache, continuing without it.", exn);
586
- }
587
- } else {
588
- Logger.info(logger, "No saved effect cache to load from .envio/cache.", undefined);
589
- }
590
- }
591
206
  let cache = {};
592
- (await client.effectCacheTables()).forEach(param => {
207
+ (await client.restoreEffectCache(cacheDirPath, withUpload)).forEach(param => {
593
208
  let tableName = param.tableName;
594
- let match = Internal.EffectCache.fromTableName(tableName);
595
- if (match !== undefined) {
596
- cache[tableName] = {
597
- effectName: match[0],
598
- scope: match[1],
599
- tableName: tableName,
600
- count: param.rows
601
- };
602
- return;
603
- }
209
+ let chainId = param.chainId;
210
+ cache[tableName] = {
211
+ effectName: param.effectName,
212
+ scope: chainId !== undefined ? Primitive_option.valFromOption(chainId) : "crossChain",
213
+ tableName: tableName,
214
+ count: param.rows
215
+ };
604
216
  });
605
217
  return cache;
606
218
  };
607
- let initialize = async (chainConfigsOpt, entitiesOpt, enumsOpt, contractMapping, envioInfo) => {
219
+ let initialize = async (chainConfigsOpt, contractMapping, envioInfo) => {
608
220
  let chainConfigs = chainConfigsOpt !== undefined ? chainConfigsOpt : [];
609
- let entities = entitiesOpt !== undefined ? entitiesOpt : [];
610
- let enums = enumsOpt !== undefined ? enumsOpt : [];
611
221
  let rowsByChain = chainConfigs.map(chainConfig => ChainState.configStorageRows(chainConfig, ecosystem, contractMapping));
612
222
  await client.initialize({
613
- sequence: CheckpointSequence.fromEntities(entities),
614
- tables: [rawEventsTable().handle].concat(entities.map(entityConfig => entityTable(entityConfig).handle)),
615
- partitions: partitions(entities, chainConfigs.map(chainConfig => chainConfig.id)),
616
- enums: enums.map(enumConfig => ({
617
- name: enumConfig.name,
618
- variants: enumConfig.variants
619
- })),
620
223
  chains: chainConfigs.map(toChainConfig),
621
224
  envioInfo: JSON.stringify(envioInfo),
622
225
  contractNames: ContractMapping.names(contractMapping),
@@ -649,9 +252,11 @@ function make(client, logger, entitiesOpt, cacheDir, ecosystem, isolatedOpt, onI
649
252
  };
650
253
  let loadOrThrow = async (filter, table) => {
651
254
  let conditions = LoadFilter.make(filter, table, field => field.pgDbFieldName);
255
+ let entity = entityIndexByName[table.tableName];
256
+ let loading = entity !== undefined ? StorageClient.loadWhere(client, entity, conditions) : StorageClient.loadEffectCache(client, table.tableName, conditions);
652
257
  let rows;
653
258
  try {
654
- rows = await StorageClient.loadWhere(client, tableOf(table).handle, conditions);
259
+ rows = await loading;
655
260
  } catch (raw_exn) {
656
261
  let exn = Primitive_exceptions.internalToException(raw_exn);
657
262
  throw {
@@ -691,50 +296,17 @@ function make(client, logger, entitiesOpt, cacheDir, ecosystem, isolatedOpt, onI
691
296
  }));
692
297
  return columns;
693
298
  };
694
- let partitionChainIds = chainIds => {
695
- if (isolated) {
696
- return Primitive_option.some(chainIds);
697
- }
698
- };
699
- let queryTableName = (entityConfig, scope) => {
700
- if (scope === "crossChain") {
701
- return entityConfig.table.tableName;
702
- } else {
703
- return indexTableNames(entityConfig, partitionChainIds([scope]))[0];
704
- }
705
- };
706
299
  let ensureQueryIndexes = async (entityConfig, scope, filters) => {
707
- let building = client.ensureQueryIndexes(entityTable(entityConfig).handle, queryTableName(entityConfig, scope), filterColumns(entityConfig.table, filters));
300
+ let building = client.ensureQueryIndexes(entityConfig.index, Stdlib_Null.fromOption(Internal.chainScopeChainId(scope)), filterColumns(entityConfig.table, filters));
708
301
  if (building === null) {
709
302
  return;
710
303
  } else {
711
304
  return await building;
712
305
  }
713
306
  };
714
- let schemaIndexes = (entities, chainIds) => getSchemaIndexes(entities.filter(e => e.storage.postgres), partitionChainIds(chainIds)).map(IndexDefinition.toInput);
715
- let ensureSchemaIndexes = (entities, chainIds) => client.ensureSchemaIndexes(schemaIndexes(entities, chainIds));
716
- let finalizeBackfill = (entities, chainIds, readyAt) => client.finalizeBackfill(schemaIndexes(entities, chainIds), chainIds, readyAt.getTime());
717
- let dumpEffectCache = async () => {
718
- try {
719
- let tables = (await client.effectCacheTables()).filter(param => param.rows > 0);
720
- if (Utils.$$Array.notEmpty(tables)) {
721
- Logger.info(logger, `Dumping cache: ` + tables.map(param => param.tableName + " (" + param.rows.toString() + " rows)").join(", "), undefined);
722
- await client.dumpEffectCache(Stdlib_Array.filterMap(tables, param => {
723
- let tableName = param.tableName;
724
- return Stdlib_Option.map(Internal.EffectCache.fromTableName(tableName), param => ({
725
- tableName: tableName,
726
- path: Path.join(cacheDirPath, Internal.EffectCache.toCachePath(param[0], param[1]))
727
- }));
728
- }));
729
- return Logger.info(logger, `Successfully dumped cache to ` + cacheDirPath, undefined);
730
- } else {
731
- return;
732
- }
733
- } catch (raw_exn) {
734
- let exn = Primitive_exceptions.internalToException(raw_exn);
735
- return Logger.errorWithExn(logger, `Failed to dump cache.`, exn);
736
- }
737
- };
307
+ let ensureSchemaIndexes = chainIds => client.ensureSchemaIndexes(chainIds);
308
+ let finalizeBackfill = (chainIds, readyAt) => client.finalizeBackfill(chainIds, readyAt.getTime());
309
+ let dumpEffectCache = () => client.dumpEffectCache(cacheDirPath);
738
310
  let readStoredConfig = async () => {
739
311
  let stored = await client.readStoredConfig();
740
312
  let configAddresses = StorageClient.read(client, stored.configAddresses);
@@ -772,19 +344,14 @@ function make(client, logger, entitiesOpt, cacheDir, ecosystem, isolatedOpt, onI
772
344
  contractMapping: ContractMapping.fromStoredNames(match$1)
773
345
  };
774
346
  };
775
- let addChain = (chainConfig, entities, contractMapping) => client.addChain({
347
+ let addChain = (chainConfig, contractMapping) => client.addChain({
776
348
  chain: toChainConfig(chainConfig),
777
- partitions: partitions(entities, [chainConfig.id]),
778
349
  addresses: addressColumns(ChainState.configStorageRows(chainConfig, ecosystem, contractMapping))
779
350
  });
780
- let resumeInitialState = async (entities, chainIds, contractMapping) => {
351
+ let resumeInitialState = async (chainIds, contractMapping) => {
781
352
  let match = await Promise.all([
782
353
  restoreEffectCache(false),
783
- client.resume({
784
- sequence: CheckpointSequence.fromEntities(entities),
785
- tables: entities.map(entityConfig => entityTable(entityConfig).handle),
786
- chainIds: chainIds
787
- })
354
+ client.resume(chainIds)
788
355
  ]);
789
356
  let resumed = match[1];
790
357
  let addressRowsByChainId = AddressRows.group(StorageClient.read(client, resumed.addresses));
@@ -811,10 +378,10 @@ function make(client, logger, entitiesOpt, cacheDir, ecosystem, isolatedOpt, onI
811
378
  });
812
379
  let checkpointFrontier = Frontier.fromEntries(stored.map(param => [
813
380
  param[0],
814
- BigInt(param[1].checkpointId)
381
+ param[1].checkpointId
815
382
  ]));
816
383
  let reorgCheckpoints = resumed.reorgCheckpoints.map(checkpoint => ({
817
- id: BigInt(checkpoint.id),
384
+ id: checkpoint.id,
818
385
  chain_id: ChainId.normalizeOrThrow(checkpoint.chainId),
819
386
  block_number: checkpoint.blockNumber,
820
387
  block_hash: checkpoint.blockHash
@@ -841,15 +408,15 @@ function make(client, logger, entitiesOpt, cacheDir, ecosystem, isolatedOpt, onI
841
408
  });
842
409
  let setChainMeta = chainsData => client.setChainMeta(chainMeta(chainsData)).then(() => (undefined));
843
410
  let pruneStaleCheckpoints = safeCheckpoints => client.pruneCheckpoints(bounds(safeCheckpoints));
844
- let pruneStaleEntityHistory = (entityConfig, safeCheckpoints) => client.pruneHistory(entityTable(entityConfig).handle, bounds(safeCheckpoints));
845
- let getRollbackTargetCheckpoint = async (reorgChainId, lastKnownValidBlockNumber, sequence) => Stdlib_Option.map(Primitive_option.fromNullable(await client.rollbackTargetCheckpoint(reorgChainId, lastKnownValidBlockNumber, sequence)), prim => BigInt(prim));
411
+ let pruneStaleEntityHistory = (entityConfig, safeCheckpoints) => client.pruneHistory(entityConfig.index, bounds(safeCheckpoints));
412
+ let getRollbackTargetCheckpoint = async (reorgChainId, lastKnownValidBlockNumber) => Primitive_option.fromNullable(await client.rollbackTargetCheckpoint(reorgChainId, lastKnownValidBlockNumber));
846
413
  let getRollbackProgressDiff = async floors => (await client.rollbackProgressDiff(bounds(floors.checkpointBounds))).map(diff => ({
847
414
  chain_id: ChainId.normalizeOrThrow(diff.chainId),
848
415
  events_processed_diff: diff.eventsProcessed,
849
416
  new_progress_block_number: diff.progressBlock
850
417
  }));
851
418
  let getRollbackData = async (entityConfig, floors) => {
852
- let match = await client.rollbackData(entityTable(entityConfig).handle, bounds(floors.checkpointBounds));
419
+ let match = await client.rollbackData(entityConfig.index, bounds(floors.checkpointBounds));
853
420
  let restored = match.restored;
854
421
  let removedIdRows;
855
422
  try {
@@ -867,29 +434,28 @@ function make(client, logger, entitiesOpt, cacheDir, ecosystem, isolatedOpt, onI
867
434
  return "crossChain";
868
435
  }
869
436
  };
870
- let removals = removedIdRows.map(row => ({
871
- entityId: S$RescriptSchema.parseOrThrow(row, rollbackRemovedIdSchema(entityConfig.table)),
872
- scope: scopeOf(row)
873
- }));
874
- let restoredEntitiesResult = [];
875
- rollbackRows.forEach(row => {
876
- let match = S$RescriptSchema.parseOrThrow(row, rollbackRowStateSchema(entityConfig.table));
877
- if (match[1] === "SET") {
878
- restoredEntitiesResult.push(row);
879
- return;
880
- }
881
- removals.push({
882
- entityId: match[0],
883
- scope: scopeOf(row)
884
- });
885
- });
886
437
  return [
887
- removals,
888
- S$RescriptSchema.parseOrThrow(restoredEntitiesResult, Table.pgRowsSchema(entityConfig.table))
438
+ removedIdRows.map(row => ({
439
+ entityId: S$RescriptSchema.parseOrThrow(row, rollbackRemovedIdSchema(entityConfig.table)),
440
+ scope: scopeOf(row)
441
+ })),
442
+ S$RescriptSchema.parseOrThrow(rollbackRows, Table.pgRowsSchema(entityConfig.table))
889
443
  ];
890
444
  };
891
- let writeBatch = async (batch, rollback, config, allEntities, updatedEffectsCache, updatedEntities, registeredAddresses, chainMetaData, onWrite) => {
892
- let match = Frontier.unnestParams(Persistence.writtenFrontier(batch, rollback));
445
+ let stageRows = (table, writers, items) => stageRowsOrThrow(StorageClient.arena(client, rows => client.beginRows(table, rows)), writers, items);
446
+ let stageChanges = param => {
447
+ let entityConfig = param.entityConfig;
448
+ return stageChangesOrThrow(StorageClient.arena(client, rows => client.beginChanges(entityConfig.index, rows)), entityWriters[entityConfig.index], param.changes);
449
+ };
450
+ let changesErrorOf = (entityConfig, exn) => ({
451
+ RE_EXN_ID: Persistence.StorageError,
452
+ message: entityConfig.storage.postgres ? `Failed to insert items into table "` + entityConfig.table.tableName + `"` : `Failed to convert items for ClickHouse table "` + EntityHistory.historyTableName(entityConfig.name, entityConfig.index) + `"`,
453
+ reason: Utils.prettifyExn(exn)
454
+ });
455
+ let writeBatch = async (batch, rollback, config, updatedEffectsCache, updatedEntities, registeredAddresses, chainMetaData, onWrite) => {
456
+ let frontier = Persistence.writtenFrontier(batch, rollback);
457
+ let chainIds = Frontier.chainIds(frontier);
458
+ let frontierCheckpointIds = chainIds.map(chainId => Frontier.get(frontier, chainId));
893
459
  let rawEvents;
894
460
  if (config.enableRawEvents) {
895
461
  let seenLogCoordinates = new Set();
@@ -908,21 +474,35 @@ function make(client, logger, entitiesOpt, cacheDir, ecosystem, isolatedOpt, onI
908
474
  } else {
909
475
  rawEvents = [];
910
476
  }
911
- let staged_handles = [];
912
- let staged_seconds = {};
913
- let staged = {
914
- handles: staged_handles,
915
- seconds: staged_seconds
477
+ let handles = [];
478
+ let staged = handle => {
479
+ handles.push(handle);
480
+ return handle;
481
+ };
482
+ let stagingStarted = Performance.now();
483
+ let clickHouseStagingSeconds = {
484
+ contents: 0
485
+ };
486
+ let clickHouseOnlyStagingSeconds = {
487
+ contents: 0
916
488
  };
917
489
  let input;
918
490
  try {
919
491
  let tmp;
920
492
  if (Utils.$$Array.notEmpty(rawEvents)) {
921
- let table = rawEventsTable();
922
- tmp = {
923
- table: table.handle,
924
- rows: rowsOrThrow(client, table, InternalTable.RawEvents.schema, rawEvents, staged)
925
- };
493
+ let tmp$1;
494
+ try {
495
+ tmp$1 = staged(stageRows("RawEvents", rawEventWriters, rawEvents));
496
+ } catch (raw_exn) {
497
+ let exn = Primitive_exceptions.internalToException(raw_exn);
498
+ throw {
499
+ RE_EXN_ID: Persistence.StorageError,
500
+ message: `Failed to insert items into table "` + InternalTable.RawEvents.table.tableName + `"`,
501
+ reason: Utils.prettifyExn(exn),
502
+ Error: new Error()
503
+ };
504
+ }
505
+ tmp = tmp$1;
926
506
  } else {
927
507
  tmp = undefined;
928
508
  }
@@ -931,7 +511,6 @@ function make(client, logger, entitiesOpt, cacheDir, ecosystem, isolatedOpt, onI
931
511
  let rolledBackAddresses = param.rolledBackAddresses;
932
512
  return {
933
513
  bounds: bounds(param.floors.checkpointBounds),
934
- tables: allEntities.map(entityConfig => entityTable(entityConfig).handle),
935
514
  progress: param.progressedChains.map(progress),
936
515
  removedAddresses: {
937
516
  chainIds: rolledBackAddresses.map(key => key.chainId),
@@ -940,7 +519,7 @@ function make(client, logger, entitiesOpt, cacheDir, ecosystem, isolatedOpt, onI
940
519
  },
941
520
  diffCheckpoints: param.diffCheckpoints.map(diff => ({
942
521
  chainId: diff.chainId,
943
- checkpointId: diff.checkpointId.toString(),
522
+ checkpointId: diff.checkpointId,
944
523
  blockNumber: diff.blockNumber
945
524
  }))
946
525
  };
@@ -953,124 +532,41 @@ function make(client, logger, entitiesOpt, cacheDir, ecosystem, isolatedOpt, onI
953
532
  totalEventsProcessed: chainAfterBatch.totalEventsProcessed
954
533
  })),
955
534
  rawEvents: tmp,
956
- entities: updatedEntities.map(update => {
957
- let shouldSaveHistory = update.shouldSaveHistory;
958
- let changes = update.changes;
959
- let scope = update.scope;
960
- let entityConfig = update.entityConfig;
961
- let table = entityTable(entityConfig);
962
- let match = Table.getChainIdField(entityConfig.table);
963
- let chainIdStamp = match !== undefined && scope !== "crossChain" ? [
964
- match,
965
- scope
966
- ] : undefined;
967
- let postgresWrite = () => {
968
- let stamped;
969
- if (chainIdStamp !== undefined) {
970
- let chainId = chainIdStamp[1];
971
- let field = chainIdStamp[0];
972
- stamped = changes.map(change => {
973
- if (change.type === "SET") {
974
- return {
975
- type: "SET",
976
- entityId: change.entityId,
977
- entity: Internal.stampChainId(change.entity, field.fieldName, chainId),
978
- checkpointId: change.checkpointId
979
- };
980
- } else {
981
- return change;
982
- }
983
- });
984
- } else {
985
- stamped = changes;
986
- }
987
- let diffCheckpointId = Stdlib_Option.flatMap(rollback, r => CheckpointSequence.findForScope(config.checkpointSequence, r.diffFrontier, scope));
988
- let historySets = [];
989
- let historySetCheckpointIds = [];
990
- let historyDeleteIds = [];
991
- let historyDeleteCheckpointIds = [];
992
- let idsWithDiff = new Set();
993
- let latestChangeById = {};
994
- let orderedIds = [];
995
- stamped.forEach(change => {
996
- let entityId = change.entityId;
997
- let entityKey = EntityId.toKey(entityId);
998
- if (Stdlib_Option.isNone(latestChangeById[entityKey])) {
999
- orderedIds.push(entityId);
1000
- }
1001
- latestChangeById[entityKey] = change;
1002
- if (!shouldSaveHistory) {
1003
- return;
1004
- }
1005
- if (change.checkpointId === diffCheckpointId) {
1006
- idsWithDiff.add(entityKey);
1007
- return;
1008
- }
1009
- if (change.type === "SET") {
1010
- historySets.push(change.entity);
1011
- historySetCheckpointIds.push(change.checkpointId.toString());
1012
- return;
1013
- }
1014
- historyDeleteIds.push(change.entityId);
1015
- historyDeleteCheckpointIds.push(change.checkpointId.toString());
1016
- });
1017
- let sets = [];
1018
- let deletes = [];
1019
- let backfill = [];
1020
- orderedIds.forEach(entityId => {
1021
- let entityKey = EntityId.toKey(entityId);
1022
- let match = latestChangeById[entityKey];
1023
- if (match.type === "SET") {
1024
- sets.push(match.entity);
1025
- } else {
1026
- deletes.push(match.entityId);
1027
- }
1028
- if (shouldSaveHistory && !idsWithDiff.has(entityKey)) {
1029
- backfill.push(entityId);
1030
- return;
1031
- }
1032
- });
1033
- let rowsOf = entities => {
1034
- if (Utils.$$Array.notEmpty(entities)) {
1035
- return rowsOrThrow(client, table, getRowSchema(entityConfig), entities, staged);
1036
- }
1037
- };
1038
- return {
1039
- sets: rowsOf(sets),
1040
- deletes: renderIds(entityConfig.table, deletes),
1041
- history: shouldSaveHistory ? ({
1042
- backfill: renderIds(entityConfig.table, backfill),
1043
- sets: rowsOf(historySets),
1044
- setCheckpointIds: historySetCheckpointIds,
1045
- deleteIds: renderIds(entityConfig.table, historyDeleteIds),
1046
- deleteCheckpointIds: historyDeleteCheckpointIds
1047
- }) : undefined
1048
- };
1049
- };
1050
- let match$1 = table.clickhouse;
1051
- let tmp;
1052
- if (changes.length !== 0 && match$1 !== undefined) {
1053
- let handle = stagedFor(staged, "ClickHouse", () => ClickHouse.stageChangesOrThrow(StorageClient.arena(client, "ClickHouse"), match$1, entityConfig, scope, changes));
1054
- staged_handles.push(handle);
1055
- tmp = handle;
1056
- } else {
1057
- tmp = undefined;
535
+ changes: Stdlib_Array.filterMap(updatedEntities, update => {
536
+ let match = update.changes;
537
+ if (match.length === 0) {
538
+ return;
539
+ }
540
+ let started = Performance.now();
541
+ let handle;
542
+ try {
543
+ handle = staged(stageChanges(update));
544
+ } catch (raw_exn) {
545
+ let exn = Primitive_exceptions.internalToException(raw_exn);
546
+ throw changesErrorOf(update.entityConfig, exn);
547
+ }
548
+ let seconds = Performance.secondsSince(started);
549
+ if (update.entityConfig.storage.clickhouse) {
550
+ clickHouseStagingSeconds.contents = clickHouseStagingSeconds.contents + seconds;
551
+ }
552
+ if (!update.entityConfig.storage.postgres) {
553
+ clickHouseOnlyStagingSeconds.contents = clickHouseOnlyStagingSeconds.contents + seconds;
1058
554
  }
1059
555
  return {
1060
- table: table.handle,
1061
- chainId: Stdlib_Option.map(chainIdStamp, param => param[1]),
1062
- postgres: Stdlib_Option.map(table.postgres, param => postgresWrite()),
1063
- clickhouse: tmp
556
+ entity: update.entityConfig.index,
557
+ chainId: Internal.chainScopeChainId(update.scope),
558
+ keepsHistory: update.shouldSaveHistory,
559
+ changes: handle
1064
560
  };
1065
561
  }),
1066
562
  chainMeta: Stdlib_Option.mapOr(chainMetaData, [], chainMeta),
1067
563
  addresses: addressColumns(registeredAddresses.map(staged => staged.row)),
1068
564
  frontier: {
1069
- chainIds: match[0],
1070
- checkpointIds: match[1]
565
+ chainIds: chainIds,
566
+ checkpointIds: frontierCheckpointIds
1071
567
  },
1072
568
  checkpoints: {
1073
- ids: batch.checkpointIds.map(id => id.toString()),
569
+ ids: batch.checkpointIds,
1074
570
  chainIds: batch.checkpointChainIds,
1075
571
  blockNumbers: batch.checkpointBlockNumbers,
1076
572
  blockHashes: batch.checkpointBlockHashes,
@@ -1078,35 +574,59 @@ function make(client, logger, entitiesOpt, cacheDir, ecosystem, isolatedOpt, onI
1078
574
  keepsHistory: batch.checkpointChainIds.map(chainId => HistoryPolicy.forChain(batch.history, chainId))
1079
575
  },
1080
576
  effectCaches: updatedEffectsCache.map(param => {
1081
- let registered = ownTable(param.table, Internal.cacheItemSchema, undefined);
577
+ let itemSchema = param.itemSchema;
578
+ let table = param.table;
579
+ let tmp;
580
+ try {
581
+ tmp = staged(stageRows("EffectCache", effectCacheWriters, param.items.map(item => S$RescriptSchema.reverseConvertOrThrow(item, itemSchema))));
582
+ } catch (raw_exn) {
583
+ let exn = Primitive_exceptions.internalToException(raw_exn);
584
+ if (exn.RE_EXN_ID === S$RescriptSchema.Raised) {
585
+ throw {
586
+ RE_EXN_ID: Persistence.StorageError,
587
+ message: `Failed to convert items for table "` + table.tableName + `"`,
588
+ reason: exn,
589
+ Error: new Error()
590
+ };
591
+ }
592
+ throw {
593
+ RE_EXN_ID: Persistence.StorageError,
594
+ message: `Failed to insert items into table "` + table.tableName + `"`,
595
+ reason: Utils.prettifyExn(exn),
596
+ Error: new Error()
597
+ };
598
+ }
1082
599
  return {
1083
- table: registered.handle,
600
+ table: table.tableName,
1084
601
  create: param.shouldInitialize,
1085
- rows: rowsOrThrow(client, registered, param.itemSchema, param.items, staged)
602
+ rows: tmp
1086
603
  };
1087
604
  })
1088
605
  };
1089
- } catch (exn) {
1090
- client.discardStaged(staged_handles);
1091
- throw exn;
606
+ } catch (exn$1) {
607
+ client.discardStaged(handles);
608
+ throw exn$1;
1092
609
  }
610
+ let postgresStagingSeconds = Performance.secondsSince(stagingStarted) - clickHouseOnlyStagingSeconds.contents;
1093
611
  let timings;
1094
612
  try {
1095
613
  timings = await client.writeBatch(input);
1096
- } catch (raw_exn) {
1097
- let exn$1 = Primitive_exceptions.internalToException(raw_exn);
1098
- client.discardStaged(staged_handles);
1099
- throw storageErrorOf(exn$1);
614
+ } catch (raw_exn$1) {
615
+ let exn$2 = Primitive_exceptions.internalToException(raw_exn$1);
616
+ client.discardStaged(handles);
617
+ throw storageErrorOf(exn$2);
1100
618
  }
1101
619
  timings.forEach(param => {
1102
- let name = storeName(param.store);
1103
- onWrite(name, param.seconds + Stdlib_Option.getOr(staged_seconds[name], 0));
620
+ let store = param.store;
621
+ let tmp;
622
+ tmp = store === "Postgres" ? postgresStagingSeconds : clickHouseStagingSeconds.contents;
623
+ onWrite(storeName(store), param.seconds + tmp);
1104
624
  });
1105
625
  };
1106
626
  let close = () => client.close();
1107
627
  return {
1108
628
  name: postgresName,
1109
- entityStoreName: entityConfig => storeName(entityTable(entityConfig).home),
629
+ entityStoreName: entityConfig => Stdlib_Option.mapOr(layouts.entities[entityConfig.index].home, postgresName, storeName),
1110
630
  holding: holding,
1111
631
  initialize: initialize,
1112
632
  readStoredConfig: readStoredConfig,
@@ -1131,13 +651,13 @@ function make(client, logger, entitiesOpt, cacheDir, ecosystem, isolatedOpt, onI
1131
651
 
1132
652
  function clientFromEnv(config, logger, pgSchemaOpt, isHasuraEnabled, maxConnections) {
1133
653
  let pgSchema = pgSchemaOpt !== undefined ? pgSchemaOpt : Env.Db.publicSchema;
1134
- return makeClient(logger, config.chainIdMode, postgresOptions(pgSchema, isHasuraEnabled, maxConnections), config.storage.clickhouse ? ClickHouse.optionsFromEnvOrThrow() : undefined);
654
+ return makeClient(logger, config, postgresOptions(pgSchema, isHasuraEnabled, maxConnections, config.isolated), config.storage.clickhouse ? ClickHouse.optionsFromEnvOrThrow() : undefined);
1135
655
  }
1136
656
 
1137
657
  function makeStorageFromEnv(config, logger, pgSchemaOpt, isHasuraEnabledOpt, maxConnections, cacheDir) {
1138
658
  let pgSchema = pgSchemaOpt !== undefined ? pgSchemaOpt : Env.Db.publicSchema;
1139
659
  let isHasuraEnabled = isHasuraEnabledOpt !== undefined ? isHasuraEnabledOpt : Env.Hasura.enabled;
1140
- return make(clientFromEnv(config, logger, pgSchema, isHasuraEnabled, maxConnections), logger, config.userEntities, cacheDir, config.ecosystem.name, config.isolated, isHasuraEnabled ? () => Stdlib_Promise.$$catch(Hasura.trackDatabase(logger, Env.Hasura.graphqlEndpoint, {
660
+ return make(clientFromEnv(config, logger, pgSchema, isHasuraEnabled, maxConnections), cacheDir, config.ecosystem.name, isHasuraEnabled ? () => Stdlib_Promise.$$catch(Hasura.trackDatabase(logger, Env.Hasura.graphqlEndpoint, {
1141
661
  role: Env.Hasura.role,
1142
662
  secret: Env.Hasura.secret
1143
663
  }, pgSchema, Config.getPgUserEntities(config), Env.Hasura.aggregateEntities, Env.Hasura.responseLimit, Schema.make(config.userEntities.map(e => e.table))), err => Promise.resolve(Logger.errorWithExn(logger, `Error tracking tables`, err))) : undefined);
@@ -1149,36 +669,24 @@ function makePersistenceFromConfig(config, logger, storageOpt) {
1149
669
  }
1150
670
 
1151
671
  export {
1152
- formatSeconds,
1153
- slowOnLargeDatabaseNotice,
1154
672
  storeName,
1155
673
  postgresName,
1156
- logIndexEvent,
1157
674
  postgresOptions,
1158
675
  makeClient,
1159
- partitionTableName,
1160
- indexTableNames,
1161
- getSchemaIndexes,
1162
- pgColumnInput,
1163
- rowSchemaCache,
1164
- getRowSchema,
1165
- registerEntity,
1166
- arenaConverters,
1167
- cellConverters,
1168
- converterOf,
1169
- stagedFor,
1170
- renderIds,
1171
676
  bounds,
1172
677
  progress,
1173
678
  addressColumns,
1174
679
  toChainConfig,
1175
680
  storageErrorOf,
1176
- rollbackRowStateSchema,
1177
681
  rollbackChainIdSchema,
1178
682
  rollbackRemovedIdSchema,
683
+ changeColumns,
684
+ stageOrThrow,
685
+ stageRowsOrThrow,
686
+ stageChangesOrThrow,
1179
687
  make,
1180
688
  clientFromEnv,
1181
689
  makeStorageFromEnv,
1182
690
  makePersistenceFromConfig,
1183
691
  }
1184
- /* rowSchemaCache Not a pure module */
692
+ /* rollbackChainIdSchema Not a pure module */