envio 3.3.0-alpha.0 → 3.3.0-alpha.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 +0 -6
- package/src/Batch.res.mjs +0 -11
- package/src/BatchProcessing.res +2 -12
- package/src/BatchProcessing.res.mjs +2 -7
- package/src/ChainFetching.res +22 -13
- package/src/ChainFetching.res.mjs +13 -13
- package/src/ChainMetadata.res +1 -11
- package/src/ChainMetadata.res.mjs +1 -10
- package/src/ChainState.res +176 -2
- package/src/ChainState.res.mjs +153 -9
- package/src/ChainState.resi +53 -1
- package/src/Config.res +3 -6
- package/src/Config.res.mjs +2 -3
- package/src/Core.res +7 -0
- package/src/CrossChainState.res +99 -91
- package/src/CrossChainState.res.mjs +72 -57
- package/src/CrossChainState.resi +1 -9
- package/src/Ecosystem.res +6 -1
- package/src/Env.res +0 -7
- package/src/Env.res.mjs +0 -6
- package/src/Envio.res +3 -2
- package/src/EventConfigBuilder.res +19 -5
- package/src/EventConfigBuilder.res.mjs +6 -4
- package/src/EventProcessing.res +26 -0
- package/src/EventProcessing.res.mjs +20 -0
- package/src/FetchState.res +92 -22
- package/src/FetchState.res.mjs +87 -14
- package/src/IndexerLoop.res +2 -3
- package/src/IndexerLoop.res.mjs +1 -1
- package/src/IndexerState.res +0 -8
- package/src/IndexerState.res.mjs +4 -8
- package/src/IndexerState.resi +0 -4
- package/src/Internal.res +43 -4
- package/src/Internal.res.mjs +18 -0
- package/src/Main.res +3 -50
- package/src/Main.res.mjs +3 -24
- package/src/Prometheus.res +0 -11
- package/src/Prometheus.res.mjs +83 -98
- package/src/Rollback.res +1 -1
- package/src/Rollback.res.mjs +1 -1
- package/src/SimulateItems.res +3 -0
- package/src/SimulateItems.res.mjs +2 -1
- package/src/sources/Evm.res +52 -38
- package/src/sources/Evm.res.mjs +48 -36
- package/src/sources/Fuel.res +3 -1
- package/src/sources/Fuel.res.mjs +1 -0
- package/src/sources/HyperFuelSource.res +5 -0
- package/src/sources/HyperFuelSource.res.mjs +2 -0
- package/src/sources/HyperSync.res +4 -1
- package/src/sources/HyperSync.res.mjs +5 -3
- package/src/sources/HyperSync.resi +1 -0
- package/src/sources/HyperSyncClient.res +9 -78
- package/src/sources/HyperSyncClient.res.mjs +1 -22
- package/src/sources/HyperSyncSource.res +3 -4
- package/src/sources/HyperSyncSource.res.mjs +2 -1
- package/src/sources/RpcSource.res +7 -2
- package/src/sources/RpcSource.res.mjs +3 -1
- package/src/sources/SimulateSource.res +3 -1
- package/src/sources/SimulateSource.res.mjs +1 -0
- package/src/sources/Source.res +4 -0
- package/src/sources/SourceManager.res +9 -8
- package/src/sources/SourceManager.res.mjs +21 -26
- package/src/sources/SourceManager.resi +2 -3
- package/src/sources/Svm.res +25 -2
- package/src/sources/Svm.res.mjs +27 -2
- package/src/sources/SvmHyperSyncClient.res +3 -29
- package/src/sources/SvmHyperSyncSource.res +60 -84
- package/src/sources/SvmHyperSyncSource.res.mjs +59 -78
- package/src/sources/TransactionStore.res +111 -0
- package/src/sources/TransactionStore.res.mjs +77 -0
- package/src/tui/Tui.res +13 -16
- package/src/tui/Tui.res.mjs +12 -14
|
@@ -306,7 +306,14 @@ let resolveFieldSelection = (
|
|
|
306
306
|
| Some(fields) => Utils.Set.fromArray(fields)
|
|
307
307
|
| None => globalTransactionFieldsSet
|
|
308
308
|
}
|
|
309
|
-
(
|
|
309
|
+
// The base eventConfig stores these as a string set (field names match the
|
|
310
|
+
// typed variants at runtime).
|
|
311
|
+
(
|
|
312
|
+
selectedBlockFields,
|
|
313
|
+
selectedTransactionFields->(
|
|
314
|
+
Utils.magic: Utils.Set.t<Internal.evmTransactionField> => Utils.Set.t<string>
|
|
315
|
+
),
|
|
316
|
+
)
|
|
310
317
|
}
|
|
311
318
|
|
|
312
319
|
// ============== Client-side address filter ==============
|
|
@@ -427,9 +434,8 @@ let buildSvmInstructionEventConfig = (
|
|
|
427
434
|
~programId: SvmTypes.Pubkey.t,
|
|
428
435
|
~discriminator: option<string>,
|
|
429
436
|
~discriminatorByteLen: int,
|
|
430
|
-
~includeTransaction: bool,
|
|
431
437
|
~includeLogs: bool,
|
|
432
|
-
~
|
|
438
|
+
~transactionFields: array<Internal.svmTransactionField>=[],
|
|
433
439
|
~accountFilters: array<Internal.svmAccountFilterGroup>,
|
|
434
440
|
~isInner: option<bool>,
|
|
435
441
|
~isWildcard: bool,
|
|
@@ -444,6 +450,13 @@ let buildSvmInstructionEventConfig = (
|
|
|
444
450
|
S.json(~validate=false)
|
|
445
451
|
->Utils.Schema.coerceToJsonPgType
|
|
446
452
|
->(Utils.magic: S.t<JSON.t> => S.t<Internal.eventParams>)
|
|
453
|
+
|
|
454
|
+
// The base eventConfig stores these as a string set (field names match the
|
|
455
|
+
// typed variants at runtime).
|
|
456
|
+
let selectedTransactionFields =
|
|
457
|
+
Utils.Set.fromArray(transactionFields)->(
|
|
458
|
+
Utils.magic: Utils.Set.t<Internal.svmTransactionField> => Utils.Set.t<string>
|
|
459
|
+
)
|
|
447
460
|
{
|
|
448
461
|
id: switch discriminator {
|
|
449
462
|
| Some(d) => d
|
|
@@ -462,9 +475,8 @@ let buildSvmInstructionEventConfig = (
|
|
|
462
475
|
programId,
|
|
463
476
|
discriminator,
|
|
464
477
|
discriminatorByteLen,
|
|
465
|
-
includeTransaction,
|
|
466
478
|
includeLogs,
|
|
467
|
-
|
|
479
|
+
selectedTransactionFields,
|
|
468
480
|
accountFilters,
|
|
469
481
|
isInner,
|
|
470
482
|
accounts,
|
|
@@ -530,6 +542,8 @@ let buildFuelEventConfig = (
|
|
|
530
542
|
filterByAddresses: false,
|
|
531
543
|
dependsOnAddresses: !isWildcard,
|
|
532
544
|
startBlock,
|
|
545
|
+
// Fuel keeps the transaction inline on the payload; nothing to materialise.
|
|
546
|
+
selectedTransactionFields: Utils.Set.make(),
|
|
533
547
|
kind: fuelKind,
|
|
534
548
|
}
|
|
535
549
|
}
|
|
@@ -297,20 +297,22 @@ function buildEvmEventConfig(contractName, eventName, sighash, params, isWildcar
|
|
|
297
297
|
paramsRawEventSchema: buildParamsSchema(params),
|
|
298
298
|
simulateParamsSchema: buildSimulateParamsSchema(params),
|
|
299
299
|
startBlock: resolvedStartBlock,
|
|
300
|
+
selectedTransactionFields: match$1[1],
|
|
300
301
|
getEventFiltersOrThrow: match.getEventFiltersOrThrow,
|
|
301
302
|
selectedBlockFields: match$1[0],
|
|
302
|
-
selectedTransactionFields: match$1[1],
|
|
303
303
|
sighash: sighash,
|
|
304
304
|
topicCount: topicCount,
|
|
305
305
|
paramsMetadata: params
|
|
306
306
|
};
|
|
307
307
|
}
|
|
308
308
|
|
|
309
|
-
function buildSvmInstructionEventConfig(contractName, instructionName, programId, discriminator, discriminatorByteLen,
|
|
309
|
+
function buildSvmInstructionEventConfig(contractName, instructionName, programId, discriminator, discriminatorByteLen, includeLogs, transactionFieldsOpt, accountFilters, isInner, isWildcard, handler, contractRegister, accountsOpt, argsOpt, definedTypesOpt, startBlock) {
|
|
310
|
+
let transactionFields = transactionFieldsOpt !== undefined ? transactionFieldsOpt : [];
|
|
310
311
|
let accounts = accountsOpt !== undefined ? accountsOpt : [];
|
|
311
312
|
let args = argsOpt !== undefined ? argsOpt : null;
|
|
312
313
|
let definedTypes = definedTypesOpt !== undefined ? definedTypesOpt : null;
|
|
313
314
|
let paramsSchema = Utils.Schema.coerceToJsonPgType(S$RescriptSchema.json(false));
|
|
315
|
+
let selectedTransactionFields = new Set(transactionFields);
|
|
314
316
|
return {
|
|
315
317
|
id: discriminator !== undefined ? discriminator : "none",
|
|
316
318
|
name: instructionName,
|
|
@@ -323,12 +325,11 @@ function buildSvmInstructionEventConfig(contractName, instructionName, programId
|
|
|
323
325
|
paramsRawEventSchema: paramsSchema,
|
|
324
326
|
simulateParamsSchema: paramsSchema,
|
|
325
327
|
startBlock: startBlock,
|
|
328
|
+
selectedTransactionFields: selectedTransactionFields,
|
|
326
329
|
programId: programId,
|
|
327
330
|
discriminator: discriminator,
|
|
328
331
|
discriminatorByteLen: discriminatorByteLen,
|
|
329
|
-
includeTransaction: includeTransaction,
|
|
330
332
|
includeLogs: includeLogs,
|
|
331
|
-
includeTokenBalances: includeTokenBalances,
|
|
332
333
|
accountFilters: accountFilters,
|
|
333
334
|
isInner: isInner,
|
|
334
335
|
accounts: accounts,
|
|
@@ -392,6 +393,7 @@ function buildFuelEventConfig(contractName, eventName, kind, sighash, rawAbi, is
|
|
|
392
393
|
paramsRawEventSchema: paramsSchema,
|
|
393
394
|
simulateParamsSchema: paramsSchema,
|
|
394
395
|
startBlock: startBlock,
|
|
396
|
+
selectedTransactionFields: new Set(),
|
|
395
397
|
kind: fuelKind
|
|
396
398
|
};
|
|
397
399
|
}
|
package/src/EventProcessing.res
CHANGED
|
@@ -281,6 +281,29 @@ type logPartitionInfo = {
|
|
|
281
281
|
lastItemBlockNumber?: int,
|
|
282
282
|
}
|
|
283
283
|
|
|
284
|
+
// Off the hot path: bulk-materialise the selected transaction fields for the
|
|
285
|
+
// batch's store-backed (HyperSync) items and write them onto the payloads, so
|
|
286
|
+
// handlers read plain objects. A batch can span chains, each with its own store
|
|
287
|
+
// and field mask, so group items by chain before materialising.
|
|
288
|
+
let materializeBatchTransactions = async (batch: Batch.t, ~chainStates: dict<ChainState.t>) => {
|
|
289
|
+
let itemsByChain: dict<array<Internal.item>> = Dict.make()
|
|
290
|
+
batch.items->Array.forEach(item => {
|
|
291
|
+
let chainId = item->Internal.getItemChainId->Int.toString
|
|
292
|
+
switch itemsByChain->Utils.Dict.dangerouslyGetNonOption(chainId) {
|
|
293
|
+
| Some(items) => items->Array.push(item)
|
|
294
|
+
| None => itemsByChain->Dict.set(chainId, [item])
|
|
295
|
+
}
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
let _ = await itemsByChain
|
|
299
|
+
->Dict.toArray
|
|
300
|
+
->Array.map(async ((chainId, items)) => {
|
|
301
|
+
let cs = chainStates->Dict.getUnsafe(chainId)
|
|
302
|
+
await cs->ChainState.materializeBatchItems(~items)
|
|
303
|
+
})
|
|
304
|
+
->Promise.all
|
|
305
|
+
}
|
|
306
|
+
|
|
284
307
|
let processEventBatch = async (
|
|
285
308
|
~batch: Batch.t,
|
|
286
309
|
~indexerState: IndexerState.t,
|
|
@@ -312,6 +335,9 @@ let processEventBatch = async (
|
|
|
312
335
|
let timeRef = Hrtime.makeTimer()
|
|
313
336
|
|
|
314
337
|
if batch.items->Utils.Array.notEmpty {
|
|
338
|
+
// Materialise store-backed transactions onto payloads before any handler
|
|
339
|
+
// (preload or execute) reads them.
|
|
340
|
+
await materializeBatchTransactions(batch, ~chainStates)
|
|
315
341
|
await batch->preloadBatchOrThrow(~loadManager, ~persistence, ~indexerState, ~chains, ~config)
|
|
316
342
|
}
|
|
317
343
|
|
|
@@ -4,6 +4,7 @@ import * as Utils from "./Utils.res.mjs";
|
|
|
4
4
|
import * as Hrtime from "./bindings/Hrtime.res.mjs";
|
|
5
5
|
import * as Logging from "./Logging.res.mjs";
|
|
6
6
|
import * as Writing from "./Writing.res.mjs";
|
|
7
|
+
import * as Internal from "./Internal.res.mjs";
|
|
7
8
|
import * as Ecosystem from "./Ecosystem.res.mjs";
|
|
8
9
|
import * as ChainState from "./ChainState.res.mjs";
|
|
9
10
|
import * as Prometheus from "./Prometheus.res.mjs";
|
|
@@ -180,6 +181,23 @@ function registerProcessEventBatchMetrics(logger, loadDuration, handlerDuration)
|
|
|
180
181
|
Prometheus.ProcessingBatch.registerMetrics(loadDuration, handlerDuration);
|
|
181
182
|
}
|
|
182
183
|
|
|
184
|
+
async function materializeBatchTransactions(batch, chainStates) {
|
|
185
|
+
let itemsByChain = {};
|
|
186
|
+
batch.items.forEach(item => {
|
|
187
|
+
let chainId = Internal.getItemChainId(item).toString();
|
|
188
|
+
let items = itemsByChain[chainId];
|
|
189
|
+
if (items !== undefined) {
|
|
190
|
+
items.push(item);
|
|
191
|
+
} else {
|
|
192
|
+
itemsByChain[chainId] = [item];
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
await Promise.all(Object.entries(itemsByChain).map(async param => {
|
|
196
|
+
let cs = chainStates[param[0]];
|
|
197
|
+
return await ChainState.materializeBatchItems(cs, param[1]);
|
|
198
|
+
}));
|
|
199
|
+
}
|
|
200
|
+
|
|
183
201
|
async function processEventBatch(batch, indexerState, loadManager, persistence, config, chainStates) {
|
|
184
202
|
let totalBatchSize = batch.totalBatchSize;
|
|
185
203
|
let chains = computeChainsState(chainStates);
|
|
@@ -196,6 +214,7 @@ async function processEventBatch(batch, indexerState, loadManager, persistence,
|
|
|
196
214
|
await Writing.awaitCapacity(indexerState);
|
|
197
215
|
let timeRef = Hrtime.makeTimer();
|
|
198
216
|
if (Utils.$$Array.notEmpty(batch.items)) {
|
|
217
|
+
await materializeBatchTransactions(batch, chainStates);
|
|
199
218
|
await preloadBatchOrThrow(batch, loadManager, persistence, config, indexerState, chains);
|
|
200
219
|
}
|
|
201
220
|
let elapsedTimeAfterLoaders = Hrtime.toSecondsFloat(Hrtime.timeSince(timeRef));
|
|
@@ -240,6 +259,7 @@ export {
|
|
|
240
259
|
preloadBatchOrThrow,
|
|
241
260
|
runBatchHandlersOrThrow,
|
|
242
261
|
registerProcessEventBatchMetrics,
|
|
262
|
+
materializeBatchTransactions,
|
|
243
263
|
processEventBatch,
|
|
244
264
|
}
|
|
245
265
|
/* Utils Not a pure module */
|
package/src/FetchState.res
CHANGED
|
@@ -19,6 +19,9 @@ type pendingQuery = {
|
|
|
19
19
|
fromBlock: int,
|
|
20
20
|
toBlock: option<int>,
|
|
21
21
|
isChunk: bool,
|
|
22
|
+
// Estimated items this in-flight query will add, carried from the query so the
|
|
23
|
+
// shared buffer budget can account for what's already being fetched.
|
|
24
|
+
estResponseSize: float,
|
|
22
25
|
// Stores latestFetchedBlock when query completes. Only needed to persist
|
|
23
26
|
// timestamp while earlier queries are still pending before updating
|
|
24
27
|
// the partition's latestFetchedBlock.
|
|
@@ -47,6 +50,10 @@ type partition = {
|
|
|
47
50
|
// Track last 3 successful query ranges for chunking heuristic (0 means no data)
|
|
48
51
|
prevQueryRange: int,
|
|
49
52
|
prevPrevQueryRange: int,
|
|
53
|
+
// Item count of the response that set prevQueryRange. Paired with it to derive
|
|
54
|
+
// the partition's event density (prevRangeSize / prevQueryRange) for sizing
|
|
55
|
+
// queries against the buffer budget.
|
|
56
|
+
prevRangeSize: int,
|
|
50
57
|
// Tracks the latestFetchedBlock.blockNumber of the most recent response
|
|
51
58
|
// that updated prevQueryRange. Prevents degradation of the chunking heuristic
|
|
52
59
|
// when parallel query responses arrive out of order.
|
|
@@ -58,11 +65,39 @@ type query = {
|
|
|
58
65
|
fromBlock: int,
|
|
59
66
|
toBlock: option<int>,
|
|
60
67
|
isChunk: bool,
|
|
68
|
+
// Estimated number of items the query will return, from the partition's
|
|
69
|
+
// density and the query's block range. Used to admit queries against the
|
|
70
|
+
// shared buffer budget.
|
|
71
|
+
estResponseSize: float,
|
|
72
|
+
// Owning chain and the chain progress % at the query's fromBlock. Set by the
|
|
73
|
+
// cross-chain scheduler so candidate queries can be pooled and ordered
|
|
74
|
+
// (furthest-behind first) without allocating a side tuple per query.
|
|
75
|
+
mutable chainId: int,
|
|
76
|
+
mutable progress: float,
|
|
61
77
|
selection: selection,
|
|
62
78
|
addressesByContractName: dict<array<Address.t>>,
|
|
63
79
|
indexingAddresses: dict<indexingAddress>,
|
|
64
80
|
}
|
|
65
81
|
|
|
82
|
+
// Default estimate for a query whose partition hasn't responded yet, so the
|
|
83
|
+
// shared budget still accounts for unknown queries instead of treating them as
|
|
84
|
+
// free.
|
|
85
|
+
let defaultEstResponseSize = 10_000.
|
|
86
|
+
|
|
87
|
+
// Estimated items a query will return, from the partition's event density
|
|
88
|
+
// (items/block derived from its last response) and the query's block range.
|
|
89
|
+
// toBlock None is the open-ended tail, capped at maxQueryBlockNumber. A partition
|
|
90
|
+
// that responded with no items has density 0, so its queries cost 0 — correct,
|
|
91
|
+
// they don't fill the buffer. Only a partition that has never responded
|
|
92
|
+
// (prevQueryRange 0) has no signal, so it falls back to defaultEstResponseSize.
|
|
93
|
+
let calculateEstResponseSize = (p: partition, ~fromBlock, ~toBlock, ~maxQueryBlockNumber) =>
|
|
94
|
+
if p.prevQueryRange > 0 {
|
|
95
|
+
let density = p.prevRangeSize->Int.toFloat /. p.prevQueryRange->Int.toFloat
|
|
96
|
+
(toBlock->Option.getOr(maxQueryBlockNumber) - fromBlock + 1)->Int.toFloat *. density
|
|
97
|
+
} else {
|
|
98
|
+
defaultEstResponseSize
|
|
99
|
+
}
|
|
100
|
+
|
|
66
101
|
// Calculate the chunk range from history using min-of-last-3-ranges heuristic
|
|
67
102
|
let getMinHistoryRange = (p: partition) => {
|
|
68
103
|
switch (p.prevQueryRange, p.prevPrevQueryRange) {
|
|
@@ -160,6 +195,7 @@ module OptimizedPartitions = {
|
|
|
160
195
|
mutPendingQueries: [],
|
|
161
196
|
prevQueryRange: minRange,
|
|
162
197
|
prevPrevQueryRange: minRange,
|
|
198
|
+
prevRangeSize: 0,
|
|
163
199
|
latestBlockRangeUpdateBlock: 0,
|
|
164
200
|
}
|
|
165
201
|
}
|
|
@@ -397,6 +433,7 @@ module OptimizedPartitions = {
|
|
|
397
433
|
optimizedPartitions: t,
|
|
398
434
|
~query,
|
|
399
435
|
~knownHeight,
|
|
436
|
+
~itemsCount,
|
|
400
437
|
~latestFetchedBlock: blockNumberAndTimestamp,
|
|
401
438
|
) => {
|
|
402
439
|
let p = optimizedPartitions->getOrThrow(~partitionId=query.partitionId)
|
|
@@ -428,6 +465,7 @@ module OptimizedPartitions = {
|
|
|
428
465
|
}
|
|
429
466
|
let updatedPrevQueryRange = shouldUpdateBlockRange ? blockRange : p.prevQueryRange
|
|
430
467
|
let updatedPrevPrevQueryRange = shouldUpdateBlockRange ? p.prevQueryRange : p.prevPrevQueryRange
|
|
468
|
+
let updatedPrevRangeSize = shouldUpdateBlockRange ? itemsCount : p.prevRangeSize
|
|
431
469
|
|
|
432
470
|
// Process fetched queries from front of queue for main partition
|
|
433
471
|
let updatedLatestFetchedBlock = consumeFetchedQueries(
|
|
@@ -449,6 +487,7 @@ module OptimizedPartitions = {
|
|
|
449
487
|
latestFetchedBlock: updatedLatestFetchedBlock,
|
|
450
488
|
prevQueryRange: updatedPrevQueryRange,
|
|
451
489
|
prevPrevQueryRange: updatedPrevPrevQueryRange,
|
|
490
|
+
prevRangeSize: updatedPrevRangeSize,
|
|
452
491
|
latestBlockRangeUpdateBlock: shouldUpdateBlockRange
|
|
453
492
|
? latestFetchedBlock.blockNumber
|
|
454
493
|
: p.latestBlockRangeUpdateBlock,
|
|
@@ -497,7 +536,7 @@ type t = {
|
|
|
497
536
|
// Buffer of items ordered from earliest to latest
|
|
498
537
|
buffer: array<Internal.item>,
|
|
499
538
|
// Caps how far ahead onBlock items are pre-generated (set to 2x the batch
|
|
500
|
-
// size). Fetch depth is bounded separately by getNextQuery's
|
|
539
|
+
// size). Fetch depth is bounded separately by getNextQuery's itemBudget, the
|
|
501
540
|
// chain's per-tick slice of the indexer-wide pool.
|
|
502
541
|
maxOnBlockBufferSize: int,
|
|
503
542
|
onBlockConfigs: array<Internal.onBlockConfig>,
|
|
@@ -838,6 +877,7 @@ OptimizedPartitions.t => {
|
|
|
838
877
|
mutPendingQueries: [],
|
|
839
878
|
prevQueryRange: 0,
|
|
840
879
|
prevPrevQueryRange: 0,
|
|
880
|
+
prevRangeSize: 0,
|
|
841
881
|
latestBlockRangeUpdateBlock: 0,
|
|
842
882
|
})
|
|
843
883
|
nextPartitionIndexRef := nextPartitionIndexRef.contents + 1
|
|
@@ -1179,6 +1219,7 @@ let registerDynamicContracts = (
|
|
|
1179
1219
|
mutPendingQueries: p.mutPendingQueries,
|
|
1180
1220
|
prevQueryRange: p.prevQueryRange,
|
|
1181
1221
|
prevPrevQueryRange: p.prevPrevQueryRange,
|
|
1222
|
+
prevRangeSize: p.prevRangeSize,
|
|
1182
1223
|
latestBlockRangeUpdateBlock: p.latestBlockRangeUpdateBlock,
|
|
1183
1224
|
})
|
|
1184
1225
|
}
|
|
@@ -1241,6 +1282,7 @@ let handleQueryResult = (
|
|
|
1241
1282
|
~optimizedPartitions=fetchState.optimizedPartitions->OptimizedPartitions.handleQueryResponse(
|
|
1242
1283
|
~query,
|
|
1243
1284
|
~knownHeight=fetchState.knownHeight,
|
|
1285
|
+
~itemsCount=newItems->Array.length,
|
|
1244
1286
|
~latestFetchedBlock,
|
|
1245
1287
|
),
|
|
1246
1288
|
~mutItems=?{
|
|
@@ -1253,7 +1295,6 @@ let handleQueryResult = (
|
|
|
1253
1295
|
}
|
|
1254
1296
|
|
|
1255
1297
|
type nextQuery =
|
|
1256
|
-
| ReachedMaxConcurrency
|
|
1257
1298
|
| WaitingForNewBlock
|
|
1258
1299
|
| NothingToQuery
|
|
1259
1300
|
| Ready(array<query>)
|
|
@@ -1267,6 +1308,7 @@ let startFetchingQueries = ({optimizedPartitions}: t, ~queries: array<query>) =>
|
|
|
1267
1308
|
fromBlock: q.fromBlock,
|
|
1268
1309
|
toBlock: q.toBlock,
|
|
1269
1310
|
isChunk: q.isChunk,
|
|
1311
|
+
estResponseSize: q.estResponseSize,
|
|
1270
1312
|
fetchedBlock: None,
|
|
1271
1313
|
}
|
|
1272
1314
|
|
|
@@ -1295,6 +1337,7 @@ let pushQueriesForRange = (
|
|
|
1295
1337
|
~rangeEndBlock: option<int>,
|
|
1296
1338
|
~maxQueryBlockNumber: int,
|
|
1297
1339
|
~maybeChunkRange: option<int>,
|
|
1340
|
+
~partition: partition,
|
|
1298
1341
|
~selection: selection,
|
|
1299
1342
|
~addressesByContractName: dict<array<Address.t>>,
|
|
1300
1343
|
~indexingAddresses: dict<indexingAddress>,
|
|
@@ -1311,6 +1354,14 @@ let pushQueriesForRange = (
|
|
|
1311
1354
|
toBlock: rangeEndBlock,
|
|
1312
1355
|
selection,
|
|
1313
1356
|
isChunk: false,
|
|
1357
|
+
estResponseSize: calculateEstResponseSize(
|
|
1358
|
+
partition,
|
|
1359
|
+
~fromBlock=rangeFromBlock,
|
|
1360
|
+
~toBlock=rangeEndBlock,
|
|
1361
|
+
~maxQueryBlockNumber,
|
|
1362
|
+
),
|
|
1363
|
+
chainId: 0,
|
|
1364
|
+
progress: 0.,
|
|
1314
1365
|
addressesByContractName,
|
|
1315
1366
|
indexingAddresses,
|
|
1316
1367
|
})
|
|
@@ -1338,6 +1389,14 @@ let pushQueriesForRange = (
|
|
|
1338
1389
|
toBlock: Some(chunkToBlock),
|
|
1339
1390
|
isChunk: true,
|
|
1340
1391
|
selection,
|
|
1392
|
+
estResponseSize: calculateEstResponseSize(
|
|
1393
|
+
partition,
|
|
1394
|
+
~fromBlock=chunkFromBlock.contents,
|
|
1395
|
+
~toBlock=Some(chunkToBlock),
|
|
1396
|
+
~maxQueryBlockNumber,
|
|
1397
|
+
),
|
|
1398
|
+
chainId: 0,
|
|
1399
|
+
progress: 0.,
|
|
1341
1400
|
addressesByContractName,
|
|
1342
1401
|
indexingAddresses,
|
|
1343
1402
|
})
|
|
@@ -1352,6 +1411,14 @@ let pushQueriesForRange = (
|
|
|
1352
1411
|
toBlock: rangeEndBlock,
|
|
1353
1412
|
selection,
|
|
1354
1413
|
isChunk: rangeEndBlock !== None,
|
|
1414
|
+
estResponseSize: calculateEstResponseSize(
|
|
1415
|
+
partition,
|
|
1416
|
+
~fromBlock=rangeFromBlock,
|
|
1417
|
+
~toBlock=rangeEndBlock,
|
|
1418
|
+
~maxQueryBlockNumber,
|
|
1419
|
+
),
|
|
1420
|
+
chainId: 0,
|
|
1421
|
+
progress: 0.,
|
|
1355
1422
|
addressesByContractName,
|
|
1356
1423
|
indexingAddresses,
|
|
1357
1424
|
})
|
|
@@ -1373,15 +1440,13 @@ let getNextQuery = (
|
|
|
1373
1440
|
latestOnBlockBlockNumber,
|
|
1374
1441
|
knownHeight,
|
|
1375
1442
|
} as fetchState: t,
|
|
1376
|
-
~
|
|
1377
|
-
~
|
|
1443
|
+
~budget,
|
|
1444
|
+
~chainPendingBudget,
|
|
1378
1445
|
) => {
|
|
1379
1446
|
let headBlockNumber = knownHeight - blockLag
|
|
1380
1447
|
if headBlockNumber <= 0 {
|
|
1381
1448
|
WaitingForNewBlock
|
|
1382
|
-
} else if
|
|
1383
|
-
ReachedMaxConcurrency
|
|
1384
|
-
} else if bufferLimit <= 0 {
|
|
1449
|
+
} else if budget <= 0 {
|
|
1385
1450
|
// No room left in the shared buffer pool for this chain; wait for processing
|
|
1386
1451
|
// to drain before fetching more.
|
|
1387
1452
|
NothingToQuery
|
|
@@ -1395,12 +1460,11 @@ let getNextQuery = (
|
|
|
1395
1460
|
!isOnBlockBehindTheHead,
|
|
1396
1461
|
)
|
|
1397
1462
|
|
|
1398
|
-
// Limit how far ahead we fetch to
|
|
1399
|
-
//
|
|
1400
|
-
//
|
|
1401
|
-
// buffer drains.
|
|
1463
|
+
// Limit how far ahead we fetch to budget items (plus what's already in
|
|
1464
|
+
// flight) so processing always has buffer without ballooning memory. A
|
|
1465
|
+
// partition that fetched further is skipped until the buffer drains.
|
|
1402
1466
|
let maxQueryBlockNumber = {
|
|
1403
|
-
switch buffer->Array.get(
|
|
1467
|
+
switch buffer->Array.get(budget + chainPendingBudget->Float.toInt - 1) {
|
|
1404
1468
|
| Some(item) =>
|
|
1405
1469
|
// Just in case check that we don't query beyond the current block
|
|
1406
1470
|
Pervasives.min(item->Internal.getItemBlockNumber, knownHeight)
|
|
@@ -1469,6 +1533,7 @@ let getNextQuery = (
|
|
|
1469
1533
|
~rangeEndBlock=Utils.Math.minOptInt(Some(pq.fromBlock - 1), queryEndBlock),
|
|
1470
1534
|
~maxQueryBlockNumber,
|
|
1471
1535
|
~maybeChunkRange,
|
|
1536
|
+
~partition=p,
|
|
1472
1537
|
~selection=p.selection,
|
|
1473
1538
|
~addressesByContractName=p.addressesByContractName,
|
|
1474
1539
|
~indexingAddresses,
|
|
@@ -1493,6 +1558,7 @@ let getNextQuery = (
|
|
|
1493
1558
|
~rangeEndBlock=queryEndBlock,
|
|
1494
1559
|
~maxQueryBlockNumber,
|
|
1495
1560
|
~maybeChunkRange,
|
|
1561
|
+
~partition=p,
|
|
1496
1562
|
~selection=p.selection,
|
|
1497
1563
|
~addressesByContractName=p.addressesByContractName,
|
|
1498
1564
|
~indexingAddresses,
|
|
@@ -1500,9 +1566,8 @@ let getNextQuery = (
|
|
|
1500
1566
|
}
|
|
1501
1567
|
|
|
1502
1568
|
// Cap parallel in-flight chunks per partition so a single partition can't
|
|
1503
|
-
//
|
|
1504
|
-
//
|
|
1505
|
-
// round once these resolve.
|
|
1569
|
+
// monopolize fetching. Keep the earliest new chunks; the furthest-ahead
|
|
1570
|
+
// ones wait for the next round once these resolve.
|
|
1506
1571
|
let maxNewChunks = Pervasives.max(0, maxPendingChunksPerPartition - pendingCount)
|
|
1507
1572
|
let generatedCount = queries->Array.length - partitionQueriesStart
|
|
1508
1573
|
if generatedCount > maxNewChunks {
|
|
@@ -1525,13 +1590,6 @@ let getNextQuery = (
|
|
|
1525
1590
|
NothingToQuery
|
|
1526
1591
|
}
|
|
1527
1592
|
} else {
|
|
1528
|
-
// Enforce concurrency limit: sort by fromBlock and take the first concurrencyLimit
|
|
1529
|
-
let queries = if queries->Array.length > concurrencyLimit {
|
|
1530
|
-
queries->Array.sort((a, b) => Int.compare(a.fromBlock, b.fromBlock))
|
|
1531
|
-
queries->Array.slice(~start=0, ~end=concurrencyLimit)
|
|
1532
|
-
} else {
|
|
1533
|
-
queries
|
|
1534
|
-
}
|
|
1535
1593
|
Ready(queries)
|
|
1536
1594
|
}
|
|
1537
1595
|
}
|
|
@@ -1641,6 +1699,7 @@ let make = (
|
|
|
1641
1699
|
mutPendingQueries: [],
|
|
1642
1700
|
prevQueryRange: 0,
|
|
1643
1701
|
prevPrevQueryRange: 0,
|
|
1702
|
+
prevRangeSize: 0,
|
|
1644
1703
|
latestBlockRangeUpdateBlock: 0,
|
|
1645
1704
|
})
|
|
1646
1705
|
}
|
|
@@ -2025,6 +2084,17 @@ let getProgressPercentage = (fetchState: t) => {
|
|
|
2025
2084
|
}
|
|
2026
2085
|
}
|
|
2027
2086
|
|
|
2087
|
+
// Progress a specific block sits at along the chain, used to order queries from
|
|
2088
|
+
// different chains: a query starting further back (lower %) is fetched first.
|
|
2089
|
+
let getProgressPercentageAt = (fetchState: t, ~blockNumber) => {
|
|
2090
|
+
switch fetchState.firstEventBlock {
|
|
2091
|
+
| None => 0.
|
|
2092
|
+
| Some(firstEventBlock) =>
|
|
2093
|
+
let totalRange = fetchState.knownHeight - firstEventBlock
|
|
2094
|
+
totalRange <= 0 ? 0. : (blockNumber - firstEventBlock)->Int.toFloat /. totalRange->Int.toFloat
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
|
|
2028
2098
|
let sortForBatch = {
|
|
2029
2099
|
let hasFullBatch = ({buffer} as fetchState: t, ~batchSizeTarget) => {
|
|
2030
2100
|
switch buffer->Array.get(batchSizeTarget - 1) {
|