envio 3.11.0 → 3.12.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 (89) 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/ChainState.res +72 -19
  8. package/src/ChainState.res.mjs +50 -12
  9. package/src/ChainState.resi +5 -0
  10. package/src/Config.res +58 -37
  11. package/src/Config.res.mjs +63 -53
  12. package/src/CrossChainState.res +49 -52
  13. package/src/CrossChainState.res.mjs +25 -23
  14. package/src/CrossChainState.resi +6 -13
  15. package/src/EffectState.res +13 -4
  16. package/src/EffectState.resi +7 -1
  17. package/src/EventProcessing.res +6 -6
  18. package/src/EventProcessing.res.mjs +6 -6
  19. package/src/FetchState.res +14 -0
  20. package/src/FetchState.res.mjs +14 -0
  21. package/src/FinalizeBackfill.res +16 -12
  22. package/src/FinalizeBackfill.res.mjs +3 -3
  23. package/src/HistoryPolicy.res +46 -0
  24. package/src/HistoryPolicy.res.mjs +50 -0
  25. package/src/InMemoryStore.res +30 -32
  26. package/src/InMemoryStore.res.mjs +33 -28
  27. package/src/IndexerState.res +66 -45
  28. package/src/IndexerState.res.mjs +57 -29
  29. package/src/IndexerState.resi +8 -13
  30. package/src/Internal.res +4 -2
  31. package/src/LoadLayer.res +11 -4
  32. package/src/LoadLayer.res.mjs +8 -5
  33. package/src/Main.res +25 -24
  34. package/src/Main.res.mjs +24 -25
  35. package/src/Metrics.res +49 -1
  36. package/src/Metrics.res.mjs +26 -3
  37. package/src/Persistence.res +55 -16
  38. package/src/Persistence.res.mjs +18 -3
  39. package/src/PgStorage.res +194 -118
  40. package/src/PgStorage.res.mjs +132 -72
  41. package/src/PruneStaleHistory.res +9 -15
  42. package/src/PruneStaleHistory.res.mjs +11 -20
  43. package/src/Rollback.res +9 -14
  44. package/src/Rollback.res.mjs +5 -3
  45. package/src/Sink.res +7 -5
  46. package/src/Sink.res.mjs +4 -4
  47. package/src/Staging.res +298 -0
  48. package/src/Staging.res.mjs +257 -0
  49. package/src/TestIndexer.res +5 -5
  50. package/src/TestIndexer.res.mjs +7 -6
  51. package/src/UserContext.res +7 -4
  52. package/src/UserContext.res.mjs +5 -2
  53. package/src/Utils.res +9 -0
  54. package/src/Utils.res.mjs +9 -0
  55. package/src/Writing.res +34 -20
  56. package/src/Writing.res.mjs +15 -20
  57. package/src/bindings/ClickHouse.res +41 -34
  58. package/src/bindings/ClickHouse.res.mjs +36 -30
  59. package/src/bindings/ClickHouseSink.res +45 -186
  60. package/src/bindings/ClickHouseSink.res.mjs +21 -174
  61. package/src/db/CheckpointSequence.res +109 -0
  62. package/src/db/CheckpointSequence.res.mjs +99 -0
  63. package/src/db/EntityHistory.res +6 -5
  64. package/src/db/EntityHistory.res.mjs +5 -5
  65. package/src/db/Frontier.res +86 -0
  66. package/src/db/Frontier.res.mjs +126 -0
  67. package/src/db/InternalTable.res +145 -27
  68. package/src/db/InternalTable.res.mjs +115 -29
  69. package/src/db/RollbackFloors.res +34 -41
  70. package/src/db/RollbackFloors.res.mjs +27 -71
  71. package/src/sources/BlockStore.res +3 -0
  72. package/src/sources/EvmHyperSyncSource.res +3 -1
  73. package/src/sources/EvmHyperSyncSource.res.mjs +2 -1
  74. package/src/sources/HyperSync.res +2 -0
  75. package/src/sources/HyperSync.res.mjs +1 -0
  76. package/src/sources/HyperSync.resi +1 -0
  77. package/src/sources/HyperSyncClient.res +3 -0
  78. package/src/sources/RequestStat.res +8 -1
  79. package/src/sources/Source.res +4 -3
  80. package/src/sources/SourceManager.res +30 -10
  81. package/src/sources/SourceManager.res.mjs +24 -7
  82. package/src/sources/SourceManager.resi +2 -0
  83. package/src/tui/Tui.res +56 -143
  84. package/src/tui/Tui.res.mjs +70 -166
  85. package/src/tui/components/TuiData.res +61 -16
  86. package/src/tui/components/TuiData.res.mjs +53 -15
  87. package/svm.schema.json +0 -7
  88. package/src/db/CheckpointBounds.res +0 -53
  89. package/src/db/CheckpointBounds.res.mjs +0 -44
