envio 3.3.0-rc.0 → 3.3.1-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 (54) hide show
  1. package/package.json +6 -7
  2. package/src/ChainFetching.res +4 -32
  3. package/src/ChainFetching.res.mjs +2 -12
  4. package/src/ChainState.res +73 -53
  5. package/src/ChainState.res.mjs +61 -30
  6. package/src/ChainState.resi +13 -17
  7. package/src/Config.res +5 -0
  8. package/src/Config.res.mjs +1 -0
  9. package/src/CrossChainState.res +4 -10
  10. package/src/CrossChainState.res.mjs +7 -9
  11. package/src/CrossChainState.resi +1 -0
  12. package/src/EffectState.res +183 -44
  13. package/src/EffectState.res.mjs +134 -26
  14. package/src/EffectState.resi +26 -3
  15. package/src/EventProcessing.res +14 -9
  16. package/src/EventProcessing.res.mjs +7 -7
  17. package/src/FetchState.res +18 -29
  18. package/src/FetchState.res.mjs +20 -37
  19. package/src/IndexerState.res +280 -31
  20. package/src/IndexerState.res.mjs +234 -25
  21. package/src/IndexerState.resi +22 -0
  22. package/src/LoadLayer.res +19 -65
  23. package/src/LoadLayer.res.mjs +13 -55
  24. package/src/Main.res +42 -23
  25. package/src/Main.res.mjs +32 -35
  26. package/src/Metrics.res +943 -66
  27. package/src/Metrics.res.mjs +367 -50
  28. package/src/Persistence.res +3 -0
  29. package/src/PgStorage.res +3 -8
  30. package/src/PgStorage.res.mjs +3 -4
  31. package/src/PruneStaleHistory.res +1 -1
  32. package/src/PruneStaleHistory.res.mjs +1 -2
  33. package/src/Rollback.res +4 -5
  34. package/src/Rollback.res.mjs +2 -3
  35. package/src/SimulateItems.res.mjs +3 -21
  36. package/src/TestIndexer.res.mjs +4 -23
  37. package/src/TestIndexerProxyStorage.res +1 -0
  38. package/src/TestIndexerProxyStorage.res.mjs +1 -1
  39. package/src/Writing.res +3 -5
  40. package/src/Writing.res.mjs +3 -6
  41. package/src/bindings/ClickHouse.res +101 -17
  42. package/src/bindings/ClickHouse.res.mjs +58 -19
  43. package/src/bindings/NodeJs.res +64 -0
  44. package/src/bindings/NodeJs.res.mjs +6 -0
  45. package/src/sources/HyperSyncClient.res +5 -11
  46. package/src/sources/SourceManager.res +72 -27
  47. package/src/sources/SourceManager.res.mjs +81 -11
  48. package/src/sources/SourceManager.resi +14 -0
  49. package/src/tui/Tui.res +1 -1
  50. package/src/tui/Tui.res.mjs +2 -2
  51. package/src/Prometheus.res +0 -789
  52. package/src/Prometheus.res.mjs +0 -847
  53. package/src/bindings/PromClient.res +0 -72
  54. package/src/bindings/PromClient.res.mjs +0 -38
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "envio",
3
- "version": "3.3.0-rc.0",
3
+ "version": "3.3.1-rc.0",
4
4
  "type": "module",
5
5
  "description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
6
6
  "bin": "./bin.mjs",
@@ -54,7 +54,6 @@
54
54
  "express": "4.19.2",
55
55
  "pino": "10.3.1",
56
56
  "pino-pretty": "13.1.3",
57
- "prom-client": "15.1.3",
58
57
  "yargs": "17.7.2",
59
58
  "@rescript/runtime": "12.2.0",
60
59
  "rescript-schema": "9.5.1",
@@ -70,10 +69,10 @@
70
69
  "tsx": "4.21.0"
71
70
  },
72
71
  "optionalDependencies": {
73
- "envio-linux-x64": "3.3.0-rc.0",
74
- "envio-linux-x64-musl": "3.3.0-rc.0",
75
- "envio-linux-arm64": "3.3.0-rc.0",
76
- "envio-darwin-x64": "3.3.0-rc.0",
77
- "envio-darwin-arm64": "3.3.0-rc.0"
72
+ "envio-linux-x64": "3.3.1-rc.0",
73
+ "envio-linux-x64-musl": "3.3.1-rc.0",
74
+ "envio-linux-arm64": "3.3.1-rc.0",
75
+ "envio-darwin-x64": "3.3.1-rc.0",
76
+ "envio-darwin-arm64": "3.3.1-rc.0"
78
77
  }
79
78
  }
@@ -128,19 +128,7 @@ let rec onQueryResponse = async (
128
128
  fromBlockQueried,
129
129
  } = response
130
130
 
