envio 3.11.0 → 3.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/index.d.ts +3 -0
  2. package/package.json +6 -6
  3. package/src/Batch.res +111 -78
  4. package/src/Batch.res.mjs +53 -46
  5. package/src/BatchProcessing.res +1 -11
  6. package/src/BatchProcessing.res.mjs +1 -4
  7. package/src/ChainFetching.res +7 -3
  8. package/src/ChainFetching.res.mjs +1 -1
  9. package/src/ChainState.res +82 -20
  10. package/src/ChainState.res.mjs +51 -13
  11. package/src/ChainState.resi +6 -1
  12. package/src/Config.res +58 -37
  13. package/src/Config.res.mjs +63 -53
  14. package/src/CrossChainState.res +50 -53
  15. package/src/CrossChainState.res.mjs +25 -23
  16. package/src/CrossChainState.resi +6 -13
  17. package/src/EffectState.res +13 -4
  18. package/src/EffectState.resi +7 -1
  19. package/src/EventProcessing.res +6 -6
  20. package/src/EventProcessing.res.mjs +6 -6
  21. package/src/FetchState.res +65 -46
  22. package/src/FetchState.res.mjs +68 -36
  23. package/src/FinalizeBackfill.res +16 -12
  24. package/src/FinalizeBackfill.res.mjs +3 -3
  25. package/src/HistoryPolicy.res +46 -0
  26. package/src/HistoryPolicy.res.mjs +50 -0
  27. package/src/InMemoryStore.res +30 -32
  28. package/src/InMemoryStore.res.mjs +33 -28
  29. package/src/InMemoryTable.res +155 -67
  30. package/src/InMemoryTable.res.mjs +151 -60
  31. package/src/IndexerState.res +66 -45
  32. package/src/IndexerState.res.mjs +57 -29
  33. package/src/IndexerState.resi +8 -13
  34. package/src/Internal.res +4 -2
  35. package/src/LoadLayer.res +68 -38
  36. package/src/LoadLayer.res.mjs +50 -64
  37. package/src/LoadLayer.resi +1 -1
  38. package/src/Main.res +25 -24
  39. package/src/Main.res.mjs +24 -25
  40. package/src/Metrics.res +49 -1
  41. package/src/Metrics.res.mjs +26 -3
  42. package/src/Persistence.res +55 -16
  43. package/src/Persistence.res.mjs +18 -3
  44. package/src/PgStorage.res +286 -180
  45. package/src/PgStorage.res.mjs +209 -122
  46. package/src/PruneStaleHistory.res +9 -15
  47. package/src/PruneStaleHistory.res.mjs +11 -20
  48. package/src/Rollback.res +9 -14
  49. package/src/Rollback.res.mjs +5 -3
  50. package/src/Sink.res +7 -5
  51. package/src/Sink.res.mjs +4 -4
  52. package/src/Staging.res +298 -0
  53. package/src/Staging.res.mjs +257 -0
  54. package/src/TestIndexer.res +14 -30
  55. package/src/TestIndexer.res.mjs +11 -9
  56. package/src/UserContext.res +20 -36
  57. package/src/UserContext.res.mjs +6 -9
  58. package/src/Utils.res +10 -4
  59. package/src/Utils.res.mjs +16 -16
  60. package/src/Writing.res +34 -20
  61. package/src/Writing.res.mjs +15 -20
  62. package/src/bindings/ClickHouse.res +41 -34
  63. package/src/bindings/ClickHouse.res.mjs +36 -30
  64. package/src/bindings/ClickHouseSink.res +45 -186
  65. package/src/bindings/ClickHouseSink.res.mjs +21 -174
  66. package/src/db/CheckpointSequence.res +109 -0
  67. package/src/db/CheckpointSequence.res.mjs +99 -0
  68. package/src/db/EntityFilter.res +487 -275
  69. package/src/db/EntityFilter.res.mjs +557 -309
  70. package/src/db/EntityHistory.res +6 -5
  71. package/src/db/EntityHistory.res.mjs +5 -5
  72. package/src/db/Frontier.res +86 -0
  73. package/src/db/Frontier.res.mjs +126 -0
  74. package/src/db/InternalTable.res +145 -27
  75. package/src/db/InternalTable.res.mjs +115 -29
  76. package/src/db/RollbackFloors.res +34 -41
  77. package/src/db/RollbackFloors.res.mjs +27 -71
  78. package/src/db/Table.res +21 -6
  79. package/src/db/Table.res.mjs +13 -4
  80. package/src/sources/BlockStore.res +8 -0
  81. package/src/sources/EvmHyperSyncSource.res +5 -1
  82. package/src/sources/EvmHyperSyncSource.res.mjs +4 -3
  83. package/src/sources/FuelHyperSyncSource.res +1 -0
  84. package/src/sources/FuelHyperSyncSource.res.mjs +1 -1
  85. package/src/sources/HyperSync.res +6 -0
  86. package/src/sources/HyperSync.res.mjs +5 -2
  87. package/src/sources/HyperSync.resi +2 -0
  88. package/src/sources/HyperSyncClient.res +6 -0
  89. package/src/sources/HyperSyncSSE.res +1 -1
  90. package/src/sources/HyperSyncSSE.res.mjs +4 -10
  91. package/src/sources/RequestStat.res +8 -1
  92. package/src/sources/RpcSource.res +1 -0
  93. package/src/sources/RpcSource.res.mjs +1 -1
  94. package/src/sources/SimulateSource.res +1 -0
  95. package/src/sources/SimulateSource.res.mjs +1 -1
  96. package/src/sources/Source.res +11 -3
  97. package/src/sources/SourceManager.res +34 -13
  98. package/src/sources/SourceManager.res.mjs +26 -9
  99. package/src/sources/SourceManager.resi +2 -0
  100. package/src/sources/SvmHyperSyncClient.res +5 -0
  101. package/src/sources/SvmHyperSyncSource.res +3 -0
  102. package/src/sources/SvmHyperSyncSource.res.mjs +4 -2
  103. package/src/tui/Tui.res +56 -143
  104. package/src/tui/Tui.res.mjs +70 -166
  105. package/src/tui/components/TuiData.res +61 -16
  106. package/src/tui/components/TuiData.res.mjs +53 -15
  107. package/svm.schema.json +0 -7
  108. package/src/db/CheckpointBounds.res +0 -53
  109. package/src/db/CheckpointBounds.res.mjs +0 -44