@@ -170,6 +170,7 @@ FROM "${pgSchema}"."${table.tableName}";`
170
170
  module Chains = {
171
171
  type progressFields = [
172
172
  | #progress_block
173
+ | #progress_block_time
173
174
  | #events_processed
174
175
  | #source_block
175
176
  ]
@@ -186,6 +187,7 @@ module Chains = {
186
187
  | #buffer_block
187
188
  | #ready_at
188
189
  | #_is_hyper_sync
190
+ | #checkpoint_id
189
191
  ]
190
192
 
191
193
  let fields: array<field> = [
@@ -198,19 +200,16 @@ module Chains = {
198
200
  #first_event_block,
199
201
  #buffer_block,
200
202
  #progress_block,
203
+ #progress_block_time,
201
204
  #ready_at,
202
205
  #events_processed,
203
206
  #_is_hyper_sync,
207
+ #checkpoint_id,
204
208
  ]
205
209
 
206
210
  type metaFields = {
207
211
  @as("first_event_block")
208
- firstEventBlockNumber: Null.t<
209
- // Push id first (for WHERE clause)
210
-
211
- // Then push all updateable field values (for SET clause)
212
- int,
213
- >,
212
+ firstEventBlockNumber: Null.t<int>,
214
213
  @as("buffer_block") latestFetchedBlockNumber: int,
215
214
  @as("ready_at")
216
215
  timestampCaughtUpToHeadOrEndblock: Null.t<Date.t>,
@@ -225,7 +224,9 @@ module Chains = {
225
224
  @as("max_reorg_depth") maxReorgDepth: int,
226
225
  @as("source_block") blockHeight: int,
227
226
  @as("progress_block") progressBlockNumber: int,
227
+ @as("progress_block_time") progressBlockTime: Null.t<Date.t>,
228
228
  @as("events_processed") numEventsProcessed: float,
229
+ @as("checkpoint_id") checkpointId: Internal.checkpointId,
229
230
  ...metaFields,
230
231
  }
231
232
 
@@ -265,6 +266,22 @@ module Chains = {
265
266
  mkField((#_is_hyper_sync: field :> string), Boolean, ~fieldSchema=S.bool),
266
267
  // Fully processed block number
267
268
  mkField((#progress_block: field :> string), Int32, ~fieldSchema=S.int),
269
+ // When the block in progress_block was produced, so that `now() -
270
+ // progress_block_time` is how far behind chain time the indexer is. Null
271
+ // while the source hasn't reported that block's header.
272
+ mkField(
273
+ (#progress_block_time: field :> string),
274
+ Date,
275
+ ~fieldSchema=S.null(Utils.Schema.dbDate),
276
+ ~isNullable,
277
+ ),
278
+ // The last checkpoint id the chain has committed. Kept here rather than
279
+ // read off the checkpoints table: checkpoint rows are only written while
280
+ // a rollback could reach them, but an append-only sink holds a row for
281
+ // every id ever handed out and resolves current state through them, so a
282
+ // resume has to continue the sequence even where no checkpoint row backs
283
+ // it.
284
+ mkField((#checkpoint_id: field :> string), UInt64, ~fieldSchema=S.bigint),
268
285
  ],
269
286
  )
270
287
 
@@ -280,8 +297,10 @@ module Chains = {
280
297
  latestFetchedBlockNumber: -1,
281
298
  timestampCaughtUpToHeadOrEndblock: Null.null,
282
299
  progressBlockNumber: -1,
300
+ progressBlockTime: Null.null,
283
301
  isHyperSync: false,
284
302
  numEventsProcessed: 0.,
303
+ checkpointId: Internal.initialCheckpointId,
285
304
  }
286
305
  }
