envio 3.3.0 → 3.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -7
- package/src/ChainFetching.res +4 -32
- package/src/ChainFetching.res.mjs +2 -12
- package/src/ChainState.res +73 -53
- package/src/ChainState.res.mjs +61 -30
- package/src/ChainState.resi +13 -17
- package/src/Config.res +5 -0
- package/src/Config.res.mjs +1 -0
- package/src/CrossChainState.res +2 -8
- package/src/CrossChainState.res.mjs +6 -8
- package/src/CrossChainState.resi +1 -0
- package/src/EffectState.res +183 -44
- package/src/EffectState.res.mjs +134 -26
- package/src/EffectState.resi +26 -3
- package/src/EventProcessing.res +14 -9
- package/src/EventProcessing.res.mjs +7 -7
- package/src/FetchState.res +18 -29
- package/src/FetchState.res.mjs +20 -37
- package/src/IndexerState.res +280 -31
- package/src/IndexerState.res.mjs +234 -25
- package/src/IndexerState.resi +22 -0
- package/src/LoadLayer.res +19 -65
- package/src/LoadLayer.res.mjs +13 -55
- package/src/Main.res +42 -23
- package/src/Main.res.mjs +32 -35
- package/src/Metrics.res +943 -66
- package/src/Metrics.res.mjs +367 -50
- package/src/Persistence.res +3 -0
- package/src/PgStorage.res +3 -8
- package/src/PgStorage.res.mjs +3 -4
- package/src/PruneStaleHistory.res +1 -1
- package/src/PruneStaleHistory.res.mjs +1 -2
- package/src/Rollback.res +4 -5
- package/src/Rollback.res.mjs +2 -3
- package/src/SimulateItems.res.mjs +3 -21
- package/src/TestIndexer.res.mjs +4 -23
- package/src/TestIndexerProxyStorage.res +1 -0
- package/src/TestIndexerProxyStorage.res.mjs +1 -1
- package/src/Writing.res +3 -5
- package/src/Writing.res.mjs +3 -6
- package/src/bindings/ClickHouse.res +101 -17
- package/src/bindings/ClickHouse.res.mjs +58 -19
- package/src/bindings/NodeJs.res +64 -0
- package/src/bindings/NodeJs.res.mjs +6 -0
- package/src/sources/HyperSyncClient.res +5 -11
- package/src/sources/SourceManager.res +72 -27
- package/src/sources/SourceManager.res.mjs +81 -11
- package/src/sources/SourceManager.resi +14 -0
- package/src/tui/Tui.res +1 -1
- package/src/tui/Tui.res.mjs +2 -2
- package/src/Prometheus.res +0 -789
- package/src/Prometheus.res.mjs +0 -847
- package/src/bindings/PromClient.res +0 -72
- package/src/bindings/PromClient.res.mjs +0 -38
package/src/EventProcessing.res
CHANGED
|
@@ -74,7 +74,7 @@ let runEventHandlerOrThrow = async (
|
|
|
74
74
|
}
|
|
75
75
|
let handlerDuration = timeBeforeHandler->Performance.secondsSince
|
|
76
76
|
let eventConfig = eventItem.onEventRegistration.eventConfig
|
|
77
|
-
|
|
77
|
+
indexerState->IndexerState.recordHandlerDuration(
|
|
78
78
|
~contract=eventConfig.contractName,
|
|
79
79
|
~event=eventConfig.name,
|
|
80
80
|
~duration=handlerDuration,
|
|
@@ -163,16 +163,18 @@ let preloadBatchOrThrow = async (
|
|
|
163
163
|
let item = batch.items->Array.getUnsafe(itemIdx.contents + idx)
|
|
164
164
|
switch item {
|
|
165
165
|
| Event(_) =>
|
|
166
|
-
let {handler, eventConfig: {contractName, name: eventName}} =
|
|
167
|
-
|
|
166
|
+
let {handler, eventConfig: {contractName, name: eventName}} = (
|
|
167
|
+
item->Internal.castUnsafeEventItem
|
|
168
|
+
).onEventRegistration
|
|
168
169
|
switch handler {
|
|
169
170
|
| None => ()
|
|
170
171
|
| Some(handler) =>
|
|
171
172
|
try {
|
|
172
|
-
let timerRef =
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
173
|
+
let timerRef =
|
|
174
|
+
indexerState->IndexerState.startPreloadHandler(
|
|
175
|
+
~contract=contractName,
|
|
176
|
+
~event=eventName,
|
|
177
|
+
)
|
|
176
178
|
promises->Array.push(
|
|
177
179
|
handler({
|
|
178
180
|
event: item->Ecosystem.getItemEvent(~ecosystem=config.ecosystem),
|
|
@@ -189,7 +191,8 @@ let preloadBatchOrThrow = async (
|
|
|
189
191
|
}),
|
|
190
192
|
})
|
|
191
193
|
->Promise.thenResolve(_ => {
|
|
192
|
-
|
|
194
|
+
indexerState->IndexerState.endPreloadHandler(
|
|
195
|
+
timerRef,
|
|
193
196
|
~contract=contractName,
|
|
194
197
|
~event=eventName,
|
|
195
198
|
)
|
|
@@ -270,6 +273,7 @@ let runBatchHandlersOrThrow = async (
|
|
|
270
273
|
let registerProcessEventBatchMetrics = (
|
|
271
274
|
~logger,
|
|
272
275
|
~batch: Batch.t,
|
|
276
|
+
~indexerState,
|
|
273
277
|
~loadDuration,
|
|
274
278
|
~handlerDuration,
|
|
275
279
|
) => {
|
|
@@ -282,7 +286,7 @@ let registerProcessEventBatchMetrics = (
|
|
|
282
286
|
})
|
|
283
287
|
})
|
|
284
288
|
|
|
285
|
-
|
|
289
|
+
indexerState->IndexerState.recordBatchDurations(~loadDuration, ~handlerDuration)
|
|
286
290
|
}
|
|
287
291
|
|
|
288
292
|
type logPartitionInfo = {
|
|
@@ -380,6 +384,7 @@ let processEventBatch = async (
|
|
|
380
384
|
registerProcessEventBatchMetrics(
|
|
381
385
|
~logger,
|
|
382
386
|
~batch,
|
|
387
|
+
~indexerState,
|
|
383
388
|
~loadDuration=loaderDuration,
|
|
384
389
|
~handlerDuration,
|
|
385
390
|
)
|
|
@@ -6,12 +6,12 @@ import * as Writing from "./Writing.res.mjs";
|
|
|
6
6
|
import * as Internal from "./Internal.res.mjs";
|
|
7
7
|
import * as Ecosystem from "./Ecosystem.res.mjs";
|
|
8
8
|
import * as ChainState from "./ChainState.res.mjs";
|
|
9
|
-
import * as Prometheus from "./Prometheus.res.mjs";
|
|
10
9
|
import * as Stdlib_Int from "@rescript/runtime/lib/es6/Stdlib_Int.js";
|
|
11
10
|
import * as Performance from "./bindings/Performance.res.mjs";
|
|
12
11
|
import * as Persistence from "./Persistence.res.mjs";
|
|
13
12
|
import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
|
|
14
13
|
import * as UserContext from "./UserContext.res.mjs";
|
|
14
|
+
import * as IndexerState from "./IndexerState.res.mjs";
|
|
15
15
|
import * as ErrorHandling from "./ErrorHandling.res.mjs";
|
|
16
16
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
17
17
|
|
|
@@ -66,7 +66,7 @@ async function runEventHandlerOrThrow(item, checkpointId, handler, indexerState,
|
|
|
66
66
|
}
|
|
67
67
|
let handlerDuration = Performance.secondsSince(timeBeforeHandler);
|
|
68
68
|
let eventConfig = item.onEventRegistration.eventConfig;
|
|
69
|
-
return
|
|
69
|
+
return IndexerState.recordHandlerDuration(indexerState, eventConfig.contractName, eventConfig.name, handlerDuration);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
async function runHandlerOrThrow(item, checkpointId, indexerState, loadManager, persistence, config, chains) {
|
|
@@ -121,7 +121,7 @@ async function preloadBatchOrThrow(batch, loadManager, persistence, config, inde
|
|
|
121
121
|
let contractName = match$1.contractName;
|
|
122
122
|
let eventName = match$1.name;
|
|
123
123
|
try {
|
|
124
|
-
let timerRef =
|
|
124
|
+
let timerRef = IndexerState.startPreloadHandler(indexerState, contractName, eventName);
|
|
125
125
|
promises.push(Utils.$$Promise.silentCatch(handler({
|
|
126
126
|
event: Ecosystem.getItemEvent(item, config.ecosystem),
|
|
127
127
|
context: UserContext.getHandlerContext({
|
|
@@ -135,7 +135,7 @@ async function preloadBatchOrThrow(batch, loadManager, persistence, config, inde
|
|
|
135
135
|
config: config,
|
|
136
136
|
isResolved: false
|
|
137
137
|
})
|
|
138
|
-
}).then(() =>
|
|
138
|
+
}).then(() => IndexerState.endPreloadHandler(indexerState, timerRef, contractName, eventName))));
|
|
139
139
|
} catch (exn) {
|
|
140
140
|
|
|
141
141
|
}
|
|
@@ -176,14 +176,14 @@ async function runBatchHandlersOrThrow(batch, indexerState, loadManager, persist
|
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
-
function registerProcessEventBatchMetrics(logger, batch, loadDuration, handlerDuration) {
|
|
179
|
+
function registerProcessEventBatchMetrics(logger, batch, indexerState, loadDuration, handlerDuration) {
|
|
180
180
|
Stdlib_Dict.forEachWithKey(batch.progressedChainsById, (chainAfterBatch, chainId) => Logging.childTrace(logger, {
|
|
181
181
|
msg: "Finished processing",
|
|
182
182
|
chainId: Stdlib_Int.fromString(chainId, undefined),
|
|
183
183
|
batchSize: chainAfterBatch.batchSize,
|
|
184
184
|
progress: chainAfterBatch.progressBlockNumber
|
|
185
185
|
}));
|
|
186
|
-
|
|
186
|
+
IndexerState.recordBatchDurations(indexerState, loadDuration, handlerDuration);
|
|
187
187
|
}
|
|
188
188
|
|
|
189
189
|
async function materializeBatchEvents(batch, chainStates, ecosystem) {
|
|
@@ -231,7 +231,7 @@ async function processEventBatch(batch, indexerState, loadManager, persistence,
|
|
|
231
231
|
let elapsedTimeAfterProcessing = Performance.secondsSince(timeRef);
|
|
232
232
|
Writing.commitBatch(indexerState, batch);
|
|
233
233
|
let handlerDuration = elapsedTimeAfterProcessing - elapsedTimeAfterLoaders;
|
|
234
|
-
registerProcessEventBatchMetrics(logger, batch, elapsedTimeAfterLoaders, handlerDuration);
|
|
234
|
+
registerProcessEventBatchMetrics(logger, batch, indexerState, elapsedTimeAfterLoaders, handlerDuration);
|
|
235
235
|
return {
|
|
236
236
|
TAG: "Ok",
|
|
237
237
|
_0: undefined
|
package/src/FetchState.res
CHANGED
|
@@ -909,19 +909,6 @@ let updateInternal = (
|
|
|
909
909
|
firstEventBlock: fetchState.firstEventBlock,
|
|
910
910
|
}
|
|
911
911
|
|
|
912
|
-
Prometheus.IndexingPartitions.set(
|
|
913
|
-
~partitionsCount=optimizedPartitions->OptimizedPartitions.count,
|
|
914
|
-
~chainId=fetchState.chainId,
|
|
915
|
-
)
|
|
916
|
-
Prometheus.IndexingBufferSize.set(
|
|
917
|
-
~bufferSize=updatedFetchState.buffer->Array.length,
|
|
918
|
-
~chainId=fetchState.chainId,
|
|
919
|
-
)
|
|
920
|
-
Prometheus.IndexingBufferBlockNumber.set(
|
|
921
|
-
~blockNumber=updatedFetchState->bufferBlockNumber,
|
|
922
|
-
~chainId=fetchState.chainId,
|
|
923
|
-
)
|
|
924
|
-
|
|
925
912
|
updatedFetchState
|
|
926
913
|
}
|
|
927
914
|
|
|
@@ -2340,22 +2327,13 @@ let make = (
|
|
|
2340
2327
|
firstEventBlock,
|
|
2341
2328
|
}
|
|
2342
2329
|
|
|
2343
|
-
Prometheus.IndexingPartitions.set(
|
|
2344
|
-
~partitionsCount=optimizedPartitions->OptimizedPartitions.count,
|
|
2345
|
-
~chainId,
|
|
2346
|
-
)
|
|
2347
|
-
Prometheus.IndexingBufferSize.set(~bufferSize=buffer->Array.length, ~chainId)
|
|
2348
|
-
Prometheus.IndexingBufferBlockNumber.set(~blockNumber=fetchState->bufferBlockNumber, ~chainId)
|
|
2349
|
-
switch endBlock {
|
|
2350
|
-
| Some(endBlock) => Prometheus.IndexingEndBlock.set(~endBlock, ~chainId)
|
|
2351
|
-
| None => ()
|
|
2352
|
-
}
|
|
2353
|
-
|
|
2354
2330
|
fetchState
|
|
2355
2331
|
}
|
|
2356
2332
|
|
|
2357
2333
|
let bufferSize = ({buffer}: t) => buffer->Array.length
|
|
2358
2334
|
|
|
2335
|
+
let partitionsCount = ({optimizedPartitions}: t) => optimizedPartitions->OptimizedPartitions.count
|
|
2336
|
+
|
|
2359
2337
|
let rollbackPendingQueries = (mutPendingQueries: array<pendingQuery>, ~targetBlockNumber) => {
|
|
2360
2338
|
// - Remove queries where fromBlock > target
|
|
2361
2339
|
// - Cap fetchedBlock at target where fetchedBlock > target
|
|
@@ -2556,14 +2534,26 @@ let isFetchingAtHead = ({endBlock, blockLag, knownHeight} as fetchState: t) => {
|
|
|
2556
2534
|
}
|
|
2557
2535
|
}
|
|
2558
2536
|
|
|
2559
|
-
|
|
2537
|
+
// Frontier at (or within `tolerance` of) the lagged head with no processable
|
|
2538
|
+
// items left — the moment the chain can cross into the reorg threshold.
|
|
2539
|
+
// `tolerance` absorbs the head advancing between a chain catching up and this
|
|
2540
|
+
// check, so it applies only when the (moving) head is the target: a finite
|
|
2541
|
+
// endBlock at or below the lagged head is an exact target and is reached without
|
|
2542
|
+
// tolerance. Uses bufferReadyCount, not an empty buffer: items stuck above the
|
|
2543
|
+
// frontier behind a lagging partition's gap are reorg-safe (all <= head -
|
|
2544
|
+
// blockLag) and must not defer entry, or a many-partition chain never enters.
|
|
2545
|
+
let isReadyToEnterReorgThreshold = (
|
|
2546
|
+
~tolerance,
|
|
2547
|
+
{endBlock, blockLag, knownHeight} as fetchState: t,
|
|
2548
|
+
) => {
|
|
2560
2549
|
let bufferBlockNumber = fetchState->bufferBlockNumber
|
|
2550
|
+
let laggedHead = knownHeight - blockLag
|
|
2561
2551
|
knownHeight !== 0 &&
|
|
2562
2552
|
switch endBlock {
|
|
2563
|
-
| Some(endBlock) if bufferBlockNumber >= endBlock
|
|
2564
|
-
| _ => bufferBlockNumber >=
|
|
2553
|
+
| Some(endBlock) if endBlock <= laggedHead => bufferBlockNumber >= endBlock
|
|
2554
|
+
| _ => bufferBlockNumber >= laggedHead - tolerance
|
|
2565
2555
|
} &&
|
|
2566
|
-
|
|
2556
|
+
fetchState->bufferReadyCount == 0
|
|
2567
2557
|
}
|
|
2568
2558
|
|
|
2569
2559
|
// Lower progress percentage = further behind = higher priority. Progress is
|
|
@@ -2630,7 +2620,6 @@ let getProgressBlockNumberAt = ({buffer} as fetchState: t, ~index) => {
|
|
|
2630
2620
|
|
|
2631
2621
|
let updateKnownHeight = (fetchState: t, ~knownHeight) => {
|
|
2632
2622
|
if knownHeight > fetchState.knownHeight {
|
|
2633
|
-
Prometheus.IndexingKnownHeight.set(~blockNumber=knownHeight, ~chainId=fetchState.chainId)
|
|
2634
2623
|
fetchState->updateInternal(~knownHeight)
|
|
2635
2624
|
} else {
|
|
2636
2625
|
fetchState
|
package/src/FetchState.res.mjs
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import * as Utils from "./Utils.res.mjs";
|
|
4
4
|
import * as Js_math from "@rescript/runtime/lib/es6/Js_math.js";
|
|
5
5
|
import * as Logging from "./Logging.res.mjs";
|
|
6
|
-
import * as Prometheus from "./Prometheus.res.mjs";
|
|
7
6
|
import * as Stdlib_Int from "@rescript/runtime/lib/es6/Stdlib_Int.js";
|
|
8
7
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
9
8
|
import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
|
|
@@ -679,34 +678,21 @@ function updateInternal(fetchState, optimizedPartitionsOpt, mutItems, mutItemsSo
|
|
|
679
678
|
} else {
|
|
680
679
|
latestOnBlockBlockNumber = knownHeight;
|
|
681
680
|
}
|
|
682
|
-
|
|
683
|
-
let updatedFetchState_endBlock = fetchState.endBlock;
|
|
684
|
-
let updatedFetchState_normalSelection = fetchState.normalSelection;
|
|
685
|
-
let updatedFetchState_contractConfigs = fetchState.contractConfigs;
|
|
686
|
-
let updatedFetchState_chainId = fetchState.chainId;
|
|
687
|
-
let updatedFetchState_buffer = blockItems.length !== 0 ? mergeIntoBuffer(base, blockItems) : base;
|
|
688
|
-
let updatedFetchState_maxOnBlockBufferSize = fetchState.maxOnBlockBufferSize;
|
|
689
|
-
let updatedFetchState_onBlockRegistrations = fetchState.onBlockRegistrations;
|
|
690
|
-
let updatedFetchState_firstEventBlock = fetchState.firstEventBlock;
|
|
691
|
-
let updatedFetchState = {
|
|
681
|
+
return {
|
|
692
682
|
optimizedPartitions: optimizedPartitions,
|
|
693
|
-
startBlock:
|
|
694
|
-
endBlock:
|
|
695
|
-
normalSelection:
|
|
696
|
-
contractConfigs:
|
|
697
|
-
chainId:
|
|
683
|
+
startBlock: fetchState.startBlock,
|
|
684
|
+
endBlock: fetchState.endBlock,
|
|
685
|
+
normalSelection: fetchState.normalSelection,
|
|
686
|
+
contractConfigs: fetchState.contractConfigs,
|
|
687
|
+
chainId: fetchState.chainId,
|
|
698
688
|
latestOnBlockBlockNumber: latestOnBlockBlockNumber,
|
|
699
689
|
blockLag: blockLag,
|
|
700
|
-
buffer:
|
|
701
|
-
maxOnBlockBufferSize:
|
|
702
|
-
onBlockRegistrations:
|
|
690
|
+
buffer: blockItems.length !== 0 ? mergeIntoBuffer(base, blockItems) : base,
|
|
691
|
+
maxOnBlockBufferSize: fetchState.maxOnBlockBufferSize,
|
|
692
|
+
onBlockRegistrations: fetchState.onBlockRegistrations,
|
|
703
693
|
knownHeight: knownHeight,
|
|
704
|
-
firstEventBlock:
|
|
694
|
+
firstEventBlock: fetchState.firstEventBlock
|
|
705
695
|
};
|
|
706
|
-
Prometheus.IndexingPartitions.set(optimizedPartitions.idsInAscOrder.length, fetchState.chainId);
|
|
707
|
-
Prometheus.IndexingBufferSize.set(updatedFetchState_buffer.length, fetchState.chainId);
|
|
708
|
-
Prometheus.IndexingBufferBlockNumber.set(bufferBlockNumber(updatedFetchState), fetchState.chainId);
|
|
709
|
-
return updatedFetchState;
|
|
710
696
|
}
|
|
711
697
|
|
|
712
698
|
function warnDifferentContractType(fetchState, existingContract, dc) {
|
|
@@ -1525,7 +1511,7 @@ function make$1(startBlock, endBlock, onEventRegistrations, contractConfigs, add
|
|
|
1525
1511
|
} else {
|
|
1526
1512
|
latestOnBlockBlockNumber = progressBlockNumber;
|
|
1527
1513
|
}
|
|
1528
|
-
|
|
1514
|
+
return {
|
|
1529
1515
|
optimizedPartitions: optimizedPartitions,
|
|
1530
1516
|
startBlock: startBlock,
|
|
1531
1517
|
endBlock: endBlock,
|
|
@@ -1540,19 +1526,16 @@ function make$1(startBlock, endBlock, onEventRegistrations, contractConfigs, add
|
|
|
1540
1526
|
knownHeight: knownHeight,
|
|
1541
1527
|
firstEventBlock: firstEventBlock
|
|
1542
1528
|
};
|
|
1543
|
-
Prometheus.IndexingPartitions.set(optimizedPartitions.idsInAscOrder.length, chainId);
|
|
1544
|
-
Prometheus.IndexingBufferSize.set(buffer.length, chainId);
|
|
1545
|
-
Prometheus.IndexingBufferBlockNumber.set(bufferBlockNumber(fetchState), chainId);
|
|
1546
|
-
if (endBlock !== undefined) {
|
|
1547
|
-
Prometheus.IndexingEndBlock.set(endBlock, chainId);
|
|
1548
|
-
}
|
|
1549
|
-
return fetchState;
|
|
1550
1529
|
}
|
|
1551
1530
|
|
|
1552
1531
|
function bufferSize(param) {
|
|
1553
1532
|
return param.buffer.length;
|
|
1554
1533
|
}
|
|
1555
1534
|
|
|
1535
|
+
function partitionsCount(param) {
|
|
1536
|
+
return param.optimizedPartitions.idsInAscOrder.length;
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1556
1539
|
function rollbackPendingQueries(mutPendingQueries, targetBlockNumber) {
|
|
1557
1540
|
let adjusted = [];
|
|
1558
1541
|
for (let qIdx = 0, qIdx_finish = mutPendingQueries.length; qIdx < qIdx_finish; ++qIdx) {
|
|
@@ -1750,15 +1733,15 @@ function isFetchingAtHead(fetchState) {
|
|
|
1750
1733
|
}
|
|
1751
1734
|
}
|
|
1752
1735
|
|
|
1753
|
-
function isReadyToEnterReorgThreshold(fetchState) {
|
|
1736
|
+
function isReadyToEnterReorgThreshold(tolerance, fetchState) {
|
|
1754
1737
|
let knownHeight = fetchState.knownHeight;
|
|
1755
|
-
let blockLag = fetchState.blockLag;
|
|
1756
1738
|
let endBlock = fetchState.endBlock;
|
|
1757
1739
|
let bufferBlockNumber$1 = bufferBlockNumber(fetchState);
|
|
1740
|
+
let laggedHead = knownHeight - fetchState.blockLag | 0;
|
|
1758
1741
|
if (knownHeight !== 0 && (
|
|
1759
|
-
endBlock !== undefined && bufferBlockNumber$1 >= endBlock
|
|
1742
|
+
endBlock !== undefined && endBlock <= laggedHead ? bufferBlockNumber$1 >= endBlock : bufferBlockNumber$1 >= (laggedHead - tolerance | 0)
|
|
1760
1743
|
)) {
|
|
1761
|
-
return
|
|
1744
|
+
return bufferReadyCount(fetchState) === 0;
|
|
1762
1745
|
} else {
|
|
1763
1746
|
return false;
|
|
1764
1747
|
}
|
|
@@ -1824,7 +1807,6 @@ function getProgressBlockNumberAt(fetchState, index) {
|
|
|
1824
1807
|
|
|
1825
1808
|
function updateKnownHeight(fetchState, knownHeight) {
|
|
1826
1809
|
if (knownHeight > fetchState.knownHeight) {
|
|
1827
|
-
Prometheus.IndexingKnownHeight.set(knownHeight, fetchState.chainId);
|
|
1828
1810
|
return updateInternal(fetchState, undefined, undefined, undefined, undefined, knownHeight);
|
|
1829
1811
|
} else {
|
|
1830
1812
|
return fetchState;
|
|
@@ -1876,6 +1858,7 @@ export {
|
|
|
1876
1858
|
getReadyItemsCount,
|
|
1877
1859
|
make$1 as make,
|
|
1878
1860
|
bufferSize,
|
|
1861
|
+
partitionsCount,
|
|
1879
1862
|
rollbackPendingQueries,
|
|
1880
1863
|
rollback,
|
|
1881
1864
|
resetPendingQueries,
|
package/src/IndexerState.res
CHANGED
|
@@ -28,6 +28,49 @@ module EntityTables = {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
// Per-(contract, event) handler counters rendered into the
|
|
32
|
+
// envio_processing_handler_* and envio_preload_handler_* metrics.
|
|
33
|
+
type handlerStat = {
|
|
34
|
+
contract: string,
|
|
35
|
+
event: string,
|
|
36
|
+
mutable processingSeconds: float,
|
|
37
|
+
// Floats: cumulative counters outgrow int32.
|
|
38
|
+
mutable processingCount: float,
|
|
39
|
+
// Wall-clock preload time: overlapping preloads of the same handler count once.
|
|
40
|
+
mutable preloadSeconds: float,
|
|
41
|
+
mutable preloadCount: float,
|
|
42
|
+
// Cumulative per-call preload time; exceeds preloadSeconds under parallel execution.
|
|
43
|
+
mutable preloadSecondsTotal: float,
|
|
44
|
+
mutable preloadPendingCount: int,
|
|
45
|
+
mutable preloadPendingTimerRef: Performance.timeRef,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Per-(storage, operation) load counters rendered into the
|
|
49
|
+
// envio_storage_load_* metrics.
|
|
50
|
+
type storageLoadStat = {
|
|
51
|
+
operation: string,
|
|
52
|
+
storage: string,
|
|
53
|
+
// Wall-clock load time: overlapping loads of the same operation count once.
|
|
54
|
+
mutable seconds: float,
|
|
55
|
+
mutable secondsTotal: float,
|
|
56
|
+
mutable count: float,
|
|
57
|
+
mutable whereSize: float,
|
|
58
|
+
mutable size: float,
|
|
59
|
+
mutable pendingCount: int,
|
|
60
|
+
mutable pendingTimerRef: Performance.timeRef,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
type storageWriteStat = {
|
|
64
|
+
storage: string,
|
|
65
|
+
mutable seconds: float,
|
|
66
|
+
mutable count: int,
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
type historyPruneStat = {
|
|
70
|
+
mutable seconds: float,
|
|
71
|
+
mutable count: int,
|
|
72
|
+
}
|
|
73
|
+
|
|
31
74
|
type t = {
|
|
32
75
|
config: Config.t,
|
|
33
76
|
persistence: Persistence.t,
|
|
@@ -82,6 +125,16 @@ type t = {
|
|
|
82
125
|
mutable epoch: int,
|
|
83
126
|
// None off the simulate path.
|
|
84
127
|
simulateDeadInputTracker: option<SimulateDeadInputTracker.t>,
|
|
128
|
+
// --- Metric counters, rendered by Metrics at scrape time. ---
|
|
129
|
+
mutable preloadSeconds: float,
|
|
130
|
+
mutable processingSeconds: float,
|
|
131
|
+
handlerStats: dict<handlerStat>,
|
|
132
|
+
storageLoadStats: dict<storageLoadStat>,
|
|
133
|
+
storageWriteStats: dict<storageWriteStat>,
|
|
134
|
+
historyPruneStats: dict<historyPruneStat>,
|
|
135
|
+
mutable rollbackSeconds: float,
|
|
136
|
+
mutable rollbackCount: int,
|
|
137
|
+
mutable rollbackEventsCount: float,
|
|
85
138
|
}
|
|
86
139
|
|
|
87
140
|
let make = (
|
|
@@ -144,6 +197,15 @@ let make = (
|
|
|
144
197
|
isStopped: false,
|
|
145
198
|
epoch: 0,
|
|
146
199
|
simulateDeadInputTracker: SimulateDeadInputTracker.makeFromConfig(config),
|
|
200
|
+
preloadSeconds: 0.,
|
|
201
|
+
processingSeconds: 0.,
|
|
202
|
+
handlerStats: Dict.make(),
|
|
203
|
+
storageLoadStats: Dict.make(),
|
|
204
|
+
storageWriteStats: Dict.make(),
|
|
205
|
+
historyPruneStats: Dict.make(),
|
|
206
|
+
rollbackSeconds: 0.,
|
|
207
|
+
rollbackCount: 0,
|
|
208
|
+
rollbackEventsCount: 0.,
|
|
147
209
|
}
|
|
148
210
|
}
|
|
149
211
|
|
|
@@ -181,16 +243,6 @@ let makeFromDbState = (
|
|
|
181
243
|
)
|
|
182
244
|
}
|
|
183
245
|
|
|
184
|
-
Prometheus.ProcessingMaxBatchSize.set(~maxBatchSize=config.batchSize)
|
|
185
|
-
Prometheus.ReorgThreshold.set(~isInReorgThreshold)
|
|
186
|
-
initialState.cache->Utils.Dict.forEach(({effectName, count, scope}) => {
|
|
187
|
-
Prometheus.EffectCacheCount.set(
|
|
188
|
-
~count,
|
|
189
|
-
~effectName,
|
|
190
|
-
~scope=scope->Internal.EffectCache.scopeToString,
|
|
191
|
-
)
|
|
192
|
-
})
|
|
193
|
-
|
|
194
246
|
// updateSyncTimeOnRestart wipes the saved timestamp so a restart re-enters
|
|
195
247
|
// backfill mode for all chains.
|
|
196
248
|
let isRealtime =
|
|
@@ -216,27 +268,7 @@ let makeFromDbState = (
|
|
|
216
268
|
)
|
|
217
269
|
})
|
|
218
270
|
|
|
219
|
-
|
|
220
|
-
// the persisted state immediately on restart
|
|
221
|
-
let allChainsReady = ref(initialState.chains->Array.length > 0)
|
|
222
|
-
chainStates->Utils.Dict.forEach(cs => {
|
|
223
|
-
let chainId = (cs->ChainState.chainConfig).id
|
|
224
|
-
Prometheus.ProgressBlockNumber.set(
|
|
225
|
-
~blockNumber=cs->ChainState.committedProgressBlockNumber,
|
|
226
|
-
~chainId,
|
|
227
|
-
)
|
|
228
|
-
Prometheus.ProgressReady.init(~chainId)
|
|
229
|
-
if cs->ChainState.isReady {
|
|
230
|
-
Prometheus.ProgressReady.set(~chainId)
|
|
231
|
-
} else {
|
|
232
|
-
allChainsReady := false
|
|
233
|
-
}
|
|
234
|
-
})
|
|
235
|
-
if allChainsReady.contents {
|
|
236
|
-
Prometheus.ProgressReady.setAllReady()
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
make(
|
|
271
|
+
let state = make(
|
|
240
272
|
~config,
|
|
241
273
|
~persistence,
|
|
242
274
|
~chainStates,
|
|
@@ -249,6 +281,10 @@ let makeFromDbState = (
|
|
|
249
281
|
~exitAfterFirstEventBlock,
|
|
250
282
|
~onError,
|
|
251
283
|
)
|
|
284
|
+
initialState.cache->Utils.Dict.forEach(({effectName, count, scope}) => {
|
|
285
|
+
state.effectState->EffectState.setUnregisteredCacheCount(~effectName, ~scope, ~count)
|
|
286
|
+
})
|
|
287
|
+
state
|
|
252
288
|
}
|
|
253
289
|
|
|
254
290
|
// A fetch response or new-block waiter is stale once the indexer stopped or the
|
|
@@ -384,6 +420,219 @@ let epoch = (state: t) => state.epoch
|
|
|
384
420
|
let lastPrunedAtMillis = (state: t) => state.lastPrunedAtMillis
|
|
385
421
|
let simulateDeadInputTracker = (state: t) => state.simulateDeadInputTracker
|
|
386
422
|
|
|
423
|
+
// Read-only snapshot of every metric; the single window into the mutable
|
|
424
|
+
// counters for the /metrics endpoint, the TUI and the console API.
|
|
425
|
+
let toMetrics = (state: t): Metrics.t => {
|
|
426
|
+
let chainStates = state.crossChainState->CrossChainState.chainStates
|
|
427
|
+
let sourceRequests = []
|
|
428
|
+
let sourceHeights = []
|
|
429
|
+
chainStates->Utils.Dict.forEach(cs => {
|
|
430
|
+
let sourceManager = cs->ChainState.sourceManager
|
|
431
|
+
sourceManager
|
|
432
|
+
->SourceManager.getRequestStatSamples
|
|
433
|
+
->Array.forEach(s =>
|
|
434
|
+
sourceRequests->Array.push({
|
|
435
|
+
Metrics.source: s.sourceName,
|
|
436
|
+
chainId: s.chainId,
|
|
437
|
+
method: s.method,
|
|
438
|
+
count: s.count,
|
|
439
|
+
seconds: s.seconds,
|
|
440
|
+
})
|
|
441
|
+
)
|
|
442
|
+
sourceManager
|
|
443
|
+
->SourceManager.getSourceHeightSamples
|
|
444
|
+
->Array.forEach(s =>
|
|
445
|
+
sourceHeights->Array.push({
|
|
446
|
+
Metrics.source: s.sourceName,
|
|
447
|
+
chainId: s.chainId,
|
|
448
|
+
height: s.height,
|
|
449
|
+
})
|
|
450
|
+
)
|
|
451
|
+
})
|
|
452
|
+
let historyPrunes = []
|
|
453
|
+
state.historyPruneStats->Utils.Dict.forEachWithKey((s, entityName) =>
|
|
454
|
+
historyPrunes->Array.push({
|
|
455
|
+
Metrics.entity: entityName,
|
|
456
|
+
seconds: s.seconds,
|
|
457
|
+
count: s.count,
|
|
458
|
+
})
|
|
459
|
+
)
|
|
460
|
+
{
|
|
461
|
+
startTime: state.indexerStartTime,
|
|
462
|
+
targetBufferSize: state.crossChainState->CrossChainState.targetBufferSize,
|
|
463
|
+
isInReorgThreshold: state.crossChainState->CrossChainState.isInReorgThreshold,
|
|
464
|
+
rollbackEnabled: state.config.shouldRollbackOnReorg,
|
|
465
|
+
maxBatchSize: state.config.batchSize,
|
|
466
|
+
preloadSeconds: state.preloadSeconds,
|
|
467
|
+
processingSeconds: state.processingSeconds,
|
|
468
|
+
rollbackSeconds: state.rollbackSeconds,
|
|
469
|
+
rollbackCount: state.rollbackCount,
|
|
470
|
+
rollbackEventsCount: state.rollbackEventsCount,
|
|
471
|
+
chains: chainStates->Dict.valuesToArray->Array.map(ChainState.toMetrics),
|
|
472
|
+
handlers: state.handlerStats
|
|
473
|
+
->Dict.valuesToArray
|
|
474
|
+
->Array.map(s => {
|
|
475
|
+
Metrics.contract: s.contract,
|
|
476
|
+
event: s.event,
|
|
477
|
+
processingSeconds: s.processingSeconds,
|
|
478
|
+
processingCount: s.processingCount,
|
|
479
|
+
preloadSeconds: s.preloadSeconds,
|
|
480
|
+
preloadCount: s.preloadCount,
|
|
481
|
+
preloadSecondsTotal: s.preloadSecondsTotal,
|
|
482
|
+
}),
|
|
483
|
+
effects: state.effectState->EffectState.toMetrics,
|
|
484
|
+
storageLoads: state.storageLoadStats
|
|
485
|
+
->Dict.valuesToArray
|
|
486
|
+
->Array.map(s => {
|
|
487
|
+
Metrics.operation: s.operation,
|
|
488
|
+
storage: s.storage,
|
|
489
|
+
seconds: s.seconds,
|
|
490
|
+
secondsTotal: s.secondsTotal,
|
|
491
|
+
count: s.count,
|
|
492
|
+
whereSize: s.whereSize,
|
|
493
|
+
size: s.size,
|
|
494
|
+
}),
|
|
495
|
+
storageWrites: state.storageWriteStats
|
|
496
|
+
->Dict.valuesToArray
|
|
497
|
+
->Array.map(s => {
|
|
498
|
+
Metrics.storage: s.storage,
|
|
499
|
+
seconds: s.seconds,
|
|
500
|
+
count: s.count,
|
|
501
|
+
}),
|
|
502
|
+
historyPrunes,
|
|
503
|
+
sourceRequests,
|
|
504
|
+
sourceHeights,
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// --- Metric counters. ---
|
|
509
|
+
|
|
510
|
+
let recordBatchDurations = (state: t, ~loadDuration, ~handlerDuration) => {
|
|
511
|
+
state.preloadSeconds = state.preloadSeconds +. loadDuration
|
|
512
|
+
state.processingSeconds = state.processingSeconds +. handlerDuration
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
let getHandlerStat = (state: t, ~contract, ~event) => {
|
|
516
|
+
// Length-prefixed so names containing the separator can't collide.
|
|
517
|
+
let key = contract->String.length->Int.toString ++ ":" ++ contract ++ event
|
|
518
|
+
switch state.handlerStats->Utils.Dict.dangerouslyGetNonOption(key) {
|
|
519
|
+
| Some(stat) => stat
|
|
520
|
+
| None =>
|
|
521
|
+
let stat: handlerStat = {
|
|
522
|
+
contract,
|
|
523
|
+
event,
|
|
524
|
+
processingSeconds: 0.,
|
|
525
|
+
processingCount: 0.,
|
|
526
|
+
preloadSeconds: 0.,
|
|
527
|
+
preloadCount: 0.,
|
|
528
|
+
preloadSecondsTotal: 0.,
|
|
529
|
+
preloadPendingCount: 0,
|
|
530
|
+
preloadPendingTimerRef: %raw(`null`),
|
|
531
|
+
}
|
|
532
|
+
state.handlerStats->Dict.set(key, stat)
|
|
533
|
+
stat
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
let recordHandlerDuration = (state: t, ~contract, ~event, ~duration) => {
|
|
538
|
+
let stat = state->getHandlerStat(~contract, ~event)
|
|
539
|
+
stat.processingSeconds = stat.processingSeconds +. duration
|
|
540
|
+
stat.processingCount = stat.processingCount +. 1.
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
let startPreloadHandler = (state: t, ~contract, ~event) => {
|
|
544
|
+
let stat = state->getHandlerStat(~contract, ~event)
|
|
545
|
+
if stat.preloadPendingCount === 0 {
|
|
546
|
+
stat.preloadPendingTimerRef = Performance.now()
|
|
547
|
+
}
|
|
548
|
+
stat.preloadPendingCount = stat.preloadPendingCount + 1
|
|
549
|
+
Performance.now()
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
let endPreloadHandler = (state: t, timerRef, ~contract, ~event) => {
|
|
553
|
+
let stat = state->getHandlerStat(~contract, ~event)
|
|
554
|
+
stat.preloadPendingCount = stat.preloadPendingCount - 1
|
|
555
|
+
if stat.preloadPendingCount === 0 {
|
|
556
|
+
stat.preloadSeconds =
|
|
557
|
+
stat.preloadSeconds +. stat.preloadPendingTimerRef->Performance.secondsSince
|
|
558
|
+
}
|
|
559
|
+
stat.preloadSecondsTotal = stat.preloadSecondsTotal +. timerRef->Performance.secondsSince
|
|
560
|
+
stat.preloadCount = stat.preloadCount +. 1.
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
let getStorageLoadStat = (state: t, ~storage, ~operation) => {
|
|
564
|
+
// Length-prefixed so names containing the separator can't collide.
|
|
565
|
+
let key = storage->String.length->Int.toString ++ ":" ++ storage ++ operation
|
|
566
|
+
switch state.storageLoadStats->Utils.Dict.dangerouslyGetNonOption(key) {
|
|
567
|
+
| Some(stat) => stat
|
|
568
|
+
| None =>
|
|
569
|
+
let stat: storageLoadStat = {
|
|
570
|
+
operation,
|
|
571
|
+
storage,
|
|
572
|
+
seconds: 0.,
|
|
573
|
+
secondsTotal: 0.,
|
|
574
|
+
count: 0.,
|
|
575
|
+
whereSize: 0.,
|
|
576
|
+
size: 0.,
|
|
577
|
+
pendingCount: 0,
|
|
578
|
+
pendingTimerRef: %raw(`null`),
|
|
579
|
+
}
|
|
580
|
+
state.storageLoadStats->Dict.set(key, stat)
|
|
581
|
+
stat
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
let startStorageLoad = (state: t, ~storage, ~operation) => {
|
|
586
|
+
let stat = state->getStorageLoadStat(~storage, ~operation)
|
|
587
|
+
if stat.pendingCount === 0 {
|
|
588
|
+
stat.pendingTimerRef = Performance.now()
|
|
589
|
+
}
|
|
590
|
+
stat.pendingCount = stat.pendingCount + 1
|
|
591
|
+
Performance.now()
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
let endStorageLoad = (state: t, timerRef, ~storage, ~operation, ~whereSize, ~size) => {
|
|
595
|
+
let stat = state->getStorageLoadStat(~storage, ~operation)
|
|
596
|
+
stat.pendingCount = stat.pendingCount - 1
|
|
597
|
+
if stat.pendingCount === 0 {
|
|
598
|
+
stat.seconds = stat.seconds +. stat.pendingTimerRef->Performance.secondsSince
|
|
599
|
+
}
|
|
600
|
+
stat.secondsTotal = stat.secondsTotal +. timerRef->Performance.secondsSince
|
|
601
|
+
stat.count = stat.count +. 1.
|
|
602
|
+
stat.whereSize = stat.whereSize +. whereSize->Int.toFloat
|
|
603
|
+
stat.size = stat.size +. size->Int.toFloat
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
let recordStorageWrite = (state: t, ~storage, ~timeSeconds) => {
|
|
607
|
+
let stat = switch state.storageWriteStats->Utils.Dict.dangerouslyGetNonOption(storage) {
|
|
608
|
+
| Some(stat) => stat
|
|
609
|
+
| None =>
|
|
610
|
+
let stat: storageWriteStat = {storage, seconds: 0., count: 0}
|
|
611
|
+
state.storageWriteStats->Dict.set(storage, stat)
|
|
612
|
+
stat
|
|
613
|
+
}
|
|
614
|
+
stat.seconds = stat.seconds +. timeSeconds
|
|
615
|
+
stat.count = stat.count + 1
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
let recordHistoryPrune = (state: t, ~entityName, ~timeSeconds) => {
|
|
619
|
+
let stat = switch state.historyPruneStats->Utils.Dict.dangerouslyGetNonOption(entityName) {
|
|
620
|
+
| Some(stat) => stat
|
|
621
|
+
| None =>
|
|
622
|
+
let stat: historyPruneStat = {seconds: 0., count: 0}
|
|
623
|
+
state.historyPruneStats->Dict.set(entityName, stat)
|
|
624
|
+
stat
|
|
625
|
+
}
|
|
626
|
+
stat.seconds = stat.seconds +. timeSeconds
|
|
627
|
+
stat.count = stat.count + 1
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
let recordRollbackSuccess = (state: t, ~timeSeconds, ~rollbackedProcessedEvents) => {
|
|
631
|
+
state.rollbackSeconds = state.rollbackSeconds +. timeSeconds
|
|
632
|
+
state.rollbackCount = state.rollbackCount + 1
|
|
633
|
+
state.rollbackEventsCount = state.rollbackEventsCount +. rollbackedProcessedEvents
|
|
634
|
+
}
|
|
635
|
+
|
|
387
636
|
// --- Store domain operations. ---
|
|
388
637
|
|
|
389
638
|
// Queue a processed batch for writing and advance the processing frontier.
|