131
- if knownHeight > chainState->ChainState.knownHeight {
132
- Prometheus.SourceHeight.set(
133
- ~blockNumber=knownHeight,
134
- ~chainId=(chainState->ChainState.chainConfig).id,
135
- // The knownHeight from response won't necessarily
136
- // belong to the currently active source.
137
- // But for simplicity, assume it does.
138
- ~sourceName=(chainState->ChainState.sourceManager->SourceManager.getActiveSource).name,
139
- )
140
- }
141
-
142
- Prometheus.FetchingBlockRange.increment(
143
- ~chainId=chain->ChainMap.Chain.toChainId,
131
+ chainState->ChainState.recordBlockRangeFetch(
144
132
  ~totalTimeElapsed=stats.totalTimeElapsed,
145
133
  ~parsingTimeElapsed=stats.parsingTimeElapsed->Option.getOr(0.),
146
134
  ~numEvents=parsedQueueItems->Array.length,
@@ -183,10 +171,8 @@ let rec onQueryResponse = async (
183
171
  ~shouldRollbackOnReorg=(state->IndexerState.config).shouldRollbackOnReorg,
184
172
  ),
185
173
  )
186
- Prometheus.ReorgCount.increment(~chain)
187
- Prometheus.ReorgDetectionBlockNumber.set(
174
+ chainState->ChainState.recordReorgDetected(
188
175
  ~blockNumber=reorgDetected.scannedBlock.blockNumber,
189
- ~chain,
190
176
  )
191
177
  if (state->IndexerState.config).shouldRollbackOnReorg {
192
178
  Some(reorgDetected.scannedBlock.blockNumber)
@@ -333,22 +319,8 @@ let finishWaitingForNewBlock = (
333
319
  let chainState = state->IndexerState.getChainState(~chain)
334
320
  chainState->ChainState.updateKnownHeight(~knownHeight)
335
321
 
336
- let isBelowReorgThreshold =
337
- !(state->IndexerState.isInReorgThreshold) &&
338
- (state->IndexerState.config).shouldRollbackOnReorg
339
- let shouldEnterReorgThreshold =
340
- isBelowReorgThreshold &&
341
- state
342
- ->IndexerState.chainStates
343
- ->Dict.valuesToArray
344
- ->Array.every(cs => {
345
- cs->ChainState.isReadyToEnterReorgThreshold
346
- })
347
-
348
- // Kick processing in case there are block handlers to run.
349
- if shouldEnterReorgThreshold {
350
- IndexerState.enterReorgThreshold(state)
351
- }
322
+ // No reorg-threshold check here: scheduleProcessing always runs at least one
323
+ // processNextBatch (even with no items), which owns the entry decision.
352
324
  scheduleFetch()
353
325
  scheduleProcessing()
354
326
  }
@@ -4,7 +4,6 @@ import * as Utils from "./Utils.res.mjs";
4
4
  import * as Logging from "./Logging.res.mjs";
5
5
  import * as Ecosystem from "./Ecosystem.res.mjs";
6
6
  import * as ChainState from "./ChainState.res.mjs";
7
- import * as Prometheus from "./Prometheus.res.mjs";
8
7
  import * as IndexerState from "./IndexerState.res.mjs";
9
8
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
10
9
  import * as ChainMetadata from "./ChainMetadata.res.mjs";
@@ -100,10 +99,7 @@ async function onQueryResponse(state, param, stateId, scheduleFetch, schedulePro
100
99
  let transactionStore = response.transactionStore;
101
100
  let parsedQueueItems = response.parsedQueueItems;
102
101
  let knownHeight = response.knownHeight;
103
- if (knownHeight > ChainState.knownHeight(chainState)) {
104
- Prometheus.SourceHeight.set(SourceManager.getActiveSource(ChainState.sourceManager(chainState)).name, ChainState.chainConfig(chainState).id, knownHeight);
105
- }
106
- Prometheus.FetchingBlockRange.increment(chain, stats["total time elapsed (s)"], Stdlib_Option.getOr(stats["parsing time (s)"], 0), parsedQueueItems.length, (latestFetchedBlockNumber - fromBlockQueried | 0) + 1 | 0);
102
+ ChainState.recordBlockRangeFetch(chainState, stats["total time elapsed (s)"], Stdlib_Option.getOr(stats["parsing time (s)"], 0), parsedQueueItems.length, (latestFetchedBlockNumber - fromBlockQueried | 0) + 1 | 0);
107
103
  let numContractRegisterEvents = Stdlib_Array.reduce(parsedQueueItems, 0, (count, item) => {
108
104
  if (item.onEventRegistration.contractRegister !== undefined) {
109
105
  return count + 1 | 0;
@@ -138,8 +134,7 @@ async function onQueryResponse(state, param, stateId, scheduleFetch, schedulePro
138
134
  } else {
139
135
  let reorgDetected = reorgResult._0;
140
136
  Logging.childInfo(ChainState.logger(chainState), ReorgDetection.reorgDetectedToLogParams(reorgDetected, IndexerState.config(state).shouldRollbackOnReorg));
141
- Prometheus.ReorgCount.increment(chain);
142
- Prometheus.ReorgDetectionBlockNumber.set(reorgDetected.scannedBlock.blockNumber, chain);
137
+ ChainState.recordReorgDetected(chainState, reorgDetected.scannedBlock.blockNumber);
143
138
  rollbackWithReorgDetectedBlockNumber = IndexerState.config(state).shouldRollbackOnReorg ? reorgDetected.scannedBlock.blockNumber : undefined;
144
139
  }
145
140
  if (rollbackWithReorgDetectedBlockNumber !== undefined) {
@@ -188,11 +183,6 @@ function finishWaitingForNewBlock(state, chain, knownHeight, stateId, scheduleFe
188
183
  }
189
184
  let chainState = IndexerState.getChainState(state, chain);
190
185
  ChainState.updateKnownHeight(chainState, knownHeight);
191
- let isBelowReorgThreshold = !IndexerState.isInReorgThreshold(state) && IndexerState.config(state).shouldRollbackOnReorg;
192
- let shouldEnterReorgThreshold = isBelowReorgThreshold && Object.values(IndexerState.chainStates(state)).every(ChainState.isReadyToEnterReorgThreshold);
193
- if (shouldEnterReorgThreshold) {
194
- IndexerState.enterReorgThreshold(state);
195
- }
196
186
  scheduleFetch();
197
187
  scheduleProcessing();
198
188
  }
@@ -39,24 +39,18 @@ type t = {
39
39
  // Holds this chain's blocks (kept in Rust) keyed by block number. Same merge /
40
40
  // prune / rollback lifecycle as the transaction store.
41
41
  blockStore: BlockStore.t,
42
- }
43
-
44
- // Per-chain shape returned by the status API.
45
- type chainData = {
46
- chainId: float,
47
- poweredByHyperSync: bool,
48
- firstEventBlockNumber: option<int>,
49
- latestProcessedBlock: option<int>,
50
- timestampCaughtUpToHeadOrEndblock: option<Date.t>,
51
- numEventsProcessed: float,
52
- latestFetchedBlockNumber: int,
53
- // Need this for API backwards compatibility
54
- @as("currentBlockHeight")
55
- knownHeight: int,
56
- numBatchesFetched: int,
57
- startBlock: int,
58
- endBlock: option<int>,
59
- numAddresses: int,
42
+ reorgThresholdReadyTolerance: int,
43
+ // --- Per-chain metric counters, rendered by Metrics at scrape time.
44
+ // Floats: cumulative counters outgrow int32. ---
45
+ mutable blockRangeFetchSeconds: float,
46
+ mutable blockRangeParseSeconds: float,
47
+ mutable blockRangeFetchCount: float,
48
+ mutable blockRangeFetchedEvents: float,
49
+ mutable blockRangeFetchedBlocks: float,
50
+ mutable reorgCount: int,
51
+ mutable reorgDetectedBlock: option<int>,
52
+ mutable rollbackTargetBlock: option<int>,
53
+ mutable progressLatencyMs: option<int>,
60
54
  }
61
55
 
62
56
  let configAddresses = (chainConfig: Config.chain): array<Internal.indexingAddress> => {
@@ -100,6 +94,7 @@ let make = (
100
94
  ~transactionStore=TransactionStore.make(~ecosystem=Ecosystem.Evm, ~shouldChecksum=false),
101
95
  ~chainDensity=None,
102
96
  ~blockStore=BlockStore.make(~ecosystem=Ecosystem.Evm, ~shouldChecksum=false),
97
+ ~reorgThresholdReadyTolerance=100,
103
98
  ~logger: Pino.t,
104
99
  ): t => {
105
100
  validateOnEventRegistrations(~chainId=chainConfig.id, onEventRegistrations)
@@ -121,6 +116,16 @@ let make = (
121
116
  safeCheckpointTracking,
122
117
  transactionStore,
123
118
  blockStore,
119
+ reorgThresholdReadyTolerance,
120
+ blockRangeFetchSeconds: 0.,
121
+ blockRangeParseSeconds: 0.,
122
+ blockRangeFetchCount: 0.,
123
+ blockRangeFetchedEvents: 0.,
124
+ blockRangeFetchedBlocks: 0.,
125
+ reorgCount: 0,
126
+ reorgDetectedBlock: None,
127
+ rollbackTargetBlock: None,
128
+ progressLatencyMs: None,
124
129
  }
125
130
  }
126
131
 
@@ -282,6 +287,7 @@ let makeInternal = (
282
287
  ~ecosystem=config.ecosystem.name,
283
288
  ~shouldChecksum=!lowercaseAddresses,
284
289
  ),
290
+ ~reorgThresholdReadyTolerance=config.reorgThresholdReadyTolerance,
285
291
  ~logger,
286
292
  )
287
293
  }
@@ -329,8 +335,6 @@ let makeFromDbState = (
329
335
  let chainId = chainConfig.id
330
336
  let logger = Logging.createChild(~params={"chainId": chainId})
331
337
 
332
- Prometheus.ProgressEventsCount.set(~processedCount=resumedChainState.numEventsProcessed, ~chainId)
333
-
334
338
  let progressBlockNumber =
335
339
  // Can be -1 when not set
336
340
  resumedChainState.progressBlockNumber >= 0
@@ -373,6 +377,29 @@ let numEventsProcessed = (cs: t) => cs.numEventsProcessed
373
377
  let pendingBudget = (cs: t) => cs.pendingBudget
374
378
  let timestampCaughtUpToHeadOrEndblock = (cs: t) => cs.timestampCaughtUpToHeadOrEndblock
375
379
 
380
+ // --- Metric counters. ---
381
+
382
+ let recordBlockRangeFetch = (
383
+ cs: t,
384
+ ~totalTimeElapsed,
385
+ ~parsingTimeElapsed,
386
+ ~numEvents,
387
+ ~blockRangeSize,
388
+ ) => {
389
+ cs.blockRangeFetchSeconds = cs.blockRangeFetchSeconds +. totalTimeElapsed
390
+ cs.blockRangeParseSeconds = cs.blockRangeParseSeconds +. parsingTimeElapsed
391
+ cs.blockRangeFetchCount = cs.blockRangeFetchCount +. 1.
392
+ cs.blockRangeFetchedEvents = cs.blockRangeFetchedEvents +. numEvents->Int.toFloat
393
+ cs.blockRangeFetchedBlocks = cs.blockRangeFetchedBlocks +. blockRangeSize->Int.toFloat
394
+ }
395
+
396
+ let recordReorgDetected = (cs: t, ~blockNumber) => {
397
+ cs.reorgCount = cs.reorgCount + 1
398
+ cs.reorgDetectedBlock = Some(blockNumber)
399
+ }
400
+
401
+ let setRollbackTargetBlock = (cs: t, ~blockNumber) => cs.rollbackTargetBlock = Some(blockNumber)
402
+
376
403
  // Fetch-frontier reads. The FetchState is owned here; callers go through these
377
404
  // rather than reaching into it.
378
405
  let knownHeight = (cs: t) => cs.fetchState.knownHeight
@@ -384,7 +411,6 @@ let getProgressPercentage = (cs: t) => cs.fetchState->FetchState.getProgressPerc
384
411
  let chainDensity = (cs: t) => cs.chainDensity
385
412
  let hasReadyItem = (cs: t) =>
386
413
  cs.fetchState->FetchState.isActivelyIndexing && cs.fetchState->FetchState.hasReadyItem
387
- let isReadyToEnterReorgThreshold = (cs: t) => cs.fetchState->FetchState.isReadyToEnterReorgThreshold
388
414
 
389
415
  // Mark queries as in flight and reserve their estimated size against the shared
390
416
  // buffer budget in one step, so the counter stays in sync with the pending
@@ -868,8 +894,7 @@ let toChainMetadata = (cs: t): InternalTable.Chains.metaFields => {
868
894
  timestampCaughtUpToHeadOrEndblock: cs.timestampCaughtUpToHeadOrEndblock->Null.fromOption,
869
895
  }
870
896
 
871
- // Snapshot the chain's view for the status API.
872
- let toChainData = (cs: t): chainData => {
897
+ let toMetrics = (cs: t): Metrics.chainMetrics => {
873
898
  chainId: cs.chainConfig.id->Int.toFloat,
874
899
  poweredByHyperSync: (cs.sourceManager->SourceManager.getActiveSource).poweredByHyperSync,
875
900
  firstEventBlockNumber: cs.fetchState.firstEventBlock,
@@ -886,6 +911,25 @@ let toChainData = (cs: t): chainData => {
886
911
  startBlock: cs.fetchState.startBlock,
887
912
  endBlock: cs.fetchState.endBlock,
888
913
  numAddresses: cs.indexingAddresses->IndexingAddresses.size,
914
+ isReady: cs->isReady,
915
+ sourceBlockNumber: cs.fetchState.knownHeight,
916
+ progressBlockNumber: cs.committedProgressBlockNumber,
917
+ progressLatencyMs: cs.progressLatencyMs,
918
+ concurrency: cs.sourceManager->SourceManager.inFlightCount,
919
+ partitionsCount: cs.fetchState->FetchState.partitionsCount,
920
+ bufferSize: cs.fetchState->FetchState.bufferSize,
921
+ bufferBlockNumber: cs.fetchState->FetchState.bufferBlockNumber,
922
+ idleSeconds: cs.sourceManager->SourceManager.idleSeconds,
923
+ waitingForNewBlockSeconds: cs.sourceManager->SourceManager.waitingForNewBlockSeconds,
924
+ queryingSeconds: cs.sourceManager->SourceManager.queryingSeconds,
925
+ blockRangeFetchSeconds: cs.blockRangeFetchSeconds,
926
+ blockRangeParseSeconds: cs.blockRangeParseSeconds,
927
+ blockRangeFetchCount: cs.blockRangeFetchCount,
928
+ blockRangeFetchedEvents: cs.blockRangeFetchedEvents,
929
+ blockRangeFetchedBlocks: cs.blockRangeFetchedBlocks,
930
+ reorgCount: cs.reorgCount,
931
+ reorgDetectedBlock: cs.reorgDetectedBlock,
932
+ rollbackTargetBlock: cs.rollbackTargetBlock,
889
933
  }
890
934
 
891
935
  // Snapshot the inputs a batch build needs from this chain.
@@ -907,7 +951,7 @@ let isReadyToEnterReorgThresholdAfterBatch = (cs: t, ~batch: Batch.t) => {
907
951
  | Some(chainAfterBatch) => chainAfterBatch.fetchState
908
952
  | None => cs.fetchState
909
953
  }
910
- fetchState->FetchState.isReadyToEnterReorgThreshold
954
+ fetchState->FetchState.isReadyToEnterReorgThreshold(~tolerance=cs.reorgThresholdReadyTolerance)
911
955
  }
912
956
 
913
957
  // Commit the post-batch fetch frontier for a chain that progressed in the batch,
@@ -928,28 +972,15 @@ let advanceAfterBatch = (cs: t, ~batch: Batch.t, ~enteringReorgThreshold) =>
928
972
  }
929
973
 
930
974
  // Commit a processed batch's progress for this chain (progress block, events
931
- // processed, head/safe-checkpoint tracking, first event block). Emits the
932
- // per-chain progress metrics. Readiness is decided by CrossChainState once every
933
- // chain is caught up (see markReady). `blockTimestampName` is the ecosystem's
934
- // block-timestamp field, read off the payload block for the latency metric.
975
+ // processed, head/safe-checkpoint tracking, first event block). Readiness is
976
+ // decided by CrossChainState once every chain is caught up (see markReady).
977
+ // `blockTimestampName` is the ecosystem's block-timestamp field, read off the
978
+ // payload block for the latency metric.
935
979
  let applyBatchProgress = (cs: t, ~batch: Batch.t, ~blockTimestampName: string) => {
936
980
  let chainId = cs.chainConfig.id
937
981
 
938
982
  switch batch.progressedChainsById->Utils.Dict.dangerouslyGetByIntNonOption(chainId) {
939
983
  | Some(chainAfterBatch) => {
940
- if cs.committedProgressBlockNumber !== chainAfterBatch.progressBlockNumber {
941
- Prometheus.ProgressBlockNumber.set(
942
- ~blockNumber=chainAfterBatch.progressBlockNumber,
943
- ~chainId,
944
- )
945
- }
946
- if cs.numEventsProcessed !== chainAfterBatch.totalEventsProcessed {
947
- Prometheus.ProgressEventsCount.set(
948
- ~processedCount=chainAfterBatch.totalEventsProcessed,
949
- ~chainId,
950
- )
951
- }
952
-
953
984
  // Calculate and set latency metrics. The payload block is materialised or
954
985
  // inline by processing time; its timestamp may still be absent (e.g. an
955
986
  // SVM slot with no block row) — the metric is skipped then.
@@ -967,10 +998,7 @@ let applyBatchProgress = (cs: t, ~batch: Batch.t, ~blockTimestampName: string) =
967
998
  ) {
968
999
  | Some(blockTimestamp) =>
969
1000
  let blockTimestampMs = blockTimestamp->(Utils.magic: unknown => int) * 1000
970
- Prometheus.ProgressLatency.set(
971
- ~latencyMs=Date.now()->Float.toInt - blockTimestampMs,
972
- ~chainId,
973
- )
1001
+ cs.progressLatencyMs = Some(Date.now()->Float.toInt - blockTimestampMs)
974
1002
  | None => ()
975
1003
  }
976
1004
 
@@ -1046,7 +1074,6 @@ let applyBatchProgress = (cs: t, ~batch: Batch.t, ~blockTimestampName: string) =
1046
1074
  let markReady = (cs: t) =>
1047
1075
  if !(cs->isReady) {
1048
1076
  cs.timestampCaughtUpToHeadOrEndblock = Date.make()->Some
1049
- Prometheus.ProgressReady.set(~chainId=cs.chainConfig.id)
1050
1077
  }
1051
1078
 
1052
1079
  // Roll a chain back to a reorg target. With a progress diff, restore fetch/
@@ -1061,7 +1088,6 @@ let rollback = (
1061
1088
  ~rollbackTargetBlockNumber,
1062
1089
  ~isReorgChain,
1063
1090
  ) => {
1064
- let chainId = cs.chainConfig.id
1065
1091
  switch newProgressBlockNumber {
1066
1092
  | Some(newProgressBlockNumber) =>
1067
1093
  let newTotalEventsProcessed =
@@ -1072,12 +1098,6 @@ let rollback = (
1072
1098
  ~message="Missing events-processed diff for rolled-back chain",
1073
1099
  )
1074
1100
 
1075
- if cs.committedProgressBlockNumber !== newProgressBlockNumber {
1076
- Prometheus.ProgressBlockNumber.set(~blockNumber=newProgressBlockNumber, ~chainId)
1077
- }
1078
- if cs.numEventsProcessed !== newTotalEventsProcessed {
1079
- Prometheus.ProgressEventsCount.set(~processedCount=newTotalEventsProcessed, ~chainId)
1080
- }
1081
1101
  if isReorgChain {
1082
1102
  cs.reorgDetection =
1083
1103
  cs.reorgDetection->ReorgDetection.rollbackToValidBlockNumber(
@@ -10,7 +10,6 @@ import * as EvmChain from "./sources/EvmChain.res.mjs";
10
10
  import * as FieldMask from "./sources/FieldMask.res.mjs";
11
11
  import * as BlockStore from "./sources/BlockStore.res.mjs";
12
12
  import * as FetchState from "./FetchState.res.mjs";
13
- import * as Prometheus from "./Prometheus.res.mjs";
14
13
  import * as Stdlib_Null from "@rescript/runtime/lib/es6/Stdlib_Null.js";
15
14
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
16
15
  import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
@@ -48,7 +47,7 @@ function validateOnEventRegistrations(chainId, registrations) {
48
47
  });
49
48
  }
50
49
 
51
- function make(chainConfig, fetchState, onEventRegistrationsOpt, indexingAddresses, sourceManager, reorgDetection, committedProgressBlockNumber, safeCheckpointTrackingOpt, numEventsProcessedOpt, timestampCaughtUpToHeadOrEndblockOpt, isProgressAtHeadOpt, transactionStoreOpt, chainDensityOpt, blockStoreOpt, logger) {
50
+ function make(chainConfig, fetchState, onEventRegistrationsOpt, indexingAddresses, sourceManager, reorgDetection, committedProgressBlockNumber, safeCheckpointTrackingOpt, numEventsProcessedOpt, timestampCaughtUpToHeadOrEndblockOpt, isProgressAtHeadOpt, transactionStoreOpt, chainDensityOpt, blockStoreOpt, reorgThresholdReadyToleranceOpt, logger) {
52
51
  let onEventRegistrations = onEventRegistrationsOpt !== undefined ? onEventRegistrationsOpt : [];
53
52
  let safeCheckpointTracking = safeCheckpointTrackingOpt !== undefined ? Primitive_option.valFromOption(safeCheckpointTrackingOpt) : undefined;
54
53
  let numEventsProcessed = numEventsProcessedOpt !== undefined ? numEventsProcessedOpt : 0;
@@ -57,6 +56,7 @@ function make(chainConfig, fetchState, onEventRegistrationsOpt, indexingAddresse
57
56
  let transactionStore = transactionStoreOpt !== undefined ? Primitive_option.valFromOption(transactionStoreOpt) : TransactionStore.make("evm", false);
58
57
  let chainDensity = chainDensityOpt !== undefined ? Primitive_option.valFromOption(chainDensityOpt) : undefined;
59
58
  let blockStore = blockStoreOpt !== undefined ? Primitive_option.valFromOption(blockStoreOpt) : BlockStore.make("evm", false);
59
+ let reorgThresholdReadyTolerance = reorgThresholdReadyToleranceOpt !== undefined ? reorgThresholdReadyToleranceOpt : 100;
60
60
  validateOnEventRegistrations(chainConfig.id, onEventRegistrations);
61
61
  return {
62
62
  logger: logger,
@@ -75,7 +75,17 @@ function make(chainConfig, fetchState, onEventRegistrationsOpt, indexingAddresse
75
75
  reorgDetection: reorgDetection,
76
76
  safeCheckpointTracking: safeCheckpointTracking,
77
77
  transactionStore: transactionStore,
78
- blockStore: blockStore
78
+ blockStore: blockStore,
79
+ reorgThresholdReadyTolerance: reorgThresholdReadyTolerance,
80
+ blockRangeFetchSeconds: 0,
81
+ blockRangeParseSeconds: 0,
82
+ blockRangeFetchCount: 0,
83
+ blockRangeFetchedEvents: 0,
84
+ blockRangeFetchedBlocks: 0,
85
+ reorgCount: 0,
86
+ reorgDetectedBlock: undefined,
87
+ rollbackTargetBlock: undefined,
88
+ progressLatencyMs: undefined
79
89
  };
80
90
  }
81
91
 
@@ -147,7 +157,7 @@ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firs
147
157
  }
148
158
  let firstEventBlock$1 = fetchState.firstEventBlock;
149
159
  let chainDensity = firstEventBlock$1 !== undefined && progressBlockNumber > firstEventBlock$1 && numEventsProcessed > 0 ? numEventsProcessed / (progressBlockNumber - firstEventBlock$1 | 0) : undefined;
150
- return make(chainConfig, fetchState, onEventRegistrations, indexingAddressIndex, SourceManager.make(sources$1, isRealtime, undefined, undefined, undefined, reducedPollingInterval, undefined, undefined), ReorgDetection.make(chainReorgCheckpoints, maxReorgDepth, config.shouldRollbackOnReorg), progressBlockNumber, Primitive_option.some(SafeCheckpointTracking.make(maxReorgDepth, config.shouldRollbackOnReorg, chainReorgCheckpoints)), numEventsProcessed, Primitive_option.some(timestampCaughtUpToHeadOrEndblock), undefined, Primitive_option.some(TransactionStore.make(config.ecosystem.name, !lowercaseAddresses)), Primitive_option.some(chainDensity), Primitive_option.some(BlockStore.make(config.ecosystem.name, !lowercaseAddresses)), logger);
160
+ return make(chainConfig, fetchState, onEventRegistrations, indexingAddressIndex, SourceManager.make(sources$1, isRealtime, undefined, undefined, undefined, reducedPollingInterval, undefined, undefined), ReorgDetection.make(chainReorgCheckpoints, maxReorgDepth, config.shouldRollbackOnReorg), progressBlockNumber, Primitive_option.some(SafeCheckpointTracking.make(maxReorgDepth, config.shouldRollbackOnReorg, chainReorgCheckpoints)), numEventsProcessed, Primitive_option.some(timestampCaughtUpToHeadOrEndblock), undefined, Primitive_option.some(TransactionStore.make(config.ecosystem.name, !lowercaseAddresses)), Primitive_option.some(chainDensity), Primitive_option.some(BlockStore.make(config.ecosystem.name, !lowercaseAddresses)), config.reorgThresholdReadyTolerance, logger);
151
161
  }
152
162
 
153
163
  function makeFromConfig(chainConfig, config, registrationsByChainId, knownHeight) {
@@ -162,7 +172,6 @@ function makeFromDbState(chainConfig, resumedChainState, reorgCheckpoints, isInR
162
172
  let logger = Logging.createChild({
163
173
  chainId: chainId
164
174
  });
165
- Prometheus.ProgressEventsCount.set(resumedChainState.numEventsProcessed, chainId);
166
175
  let progressBlockNumber = resumedChainState.progressBlockNumber >= 0 ? resumedChainState.progressBlockNumber : resumedChainState.startBlock - 1 | 0;
167
176
  return makeInternal(chainConfig, resumedChainState.indexingAddresses, resumedChainState.startBlock, resumedChainState.endBlock, Primitive_option.some(resumedChainState.firstEventBlockNumber), progressBlockNumber, config, registrationsByChainId, logger, Env.updateSyncTimeOnRestart ? undefined : resumedChainState.timestampCaughtUpToHeadOrEndblock, resumedChainState.numEventsProcessed, isInReorgThreshold, isRealtime, reorgCheckpoints, resumedChainState.maxReorgDepth, resumedChainState.sourceBlockNumber, reducedPollingInterval);
168
177
  }
@@ -207,6 +216,23 @@ function timestampCaughtUpToHeadOrEndblock(cs) {
207
216
  return cs.timestampCaughtUpToHeadOrEndblock;
208
217
  }
209
218
 
219
+ function recordBlockRangeFetch(cs, totalTimeElapsed, parsingTimeElapsed, numEvents, blockRangeSize) {
220
+ cs.blockRangeFetchSeconds = cs.blockRangeFetchSeconds + totalTimeElapsed;
221
+ cs.blockRangeParseSeconds = cs.blockRangeParseSeconds + parsingTimeElapsed;
222
+ cs.blockRangeFetchCount = cs.blockRangeFetchCount + 1;
223
+ cs.blockRangeFetchedEvents = cs.blockRangeFetchedEvents + numEvents;
224
+ cs.blockRangeFetchedBlocks = cs.blockRangeFetchedBlocks + blockRangeSize;
225
+ }
226
+
227
+ function recordReorgDetected(cs, blockNumber) {
228
+ cs.reorgCount = cs.reorgCount + 1 | 0;
229
+ cs.reorgDetectedBlock = blockNumber;
230
+ }
231
+
232
+ function setRollbackTargetBlock(cs, blockNumber) {
233
+ cs.rollbackTargetBlock = blockNumber;
234
+ }
235
+
210
236
  function knownHeight(cs) {
211
237
  return cs.fetchState.knownHeight;
212
238
  }
@@ -239,10 +265,6 @@ function hasReadyItem(cs) {
239
265
  }
240
266
  }
241
267
 
242
- function isReadyToEnterReorgThreshold(cs) {
243
- return FetchState.isReadyToEnterReorgThreshold(cs.fetchState);
244
- }
245
-
246
268
  function startFetchingQueries(cs, queries) {
247
269
  FetchState.startFetchingQueries(cs.fetchState, queries);
248
270
  cs.pendingBudget = cs.pendingBudget + Stdlib_Array.reduce(queries, 0, (acc, query) => acc + query.itemsEst);
@@ -598,7 +620,7 @@ function toChainMetadata(cs) {
598
620
  };
599
621
  }
600
622
 
601
- function toChainData(cs) {
623
+ function toMetrics(cs) {
602
624
  return {
603
625
  chainId: cs.chainConfig.id,
604
626
  poweredByHyperSync: SourceManager.getActiveSource(cs.sourceManager).poweredByHyperSync,
@@ -607,11 +629,30 @@ function toChainData(cs) {
607
629
  timestampCaughtUpToHeadOrEndblock: cs.timestampCaughtUpToHeadOrEndblock,
608
630
  numEventsProcessed: cs.numEventsProcessed,
609
631
  latestFetchedBlockNumber: Primitive_int.max(FetchState.bufferBlockNumber(cs.fetchState), 0),
610
- currentBlockHeight: hasProcessedToEndblock(cs) ? Stdlib_Option.getOr(cs.fetchState.endBlock, cs.fetchState.knownHeight) : cs.fetchState.knownHeight,
632
+ knownHeight: hasProcessedToEndblock(cs) ? Stdlib_Option.getOr(cs.fetchState.endBlock, cs.fetchState.knownHeight) : cs.fetchState.knownHeight,
611
633
  numBatchesFetched: 0,
612
634
  startBlock: cs.fetchState.startBlock,
613
635
  endBlock: cs.fetchState.endBlock,
614
- numAddresses: IndexingAddresses.size(cs.indexingAddresses)
636
+ numAddresses: IndexingAddresses.size(cs.indexingAddresses),
637
+ isReady: cs.timestampCaughtUpToHeadOrEndblock !== undefined,
638
+ sourceBlockNumber: cs.fetchState.knownHeight,
639
+ progressBlockNumber: cs.committedProgressBlockNumber,
640
+ progressLatencyMs: cs.progressLatencyMs,
641
+ concurrency: SourceManager.inFlightCount(cs.sourceManager),
642
+ partitionsCount: FetchState.partitionsCount(cs.fetchState),
643
+ bufferSize: FetchState.bufferSize(cs.fetchState),
644
+ bufferBlockNumber: FetchState.bufferBlockNumber(cs.fetchState),
645
+ idleSeconds: SourceManager.idleSeconds(cs.sourceManager),
646
+ waitingForNewBlockSeconds: SourceManager.waitingForNewBlockSeconds(cs.sourceManager),
647
+ queryingSeconds: SourceManager.queryingSeconds(cs.sourceManager),
648
+ blockRangeFetchSeconds: cs.blockRangeFetchSeconds,
649
+ blockRangeParseSeconds: cs.blockRangeParseSeconds,
650
+ blockRangeFetchCount: cs.blockRangeFetchCount,
651
+ blockRangeFetchedEvents: cs.blockRangeFetchedEvents,
652
+ blockRangeFetchedBlocks: cs.blockRangeFetchedBlocks,
653
+ reorgCount: cs.reorgCount,
654
+ reorgDetectedBlock: cs.reorgDetectedBlock,
655
+ rollbackTargetBlock: cs.rollbackTargetBlock
615
656
  };
616
657
  }
617
658
 
@@ -628,7 +669,8 @@ function toChainBeforeBatch(cs) {
628
669
 
629
670
  function isReadyToEnterReorgThresholdAfterBatch(cs, batch) {
630
671
  let chainAfterBatch = batch.progressedChainsById[cs.fetchState.chainId];
631
- return FetchState.isReadyToEnterReorgThreshold(chainAfterBatch !== undefined ? chainAfterBatch.fetchState : cs.fetchState);
672
+ let fetchState = chainAfterBatch !== undefined ? chainAfterBatch.fetchState : cs.fetchState;
673
+ return FetchState.isReadyToEnterReorgThreshold(cs.reorgThresholdReadyTolerance, fetchState);
632
674
  }
633
675
 
634
676
  function advanceAfterBatch(cs, batch, enteringReorgThreshold) {
@@ -646,16 +688,10 @@ function applyBatchProgress(cs, batch, blockTimestampName) {
646
688
  if (chainAfterBatch === undefined) {
647
689
  return;
648
690
  }
649
- if (cs.committedProgressBlockNumber !== chainAfterBatch.progressBlockNumber) {
650
- Prometheus.ProgressBlockNumber.set(chainAfterBatch.progressBlockNumber, chainId);
651
- }
652
- if (cs.numEventsProcessed !== chainAfterBatch.totalEventsProcessed) {
653
- Prometheus.ProgressEventsCount.set(chainAfterBatch.totalEventsProcessed, chainId);
654
- }
655
691
  let blockTimestamp = Stdlib_Option.flatMap(Stdlib_Option.flatMap(Batch.findLastEventItem(batch, chainId), eventItem => Primitive_option.fromNullable(eventItem.payload.block)), block => block[blockTimestampName]);
656
692
  if (blockTimestamp !== undefined) {
657
693
  let blockTimestampMs = Primitive_option.valFromOption(blockTimestamp) * 1000 | 0;
658
- Prometheus.ProgressLatency.set((Date.now() | 0) - blockTimestampMs | 0, chainId);
694
+ cs.progressLatencyMs = (Date.now() | 0) - blockTimestampMs | 0;
659
695
  }
660
696
  let match = cs.fetchState.firstEventBlock;
661
697
  if (match === undefined) {
@@ -717,12 +753,11 @@ function applyBatchProgress(cs, batch, blockTimestampName) {
717
753
  function markReady(cs) {
718
754
  if (cs.timestampCaughtUpToHeadOrEndblock === undefined) {
719
755
  cs.timestampCaughtUpToHeadOrEndblock = new Date();
720
- return Prometheus.ProgressReady.set(cs.chainConfig.id);
756
+ return;
721
757
  }
722
758
  }
723
759
 
724
760
  function rollback(cs, newProgressBlockNumber, eventsProcessedDiff, rollbackTargetBlockNumber, isReorgChain) {
725
- let chainId = cs.chainConfig.id;
726
761
  if (newProgressBlockNumber === undefined) {
727
762
  if (isReorgChain) {
728
763
  cs.reorgDetection = ReorgDetection.rollbackToValidBlockNumber(cs.reorgDetection, rollbackTargetBlockNumber);
@@ -737,12 +772,6 @@ function rollback(cs, newProgressBlockNumber, eventsProcessedDiff, rollbackTarge
737
772
  }
738
773
  }
739
774
  let newTotalEventsProcessed = cs.numEventsProcessed - Stdlib_Option.getOrThrow(eventsProcessedDiff, "Missing events-processed diff for rolled-back chain");
740
- if (cs.committedProgressBlockNumber !== newProgressBlockNumber) {
741
- Prometheus.ProgressBlockNumber.set(newProgressBlockNumber, chainId);
742
- }
743
- if (cs.numEventsProcessed !== newTotalEventsProcessed) {
744
- Prometheus.ProgressEventsCount.set(newTotalEventsProcessed, chainId);
745
- }
746
775
  if (isReorgChain) {
747
776
  cs.reorgDetection = ReorgDetection.rollbackToValidBlockNumber(cs.reorgDetection, rollbackTargetBlockNumber);
748
777
  }
@@ -774,6 +803,9 @@ export {
774
803
  pendingBudget,
775
804
  startFetchingQueries,
776
805
  timestampCaughtUpToHeadOrEndblock,
806
+ recordBlockRangeFetch,
807
+ recordReorgDetected,
808
+ setRollbackTargetBlock,
777
809
  knownHeight,
778
810
  contractAddresses,
779
811
  bufferSize,
@@ -782,13 +814,12 @@ export {
782
814
  chainDensity,
783
815
  effectiveDensity,
784
816
  hasReadyItem,
785
- isReadyToEnterReorgThreshold,
786
817
  targetBlock,
787
818
  blockAtProgress,
788
819
  frontierProgress,
789
820
  getNextQuery,
790
821
  dispatch,
791
- toChainData,
822
+ toMetrics,
792
823
  toChainMetadata,
793
824
  toChainBeforeBatch,
794
825
  isReadyToEnterReorgThresholdAfterBatch,
@@ -20,6 +20,7 @@ let make: (
20
20
  ~transactionStore: TransactionStore.t=?,
21
21
  ~chainDensity: option<float>=?,
22
22
  ~blockStore: BlockStore.t=?,
23
+ ~reorgThresholdReadyTolerance: int=?,
23
24
  ~logger: Pino.t,
24
25
  ) => t
25
26
 
@@ -54,6 +55,17 @@ let pendingBudget: t => float
54
55
  let startFetchingQueries: (t, ~queries: array<FetchState.query>) => unit
55
56
  let timestampCaughtUpToHeadOrEndblock: t => option<Date.t>
56
57
 
58
+ // Metric counters, rendered by Metrics at scrape time.
59
+ let recordBlockRangeFetch: (
60
+ t,
61
+ ~totalTimeElapsed: float,
62
+ ~parsingTimeElapsed: float,
63
+ ~numEvents: int,
64
+ ~blockRangeSize: int,
65
+ ) => unit
66
+ let recordReorgDetected: (t, ~blockNumber: int) => unit
67
+ let setRollbackTargetBlock: (t, ~blockNumber: int) => unit
68
+
57
69
  // Fetch-frontier reads.
58
70
  let knownHeight: t => int
59
71
  let contractAddresses: (t, ~contractName: string) => array<Address.t>
@@ -63,7 +75,6 @@ let getProgressPercentage: t => float
63
75
  let chainDensity: t => option<float>
64
76
  let effectiveDensity: t => option<float>
65
77
  let hasReadyItem: t => bool
66
- let isReadyToEnterReorgThreshold: t => bool
67
78
 
68
79
  // Fetch control.
69
80
  let targetBlock: (t, ~chainTargetItems: float) => int
@@ -86,22 +97,7 @@ let dispatch: (
86
97
  ) => promise<unit>
87
98
 
88
99
  // Views.
89
- type chainData = {
90
- chainId: float,
91
- poweredByHyperSync: bool,
92
- firstEventBlockNumber: option<int>,
93
- latestProcessedBlock: option<int>,
94
- timestampCaughtUpToHeadOrEndblock: option<Date.t>,
95
- numEventsProcessed: float,
96
- latestFetchedBlockNumber: int,
97
- @as("currentBlockHeight")
98
- knownHeight: int,
99
- numBatchesFetched: int,
100
- startBlock: int,
101
- endBlock: option<int>,
102
- numAddresses: int,
103
- }
104
- let toChainData: t => chainData
100
+ let toMetrics: t => Metrics.chainMetrics
105
101
  let toChainMetadata: t => InternalTable.Chains.metaFields
106
102
  let toChainBeforeBatch: t => Batch.chainBeforeBatch
107
103
  let isReadyToEnterReorgThresholdAfterBatch: (t, ~batch: Batch.t) => bool
package/src/Config.res CHANGED
@@ -79,6 +79,10 @@ type t = {
79
79
  enableRawEvents: bool,
80
80
  maxAddrInPartition: int,
81
81
  batchSize: int,
82
+ // Slack (in blocks) below the lagged head within which a chain still counts as
83
+ // ready to enter the reorg threshold, absorbing head advances between catch-up
84
+ // and the entry check. Overridable in tests.
85
+ reorgThresholdReadyTolerance: int,
82
86
  lowercaseAddresses: bool,
83
87
  isDev: bool,
84
88
  userEntitiesByName: dict<Internal.entityConfig>,
@@ -997,6 +1001,7 @@ let fromPublic = (publicConfigJson: JSON.t) => {
997
1001
  ecosystem,
998
1002
  maxAddrInPartition,
999
1003
  batchSize: publicConfig["fullBatchSize"]->Option.getOr(5000),
1004
+ reorgThresholdReadyTolerance: 100,
1000
1005
  lowercaseAddresses,
1001
1006
  isDev: publicConfig["isDev"]->Option.getOr(false),
1002
1007
  userEntitiesByName,
@@ -698,6 +698,7 @@ function fromPublic(publicConfigJson) {
698
698
  enableRawEvents: Stdlib_Option.getOr(publicConfig.rawEvents, false),
699
699
  maxAddrInPartition: Env.maxAddrInPartition,
700
700
  batchSize: Stdlib_Option.getOr(publicConfig.fullBatchSize, 5000),
701
+ reorgThresholdReadyTolerance: 100,
701
702
  lowercaseAddresses: lowercaseAddresses,
702
703
  isDev: Stdlib_Option.getOr(publicConfig.isDev, false),
703
704
  userEntitiesByName: userEntitiesByName,