287
306
 
@@ -358,8 +377,11 @@ WHERE "${(#id: field :> string)}" = $2
358
377
  timestampCaughtUpToHeadOrEndblock: Null.t<Date.t>,
359
378
  numEventsProcessed: float,
360
379
  progressBlockNumber: int,
380
+ progressBlockTime: Null.t<Date.t>,
361
381
  addressRows: AddressRows.seedRows,
362
382
  sourceBlockNumber: int,
383
+ // BIGINT, which the driver hands back as a string.
384
+ checkpointId: string,
363
385
  }
364
386
 
365
387
  let makeGetInitialStateQuery = (~pgSchema) => {
@@ -371,7 +393,9 @@ WHERE "${(#id: field :> string)}" = $2
371
393
  "${(#ready_at: field :> string)}" as "timestampCaughtUpToHeadOrEndblock",
372
394
  "${(#events_processed: field :> string)}"::float8 as "numEventsProcessed",
373
395
  "${(#progress_block: field :> string)}" as "progressBlockNumber",
374
- "${(#source_block: field :> string)}" as "sourceBlockNumber"
396
+ "${(#progress_block_time: field :> string)}" as "progressBlockTime",
397
+ "${(#source_block: field :> string)}" as "sourceBlockNumber",
398
+ "${(#checkpoint_id: field :> string)}"::TEXT as "checkpointId"
375
399
  FROM "${pgSchema}"."${table.tableName}";`
376
400
  }
377
401
 
@@ -403,7 +427,20 @@ FROM "${pgSchema}"."${table.tableName}";`
403
427
  })
404
428
  }
405
429
 
