envio 3.5.0-alpha.3 → 3.5.0-rc.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 (127) hide show
  1. package/package.json +6 -6
  2. package/src/Batch.res +8 -8
  3. package/src/Batch.res.mjs +4 -3
  4. package/src/BatchProcessing.res +17 -7
  5. package/src/BatchProcessing.res.mjs +8 -4
  6. package/src/ChainFetching.res +20 -19
  7. package/src/ChainFetching.res.mjs +14 -14
  8. package/src/ChainId.res +58 -0
  9. package/src/ChainId.res.mjs +49 -0
  10. package/src/ChainId.resi +27 -0
  11. package/src/ChainMap.res +12 -26
  12. package/src/ChainMap.res.mjs +5 -41
  13. package/src/ChainMap.resi +5 -20
  14. package/src/ChainMetadata.res +1 -1
  15. package/src/ChainMetadata.res.mjs +3 -2
  16. package/src/ChainState.res +35 -20
  17. package/src/ChainState.res.mjs +34 -11
  18. package/src/ChainState.resi +2 -0
  19. package/src/Config.res +41 -24
  20. package/src/Config.res.mjs +17 -14
  21. package/src/ContractRegisterContext.res +1 -1
  22. package/src/ContractRegisterContext.res.mjs +1 -1
  23. package/src/Core.res +1 -0
  24. package/src/CrossChainState.res +61 -16
  25. package/src/CrossChainState.res.mjs +37 -13
  26. package/src/CrossChainState.resi +3 -2
  27. package/src/Env.res +0 -3
  28. package/src/Env.res.mjs +0 -3
  29. package/src/EventConfigBuilder.res +1 -1
  30. package/src/EventProcessing.res +4 -4
  31. package/src/EventProcessing.res.mjs +5 -5
  32. package/src/FetchState.res +261 -89
  33. package/src/FetchState.res.mjs +187 -63
  34. package/src/FinalizeBackfill.res +33 -6
  35. package/src/FinalizeBackfill.res.mjs +19 -1
  36. package/src/HandlerRegister.res +8 -8
  37. package/src/HandlerRegister.res.mjs +5 -4
  38. package/src/HandlerRegister.resi +1 -1
  39. package/src/InMemoryStore.res +4 -0
  40. package/src/InMemoryStore.res.mjs +1 -1
  41. package/src/IndexerLoop.res +9 -2
  42. package/src/IndexerLoop.res.mjs +8 -1
  43. package/src/IndexerState.res +41 -24
  44. package/src/IndexerState.res.mjs +36 -17
  45. package/src/IndexerState.resi +9 -7
  46. package/src/Internal.res +19 -17
  47. package/src/Internal.res.mjs +18 -9
  48. package/src/LoadLayer.res +1 -1
  49. package/src/LoadLayer.res.mjs +2 -1
  50. package/src/LogSelection.res +2 -2
  51. package/src/Main.res +7 -11
  52. package/src/Main.res.mjs +4 -6
  53. package/src/Metrics.res +6 -6
  54. package/src/Metrics.res.mjs +4 -3
  55. package/src/Persistence.res +15 -7
  56. package/src/PgStorage.res +284 -258
  57. package/src/PgStorage.res.mjs +181 -208
  58. package/src/RawEvent.res +1 -2
  59. package/src/RawEvent.res.mjs +1 -1
  60. package/src/Rollback.res +18 -20
  61. package/src/Rollback.res.mjs +9 -9
  62. package/src/RollbackCommit.res +2 -2
  63. package/src/RollbackCommit.res.mjs +3 -3
  64. package/src/SafeCheckpointTracking.res +2 -2
  65. package/src/SimulateDeadInputTracker.res +17 -21
  66. package/src/SimulateDeadInputTracker.res.mjs +9 -8
  67. package/src/SimulateItems.res +8 -10
  68. package/src/SimulateItems.res.mjs +9 -10
  69. package/src/Sink.res +2 -2
  70. package/src/Sink.res.mjs +3 -2
  71. package/src/TestIndexer.res +11 -14
  72. package/src/TestIndexer.res.mjs +14 -10
  73. package/src/UserContext.res +2 -2
  74. package/src/UserContext.res.mjs +2 -1
  75. package/src/Utils.res +0 -4
  76. package/src/Utils.res.mjs +1 -6
  77. package/src/bindings/ClickHouse.res +20 -2
  78. package/src/bindings/ClickHouse.res.mjs +25 -14
  79. package/src/db/EntityHistory.res +1 -1
  80. package/src/db/IndexCatalog.res +248 -0
  81. package/src/db/IndexCatalog.res.mjs +222 -0
  82. package/src/db/IndexDefinition.res +131 -0
  83. package/src/db/IndexDefinition.res.mjs +138 -0
  84. package/src/db/IndexManager.res +145 -0
  85. package/src/db/IndexManager.res.mjs +130 -0
  86. package/src/db/InternalTable.res +50 -26
  87. package/src/db/InternalTable.res.mjs +32 -24
  88. package/src/db/Table.res +11 -1
  89. package/src/db/Table.res.mjs +9 -4
  90. package/src/sources/AddressSet.res +5 -4
  91. package/src/sources/AddressStore.res +25 -14
  92. package/src/sources/AddressStore.res.mjs +10 -12
  93. package/src/sources/Evm.res +2 -2
  94. package/src/sources/Evm.res.mjs +1 -1
  95. package/src/sources/EvmChain.res +3 -3
  96. package/src/sources/EvmChain.res.mjs +3 -3
  97. package/src/sources/EvmHyperSyncSource.res +7 -7
  98. package/src/sources/EvmHyperSyncSource.res.mjs +6 -6
  99. package/src/sources/Fuel.res +2 -2
  100. package/src/sources/Fuel.res.mjs +1 -1
  101. package/src/sources/FuelHyperSyncClient.res +3 -0
  102. package/src/sources/FuelHyperSyncClient.res.mjs +1 -0
  103. package/src/sources/FuelHyperSyncSource.res +5 -7
  104. package/src/sources/FuelHyperSyncSource.res.mjs +5 -5
  105. package/src/sources/HyperSync.resi +2 -2
  106. package/src/sources/HyperSyncClient.res +5 -0
  107. package/src/sources/HyperSyncClient.res.mjs +1 -0
  108. package/src/sources/RpcSource.res +13 -14
  109. package/src/sources/RpcSource.res.mjs +13 -12
  110. package/src/sources/SimulateSource.res +42 -15
  111. package/src/sources/SimulateSource.res.mjs +27 -9
  112. package/src/sources/Source.res +1 -5
  113. package/src/sources/SourceManager.res +12 -12
  114. package/src/sources/SourceManager.res.mjs +11 -10
  115. package/src/sources/SourceManager.resi +2 -2
  116. package/src/sources/Svm.res +4 -5
  117. package/src/sources/Svm.res.mjs +5 -4
  118. package/src/sources/SvmHyperSyncClient.res +3 -0
  119. package/src/sources/SvmHyperSyncClient.res.mjs +1 -0
  120. package/src/sources/SvmHyperSyncSource.res +4 -4
  121. package/src/sources/SvmHyperSyncSource.res.mjs +3 -3
  122. package/src/tui/Tui.res +1 -1
  123. package/src/tui/Tui.res.mjs +2 -1
  124. package/src/tui/components/CustomHooks.res +5 -4
  125. package/src/tui/components/CustomHooks.res.mjs +4 -2
  126. package/src/db/IndexRegistry.res +0 -219
  127. package/src/db/IndexRegistry.res.mjs +0 -195
