envio 3.11.0 → 3.12.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/index.d.ts +3 -0
- package/package.json +6 -6
- package/src/Batch.res +111 -78
- package/src/Batch.res.mjs +53 -46
- package/src/BatchProcessing.res +1 -11
- package/src/BatchProcessing.res.mjs +1 -4
- package/src/ChainFetching.res +7 -3
- package/src/ChainFetching.res.mjs +1 -1
- package/src/ChainState.res +82 -20
- package/src/ChainState.res.mjs +51 -13
- package/src/ChainState.resi +6 -1
- package/src/Config.res +58 -37
- package/src/Config.res.mjs +63 -53
- package/src/CrossChainState.res +50 -53
- package/src/CrossChainState.res.mjs +25 -23
- package/src/CrossChainState.resi +6 -13
- package/src/EffectState.res +13 -4
- package/src/EffectState.resi +7 -1
- package/src/EventProcessing.res +6 -6
- package/src/EventProcessing.res.mjs +6 -6
- package/src/FetchState.res +65 -46
- package/src/FetchState.res.mjs +68 -36
- package/src/FinalizeBackfill.res +16 -12
- package/src/FinalizeBackfill.res.mjs +3 -3
- package/src/HistoryPolicy.res +46 -0
- package/src/HistoryPolicy.res.mjs +50 -0
- package/src/InMemoryStore.res +30 -32
- package/src/InMemoryStore.res.mjs +33 -28
- package/src/InMemoryTable.res +155 -67
- package/src/InMemoryTable.res.mjs +151 -60
- package/src/IndexerState.res +66 -45
- package/src/IndexerState.res.mjs +57 -29
- package/src/IndexerState.resi +8 -13
- package/src/Internal.res +4 -2
- package/src/LoadLayer.res +68 -38
- package/src/LoadLayer.res.mjs +50 -64
- package/src/LoadLayer.resi +1 -1
- package/src/Main.res +25 -24
- package/src/Main.res.mjs +24 -25
- package/src/Metrics.res +49 -1
- package/src/Metrics.res.mjs +26 -3
- package/src/Persistence.res +55 -16
- package/src/Persistence.res.mjs +18 -3
- package/src/PgStorage.res +286 -180
- package/src/PgStorage.res.mjs +209 -122
- package/src/PruneStaleHistory.res +9 -15
- package/src/PruneStaleHistory.res.mjs +11 -20
- package/src/Rollback.res +9 -14
- package/src/Rollback.res.mjs +5 -3
- package/src/Sink.res +7 -5
- package/src/Sink.res.mjs +4 -4
- package/src/Staging.res +298 -0
- package/src/Staging.res.mjs +257 -0
- package/src/TestIndexer.res +14 -30
- package/src/TestIndexer.res.mjs +11 -9
- package/src/UserContext.res +20 -36
- package/src/UserContext.res.mjs +6 -9
- package/src/Utils.res +10 -4
- package/src/Utils.res.mjs +16 -16
- package/src/Writing.res +34 -20
- package/src/Writing.res.mjs +15 -20
- package/src/bindings/ClickHouse.res +41 -34
- package/src/bindings/ClickHouse.res.mjs +36 -30
- package/src/bindings/ClickHouseSink.res +45 -186
- package/src/bindings/ClickHouseSink.res.mjs +21 -174
- package/src/db/CheckpointSequence.res +109 -0
- package/src/db/CheckpointSequence.res.mjs +99 -0
- package/src/db/EntityFilter.res +487 -275
- package/src/db/EntityFilter.res.mjs +557 -309
- package/src/db/EntityHistory.res +6 -5
- package/src/db/EntityHistory.res.mjs +5 -5
- package/src/db/Frontier.res +86 -0
- package/src/db/Frontier.res.mjs +126 -0
- package/src/db/InternalTable.res +145 -27
- package/src/db/InternalTable.res.mjs +115 -29
- package/src/db/RollbackFloors.res +34 -41
- package/src/db/RollbackFloors.res.mjs +27 -71
- package/src/db/Table.res +21 -6
- package/src/db/Table.res.mjs +13 -4
- package/src/sources/BlockStore.res +8 -0
- package/src/sources/EvmHyperSyncSource.res +5 -1
- package/src/sources/EvmHyperSyncSource.res.mjs +4 -3
- package/src/sources/FuelHyperSyncSource.res +1 -0
- package/src/sources/FuelHyperSyncSource.res.mjs +1 -1
- package/src/sources/HyperSync.res +6 -0
- package/src/sources/HyperSync.res.mjs +5 -2
- package/src/sources/HyperSync.resi +2 -0
- package/src/sources/HyperSyncClient.res +6 -0
- package/src/sources/HyperSyncSSE.res +1 -1
- package/src/sources/HyperSyncSSE.res.mjs +4 -10
- package/src/sources/RequestStat.res +8 -1
- package/src/sources/RpcSource.res +1 -0
- package/src/sources/RpcSource.res.mjs +1 -1
- package/src/sources/SimulateSource.res +1 -0
- package/src/sources/SimulateSource.res.mjs +1 -1
- package/src/sources/Source.res +11 -3
- package/src/sources/SourceManager.res +34 -13
- package/src/sources/SourceManager.res.mjs +26 -9
- package/src/sources/SourceManager.resi +2 -0
- package/src/sources/SvmHyperSyncClient.res +5 -0
- package/src/sources/SvmHyperSyncSource.res +3 -0
- package/src/sources/SvmHyperSyncSource.res.mjs +4 -2
- package/src/tui/Tui.res +56 -143
- package/src/tui/Tui.res.mjs +70 -166
- package/src/tui/components/TuiData.res +61 -16
- package/src/tui/components/TuiData.res.mjs +53 -15
- package/svm.schema.json +0 -7
- package/src/db/CheckpointBounds.res +0 -53
- package/src/db/CheckpointBounds.res.mjs +0 -44
package/src/FetchState.res.mjs
CHANGED
|
@@ -47,6 +47,15 @@ function makeSelection(onEventRegistrations, dependsOnAddresses, clientFilteredC
|
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
function floorAtSelectionStart(latestFetchedBlock, selection) {
|
|
51
|
+
let startBlock = selection.startBlock;
|
|
52
|
+
if (startBlock !== undefined) {
|
|
53
|
+
return Primitive_int.max(latestFetchedBlock, startBlock - 1 | 0);
|
|
54
|
+
} else {
|
|
55
|
+
return latestFetchedBlock;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
50
59
|
function withAddresses(p, addresses) {
|
|
51
60
|
return {
|
|
52
61
|
id: p.id,
|
|
@@ -305,6 +314,26 @@ function getAnchorSafeBlock(p) {
|
|
|
305
314
|
}
|
|
306
315
|
|
|
307
316
|
function make(partitions, maxAddrInPartition, nextPartitionIndex, dynamicContracts, clientFilteredContracts) {
|
|
317
|
+
let partitions$1 = partitions.map(p => {
|
|
318
|
+
let floored = floorAtSelectionStart(p.latestFetchedBlock, p.selection);
|
|
319
|
+
if (floored === p.latestFetchedBlock) {
|
|
320
|
+
return p;
|
|
321
|
+
} else {
|
|
322
|
+
return {
|
|
323
|
+
id: p.id,
|
|
324
|
+
latestFetchedBlock: floored,
|
|
325
|
+
selection: p.selection,
|
|
326
|
+
addresses: p.addresses,
|
|
327
|
+
mergeBlock: p.mergeBlock,
|
|
328
|
+
dynamicContract: p.dynamicContract,
|
|
329
|
+
mutPendingQueries: p.mutPendingQueries,
|
|
330
|
+
sourceRangeCapacity: p.sourceRangeCapacity,
|
|
331
|
+
prevSourceRangeCapacity: p.prevSourceRangeCapacity,
|
|
332
|
+
eventDensity: p.eventDensity,
|
|
333
|
+
latestSourceRangeCapacityUpdateBlock: p.latestSourceRangeCapacityUpdateBlock
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
});
|
|
308
337
|
let newPartitions = [];
|
|
309
338
|
let mergingPartitions = {};
|
|
310
339
|
let nextPartitionIndexRef = {
|
|
@@ -313,7 +342,7 @@ function make(partitions, maxAddrInPartition, nextPartitionIndex, dynamicContrac
|
|
|
313
342
|
let anchorSafeBlocks = {};
|
|
314
343
|
let anchoredContractsSet = new Set();
|
|
315
344
|
if (clientFilteredContracts.size > 0) {
|
|
316
|
-
partitions.forEach(p => {
|
|
345
|
+
partitions$1.forEach(p => {
|
|
317
346
|
let match = p.mergeBlock;
|
|
318
347
|
let match$1 = p.selection.clientFilteredContracts;
|
|
319
348
|
if (match !== undefined) {
|
|
@@ -332,8 +361,8 @@ function make(partitions, maxAddrInPartition, nextPartitionIndex, dynamicContrac
|
|
|
332
361
|
});
|
|
333
362
|
});
|
|
334
363
|
}
|
|
335
|
-
for (let idx = 0, idx_finish = partitions.length; idx < idx_finish; ++idx) {
|
|
336
|
-
let p = partitions[idx];
|
|
364
|
+
for (let idx = 0, idx_finish = partitions$1.length; idx < idx_finish; ++idx) {
|
|
365
|
+
let p = partitions$1[idx];
|
|
337
366
|
let mergeBlock = p.mergeBlock;
|
|
338
367
|
if (mergeBlock !== undefined) {
|
|
339
368
|
if (p.latestFetchedBlock < mergeBlock || isFetching(p)) {
|
|
@@ -676,20 +705,8 @@ function bufferBlockNumber(param) {
|
|
|
676
705
|
let optimizedPartitions = param.optimizedPartitions;
|
|
677
706
|
let id = optimizedPartitions.idsInAscOrder[0];
|
|
678
707
|
let latestFullyFetchedBlock = id !== undefined ? optimizedPartitions.entities[id].latestFetchedBlock : undefined;
|
|
679
|
-
if (latestFullyFetchedBlock !== undefined
|
|
680
|
-
return latestFullyFetchedBlock;
|
|
681
|
-
} else {
|
|
682
|
-
return latestOnBlockBlockNumber;
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
function bufferBlock(param) {
|
|
687
|
-
let latestOnBlockBlockNumber = param.latestOnBlockBlockNumber;
|
|
688
|
-
let optimizedPartitions = param.optimizedPartitions;
|
|
689
|
-
let id = optimizedPartitions.idsInAscOrder[0];
|
|
690
|
-
let latestFullyFetchedBlock = id !== undefined ? optimizedPartitions.entities[id].latestFetchedBlock : undefined;
|
|
691
|
-
if (latestFullyFetchedBlock !== undefined && latestOnBlockBlockNumber >= latestFullyFetchedBlock) {
|
|
692
|
-
return latestFullyFetchedBlock;
|
|
708
|
+
if (latestFullyFetchedBlock !== undefined) {
|
|
709
|
+
return Primitive_int.min(latestOnBlockBlockNumber, latestFullyFetchedBlock);
|
|
693
710
|
} else {
|
|
694
711
|
return latestOnBlockBlockNumber;
|
|
695
712
|
}
|
|
@@ -795,6 +812,19 @@ function compareBufferItem(a, b) {
|
|
|
795
812
|
}
|
|
796
813
|
}
|
|
797
814
|
|
|
815
|
+
function isSameLog(a, b) {
|
|
816
|
+
if (a.kind !== 0 || b.kind !== 0 || a.blockNumber !== b.blockNumber || a.logIndex !== b.logIndex) {
|
|
817
|
+
return false;
|
|
818
|
+
}
|
|
819
|
+
let match = a.orderPath;
|
|
820
|
+
let match$1 = b.orderPath;
|
|
821
|
+
if ((match == null) || (match$1 == null)) {
|
|
822
|
+
return true;
|
|
823
|
+
} else {
|
|
824
|
+
return comparePath(match, match$1) === 0;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
|
|
798
828
|
function mergeIntoBuffer(buffer, newItems) {
|
|
799
829
|
let n = newItems.length;
|
|
800
830
|
for (let i = 1; i < n; ++i) {
|
|
@@ -884,14 +914,15 @@ function updateInternal(fetchState, optimizedPartitionsOpt, mutItems, mutItemsSo
|
|
|
884
914
|
let latestOnBlockBlockNumber;
|
|
885
915
|
if (onBlockRegistrations.length !== 0) {
|
|
886
916
|
let item = base[fetchState.maxOnBlockBufferSize - 1 | 0];
|
|
887
|
-
let
|
|
917
|
+
let tmp;
|
|
888
918
|
if (item !== undefined) {
|
|
889
|
-
|
|
919
|
+
tmp = item.blockNumber;
|
|
890
920
|
} else {
|
|
891
921
|
let id = optimizedPartitions.idsInAscOrder[0];
|
|
892
922
|
let latestFullyFetchedBlock = id !== undefined ? optimizedPartitions.entities[id].latestFetchedBlock : undefined;
|
|
893
|
-
|
|
923
|
+
tmp = latestFullyFetchedBlock !== undefined ? latestFullyFetchedBlock : knownHeight;
|
|
894
924
|
}
|
|
925
|
+
let maxBlockNumber = Primitive_int.min(tmp, knownHeight);
|
|
895
926
|
latestOnBlockBlockNumber = appendOnBlockItems(blockItems, onBlockRegistrations, fetchState.startBlock, fetchState.latestOnBlockBlockNumber, maxBlockNumber, fetchState.maxOnBlockBufferSize);
|
|
896
927
|
} else {
|
|
897
928
|
latestOnBlockBlockNumber = knownHeight;
|
|
@@ -1473,19 +1504,11 @@ function walkPartitionPending(p, partitionId, inFlightCount, candidates, headBlo
|
|
|
1473
1504
|
}
|
|
1474
1505
|
pqIdx = pqIdx + 1 | 0;
|
|
1475
1506
|
};
|
|
1476
|
-
let startBlock = p.selection.startBlock;
|
|
1477
|
-
let cursor$1;
|
|
1478
|
-
if (startBlock !== undefined && startBlock > cursor) {
|
|
1479
|
-
let reachable = Utils.$$Math.minOptInt(headBlockNumber, queryEndBlock);
|
|
1480
|
-
cursor$1 = reachable !== undefined && startBlock <= reachable ? startBlock : cursor;
|
|
1481
|
-
} else {
|
|
1482
|
-
cursor$1 = cursor;
|
|
1483
|
-
}
|
|
1484
1507
|
if (canContinue) {
|
|
1485
1508
|
return {
|
|
1486
1509
|
partitionId: partitionId,
|
|
1487
1510
|
p: p,
|
|
1488
|
-
cursor: cursor
|
|
1511
|
+
cursor: cursor,
|
|
1489
1512
|
chunksUsedThisCall: chunksUsedThisCall,
|
|
1490
1513
|
inFlightCount: inFlightCount,
|
|
1491
1514
|
queryEndBlock: queryEndBlock,
|
|
@@ -1787,13 +1810,21 @@ function make$1(startBlock, endBlock, onEventRegistrations, addressStore, addres
|
|
|
1787
1810
|
}
|
|
1788
1811
|
let buffer = [];
|
|
1789
1812
|
let latestOnBlockBlockNumber;
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
let maxBlockNumber = latestFullyFetchedBlock !== undefined ? latestFullyFetchedBlock : knownHeight;
|
|
1794
|
-
latestOnBlockBlockNumber = appendOnBlockItems(buffer, onBlockRegistrations, startBlock, progressBlockNumber, maxBlockNumber, maxOnBlockBufferSize);
|
|
1813
|
+
let exit = 0;
|
|
1814
|
+
if (onBlockRegistrations.length !== 0 || knownHeight <= 0) {
|
|
1815
|
+
exit = 1;
|
|
1795
1816
|
} else {
|
|
1796
|
-
latestOnBlockBlockNumber =
|
|
1817
|
+
latestOnBlockBlockNumber = knownHeight;
|
|
1818
|
+
}
|
|
1819
|
+
if (exit === 1) {
|
|
1820
|
+
if (knownHeight > 0) {
|
|
1821
|
+
let id = optimizedPartitions.idsInAscOrder[0];
|
|
1822
|
+
let latestFullyFetchedBlock = id !== undefined ? optimizedPartitions.entities[id].latestFetchedBlock : undefined;
|
|
1823
|
+
let maxBlockNumber = latestFullyFetchedBlock !== undefined ? Primitive_int.min(latestFullyFetchedBlock, knownHeight) : knownHeight;
|
|
1824
|
+
latestOnBlockBlockNumber = appendOnBlockItems(buffer, onBlockRegistrations, startBlock, progressBlockNumber, maxBlockNumber, maxOnBlockBufferSize);
|
|
1825
|
+
} else {
|
|
1826
|
+
latestOnBlockBlockNumber = progressBlockNumber;
|
|
1827
|
+
}
|
|
1797
1828
|
}
|
|
1798
1829
|
return {
|
|
1799
1830
|
optimizedPartitions: optimizedPartitions,
|
|
@@ -2098,6 +2129,7 @@ let chunkRangeGrowthFactor = 1.8;
|
|
|
2098
2129
|
export {
|
|
2099
2130
|
deriveSelectionStartBlock,
|
|
2100
2131
|
makeSelection,
|
|
2132
|
+
floorAtSelectionStart,
|
|
2101
2133
|
withAddresses,
|
|
2102
2134
|
narrowSelectionToRange,
|
|
2103
2135
|
densityItemsTarget,
|
|
@@ -2107,12 +2139,12 @@ export {
|
|
|
2107
2139
|
getMinQueryRange,
|
|
2108
2140
|
OptimizedPartitions,
|
|
2109
2141
|
bufferBlockNumber,
|
|
2110
|
-
bufferBlock,
|
|
2111
2142
|
bufferReadyCount,
|
|
2112
2143
|
getRegistrationIndex,
|
|
2113
2144
|
comparePath,
|
|
2114
2145
|
compareTiebreak,
|
|
2115
2146
|
compareBufferItem,
|
|
2147
|
+
isSameLog,
|
|
2116
2148
|
mergeIntoBuffer,
|
|
2117
2149
|
appendOnBlockItems,
|
|
2118
2150
|
updateInternal,
|
package/src/FinalizeBackfill.res
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
// The FinalizingIndexes phase. Reached from the processing loop when
|
|
2
|
-
//
|
|
3
|
-
// pending writes are flushed, then storage builds every
|
|
4
|
-
//
|
|
5
|
-
// leaves the indexes built so far in place and
|
|
6
|
-
// error boundary; the retry only owes what's left.
|
|
7
|
-
// reports ready with an index the schema promised
|
|
1
|
+
// The FinalizingIndexes phase. Reached from the processing loop when the chains
|
|
2
|
+
// this process drives have caught up: processing is already paused (the loop
|
|
3
|
+
// awaits this), pending writes are flushed, then storage builds every index the
|
|
4
|
+
// schema promises for those chains and, once they all verify, commits their
|
|
5
|
+
// `ready_at`. A failure part-way leaves the indexes built so far in place and
|
|
6
|
+
// reaches the processing loop's error boundary; the retry only owes what's left.
|
|
7
|
+
// Either way a chain never reports ready with an index the schema promised for
|
|
8
|
+
// it still missing.
|
|
9
|
+
//
|
|
10
|
+
// A chain's rows live in a partition of its own, so this touches nothing another
|
|
11
|
+
// `envio start --chain` process is indexing and never waits on one.
|
|
8
12
|
|
|
9
13
|
let runOnce = async (state: IndexerState.t) => {
|
|
10
14
|
Logging.info(
|
|
@@ -22,10 +26,7 @@ let runOnce = async (state: IndexerState.t) => {
|
|
|
22
26
|
|
|
23
27
|
await storage.finalizeBackfill(
|
|
24
28
|
~entities=persistence.allEntities,
|
|
25
|
-
~chainIds=state
|
|
26
|
-
->IndexerState.chainStates
|
|
27
|
-
->Dict.valuesToArray
|
|
28
|
-
->Array.map(cs => (cs->ChainState.chainConfig).id),
|
|
29
|
+
~chainIds=state->IndexerState.crossChainState->CrossChainState.chainIds,
|
|
29
30
|
~readyAt,
|
|
30
31
|
)
|
|
31
32
|
|
|
@@ -57,5 +58,8 @@ let run = (state: IndexerState.t) =>
|
|
|
57
58
|
let repairSchemaIndexes = (state: IndexerState.t) => {
|
|
58
59
|
let persistence = state->IndexerState.persistence
|
|
59
60
|
let storage = persistence->Persistence.getInitializedStorageOrThrow
|
|
60
|
-
storage.ensureSchemaIndexes(
|
|
61
|
+
storage.ensureSchemaIndexes(
|
|
62
|
+
~entities=persistence.allEntities,
|
|
63
|
+
~chainIds=state->IndexerState.crossChainState->CrossChainState.chainIds,
|
|
64
|
+
)
|
|
61
65
|
}
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import * as Logging from "./Logging.res.mjs";
|
|
4
4
|
import * as Writing from "./Writing.res.mjs";
|
|
5
|
-
import * as ChainState from "./ChainState.res.mjs";
|
|
6
5
|
import * as Persistence from "./Persistence.res.mjs";
|
|
7
6
|
import * as IndexerState from "./IndexerState.res.mjs";
|
|
7
|
+
import * as CrossChainState from "./CrossChainState.res.mjs";
|
|
8
8
|
|
|
9
9
|
async function runOnce(state) {
|
|
10
10
|
Logging.info("All chains are caught up. Finalizing the indexer before switching to realtime: flushing pending writes, then creating the indexes the schema promises.");
|
|
@@ -15,7 +15,7 @@ async function runOnce(state) {
|
|
|
15
15
|
let persistence = IndexerState.persistence(state);
|
|
16
16
|
let storage = Persistence.getInitializedStorageOrThrow(persistence);
|
|
17
17
|
let readyAt = new Date();
|
|
18
|
-
await storage.finalizeBackfill(persistence.allEntities,
|
|
18
|
+
await storage.finalizeBackfill(persistence.allEntities, CrossChainState.chainIds(IndexerState.crossChainState(state)), readyAt);
|
|
19
19
|
IndexerState.markReady(state, readyAt);
|
|
20
20
|
return Logging.info("The indexer is ready. Switching to realtime indexing.");
|
|
21
21
|
}
|
|
@@ -33,7 +33,7 @@ function run(state) {
|
|
|
33
33
|
function repairSchemaIndexes(state) {
|
|
34
34
|
let persistence = IndexerState.persistence(state);
|
|
35
35
|
let storage = Persistence.getInitializedStorageOrThrow(persistence);
|
|
36
|
-
return storage.ensureSchemaIndexes(persistence.allEntities);
|
|
36
|
+
return storage.ensureSchemaIndexes(persistence.allEntities, CrossChainState.chainIds(IndexerState.crossChainState(state)));
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
export {
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Whether each chain's rows get history on a write, keyed by chain id. History
|
|
2
|
+
// is only ever kept for what a rollback could reach: under one shared sequence
|
|
3
|
+
// any chain's rollback reaches every chain's rows, so the run decides together;
|
|
4
|
+
// with a counter per chain only the group's own chain can reach its rows.
|
|
5
|
+
type t = dict<bool>
|
|
6
|
+
|
|
7
|
+
%%private(
|
|
8
|
+
let anyChainSaves = (shouldSaveHistory: dict<bool>) =>
|
|
9
|
+
shouldSaveHistory->Dict.valuesToArray->Array.some(saves => saves)
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
let decide = (config: Config.t, ~shouldSaveHistory: dict<bool>): t =>
|
|
13
|
+
if config.shouldSaveFullHistory {
|
|
14
|
+
shouldSaveHistory->Utils.Dict.mapValues(_ => true)
|
|
15
|
+
} else {
|
|
16
|
+
switch config.checkpointSequence {
|
|
17
|
+
| SharedAcrossChains =>
|
|
18
|
+
let saves = shouldSaveHistory->anyChainSaves
|
|
19
|
+
shouldSaveHistory->Utils.Dict.mapValues(_ => saves)
|
|
20
|
+
| PerChain => shouldSaveHistory
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Every chain the run indexes was decided for, so an absent one is a bug —
|
|
25
|
+
// answering `false` for it would silently leave the chain with nothing to roll
|
|
26
|
+
// back to.
|
|
27
|
+
let forChain = (t: t, chainId: ChainId.t): bool =>
|
|
28
|
+
switch t->ChainId.Dict.dangerouslyGetNonOption(chainId) {
|
|
29
|
+
| Some(saves) => saves
|
|
30
|
+
| None =>
|
|
31
|
+
JsError.throwWithMessage(
|
|
32
|
+
`Internal error: no history decision for chain ${chainId->ChainId.toString}. The policy is decided for every chain the run indexes.`,
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// A cross-chain group's rows are reachable by any chain's rollback.
|
|
37
|
+
let forScope = (t: t, ~scope: Internal.chainScope): bool =>
|
|
38
|
+
switch scope {
|
|
39
|
+
| Chain(chainId) => t->forChain(chainId)
|
|
40
|
+
| CrossChain => t->anyChainSaves
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Whether a run has stale history to prune at all: history it keeps but doesn't
|
|
44
|
+
// keep forever.
|
|
45
|
+
let mayPrune = (config: Config.t, ~shouldSaveHistory: dict<bool>) =>
|
|
46
|
+
!config.shouldSaveFullHistory && shouldSaveHistory->anyChainSaves
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Utils from "./Utils.res.mjs";
|
|
4
|
+
import * as ChainId from "./ChainId.res.mjs";
|
|
5
|
+
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
6
|
+
|
|
7
|
+
function decide(config, shouldSaveHistory) {
|
|
8
|
+
if (config.shouldSaveFullHistory) {
|
|
9
|
+
return Utils.Dict.mapValues(shouldSaveHistory, param => true);
|
|
10
|
+
}
|
|
11
|
+
let match = config.checkpointSequence;
|
|
12
|
+
if (match !== "SharedAcrossChains") {
|
|
13
|
+
return shouldSaveHistory;
|
|
14
|
+
}
|
|
15
|
+
let saves = Object.values(shouldSaveHistory).some(saves => saves);
|
|
16
|
+
return Utils.Dict.mapValues(shouldSaveHistory, param => saves);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function forChain(t, chainId) {
|
|
20
|
+
let saves = t[chainId];
|
|
21
|
+
if (saves !== undefined) {
|
|
22
|
+
return saves;
|
|
23
|
+
} else {
|
|
24
|
+
return Stdlib_JsError.throwWithMessage(`Internal error: no history decision for chain ` + ChainId.toString(chainId) + `. The policy is decided for every chain the run indexes.`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function forScope(t, scope) {
|
|
29
|
+
if (scope === "crossChain") {
|
|
30
|
+
return Object.values(t).some(saves => saves);
|
|
31
|
+
} else {
|
|
32
|
+
return forChain(t, scope);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function mayPrune(config, shouldSaveHistory) {
|
|
37
|
+
if (config.shouldSaveFullHistory) {
|
|
38
|
+
return false;
|
|
39
|
+
} else {
|
|
40
|
+
return Object.values(shouldSaveHistory).some(saves => saves);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export {
|
|
45
|
+
decide,
|
|
46
|
+
forChain,
|
|
47
|
+
forScope,
|
|
48
|
+
mayPrune,
|
|
49
|
+
}
|
|
50
|
+
/* Utils Not a pure module */
|
package/src/InMemoryStore.res
CHANGED
|
@@ -49,10 +49,7 @@ let getEffectInMemTable = (
|
|
|
49
49
|
) => state->IndexerState.effectState->EffectState.getTable(~effect, ~scope)
|
|
50
50
|
|
|
51
51
|
let hasEffectOutput = (inMemTable: EffectState.effectCacheInMemTable, key) =>
|
|
52
|
-
|
|
53
|
-
| Some(Set(_)) => true
|
|
54
|
-
| Some(Delete(_)) | None => false
|
|
55
|
-
}
|
|
52
|
+
inMemTable.dict->Utils.Dict.dangerouslyGetNonOption(key)->Option.isSome
|
|
56
53
|
|
|
57
54
|
// Returns the raw output. The output is itself an option for effects with an
|
|
58
55
|
// optional output, so it must never be wrapped in another option here: Some(None)
|
|
@@ -62,14 +59,15 @@ let getEffectOutputUnsafe = (
|
|
|
62
59
|
key,
|
|
63
60
|
): Internal.effectOutput =>
|
|
64
61
|
switch inMemTable.dict->Utils.Dict.dangerouslyGetNonOption(key) {
|
|
65
|
-
| Some(
|
|
66
|
-
|
|
|
62
|
+
| Some({output}) => output
|
|
63
|
+
| None => %raw(`undefined`)
|
|
67
64
|
}
|
|
68
65
|
|
|
69
66
|
// Records a handler output. Persisted on the next write only when shouldCache;
|
|
70
67
|
// otherwise kept in memory (re-run on a later miss) but never written to the db.
|
|
71
68
|
let setEffectOutput = (
|
|
72
69
|
inMemTable: EffectState.effectCacheInMemTable,
|
|
70
|
+
~chainId,
|
|
73
71
|
~checkpointId,
|
|
74
72
|
~cacheKey,
|
|
75
73
|
~output,
|
|
@@ -79,10 +77,7 @@ let setEffectOutput = (
|
|
|
79
77
|
| Some(_) => ()
|
|
80
78
|
| None => inMemTable.changesCount = inMemTable.changesCount +. 1.
|
|
81
79
|
}
|
|
82
|
-
inMemTable.dict->Dict.set(
|
|
83
|
-
cacheKey,
|
|
84
|
-
Set({entityId: cacheKey->EntityId.unsafeOfString, entity: output, checkpointId}),
|
|
85
|
-
)
|
|
80
|
+
inMemTable.dict->Dict.set(cacheKey, {output, chainId, checkpointId})
|
|
86
81
|
if shouldCache {
|
|
87
82
|
inMemTable.idsToStore->Array.push(cacheKey)->ignore
|
|
88
83
|
}
|
|
@@ -90,16 +85,17 @@ let setEffectOutput = (
|
|
|
90
85
|
|
|
91
86
|
// Seeds an entry from a db read. Stamped with loadedFromDbCheckpointId so it's
|
|
92
87
|
// always droppable (re-readable from the db) and never re-persisted.
|
|
93
|
-
let initEffectOutputFromDb = (
|
|
88
|
+
let initEffectOutputFromDb = (
|
|
89
|
+
inMemTable: EffectState.effectCacheInMemTable,
|
|
90
|
+
~chainId,
|
|
91
|
+
~cacheKey,
|
|
92
|
+
~output,
|
|
93
|
+
) =>
|
|
94
94
|
if inMemTable.dict->Utils.Dict.dangerouslyGetNonOption(cacheKey)->Option.isNone {
|
|
95
95
|
inMemTable.changesCount = inMemTable.changesCount +. 1.
|
|
96
96
|
inMemTable.dict->Dict.set(
|
|
97
97
|
cacheKey,
|
|
98
|
-
|
|
99
|
-
entityId: cacheKey->EntityId.unsafeOfString,
|
|
100
|
-
entity: output,
|
|
101
|
-
checkpointId: Internal.loadedFromDbCheckpointId,
|
|
102
|
-
}),
|
|
98
|
+
{output, chainId, checkpointId: Internal.loadedFromDbCheckpointId},
|
|
103
99
|
)
|
|
104
100
|
}
|
|
105
101
|
|
|
@@ -108,14 +104,13 @@ let initEffectOutputFromDb = (inMemTable: EffectState.effectCacheInMemTable, ~ca
|
|
|
108
104
|
// seeded from a db read are spared. Mirrors entity dropCommittedChanges.
|
|
109
105
|
let dropCommittedEffects = (
|
|
110
106
|
inMemTable: EffectState.effectCacheInMemTable,
|
|
111
|
-
~
|
|
107
|
+
~committedFrontier: Frontier.t,
|
|
112
108
|
~keepLoadedFromDb,
|
|
113
109
|
) => {
|
|
114
110
|
let keysToDelete = []
|
|
115
|
-
inMemTable.dict->Utils.Dict.forEachWithKey((
|
|
116
|
-
let checkpointId = change->Change.getCheckpointId
|
|
111
|
+
inMemTable.dict->Utils.Dict.forEachWithKey(({chainId, checkpointId}, key) => {
|
|
117
112
|
if (
|
|
118
|
-
!(checkpointId >
|
|
113
|
+
!(checkpointId > committedFrontier->Frontier.get(chainId)) &&
|
|
119
114
|
!(keepLoadedFromDb && checkpointId == Internal.loadedFromDbCheckpointId)
|
|
120
115
|
) {
|
|
121
116
|
keysToDelete->Array.push(key)
|
|
@@ -128,18 +123,21 @@ let dropCommittedEffects = (
|
|
|
128
123
|
let prepareRollbackDiff = async (
|
|
129
124
|
state: IndexerState.t,
|
|
130
125
|
~floors: RollbackFloors.t,
|
|
131
|
-
~rollbackDiffCheckpointId,
|
|
132
126
|
~progressedChains,
|
|
133
127
|
~rolledBackAddresses,
|
|
134
128
|
) => {
|
|
135
|
-
|
|
136
|
-
~
|
|
137
|
-
~floors,
|
|
138
|
-
~progressedChains,
|
|
139
|
-
~rolledBackAddresses,
|
|
140
|
-
)
|
|
129
|
+
let diffFrontier =
|
|
130
|
+
state->IndexerState.beginRollbackDiff(~floors, ~progressedChains, ~rolledBackAddresses)
|
|
141
131
|
let persistence = state->IndexerState.persistence
|
|
142
|
-
let
|
|
132
|
+
let sequence = (state->IndexerState.config).checkpointSequence
|
|
133
|
+
let diffCheckpointId = scope =>
|
|
134
|
+
switch sequence->CheckpointSequence.findForScope(diffFrontier, ~scope) {
|
|
135
|
+
| Some(checkpointId) => checkpointId
|
|
136
|
+
| None =>
|
|
137
|
+
JsError.throwWithMessage(
|
|
138
|
+
"Internal error: the rollback returned rows for a chain its diff never moved.",
|
|
139
|
+
)
|
|
140
|
+
}
|
|
143
141
|
|
|
144
142
|
let deletedEntities = Dict.make()
|
|
145
143
|
let setEntities = Dict.make()
|
|
@@ -160,10 +158,10 @@ let prepareRollbackDiff = async (
|
|
|
160
158
|
state
|
|
161
159
|
->getInMemTable(~entityConfig, ~scope)
|
|
162
160
|
->InMemoryTable.Entity.set(
|
|
163
|
-
~committedCheckpointId,
|
|
161
|
+
~committedCheckpointId=state->IndexerState.committedCheckpointIdFor(~scope),
|
|
164
162
|
Delete({
|
|
165
163
|
entityId,
|
|
166
|
-
checkpointId:
|
|
164
|
+
checkpointId: diffCheckpointId(scope),
|
|
167
165
|
}),
|
|
168
166
|
)
|
|
169
167
|
})
|
|
@@ -174,10 +172,10 @@ let prepareRollbackDiff = async (
|
|
|
174
172
|
state
|
|
175
173
|
->getInMemTable(~entityConfig, ~scope)
|
|
176
174
|
->InMemoryTable.Entity.set(
|
|
177
|
-
~committedCheckpointId,
|
|
175
|
+
~committedCheckpointId=state->IndexerState.committedCheckpointIdFor(~scope),
|
|
178
176
|
Set({
|
|
179
177
|
entityId: entity.id->EntityId.unsafeOfString,
|
|
180
|
-
checkpointId:
|
|
178
|
+
checkpointId: diffCheckpointId(scope),
|
|
181
179
|
entity,
|
|
182
180
|
}),
|
|
183
181
|
)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import * as Table from "./db/Table.res.mjs";
|
|
4
4
|
import * as Utils from "./Utils.res.mjs";
|
|
5
|
+
import * as Frontier from "./db/Frontier.res.mjs";
|
|
5
6
|
import * as Internal from "./Internal.res.mjs";
|
|
6
7
|
import * as ChainState from "./ChainState.res.mjs";
|
|
7
8
|
import * as EffectState from "./EffectState.res.mjs";
|
|
@@ -11,6 +12,7 @@ import * as InMemoryTable from "./InMemoryTable.res.mjs";
|
|
|
11
12
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
12
13
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
13
14
|
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
15
|
+
import * as CheckpointSequence from "./db/CheckpointSequence.res.mjs";
|
|
14
16
|
|
|
15
17
|
function getInMemTable(state, entityConfig, scope) {
|
|
16
18
|
let tmp;
|
|
@@ -45,23 +47,19 @@ function getEffectInMemTable(state, effect, scope) {
|
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
function hasEffectOutput(inMemTable, key) {
|
|
48
|
-
|
|
49
|
-
if (match !== undefined) {
|
|
50
|
-
return match.type === "SET";
|
|
51
|
-
} else {
|
|
52
|
-
return false;
|
|
53
|
-
}
|
|
50
|
+
return Stdlib_Option.isSome(inMemTable.dict[key]);
|
|
54
51
|
}
|
|
55
52
|
|
|
56
53
|
function getEffectOutputUnsafe(inMemTable, key) {
|
|
57
54
|
let match = inMemTable.dict[key];
|
|
58
|
-
if (match !== undefined
|
|
59
|
-
return match.
|
|
55
|
+
if (match !== undefined) {
|
|
56
|
+
return match.output;
|
|
57
|
+
} else {
|
|
58
|
+
return undefined;
|
|
60
59
|
}
|
|
61
|
-
return undefined;
|
|
62
60
|
}
|
|
63
61
|
|
|
64
|
-
function setEffectOutput(inMemTable, checkpointId, cacheKey, output, shouldCache) {
|
|
62
|
+
function setEffectOutput(inMemTable, chainId, checkpointId, cacheKey, output, shouldCache) {
|
|
65
63
|
let match = inMemTable.dict[cacheKey];
|
|
66
64
|
if (match !== undefined) {
|
|
67
65
|
|
|
@@ -69,9 +67,8 @@ function setEffectOutput(inMemTable, checkpointId, cacheKey, output, shouldCache
|
|
|
69
67
|
inMemTable.changesCount = inMemTable.changesCount + 1;
|
|
70
68
|
}
|
|
71
69
|
inMemTable.dict[cacheKey] = {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
entity: output,
|
|
70
|
+
output: output,
|
|
71
|
+
chainId: chainId,
|
|
75
72
|
checkpointId: checkpointId
|
|
76
73
|
};
|
|
77
74
|
if (shouldCache) {
|
|
@@ -80,24 +77,23 @@ function setEffectOutput(inMemTable, checkpointId, cacheKey, output, shouldCache
|
|
|
80
77
|
}
|
|
81
78
|
}
|
|
82
79
|
|
|
83
|
-
function initEffectOutputFromDb(inMemTable, cacheKey, output) {
|
|
80
|
+
function initEffectOutputFromDb(inMemTable, chainId, cacheKey, output) {
|
|
84
81
|
if (Stdlib_Option.isNone(inMemTable.dict[cacheKey])) {
|
|
85
82
|
inMemTable.changesCount = inMemTable.changesCount + 1;
|
|
86
83
|
inMemTable.dict[cacheKey] = {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
entity: output,
|
|
84
|
+
output: output,
|
|
85
|
+
chainId: chainId,
|
|
90
86
|
checkpointId: Internal.loadedFromDbCheckpointId
|
|
91
87
|
};
|
|
92
88
|
return;
|
|
93
89
|
}
|
|
94
90
|
}
|
|
95
91
|
|
|
96
|
-
function dropCommittedEffects(inMemTable,
|
|
92
|
+
function dropCommittedEffects(inMemTable, committedFrontier, keepLoadedFromDb) {
|
|
97
93
|
let keysToDelete = [];
|
|
98
|
-
Utils.Dict.forEachWithKey(inMemTable.dict, (
|
|
99
|
-
let checkpointId =
|
|
100
|
-
if (checkpointId <=
|
|
94
|
+
Utils.Dict.forEachWithKey(inMemTable.dict, (param, key) => {
|
|
95
|
+
let checkpointId = param.checkpointId;
|
|
96
|
+
if (checkpointId <= Frontier.get(committedFrontier, param.chainId) && !(keepLoadedFromDb && checkpointId === Internal.loadedFromDbCheckpointId)) {
|
|
101
97
|
keysToDelete.push(key);
|
|
102
98
|
return;
|
|
103
99
|
}
|
|
@@ -106,31 +102,40 @@ function dropCommittedEffects(inMemTable, committedCheckpointId, keepLoadedFromD
|
|
|
106
102
|
inMemTable.changesCount = inMemTable.changesCount - keysToDelete.length;
|
|
107
103
|
}
|
|
108
104
|
|
|
109
|
-
async function prepareRollbackDiff(state, floors,
|
|
110
|
-
IndexerState.beginRollbackDiff(state,
|
|
105
|
+
async function prepareRollbackDiff(state, floors, progressedChains, rolledBackAddresses) {
|
|
106
|
+
let diffFrontier = IndexerState.beginRollbackDiff(state, floors, progressedChains, rolledBackAddresses);
|
|
111
107
|
let persistence = IndexerState.persistence(state);
|
|
112
|
-
let
|
|
108
|
+
let sequence = IndexerState.config(state).checkpointSequence;
|
|
109
|
+
let diffCheckpointId = scope => {
|
|
110
|
+
let checkpointId = CheckpointSequence.findForScope(sequence, diffFrontier, scope);
|
|
111
|
+
if (checkpointId !== undefined) {
|
|
112
|
+
return checkpointId;
|
|
113
|
+
} else {
|
|
114
|
+
return Stdlib_JsError.throwWithMessage("Internal error: the rollback returned rows for a chain its diff never moved.");
|
|
115
|
+
}
|
|
116
|
+
};
|
|
113
117
|
let deletedEntities = {};
|
|
114
118
|
let setEntities = {};
|
|
115
119
|
await Promise.all(persistence.allEntities.filter(entityConfig => entityConfig.storage.postgres).map(async entityConfig => {
|
|
116
120
|
let match = await persistence.storage.getRollbackData(entityConfig, floors);
|
|
117
121
|
match[0].forEach(param => {
|
|
122
|
+
let scope = param.scope;
|
|
118
123
|
let entityId = param.entityId;
|
|
119
124
|
Utils.Dict.push(deletedEntities, entityConfig.name, entityId);
|
|
120
|
-
InMemoryTable.Entity.set(getInMemTable(state, entityConfig,
|
|
125
|
+
InMemoryTable.Entity.set(getInMemTable(state, entityConfig, scope), IndexerState.committedCheckpointIdFor(state, scope), {
|
|
121
126
|
type: "DELETE",
|
|
122
127
|
entityId: entityId,
|
|
123
|
-
checkpointId:
|
|
128
|
+
checkpointId: diffCheckpointId(scope)
|
|
124
129
|
});
|
|
125
130
|
});
|
|
126
131
|
match[1].forEach(entity => {
|
|
127
132
|
let scope = takeRowScope(entity, entityConfig);
|
|
128
133
|
Utils.Dict.push(setEntities, entityConfig.name, entity.id);
|
|
129
|
-
InMemoryTable.Entity.set(getInMemTable(state, entityConfig, scope),
|
|
134
|
+
InMemoryTable.Entity.set(getInMemTable(state, entityConfig, scope), IndexerState.committedCheckpointIdFor(state, scope), {
|
|
130
135
|
type: "SET",
|
|
131
136
|
entityId: entity.id,
|
|
132
137
|
entity: entity,
|
|
133
|
-
checkpointId:
|
|
138
|
+
checkpointId: diffCheckpointId(scope)
|
|
134
139
|
});
|
|
135
140
|
});
|
|
136
141
|
}));
|