@@ -5,13 +5,16 @@ import * as Utils from "../Utils.res.mjs";
5
5
  import * as Config from "../Config.res.mjs";
6
6
  import * as Address from "../Address.res.mjs";
7
7
  import * as ChainId from "../ChainId.res.mjs";
8
+ import * as Frontier from "./Frontier.res.mjs";
9
+ import * as Internal from "../Internal.res.mjs";
8
10
  import * as Postgres from "../bindings/Postgres.res.mjs";
9
11
  import * as AddressRows from "../AddressRows.res.mjs";
10
12
  import * as Stdlib_Null from "@rescript/runtime/lib/es6/Stdlib_Null.js";
11
13
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
12
14
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
13
- import * as CheckpointBounds from "./CheckpointBounds.res.mjs";
15
+ import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
14
16
  import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
17
+ import * as CheckpointSequence from "./CheckpointSequence.res.mjs";
15
18
  import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
16
19
 
17
20
  let undefinedTableSqlState = "42P01";
@@ -158,9 +161,11 @@ let fields = [
158
161
  "first_event_block",
159
162
  "buffer_block",
160
163
  "progress_block",
164
+ "progress_block_time",
161
165
  "ready_at",
162
166
  "events_processed",
163
- "_is_hyper_sync"
167
+ "_is_hyper_sync",
168
+ "checkpoint_id"
164
169
  ];
165
170
 
