envio 3.3.0-alpha.0 → 3.3.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -6
- package/src/ChainFetching.res +6 -8
- package/src/ChainFetching.res.mjs +3 -3
- package/src/ChainState.res +26 -1
- package/src/ChainState.res.mjs +15 -0
- package/src/ChainState.resi +2 -0
- package/src/CrossChainState.res +97 -72
- package/src/CrossChainState.res.mjs +68 -44
- package/src/CrossChainState.resi +1 -9
- package/src/Env.res +0 -7
- package/src/Env.res.mjs +0 -6
- 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/Main.res.mjs +1 -1
- package/src/Prometheus.res +0 -11
- package/src/Prometheus.res.mjs +83 -98
- package/src/sources/SourceManager.res +9 -8
- package/src/sources/SourceManager.res.mjs +21 -26
- package/src/sources/SourceManager.resi +2 -3
package/src/Env.res.mjs
CHANGED
|
@@ -19,10 +19,6 @@ let targetBufferSize = EnvSafe.get(envSafe, "ENVIO_INDEXING_MAX_BUFFER_SIZE", S$
|
|
|
19
19
|
|
|
20
20
|
let maxAddrInPartition = EnvSafe.get(envSafe, "MAX_PARTITION_SIZE", S$RescriptSchema.int, undefined, 5000, undefined, undefined);
|
|
21
21
|
|
|
22
|
-
let maxBackfillConcurrency = EnvSafe.get(envSafe, "ENVIO_MAX_BACKFILL_CONCURRENCY", S$RescriptSchema.int, undefined, 30, undefined, undefined);
|
|
23
|
-
|
|
24
|
-
let maxRealtimeConcurrency = EnvSafe.get(envSafe, "ENVIO_MAX_REALTIME_CONCURRENCY", S$RescriptSchema.int, undefined, 200, undefined, undefined);
|
|
25
|
-
|
|
26
22
|
let inMemoryObjectsTarget = EnvSafe.get(envSafe, "ENVIO_IN_MEMORY_OBJECTS_TARGET", S$RescriptSchema.int, undefined, 100000, undefined, undefined);
|
|
27
23
|
|
|
28
24
|
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);
|
|
@@ -224,8 +220,6 @@ export {
|
|
|
224
220
|
updateSyncTimeOnRestart,
|
|
225
221
|
targetBufferSize,
|
|
226
222
|
maxAddrInPartition,
|
|
227
|
-
maxBackfillConcurrency,
|
|
228
|
-
maxRealtimeConcurrency,
|
|
229
223
|
inMemoryObjectsTarget,
|
|
230
224
|
serverPort,
|
|
231
225
|
tuiEnvVar,
|
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) {
|