@@ -85,7 +85,7 @@ let extractStartBlock = (
85
85
  // getter — the enclosing chainObj is a plain JS object.
86
86
  let makeChainArg = (
87
87
  ~contractName: string,
88
- ~chainId: int,
88
+ ~chainId: ChainId.t,
89
89
  ~getAddresses: unit => array<Address.t>,
90
90
  ) => {
91
91
  let contractObj = Utils.Object.createNullObject()
@@ -125,7 +125,7 @@ let parseWhereOrThrow = {
125
125
  ~sighash,
126
126
  ~params: array<string>,
127
127
  ~contractName: string,
128
- ~chainId: int,
128
+ ~chainId: ChainId.t,
129
129
  ~onEventBlockFilterSchema: S.t<option<unknown>>,
130
130
  ~topic1=noopGetter,
131
131
  ~topic2=noopGetter,
package/src/Main.res CHANGED
@@ -2,7 +2,7 @@
2
2
  // backward compatibility with consumers like RACE — new metric fields stay off
3
3
  // the HTTP response.
4
4
  type chainData = {
5
- chainId: float,
5
+ chainId: ChainId.t,
6
6
  poweredByHyperSync: bool,
7
7
  firstEventBlockNumber: option<int>,
8
8
  latestProcessedBlock: option<int>,
@@ -46,7 +46,7 @@ let toChainData = (m: Metrics.chainMetrics): chainData => {
46
46
  }
47
47
 
48
48
  let chainDataSchema = S.schema((s): chainData => {
49
- chainId: s.matches(S.float),
49
+ chainId: s.matches(ChainId.schema),
50
50
  poweredByHyperSync: s.matches(S.bool),
51
51
  firstEventBlockNumber: s.matches(S.option(S.int)),
52
52
  latestProcessedBlock: s.matches(S.option(S.int)),
@@ -88,7 +88,7 @@ let getGlobalPersistence = () =>
88
88
  let setGlobalPersistence = (persistence: Persistence.t) =>
89
89
  EnvioGlobal.value.persistence = Some(persistence->(Utils.magic: Persistence.t => unknown))
90
90
 
91
- let getInitialChainState = (~chainId: int): option<Persistence.initialChainState> => {
91
+ let getInitialChainState = (~chainId: ChainId.t): option<Persistence.initialChainState> => {
92
92
  switch getGlobalPersistence() {
93
93
  | Some(persistence) =>
94
94
  switch persistence.storageStatus {
@@ -107,7 +107,7 @@ let buildChainsObject = (~config: Config.t) => {
107
107
  config.chainMap
108
108
  ->ChainMap.values
109
109
  ->Array.forEach(chainConfig => {
110
- let chainIdStr = chainConfig.id->Int.toString
110
+ let chainIdStr = chainConfig.id->ChainId.toString
111
111
 
112
112
  chainIds->Array.push(chainConfig.id)->ignore
113
113
 
@@ -151,10 +151,7 @@ let buildChainsObject = (~config: Config.t) => {
151
151
  | Some(state) => state->IndexerState.isRealtime
152
152
  // Before the global state is available (eg during handler
153
153
  // module load after resume), derive from persistence: every chain
154
- // must have previously caught up to head or endBlock. Mirror the
155
- // IndexerState.makeFromDbState path: updateSyncTimeOnRestart wipes
156
- // the saved timestamps so a restart re-enters backfill.
157
- | None if Env.updateSyncTimeOnRestart => false
154
+ // must have previously caught up to head or endBlock.
158
155
  | None =>
159
156
  config.chainMap
160
157
  ->ChainMap.values
@@ -183,8 +180,7 @@ let buildChainsObject = (~config: Config.t) => {
183
180
  get: () => {
184
181
  switch getIndexerState() {
185
182
  | Some(state) => {
186
- let chain = ChainMap.Chain.makeUnsafe(~chainId=chainConfig.id)
187
- let chainState = state->IndexerState.getChainState(~chain)
183
+ let chainState = state->IndexerState.getChainState(~chainId=chainConfig.id)
188
184
  chainState->ChainState.contractAddresses(~contractName=contract.name)
189
185
  }
190
186
  // Before the global state is available (eg during handler
@@ -423,7 +419,7 @@ let getGlobalIndexer = (): 'indexer => {
423
419
  | "description" => Config.load().description->(Utils.magic: option<string> => unknown)
424
420
  | "chainIds" => {
425
421
  let (_, chainIds) = buildChainsObject(~config=Config.load())
426
- chainIds->(Utils.magic: array<int> => unknown)
422
+ chainIds->(Utils.magic: array<ChainId.t> => unknown)
427
423
  }
428
424
  | "chains" => {
429
425
  let (chains, _) = buildChainsObject(~config=Config.load())
package/src/Main.res.mjs CHANGED
@@ -5,6 +5,7 @@ import * as Tui from "./tui/Tui.res.mjs";
5
5
  import * as Envio from "./Envio.res.mjs";
6
6
  import * as Utils from "./Utils.res.mjs";
7
7
  import * as Config from "./Config.res.mjs";
8
+ import * as ChainId from "./ChainId.res.mjs";
8
9
  import * as Logging from "./Logging.res.mjs";
9
10
  import * as Metrics from "./Metrics.res.mjs";
10
11
  import Express from "express";
@@ -46,7 +47,7 @@ function toChainData(m) {
46
47
  }
47
48
 
48
49
  let chainDataSchema = S$RescriptSchema.schema(s => ({
49
- chainId: s.m(S$RescriptSchema.float),
50
+ chainId: s.m(ChainId.schema),
50
51
  poweredByHyperSync: s.m(S$RescriptSchema.bool),
51
52
  firstEventBlockNumber: s.m(S$RescriptSchema.option(S$RescriptSchema.int)),
52
53
  latestProcessedBlock: s.m(S$RescriptSchema.option(S$RescriptSchema.int)),
@@ -110,7 +111,7 @@ function buildChainsObject(config) {
110
111
  let chainIds = [];
111
112
  let chains = Object.create(null);
112
113
  ChainMap.values(config.chainMap).forEach(chainConfig => {
113
- let chainIdStr = chainConfig.id.toString();
114
+ let chainIdStr = ChainId.toString(chainConfig.id);
114
115
  chainIds.push(chainConfig.id);
115
116
  let chainObj = Object.create(null);
116
117
  Object.defineProperty(Object.defineProperty(Object.defineProperty(Object.defineProperty(Object.defineProperty(chainObj, "id", {
@@ -149,8 +150,6 @@ function buildChainsObject(config) {
149
150
  let state = EnvioGlobal.value.indexerState;
150
151
  if (state !== undefined) {
151
152
  return IndexerState.isRealtime(Primitive_option.valFromOption(state));
152
- } else if (Env.updateSyncTimeOnRestart) {
153
- return false;
154
153
  } else {
155
154
  return ChainMap.values(config.chainMap).every(c => {
156
155
  let chainState = getInitialChainState(c.id);
@@ -176,8 +175,7 @@ function buildChainsObject(config) {
176
175
  get: () => {
177
176
  let state = EnvioGlobal.value.indexerState;
178
177
  if (state !== undefined) {
179
- let chain = ChainMap.Chain.makeUnsafe(chainConfig.id);
180
- let chainState = IndexerState.getChainState(Primitive_option.valFromOption(state), chain);
178
+ let chainState = IndexerState.getChainState(Primitive_option.valFromOption(state), chainConfig.id);
181
179
  return ChainState.contractAddresses(chainState, contract.name);
182
180
  }
183
181
  let chainState$1 = getInitialChainState(chainConfig.id);
package/src/Metrics.res CHANGED
@@ -1,5 +1,5 @@
1
1
  type chainMetrics = {
2
- chainId: float,
2
+ chainId: ChainId.t,
3
3
  poweredByHyperSync: bool,
4
4
  firstEventBlockNumber: option<int>,
5
5
  latestProcessedBlock: option<int>,
@@ -84,7 +84,7 @@ type historyPruneMetrics = {
84
84
 
85
85
  type sourceRequestMetrics = {
86
86
  source: string,
87
- chainId: int,
87
+ chainId: ChainId.t,
88
88
  method: string,
89
89
  count: int,
90
90
  seconds: float,
@@ -92,7 +92,7 @@ type sourceRequestMetrics = {
92
92
 
93
93
  type sourceHeightMetrics = {
94
94
  source: string,
95
- chainId: int,
95
+ chainId: ChainId.t,
96
96
  height: int,
97
97
  }
98
98
 
@@ -198,7 +198,7 @@ let single = (b: builder, ~name, ~help, ~kind, ~value) => {
198
198
  }
199
199
 
200
200
  let renderMetrics = (b: builder, metrics: t) => {
201
- let chains = metrics.chains->Array.map(m => (`{chainId="${m.chainId->Float.toString}"}`, m))
201
+ let chains = metrics.chains->Array.map(m => (`{chainId="${m.chainId->ChainId.toString}"}`, m))
202
202
  let handlers =
203
203
  metrics.handlers->Array.map(s => (
204
204
  `{contract="${s.contract->escapeLabelValue}",event="${s.event->escapeLabelValue}"}`,
@@ -224,7 +224,7 @@ let renderMetrics = (b: builder, metrics: t) => {
224
224
  let sourceRequests = {
225
225
  let byLabels: dict<sourceRequestMetrics> = Dict.make()
226
226
  metrics.sourceRequests->Array.forEach(s => {
227
- let labels = `{source="${s.source->escapeLabelValue}",chainId="${s.chainId->Int.toString}",method="${s.method->escapeLabelValue}"}`
227
+ let labels = `{source="${s.source->escapeLabelValue}",chainId="${s.chainId->ChainId.toString}",method="${s.method->escapeLabelValue}"}`
228
228
  switch byLabels->Utils.Dict.dangerouslyGetNonOption(labels) {
229
229
  | Some(existing) =>
230
230
  byLabels->Dict.set(
@@ -239,7 +239,7 @@ let renderMetrics = (b: builder, metrics: t) => {
239
239
  let sources = {
240
240
  let byLabels: dict<int> = Dict.make()
241
241
  metrics.sourceHeights->Array.forEach(s => {
242
- let labels = `{source="${s.source->escapeLabelValue}",chainId="${s.chainId->Int.toString}"}`
242
+ let labels = `{source="${s.source->escapeLabelValue}",chainId="${s.chainId->ChainId.toString}"}`
243
243
  switch byLabels->Utils.Dict.dangerouslyGetNonOption(labels) {
244
244
  | Some(existing) if existing >= s.height => ()
245
245
  | _ => byLabels->Dict.set(labels, s.height)
@@ -2,6 +2,7 @@
2
2
 
3
3
  import * as V8 from "v8";
4
4
  import * as Utils from "./Utils.res.mjs";
5
+ import * as ChainId from "./ChainId.res.mjs";
5
6
  import * as Process from "process";
6
7
  import * as Perf_hooks from "perf_hooks";
7
8
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
@@ -56,7 +57,7 @@ function single(b, name, help, kind, value) {
56
57
 
57
58
  function renderMetrics(b, metrics) {
58
59
  let chains = metrics.chains.map(m => [
59
- `{chainId="` + m.chainId.toString() + `"}`,
60
+ `{chainId="` + ChainId.toString(m.chainId) + `"}`,
60
61
  m
61
62
  ]);
62
63
  let handlers = metrics.handlers.map(s => [
@@ -81,7 +82,7 @@ function renderMetrics(b, metrics) {
81
82
  ]);
82
83
  let byLabels = {};
83
84
  metrics.sourceRequests.forEach(s => {
84
- let labels = `{source="` + escapeLabelValue(s.source) + `",chainId="` + s.chainId.toString() + `",method="` + escapeLabelValue(s.method) + `"}`;
85
+ let labels = `{source="` + escapeLabelValue(s.source) + `",chainId="` + ChainId.toString(s.chainId) + `",method="` + escapeLabelValue(s.method) + `"}`;
85
86
  let existing = byLabels[labels];
86
87
  if (existing !== undefined) {
87
88
  byLabels[labels] = {
@@ -98,7 +99,7 @@ function renderMetrics(b, metrics) {
98
99
  let sourceRequests = Object.entries(byLabels);
99
100
  let byLabels$1 = {};
100
101
  metrics.sourceHeights.forEach(s => {
101
- let labels = `{source="` + escapeLabelValue(s.source) + `",chainId="` + s.chainId.toString() + `"}`;
102
+ let labels = `{source="` + escapeLabelValue(s.source) + `",chainId="` + ChainId.toString(s.chainId) + `"}`;
102
103
  let existing = byLabels$1[labels];
103
104
  if (existing !== undefined && existing >= s.height) {
104
105
  return;
@@ -19,7 +19,7 @@ type effectCacheRecord = {
19
19
  }
20
20
 
21
21
  type initialChainState = {
22
- id: int,
22
+ id: ChainId.t,
23
23
  startBlock: int,
24
24
  endBlock: option<int>,
25
25
  maxReorgDepth: int,
@@ -93,11 +93,19 @@ type storage = {
93
93
  // once they're queryable. Best-effort: it resolves even when a build fails,
94
94
  // leaving the query to run unindexed rather than failing the handler.
95
95
  ensureQueryIndexes: (~table: Table.table, ~filters: array<EntityFilter.t>) => promise<unit>,
96
- // Creates every schema-defined index still missing and stamps `ready_at` on
97
- // the given chains, atomically. Called once, when backfill completes.
96
+ // Creates every schema-defined index still missing, without touching
97
+ // `ready_at`. For a resumed indexer that is already ready and so never runs
98
+ // `finalizeBackfill`: an index dropped or invalidated while it was down would
99
+ // otherwise never be rebuilt. Best-effort, and safe to run with indexing live.
100
+ ensureSchemaIndexes: (~entities: array<Internal.entityConfig>) => promise<unit>,
101
+ // Creates every schema-defined index still missing, then stamps `ready_at` on
102
+ // the given chains. Called once, when backfill completes. The indexes are
103
+ // committed one at a time so a failure part way through doesn't undo the ones
104
+ // already built; `ready_at` is only written once they all verify, and all
105
+ // chains are stamped together.
98
106
  finalizeBackfill: (
99
107
  ~entities: array<Internal.entityConfig>,
100
- ~chainIds: array<int>,
108
+ ~chainIds: array<ChainId.t>,
101
109
  ~readyAt: Date.t,
102
110
  ) => promise<unit>,
103
111
  // This is to download cache from the database to .envio/cache
@@ -115,7 +123,7 @@ type storage = {
115
123
  ) => promise<unit>,
116
124
  // Get rollback target checkpoint
117
125
  getRollbackTargetCheckpoint: (
118
- ~reorgChainId: int,
126
+ ~reorgChainId: ChainId.t,
119
127
  ~lastKnownValidBlockNumber: int,
120
128
  ) => promise<option<Internal.checkpointId>>,
121
129
  // Get rollback progress diff
@@ -123,7 +131,7 @@ type storage = {
123
131
  ~rollbackTargetCheckpointId: Internal.checkpointId,
124
132
  ) => promise<
125
133
  array<{
126
- "chain_id": int,
134
+ "chain_id": ChainId.t,
127
135
  "events_processed_diff": string,
128
136
  "new_progress_block_number": int,
129
137
  }>,
@@ -251,7 +259,7 @@ let init = {
251
259
  persistence.storageStatus = Ready(initialState)
252
260
  let progress = Dict.make()
253
261
  initialState.chains->Array.forEach(c => {
254
- progress->Utils.Dict.setByInt(c.id, c.progressBlockNumber)
262
+ progress->ChainId.Dict.set(c.id, c.progressBlockNumber)
255
263
  })
256
264
  Logging.info({
257
265
  "msg": `Successfully resumed indexing state! Continuing from the last checkpoint.`,