166
171
  let table$2 = Table.mkTable("envio_chains", undefined, [
@@ -175,7 +180,9 @@ let table$2 = Table.mkTable("envio_chains", undefined, [
175
180
  Table.mkField("ready_at", "Date", S$RescriptSchema.$$null(Utils.Schema.dbDate), undefined, undefined, true, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
176
181
  Table.mkField("events_processed", "UInt52", S$RescriptSchema.float, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
177
182
  Table.mkField("_is_hyper_sync", "Boolean", S$RescriptSchema.bool, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
178
- Table.mkField("progress_block", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined)
183
+ Table.mkField("progress_block", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
184
+ Table.mkField("progress_block_time", "Date", S$RescriptSchema.$$null(Utils.Schema.dbDate), undefined, undefined, true, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
185
+ Table.mkField("checkpoint_id", "UInt64", S$RescriptSchema.bigint, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined)
179
186
  ], undefined);
180
187
 
181
188
  function initialFromConfig(chainConfig) {
@@ -187,7 +194,9 @@ function initialFromConfig(chainConfig) {
187
194
  max_reorg_depth: chainConfig.maxReorgDepth,
188
195
  source_block: 0,
189
196
  progress_block: -1,
197
+ progress_block_time: null,
190
198
  events_processed: 0,
199
+ checkpoint_id: Internal.initialCheckpointId,
191
200
  first_event_block: null,
192
201
  buffer_block: -1,
193
202
  ready_at: null,
@@ -260,7 +269,9 @@ function makeGetInitialStateQuery(pgSchema) {
260
269
  "` + "ready_at" + `" as "timestampCaughtUpToHeadOrEndblock",
261
270
  "` + "events_processed" + `"::float8 as "numEventsProcessed",
262
271
  "` + "progress_block" + `" as "progressBlockNumber",
263
- "` + "source_block" + `" as "sourceBlockNumber"
272
+ "` + "progress_block_time" + `" as "progressBlockTime",
273
+ "` + "source_block" + `" as "sourceBlockNumber",
274
+ "` + "checkpoint_id" + `"::TEXT as "checkpointId"
264
275
  FROM "` + pgSchema + `"."` + table$2.tableName + `";`;
265
276
  }
266
277
 
@@ -281,14 +292,25 @@ async function getInitialState(sql, pgSchema) {
281
292
  timestampCaughtUpToHeadOrEndblock: rawInitialState.timestampCaughtUpToHeadOrEndblock,
282
293
  numEventsProcessed: rawInitialState.numEventsProcessed,
283
294
  progressBlockNumber: rawInitialState.progressBlockNumber,
295
+ progressBlockTime: rawInitialState.progressBlockTime,
284
296
  addressRows: Stdlib_Option.getOr(addressRowsByChainId[ChainId.toString(id)], AddressRows.emptySeedRows()),
285
- sourceBlockNumber: rawInitialState.sourceBlockNumber
297
+ sourceBlockNumber: rawInitialState.sourceBlockNumber,
298
+ checkpointId: rawInitialState.checkpointId
286
299
  };
287
300
  });
288
301
  }
289
302
 
303
+ function blockTimeToDb(blockTime) {
304
+ return Stdlib_Null.fromOption(Stdlib_Option.map(blockTime, seconds => new Date(seconds * 1000)));
305
+ }
306
+
307
+ function blockTimeFromDb(date) {
308
+ return Stdlib_Option.map(date === null ? undefined : Primitive_option.some(date), date => date.getTime() / 1000 | 0);
309
+ }
310
+
290
311
  let progressFields = [
291
312
  "progress_block",
313
+ "progress_block_time",
292
314
  "events_processed",
293
315
  "source_block"
294
316
  ];
@@ -325,8 +347,10 @@ function setProgressedChains(sql, pgSchema, progressedChains) {
325
347
  let params = [];
326
348
  params.push(data.chainId);
327
349
  progressFields.forEach(field => {
328
- params.push(field === "source_block" ? data.sourceBlockNumber : (
329
- field === "progress_block" ? data.progressBlockNumber : data.totalEventsProcessed
350
+ params.push(field === "progress_block_time" ? blockTimeToDb(data.progressBlockTime) : (
351
+ field === "source_block" ? data.sourceBlockNumber : (
352
+ field === "progress_block" ? data.progressBlockNumber : data.totalEventsProcessed
353
+ )
330
354
  ));
331
355
  });
332
356
  promises.push(sql.unsafe(query, params, {prepare: true}));
@@ -334,6 +358,20 @@ function setProgressedChains(sql, pgSchema, progressedChains) {
334
358
  return Promise.all(promises);
335
359
  }
336
360
 
361
+ function makeSetCheckpointFrontierQuery(pgSchema, chainIdModeOpt) {
362
+ let chainIdMode = chainIdModeOpt !== undefined ? chainIdModeOpt : "int32";
363
+ let chainIdArrayType$1 = chainIdArrayType(pgSchema, chainIdMode);
364
+ return `UPDATE "` + pgSchema + `"."` + table$2.tableName + `"
365
+ SET "` + "checkpoint_id" + `" = envio_frontier.checkpoint_id
366
+ FROM unnest($1::` + chainIdArrayType$1 + `,$2::` + "BIGINT" + `[]) AS envio_frontier(chain_id, checkpoint_id)
367
+ WHERE "` + table$2.tableName + `"."` + "id" + `" = envio_frontier.chain_id;`;
368
+ }
369
+
370
+ function setCheckpointFrontier(sql, pgSchema, frontier, chainIdModeOpt) {
371
+ let chainIdMode = chainIdModeOpt !== undefined ? chainIdModeOpt : "int32";
372
+ return sql.unsafe(makeSetCheckpointFrontierQuery(pgSchema, chainIdMode), Frontier.unnestParams(frontier), {prepare: true});
373
+ }
374
+
337
375
  let Chains = {
338
376
  fields: fields,
339
377
  table: table$2,
@@ -344,10 +382,14 @@ let Chains = {
344
382
  makeSetReadyAtQuery: makeSetReadyAtQuery,
345
383
  makeGetInitialStateQuery: makeGetInitialStateQuery,
346
384
  getInitialState: getInitialState,
385
+ blockTimeToDb: blockTimeToDb,
386
+ blockTimeFromDb: blockTimeFromDb,
347
387
  progressFields: progressFields,
348
388
  makeProgressFieldsUpdateQuery: makeProgressFieldsUpdateQuery,
349
389
  setMeta: setMeta,
350
- setProgressedChains: setProgressedChains
390
+ setProgressedChains: setProgressedChains,
391
+ makeSetCheckpointFrontierQuery: makeSetCheckpointFrontierQuery,
392
+ setCheckpointFrontier: setCheckpointFrontier
351
393
  };
352
394
 
353
395
  let table$3 = Table.mkTable("envio_info", undefined, [
@@ -393,33 +435,78 @@ let dbSchema = S$RescriptSchema.object(s => ({
393
435
 
394
436
  let columns = [
395
437
  {
396
- field: Table.mkField("id", "UInt64", S$RescriptSchema.bigint, undefined, undefined, undefined, true, undefined, undefined, undefined, undefined, undefined, undefined),
397
- clickHouseFieldType: "UInt64",
398
- valuesOf: batch => batch.checkpointIds
438
+ field: Table.mkField("chain_id", "ChainId", ChainId.schema, undefined, undefined, undefined, true, undefined, undefined, undefined, undefined, undefined, undefined),
439
+ clickHouseFieldType: "ChainId",
440
+ valuesOf: batch => batch.checkpointChainIds,
441
+ diffValuesOf: diffs => diffs.map(diff => diff.chainId)
399
442
  },
400
443
  {
401
- field: Table.mkField("chain_id", "ChainId", ChainId.schema, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
402
- clickHouseFieldType: "ChainId",
403
- valuesOf: batch => batch.checkpointChainIds
444
+ field: Table.mkField("id", "UInt64", S$RescriptSchema.bigint, undefined, undefined, undefined, true, undefined, undefined, undefined, undefined, undefined, undefined),
445
+ clickHouseFieldType: "UInt64",
446
+ valuesOf: batch => batch.checkpointIds,
447
+ diffValuesOf: diffs => diffs.map(diff => diff.checkpointId)
404
448
  },
405
449
  {
406
450
  field: Table.mkField("block_number", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
407
451
  clickHouseFieldType: "Int32",
408
- valuesOf: batch => batch.checkpointBlockNumbers
452
+ valuesOf: batch => batch.checkpointBlockNumbers,
453
+ diffValuesOf: diffs => diffs.map(diff => diff.blockNumber)
409
454
  },
410
455
  {
411
456
  field: Table.mkField("block_hash", "String", S$RescriptSchema.$$null(S$RescriptSchema.string), undefined, undefined, true, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
412
457
  clickHouseFieldType: "String",
413
- valuesOf: batch => batch.checkpointBlockHashes
458
+ valuesOf: batch => batch.checkpointBlockHashes,
459
+ diffValuesOf: diffs => diffs.map(param => null)
414
460
  },
415
461
  {
416
462
  field: Table.mkField("events_processed", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
417
463
  clickHouseFieldType: "UInt64",
418
- valuesOf: batch => batch.checkpointEventsProcessed
464
+ valuesOf: batch => batch.checkpointEventsProcessed,
465
+ diffValuesOf: diffs => diffs.map(param => 0)
419
466
  }
420
467
  ];
421
468
 
422
- let table$4 = Table.mkTable("envio_checkpoints", undefined, columns.map(param => param.field), undefined);
469
+ let tableName = "envio_checkpoints";
470
+
471
+ let table$4 = Table.mkTable(tableName, undefined, columns.map(param => param.field), undefined);
472
+
473
+ let globalTable = Table.mkTable(tableName, undefined, columns.map(param => {
474
+ let column = param.field;
475
+ if (column.TAG !== "Field") {
476
+ return column;
477
+ }
478
+ let f = column._0;
479
+ if (f.fieldName === "chain_id") {
480
+ return {
481
+ TAG: "Field",
482
+ _0: {
483
+ fieldName: f.fieldName,
484
+ fieldType: f.fieldType,
485
+ fieldSchema: f.fieldSchema,
486
+ isArray: f.isArray,
487
+ isNullable: f.isNullable,
488
+ isPrimaryKey: false,
489
+ isIndex: f.isIndex,
490
+ isChainId: f.isChainId,
491
+ linkedEntity: f.linkedEntity,
492
+ defaultValue: f.defaultValue,
493
+ description: f.description,
494
+ postgresDbName: f.postgresDbName,
495
+ clickhouseDbName: f.clickhouseDbName
496
+ }
497
+ };
498
+ } else {
499
+ return column;
500
+ }
501
+ }), undefined);
502
+
503
+ function tableFor(sequence) {
504
+ if (sequence === "SharedAcrossChains") {
505
+ return globalTable;
506
+ } else {
507
+ return table$4;
508
+ }
509
+ }
423
510
 
424
511
  function makeGetReorgCheckpointsQuery(pgSchema) {
425
512
  return `WITH reorg_chains AS (
@@ -443,10 +530,6 @@ WHERE cp."` + "block_hash" + `" IS NOT NULL
443
530
  ORDER BY cp."` + "id" + `";`;
444
531
  }
445
532
 
446
- function makeCommitedCheckpointIdQuery(pgSchema) {
447
- return `SELECT COALESCE(MAX(` + "id" + `), ` + (0n).toString() + `) AS id FROM "` + pgSchema + `"."` + table$4.tableName + `";`;
448
- }
449
-
450
533
  function makeInsertCheckpointQuery(pgSchema, chainIdModeOpt) {
451
534
  let chainIdMode = chainIdModeOpt !== undefined ? chainIdModeOpt : "int32";
452
535
  let chainIdArrayType$1 = chainIdArrayType(pgSchema, chainIdMode);
@@ -471,18 +554,18 @@ let chainIdColumn = "chain_id";
471
554
 
472
555
  function rollback(sql, pgSchema, floors) {
473
556
  let tableRef = `"` + pgSchema + `"."` + table$4.tableName + `"`;
474
- let bounds = CheckpointBounds.sql(floors.floors, chainIdColumn, tableRef);
475
- return sql.unsafe(`DELETE FROM ` + tableRef + bounds.using + ` WHERE "` + "id" + `" > ` + bounds.checkpointId + bounds.usingMatch + `;`, CheckpointBounds.params(floors.floors), {prepare: true});
557
+ let bounds = CheckpointSequence.sql(floors.checkpointBounds, chainIdColumn, tableRef);
558
+ return sql.unsafe(`DELETE FROM ` + tableRef + bounds.using + ` WHERE "` + "id" + `" > ` + bounds.checkpointId + bounds.usingMatch + `;`, CheckpointSequence.params(floors.checkpointBounds), {prepare: true});
476
559
  }
477
560
 
478
561
  function makePruneStaleCheckpointsQuery(pgSchema, safeCheckpoints) {
479
562
  let tableRef = `"` + pgSchema + `"."` + table$4.tableName + `"`;
480
- let bounds = CheckpointBounds.sql(safeCheckpoints, chainIdColumn, tableRef);
563
+ let bounds = CheckpointSequence.sql(safeCheckpoints, chainIdColumn, tableRef);
481
564
  return `DELETE FROM ` + tableRef + bounds.using + ` WHERE "` + "id" + `" < ` + bounds.checkpointId + bounds.usingMatch + `;`;
482
565
  }
483
566
 
484
567
  function pruneStaleCheckpoints(sql, pgSchema, safeCheckpoints) {
485
- return sql.unsafe(makePruneStaleCheckpointsQuery(pgSchema, safeCheckpoints), CheckpointBounds.params(safeCheckpoints), {prepare: true});
568
+ return sql.unsafe(makePruneStaleCheckpointsQuery(pgSchema, safeCheckpoints), CheckpointSequence.params(safeCheckpoints), {prepare: true});
486
569
  }
487
570
 
488
571
  function makeGetRollbackTargetCheckpointQuery(pgSchema) {
@@ -503,7 +586,7 @@ function getRollbackTargetCheckpoint(sql, pgSchema, reorgChainId, lastKnownValid
503
586
  }
504
587
 
505
588
  function makeGetRollbackProgressDiffQuery(pgSchema, floors) {
506
- let bounds = CheckpointBounds.sql(floors.floors, chainIdColumn, "t");
589
+ let bounds = CheckpointSequence.sql(floors.checkpointBounds, chainIdColumn, "t");
507
590
  return `SELECT
508
591
  t."` + "chain_id" + `"::float8 as "` + "chain_id" + `",
509
592
  SUM(t."` + "events_processed" + `") as events_processed_diff,
@@ -514,16 +597,18 @@ GROUP BY t."` + "chain_id" + `";`;
514
597
  }
515
598
 
516
599
  function getRollbackProgressDiff(sql, pgSchema, floors) {
517
- return sql.unsafe(makeGetRollbackProgressDiffQuery(pgSchema, floors), CheckpointBounds.params(floors.floors), {prepare: true});
600
+ return sql.unsafe(makeGetRollbackProgressDiffQuery(pgSchema, floors), CheckpointSequence.params(floors.checkpointBounds), {prepare: true});
518
601
  }
519
602
 
520
603
  let Checkpoints = {
521
604
  dbSchema: dbSchema,
522
605
  initialCheckpointId: 0n,
523
606
  columns: columns,
607
+ tableName: tableName,
524
608
  table: table$4,
609
+ globalTable: globalTable,
610
+ tableFor: tableFor,
525
611
  makeGetReorgCheckpointsQuery: makeGetReorgCheckpointsQuery,
526
- makeCommitedCheckpointIdQuery: makeCommitedCheckpointIdQuery,
527
612
  makeInsertCheckpointQuery: makeInsertCheckpointQuery,
528
613
  insert: insert$2,
529
614
  chainIdColumn: chainIdColumn,
@@ -584,6 +669,7 @@ SELECT
584
669
  "` + "start_block" + `" AS "startBlock",
585
670
  "` + "end_block" + `" AS "endBlock",
586
671
  "` + "progress_block" + `" AS "progressBlock",
672
+ "` + "progress_block_time" + `" AS "progressBlockTime",
587
673
  "` + "buffer_block" + `" AS "bufferBlock",
588
674
  "` + "first_event_block" + `" AS "firstEventBlock",
589
675
  "` + "events_processed" + `"::float4 AS "eventsProcessed",
@@ -1,55 +1,48 @@
1
- // How far back a reorg rollback takes each chain: the last checkpoint each keeps,
2
- // and for a chain whose own reorg set its floor, the last block that reorg left
3
- // valid. Superseding an unwritten rollback with a new one is a pointwise
4
- // minimum, so a merge can neither lose a chain's floor nor raise it.
1
+ // How far back a reorg rollback takes each chain: the last checkpoint each
2
+ // keeps, and for a chain whose own reorg set its floor, the last block that
3
+ // reorg left valid. Superseding an unwritten rollback with a new one is a
4
+ // pointwise minimum, so a merge can neither lose a chain's floor nor raise it.
5
5
  type t = {
6
- floors: CheckpointBounds.t,
6
+ checkpointBounds: CheckpointSequence.checkpointBoundsByChain,
7
7
  // Progress recomputed from the surviving checkpoints alone can land above the
8
8
  // fork: the blocks between it and the chain's next checkpoint carried no
9
9
  // events on the orphaned chain, but the chain replacing it can have its own.
10
10
  // Absent for a chain dragged along by another chain's reorg.
11
- forkBlockNumberByChain: array<(ChainId.t, int)>,
11
+ forkBlockNumberByChain: dict<int>,
12
12
  }
13
13
 
14
- // The rollback of a schema whose chains can't have written each other's rows.
15
- let isolated = (~chainId, ~floorCheckpointId, ~forkBlockNumber) => {
16
- floors: PerChain([(chainId, floorCheckpointId)]),
17
- forkBlockNumberByChain: [(chainId, forkBlockNumber)],
18
- }
19
-
20
- // The rollback of a schema with a cross-chain entity: every chain goes back to
21
- // the same checkpoint.
22
- let global = (~floorCheckpointId, ~reorgChainId, ~forkBlockNumber) => {
23
- floors: EveryChain(floorCheckpointId),
24
- forkBlockNumberByChain: [(reorgChainId, forkBlockNumber)],
25
- }
26
-
27
- let mergeByChain = (pending: array<(ChainId.t, 'a)>, next: array<(ChainId.t, 'a)>) => {
28
- let merged = pending->Array.copy
29
- next->Array.forEach(((chainId, value)) =>
30
- switch merged->Array.findIndexOpt(((mergedChainId, _)) => mergedChainId === chainId) {
31
- | Some(idx) =>
32
- let (_, pendingValue) = merged->Array.getUnsafe(idx)
33
- merged->Array.setUnsafe(idx, (chainId, Pervasives.min(pendingValue, value)))
34
- | None => merged->Array.push((chainId, value))
35
- }
36
- )
37
- merged
14
+ // Under one shared sequence a reorg on any chain can have changed any chain's
15
+ // rows, so every chain goes back to the same checkpoint; with a counter per
16
+ // chain the floor names the reorg chain alone and every sibling is left alone.
17
+ let make = (
18
+ ~sequence: CheckpointSequence.t,
19
+ ~chainIds: array<ChainId.t>,
20
+ ~reorgChainId,
21
+ ~floorCheckpointId,
22
+ ~forkBlockNumber,
23
+ ) => {
24
+ checkpointBounds: {
25
+ CheckpointSequence.sequence,
26
+ byChain: switch sequence {
27
+ | SharedAcrossChains =>
28
+ chainIds->Array.map(chainId => (chainId, floorCheckpointId))->Frontier.fromEntries
29
+ | PerChain => Frontier.fromEntries([(reorgChainId, floorCheckpointId)])
30
+ },
31
+ },
32
+ forkBlockNumberByChain: Dict.fromArray([(reorgChainId->ChainId.toString, forkBlockNumber)]),
38
33
  }
39
34
 
40
35
  let merge = (pending: t, next: t) => {
41
- floors: switch (pending.floors, next.floors) {
42
- | (EveryChain(pending), EveryChain(next)) => EveryChain(Pervasives.min(pending, next))
43
- | (PerChain(pending), PerChain(next)) => PerChain(mergeByChain(pending, next))
44
- | (EveryChain(_), PerChain(_)) | (PerChain(_), EveryChain(_)) =>
45
- JsError.throwWithMessage(
46
- "Internal error: a rollback reaching every chain can't be merged with one isolated to a chain. The schema decides which a run builds, and it doesn't change.",
47
- )
36
+ checkpointBounds: {
37
+ CheckpointSequence.sequence: next.checkpointBounds.sequence,
38
+ byChain: Frontier.mergeMin(pending.checkpointBounds.byChain, next.checkpointBounds.byChain),
48
39
  },
49
- forkBlockNumberByChain: mergeByChain(pending.forkBlockNumberByChain, next.forkBlockNumberByChain),
40
+ forkBlockNumberByChain: Utils.Dict.mergeWith(
41
+ pending.forkBlockNumberByChain,
42
+ next.forkBlockNumberByChain,
43
+ Pervasives.min,
44
+ ),
50
45
  }
51
46
 
52
47
  let forkBlockNumber = (floors: t, chainId) =>
53
- floors.forkBlockNumberByChain
54
- ->Array.find(((forkChainId, _)) => forkChainId === chainId)
55
- ->Option.map(((_, forkBlockNumber)) => forkBlockNumber)
48
+ floors.forkBlockNumberByChain->ChainId.Dict.dangerouslyGetNonOption(chainId)
@@ -1,92 +1,48 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
- import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
4
- import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
5
- import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
6
- import * as Primitive_bigint from "@rescript/runtime/lib/es6/Primitive_bigint.js";
7
- import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
3
+ import * as Utils from "../Utils.res.mjs";
4
+ import * as ChainId from "../ChainId.res.mjs";
5
+ import * as Frontier from "./Frontier.res.mjs";
6
+ import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
8
7
 
9
- function isolated(chainId, floorCheckpointId, forkBlockNumber) {
10
- return {
11
- floors: {
12
- TAG: "PerChain",
13
- _0: [[
14
- chainId,
15
- floorCheckpointId
16
- ]]
17
- },
18
- forkBlockNumberByChain: [[
19
- chainId,
20
- forkBlockNumber
21
- ]]
22
- };
23
- }
24
-
25
- function global(floorCheckpointId, reorgChainId, forkBlockNumber) {
8
+ function make(sequence, chainIds, reorgChainId, floorCheckpointId, forkBlockNumber) {
9
+ let tmp;
10
+ tmp = sequence === "SharedAcrossChains" ? Frontier.fromEntries(chainIds.map(chainId => [
11
+ chainId,
12
+ floorCheckpointId
13
+ ])) : Frontier.fromEntries([[
14
+ reorgChainId,
15
+ floorCheckpointId
16
+ ]]);
26
17
  return {
27
- floors: {
28
- TAG: "EveryChain",
29
- _0: floorCheckpointId
18
+ checkpointBounds: {
19
+ sequence: sequence,
20
+ byChain: tmp
30
21
  },
31
- forkBlockNumberByChain: [[
32
- reorgChainId,
22
+ forkBlockNumberByChain: Object.fromEntries([[
23
+ ChainId.toString(reorgChainId),
33
24
  forkBlockNumber
34
- ]]
25
+ ]])
35
26
  };
36
27
  }
37
28
 
38
- function mergeByChain(pending, next) {
39
- let merged = pending.slice();
40
- next.forEach(param => {
41
- let value = param[1];
42
- let chainId = param[0];
43
- let idx = Stdlib_Array.findIndexOpt(merged, param => param[0] === chainId);
44
- if (idx !== undefined) {
45
- let match = merged[idx];
46
- merged[idx] = [
47
- chainId,
48
- Primitive_object.min(match[1], value)
49
- ];
50
- return;
51
- }
52
- merged.push([
53
- chainId,
54
- value
55
- ]);
56
- });
57
- return merged;
58
- }
59
-
60
29
  function merge(pending, next) {
61
- let match = pending.floors;
62
- let match$1 = next.floors;
63
- let tmp;
64
- tmp = match.TAG === "EveryChain" ? (
65
- match$1.TAG === "EveryChain" ? ({
66
- TAG: "EveryChain",
67
- _0: Primitive_bigint.min(match._0, match$1._0)
68
- }) : Stdlib_JsError.throwWithMessage("Internal error: a rollback reaching every chain can't be merged with one isolated to a chain. The schema decides which a run builds, and it doesn't change.")
69
- ) : (
70
- match$1.TAG === "EveryChain" ? Stdlib_JsError.throwWithMessage("Internal error: a rollback reaching every chain can't be merged with one isolated to a chain. The schema decides which a run builds, and it doesn't change.") : ({
71
- TAG: "PerChain",
72
- _0: mergeByChain(match._0, match$1._0)
73
- })
74
- );
75
30
  return {
76
- floors: tmp,
77
- forkBlockNumberByChain: mergeByChain(pending.forkBlockNumberByChain, next.forkBlockNumberByChain)
31
+ checkpointBounds: {
32
+ sequence: next.checkpointBounds.sequence,
33
+ byChain: Frontier.mergeMin(pending.checkpointBounds.byChain, next.checkpointBounds.byChain)
34
+ },
35
+ forkBlockNumberByChain: Utils.Dict.mergeWith(pending.forkBlockNumberByChain, next.forkBlockNumberByChain, Primitive_int.min)
78
36
  };
79
37
  }
80
38
 
81
39
  function forkBlockNumber(floors, chainId) {
82
- return Stdlib_Option.map(floors.forkBlockNumberByChain.find(param => param[0] === chainId), param => param[1]);
40
+ return floors.forkBlockNumberByChain[chainId];
83
41
  }
84
42
 
85
43
  export {
86
- isolated,
87
- global,
88
- mergeByChain,
44
+ make,
89
45
  merge,
90
46
  forkBlockNumber,
91
47
  }
92
- /* No side effect */
48
+ /* Utils Not a pure module */
package/src/db/Table.res CHANGED
@@ -331,18 +331,31 @@ let encodeIdsToJson = (table, ids: array<EntityId.t>): JSON.t =>
331
331
 
332
332
  // TODO: Test whether it should be passed via args and match the column type
333
333
 
334
- let getFieldByApiName = (table, apiFieldName) =>
335
- table.fields->Array.find(field =>
336
- switch field {
337
- | Field(f) => f->getApiFieldName
338
- | DerivedFrom({fieldName}) => fieldName
339
- } === apiFieldName
334
+ // Resolved once per table, because a getWhere looks up every field it filters
335
+ // on and scanning the array made that cost grow with the entity's width.
336
+ let fieldsByApiName: table => dict<fieldOrDerived> = Utils.WeakMap.memoize(table => {
337
+ let byApiName = Dict.make()
338
+ table.fields->Array.forEach(field =>
339
+ byApiName->Dict.set(
340
+ switch field {
341
+ | Field(f) => f->getApiFieldName
342
+ | DerivedFrom({fieldName}) => fieldName
343
+ },
344
+ field,
345
+ )
340
346
  )
347
+ byApiName
348
+ })
349
+
350
+ let getFieldByApiName = (table, apiFieldName) =>
351
+ table->fieldsByApiName->Utils.Dict.dangerouslyGetNonOption(apiFieldName)
341
352
 
342
353
  // Both schema instances are created once per field: rescript-schema compiles
343
354
  // and caches operations on the schema instance, so building S.array(fieldSchema)
344
355
  // per query would recompile the serializer on every call.
345
356
  type queryField = {
357
+ fieldType: fieldType,
358
+ isArray: bool,
346
359
  fieldSchema: S.t<unknown>,
347
360
  // Serializes the values array of an "in" filter
348
361
  arrayFieldSchema: S.t<unknown>,
@@ -363,6 +376,8 @@ let queryFields: table => dict<queryField> = Utils.WeakMap.memoize(table => {
363
376
  dict->Dict.set(
364
377
  field->getApiFieldName,
365
378
  {
379
+ fieldType: field.fieldType,
380
+ isArray: field.isArray,
366
381
  fieldSchema: field.fieldSchema,
367
382
  arrayFieldSchema: switch field.fieldType {
368
383
  | Bytea => Utils.Schema.bytesArray->S.toUnknown
@@ -296,12 +296,18 @@ function encodeIdsToJson(table, ids) {
296
296
  return S$RescriptSchema.reverseConvertToJsonOrThrow(ids, idsArraySchema(table));
297
297
  }
298
298
 
299
- function getFieldByApiName(table, apiFieldName) {
300
- return table.fields.find(field => {
299
+ let fieldsByApiName = Utils.$$WeakMap.memoize(table => {
300
+ let byApiName = {};
301
+ table.fields.forEach(field => {
301
302
  let tmp;
302
303
  tmp = field.TAG === "Field" ? getApiFieldName(field._0) : field._0.fieldName;
303
- return tmp === apiFieldName;
304
+ byApiName[tmp] = field;
304
305
  });
306
+ return byApiName;
307
+ });
308
+
309
+ function getFieldByApiName(table, apiFieldName) {
310
+ return fieldsByApiName(table)[apiFieldName];
305
311
  }
306
312
 
307
313
  let queryFields = Utils.$$WeakMap.memoize(table => {
@@ -315,6 +321,8 @@ let queryFields = Utils.$$WeakMap.memoize(table => {
315
321
  let tmp;
316
322
  tmp = typeof match !== "object" && match === "Bytea" ? Utils.Schema.bytesArray : S$RescriptSchema.array(field$1.fieldSchema);
317
323
  dict[getApiFieldName(field$1)] = {
324
+ fieldType: field$1.fieldType,
325
+ isArray: field$1.isArray,
318
326
  fieldSchema: field$1.fieldSchema,
319
327
  arrayFieldSchema: tmp,
320
328
  pgDbFieldName: getPgDbFieldName(field$1),
@@ -420,7 +428,7 @@ function toSqlParams(table, schema, pgSchema, chainIdModeOpt) {
420
428
  }
421
429
  }
422
430
  };
423
- let field = getFieldByApiName(table, location);
431
+ let field = fieldsByApiName(table)[location];
424
432
  let field$1;
425
433
  if (field !== undefined) {
426
434
  field$1 = field;
@@ -533,6 +541,7 @@ export {
533
541
  getIdSchema,
534
542
  idsArraySchema,
535
543
  encodeIdsToJson,
544
+ fieldsByApiName,
536
545
  getFieldByApiName,
537
546
  queryFields,
538
547
  makeRowsSchema,
@@ -142,6 +142,14 @@ external materialize: (
142
142
  // Hash of a stored block, if the store still holds it.
143
143
  @send external getHash: (t, int) => Null.t<string> = "getHash"
144
144
 
145
+ // Unix timestamp of a stored block - exactly that block, except for an SVM slot
146
+ // that produced none: with `allowSkippedSlot` the last real slot below it
147
+ // answers instead, which is what chain time is at a skipped slot. Only set it
148
+ // where the query covered every slot in its range, or a slot that was never
149
+ // asked about reads as one the chain skipped.
150
+ @send
151
+ external getTimestamp: (t, int, ~allowSkippedSlot: bool) => Null.t<int> = "getTimestamp"
152
+
145
153
  // Block numbers in `[fromBlock, belowBlock)` with a stored hash, ascending.
146
154
  @send
147
155
  external getHashedBlockNumbers: (t, ~fromBlock: int, ~belowBlock: int) => array<int> =
@@ -84,6 +84,7 @@ let make = (
84
84
  ~fromBlock,
85
85
  ~toBlock,
86
86
  ~addressSet,
87
+ ~includeAllBlocks,
87
88
  ~knownHeight,
88
89
  ~partitionId as _,
89
90
  ~selection: FetchState.selection,
@@ -104,6 +105,7 @@ let make = (
104
105
  ~registrationIndexes=selection.onEventRegistrations->Array.map(reg => reg.index),
105
106
  ~addressSet,
106
107
  ~clientFilteredContracts=selection.clientFilteredContracts,
108
+ ~includeAllBlocks,
107
109
  ) catch {
108
110
  | HyperSync.GetLogs.Error(WrongInstance) =>
109
111
  throw(Source.SourceBehindHead({blockNumber: fromBlock, requestStats: []}))
@@ -141,7 +143,9 @@ let make = (
141
143
  }
142
144
 
143
145
  let pageFetchTime = startFetchingBatchTimeRef->Performance.secondsSince
144
- let requestStats = [{Source.method: "getLogs", seconds: pageFetchTime}]
146
+ let requestStats = [
147
+ {Source.method: "getLogs", seconds: pageFetchTime, responseBlocks: pageUnsafe.responseBlocks},
148
+ ]
145
149
 
146
150
  //set height and next from block
147
151
  let knownHeight = pageUnsafe.archiveHeight