envio 3.1.0-rc.0 → 3.1.0-rc.2
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 +7 -1
- package/src/Batch.res.mjs +2 -1
- package/src/ChainFetcher.res +8 -23
- package/src/ChainFetcher.res.mjs +2 -10
- package/src/ChainManager.res +3 -2
- package/src/ChainManager.res.mjs +3 -3
- package/src/Env.res +6 -7
- package/src/Env.res.mjs +3 -3
- package/src/EventProcessing.res +24 -122
- package/src/EventProcessing.res.mjs +24 -88
- package/src/FetchState.res +1 -4
- package/src/FetchState.res.mjs +1 -2
- package/src/GlobalState.res +33 -54
- package/src/GlobalState.res.mjs +55 -34
- package/src/GlobalStateManager.res +1 -3
- package/src/InMemoryStore.res +400 -86
- package/src/InMemoryStore.res.mjs +330 -70
- package/src/InMemoryTable.res +53 -18
- package/src/InMemoryTable.res.mjs +38 -18
- package/src/Internal.res +3 -0
- package/src/LoadLayer.res +9 -7
- package/src/LoadLayer.res.mjs +4 -10
- package/src/LogSelection.res +15 -19
- package/src/LogSelection.res.mjs +5 -6
- package/src/Main.res +14 -1
- package/src/Main.res.mjs +8 -3
- package/src/Persistence.res +3 -1
- package/src/PgStorage.res +171 -68
- package/src/PgStorage.res.mjs +125 -39
- package/src/TestIndexerProxyStorage.res +1 -1
- package/src/TestIndexerProxyStorage.res.mjs +1 -1
- package/src/Throttler.res +22 -15
- package/src/Throttler.res.mjs +19 -14
- package/src/UserContext.res +1 -0
- package/src/UserContext.res.mjs +3 -1
- package/src/bindings/NodeJs.res +1 -0
- package/src/sources/EnvioApiClient.res +15 -0
- package/src/sources/EnvioApiClient.res.mjs +24 -0
- package/src/sources/EvmChain.res +0 -1
- package/src/sources/EvmChain.res.mjs +0 -1
- package/src/sources/HyperFuelSource.res +1 -1
- package/src/sources/HyperFuelSource.res.mjs +2 -1
- package/src/sources/HyperSync.res +20 -1
- package/src/sources/HyperSync.res.mjs +26 -1
- package/src/sources/HyperSyncClient.res +3 -2
- package/src/sources/HyperSyncClient.res.mjs +2 -2
- package/src/sources/HyperSyncSource.res +18 -19
- package/src/sources/HyperSyncSource.res.mjs +40 -14
- package/src/sources/Source.res +2 -0
- package/src/sources/Source.res.mjs +3 -0
- package/src/sources/SourceManager.res +168 -8
- package/src/sources/SourceManager.res.mjs +131 -3
- package/src/sources/SourceManager.resi +17 -0
- package/src/tui/Tui.res +44 -6
- package/src/tui/Tui.res.mjs +56 -8
- package/src/tui/components/TuiData.res +3 -0
- package/src/sources/HyperSyncJsonApi.res +0 -390
- package/src/sources/HyperSyncJsonApi.res.mjs +0 -237
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "envio",
|
|
3
|
-
"version": "3.1.0-rc.
|
|
3
|
+
"version": "3.1.0-rc.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
|
|
6
6
|
"bin": "./bin.mjs",
|
|
@@ -70,10 +70,10 @@
|
|
|
70
70
|
"tsx": "4.21.0"
|
|
71
71
|
},
|
|
72
72
|
"optionalDependencies": {
|
|
73
|
-
"envio-linux-x64": "3.1.0-rc.
|
|
74
|
-
"envio-linux-x64-musl": "3.1.0-rc.
|
|
75
|
-
"envio-linux-arm64": "3.1.0-rc.
|
|
76
|
-
"envio-darwin-x64": "3.1.0-rc.
|
|
77
|
-
"envio-darwin-arm64": "3.1.0-rc.
|
|
73
|
+
"envio-linux-x64": "3.1.0-rc.2",
|
|
74
|
+
"envio-linux-x64-musl": "3.1.0-rc.2",
|
|
75
|
+
"envio-linux-arm64": "3.1.0-rc.2",
|
|
76
|
+
"envio-darwin-x64": "3.1.0-rc.2",
|
|
77
|
+
"envio-darwin-arm64": "3.1.0-rc.2"
|
|
78
78
|
}
|
|
79
79
|
}
|
package/src/Batch.res
CHANGED
|
@@ -23,6 +23,9 @@ type t = {
|
|
|
23
23
|
totalBatchSize: int,
|
|
24
24
|
items: array<Internal.item>,
|
|
25
25
|
progressedChainsById: dict<chainAfterBatch>,
|
|
26
|
+
// Processed inside the reorg threshold. Drives whether history is saved, so
|
|
27
|
+
// writes never merge across a change in this value.
|
|
28
|
+
isInReorgThreshold: bool,
|
|
26
29
|
// Unnest-like checkpoint fields:
|
|
27
30
|
checkpointIds: array<bigint>,
|
|
28
31
|
checkpointChainIds: array<int>,
|
|
@@ -165,6 +168,7 @@ let prepareBatch = (
|
|
|
165
168
|
~checkpointIdBeforeBatch,
|
|
166
169
|
~chainsBeforeBatch: ChainMap.t<chainBeforeBatch>,
|
|
167
170
|
~batchSizeTarget,
|
|
171
|
+
~isInReorgThreshold,
|
|
168
172
|
) => {
|
|
169
173
|
let preparedFetchStates =
|
|
170
174
|
chainsBeforeBatch
|
|
@@ -286,6 +290,7 @@ let prepareBatch = (
|
|
|
286
290
|
~batchSizePerChain=mutBatchSizePerChain,
|
|
287
291
|
~progressBlockNumberPerChain=mutProgressBlockNumberPerChain,
|
|
288
292
|
),
|
|
293
|
+
isInReorgThreshold,
|
|
289
294
|
checkpointIds,
|
|
290
295
|
checkpointChainIds,
|
|
291
296
|
checkpointBlockNumbers,
|
|
@@ -298,8 +303,9 @@ let make = (
|
|
|
298
303
|
~checkpointIdBeforeBatch,
|
|
299
304
|
~chainsBeforeBatch: ChainMap.t<chainBeforeBatch>,
|
|
300
305
|
~batchSizeTarget,
|
|
306
|
+
~isInReorgThreshold,
|
|
301
307
|
) => {
|
|
302
|
-
prepareBatch(~checkpointIdBeforeBatch, ~chainsBeforeBatch, ~batchSizeTarget)
|
|
308
|
+
prepareBatch(~checkpointIdBeforeBatch, ~chainsBeforeBatch, ~batchSizeTarget, ~isInReorgThreshold)
|
|
303
309
|
}
|
|
304
310
|
|
|
305
311
|
let findFirstEventBlockNumber = (batch: t, ~chainId) => {
|
package/src/Batch.res.mjs
CHANGED
|
@@ -70,7 +70,7 @@ function addReorgCheckpoints(prevCheckpointId, reorgDetection, fromBlockExclusiv
|
|
|
70
70
|
return prevCheckpointId$1;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
function prepareBatch(checkpointIdBeforeBatch, chainsBeforeBatch, batchSizeTarget) {
|
|
73
|
+
function prepareBatch(checkpointIdBeforeBatch, chainsBeforeBatch, batchSizeTarget, isInReorgThreshold) {
|
|
74
74
|
let preparedFetchStates = FetchState.sortForUnorderedBatch(ChainMap.values(chainsBeforeBatch).map(chainBeforeBatch => chainBeforeBatch.fetchState), batchSizeTarget);
|
|
75
75
|
let chainIdx = 0;
|
|
76
76
|
let preparedNumber = preparedFetchStates.length;
|
|
@@ -168,6 +168,7 @@ function prepareBatch(checkpointIdBeforeBatch, chainsBeforeBatch, batchSizeTarge
|
|
|
168
168
|
totalBatchSize: totalBatchSize,
|
|
169
169
|
items: items,
|
|
170
170
|
progressedChainsById: getProgressedChainsById(chainsBeforeBatch, mutBatchSizePerChain, mutProgressBlockNumberPerChain),
|
|
171
|
+
isInReorgThreshold: isInReorgThreshold,
|
|
171
172
|
checkpointIds: checkpointIds,
|
|
172
173
|
checkpointChainIds: checkpointChainIds,
|
|
173
174
|
checkpointBlockNumbers: checkpointBlockNumbers,
|
package/src/ChainFetcher.res
CHANGED
|
@@ -462,38 +462,23 @@ let getHighestBlockBelowThreshold = (cf: t): int => {
|
|
|
462
462
|
Finds the last known valid block number below the reorg block
|
|
463
463
|
If not found, returns the highest block below threshold
|
|
464
464
|
*/
|
|
465
|
-
let getLastKnownValidBlock = async (
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
//
|
|
469
|
-
~getBlockHashes=(chainFetcher.sourceManager->SourceManager.getActiveSource).getBlockHashes,
|
|
470
|
-
) => {
|
|
471
|
-
// Improtant: It's important to not include the reorg detection block number
|
|
472
|
-
// because there might be different instances of the source
|
|
473
|
-
// with mismatching hashes between them.
|
|
474
|
-
// So we MUST always rollback the block number where we detected a reorg.
|
|
465
|
+
let getLastKnownValidBlock = async (chainFetcher: t, ~reorgBlockNumber: int, ~isRealtime: bool) => {
|
|
466
|
+
// Don't include the reorg block itself — different source instances
|
|
467
|
+
// may have mismatching hashes at the head, so we always rollback
|
|
468
|
+
// the block where we detected the reorg.
|
|
475
469
|
let scannedBlockNumbers =
|
|
476
470
|
chainFetcher.reorgDetection->ReorgDetection.getThresholdBlockNumbersBelowBlock(
|
|
477
471
|
~blockNumber=reorgBlockNumber,
|
|
478
472
|
~knownHeight=chainFetcher.fetchState.knownHeight,
|
|
479
473
|
)
|
|
480
474
|
|
|
481
|
-
let getBlockHashes = blockNumbers => {
|
|
482
|
-
getBlockHashes(~blockNumbers, ~logger=chainFetcher.logger)->Promise.thenResolve(res =>
|
|
483
|
-
switch res {
|
|
484
|
-
| Ok(v) => v
|
|
485
|
-
| Error(exn) =>
|
|
486
|
-
exn->ErrorHandling.mkLogAndRaise(
|
|
487
|
-
~msg="Failed to fetch blockHashes for given blockNumbers during rollback",
|
|
488
|
-
)
|
|
489
|
-
}
|
|
490
|
-
)
|
|
491
|
-
}
|
|
492
|
-
|
|
493
475
|
switch scannedBlockNumbers {
|
|
494
476
|
| [] => chainFetcher->getHighestBlockBelowThreshold
|
|
495
477
|
| _ => {
|
|
496
|
-
let blockNumbersAndHashes = await getBlockHashes(
|
|
478
|
+
let blockNumbersAndHashes = await chainFetcher.sourceManager->SourceManager.getBlockHashes(
|
|
479
|
+
~blockNumbers=scannedBlockNumbers,
|
|
480
|
+
~isRealtime,
|
|
481
|
+
)
|
|
497
482
|
|
|
498
483
|
switch chainFetcher.reorgDetection->ReorgDetection.getLatestValidScannedBlock(
|
|
499
484
|
~blockNumbersAndHashes,
|
package/src/ChainFetcher.res.mjs
CHANGED
|
@@ -261,20 +261,12 @@ function getHighestBlockBelowThreshold(cf) {
|
|
|
261
261
|
}
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
-
async function getLastKnownValidBlock(chainFetcher, reorgBlockNumber,
|
|
265
|
-
let getBlockHashes = getBlockHashesOpt !== undefined ? getBlockHashesOpt : SourceManager.getActiveSource(chainFetcher.sourceManager).getBlockHashes;
|
|
264
|
+
async function getLastKnownValidBlock(chainFetcher, reorgBlockNumber, isRealtime) {
|
|
266
265
|
let scannedBlockNumbers = ReorgDetection.getThresholdBlockNumbersBelowBlock(chainFetcher.reorgDetection, reorgBlockNumber, chainFetcher.fetchState.knownHeight);
|
|
267
|
-
let getBlockHashes$1 = blockNumbers => getBlockHashes(blockNumbers, chainFetcher.logger).then(res => {
|
|
268
|
-
if (res.TAG === "Ok") {
|
|
269
|
-
return res._0;
|
|
270
|
-
} else {
|
|
271
|
-
return ErrorHandling.mkLogAndRaise(undefined, "Failed to fetch blockHashes for given blockNumbers during rollback", res._0);
|
|
272
|
-
}
|
|
273
|
-
});
|
|
274
266
|
if (scannedBlockNumbers.length === 0) {
|
|
275
267
|
return getHighestBlockBelowThreshold(chainFetcher);
|
|
276
268
|
}
|
|
277
|
-
let blockNumbersAndHashes = await getBlockHashes
|
|
269
|
+
let blockNumbersAndHashes = await SourceManager.getBlockHashes(chainFetcher.sourceManager, scannedBlockNumbers, isRealtime);
|
|
278
270
|
let blockNumber = ReorgDetection.getLatestValidScannedBlock(chainFetcher.reorgDetection, blockNumbersAndHashes);
|
|
279
271
|
if (blockNumber !== undefined) {
|
|
280
272
|
return blockNumber;
|
package/src/ChainManager.res
CHANGED
|
@@ -135,12 +135,13 @@ let nextItemIsNone = (chainManager: t): bool => {
|
|
|
135
135
|
|
|
136
136
|
let createBatch = (
|
|
137
137
|
chainManager: t,
|
|
138
|
-
~
|
|
138
|
+
~processedCheckpointId,
|
|
139
139
|
~batchSizeTarget: int,
|
|
140
140
|
~isRollback: bool,
|
|
141
141
|
): Batch.t => {
|
|
142
142
|
Batch.make(
|
|
143
|
-
~
|
|
143
|
+
~isInReorgThreshold=chainManager.isInReorgThreshold,
|
|
144
|
+
~checkpointIdBeforeBatch=processedCheckpointId->BigInt.add(
|
|
144
145
|
// Since for rollback we have a diff checkpoint id.
|
|
145
146
|
// This is needed to currectly overwrite old state
|
|
146
147
|
// in an append-only ClickHouse insert.
|
package/src/ChainManager.res.mjs
CHANGED
|
@@ -99,8 +99,8 @@ function nextItemIsNone(chainManager) {
|
|
|
99
99
|
return !Batch.hasReadyItem(ChainMap.map(chainManager.chainFetchers, cf => cf.fetchState));
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
function createBatch(chainManager,
|
|
103
|
-
return Batch.make(
|
|
102
|
+
function createBatch(chainManager, processedCheckpointId, batchSizeTarget, isRollback) {
|
|
103
|
+
return Batch.make(processedCheckpointId + (
|
|
104
104
|
isRollback ? 1n : 0n
|
|
105
105
|
), ChainMap.map(chainManager.chainFetchers, cf => ({
|
|
106
106
|
fetchState: cf.fetchState,
|
|
@@ -109,7 +109,7 @@ function createBatch(chainManager, committedCheckpointId, batchSizeTarget, isRol
|
|
|
109
109
|
sourceBlockNumber: cf.fetchState.knownHeight,
|
|
110
110
|
totalEventsProcessed: cf.numEventsProcessed,
|
|
111
111
|
chainConfig: cf.chainConfig
|
|
112
|
-
})), batchSizeTarget);
|
|
112
|
+
})), batchSizeTarget, chainManager.isInReorgThreshold);
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
function isProgressAtHead(chainManager) {
|
package/src/Env.res
CHANGED
|
@@ -11,6 +11,12 @@ let maxAddrInPartition = envSafe->EnvSafe.get("MAX_PARTITION_SIZE", S.int, ~fall
|
|
|
11
11
|
let maxPartitionConcurrency =
|
|
12
12
|
envSafe->EnvSafe.get("ENVIO_MAX_PARTITION_CONCURRENCY", S.int, ~fallback=10)
|
|
13
13
|
|
|
14
|
+
// Target number of in-memory objects (uncommitted entity/effect changes plus
|
|
15
|
+
// unwritten batch items) the store holds before processing waits for the write
|
|
16
|
+
// cycle to catch up.
|
|
17
|
+
let inMemoryObjectsTarget =
|
|
18
|
+
envSafe->EnvSafe.get("ENVIO_IN_MEMORY_OBJECTS_TARGET", S.int, ~fallback=100_000)->Belt.Int.toFloat
|
|
19
|
+
|
|
14
20
|
// FIXME: This broke HS grafana dashboard. Should investigate it later. Maybe we should use :: as a default value?
|
|
15
21
|
// We want to be able to set it to 0.0.0.0
|
|
16
22
|
// to allow to passthrough the port from a Docker container
|
|
@@ -47,13 +53,6 @@ let envioApiToken = envSafe->EnvSafe.get("ENVIO_API_TOKEN", S.option(S.string))
|
|
|
47
53
|
let hyperSyncClientTimeoutMillis =
|
|
48
54
|
envSafe->EnvSafe.get("ENVIO_HYPERSYNC_CLIENT_TIMEOUT_MILLIS", S.int, ~fallback=120_000)
|
|
49
55
|
|
|
50
|
-
/**
|
|
51
|
-
This is the number of retries that the binary client makes before rejecting the promise with an error
|
|
52
|
-
Default is 0 so that the indexer can handle retries internally
|
|
53
|
-
*/
|
|
54
|
-
let hyperSyncClientMaxRetries =
|
|
55
|
-
envSafe->EnvSafe.get("ENVIO_HYPERSYNC_CLIENT_MAX_RETRIES", S.int, ~fallback=0)
|
|
56
|
-
|
|
57
56
|
let hypersyncClientSerializationFormat =
|
|
58
57
|
envSafe->EnvSafe.get(
|
|
59
58
|
"ENVIO_HYPERSYNC_CLIENT_SERIALIZATION_FORMAT",
|
package/src/Env.res.mjs
CHANGED
|
@@ -21,6 +21,8 @@ let maxAddrInPartition = EnvSafe.get(envSafe, "MAX_PARTITION_SIZE", S$RescriptSc
|
|
|
21
21
|
|
|
22
22
|
let maxPartitionConcurrency = EnvSafe.get(envSafe, "ENVIO_MAX_PARTITION_CONCURRENCY", S$RescriptSchema.int, undefined, 10, undefined, undefined);
|
|
23
23
|
|
|
24
|
+
let inMemoryObjectsTarget = EnvSafe.get(envSafe, "ENVIO_IN_MEMORY_OBJECTS_TARGET", S$RescriptSchema.int, undefined, 100000, undefined, undefined);
|
|
25
|
+
|
|
24
26
|
let serverPort = EnvSafe.get(envSafe, "ENVIO_INDEXER_PORT", S$RescriptSchema.port(S$RescriptSchema.int, undefined), undefined, EnvSafe.get(envSafe, "METRICS_PORT", S$RescriptSchema.port(S$RescriptSchema.int, undefined), undefined, 9898, undefined, undefined), undefined, undefined);
|
|
25
27
|
|
|
26
28
|
let tuiEnvVar = EnvSafe.get(envSafe, "ENVIO_TUI", S$RescriptSchema.option(S$RescriptSchema.bool), undefined, undefined, undefined, undefined);
|
|
@@ -53,8 +55,6 @@ let envioApiToken = EnvSafe.get(envSafe, "ENVIO_API_TOKEN", S$RescriptSchema.opt
|
|
|
53
55
|
|
|
54
56
|
let hyperSyncClientTimeoutMillis = EnvSafe.get(envSafe, "ENVIO_HYPERSYNC_CLIENT_TIMEOUT_MILLIS", S$RescriptSchema.int, undefined, 120000, undefined, undefined);
|
|
55
57
|
|
|
56
|
-
let hyperSyncClientMaxRetries = EnvSafe.get(envSafe, "ENVIO_HYPERSYNC_CLIENT_MAX_RETRIES", S$RescriptSchema.int, undefined, 0, undefined, undefined);
|
|
57
|
-
|
|
58
58
|
let hypersyncClientSerializationFormat = EnvSafe.get(envSafe, "ENVIO_HYPERSYNC_CLIENT_SERIALIZATION_FORMAT", HyperSyncClient.serializationFormatSchema, undefined, "CapnProto", undefined, undefined);
|
|
59
59
|
|
|
60
60
|
let hypersyncClientEnableQueryCaching = EnvSafe.get(envSafe, "ENVIO_HYPERSYNC_CLIENT_ENABLE_QUERY_CACHING", S$RescriptSchema.bool, undefined, true, undefined, undefined);
|
|
@@ -223,6 +223,7 @@ export {
|
|
|
223
223
|
targetBufferSize,
|
|
224
224
|
maxAddrInPartition,
|
|
225
225
|
maxPartitionConcurrency,
|
|
226
|
+
inMemoryObjectsTarget,
|
|
226
227
|
serverPort,
|
|
227
228
|
tuiEnvVar,
|
|
228
229
|
logLevelSchema,
|
|
@@ -233,7 +234,6 @@ export {
|
|
|
233
234
|
envioAppUrl,
|
|
234
235
|
envioApiToken,
|
|
235
236
|
hyperSyncClientTimeoutMillis,
|
|
236
|
-
hyperSyncClientMaxRetries,
|
|
237
237
|
hypersyncClientSerializationFormat,
|
|
238
238
|
hypersyncClientEnableQueryCaching,
|
|
239
239
|
hypersyncLogLevel,
|
package/src/EventProcessing.res
CHANGED
|
@@ -25,77 +25,6 @@ let computeChainsState = (chainFetchers: ChainMap.t<ChainFetcher.t>): Internal.c
|
|
|
25
25
|
chains
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
let convertFieldsToJson = (fields: option<dict<unknown>>) => {
|
|
29
|
-
switch fields {
|
|
30
|
-
| None => %raw(`{}`)
|
|
31
|
-
| Some(fields) =>
|
|
32
|
-
// Convert bigint fields to string. There are no fields with nested
|
|
33
|
-
// bigints, so iterating only the top level is safe.
|
|
34
|
-
fields
|
|
35
|
-
->Utils.Dict.mapValues(value =>
|
|
36
|
-
typeof(value) === #bigint
|
|
37
|
-
? value
|
|
38
|
-
->(Utils.magic: unknown => bigint)
|
|
39
|
-
->BigInt.toString
|
|
40
|
-
->(Utils.magic: string => unknown)
|
|
41
|
-
: value
|
|
42
|
-
)
|
|
43
|
-
->(Utils.magic: dict<unknown> => JSON.t)
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
let addItemToRawEvents = (
|
|
48
|
-
eventItem: Internal.eventItem,
|
|
49
|
-
~inMemoryStore: InMemoryStore.t,
|
|
50
|
-
~config: Config.t,
|
|
51
|
-
) => {
|
|
52
|
-
let {event, eventConfig, chain, blockNumber, timestamp: blockTimestamp} = eventItem
|
|
53
|
-
let {block, transaction, params, logIndex, srcAddress} = event
|
|
54
|
-
let chainId = chain->ChainMap.Chain.toChainId
|
|
55
|
-
let eventId = EventUtils.packEventIndex(~logIndex, ~blockNumber)
|
|
56
|
-
let blockFields =
|
|
57
|
-
block
|
|
58
|
-
->(Utils.magic: Internal.eventBlock => option<dict<unknown>>)
|
|
59
|
-
->convertFieldsToJson
|
|
60
|
-
let transactionFields =
|
|
61
|
-
transaction
|
|
62
|
-
->(Utils.magic: Internal.eventTransaction => option<dict<unknown>>)
|
|
63
|
-
->convertFieldsToJson
|
|
64
|
-
|
|
65
|
-
blockFields->config.ecosystem.cleanUpRawEventFieldsInPlace
|
|
66
|
-
|
|
67
|
-
// Serialize to unknown, because serializing to Js.Json.t fails for Bytes Fuel type, since it has unknown schema
|
|
68
|
-
let params =
|
|
69
|
-
params
|
|
70
|
-
->S.reverseConvertOrThrow(eventConfig.paramsRawEventSchema)
|
|
71
|
-
->(Utils.magic: unknown => JSON.t)
|
|
72
|
-
let params = if params === %raw(`null`) {
|
|
73
|
-
// Should probably make the params field nullable
|
|
74
|
-
// But this is currently needed to make events
|
|
75
|
-
// with empty params work
|
|
76
|
-
%raw(`"null"`)
|
|
77
|
-
} else {
|
|
78
|
-
params
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
let rawEvent: InternalTable.RawEvents.t = {
|
|
82
|
-
chainId,
|
|
83
|
-
eventId,
|
|
84
|
-
eventName: eventConfig.name,
|
|
85
|
-
contractName: eventConfig.contractName,
|
|
86
|
-
blockNumber,
|
|
87
|
-
logIndex,
|
|
88
|
-
srcAddress,
|
|
89
|
-
blockHash: block->config.ecosystem.getId,
|
|
90
|
-
blockTimestamp,
|
|
91
|
-
blockFields,
|
|
92
|
-
transactionFields,
|
|
93
|
-
params,
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
inMemoryStore.rawEvents->Array.push(rawEvent)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
28
|
exception ProcessingError({message: string, exn: exn, item: Internal.item})
|
|
100
29
|
|
|
101
30
|
let runEventHandlerOrThrow = async (
|
|
@@ -192,26 +121,18 @@ let runHandlerOrThrow = async (
|
|
|
192
121
|
}),
|
|
193
122
|
)
|
|
194
123
|
}
|
|
195
|
-
| Event({eventConfig}) => {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
| None => ()
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
if ctx.config.enableRawEvents {
|
|
211
|
-
item
|
|
212
|
-
->Internal.castUnsafeEventItem
|
|
213
|
-
->addItemToRawEvents(~inMemoryStore, ~config=ctx.config)
|
|
214
|
-
}
|
|
124
|
+
| Event({eventConfig}) => switch eventConfig.handler {
|
|
125
|
+
| Some(handler) =>
|
|
126
|
+
await item->runEventHandlerOrThrow(
|
|
127
|
+
~handler,
|
|
128
|
+
~checkpointId,
|
|
129
|
+
~inMemoryStore,
|
|
130
|
+
~loadManager,
|
|
131
|
+
~persistence=ctx.persistence,
|
|
132
|
+
~chains,
|
|
133
|
+
~config=ctx.config,
|
|
134
|
+
)
|
|
135
|
+
| None => ()
|
|
215
136
|
}
|
|
216
137
|
}
|
|
217
138
|
}
|
|
@@ -333,17 +254,11 @@ let runBatchHandlersOrThrow = async (
|
|
|
333
254
|
}
|
|
334
255
|
}
|
|
335
256
|
|
|
336
|
-
let registerProcessEventBatchMetrics = (
|
|
337
|
-
~logger,
|
|
338
|
-
~loadDuration,
|
|
339
|
-
~handlerDuration,
|
|
340
|
-
~dbWriteDuration,
|
|
341
|
-
) => {
|
|
257
|
+
let registerProcessEventBatchMetrics = (~logger, ~loadDuration, ~handlerDuration) => {
|
|
342
258
|
logger->Logging.childTrace({
|
|
343
259
|
"msg": "Finished processing batch",
|
|
344
260
|
"loader_time_elapsed": loadDuration,
|
|
345
261
|
"handlers_time_elapsed": handlerDuration,
|
|
346
|
-
"write_time_elapsed": dbWriteDuration,
|
|
347
262
|
})
|
|
348
263
|
|
|
349
264
|
Prometheus.ProcessingBatch.registerMetrics(~loadDuration, ~handlerDuration)
|
|
@@ -359,7 +274,6 @@ type logPartitionInfo = {
|
|
|
359
274
|
let processEventBatch = async (
|
|
360
275
|
~batch: Batch.t,
|
|
361
276
|
~inMemoryStore: InMemoryStore.t,
|
|
362
|
-
~isInReorgThreshold,
|
|
363
277
|
~loadManager,
|
|
364
278
|
~ctx: Ctx.t,
|
|
365
279
|
~chainFetchers: ChainMap.t<ChainFetcher.t>,
|
|
@@ -381,6 +295,9 @@ let processEventBatch = async (
|
|
|
381
295
|
})
|
|
382
296
|
|
|
383
297
|
try {
|
|
298
|
+
// Backpressure: keep processing within keepLatestChangesLimit of the cycle.
|
|
299
|
+
await inMemoryStore->InMemoryStore.awaitCapacity
|
|
300
|
+
|
|
384
301
|
let timeRef = Hrtime.makeTimer()
|
|
385
302
|
|
|
386
303
|
if batch.items->Utils.Array.notEmpty {
|
|
@@ -401,34 +318,19 @@ let processEventBatch = async (
|
|
|
401
318
|
|
|
402
319
|
let elapsedTimeAfterProcessing = timeRef->Hrtime.timeSince->Hrtime.toSecondsFloat
|
|
403
320
|
|
|
404
|
-
|
|
405
|
-
await inMemoryStore->InMemoryStore.writeBatch(
|
|
406
|
-
~persistence=ctx.persistence,
|
|
407
|
-
~batch,
|
|
408
|
-
~config=ctx.config,
|
|
409
|
-
~isInReorgThreshold,
|
|
410
|
-
)
|
|
321
|
+
inMemoryStore->InMemoryStore.commitBatch(~batch)
|
|
411
322
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
registerProcessEventBatchMetrics(
|
|
417
|
-
~logger,
|
|
418
|
-
~loadDuration=loaderDuration,
|
|
419
|
-
~handlerDuration,
|
|
420
|
-
~dbWriteDuration,
|
|
421
|
-
)
|
|
422
|
-
Ok()
|
|
423
|
-
} catch {
|
|
424
|
-
| Persistence.StorageError({message, reason}) =>
|
|
425
|
-
reason->ErrorHandling.make(~msg=message, ~logger)->Error
|
|
426
|
-
| exn => exn->ErrorHandling.make(~msg="Failed writing batch to database", ~logger)->Error
|
|
427
|
-
}
|
|
323
|
+
let loaderDuration = elapsedTimeAfterLoaders
|
|
324
|
+
let handlerDuration = elapsedTimeAfterProcessing -. loaderDuration
|
|
325
|
+
registerProcessEventBatchMetrics(~logger, ~loadDuration=loaderDuration, ~handlerDuration)
|
|
326
|
+
Ok()
|
|
428
327
|
} catch {
|
|
328
|
+
| Persistence.StorageError({message, reason}) =>
|
|
329
|
+
reason->ErrorHandling.make(~msg=message, ~logger)->Error
|
|
429
330
|
| ProcessingError({message, exn, item}) =>
|
|
430
331
|
exn
|
|
431
332
|
->ErrorHandling.make(~msg=message, ~logger=item->Logging.getItemLogger)
|
|
432
333
|
->Error
|
|
334
|
+
| exn => exn->ErrorHandling.make(~msg="Failed processing batch", ~logger)->Error
|
|
433
335
|
}
|
|
434
336
|
}
|
|
@@ -5,14 +5,12 @@ import * as Hrtime from "./bindings/Hrtime.res.mjs";
|
|
|
5
5
|
import * as Logging from "./Logging.res.mjs";
|
|
6
6
|
import * as ChainMap from "./ChainMap.res.mjs";
|
|
7
7
|
import * as Ecosystem from "./Ecosystem.res.mjs";
|
|
8
|
-
import * as EventUtils from "./EventUtils.res.mjs";
|
|
9
8
|
import * as Prometheus from "./Prometheus.res.mjs";
|
|
10
9
|
import * as Persistence from "./Persistence.res.mjs";
|
|
11
10
|
import * as UserContext from "./UserContext.res.mjs";
|
|
12
11
|
import * as ChainFetcher from "./ChainFetcher.res.mjs";
|
|
13
12
|
import * as ErrorHandling from "./ErrorHandling.res.mjs";
|
|
14
13
|
import * as InMemoryStore from "./InMemoryStore.res.mjs";
|
|
15
|
-
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
16
14
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
17
15
|
|
|
18
16
|
function allChainsEventsProcessedToEndblock(chainFetchers) {
|
|
@@ -31,55 +29,6 @@ function computeChainsState(chainFetchers) {
|
|
|
31
29
|
return chains;
|
|
32
30
|
}
|
|
33
31
|
|
|
34
|
-
function convertFieldsToJson(fields) {
|
|
35
|
-
if (fields !== undefined) {
|
|
36
|
-
return Utils.Dict.mapValues(fields, value => {
|
|
37
|
-
if (typeof value === "bigint") {
|
|
38
|
-
return value.toString();
|
|
39
|
-
} else {
|
|
40
|
-
return value;
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
} else {
|
|
44
|
-
return {};
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function addItemToRawEvents(eventItem, inMemoryStore, config) {
|
|
49
|
-
let event = eventItem.event;
|
|
50
|
-
let block = event.block;
|
|
51
|
-
let logIndex = event.logIndex;
|
|
52
|
-
let blockNumber = eventItem.blockNumber;
|
|
53
|
-
let eventConfig = eventItem.eventConfig;
|
|
54
|
-
let eventId = EventUtils.packEventIndex(blockNumber, logIndex);
|
|
55
|
-
let blockFields = convertFieldsToJson(block);
|
|
56
|
-
let transactionFields = convertFieldsToJson(event.transaction);
|
|
57
|
-
config.ecosystem.cleanUpRawEventFieldsInPlace(blockFields);
|
|
58
|
-
let params = S$RescriptSchema.reverseConvertOrThrow(event.params, eventConfig.paramsRawEventSchema);
|
|
59
|
-
let params$1 = params === null ? "null" : params;
|
|
60
|
-
let rawEvent_chain_id = eventItem.chain;
|
|
61
|
-
let rawEvent_event_name = eventConfig.name;
|
|
62
|
-
let rawEvent_contract_name = eventConfig.contractName;
|
|
63
|
-
let rawEvent_src_address = event.srcAddress;
|
|
64
|
-
let rawEvent_block_hash = config.ecosystem.getId(block);
|
|
65
|
-
let rawEvent_block_timestamp = eventItem.timestamp;
|
|
66
|
-
let rawEvent = {
|
|
67
|
-
chain_id: rawEvent_chain_id,
|
|
68
|
-
event_id: eventId,
|
|
69
|
-
event_name: rawEvent_event_name,
|
|
70
|
-
contract_name: rawEvent_contract_name,
|
|
71
|
-
block_number: blockNumber,
|
|
72
|
-
log_index: logIndex,
|
|
73
|
-
src_address: rawEvent_src_address,
|
|
74
|
-
block_hash: rawEvent_block_hash,
|
|
75
|
-
block_timestamp: rawEvent_block_timestamp,
|
|
76
|
-
block_fields: blockFields,
|
|
77
|
-
transaction_fields: transactionFields,
|
|
78
|
-
params: params$1
|
|
79
|
-
};
|
|
80
|
-
inMemoryStore.rawEvents.push(rawEvent);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
32
|
let ProcessingError = /* @__PURE__ */Primitive_exceptions.create("EventProcessing.ProcessingError");
|
|
84
33
|
|
|
85
34
|
async function runEventHandlerOrThrow(item, checkpointId, handler, inMemoryStore, loadManager, persistence, chains, config) {
|
|
@@ -119,10 +68,7 @@ async function runHandlerOrThrow(item, checkpointId, inMemoryStore, loadManager,
|
|
|
119
68
|
if (item.kind === 0) {
|
|
120
69
|
let handler = item.eventConfig.handler;
|
|
121
70
|
if (handler !== undefined) {
|
|
122
|
-
await runEventHandlerOrThrow(item, checkpointId, handler, inMemoryStore, loadManager, ctx.persistence, chains, ctx.config);
|
|
123
|
-
}
|
|
124
|
-
if (ctx.config.enableRawEvents) {
|
|
125
|
-
return addItemToRawEvents(item, inMemoryStore, ctx.config);
|
|
71
|
+
return await runEventHandlerOrThrow(item, checkpointId, handler, inMemoryStore, loadManager, ctx.persistence, chains, ctx.config);
|
|
126
72
|
} else {
|
|
127
73
|
return;
|
|
128
74
|
}
|
|
@@ -224,17 +170,16 @@ async function runBatchHandlersOrThrow(batch, inMemoryStore, loadManager, ctx, c
|
|
|
224
170
|
}
|
|
225
171
|
}
|
|
226
172
|
|
|
227
|
-
function registerProcessEventBatchMetrics(logger, loadDuration, handlerDuration
|
|
173
|
+
function registerProcessEventBatchMetrics(logger, loadDuration, handlerDuration) {
|
|
228
174
|
Logging.childTrace(logger, {
|
|
229
175
|
msg: "Finished processing batch",
|
|
230
176
|
loader_time_elapsed: loadDuration,
|
|
231
|
-
handlers_time_elapsed: handlerDuration
|
|
232
|
-
write_time_elapsed: dbWriteDuration
|
|
177
|
+
handlers_time_elapsed: handlerDuration
|
|
233
178
|
});
|
|
234
179
|
Prometheus.ProcessingBatch.registerMetrics(loadDuration, handlerDuration);
|
|
235
180
|
}
|
|
236
181
|
|
|
237
|
-
async function processEventBatch(batch, inMemoryStore,
|
|
182
|
+
async function processEventBatch(batch, inMemoryStore, loadManager, ctx, chainFetchers) {
|
|
238
183
|
let totalBatchSize = batch.totalBatchSize;
|
|
239
184
|
let chains = computeChainsState(chainFetchers);
|
|
240
185
|
let logger = Logging.getLogger();
|
|
@@ -247,6 +192,7 @@ async function processEventBatch(batch, inMemoryStore, isInReorgThreshold, loadM
|
|
|
247
192
|
}))
|
|
248
193
|
});
|
|
249
194
|
try {
|
|
195
|
+
await InMemoryStore.awaitCapacity(inMemoryStore);
|
|
250
196
|
let timeRef = Hrtime.makeTimer();
|
|
251
197
|
if (Utils.$$Array.notEmpty(batch.items)) {
|
|
252
198
|
await preloadBatchOrThrow(batch, loadManager, ctx.persistence, ctx.config, inMemoryStore, chains);
|
|
@@ -256,47 +202,37 @@ async function processEventBatch(batch, inMemoryStore, isInReorgThreshold, loadM
|
|
|
256
202
|
await runBatchHandlersOrThrow(batch, inMemoryStore, loadManager, ctx, chains);
|
|
257
203
|
}
|
|
258
204
|
let elapsedTimeAfterProcessing = Hrtime.toSecondsFloat(Hrtime.timeSince(timeRef));
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
205
|
+
InMemoryStore.commitBatch(inMemoryStore, batch);
|
|
206
|
+
let handlerDuration = elapsedTimeAfterProcessing - elapsedTimeAfterLoaders;
|
|
207
|
+
registerProcessEventBatchMetrics(logger, elapsedTimeAfterLoaders, handlerDuration);
|
|
208
|
+
return {
|
|
209
|
+
TAG: "Ok",
|
|
210
|
+
_0: undefined
|
|
211
|
+
};
|
|
212
|
+
} catch (raw_exn) {
|
|
213
|
+
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
214
|
+
if (exn.RE_EXN_ID === Persistence.StorageError) {
|
|
265
215
|
return {
|
|
266
|
-
TAG: "
|
|
267
|
-
_0:
|
|
216
|
+
TAG: "Error",
|
|
217
|
+
_0: ErrorHandling.make(exn.reason, logger, exn.message)
|
|
268
218
|
};
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
};
|
|
276
|
-
} else {
|
|
277
|
-
return {
|
|
278
|
-
TAG: "Error",
|
|
279
|
-
_0: ErrorHandling.make(exn, logger, "Failed writing batch to database")
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
} catch (raw_exn$1) {
|
|
284
|
-
let exn$1 = Primitive_exceptions.internalToException(raw_exn$1);
|
|
285
|
-
if (exn$1.RE_EXN_ID === ProcessingError) {
|
|
219
|
+
} else if (exn.RE_EXN_ID === ProcessingError) {
|
|
220
|
+
return {
|
|
221
|
+
TAG: "Error",
|
|
222
|
+
_0: ErrorHandling.make(exn.exn, Logging.getItemLogger(exn.item), exn.message)
|
|
223
|
+
};
|
|
224
|
+
} else {
|
|
286
225
|
return {
|
|
287
226
|
TAG: "Error",
|
|
288
|
-
_0: ErrorHandling.make(exn
|
|
227
|
+
_0: ErrorHandling.make(exn, logger, "Failed processing batch")
|
|
289
228
|
};
|
|
290
229
|
}
|
|
291
|
-
throw exn$1;
|
|
292
230
|
}
|
|
293
231
|
}
|
|
294
232
|
|
|
295
233
|
export {
|
|
296
234
|
allChainsEventsProcessedToEndblock,
|
|
297
235
|
computeChainsState,
|
|
298
|
-
convertFieldsToJson,
|
|
299
|
-
addItemToRawEvents,
|
|
300
236
|
ProcessingError,
|
|
301
237
|
runEventHandlerOrThrow,
|
|
302
238
|
runHandlerOrThrow,
|
package/src/FetchState.res
CHANGED
|
@@ -1730,10 +1730,7 @@ let rollback = (fetchState: t, ~targetBlockNumber) => {
|
|
|
1730
1730
|
let addressesToRemove = Utils.Set.make()
|
|
1731
1731
|
let indexingAddresses = Dict.make()
|
|
1732
1732
|
|
|
1733
|
-
fetchState.indexingAddresses
|
|
1734
|
-
->Dict.keysToArray
|
|
1735
|
-
->Array.forEach(address => {
|
|
1736
|
-
let indexingContract = fetchState.indexingAddresses->Dict.getUnsafe(address)
|
|
1733
|
+
fetchState.indexingAddresses->Utils.Dict.forEachWithKey((indexingContract, address) => {
|
|
1737
1734
|
if indexingContract.registrationBlock > targetBlockNumber {
|
|
1738
1735
|
let _ = addressesToRemove->Utils.Set.add(address->Address.unsafeFromString)
|
|
1739
1736
|
} else {
|
package/src/FetchState.res.mjs
CHANGED
|
@@ -1360,8 +1360,7 @@ function rollbackPendingQueries(mutPendingQueries, targetBlockNumber) {
|
|
|
1360
1360
|
function rollback(fetchState, targetBlockNumber) {
|
|
1361
1361
|
let addressesToRemove = new Set();
|
|
1362
1362
|
let indexingAddresses = {};
|
|
1363
|
-
|
|
1364
|
-
let indexingContract = fetchState.indexingAddresses[address];
|
|
1363
|
+
Utils.Dict.forEachWithKey(fetchState.indexingAddresses, (indexingContract, address) => {
|
|
1365
1364
|
if (indexingContract.registrationBlock > targetBlockNumber) {
|
|
1366
1365
|
addressesToRemove.add(address);
|
|
1367
1366
|
} else {
|