envio 3.11.0-svm-alpha.1 → 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.
- package/package.json +6 -6
- package/src/Batch.res +11 -0
- package/src/Batch.res.mjs +1 -0
- package/src/ChainState.res +35 -5
- package/src/ChainState.res.mjs +25 -5
- package/src/ChainState.resi +2 -0
- package/src/CrossChainState.res +10 -3
- package/src/CrossChainState.res.mjs +1 -1
- package/src/Metrics.res +8 -0
- package/src/Metrics.res.mjs +1 -0
- package/src/Persistence.res +1 -0
- package/src/PgStorage.res +3 -0
- package/src/PgStorage.res.mjs +3 -0
- package/src/Rollback.res +1 -0
- package/src/Rollback.res.mjs +1 -0
- package/src/Staging.res +298 -0
- package/src/Staging.res.mjs +257 -0
- package/src/TestIndexer.res +1 -0
- package/src/TestIndexer.res.mjs +1 -0
- package/src/bindings/ClickHouse.res +19 -28
- package/src/bindings/ClickHouse.res.mjs +18 -22
- package/src/bindings/ClickHouseSink.res +29 -181
- package/src/bindings/ClickHouseSink.res.mjs +18 -173
- package/src/db/InternalTable.res +34 -7
- package/src/db/InternalTable.res.mjs +22 -2
- package/src/sources/BlockStore.res +3 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "envio",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.12.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
|
|
6
6
|
"bin": "./bin.mjs",
|
|
@@ -68,10 +68,10 @@
|
|
|
68
68
|
"tsx": "4.21.0"
|
|
69
69
|
},
|
|
70
70
|
"optionalDependencies": {
|
|
71
|
-
"envio-linux-x64": "3.
|
|
72
|
-
"envio-linux-x64-musl": "3.
|
|
73
|
-
"envio-linux-arm64": "3.
|
|
74
|
-
"envio-darwin-x64": "3.
|
|
75
|
-
"envio-darwin-arm64": "3.
|
|
71
|
+
"envio-linux-x64": "3.12.0",
|
|
72
|
+
"envio-linux-x64-musl": "3.12.0",
|
|
73
|
+
"envio-linux-arm64": "3.12.0",
|
|
74
|
+
"envio-darwin-x64": "3.12.0",
|
|
75
|
+
"envio-darwin-arm64": "3.12.0"
|
|
76
76
|
}
|
|
77
77
|
}
|
package/src/Batch.res
CHANGED
|
@@ -4,6 +4,12 @@ open Utils.UnsafeIntOperators
|
|
|
4
4
|
type chainAfterBatch = {
|
|
5
5
|
batchSize: int,
|
|
6
6
|
progressBlockNumber: int,
|
|
7
|
+
// Timestamp of `progressBlockNumber` itself, never of a lower block, so it
|
|
8
|
+
// measures how far behind chain time the committed progress is. `None` when
|
|
9
|
+
// the source didn't return that block's header - during backfill it only
|
|
10
|
+
// returns blocks carrying items, and an SVM slot may have produced no block
|
|
11
|
+
// at all.
|
|
12
|
+
progressBlockTime: option<int>,
|
|
7
13
|
sourceBlockNumber: int,
|
|
8
14
|
totalEventsProcessed: float,
|
|
9
15
|
fetchState: FetchState.t,
|
|
@@ -23,6 +29,10 @@ type reorgHashSnapshot = {
|
|
|
23
29
|
type chainBeforeBatch = {
|
|
24
30
|
fetchState: FetchState.t,
|
|
25
31
|
scannedHashes: reorgHashSnapshot,
|
|
32
|
+
// The progress block isn't known until the build computes it, so its
|
|
33
|
+
// timestamp can't be snapshotted with the rest. Reads the chain's store,
|
|
34
|
+
// which the synchronous build can't see mutate.
|
|
35
|
+
blockTimeAt: int => option<int>,
|
|
26
36
|
shouldRollbackOnReorg: bool,
|
|
27
37
|
progressBlockNumber: int,
|
|
28
38
|
sourceBlockNumber: int,
|
|
@@ -70,6 +80,7 @@ let getProgressedChainsById = {
|
|
|
70
80
|
{
|
|
71
81
|
batchSize: counts.size,
|
|
72
82
|
progressBlockNumber: progressBlockNumberAfterBatch,
|
|
83
|
+
progressBlockTime: chainBeforeBatch.blockTimeAt(progressBlockNumberAfterBatch),
|
|
73
84
|
sourceBlockNumber: chainBeforeBatch.sourceBlockNumber,
|
|
74
85
|
totalEventsProcessed: chainBeforeBatch.totalEventsProcessed +.
|
|
75
86
|
counts.eventsProcessed->Int.toFloat,
|
package/src/Batch.res.mjs
CHANGED
|
@@ -10,6 +10,7 @@ function getChainAfterBatchIfProgressed(chainBeforeBatch, progressBlockNumberAft
|
|
|
10
10
|
return {
|
|
11
11
|
batchSize: counts.size,
|
|
12
12
|
progressBlockNumber: progressBlockNumberAfterBatch,
|
|
13
|
+
progressBlockTime: chainBeforeBatch.blockTimeAt(progressBlockNumberAfterBatch),
|
|
13
14
|
sourceBlockNumber: chainBeforeBatch.sourceBlockNumber,
|
|
14
15
|
totalEventsProcessed: chainBeforeBatch.totalEventsProcessed + counts.eventsProcessed,
|
|
15
16
|
fetchState: fetchStateAfterBatch,
|
package/src/ChainState.res
CHANGED
|
@@ -62,6 +62,9 @@ type t = {
|
|
|
62
62
|
mutable reorgDetectedBlock: option<int>,
|
|
63
63
|
mutable rollbackTargetBlock: option<int>,
|
|
64
64
|
mutable progressLatencyMs: option<int>,
|
|
65
|
+
// Timestamp of the committed progress block itself. See
|
|
66
|
+
// `Batch.chainAfterBatch.progressBlockTime`.
|
|
67
|
+
mutable committedProgressBlockTime: option<int>,
|
|
65
68
|
}
|
|
66
69
|
|
|
67
70
|
// The chain's config-declared addresses as storage rows: what a clean run
|
|
@@ -125,6 +128,7 @@ let make = (
|
|
|
125
128
|
~addressStore: AddressStore.t,
|
|
126
129
|
~sourceManager: SourceManager.t,
|
|
127
130
|
~committedProgressBlockNumber: int,
|
|
131
|
+
~committedProgressBlockTime=None,
|
|
128
132
|
~safeCheckpointTracking=None,
|
|
129
133
|
~shouldRollbackOnReorg,
|
|
130
134
|
~maxReorgDepth,
|
|
@@ -171,6 +175,7 @@ let make = (
|
|
|
171
175
|
reorgDetectedBlock: None,
|
|
172
176
|
rollbackTargetBlock: None,
|
|
173
177
|
progressLatencyMs: None,
|
|
178
|
+
committedProgressBlockTime,
|
|
174
179
|
}
|
|
175
180
|
}
|
|
176
181
|
|
|
@@ -184,6 +189,7 @@ let makeInternal = (
|
|
|
184
189
|
~endBlock,
|
|
185
190
|
~firstEventBlock=None,
|
|
186
191
|
~progressBlockNumber,
|
|
192
|
+
~committedProgressBlockTime=None,
|
|
187
193
|
~config: Config.t,
|
|
188
194
|
~registrationsByChainId: HandlerRegister.registrationsByChainId,
|
|
189
195
|
~logger,
|
|
@@ -319,6 +325,7 @@ let makeInternal = (
|
|
|
319
325
|
~chainReorgCheckpoints,
|
|
320
326
|
),
|
|
321
327
|
~committedProgressBlockNumber=progressBlockNumber,
|
|
328
|
+
~committedProgressBlockTime,
|
|
322
329
|
~perChainEntities=config.userEntities->EntityTables.perChain,
|
|
323
330
|
~timestampCaughtUpToHeadOrEndblock,
|
|
324
331
|
~numEventsProcessed,
|
|
@@ -368,6 +375,10 @@ let makeFromDbState = (
|
|
|
368
375
|
~maxReorgDepth=resumedChainState.maxReorgDepth,
|
|
369
376
|
~firstEventBlock=resumedChainState.firstEventBlockNumber,
|
|
370
377
|
~progressBlockNumber,
|
|
378
|
+
// A block's time never changes, so the stored one stays true for the block
|
|
379
|
+
// progress is at - and keeps the metric reporting from the first scrape
|
|
380
|
+
// after a restart, rather than only once a batch commits.
|
|
381
|
+
~committedProgressBlockTime=resumedChainState.progressBlockTime,
|
|
371
382
|
~timestampCaughtUpToHeadOrEndblock=resumedChainState.timestampCaughtUpToHeadOrEndblock,
|
|
372
383
|
~numEventsProcessed=resumedChainState.numEventsProcessed,
|
|
373
384
|
~logger,
|
|
@@ -407,6 +418,7 @@ let getLatestValidScannedBlock = (cs: t, ~blockStore: BlockStore.t, ~blockNumber
|
|
|
407
418
|
let safeCheckpointTracking = (cs: t) => cs.safeCheckpointTracking
|
|
408
419
|
let isProgressAtHead = (cs: t) => cs.isProgressAtHead
|
|
409
420
|
let committedProgressBlockNumber = (cs: t) => cs.committedProgressBlockNumber
|
|
421
|
+
let committedProgressBlockTime = (cs: t) => cs.committedProgressBlockTime
|
|
410
422
|
let numEventsProcessed = (cs: t) => cs.numEventsProcessed
|
|
411
423
|
let pendingBudget = (cs: t) => cs.pendingBudget
|
|
412
424
|
let timestampCaughtUpToHeadOrEndblock = (cs: t) => cs.timestampCaughtUpToHeadOrEndblock
|
|
@@ -985,6 +997,7 @@ let toMetrics = (cs: t): Metrics.chainMetrics => {
|
|
|
985
997
|
sourceBlockNumber: cs.fetchState.knownHeight,
|
|
986
998
|
progressBlockNumber: cs.committedProgressBlockNumber,
|
|
987
999
|
progressLatencyMs: cs.progressLatencyMs,
|
|
1000
|
+
progressBlockTime: cs.committedProgressBlockTime,
|
|
988
1001
|
concurrency: cs.sourceManager->SourceManager.inFlightCount,
|
|
989
1002
|
partitionsCount: cs.fetchState->FetchState.partitionsCount,
|
|
990
1003
|
bufferSize: cs.fetchState->FetchState.bufferSize,
|
|
@@ -1004,9 +1017,10 @@ let toMetrics = (cs: t): Metrics.chainMetrics => {
|
|
|
1004
1017
|
rateLimitResetInMs: cs.sourceManager->SourceManager.getRateLimitResetInMs,
|
|
1005
1018
|
}
|
|
1006
1019
|
|
|
1007
|
-
// Snapshot the inputs a batch build needs from this chain
|
|
1008
|
-
//
|
|
1009
|
-
//
|
|
1020
|
+
// Snapshot the inputs a batch build needs from this chain. The scanned
|
|
1021
|
+
// in-threshold block hashes are copied up front because the build reuses the
|
|
1022
|
+
// whole set; the block time comes as a lookup instead, since the key it needs -
|
|
1023
|
+
// the batch's own progress block - isn't known until the build computes it.
|
|
1010
1024
|
let toChainBeforeBatch = (cs: t): Batch.chainBeforeBatch => {
|
|
1011
1025
|
let {blockNumbers, hashes} =
|
|
1012
1026
|
cs.blockStore->BlockStore.getHashes(
|
|
@@ -1023,6 +1037,8 @@ let toChainBeforeBatch = (cs: t): Batch.chainBeforeBatch => {
|
|
|
1023
1037
|
totalEventsProcessed: cs.numEventsProcessed,
|
|
1024
1038
|
sourceBlockNumber: cs.fetchState.knownHeight,
|
|
1025
1039
|
scannedHashes: {blockNumbers, hashByBlockNumber},
|
|
1040
|
+
blockTimeAt: blockNumber =>
|
|
1041
|
+
cs.blockStore->BlockStore.getTimestamp(blockNumber)->Null.toOption,
|
|
1026
1042
|
shouldRollbackOnReorg: cs.shouldRollbackOnReorg,
|
|
1027
1043
|
chainConfig: cs.chainConfig,
|
|
1028
1044
|
}
|
|
@@ -1122,6 +1138,7 @@ let applyBatchProgress = (cs: t, ~batch: Batch.t, ~blockTimestampName: string) =
|
|
|
1122
1138
|
}
|
|
1123
1139
|
|
|
1124
1140
|
cs.committedProgressBlockNumber = chainAfterBatch.progressBlockNumber
|
|
1141
|
+
cs.committedProgressBlockTime = chainAfterBatch.progressBlockTime
|
|
1125
1142
|
|
|
1126
1143
|
// Normally already set by advanceAfterBatch at batch creation; catch up
|
|
1127
1144
|
// here for paths that commit progress without it.
|
|
@@ -1168,6 +1185,19 @@ let markReady = (cs: t, ~readyAt) =>
|
|
|
1168
1185
|
cs.timestampCaughtUpToHeadOrEndblock = Some(readyAt)
|
|
1169
1186
|
}
|
|
1170
1187
|
|
|
1188
|
+
// Rewind progress to a reorg target. Moving it means the timestamp now belongs
|
|
1189
|
+
// to an orphaned block, so it's re-read for the block progress actually lands
|
|
1190
|
+
// on - normally `None`, since the store keeps only hashes below the last
|
|
1191
|
+
// committed progress, until the re-fetch commits a new progress block. A target
|
|
1192
|
+
// at or above the progress block leaves both alone: that block was never
|
|
1193
|
+
// orphaned, and re-reading its long-pruned header would drop a valid timestamp.
|
|
1194
|
+
let rollbackCommittedProgress = (cs: t, blockNumber) =>
|
|
1195
|
+
if blockNumber !== cs.committedProgressBlockNumber {
|
|
1196
|
+
cs.committedProgressBlockNumber = blockNumber
|
|
1197
|
+
cs.committedProgressBlockTime =
|
|
1198
|
+
cs.blockStore->BlockStore.getTimestamp(blockNumber)->Null.toOption
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1171
1201
|
type progressDiff = {blockNumber: int, eventsProcessed: float}
|
|
1172
1202
|
|
|
1173
1203
|
type rolledBackTo =
|
|
@@ -1215,7 +1245,7 @@ let rollback = (cs: t, ~rolledBackTo: rolledBackTo): array<AddressRows.key> => {
|
|
|
1215
1245
|
let rolledBackAddresses = rollbackTo(newProgressBlockNumber)
|
|
1216
1246
|
cs.transactionStore->TransactionStore.rollback(newProgressBlockNumber)
|
|
1217
1247
|
cs.blockStore->BlockStore.rollback(newProgressBlockNumber)
|
|
1218
|
-
cs
|
|
1248
|
+
cs->rollbackCommittedProgress(newProgressBlockNumber)
|
|
1219
1249
|
cs.processingBlockNumber = newProgressBlockNumber
|
|
1220
1250
|
cs.numEventsProcessed = newTotalEventsProcessed
|
|
1221
1251
|
rolledBackAddresses
|
|
@@ -1225,7 +1255,7 @@ let rollback = (cs: t, ~rolledBackTo: rolledBackTo): array<AddressRows.key> => {
|
|
|
1225
1255
|
let rolledBackAddresses = rollbackTo(forkBlock)
|
|
1226
1256
|
cs.transactionStore->TransactionStore.rollback(forkBlock)
|
|
1227
1257
|
cs.blockStore->BlockStore.rollback(forkBlock)
|
|
1228
|
-
cs
|
|
1258
|
+
cs->rollbackCommittedProgress(Pervasives.min(cs.committedProgressBlockNumber, forkBlock))
|
|
1229
1259
|
cs.processingBlockNumber = Pervasives.min(cs.processingBlockNumber, forkBlock)
|
|
1230
1260
|
rolledBackAddresses
|
|
1231
1261
|
| Untouched => []
|
package/src/ChainState.res.mjs
CHANGED
|
@@ -55,8 +55,9 @@ function validateOnEventRegistrations(chainId, registrations) {
|
|
|
55
55
|
});
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
function make(chainConfig, fetchState, onEventRegistrationsOpt, addressStore, sourceManager, committedProgressBlockNumber, safeCheckpointTrackingOpt, shouldRollbackOnReorg, maxReorgDepth, isInReorgThresholdOpt, numEventsProcessedOpt, timestampCaughtUpToHeadOrEndblockOpt, isProgressAtHeadOpt, transactionStoreOpt, chainDensityOpt, blockStoreOpt, reorgThresholdReadyToleranceOpt, perChainEntitiesOpt, logger) {
|
|
58
|
+
function make(chainConfig, fetchState, onEventRegistrationsOpt, addressStore, sourceManager, committedProgressBlockNumber, committedProgressBlockTimeOpt, safeCheckpointTrackingOpt, shouldRollbackOnReorg, maxReorgDepth, isInReorgThresholdOpt, numEventsProcessedOpt, timestampCaughtUpToHeadOrEndblockOpt, isProgressAtHeadOpt, transactionStoreOpt, chainDensityOpt, blockStoreOpt, reorgThresholdReadyToleranceOpt, perChainEntitiesOpt, logger) {
|
|
59
59
|
let onEventRegistrations = onEventRegistrationsOpt !== undefined ? onEventRegistrationsOpt : [];
|
|
60
|
+
let committedProgressBlockTime = committedProgressBlockTimeOpt !== undefined ? Primitive_option.valFromOption(committedProgressBlockTimeOpt) : undefined;
|
|
60
61
|
let safeCheckpointTracking = safeCheckpointTrackingOpt !== undefined ? Primitive_option.valFromOption(safeCheckpointTrackingOpt) : undefined;
|
|
61
62
|
let isInReorgThreshold = isInReorgThresholdOpt !== undefined ? isInReorgThresholdOpt : false;
|
|
62
63
|
let numEventsProcessed = numEventsProcessedOpt !== undefined ? numEventsProcessedOpt : 0;
|
|
@@ -98,7 +99,8 @@ function make(chainConfig, fetchState, onEventRegistrationsOpt, addressStore, so
|
|
|
98
99
|
reorgCount: 0,
|
|
99
100
|
reorgDetectedBlock: undefined,
|
|
100
101
|
rollbackTargetBlock: undefined,
|
|
101
|
-
progressLatencyMs: undefined
|
|
102
|
+
progressLatencyMs: undefined,
|
|
103
|
+
committedProgressBlockTime: committedProgressBlockTime
|
|
102
104
|
};
|
|
103
105
|
}
|
|
104
106
|
|
|
@@ -112,12 +114,14 @@ function makeFromDbState(chainConfig, resumedChainState, reorgCheckpoints, isInR
|
|
|
112
114
|
let startBlock = resumedChainState.startBlock;
|
|
113
115
|
let endBlock = resumedChainState.endBlock;
|
|
114
116
|
let firstEventBlockOpt = Primitive_option.some(resumedChainState.firstEventBlockNumber);
|
|
117
|
+
let committedProgressBlockTimeOpt = Primitive_option.some(resumedChainState.progressBlockTime);
|
|
115
118
|
let timestampCaughtUpToHeadOrEndblock = resumedChainState.timestampCaughtUpToHeadOrEndblock;
|
|
116
119
|
let numEventsProcessed = resumedChainState.numEventsProcessed;
|
|
117
120
|
let maxReorgDepth = resumedChainState.maxReorgDepth;
|
|
118
121
|
let knownHeightOpt = resumedChainState.sourceBlockNumber;
|
|
119
122
|
let isResumedOpt = true;
|
|
120
123
|
let firstEventBlock = firstEventBlockOpt !== undefined ? Primitive_option.valFromOption(firstEventBlockOpt) : undefined;
|
|
124
|
+
let committedProgressBlockTime = committedProgressBlockTimeOpt !== undefined ? Primitive_option.valFromOption(committedProgressBlockTimeOpt) : undefined;
|
|
121
125
|
let knownHeight = knownHeightOpt !== undefined ? knownHeightOpt : 0;
|
|
122
126
|
let isResumed = isResumedOpt !== undefined ? isResumedOpt : false;
|
|
123
127
|
let match = Stdlib_Option.getOr(registrationsByChainId[ChainId.toString(chainConfig.id)], {
|
|
@@ -161,7 +165,7 @@ function makeFromDbState(chainConfig, resumedChainState, reorgCheckpoints, isInR
|
|
|
161
165
|
}
|
|
162
166
|
let firstEventBlock$1 = fetchState.firstEventBlock;
|
|
163
167
|
let chainDensity = firstEventBlock$1 !== undefined && progressBlockNumber > firstEventBlock$1 && numEventsProcessed > 0 ? numEventsProcessed / (progressBlockNumber - firstEventBlock$1 | 0) : undefined;
|
|
164
|
-
return make(chainConfig, fetchState, onEventRegistrations, addressStore, SourceManager.make(sources, isRealtime, undefined, undefined, undefined, reducedPollingInterval, undefined, undefined), progressBlockNumber, Primitive_option.some(SafeCheckpointTracking.make(maxReorgDepth, config.shouldRollbackOnReorg, chainReorgCheckpoints)), config.shouldRollbackOnReorg, maxReorgDepth, isInReorgThreshold, numEventsProcessed, Primitive_option.some(timestampCaughtUpToHeadOrEndblock), undefined, Primitive_option.some(TransactionStore.make(config.ecosystem.name, !lowercaseAddresses)), Primitive_option.some(chainDensity), Primitive_option.some(blockStore), config.reorgThresholdReadyTolerance, EntityTables.perChain(config.userEntities), logger);
|
|
168
|
+
return make(chainConfig, fetchState, onEventRegistrations, addressStore, SourceManager.make(sources, isRealtime, undefined, undefined, undefined, reducedPollingInterval, undefined, undefined), progressBlockNumber, Primitive_option.some(committedProgressBlockTime), Primitive_option.some(SafeCheckpointTracking.make(maxReorgDepth, config.shouldRollbackOnReorg, chainReorgCheckpoints)), config.shouldRollbackOnReorg, maxReorgDepth, isInReorgThreshold, numEventsProcessed, Primitive_option.some(timestampCaughtUpToHeadOrEndblock), undefined, Primitive_option.some(TransactionStore.make(config.ecosystem.name, !lowercaseAddresses)), Primitive_option.some(chainDensity), Primitive_option.some(blockStore), config.reorgThresholdReadyTolerance, EntityTables.perChain(config.userEntities), logger);
|
|
165
169
|
}
|
|
166
170
|
|
|
167
171
|
function logger(cs) {
|
|
@@ -212,6 +216,10 @@ function committedProgressBlockNumber(cs) {
|
|
|
212
216
|
return cs.committedProgressBlockNumber;
|
|
213
217
|
}
|
|
214
218
|
|
|
219
|
+
function committedProgressBlockTime(cs) {
|
|
220
|
+
return cs.committedProgressBlockTime;
|
|
221
|
+
}
|
|
222
|
+
|
|
215
223
|
function numEventsProcessed(cs) {
|
|
216
224
|
return cs.numEventsProcessed;
|
|
217
225
|
}
|
|
@@ -689,6 +697,7 @@ function toMetrics(cs) {
|
|
|
689
697
|
sourceBlockNumber: cs.fetchState.knownHeight,
|
|
690
698
|
progressBlockNumber: cs.committedProgressBlockNumber,
|
|
691
699
|
progressLatencyMs: cs.progressLatencyMs,
|
|
700
|
+
progressBlockTime: cs.committedProgressBlockTime,
|
|
692
701
|
concurrency: SourceManager.inFlightCount(cs.sourceManager),
|
|
693
702
|
partitionsCount: FetchState.partitionsCount(cs.fetchState),
|
|
694
703
|
bufferSize: FetchState.bufferSize(cs.fetchState),
|
|
@@ -723,6 +732,7 @@ function toChainBeforeBatch(cs) {
|
|
|
723
732
|
blockNumbers: blockNumbers,
|
|
724
733
|
hashByBlockNumber: hashByBlockNumber
|
|
725
734
|
},
|
|
735
|
+
blockTimeAt: blockNumber => Primitive_option.fromNull(cs.blockStore.getTimestamp(blockNumber)),
|
|
726
736
|
shouldRollbackOnReorg: cs.shouldRollbackOnReorg,
|
|
727
737
|
progressBlockNumber: cs.committedProgressBlockNumber,
|
|
728
738
|
sourceBlockNumber: cs.fetchState.knownHeight,
|
|
@@ -802,6 +812,7 @@ function applyBatchProgress(cs, batch, blockTimestampName) {
|
|
|
802
812
|
}
|
|
803
813
|
}
|
|
804
814
|
cs.committedProgressBlockNumber = chainAfterBatch.progressBlockNumber;
|
|
815
|
+
cs.committedProgressBlockTime = chainAfterBatch.progressBlockTime;
|
|
805
816
|
cs.processingBlockNumber = Primitive_int.max(cs.processingBlockNumber, chainAfterBatch.progressBlockNumber);
|
|
806
817
|
cs.numEventsProcessed = chainAfterBatch.totalEventsProcessed;
|
|
807
818
|
cs.transactionStore.prune(chainAfterBatch.progressBlockNumber);
|
|
@@ -821,6 +832,14 @@ function markReady(cs, readyAt) {
|
|
|
821
832
|
}
|
|
822
833
|
}
|
|
823
834
|
|
|
835
|
+
function rollbackCommittedProgress(cs, blockNumber) {
|
|
836
|
+
if (blockNumber !== cs.committedProgressBlockNumber) {
|
|
837
|
+
cs.committedProgressBlockNumber = blockNumber;
|
|
838
|
+
cs.committedProgressBlockTime = Primitive_option.fromNull(cs.blockStore.getTimestamp(blockNumber));
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
824
843
|
function rollback(cs, rolledBackTo) {
|
|
825
844
|
let rollbackTo = targetBlockNumber => {
|
|
826
845
|
let match = FetchState.rollback(cs.fetchState, cs.addressStore, targetBlockNumber);
|
|
@@ -845,7 +864,7 @@ function rollback(cs, rolledBackTo) {
|
|
|
845
864
|
let rolledBackAddresses = rollbackTo(newProgressBlockNumber);
|
|
846
865
|
cs.transactionStore.rollback(newProgressBlockNumber);
|
|
847
866
|
cs.blockStore.rollback(newProgressBlockNumber);
|
|
848
|
-
cs
|
|
867
|
+
rollbackCommittedProgress(cs, newProgressBlockNumber);
|
|
849
868
|
cs.processingBlockNumber = newProgressBlockNumber;
|
|
850
869
|
cs.numEventsProcessed = newTotalEventsProcessed;
|
|
851
870
|
return rolledBackAddresses;
|
|
@@ -854,7 +873,7 @@ function rollback(cs, rolledBackTo) {
|
|
|
854
873
|
let rolledBackAddresses$1 = rollbackTo(forkBlock);
|
|
855
874
|
cs.transactionStore.rollback(forkBlock);
|
|
856
875
|
cs.blockStore.rollback(forkBlock);
|
|
857
|
-
cs
|
|
876
|
+
rollbackCommittedProgress(cs, Primitive_int.min(cs.committedProgressBlockNumber, forkBlock));
|
|
858
877
|
cs.processingBlockNumber = Primitive_int.min(cs.processingBlockNumber, forkBlock);
|
|
859
878
|
return rolledBackAddresses$1;
|
|
860
879
|
}
|
|
@@ -876,6 +895,7 @@ export {
|
|
|
876
895
|
safeCheckpointTracking,
|
|
877
896
|
isProgressAtHead,
|
|
878
897
|
committedProgressBlockNumber,
|
|
898
|
+
committedProgressBlockTime,
|
|
879
899
|
numEventsProcessed,
|
|
880
900
|
pendingBudget,
|
|
881
901
|
startFetchingQueries,
|
package/src/ChainState.resi
CHANGED
|
@@ -23,6 +23,7 @@ let make: (
|
|
|
23
23
|
~addressStore: AddressStore.t,
|
|
24
24
|
~sourceManager: SourceManager.t,
|
|
25
25
|
~committedProgressBlockNumber: int,
|
|
26
|
+
~committedProgressBlockTime: option<int>=?,
|
|
26
27
|
~safeCheckpointTracking: option<SafeCheckpointTracking.t>=?,
|
|
27
28
|
~shouldRollbackOnReorg: bool,
|
|
28
29
|
~maxReorgDepth: int,
|
|
@@ -68,6 +69,7 @@ let getLatestValidScannedBlock: (
|
|
|
68
69
|
let safeCheckpointTracking: t => option<SafeCheckpointTracking.t>
|
|
69
70
|
let isProgressAtHead: t => bool
|
|
70
71
|
let committedProgressBlockNumber: t => int
|
|
72
|
+
let committedProgressBlockTime: t => option<int>
|
|
71
73
|
let numEventsProcessed: t => float
|
|
72
74
|
let pendingBudget: t => float
|
|
73
75
|
let startFetchingQueries: (t, ~queries: array<FetchState.query>) => unit
|
package/src/CrossChainState.res
CHANGED
|
@@ -257,6 +257,10 @@ let idleOrWaitAction = (cs: ChainState.t) =>
|
|
|
257
257
|
? FetchState.NothingToQuery
|
|
258
258
|
: FetchState.WaitingForNewBlock
|
|
259
259
|
|
|
260
|
+
// How far past the alignment anchor's frontier progress a follower may fetch,
|
|
261
|
+
// as a fraction of its own alignable range.
|
|
262
|
+
let alignmentMargin = 0.2
|
|
263
|
+
|
|
260
264
|
// Dispatch a fetch tick across the whole indexer from one shared pool of
|
|
261
265
|
// ~targetBufferSize ready events, as a waterfall: visit chains furthest-behind
|
|
262
266
|
// first, hand each the budget remaining at that point (plus its own
|
|
@@ -340,11 +344,14 @@ let checkAndFetch = async (
|
|
|
340
344
|
(isCold ? Pervasives.min(remaining.contents, coldChainBudget) : remaining.contents) +.
|
|
341
345
|
cs->ChainState.pendingBudget
|
|
342
346
|
let maxTargetBlock = switch alignment {
|
|
343
|
-
//
|
|
347
|
+
// 20% margin past the anchor's line: chains whose progress tracks the
|
|
344
348
|
// anchor closely would otherwise flap in and out of the clamp on every
|
|
345
|
-
// small frontier move, stalling their pipeline every other tick.
|
|
349
|
+
// small frontier move, stalling their pipeline every other tick. The
|
|
350
|
+
// margin is also the headroom a follower keeps buffered while the anchor
|
|
351
|
+
// is mid-fetch, so it has to outlast a slow anchor response, not just
|
|
352
|
+
// absorb frontier jitter.
|
|
346
353
|
| Some((anchorChainId, progress)) if anchorChainId !== chainId =>
|
|
347
|
-
Some(cs->ChainState.blockAtProgress(~progress=progress +.
|
|
354
|
+
Some(cs->ChainState.blockAtProgress(~progress=progress +. alignmentMargin))
|
|
348
355
|
| _ => None
|
|
349
356
|
}
|
|
350
357
|
switch cs->ChainState.getNextQuery(~chainTargetItems, ~maxTargetBlock?) {
|
|
@@ -199,7 +199,7 @@ async function checkAndFetch(crossChainState, dispatchChain) {
|
|
|
199
199
|
let chainTargetItems = (
|
|
200
200
|
isCold ? Primitive_float.min(remaining.contents, minimumAdmissionBudget) : remaining.contents
|
|
201
201
|
) + ChainState.pendingBudget(cs);
|
|
202
|
-
let maxTargetBlock = alignment !== undefined && alignment[0] !== chainId ? ChainState.blockAtProgress(cs, alignment[1] + 0.
|
|
202
|
+
let maxTargetBlock = alignment !== undefined && alignment[0] !== chainId ? ChainState.blockAtProgress(cs, alignment[1] + 0.2) : undefined;
|
|
203
203
|
let action = ChainState.getNextQuery(cs, chainTargetItems, maxTargetBlock);
|
|
204
204
|
if (typeof action !== "object") {
|
|
205
205
|
if (action === "WaitingForNewBlock") {
|
package/src/Metrics.res
CHANGED
|
@@ -20,6 +20,7 @@ type chainMetrics = {
|
|
|
20
20
|
// Raw committed progress (may be -1), unlike the optional latestProcessedBlock.
|
|
21
21
|
progressBlockNumber: int,
|
|
22
22
|
progressLatencyMs: option<int>,
|
|
23
|
+
progressBlockTime: option<int>,
|
|
23
24
|
concurrency: int,
|
|
24
25
|
partitionsCount: int,
|
|
25
26
|
bufferSize: int,
|
|
@@ -659,6 +660,13 @@ let renderMetrics = (b: builder, metrics: t) => {
|
|
|
659
660
|
~entries=chains,
|
|
660
661
|
~value=m => m.numEventsProcessed,
|
|
661
662
|
)
|
|
663
|
+
b->seriesOpt(
|
|
664
|
+
~name="envio_progress_block_time_seconds",
|
|
665
|
+
~help="Unix timestamp of the block the chain has processed up to. Subtract it from the scrape time for how far behind chain time the indexer is, which stays honest when the data source itself is behind the chain. Best effort in realtime mode, absent during backfill.",
|
|
666
|
+
~kind="gauge",
|
|
667
|
+
~entries=chains,
|
|
668
|
+
~value=m => m.progressBlockTime->Option.map(Int.toFloat),
|
|
669
|
+
)
|
|
662
670
|
b->seriesOpt(
|
|
663
671
|
~name="envio_progress_latency",
|
|
664
672
|
~help="The latency in milliseconds between the latest processed event creation and the time it was written to storage.",
|
package/src/Metrics.res.mjs
CHANGED
|
@@ -221,6 +221,7 @@ function renderMetrics(b, metrics) {
|
|
|
221
221
|
single(b, "envio_processing_max_batch_size", "The maximum number of items to process in a single batch.", "gauge", metrics.maxBatchSize);
|
|
222
222
|
series(b, "envio_progress_block", "The block number of the latest block processed and stored in the database.", "gauge", chains, m => m.progressBlockNumber);
|
|
223
223
|
series(b, "envio_progress_events", "The number of events processed and reflected in the database.", "gauge", chains, m => m.numEventsProcessed);
|
|
224
|
+
seriesOpt(b, "envio_progress_block_time_seconds", "Unix timestamp of the block the chain has processed up to. Subtract it from the scrape time for how far behind chain time the indexer is, which stays honest when the data source itself is behind the chain. Best effort in realtime mode, absent during backfill.", "gauge", chains, m => Stdlib_Option.map(m.progressBlockTime, prim => prim));
|
|
224
225
|
seriesOpt(b, "envio_progress_latency", "The latency in milliseconds between the latest processed event creation and the time it was written to storage.", "gauge", chains, m => Stdlib_Option.map(m.progressLatencyMs, prim => prim));
|
|
225
226
|
let ifCalled = (s, value) => {
|
|
226
227
|
if (s.callCount > 0 || s.activeCallsCount > 0) {
|
package/src/Persistence.res
CHANGED
|
@@ -24,6 +24,7 @@ type initialChainState = {
|
|
|
24
24
|
endBlock: option<int>,
|
|
25
25
|
maxReorgDepth: int,
|
|
26
26
|
progressBlockNumber: int,
|
|
27
|
+
progressBlockTime: option<int>,
|
|
27
28
|
numEventsProcessed: float,
|
|
28
29
|
firstEventBlockNumber: option<int>,
|
|
29
30
|
timestampCaughtUpToHeadOrEndblock: option<Date.t>,
|
package/src/PgStorage.res
CHANGED
|
@@ -1449,6 +1449,7 @@ let rec writeBatch = async (
|
|
|
1449
1449
|
): InternalTable.Chains.progressedChain => {
|
|
1450
1450
|
chainId: chainAfterBatch.fetchState.chainId,
|
|
1451
1451
|
progressBlockNumber: chainAfterBatch.progressBlockNumber,
|
|
1452
|
+
progressBlockTime: chainAfterBatch.progressBlockTime,
|
|
1452
1453
|
sourceBlockNumber: chainAfterBatch.sourceBlockNumber,
|
|
1453
1454
|
totalEventsProcessed: chainAfterBatch.totalEventsProcessed,
|
|
1454
1455
|
}),
|
|
@@ -1992,6 +1993,7 @@ let make = (
|
|
|
1992
1993
|
firstEventBlockNumber: None,
|
|
1993
1994
|
timestampCaughtUpToHeadOrEndblock: None,
|
|
1994
1995
|
addressRows: rowsByChain->Array.getUnsafe(idx)->AddressRows.seedRowsOf,
|
|
1996
|
+
progressBlockTime: None,
|
|
1995
1997
|
sourceBlockNumber: 0,
|
|
1996
1998
|
}),
|
|
1997
1999
|
checkpointFrontier: Frontier.empty(),
|
|
@@ -2427,6 +2429,7 @@ let make = (
|
|
|
2427
2429
|
timestampCaughtUpToHeadOrEndblock: rawInitialState.timestampCaughtUpToHeadOrEndblock->Null.toOption,
|
|
2428
2430
|
numEventsProcessed: rawInitialState.numEventsProcessed,
|
|
2429
2431
|
progressBlockNumber: rawInitialState.progressBlockNumber,
|
|
2432
|
+
progressBlockTime: rawInitialState.progressBlockTime->InternalTable.Chains.blockTimeFromDb,
|
|
2430
2433
|
addressRows: rawInitialState.addressRows,
|
|
2431
2434
|
sourceBlockNumber: rawInitialState.sourceBlockNumber,
|
|
2432
2435
|
}),
|
package/src/PgStorage.res.mjs
CHANGED
|
@@ -914,6 +914,7 @@ async function writeBatch(sql, batch, pgSchema, rollback, config, allEntities, s
|
|
|
914
914
|
sql => InternalTable.Chains.setProgressedChains(sql, pgSchema, Utils.Dict.mapValuesToArray(batch.progressedChainsById, chainAfterBatch => ({
|
|
915
915
|
chainId: chainAfterBatch.fetchState.chainId,
|
|
916
916
|
progressBlockNumber: chainAfterBatch.progressBlockNumber,
|
|
917
|
+
progressBlockTime: chainAfterBatch.progressBlockTime,
|
|
917
918
|
sourceBlockNumber: chainAfterBatch.sourceBlockNumber,
|
|
918
919
|
totalEventsProcessed: chainAfterBatch.totalEventsProcessed
|
|
919
920
|
}))),
|
|
@@ -1208,6 +1209,7 @@ function make(sql, pgHost, pgSchema, pgPort, pgUser, pgDatabase, pgPassword, isH
|
|
|
1208
1209
|
endBlock: chainConfig.endBlock,
|
|
1209
1210
|
maxReorgDepth: chainConfig.maxReorgDepth,
|
|
1210
1211
|
progressBlockNumber: -1,
|
|
1212
|
+
progressBlockTime: undefined,
|
|
1211
1213
|
numEventsProcessed: 0,
|
|
1212
1214
|
firstEventBlockNumber: undefined,
|
|
1213
1215
|
timestampCaughtUpToHeadOrEndblock: undefined,
|
|
@@ -1480,6 +1482,7 @@ function make(sql, pgHost, pgSchema, pgPort, pgUser, pgDatabase, pgPassword, isH
|
|
|
1480
1482
|
endBlock: Primitive_option.fromNull(rawInitialState.endBlock),
|
|
1481
1483
|
maxReorgDepth: rawInitialState.maxReorgDepth,
|
|
1482
1484
|
progressBlockNumber: rawInitialState.progressBlockNumber,
|
|
1485
|
+
progressBlockTime: InternalTable.Chains.blockTimeFromDb(rawInitialState.progressBlockTime),
|
|
1483
1486
|
numEventsProcessed: rawInitialState.numEventsProcessed,
|
|
1484
1487
|
firstEventBlockNumber: Primitive_option.fromNull(rawInitialState.firstEventBlockNumber),
|
|
1485
1488
|
timestampCaughtUpToHeadOrEndblock: Primitive_option.fromNull(rawInitialState.timestampCaughtUpToHeadOrEndblock),
|
package/src/Rollback.res
CHANGED
|
@@ -200,6 +200,7 @@ and executeRollback = async (
|
|
|
200
200
|
->Array.push({
|
|
201
201
|
chainId,
|
|
202
202
|
progressBlockNumber: toBlock,
|
|
203
|
+
progressBlockTime: cs->ChainState.committedProgressBlockTime,
|
|
203
204
|
sourceBlockNumber: cs->ChainState.knownHeight,
|
|
204
205
|
totalEventsProcessed: cs->ChainState.numEventsProcessed,
|
|
205
206
|
})
|
package/src/Rollback.res.mjs
CHANGED
|
@@ -86,6 +86,7 @@ async function executeRollback(state, reorgChain, rollbackTargetBlockNumber, sch
|
|
|
86
86
|
rolledBackChains.push({
|
|
87
87
|
chainId: chainId,
|
|
88
88
|
progressBlockNumber: toBlock,
|
|
89
|
+
progressBlockTime: ChainState.committedProgressBlockTime(cs),
|
|
89
90
|
sourceBlockNumber: ChainState.knownHeight(cs),
|
|
90
91
|
totalEventsProcessed: ChainState.numEventsProcessed(cs)
|
|
91
92
|
});
|