406
- let progressFields: array<progressFields> = [#progress_block, #events_processed, #source_block]
430
+ // Block timestamps are unix seconds everywhere inside the indexer - that's
431
+ // what a block header carries - and a timestamp in storage, where `now() -
432
+ // progress_block_time` is the query the column exists for.
433
+ let blockTimeToDb = (blockTime: option<int>) =>
434
+ blockTime->Option.map(seconds => Date.fromTime(seconds->Int.toFloat *. 1000.))->Null.fromOption
435
+ let blockTimeFromDb = (date: Null.t<Date.t>) =>
436
+ date->Null.toOption->Option.map(date => (date->Date.getTime /. 1000.)->Float.toInt)
437
+
438
+ let progressFields: array<progressFields> = [
439
+ #progress_block,
440
+ #progress_block_time,
441
+ #events_processed,
442
+ #source_block,
443
+ ]
407
444
 
408
445
  let makeProgressFieldsUpdateQuery = (~pgSchema) => {
409
446
  let setClauses = Array.mapWithIndex(progressFields, (field, index) => {
@@ -444,6 +481,7 @@ WHERE "id" = $1;`
444
481
  type progressedChain = {
445
482
  chainId: ChainId.t,
446
483
  progressBlockNumber: int,
484
+ progressBlockTime: option<int>,
447
485
  sourceBlockNumber: int,
448
486
  totalEventsProcessed: float,
449
487
  }
@@ -463,6 +501,8 @@ WHERE "id" = $1;`
463
501
  ->Array.push(
464
502
  switch field {
465
503
  | #progress_block => data.progressBlockNumber->(Utils.magic: int => unknown)
504
+ | #progress_block_time =>
505
+ data.progressBlockTime->blockTimeToDb->(Utils.magic: Null.t<Date.t> => unknown)
466
506
  | #events_processed => data.totalEventsProcessed->(Utils.magic: float => unknown)
467
507
  | #source_block => data.sourceBlockNumber->(Utils.magic: int => unknown)
468
508
  },
@@ -475,6 +515,31 @@ WHERE "id" = $1;`
475
515
 
476
516
  Promise.all(promises)->Utils.Promise.ignoreValue
477
517
  }
518
+
519
+ let makeSetCheckpointFrontierQuery = (~pgSchema, ~chainIdMode: ChainId.mode=Int32) => {
520
+ let chainIdArrayType = chainIdArrayType(~pgSchema, ~chainIdMode)
521
+ `UPDATE "${pgSchema}"."${table.tableName}"
522
+ SET "${(#checkpoint_id: field :> string)}" = envio_frontier.checkpoint_id
523
+ FROM unnest($1::${chainIdArrayType},$2::${(BigInt: Postgres.columnType :> string)}[]) AS envio_frontier(chain_id, checkpoint_id)
524
+ WHERE "${table.tableName}"."${(#id: field :> string)}" = envio_frontier.chain_id;`
525
+ }
526
+
527
+ // The chains the write moved, in one statement and in the batch's own
528
+ // transaction — so a chain's stored id can never outlive the rows it covers,
529
+ // nor lag behind them.
530
+ let setCheckpointFrontier = (
531
+ sql,
532
+ ~pgSchema,
533
+ ~frontier: Frontier.t,
534
+ ~chainIdMode: ChainId.mode=Int32,
535
+ ) => {
536
+ sql
537
+ ->Postgres.preparedUnsafe(
538
+ makeSetCheckpointFrontierQuery(~pgSchema, ~chainIdMode),
539
+ frontier->Frontier.unnestParams->(Utils.magic: Frontier.unnestParams => unknown),
540
+ )
541
+ ->Utils.Promise.ignoreValue
542
+ }
478
543
  }
479
544
 
480
545
  module EnvioInfo = {
@@ -556,31 +621,58 @@ module Checkpoints = {
556
621
 
557
622
  let initialCheckpointId = 0n
558
623
 
624
+ // The checkpoint a rollback's diff rows are stamped with. It never reaches
625
+ // Postgres — there the diff is written straight to the entity table — but an
626
+ // append-only sink resolves current state through the checkpoints, so without
627
+ // one of these the diff sits above the frontier while the rows it supersedes
628
+ // sit below it, and the orphaned values are what a reader sees.
629
+ type diffCheckpoint = {
630
+ chainId: ChainId.t,
631
+ checkpointId: Internal.checkpointId,
632
+ // Where the rollback left the chain. At or below its stored progress, so a
633
+ // resume counts the row as covered rather than as something to trim back to.
634
+ blockNumber: int,
635
+ }
636
+
559
637
  // One definition per column, carrying what each storage needs: the field
560
638
  // itself, the type ClickHouse gives it where that differs from Postgres, and
561
- // where a batch keeps the column's values.
639
+ // where a batch — or a rollback diff — keeps the column's values.
562
640
  type column = {
563
641
  field: fieldOrDerived,
564
642
  clickHouseFieldType: fieldType,
565
643
  valuesOf: Batch.t => array<unknown>,
644
+ diffValuesOf: array<diffCheckpoint> => array<unknown>,
566
645
  }
567
646
 
647
+ // The chain leads the key: ids are only unique within a chain, and every
648
+ // query that narrows to one chain reads a contiguous run of it.
568
649
  let columns: array<column> = [
569
650
  {
570
- field: mkField((#id: field :> string), UInt64, ~fieldSchema=S.bigint, ~isPrimaryKey),
571
- clickHouseFieldType: UInt64,
572
- valuesOf: batch => batch.checkpointIds->(Utils.magic: array<bigint> => array<unknown>),
573
- },
574
- {
575
- field: mkField((#chain_id: field :> string), ChainId, ~fieldSchema=ChainId.schema),
651
+ field: mkField(
652
+ (#chain_id: field :> string),
653
+ ChainId,
654
+ ~fieldSchema=ChainId.schema,
655
+ ~isPrimaryKey,
656
+ ),
576
657
  clickHouseFieldType: ChainId,
577
658
  valuesOf: batch =>
578
659
  batch.checkpointChainIds->(Utils.magic: array<ChainId.t> => array<unknown>),
660
+ diffValuesOf: diffs =>
661
+ diffs->Array.map(diff => diff.chainId->(Utils.magic: ChainId.t => unknown)),
662
+ },
663
+ {
664
+ field: mkField((#id: field :> string), UInt64, ~fieldSchema=S.bigint, ~isPrimaryKey),
665
+ clickHouseFieldType: UInt64,
666
+ valuesOf: batch => batch.checkpointIds->(Utils.magic: array<bigint> => array<unknown>),
667
+ diffValuesOf: diffs =>
668
+ diffs->Array.map(diff => diff.checkpointId->(Utils.magic: bigint => unknown)),
579
669
  },
580
670
  {
581
671
  field: mkField((#block_number: field :> string), Int32, ~fieldSchema=S.int),
582
672
  clickHouseFieldType: Int32,
583
673
  valuesOf: batch => batch.checkpointBlockNumbers->(Utils.magic: array<int> => array<unknown>),
674
+ diffValuesOf: diffs =>
675
+ diffs->Array.map(diff => diff.blockNumber->(Utils.magic: int => unknown)),
584
676
  },
585
677
  {
586
678
  field: mkField(
@@ -592,6 +684,8 @@ module Checkpoints = {
592
684
  clickHouseFieldType: String,
593
685
  valuesOf: batch =>
594
686
  batch.checkpointBlockHashes->(Utils.magic: array<Null.t<string>> => array<unknown>),
687
+ diffValuesOf: diffs =>
688
+ diffs->Array.map(_ => Null.Null->(Utils.magic: Null.t<string> => unknown)),
595
689
  },
596
690
  {
597
691
  field: mkField((#events_processed: field :> string), Int32, ~fieldSchema=S.int),
@@ -600,10 +694,34 @@ module Checkpoints = {
600
694
  clickHouseFieldType: UInt64,
601
695
  valuesOf: batch =>
602
696
  batch.checkpointEventsProcessed->(Utils.magic: array<int> => array<unknown>),
697
+ diffValuesOf: diffs => diffs->Array.map(_ => 0->(Utils.magic: int => unknown)),
603
698
  },
604
699
  ]
605
700
 
606
- let table = mkTable("envio_checkpoints", ~fields=columns->Array.map(({field}) => field))
701
+ let tableName = "envio_checkpoints"
702
+
703
+ let table = mkTable(tableName, ~fields=columns->Array.map(({field}) => field))
704
+
705
+ // Where each chain counts its own ids the chain has to be part of the key,
706
+ // and every bound a rollback or a prune applies names it. Under one shared
707
+ // sequence the id is unique by itself and those bounds are id ranges with no
708
+ // chain in them — which a key led by the chain can't serve.
709
+ let globalTable = mkTable(
710
+ tableName,
711
+ ~fields=columns->Array.map(({field: column}) =>
712
+ switch column {
713
+ | Table.Field(f) if f.fieldName === (#chain_id: field :> string) =>
714
+ Table.Field({...f, isPrimaryKey: false})
715
+ | column => column
716
+ }
717
+ ),
718
+ )
719
+
720
+ let tableFor = (sequence: CheckpointSequence.t) =>
721
+ switch sequence {
722
+ | SharedAcrossChains => globalTable
723
+ | PerChain => table
724
+ }
607
725
 
608
726
  let makeGetReorgCheckpointsQuery = (~pgSchema): string => {
609
727
  // The safe_block checkpoint itself is included, so it can be used for safe
@@ -640,10 +758,6 @@ WHERE cp."${(#block_hash: field :> string)}" IS NOT NULL
640
758
  ORDER BY cp."${(#id: field :> string)}";`
641
759
  }
642
760
 
643
- let makeCommitedCheckpointIdQuery = (~pgSchema) => {
644
- `SELECT COALESCE(MAX(${(#id: field :> string)}), ${initialCheckpointId->BigInt.toString}) AS id FROM "${pgSchema}"."${table.tableName}";`
645
- }
646
-
647
761
  let makeInsertCheckpointQuery = (~pgSchema, ~chainIdMode: ChainId.mode=Int32) => {
648
762
  let chainIdArrayType = chainIdArrayType(~pgSchema, ~chainIdMode)
649
763
  `INSERT INTO "${pgSchema}"."${table.tableName}" ("${(#id: field :> string)}", "${(#chain_id: field :> string)}", "${(#block_number: field :> string)}", "${(#block_hash: field :> string)}", "${(#events_processed: field :> string)}")
@@ -687,18 +801,21 @@ SELECT * FROM unnest($1::${(BigInt: Postgres.columnType :> string)}[],$2::${chai
687
801
 
688
802
  let rollback = (sql, ~pgSchema, ~floors: RollbackFloors.t) => {
689
803
  let tableRef = `"${pgSchema}"."${table.tableName}"`
690
- let bounds = floors.floors->CheckpointBounds.sql(~chainIdColumn, ~tableRef)
804
+ let bounds = floors.checkpointBounds->CheckpointSequence.sql(~chainIdColumn, ~tableRef)
691
805
  sql
692
806
  ->Postgres.preparedUnsafe(
693
807
  `DELETE FROM ${tableRef}${bounds.using} WHERE "${(#id: field :> string)}" > ${bounds.checkpointId}${bounds.usingMatch};`,
694
- floors.floors->CheckpointBounds.params,
808
+ floors.checkpointBounds->CheckpointSequence.params,
695
809
  )
696
810
  ->Utils.Promise.ignoreValue
697
811
  }
698
812
 
699
- let makePruneStaleCheckpointsQuery = (~pgSchema, ~safeCheckpoints: CheckpointBounds.t) => {
813
+ let makePruneStaleCheckpointsQuery = (
814
+ ~pgSchema,
815
+ ~safeCheckpoints: CheckpointSequence.checkpointBoundsByChain,
816
+ ) => {
700
817
  let tableRef = `"${pgSchema}"."${table.tableName}"`
701
- let bounds = safeCheckpoints->CheckpointBounds.sql(~chainIdColumn, ~tableRef)
818
+ let bounds = safeCheckpoints->CheckpointSequence.sql(~chainIdColumn, ~tableRef)
702
819
  `DELETE FROM ${tableRef}${bounds.using} WHERE "${(#id: field :> string)}" < ${bounds.checkpointId}${bounds.usingMatch};`
703
820
  }
704
821
 
@@ -706,7 +823,7 @@ SELECT * FROM unnest($1::${(BigInt: Postgres.columnType :> string)}[],$2::${chai
706
823
  sql
707
824
  ->Postgres.preparedUnsafe(
708
825
  makePruneStaleCheckpointsQuery(~pgSchema, ~safeCheckpoints),
709
- safeCheckpoints->CheckpointBounds.params,
826
+ safeCheckpoints->CheckpointSequence.params,
710
827
  )
711
828
  ->Utils.Promise.ignoreValue
712
829
 
@@ -738,7 +855,7 @@ LIMIT 1;`
738
855
  }
739
856
 
740
857
  let makeGetRollbackProgressDiffQuery = (~pgSchema, ~floors: RollbackFloors.t) => {
741
- let bounds = floors.floors->CheckpointBounds.sql(~chainIdColumn, ~tableRef="t")
858
+ let bounds = floors.checkpointBounds->CheckpointSequence.sql(~chainIdColumn, ~tableRef="t")
742
859
  `SELECT
743
860
  t."${(#chain_id: field :> string)}"::float8 as "${(#chain_id: field :> string)}",
744
861
  SUM(t."${(#events_processed: field :> string)}") as events_processed_diff,
@@ -752,7 +869,7 @@ GROUP BY t."${(#chain_id: field :> string)}";`
752
869
  sql
753
870
  ->Postgres.preparedUnsafe(
754
871
  makeGetRollbackProgressDiffQuery(~pgSchema, ~floors),
755
- floors.floors->CheckpointBounds.params,
872
+ floors.checkpointBounds->CheckpointSequence.params,
756
873
  )
757
874
  ->(
758
875
  Utils.magic: promise<unknown> => promise<
@@ -816,6 +933,7 @@ SELECT
816
933
  "${(#start_block: Chains.field :> string)}" AS "startBlock",
817
934
  "${(#end_block: Chains.field :> string)}" AS "endBlock",
818
935
  "${(#progress_block: Chains.field :> string)}" AS "progressBlock",
936
+ "${(#progress_block_time: Chains.field :> string)}" AS "progressBlockTime",
819
937
  "${(#buffer_block: Chains.field :> string)}" AS "bufferBlock",
820
938
  "${(#first_event_block: Chains.field :> string)}" AS "firstEventBlock",
821
939
  "${(#events_processed: Chains.field :> string)}"::float4 AS "eventsProcessed",
@@ -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",