envio 3.3.0-alpha.5 → 3.3.0-alpha.7
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/Address.res +5 -2
- package/src/Address.res.mjs +3 -1
- package/src/BatchProcessing.res +5 -0
- package/src/BatchProcessing.res.mjs +6 -0
- package/src/ChainFetching.res +14 -25
- package/src/ChainFetching.res.mjs +19 -20
- package/src/ChainState.res +4 -4
- package/src/ChainState.res.mjs +2 -2
- package/src/ChainState.resi +1 -1
- package/src/Config.res +44 -0
- package/src/Config.res.mjs +38 -0
- package/src/ContractRegisterContext.res +2 -12
- package/src/ContractRegisterContext.res.mjs +3 -5
- package/src/CrossChainState.res +5 -3
- package/src/CrossChainState.res.mjs +5 -4
- package/src/ExitOnCaughtUp.res +10 -2
- package/src/ExitOnCaughtUp.res.mjs +10 -1
- package/src/FetchState.res +49 -52
- package/src/FetchState.res.mjs +45 -47
- package/src/IndexerState.res +4 -0
- package/src/IndexerState.res.mjs +8 -1
- package/src/IndexerState.resi +1 -0
- package/src/SimulateDeadInputTracker.res +85 -0
- package/src/SimulateDeadInputTracker.res.mjs +73 -0
- package/src/SimulateDeadInputTracker.resi +12 -0
- package/src/SimulateItems.res +29 -43
- package/src/SimulateItems.res.mjs +16 -33
- package/src/TestIndexer.res +3 -27
- package/src/TestIndexer.res.mjs +2 -9
- package/src/sources/EvmChain.res +1 -1
- package/src/sources/EvmChain.res.mjs +1 -1
- package/src/sources/HyperFuelSource.res +1 -0
- package/src/sources/HyperFuelSource.res.mjs +1 -1
- package/src/sources/HyperSync.res +4 -0
- package/src/sources/HyperSync.res.mjs +5 -4
- package/src/sources/HyperSync.resi +1 -0
- package/src/sources/HyperSyncSource.res +2 -0
- package/src/sources/HyperSyncSource.res.mjs +2 -2
- package/src/sources/RpcSource.res +287 -146
- package/src/sources/RpcSource.res.mjs +286 -247
- package/src/sources/SimulateSource.res +2 -0
- package/src/sources/SimulateSource.res.mjs +3 -2
- package/src/sources/Source.res +10 -0
- package/src/sources/SourceManager.res +7 -0
- package/src/sources/SourceManager.res.mjs +2 -1
- package/src/sources/Svm.res +1 -0
- package/src/sources/Svm.res.mjs +1 -1
- package/src/sources/SvmHyperSyncSource.res +2 -0
- package/src/sources/SvmHyperSyncSource.res.mjs +4 -2
package/src/FetchState.res
CHANGED
|
@@ -90,23 +90,41 @@ let deriveContractNameByAddress: dict<array<Address.t>> => dict<
|
|
|
90
90
|
result
|
|
91
91
|
})
|
|
92
92
|
|
|
93
|
+
// Bounds for the no-history default estimate below. Also used by SourceManager
|
|
94
|
+
// as the floor for its own maxNumLogs-style safety net.
|
|
95
|
+
let minEstResponseSize = 2_000.
|
|
96
|
+
let maxEstResponseSize = 10_000.
|
|
97
|
+
|
|
93
98
|
// Default estimate for a query whose partition hasn't responded yet, so the
|
|
94
99
|
// shared budget still accounts for unknown queries instead of treating them as
|
|
95
|
-
// free.
|
|
96
|
-
|
|
100
|
+
// free. Scaled down as partitionsCount grows: assuming every partition's first
|
|
101
|
+
// query is "big" starves the rest of that tick's admission when many
|
|
102
|
+
// partitions share the same buffer, so the per-partition default shrinks
|
|
103
|
+
// accordingly — clamped to [minEstResponseSize, maxEstResponseSize] either way.
|
|
104
|
+
let calculateDefaultEstResponseSize = (~partitionsCount) =>
|
|
105
|
+
Pervasives.max(
|
|
106
|
+
minEstResponseSize,
|
|
107
|
+
Pervasives.min(maxEstResponseSize, 20_000. /. partitionsCount->Int.toFloat),
|
|
108
|
+
)
|
|
97
109
|
|
|
98
110
|
// Estimated items a query will return, from the partition's event density
|
|
99
111
|
// (items/block derived from its last response) and the query's block range.
|
|
100
|
-
// toBlock None is the open-ended tail, capped at
|
|
112
|
+
// toBlock None is the open-ended tail, capped at knownHeight. A partition
|
|
101
113
|
// that responded with no items has density 0, so its queries cost 0 — correct,
|
|
102
114
|
// they don't fill the buffer. Only a partition that has never responded
|
|
103
|
-
// (prevQueryRange 0) has no signal, so it falls back to
|
|
104
|
-
let calculateEstResponseSize = (
|
|
115
|
+
// (prevQueryRange 0) has no signal, so it falls back to calculateDefaultEstResponseSize.
|
|
116
|
+
let calculateEstResponseSize = (
|
|
117
|
+
p: partition,
|
|
118
|
+
~fromBlock,
|
|
119
|
+
~toBlock,
|
|
120
|
+
~knownHeight,
|
|
121
|
+
~partitionsCount,
|
|
122
|
+
) =>
|
|
105
123
|
if p.prevQueryRange > 0 {
|
|
106
124
|
let density = p.prevRangeSize->Int.toFloat /. p.prevQueryRange->Int.toFloat
|
|
107
|
-
(toBlock->Option.getOr(
|
|
125
|
+
(toBlock->Option.getOr(knownHeight) - fromBlock + 1)->Int.toFloat *. density
|
|
108
126
|
} else {
|
|
109
|
-
|
|
127
|
+
calculateDefaultEstResponseSize(~partitionsCount)
|
|
110
128
|
}
|
|
111
129
|
|
|
112
130
|
// Calculate the chunk range from history using min-of-last-3-ranges heuristic
|
|
@@ -545,8 +563,8 @@ type t = {
|
|
|
545
563
|
// Buffer of items ordered from earliest to latest
|
|
546
564
|
buffer: array<Internal.item>,
|
|
547
565
|
// Caps how far ahead onBlock items are pre-generated (set to 2x the batch
|
|
548
|
-
// size).
|
|
549
|
-
// chain
|
|
566
|
+
// size). Event fetch depth is bounded separately, by CrossChainState's
|
|
567
|
+
// cross-chain admission against the indexer-wide buffer pool.
|
|
550
568
|
maxOnBlockBufferSize: int,
|
|
551
569
|
onBlockConfigs: array<Internal.onBlockConfig>,
|
|
552
570
|
knownHeight: int,
|
|
@@ -1352,14 +1370,15 @@ let pushQueriesForRange = (
|
|
|
1352
1370
|
~partitionId: string,
|
|
1353
1371
|
~rangeFromBlock: int,
|
|
1354
1372
|
~rangeEndBlock: option<int>,
|
|
1355
|
-
~
|
|
1373
|
+
~knownHeight: int,
|
|
1356
1374
|
~maybeChunkRange: option<int>,
|
|
1357
1375
|
~maxChunks: int,
|
|
1358
1376
|
~partition: partition,
|
|
1359
1377
|
~selection: selection,
|
|
1360
1378
|
~addressesByContractName: dict<array<Address.t>>,
|
|
1379
|
+
~partitionsCount: int,
|
|
1361
1380
|
) => {
|
|
1362
|
-
if rangeFromBlock <=
|
|
1381
|
+
if rangeFromBlock <= knownHeight && maxChunks > 0 {
|
|
1363
1382
|
switch rangeEndBlock {
|
|
1364
1383
|
| Some(endBlock) if rangeFromBlock > endBlock => ()
|
|
1365
1384
|
| _ =>
|
|
@@ -1375,7 +1394,8 @@ let pushQueriesForRange = (
|
|
|
1375
1394
|
partition,
|
|
1376
1395
|
~fromBlock=rangeFromBlock,
|
|
1377
1396
|
~toBlock=rangeEndBlock,
|
|
1378
|
-
~
|
|
1397
|
+
~knownHeight,
|
|
1398
|
+
~partitionsCount,
|
|
1379
1399
|
),
|
|
1380
1400
|
chainId: 0,
|
|
1381
1401
|
progress: 0.,
|
|
@@ -1384,7 +1404,7 @@ let pushQueriesForRange = (
|
|
|
1384
1404
|
| Some(chunkRange) =>
|
|
1385
1405
|
let maxBlock = switch rangeEndBlock {
|
|
1386
1406
|
| Some(eb) => eb
|
|
1387
|
-
| None =>
|
|
1407
|
+
| None => knownHeight
|
|
1388
1408
|
}
|
|
1389
1409
|
let chunkSize = Js.Math.ceil_int(chunkRange->Int.toFloat *. 1.8)
|
|
1390
1410
|
if rangeFromBlock + chunkSize * 2 - 1 <= maxBlock {
|
|
@@ -1404,7 +1424,8 @@ let pushQueriesForRange = (
|
|
|
1404
1424
|
partition,
|
|
1405
1425
|
~fromBlock=chunkFromBlock.contents,
|
|
1406
1426
|
~toBlock=Some(chunkToBlock),
|
|
1407
|
-
~
|
|
1427
|
+
~knownHeight,
|
|
1428
|
+
~partitionsCount,
|
|
1408
1429
|
),
|
|
1409
1430
|
chainId: 0,
|
|
1410
1431
|
progress: 0.,
|
|
@@ -1425,7 +1446,8 @@ let pushQueriesForRange = (
|
|
|
1425
1446
|
partition,
|
|
1426
1447
|
~fromBlock=rangeFromBlock,
|
|
1427
1448
|
~toBlock=rangeEndBlock,
|
|
1428
|
-
~
|
|
1449
|
+
~knownHeight,
|
|
1450
|
+
~partitionsCount,
|
|
1429
1451
|
),
|
|
1430
1452
|
chainId: 0,
|
|
1431
1453
|
progress: 0.,
|
|
@@ -1437,45 +1459,27 @@ let pushQueriesForRange = (
|
|
|
1437
1459
|
}
|
|
1438
1460
|
}
|
|
1439
1461
|
|
|
1462
|
+
// Candidate queries are sized against the natural ceiling (head/endBlock/mergeBlock),
|
|
1463
|
+
// not a share of the shared buffer — CrossChainState.checkAndFetch's admission loop
|
|
1464
|
+
// is what actually bounds how much gets fetched per tick, by estResponseSize (which
|
|
1465
|
+
// the HyperSync/SVM sources also enforce server-side via a maxNumLogs-style cap, so
|
|
1466
|
+
// a wrong estimate truncates the response instead of overshooting the buffer).
|
|
1440
1467
|
let getNextQuery = (
|
|
1441
|
-
{
|
|
1442
|
-
~budget,
|
|
1443
|
-
~chainPendingBudget,
|
|
1468
|
+
{optimizedPartitions, blockLag, latestOnBlockBlockNumber, knownHeight, endBlock}: t,
|
|
1444
1469
|
) => {
|
|
1445
1470
|
let headBlockNumber = knownHeight - blockLag
|
|
1446
1471
|
if headBlockNumber <= 0 {
|
|
1447
1472
|
WaitingForNewBlock
|
|
1448
|
-
} else if budget <= 0 {
|
|
1449
|
-
// No room left in the shared buffer pool for this chain; wait for processing
|
|
1450
|
-
// to drain before fetching more.
|
|
1451
|
-
NothingToQuery
|
|
1452
1473
|
} else {
|
|
1453
1474
|
let isOnBlockBehindTheHead = latestOnBlockBlockNumber < headBlockNumber
|
|
1454
1475
|
let shouldWaitForNewBlock = ref(
|
|
1455
|
-
switch
|
|
1476
|
+
switch endBlock {
|
|
1456
1477
|
| Some(endBlock) => headBlockNumber < endBlock
|
|
1457
1478
|
| None => true
|
|
1458
1479
|
} &&
|
|
1459
1480
|
!isOnBlockBehindTheHead,
|
|
1460
1481
|
)
|
|
1461
1482
|
|
|
1462
|
-
// Fetch at most `budget` items past the ready frontier (plus what's already
|
|
1463
|
-
// in flight) so processing always has buffer without ballooning memory.
|
|
1464
|
-
// budget already excludes items at/below the frontier (they're in the shared
|
|
1465
|
-
// totalReadyCount), so offset the index by bufferReadyCount — otherwise the
|
|
1466
|
-
// ready prefix is subtracted twice and the buffer caps at a fraction of its
|
|
1467
|
-
// target. A partition that fetched further is skipped until the buffer drains.
|
|
1468
|
-
let maxQueryBlockNumber = {
|
|
1469
|
-
switch buffer->Array.get(
|
|
1470
|
-
fetchState->bufferReadyCount + budget + chainPendingBudget->Float.toInt - 1,
|
|
1471
|
-
) {
|
|
1472
|
-
| Some(item) =>
|
|
1473
|
-
// Just in case check that we don't query beyond the current block
|
|
1474
|
-
Pervasives.min(item->Internal.getItemBlockNumber, knownHeight)
|
|
1475
|
-
| None => knownHeight
|
|
1476
|
-
}
|
|
1477
|
-
}
|
|
1478
|
-
|
|
1479
1483
|
let queries = []
|
|
1480
1484
|
|
|
1481
1485
|
let partitionsCount = optimizedPartitions.idsInAscOrder->Array.length
|
|
@@ -1501,7 +1505,7 @@ let getNextQuery = (
|
|
|
1501
1505
|
let partitionQueriesStart = queries->Array.length
|
|
1502
1506
|
|
|
1503
1507
|
// Compute queryEndBlock for this partition
|
|
1504
|
-
let queryEndBlock = Utils.Math.minOptInt(
|
|
1508
|
+
let queryEndBlock = Utils.Math.minOptInt(endBlock, p.mergeBlock)
|
|
1505
1509
|
let queryEndBlock = switch blockLag {
|
|
1506
1510
|
| 0 => queryEndBlock
|
|
1507
1511
|
| _ =>
|
|
@@ -1509,15 +1513,6 @@ let getNextQuery = (
|
|
|
1509
1513
|
// because otherwise HyperSync might return bigger range
|
|
1510
1514
|
Utils.Math.minOptInt(Some(headBlockNumber), queryEndBlock)
|
|
1511
1515
|
}
|
|
1512
|
-
// Enforce the response range up until target block
|
|
1513
|
-
// Otherwise for indexers with 100+ partitions
|
|
1514
|
-
// we might blow up the buffer size to more than 600k events
|
|
1515
|
-
// simply because of HyperSync returning extra blocks
|
|
1516
|
-
let queryEndBlock = switch (queryEndBlock, maxQueryBlockNumber < knownHeight) {
|
|
1517
|
-
| (Some(endBlock), true) => Some(Pervasives.min(maxQueryBlockNumber, endBlock))
|
|
1518
|
-
| (None, true) => Some(maxQueryBlockNumber)
|
|
1519
|
-
| (_, false) => queryEndBlock
|
|
1520
|
-
}
|
|
1521
1516
|
|
|
1522
1517
|
let maybeChunkRange = getMinHistoryRange(p)
|
|
1523
1518
|
|
|
@@ -1535,7 +1530,7 @@ let getNextQuery = (
|
|
|
1535
1530
|
~partitionId,
|
|
1536
1531
|
~rangeFromBlock=cursor.contents,
|
|
1537
1532
|
~rangeEndBlock=Utils.Math.minOptInt(Some(pq.fromBlock - 1), queryEndBlock),
|
|
1538
|
-
~
|
|
1533
|
+
~knownHeight,
|
|
1539
1534
|
~maybeChunkRange,
|
|
1540
1535
|
~maxChunks=maxPendingChunksPerPartition -
|
|
1541
1536
|
pendingCount -
|
|
@@ -1544,6 +1539,7 @@ let getNextQuery = (
|
|
|
1544
1539
|
~partition=p,
|
|
1545
1540
|
~selection=p.selection,
|
|
1546
1541
|
~addressesByContractName=p.addressesByContractName,
|
|
1542
|
+
~partitionsCount,
|
|
1547
1543
|
)
|
|
1548
1544
|
}
|
|
1549
1545
|
switch pq {
|
|
@@ -1563,7 +1559,7 @@ let getNextQuery = (
|
|
|
1563
1559
|
~partitionId,
|
|
1564
1560
|
~rangeFromBlock=cursor.contents,
|
|
1565
1561
|
~rangeEndBlock=queryEndBlock,
|
|
1566
|
-
~
|
|
1562
|
+
~knownHeight,
|
|
1567
1563
|
~maybeChunkRange,
|
|
1568
1564
|
~maxChunks=maxPendingChunksPerPartition -
|
|
1569
1565
|
pendingCount -
|
|
@@ -1572,6 +1568,7 @@ let getNextQuery = (
|
|
|
1572
1568
|
~partition=p,
|
|
1573
1569
|
~selection=p.selection,
|
|
1574
1570
|
~addressesByContractName=p.addressesByContractName,
|
|
1571
|
+
~partitionsCount,
|
|
1575
1572
|
)
|
|
1576
1573
|
}
|
|
1577
1574
|
|
package/src/FetchState.res.mjs
CHANGED
|
@@ -8,6 +8,7 @@ import * as Stdlib_Int from "@rescript/runtime/lib/es6/Stdlib_Int.js";
|
|
|
8
8
|
import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
|
|
9
9
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
10
10
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
11
|
+
import * as Primitive_float from "@rescript/runtime/lib/es6/Primitive_float.js";
|
|
11
12
|
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
12
13
|
import * as IndexingAddresses from "./IndexingAddresses.res.mjs";
|
|
13
14
|
|
|
@@ -21,12 +22,16 @@ let deriveContractNameByAddress = Utils.$$WeakMap.memoize(addressesByContractNam
|
|
|
21
22
|
return result;
|
|
22
23
|
});
|
|
23
24
|
|
|
24
|
-
function
|
|
25
|
+
function calculateDefaultEstResponseSize(partitionsCount) {
|
|
26
|
+
return Primitive_float.max(2000, Primitive_float.min(10000, 20000 / partitionsCount));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function calculateEstResponseSize(p, fromBlock, toBlock, knownHeight, partitionsCount) {
|
|
25
30
|
if (p.prevQueryRange <= 0) {
|
|
26
|
-
return
|
|
31
|
+
return calculateDefaultEstResponseSize(partitionsCount);
|
|
27
32
|
}
|
|
28
33
|
let density = p.prevRangeSize / p.prevQueryRange;
|
|
29
|
-
return ((Stdlib_Option.getOr(toBlock,
|
|
34
|
+
return ((Stdlib_Option.getOr(toBlock, knownHeight) - fromBlock | 0) + 1 | 0) * density;
|
|
30
35
|
}
|
|
31
36
|
|
|
32
37
|
function getMinHistoryRange(p) {
|
|
@@ -977,15 +982,15 @@ function startFetchingQueries(param, queries) {
|
|
|
977
982
|
}
|
|
978
983
|
}
|
|
979
984
|
|
|
980
|
-
function pushQueriesForRange(queries, partitionId, rangeFromBlock, rangeEndBlock,
|
|
981
|
-
if (!(rangeFromBlock <=
|
|
985
|
+
function pushQueriesForRange(queries, partitionId, rangeFromBlock, rangeEndBlock, knownHeight, maybeChunkRange, maxChunks, partition, selection, addressesByContractName, partitionsCount) {
|
|
986
|
+
if (!(rangeFromBlock <= knownHeight && maxChunks > 0)) {
|
|
982
987
|
return;
|
|
983
988
|
}
|
|
984
989
|
if (rangeEndBlock !== undefined && rangeFromBlock > rangeEndBlock) {
|
|
985
990
|
return;
|
|
986
991
|
}
|
|
987
992
|
if (maybeChunkRange !== undefined) {
|
|
988
|
-
let maxBlock = rangeEndBlock !== undefined ? rangeEndBlock :
|
|
993
|
+
let maxBlock = rangeEndBlock !== undefined ? rangeEndBlock : knownHeight;
|
|
989
994
|
let chunkSize = Js_math.ceil_int(maybeChunkRange * 1.8);
|
|
990
995
|
if (((rangeFromBlock + (chunkSize << 1) | 0) - 1 | 0) <= maxBlock) {
|
|
991
996
|
let chunkFromBlock = rangeFromBlock;
|
|
@@ -997,7 +1002,7 @@ function pushQueriesForRange(queries, partitionId, rangeFromBlock, rangeEndBlock
|
|
|
997
1002
|
fromBlock: chunkFromBlock,
|
|
998
1003
|
toBlock: chunkToBlock,
|
|
999
1004
|
isChunk: true,
|
|
1000
|
-
estResponseSize: calculateEstResponseSize(partition, chunkFromBlock, chunkToBlock,
|
|
1005
|
+
estResponseSize: calculateEstResponseSize(partition, chunkFromBlock, chunkToBlock, knownHeight, partitionsCount),
|
|
1001
1006
|
chainId: 0,
|
|
1002
1007
|
progress: 0,
|
|
1003
1008
|
selection: selection,
|
|
@@ -1013,7 +1018,7 @@ function pushQueriesForRange(queries, partitionId, rangeFromBlock, rangeEndBlock
|
|
|
1013
1018
|
fromBlock: rangeFromBlock,
|
|
1014
1019
|
toBlock: rangeEndBlock,
|
|
1015
1020
|
isChunk: rangeEndBlock !== undefined,
|
|
1016
|
-
estResponseSize: calculateEstResponseSize(partition, rangeFromBlock, rangeEndBlock,
|
|
1021
|
+
estResponseSize: calculateEstResponseSize(partition, rangeFromBlock, rangeEndBlock, knownHeight, partitionsCount),
|
|
1017
1022
|
chainId: 0,
|
|
1018
1023
|
progress: 0,
|
|
1019
1024
|
selection: selection,
|
|
@@ -1026,7 +1031,7 @@ function pushQueriesForRange(queries, partitionId, rangeFromBlock, rangeEndBlock
|
|
|
1026
1031
|
fromBlock: rangeFromBlock,
|
|
1027
1032
|
toBlock: rangeEndBlock,
|
|
1028
1033
|
isChunk: false,
|
|
1029
|
-
estResponseSize: calculateEstResponseSize(partition, rangeFromBlock, rangeEndBlock,
|
|
1034
|
+
estResponseSize: calculateEstResponseSize(partition, rangeFromBlock, rangeEndBlock, knownHeight, partitionsCount),
|
|
1030
1035
|
chainId: 0,
|
|
1031
1036
|
progress: 0,
|
|
1032
1037
|
selection: selection,
|
|
@@ -1034,24 +1039,19 @@ function pushQueriesForRange(queries, partitionId, rangeFromBlock, rangeEndBlock
|
|
|
1034
1039
|
});
|
|
1035
1040
|
}
|
|
1036
1041
|
|
|
1037
|
-
function getNextQuery(
|
|
1038
|
-
let knownHeight =
|
|
1039
|
-
let blockLag =
|
|
1040
|
-
let
|
|
1042
|
+
function getNextQuery(param) {
|
|
1043
|
+
let knownHeight = param.knownHeight;
|
|
1044
|
+
let blockLag = param.blockLag;
|
|
1045
|
+
let endBlock = param.endBlock;
|
|
1046
|
+
let optimizedPartitions = param.optimizedPartitions;
|
|
1041
1047
|
let headBlockNumber = knownHeight - blockLag | 0;
|
|
1042
1048
|
if (headBlockNumber <= 0) {
|
|
1043
1049
|
return "WaitingForNewBlock";
|
|
1044
1050
|
}
|
|
1045
|
-
|
|
1046
|
-
return "NothingToQuery";
|
|
1047
|
-
}
|
|
1048
|
-
let isOnBlockBehindTheHead = fetchState.latestOnBlockBlockNumber < headBlockNumber;
|
|
1049
|
-
let endBlock = fetchState.endBlock;
|
|
1051
|
+
let isOnBlockBehindTheHead = param.latestOnBlockBlockNumber < headBlockNumber;
|
|
1050
1052
|
let shouldWaitForNewBlock = (
|
|
1051
1053
|
endBlock !== undefined ? headBlockNumber < endBlock : true
|
|
1052
1054
|
) && !isOnBlockBehindTheHead;
|
|
1053
|
-
let item = fetchState.buffer[((bufferReadyCount(fetchState) + budget | 0) + (chainPendingBudget | 0) | 0) - 1 | 0];
|
|
1054
|
-
let maxQueryBlockNumber = item !== undefined ? Primitive_int.min(item.blockNumber, knownHeight) : knownHeight;
|
|
1055
1055
|
let queries = [];
|
|
1056
1056
|
let partitionsCount = optimizedPartitions.idsInAscOrder.length;
|
|
1057
1057
|
let idxRef = 0;
|
|
@@ -1066,14 +1066,8 @@ function getNextQuery(fetchState, budget, chainPendingBudget) {
|
|
|
1066
1066
|
shouldWaitForNewBlock = false;
|
|
1067
1067
|
}
|
|
1068
1068
|
let partitionQueriesStart = queries.length;
|
|
1069
|
-
let queryEndBlock = Utils.$$Math.minOptInt(
|
|
1069
|
+
let queryEndBlock = Utils.$$Math.minOptInt(endBlock, p.mergeBlock);
|
|
1070
1070
|
let queryEndBlock$1 = blockLag !== 0 ? Utils.$$Math.minOptInt(headBlockNumber, queryEndBlock) : queryEndBlock;
|
|
1071
|
-
let match = maxQueryBlockNumber < knownHeight;
|
|
1072
|
-
let queryEndBlock$2 = queryEndBlock$1 !== undefined ? (
|
|
1073
|
-
match ? Primitive_int.min(maxQueryBlockNumber, queryEndBlock$1) : queryEndBlock$1
|
|
1074
|
-
) : (
|
|
1075
|
-
match ? maxQueryBlockNumber : queryEndBlock$1
|
|
1076
|
-
);
|
|
1077
1071
|
let maybeChunkRange = getMinHistoryRange(p);
|
|
1078
1072
|
let cursor = p.latestFetchedBlock.blockNumber + 1 | 0;
|
|
1079
1073
|
let canContinue = true;
|
|
@@ -1084,16 +1078,16 @@ function getNextQuery(fetchState, budget, chainPendingBudget) {
|
|
|
1084
1078
|
let addressesByContractName = p.addressesByContractName;
|
|
1085
1079
|
let selection = p.selection;
|
|
1086
1080
|
let maxChunks = (10 - pendingCount | 0) - (queries.length - partitionQueriesStart | 0) | 0;
|
|
1087
|
-
let rangeEndBlock = Utils.$$Math.minOptInt(pq.fromBlock - 1 | 0, queryEndBlock$
|
|
1081
|
+
let rangeEndBlock = Utils.$$Math.minOptInt(pq.fromBlock - 1 | 0, queryEndBlock$1);
|
|
1088
1082
|
let rangeFromBlock = cursor;
|
|
1089
|
-
if (rangeFromBlock <=
|
|
1083
|
+
if (rangeFromBlock <= knownHeight && maxChunks > 0) {
|
|
1090
1084
|
let exit = 0;
|
|
1091
1085
|
if (!(rangeEndBlock !== undefined && rangeFromBlock > rangeEndBlock)) {
|
|
1092
1086
|
exit = 1;
|
|
1093
1087
|
}
|
|
1094
1088
|
if (exit === 1) {
|
|
1095
1089
|
if (maybeChunkRange !== undefined) {
|
|
1096
|
-
let maxBlock = rangeEndBlock !== undefined ? rangeEndBlock :
|
|
1090
|
+
let maxBlock = rangeEndBlock !== undefined ? rangeEndBlock : knownHeight;
|
|
1097
1091
|
let chunkSize = Js_math.ceil_int(maybeChunkRange * 1.8);
|
|
1098
1092
|
if (((rangeFromBlock + (chunkSize << 1) | 0) - 1 | 0) <= maxBlock) {
|
|
1099
1093
|
let chunkFromBlock = rangeFromBlock;
|
|
@@ -1105,7 +1099,7 @@ function getNextQuery(fetchState, budget, chainPendingBudget) {
|
|
|
1105
1099
|
fromBlock: chunkFromBlock,
|
|
1106
1100
|
toBlock: chunkToBlock,
|
|
1107
1101
|
isChunk: true,
|
|
1108
|
-
estResponseSize: calculateEstResponseSize(p, chunkFromBlock, chunkToBlock,
|
|
1102
|
+
estResponseSize: calculateEstResponseSize(p, chunkFromBlock, chunkToBlock, knownHeight, partitionsCount),
|
|
1109
1103
|
chainId: 0,
|
|
1110
1104
|
progress: 0,
|
|
1111
1105
|
selection: selection,
|
|
@@ -1120,7 +1114,7 @@ function getNextQuery(fetchState, budget, chainPendingBudget) {
|
|
|
1120
1114
|
fromBlock: rangeFromBlock,
|
|
1121
1115
|
toBlock: rangeEndBlock,
|
|
1122
1116
|
isChunk: rangeEndBlock !== undefined,
|
|
1123
|
-
estResponseSize: calculateEstResponseSize(p, rangeFromBlock, rangeEndBlock,
|
|
1117
|
+
estResponseSize: calculateEstResponseSize(p, rangeFromBlock, rangeEndBlock, knownHeight, partitionsCount),
|
|
1124
1118
|
chainId: 0,
|
|
1125
1119
|
progress: 0,
|
|
1126
1120
|
selection: selection,
|
|
@@ -1133,7 +1127,7 @@ function getNextQuery(fetchState, budget, chainPendingBudget) {
|
|
|
1133
1127
|
fromBlock: rangeFromBlock,
|
|
1134
1128
|
toBlock: rangeEndBlock,
|
|
1135
1129
|
isChunk: false,
|
|
1136
|
-
estResponseSize: calculateEstResponseSize(p, rangeFromBlock, rangeEndBlock,
|
|
1130
|
+
estResponseSize: calculateEstResponseSize(p, rangeFromBlock, rangeEndBlock, knownHeight, partitionsCount),
|
|
1137
1131
|
chainId: 0,
|
|
1138
1132
|
progress: 0,
|
|
1139
1133
|
selection: selection,
|
|
@@ -1145,9 +1139,9 @@ function getNextQuery(fetchState, budget, chainPendingBudget) {
|
|
|
1145
1139
|
}
|
|
1146
1140
|
let toBlock = pq.toBlock;
|
|
1147
1141
|
if (toBlock !== undefined && pq.isChunk) {
|
|
1148
|
-
let match
|
|
1149
|
-
if (match
|
|
1150
|
-
let blockNumber = match
|
|
1142
|
+
let match = pq.fetchedBlock;
|
|
1143
|
+
if (match !== undefined) {
|
|
1144
|
+
let blockNumber = match.blockNumber;
|
|
1151
1145
|
cursor = blockNumber < toBlock ? blockNumber + 1 | 0 : toBlock + 1 | 0;
|
|
1152
1146
|
} else {
|
|
1153
1147
|
cursor = toBlock + 1 | 0;
|
|
@@ -1162,14 +1156,14 @@ function getNextQuery(fetchState, budget, chainPendingBudget) {
|
|
|
1162
1156
|
let selection$1 = p.selection;
|
|
1163
1157
|
let maxChunks$1 = (10 - pendingCount | 0) - (queries.length - partitionQueriesStart | 0) | 0;
|
|
1164
1158
|
let rangeFromBlock$1 = cursor;
|
|
1165
|
-
if (rangeFromBlock$1 <=
|
|
1159
|
+
if (rangeFromBlock$1 <= knownHeight && maxChunks$1 > 0) {
|
|
1166
1160
|
let exit$1 = 0;
|
|
1167
|
-
if (!(queryEndBlock$
|
|
1161
|
+
if (!(queryEndBlock$1 !== undefined && rangeFromBlock$1 > queryEndBlock$1)) {
|
|
1168
1162
|
exit$1 = 1;
|
|
1169
1163
|
}
|
|
1170
1164
|
if (exit$1 === 1) {
|
|
1171
1165
|
if (maybeChunkRange !== undefined) {
|
|
1172
|
-
let maxBlock$1 = queryEndBlock$
|
|
1166
|
+
let maxBlock$1 = queryEndBlock$1 !== undefined ? queryEndBlock$1 : knownHeight;
|
|
1173
1167
|
let chunkSize$1 = Js_math.ceil_int(maybeChunkRange * 1.8);
|
|
1174
1168
|
if (((rangeFromBlock$1 + (chunkSize$1 << 1) | 0) - 1 | 0) <= maxBlock$1) {
|
|
1175
1169
|
let chunkFromBlock$1 = rangeFromBlock$1;
|
|
@@ -1181,7 +1175,7 @@ function getNextQuery(fetchState, budget, chainPendingBudget) {
|
|
|
1181
1175
|
fromBlock: chunkFromBlock$1,
|
|
1182
1176
|
toBlock: chunkToBlock$1,
|
|
1183
1177
|
isChunk: true,
|
|
1184
|
-
estResponseSize: calculateEstResponseSize(p, chunkFromBlock$1, chunkToBlock$1,
|
|
1178
|
+
estResponseSize: calculateEstResponseSize(p, chunkFromBlock$1, chunkToBlock$1, knownHeight, partitionsCount),
|
|
1185
1179
|
chainId: 0,
|
|
1186
1180
|
progress: 0,
|
|
1187
1181
|
selection: selection$1,
|
|
@@ -1194,9 +1188,9 @@ function getNextQuery(fetchState, budget, chainPendingBudget) {
|
|
|
1194
1188
|
queries.push({
|
|
1195
1189
|
partitionId: partitionId,
|
|
1196
1190
|
fromBlock: rangeFromBlock$1,
|
|
1197
|
-
toBlock: queryEndBlock$
|
|
1198
|
-
isChunk: queryEndBlock$
|
|
1199
|
-
estResponseSize: calculateEstResponseSize(p, rangeFromBlock$1, queryEndBlock$
|
|
1191
|
+
toBlock: queryEndBlock$1,
|
|
1192
|
+
isChunk: queryEndBlock$1 !== undefined,
|
|
1193
|
+
estResponseSize: calculateEstResponseSize(p, rangeFromBlock$1, queryEndBlock$1, knownHeight, partitionsCount),
|
|
1200
1194
|
chainId: 0,
|
|
1201
1195
|
progress: 0,
|
|
1202
1196
|
selection: selection$1,
|
|
@@ -1207,9 +1201,9 @@ function getNextQuery(fetchState, budget, chainPendingBudget) {
|
|
|
1207
1201
|
queries.push({
|
|
1208
1202
|
partitionId: partitionId,
|
|
1209
1203
|
fromBlock: rangeFromBlock$1,
|
|
1210
|
-
toBlock: queryEndBlock$
|
|
1204
|
+
toBlock: queryEndBlock$1,
|
|
1211
1205
|
isChunk: false,
|
|
1212
|
-
estResponseSize: calculateEstResponseSize(p, rangeFromBlock$1, queryEndBlock$
|
|
1206
|
+
estResponseSize: calculateEstResponseSize(p, rangeFromBlock$1, queryEndBlock$1, knownHeight, partitionsCount),
|
|
1213
1207
|
chainId: 0,
|
|
1214
1208
|
progress: 0,
|
|
1215
1209
|
selection: selection$1,
|
|
@@ -1656,7 +1650,9 @@ function updateKnownHeight(fetchState, knownHeight) {
|
|
|
1656
1650
|
}
|
|
1657
1651
|
}
|
|
1658
1652
|
|
|
1659
|
-
let
|
|
1653
|
+
let minEstResponseSize = 2000;
|
|
1654
|
+
|
|
1655
|
+
let maxEstResponseSize = 10000;
|
|
1660
1656
|
|
|
1661
1657
|
let blockItemLogIndex = 16777216;
|
|
1662
1658
|
|
|
@@ -1664,7 +1660,9 @@ let maxPendingChunksPerPartition = 10;
|
|
|
1664
1660
|
|
|
1665
1661
|
export {
|
|
1666
1662
|
deriveContractNameByAddress,
|
|
1667
|
-
|
|
1663
|
+
minEstResponseSize,
|
|
1664
|
+
maxEstResponseSize,
|
|
1665
|
+
calculateDefaultEstResponseSize,
|
|
1668
1666
|
calculateEstResponseSize,
|
|
1669
1667
|
getMinHistoryRange,
|
|
1670
1668
|
getMinQueryRange,
|
package/src/IndexerState.res
CHANGED
|
@@ -108,6 +108,8 @@ type t = {
|
|
|
108
108
|
// waitForNewBlock waiter is bound to the old, pre-realtime source). A fetch
|
|
109
109
|
// response or waiter carrying an older epoch than this is discarded.
|
|
110
110
|
mutable epoch: int,
|
|
111
|
+
// None off the simulate path.
|
|
112
|
+
simulateDeadInputTracker: option<SimulateDeadInputTracker.t>,
|
|
111
113
|
}
|
|
112
114
|
|
|
113
115
|
let make = (
|
|
@@ -169,6 +171,7 @@ let make = (
|
|
|
169
171
|
onError,
|
|
170
172
|
isStopped: false,
|
|
171
173
|
epoch: 0,
|
|
174
|
+
simulateDeadInputTracker: SimulateDeadInputTracker.makeFromConfig(config),
|
|
172
175
|
}
|
|
173
176
|
}
|
|
174
177
|
|
|
@@ -400,6 +403,7 @@ let exitAfterFirstEventBlock = (state: t) => state.exitAfterFirstEventBlock
|
|
|
400
403
|
let isStopped = (state: t) => state.isStopped
|
|
401
404
|
let epoch = (state: t) => state.epoch
|
|
402
405
|
let pruneStaleEntityHistoryThrottler = (state: t) => state.writeThrottlers.pruneStaleEntityHistory
|
|
406
|
+
let simulateDeadInputTracker = (state: t) => state.simulateDeadInputTracker
|
|
403
407
|
|
|
404
408
|
// --- Store domain operations. ---
|
|
405
409
|
|
package/src/IndexerState.res.mjs
CHANGED
|
@@ -18,6 +18,7 @@ import * as CrossChainState from "./CrossChainState.res.mjs";
|
|
|
18
18
|
import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
|
|
19
19
|
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
20
20
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
21
|
+
import * as SimulateDeadInputTracker from "./SimulateDeadInputTracker.res.mjs";
|
|
21
22
|
|
|
22
23
|
function make() {
|
|
23
24
|
let intervalMillis = Env.ThrottleWrites.pruneStaleDataIntervalMillis;
|
|
@@ -91,7 +92,8 @@ function make$2(config, persistence, chainStates, isInReorgThreshold, isRealtime
|
|
|
91
92
|
exitAfterFirstEventBlock: exitAfterFirstEventBlock,
|
|
92
93
|
onError: onError,
|
|
93
94
|
isStopped: false,
|
|
94
|
-
epoch: 0
|
|
95
|
+
epoch: 0,
|
|
96
|
+
simulateDeadInputTracker: SimulateDeadInputTracker.makeFromConfig(config)
|
|
95
97
|
};
|
|
96
98
|
}
|
|
97
99
|
|
|
@@ -348,6 +350,10 @@ function pruneStaleEntityHistoryThrottler(state) {
|
|
|
348
350
|
return state.writeThrottlers.pruneStaleEntityHistory;
|
|
349
351
|
}
|
|
350
352
|
|
|
353
|
+
function simulateDeadInputTracker(state) {
|
|
354
|
+
return state.simulateDeadInputTracker;
|
|
355
|
+
}
|
|
356
|
+
|
|
351
357
|
function queueProcessedBatch(state, batch) {
|
|
352
358
|
state.processedBatches.push(batch);
|
|
353
359
|
let checkpointId = Utils.$$Array.last(batch.checkpointIds);
|
|
@@ -527,6 +533,7 @@ export {
|
|
|
527
533
|
isStopped,
|
|
528
534
|
epoch,
|
|
529
535
|
pruneStaleEntityHistoryThrottler,
|
|
536
|
+
simulateDeadInputTracker,
|
|
530
537
|
queueProcessedBatch,
|
|
531
538
|
drainBatchRun,
|
|
532
539
|
takeRollback,
|
package/src/IndexerState.resi
CHANGED
|
@@ -107,6 +107,7 @@ let exitAfterFirstEventBlock: t => bool
|
|
|
107
107
|
let isStopped: t => bool
|
|
108
108
|
let epoch: t => int
|
|
109
109
|
let pruneStaleEntityHistoryThrottler: t => Throttler.t
|
|
110
|
+
let simulateDeadInputTracker: t => option<SimulateDeadInputTracker.t>
|
|
110
111
|
|
|
111
112
|
// Store domain operations.
|
|
112
113
|
let queueProcessedBatch: (t, ~batch: Batch.t) => unit
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// Lives on IndexerState and is fed each processed batch, so ChainState and the
|
|
2
|
+
// fetch loop carry no simulate-specific state.
|
|
3
|
+
|
|
4
|
+
// Match a provided item to a processed one by its (chain, block, logIndex)
|
|
5
|
+
// coordinate rather than object identity, so matching survives any copy or
|
|
6
|
+
// transform of the item between the source and the batch.
|
|
7
|
+
let itemKey = (item: Internal.item): string =>
|
|
8
|
+
switch item {
|
|
9
|
+
| Internal.Event({chain, blockNumber, logIndex}) =>
|
|
10
|
+
`${chain
|
|
11
|
+
->ChainMap.Chain.toChainId
|
|
12
|
+
->Int.toString}:${blockNumber->Int.toString}:${logIndex->Int.toString}`
|
|
13
|
+
| _ => "" // non-event items are never a provided simulate input
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// `index` is the item's position in its chain's `simulate` array, reported back
|
|
17
|
+
// so a user finds it without echoing its fields.
|
|
18
|
+
type entry = {chainId: int, index: int, key: string}
|
|
19
|
+
|
|
20
|
+
type t = {mutable unprocessed: array<entry>}
|
|
21
|
+
|
|
22
|
+
let makeFromConfig = (config: Config.t): option<t> => {
|
|
23
|
+
let entries =
|
|
24
|
+
config.chainMap
|
|
25
|
+
->ChainMap.values
|
|
26
|
+
->Array.flatMap(chainConfig =>
|
|
27
|
+
switch chainConfig.sourceConfig {
|
|
28
|
+
| Config.CustomSources(sources) =>
|
|
29
|
+
sources->Array.flatMap(source =>
|
|
30
|
+
source.simulateItems
|
|
31
|
+
->Option.getOr([])
|
|
32
|
+
->Array.mapWithIndex(
|
|
33
|
+
(item, index) => {
|
|
34
|
+
chainId: chainConfig.id,
|
|
35
|
+
index,
|
|
36
|
+
key: itemKey(item),
|
|
37
|
+
},
|
|
38
|
+
)
|
|
39
|
+
)
|
|
40
|
+
| _ => []
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
switch entries {
|
|
44
|
+
| [] => None
|
|
45
|
+
| _ => Some({unprocessed: entries})
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let recordProcessed = (t: t, ~batch: Batch.t) => {
|
|
50
|
+
let processedKeys = batch.items->Array.map(itemKey)->Utils.Set.fromArray
|
|
51
|
+
t.unprocessed = t.unprocessed->Array.filter(entry => !(processedKeys->Utils.Set.has(entry.key)))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Unrouted item indices grouped by chain, in the order chains were first seen.
|
|
55
|
+
let unroutedByChain = (t: t): array<(int, array<int>)> => {
|
|
56
|
+
let indicesByChain = Dict.make()
|
|
57
|
+
let chainOrder = []
|
|
58
|
+
t.unprocessed->Array.forEach(entry => {
|
|
59
|
+
let key = entry.chainId->Int.toString
|
|
60
|
+
if indicesByChain->Dict.get(key)->Option.isNone {
|
|
61
|
+
chainOrder->Array.push(entry.chainId)->ignore
|
|
62
|
+
}
|
|
63
|
+
indicesByChain->Utils.Dict.push(key, entry.index)
|
|
64
|
+
})
|
|
65
|
+
chainOrder->Array.map(chainId => (chainId, indicesByChain->Dict.getUnsafe(chainId->Int.toString)))
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let failureMessage = (t: t): option<string> =>
|
|
69
|
+
switch t->unroutedByChain {
|
|
70
|
+
| [] => None
|
|
71
|
+
| byChain =>
|
|
72
|
+
let count = byChain->Array.reduce(0, (acc, (_chainId, indices)) => acc + indices->Array.length)
|
|
73
|
+
let itemWord = count === 1 ? "item" : "items"
|
|
74
|
+
let lines =
|
|
75
|
+
byChain
|
|
76
|
+
->Array.map(((chainId, indices)) =>
|
|
77
|
+
` - chain ${chainId->Int.toString}: ${indices
|
|
78
|
+
->Array.map(index => index->Int.toString)
|
|
79
|
+
->Array.join(", ")}`
|
|
80
|
+
)
|
|
81
|
+
->Array.join("\n")
|
|
82
|
+
Some(
|
|
83
|
+
`simulate: ${count->Int.toString} ${itemWord} you passed to simulate never reached a handler, so nothing ran for them. Each was filtered out before the handler — usually a non-wildcard srcAddress that isn't indexed for the contract, or a where/block filter that excluded the event. Unrouted items, by index in each chain's simulate array:\n${lines}`,
|
|
84
|
+
)
|
|
85
|
+
}
|