envio 3.9.0 → 3.10.0-subgraph
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/evm.schema.json +26 -0
- package/fuel.schema.json +26 -0
- package/index.d.ts +41 -8
- package/package.json +6 -7
- package/src/Batch.res +9 -0
- package/src/ChainState.res +35 -39
- package/src/ChainState.res.mjs +14 -11
- package/src/ChainState.resi +15 -7
- package/src/Config.res +61 -44
- package/src/Config.res.mjs +56 -19
- package/src/Core.res +19 -0
- package/src/Core.res.mjs +11 -0
- package/src/CrossChainState.res +15 -23
- package/src/CrossChainState.res.mjs +10 -18
- package/src/CrossChainState.resi +4 -1
- package/src/Ecosystem.res +7 -2
- package/src/Envio.res +6 -4
- package/src/EventConfigBuilder.res +169 -26
- package/src/EventConfigBuilder.res.mjs +124 -15
- package/src/EventProcessing.res +6 -6
- package/src/EventProcessing.res.mjs +8 -6
- package/src/HandlerLoader.res +20 -8
- package/src/HandlerLoader.res.mjs +11 -2
- package/src/HandlerRegister.res +21 -4
- package/src/HandlerRegister.res.mjs +24 -5
- package/src/InMemoryStore.res +9 -7
- package/src/InMemoryStore.res.mjs +3 -3
- package/src/IndexerState.res +43 -6
- package/src/IndexerState.res.mjs +36 -7
- package/src/IndexerState.resi +4 -4
- package/src/Internal.res +16 -17
- package/src/Metrics.res +72 -6
- package/src/Metrics.res.mjs +30 -2
- package/src/Persistence.res +36 -30
- package/src/Persistence.res.mjs +1 -20
- package/src/PgStorage.res +104 -56
- package/src/PgStorage.res.mjs +57 -35
- package/src/PruneStaleHistory.res +85 -52
- package/src/PruneStaleHistory.res.mjs +63 -37
- package/src/Rollback.res +68 -45
- package/src/Rollback.res.mjs +38 -19
- package/src/RollbackCommit.res +7 -9
- package/src/RollbackCommit.res.mjs +5 -6
- package/src/SafeCheckpointTracking.res +11 -11
- package/src/SafeCheckpointTracking.res.mjs +7 -6
- package/src/SimulateItems.res +33 -18
- package/src/SimulateItems.res.mjs +26 -22
- package/src/Sink.res +38 -26
- package/src/Sink.res.mjs +21 -16
- package/src/TestIndexer.res +5 -5
- package/src/TestIndexer.res.mjs +1 -1
- package/src/UserContext.res +358 -51
- package/src/UserContext.res.mjs +271 -35
- package/src/Utils.res +170 -0
- package/src/Utils.res.mjs +153 -0
- package/src/Writing.res +2 -2
- package/src/Writing.res.mjs +1 -1
- package/src/bindings/ClickHouse.res +366 -698
- package/src/bindings/ClickHouse.res.mjs +371 -421
- package/src/bindings/ClickHouseSink.res +337 -0
- package/src/bindings/ClickHouseSink.res.mjs +246 -0
- package/src/bindings/EventSource.res +8 -2
- package/src/bindings/NodeJs.res +6 -0
- package/src/bindings/Vitest.res +23 -0
- package/src/bindings/Vitest.res.mjs +3 -0
- package/src/bindings/WebSocket.res +9 -10
- package/src/db/CheckpointBounds.res +53 -0
- package/src/db/CheckpointBounds.res.mjs +44 -0
- package/src/db/EntityFilter.res +36 -17
- package/src/db/EntityFilter.res.mjs +25 -14
- package/src/db/EntityHistory.res +47 -22
- package/src/db/EntityHistory.res.mjs +19 -12
- package/src/db/InternalTable.res +84 -35
- package/src/db/InternalTable.res.mjs +56 -23
- package/src/db/RollbackFloors.res +55 -0
- package/src/db/RollbackFloors.res.mjs +92 -0
- package/src/db/Table.res +22 -4
- package/src/db/Table.res.mjs +30 -9
- package/src/sources/Evm.res +2 -0
- package/src/sources/Evm.res.mjs +2 -0
- package/src/sources/EvmHyperSyncSource.res +3 -3
- package/src/sources/EvmHyperSyncSource.res.mjs +3 -3
- package/src/sources/EvmRpcWs.res +124 -0
- package/src/sources/EvmRpcWs.res.mjs +117 -0
- package/src/sources/Fuel.res +2 -0
- package/src/sources/Fuel.res.mjs +2 -0
- package/src/sources/FuelHyperSyncSource.res +1 -1
- package/src/sources/FuelHyperSyncSource.res.mjs +2 -1
- package/src/sources/HeightFeed.res +568 -0
- package/src/sources/HeightFeed.res.mjs +452 -0
- package/src/sources/HeightStream.res +257 -0
- package/src/sources/HeightStream.res.mjs +194 -0
- package/src/sources/HyperSync.res +5 -0
- package/src/sources/HyperSync.res.mjs +3 -0
- package/src/sources/HyperSync.resi +4 -0
- package/src/sources/HyperSyncSSE.res +49 -0
- package/src/sources/HyperSyncSSE.res.mjs +79 -0
- package/src/sources/RpcSource.res +1 -1
- package/src/sources/RpcSource.res.mjs +2 -2
- package/src/sources/Source.res +54 -1
- package/src/sources/Source.res.mjs +25 -0
- package/src/sources/SourceManager.res +220 -227
- package/src/sources/SourceManager.res.mjs +139 -164
- package/src/sources/SourceManager.resi +8 -0
- package/src/sources/Svm.res +2 -0
- package/src/sources/Svm.res.mjs +2 -0
- package/src/sources/SvmHyperSyncClient.res +70 -127
- package/src/sources/SvmHyperSyncClient.res.mjs +51 -25
- package/src/sources/SvmHyperSyncSource.res +18 -15
- package/src/sources/SvmHyperSyncSource.res.mjs +5 -4
- package/src/subgraph/blocks.ts +176 -0
- package/src/subgraph/calls.ts +217 -0
- package/src/subgraph/conformance.ts +102 -0
- package/src/subgraph/division.ts +133 -0
- package/src/subgraph/errors.ts +90 -0
- package/src/subgraph/graph-ts-types/VERSION +2 -0
- package/src/subgraph/graph-ts-types/chain/arweave.d.ts +70 -0
- package/src/subgraph/graph-ts-types/chain/cosmos.d.ts +327 -0
- package/src/subgraph/graph-ts-types/chain/ethereum.d.ts +233 -0
- package/src/subgraph/graph-ts-types/chain/near.d.ts +253 -0
- package/src/subgraph/graph-ts-types/chain/starknet.d.ts +32 -0
- package/src/subgraph/graph-ts-types/common/collections.d.ts +136 -0
- package/src/subgraph/graph-ts-types/common/conversion.d.ts +11 -0
- package/src/subgraph/graph-ts-types/common/datasource.d.ts +30 -0
- package/src/subgraph/graph-ts-types/common/eager-offset.d.ts +0 -0
- package/src/subgraph/graph-ts-types/common/json.d.ts +17 -0
- package/src/subgraph/graph-ts-types/common/numbers.d.ts +120 -0
- package/src/subgraph/graph-ts-types/common/value.d.ts +120 -0
- package/src/subgraph/graph-ts-types/common/yaml.d.ts +90 -0
- package/src/subgraph/graph-ts-types/global/global.d.ts +194 -0
- package/src/subgraph/graph-ts-types/helper-functions.d.ts +22 -0
- package/src/subgraph/graph-ts-types/index.d.ts +102 -0
- package/src/subgraph/graph-ts.ts +1948 -0
- package/src/subgraph/hosts.ts +148 -0
- package/src/subgraph/runtime.ts +863 -0
- package/src/subgraph/scope.ts +66 -0
- package/svm.schema.json +6 -81
- package/src/MemoryStorage.res +0 -729
- package/src/MemoryStorage.res.mjs +0 -586
- package/src/sources/HyperSyncHeightStream.res +0 -129
- package/src/sources/HyperSyncHeightStream.res.mjs +0 -110
- package/src/sources/RpcWebSocketHeightStream.res +0 -175
- package/src/sources/RpcWebSocketHeightStream.res.mjs +0 -180
|
@@ -12,11 +12,43 @@ let maxEntitiesPerWrite = 5
|
|
|
12
12
|
let forcedIntervalMultiplier = 5.
|
|
13
13
|
|
|
14
14
|
type targets = {
|
|
15
|
-
|
|
15
|
+
safeCheckpoints: CheckpointBounds.t,
|
|
16
16
|
concurrent: array<Internal.entityConfig>,
|
|
17
17
|
forced: array<Internal.entityConfig>,
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
// Where each chain's history may be pruned back to, or None while nowhere can.
|
|
21
|
+
//
|
|
22
|
+
// A rollback only ever deletes a chain's rows down to that chain's own floor,
|
|
23
|
+
// which is set by its own reorg and so never reaches below its own safe
|
|
24
|
+
// checkpoint. Each chain can therefore prune to its own, and a chain with
|
|
25
|
+
// nothing safe yet holds only itself back.
|
|
26
|
+
//
|
|
27
|
+
// Unless the schema has a cross-chain entity: a reorg on any chain then rolls
|
|
28
|
+
// every chain back to one checkpoint, which can sit below another chain's safe
|
|
29
|
+
// point, so the lowest is the only bound every chain agrees on.
|
|
30
|
+
let selectSafeCheckpoints = (state: IndexerState.t) => {
|
|
31
|
+
let byChain = state->IndexerState.getSafeCheckpointIdByChain
|
|
32
|
+
let safe =
|
|
33
|
+
byChain->Array.filterMap(((chainId, checkpointId)) =>
|
|
34
|
+
checkpointId->Option.map(checkpointId => (chainId, checkpointId))
|
|
35
|
+
)
|
|
36
|
+
if state->IndexerState.config->Config.isIsolatedMultichain {
|
|
37
|
+
safe->Utils.Array.notEmpty ? Some(CheckpointBounds.PerChain(safe)) : None
|
|
38
|
+
} else if safe->Array.length === byChain->Array.length {
|
|
39
|
+
let (_, lowest) = safe->Array.getUnsafe(0)
|
|
40
|
+
Some(
|
|
41
|
+
CheckpointBounds.EveryChain(
|
|
42
|
+
safe->Array.reduce(lowest, (lowest, (_, checkpointId)) =>
|
|
43
|
+
Pervasives.min(lowest, checkpointId)
|
|
44
|
+
),
|
|
45
|
+
),
|
|
46
|
+
)
|
|
47
|
+
} else {
|
|
48
|
+
None
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
20
52
|
let selectFrom = (
|
|
21
53
|
~allEntities: array<Internal.entityConfig>,
|
|
22
54
|
~lastPrunedAtMillis: dict<float>,
|
|
@@ -24,7 +56,7 @@ let selectFrom = (
|
|
|
24
56
|
~isRollback,
|
|
25
57
|
~nowMillis,
|
|
26
58
|
~intervalMillis,
|
|
27
|
-
~
|
|
59
|
+
~safeCheckpoints,
|
|
28
60
|
) => {
|
|
29
61
|
let byOldestPrune = ((a, _), (b, _)) => a -. b
|
|
30
62
|
let toEntities = candidates => candidates->Array.map(((_, entityConfig)) => entityConfig)
|
|
@@ -60,7 +92,7 @@ let selectFrom = (
|
|
|
60
92
|
}
|
|
61
93
|
|
|
62
94
|
{
|
|
63
|
-
|
|
95
|
+
safeCheckpoints,
|
|
64
96
|
concurrent: sortedConcurrent->Array.slice(~start=0, ~end=maxEntitiesPerWrite)->toEntities,
|
|
65
97
|
forced: forcedCandidates
|
|
66
98
|
->Array.toSorted(byOldestPrune)
|
|
@@ -72,62 +104,63 @@ let selectFrom = (
|
|
|
72
104
|
let select = (state: IndexerState.t, ~writtenEntityNames, ~isRollback) => {
|
|
73
105
|
let config = state->IndexerState.config
|
|
74
106
|
if config->Config.shouldPruneHistory(~isInReorgThreshold=state->IndexerState.isInReorgThreshold) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
~safeCheckpointId,
|
|
87
|
-
),
|
|
107
|
+
state
|
|
108
|
+
->selectSafeCheckpoints
|
|
109
|
+
->Option.map(safeCheckpoints =>
|
|
110
|
+
selectFrom(
|
|
111
|
+
~allEntities=(state->IndexerState.persistence).allEntities,
|
|
112
|
+
~lastPrunedAtMillis=state->IndexerState.lastPrunedAtMillis,
|
|
113
|
+
~writtenEntityNames,
|
|
114
|
+
~isRollback,
|
|
115
|
+
~nowMillis=Date.now(),
|
|
116
|
+
~intervalMillis=Env.ThrottleWrites.pruneStaleDataIntervalMillis->Int.toFloat,
|
|
117
|
+
~safeCheckpoints,
|
|
88
118
|
)
|
|
89
|
-
|
|
119
|
+
)
|
|
90
120
|
} else {
|
|
91
121
|
None
|
|
92
122
|
}
|
|
93
123
|
}
|
|
94
124
|
|
|
95
|
-
let
|
|
125
|
+
let pruneEntity = async (
|
|
126
|
+
state: IndexerState.t,
|
|
127
|
+
~entityConfig: Internal.entityConfig,
|
|
128
|
+
~safeCheckpoints,
|
|
129
|
+
) => {
|
|
96
130
|
let persistence = state->IndexerState.persistence
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
131
|
+
let timeRef = Performance.now()
|
|
132
|
+
// Recorded for failures too, so a failing prune retries on the same
|
|
133
|
+
// interval instead of on every write.
|
|
134
|
+
state->IndexerState.lastPrunedAtMillis->Dict.set(entityConfig.name, Date.now())
|
|
135
|
+
switch await persistence.storage.pruneStaleEntityHistory(
|
|
136
|
+
~entityName=entityConfig.name,
|
|
137
|
+
~entityIndex=entityConfig.index,
|
|
138
|
+
~chainIdColumn=entityConfig.table->Table.getPgChainIdColumn,
|
|
139
|
+
~safeCheckpoints,
|
|
140
|
+
) {
|
|
141
|
+
| () =>
|
|
142
|
+
state->IndexerState.recordHistoryPrune(
|
|
143
|
+
~timeSeconds=Performance.secondsSince(timeRef),
|
|
104
144
|
~entityName=entityConfig.name,
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
~timeSeconds=Performance.secondsSince(timeRef),
|
|
112
|
-
~entityName=entityConfig.name,
|
|
113
|
-
)
|
|
114
|
-
| exception exn =>
|
|
115
|
-
// Pruning is cleanup; a failure must not fail the write loop.
|
|
116
|
-
Logging.createChild(
|
|
117
|
-
~params={
|
|
118
|
-
"entityName": entityConfig.name,
|
|
119
|
-
"safeCheckpointId": safeCheckpointId,
|
|
120
|
-
},
|
|
121
|
-
)->Logging.childErrorWithExn(exn->Utils.prettifyExn, `Failed to prune stale entity history`)
|
|
122
|
-
}
|
|
145
|
+
)
|
|
146
|
+
| exception exn =>
|
|
147
|
+
// Pruning is cleanup; a failure must not fail the write loop.
|
|
148
|
+
Logging.createChild(
|
|
149
|
+
~params={"entityName": entityConfig.name, "safeCheckpoints": safeCheckpoints},
|
|
150
|
+
)->Logging.childErrorWithExn(exn->Utils.prettifyExn, `Failed to prune stale entity history`)
|
|
123
151
|
}
|
|
124
152
|
}
|
|
125
153
|
|
|
126
|
-
let
|
|
127
|
-
|
|
154
|
+
let pruneEntities = (state: IndexerState.t, ~entities, ~safeCheckpoints) =>
|
|
155
|
+
entities->Utils.Array.awaitEach(entityConfig =>
|
|
156
|
+
state->pruneEntity(~entityConfig, ~safeCheckpoints)
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
let pruneCheckpoints = async (state: IndexerState.t, ~safeCheckpoints) => {
|
|
160
|
+
switch await (state->IndexerState.persistence).storage.pruneStaleCheckpoints(~safeCheckpoints) {
|
|
128
161
|
| () => ()
|
|
129
162
|
| exception exn =>
|
|
130
|
-
Logging.createChild(~params={"
|
|
163
|
+
Logging.createChild(~params={"safeCheckpoints": safeCheckpoints})->Logging.childErrorWithExn(
|
|
131
164
|
exn->Utils.prettifyExn,
|
|
132
165
|
`Failed to prune stale checkpoints`,
|
|
133
166
|
)
|
|
@@ -136,22 +169,22 @@ let pruneCheckpoints = async (state: IndexerState.t, ~safeCheckpointId) => {
|
|
|
136
169
|
|
|
137
170
|
let runConcurrent = async (state: IndexerState.t, ~targets) => {
|
|
138
171
|
switch targets {
|
|
139
|
-
| Some({
|
|
140
|
-
await pruneCheckpoints(state, ~
|
|
141
|
-
await pruneEntities(state, ~entities=concurrent, ~
|
|
172
|
+
| Some({safeCheckpoints, concurrent}) if concurrent->Utils.Array.notEmpty =>
|
|
173
|
+
await pruneCheckpoints(state, ~safeCheckpoints)
|
|
174
|
+
await pruneEntities(state, ~entities=concurrent, ~safeCheckpoints)
|
|
142
175
|
| Some(_) | None => ()
|
|
143
176
|
}
|
|
144
177
|
}
|
|
145
178
|
|
|
146
179
|
let runForced = async (state: IndexerState.t, ~targets) => {
|
|
147
180
|
switch targets {
|
|
148
|
-
| Some({
|
|
181
|
+
| Some({safeCheckpoints, concurrent, forced}) if forced->Utils.Array.notEmpty =>
|
|
149
182
|
// When nothing ran concurrently (eg a rollback write), checkpoint pruning
|
|
150
183
|
// lands here, after the write, so it never overlaps a rollback transaction.
|
|
151
184
|
if concurrent->Utils.Array.isEmpty {
|
|
152
|
-
await pruneCheckpoints(state, ~
|
|
185
|
+
await pruneCheckpoints(state, ~safeCheckpoints)
|
|
153
186
|
}
|
|
154
|
-
await pruneEntities(state, ~entities=forced, ~
|
|
187
|
+
await pruneEntities(state, ~entities=forced, ~safeCheckpoints)
|
|
155
188
|
| Some(_) | None => ()
|
|
156
189
|
}
|
|
157
190
|
}
|
|
@@ -7,10 +7,41 @@ import * as Config from "./Config.res.mjs";
|
|
|
7
7
|
import * as Logging from "./Logging.res.mjs";
|
|
8
8
|
import * as Performance from "./bindings/Performance.res.mjs";
|
|
9
9
|
import * as IndexerState from "./IndexerState.res.mjs";
|
|
10
|
+
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
10
11
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
12
|
+
import * as Primitive_bigint from "@rescript/runtime/lib/es6/Primitive_bigint.js";
|
|
11
13
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
12
14
|
|
|
13
|
-
function
|
|
15
|
+
function selectSafeCheckpoints(state) {
|
|
16
|
+
let byChain = IndexerState.getSafeCheckpointIdByChain(state);
|
|
17
|
+
let safe = Stdlib_Array.filterMap(byChain, param => {
|
|
18
|
+
let chainId = param[0];
|
|
19
|
+
return Stdlib_Option.map(param[1], checkpointId => [
|
|
20
|
+
chainId,
|
|
21
|
+
checkpointId
|
|
22
|
+
]);
|
|
23
|
+
});
|
|
24
|
+
if (Config.isIsolatedMultichain(IndexerState.config(state))) {
|
|
25
|
+
if (Utils.$$Array.notEmpty(safe)) {
|
|
26
|
+
return {
|
|
27
|
+
TAG: "PerChain",
|
|
28
|
+
_0: safe
|
|
29
|
+
};
|
|
30
|
+
} else {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (safe.length !== byChain.length) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
let match = safe[0];
|
|
38
|
+
return {
|
|
39
|
+
TAG: "EveryChain",
|
|
40
|
+
_0: Stdlib_Array.reduce(safe, match[1], (lowest, param) => Primitive_bigint.min(lowest, param[1]))
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function selectFrom(allEntities, lastPrunedAtMillis, writtenEntityNames, isRollback, nowMillis, intervalMillis, safeCheckpoints) {
|
|
14
45
|
let byOldestPrune = (param, param$1) => param[0] - param$1[0];
|
|
15
46
|
let toEntities = candidates => candidates.map(param => param[1]);
|
|
16
47
|
let concurrentCandidates = [];
|
|
@@ -44,7 +75,7 @@ function selectFrom(allEntities, lastPrunedAtMillis, writtenEntityNames, isRollb
|
|
|
44
75
|
}
|
|
45
76
|
}
|
|
46
77
|
return {
|
|
47
|
-
|
|
78
|
+
safeCheckpoints: safeCheckpoints,
|
|
48
79
|
concurrent: toEntities(sortedConcurrent.slice(0, 5)),
|
|
49
80
|
forced: toEntities(forcedCandidates.toSorted(byOldestPrune).slice(0, 5))
|
|
50
81
|
};
|
|
@@ -52,47 +83,40 @@ function selectFrom(allEntities, lastPrunedAtMillis, writtenEntityNames, isRollb
|
|
|
52
83
|
|
|
53
84
|
function select(state, writtenEntityNames, isRollback) {
|
|
54
85
|
let config = IndexerState.config(state);
|
|
55
|
-
if (
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
let safeCheckpointId = IndexerState.getSafeCheckpointId(state);
|
|
59
|
-
if (safeCheckpointId !== undefined) {
|
|
60
|
-
return selectFrom(IndexerState.persistence(state).allEntities, IndexerState.lastPrunedAtMillis(state), writtenEntityNames, isRollback, Date.now(), Env.ThrottleWrites.pruneStaleDataIntervalMillis, safeCheckpointId);
|
|
86
|
+
if (Config.shouldPruneHistory(config, IndexerState.isInReorgThreshold(state))) {
|
|
87
|
+
return Stdlib_Option.map(selectSafeCheckpoints(state), safeCheckpoints => selectFrom(IndexerState.persistence(state).allEntities, IndexerState.lastPrunedAtMillis(state), writtenEntityNames, isRollback, Date.now(), Env.ThrottleWrites.pruneStaleDataIntervalMillis, safeCheckpoints));
|
|
61
88
|
}
|
|
62
89
|
}
|
|
63
90
|
|
|
64
|
-
async function
|
|
91
|
+
async function pruneEntity(state, entityConfig, safeCheckpoints) {
|
|
65
92
|
let persistence = IndexerState.persistence(state);
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
Logging.childErrorWithExn(Logging.createChild({
|
|
78
|
-
entityName: entityConfig.name,
|
|
79
|
-
safeCheckpointId: safeCheckpointId
|
|
80
|
-
}), Utils.prettifyExn(exn), `Failed to prune stale entity history`);
|
|
81
|
-
}
|
|
82
|
-
if (exit === 1) {
|
|
83
|
-
IndexerState.recordHistoryPrune(state, entityConfig.name, Performance.secondsSince(timeRef));
|
|
84
|
-
}
|
|
93
|
+
let timeRef = Performance.now();
|
|
94
|
+
IndexerState.lastPrunedAtMillis(state)[entityConfig.name] = Date.now();
|
|
95
|
+
let val;
|
|
96
|
+
try {
|
|
97
|
+
val = await persistence.storage.pruneStaleEntityHistory(entityConfig.name, entityConfig.index, Table.getPgChainIdColumn(entityConfig.table), safeCheckpoints);
|
|
98
|
+
} catch (raw_exn) {
|
|
99
|
+
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
100
|
+
return Logging.childErrorWithExn(Logging.createChild({
|
|
101
|
+
entityName: entityConfig.name,
|
|
102
|
+
safeCheckpoints: safeCheckpoints
|
|
103
|
+
}), Utils.prettifyExn(exn), `Failed to prune stale entity history`);
|
|
85
104
|
}
|
|
105
|
+
return IndexerState.recordHistoryPrune(state, entityConfig.name, Performance.secondsSince(timeRef));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function pruneEntities(state, entities, safeCheckpoints) {
|
|
109
|
+
return Utils.$$Array.awaitEach(entities, entityConfig => pruneEntity(state, entityConfig, safeCheckpoints));
|
|
86
110
|
}
|
|
87
111
|
|
|
88
|
-
async function pruneCheckpoints(state,
|
|
112
|
+
async function pruneCheckpoints(state, safeCheckpoints) {
|
|
89
113
|
try {
|
|
90
|
-
await IndexerState.persistence(state).storage.pruneStaleCheckpoints(
|
|
114
|
+
await IndexerState.persistence(state).storage.pruneStaleCheckpoints(safeCheckpoints);
|
|
91
115
|
return;
|
|
92
116
|
} catch (raw_exn) {
|
|
93
117
|
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
94
118
|
return Logging.childErrorWithExn(Logging.createChild({
|
|
95
|
-
|
|
119
|
+
safeCheckpoints: safeCheckpoints
|
|
96
120
|
}), Utils.prettifyExn(exn), `Failed to prune stale checkpoints`);
|
|
97
121
|
}
|
|
98
122
|
}
|
|
@@ -102,10 +126,10 @@ async function runConcurrent(state, targets) {
|
|
|
102
126
|
return;
|
|
103
127
|
}
|
|
104
128
|
let concurrent = targets.concurrent;
|
|
105
|
-
let
|
|
129
|
+
let safeCheckpoints = targets.safeCheckpoints;
|
|
106
130
|
if (Utils.$$Array.notEmpty(concurrent)) {
|
|
107
|
-
await pruneCheckpoints(state,
|
|
108
|
-
return await pruneEntities(state, concurrent,
|
|
131
|
+
await pruneCheckpoints(state, safeCheckpoints);
|
|
132
|
+
return await pruneEntities(state, concurrent, safeCheckpoints);
|
|
109
133
|
}
|
|
110
134
|
}
|
|
111
135
|
|
|
@@ -114,12 +138,12 @@ async function runForced(state, targets) {
|
|
|
114
138
|
return;
|
|
115
139
|
}
|
|
116
140
|
let forced = targets.forced;
|
|
117
|
-
let
|
|
141
|
+
let safeCheckpoints = targets.safeCheckpoints;
|
|
118
142
|
if (Utils.$$Array.notEmpty(forced)) {
|
|
119
143
|
if (Utils.$$Array.isEmpty(targets.concurrent)) {
|
|
120
|
-
await pruneCheckpoints(state,
|
|
144
|
+
await pruneCheckpoints(state, safeCheckpoints);
|
|
121
145
|
}
|
|
122
|
-
return await pruneEntities(state, forced,
|
|
146
|
+
return await pruneEntities(state, forced, safeCheckpoints);
|
|
123
147
|
}
|
|
124
148
|
}
|
|
125
149
|
|
|
@@ -130,8 +154,10 @@ let forcedIntervalMultiplier = 5;
|
|
|
130
154
|
export {
|
|
131
155
|
maxEntitiesPerWrite,
|
|
132
156
|
forcedIntervalMultiplier,
|
|
157
|
+
selectSafeCheckpoints,
|
|
133
158
|
selectFrom,
|
|
134
159
|
select,
|
|
160
|
+
pruneEntity,
|
|
135
161
|
pruneEntities,
|
|
136
162
|
pruneCheckpoints,
|
|
137
163
|
runConcurrent,
|
package/src/Rollback.res
CHANGED
|
@@ -127,86 +127,105 @@ and executeRollback = async (
|
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
// The diff computed here replaces a pending one rather than merging with it,
|
|
131
|
+
// so its deletes have to cover everything that one would have deleted. The
|
|
132
|
+
// flush above leaves a diff pending only when no batch has come along to
|
|
133
|
+
// carry it.
|
|
134
|
+
let floors = {
|
|
135
|
+
let next = if state->IndexerState.config->Config.isIsolatedMultichain {
|
|
136
|
+
RollbackFloors.isolated(
|
|
137
|
+
~chainId=reorgChain,
|
|
138
|
+
~floorCheckpointId=rollbackTargetCheckpointId,
|
|
139
|
+
~forkBlockNumber=rollbackTargetBlockNumber,
|
|
140
|
+
)
|
|
141
|
+
} else {
|
|
142
|
+
RollbackFloors.global(
|
|
143
|
+
~floorCheckpointId=rollbackTargetCheckpointId,
|
|
144
|
+
~reorgChainId=reorgChain,
|
|
145
|
+
~forkBlockNumber=rollbackTargetBlockNumber,
|
|
146
|
+
)
|
|
147
|
+
}
|
|
148
|
+
switch state->IndexerState.pendingRollback {
|
|
149
|
+
| None => next
|
|
150
|
+
| Some({floors: pending}) => RollbackFloors.merge(pending, next)
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
let progressDiffByChain: dict<ChainState.progressDiff> = Dict.make()
|
|
132
155
|
let rollbackedProcessedEvents = ref(0.)
|
|
133
156
|
|
|
134
157
|
{
|
|
135
158
|
let rollbackProgressDiff = await (
|
|
136
159
|
state->IndexerState.persistence
|
|
137
|
-
).storage.getRollbackProgressDiff(~
|
|
160
|
+
).storage.getRollbackProgressDiff(~floors)
|
|
138
161
|
for idx in 0 to rollbackProgressDiff->Array.length - 1 {
|
|
139
162
|
let diff = rollbackProgressDiff->Array.getUnsafe(idx)
|
|
140
|
-
|
|
141
|
-
|
|
163
|
+
let chainId = diff["chain_id"]
|
|
164
|
+
let eventsProcessed = Float.fromString(diff["events_processed_diff"])->Option.getOrThrow
|
|
165
|
+
rollbackedProcessedEvents := rollbackedProcessedEvents.contents +. eventsProcessed
|
|
166
|
+
progressDiffByChain->ChainId.Dict.set(
|
|
167
|
+
chainId,
|
|
142
168
|
{
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
rollbackedProcessedEvents := rollbackedProcessedEvents.contents +. eventsProcessedDiff
|
|
146
|
-
eventsProcessedDiff
|
|
147
|
-
},
|
|
148
|
-
)
|
|
149
|
-
newProgressBlockNumberPerChain->ChainId.Dict.set(
|
|
150
|
-
diff["chain_id"],
|
|
151
|
-
if rollbackTargetCheckpointId === 0n && diff["chain_id"] === reorgChain {
|
|
152
|
-
Pervasives.min(diff["new_progress_block_number"], rollbackTargetBlockNumber)
|
|
153
|
-
} else {
|
|
154
|
-
diff["new_progress_block_number"]
|
|
169
|
+
blockNumber: diff["new_progress_block_number"],
|
|
170
|
+
eventsProcessed,
|
|
155
171
|
},
|
|
156
172
|
)
|
|
157
173
|
}
|
|
158
174
|
}
|
|
159
175
|
|
|
160
|
-
|
|
176
|
+
// Where the rollback leaves each chain it moved, written with the diff: the
|
|
177
|
+
// batch that carries it may belong to a chain the rollback never touched.
|
|
178
|
+
let rolledBackChains: array<InternalTable.Chains.progressedChain> = []
|
|
161
179
|
let rolledBackAddresses = []
|
|
162
180
|
state
|
|
163
181
|
->IndexerState.chainStates
|
|
164
182
|
->Utils.Dict.forEach(cs => {
|
|
165
183
|
let chainId = (cs->ChainState.chainConfig).id
|
|
166
184
|
let fromBlock = cs->ChainState.committedProgressBlockNumber
|
|
167
|
-
let
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
185
|
+
let progressDiff = progressDiffByChain->ChainId.Dict.dangerouslyGetNonOption(chainId)
|
|
186
|
+
let killedAddresses = cs->ChainState.rollback(
|
|
187
|
+
~rolledBackTo=switch (progressDiff, floors->RollbackFloors.forkBlockNumber(chainId)) {
|
|
188
|
+
| (Some(progressDiff), forkBlockNumber) =>
|
|
189
|
+
RecomputedProgress({
|
|
190
|
+
...progressDiff,
|
|
191
|
+
blockNumber: forkBlockNumber->Option.mapOr(progressDiff.blockNumber, forkBlockNumber =>
|
|
192
|
+
Pervasives.min(progressDiff.blockNumber, forkBlockNumber)
|
|
193
|
+
),
|
|
194
|
+
})
|
|
195
|
+
// Nothing of this chain's is above its floor, so there are no checkpoints
|
|
196
|
+
// to recompute from.
|
|
197
|
+
| (None, Some(forkBlockNumber)) => ForkBlock(forkBlockNumber)
|
|
198
|
+
| (None, None) => Untouched
|
|
199
|
+
},
|
|
200
|
+
)
|
|
178
201
|
rolledBackAddresses->Array.pushMany(killedAddresses)->ignore
|
|
179
202
|
let toBlock = cs->ChainState.committedProgressBlockNumber
|
|
180
203
|
if fromBlock !== toBlock {
|
|
181
204
|
rolledBackChains
|
|
182
205
|
->Array.push({
|
|
206
|
+
chainId,
|
|
207
|
+
progressBlockNumber: toBlock,
|
|
208
|
+
sourceBlockNumber: cs->ChainState.knownHeight,
|
|
209
|
+
totalEventsProcessed: cs->ChainState.numEventsProcessed,
|
|
210
|
+
})
|
|
211
|
+
->ignore
|
|
212
|
+
logger->Logging.childInfo({
|
|
213
|
+
"msg": "Rollbacked",
|
|
183
214
|
"chainId": chainId,
|
|
184
215
|
"fromBlock": fromBlock,
|
|
185
216
|
"toBlock": toBlock,
|
|
186
|
-
"rollbackedEvents":
|
|
187
|
-
->ChainId.Dict.dangerouslyGetNonOption(chainId)
|
|
188
|
-
->Option.getOr(0.),
|
|
217
|
+
"rollbackedEvents": progressDiff->Option.mapOr(0., diff => diff.eventsProcessed),
|
|
189
218
|
})
|
|
190
|
-
->ignore
|
|
191
219
|
}
|
|
192
220
|
})
|
|
193
221
|
|
|
194
222
|
let diff = await state->InMemoryStore.prepareRollbackDiff(
|
|
195
|
-
~
|
|
223
|
+
~floors,
|
|
196
224
|
~rollbackDiffCheckpointId=state->IndexerState.committedCheckpointId->BigInt.add(1n),
|
|
197
|
-
~
|
|
225
|
+
~progressedChains=rolledBackChains,
|
|
198
226
|
~rolledBackAddresses,
|
|
199
227
|
)
|
|
200
228
|
|
|
201
|
-
rolledBackChains->Array.forEach(rolledBack => {
|
|
202
|
-
logger->Logging.childInfo({
|
|
203
|
-
"msg": "Rollbacked",
|
|
204
|
-
"chainId": rolledBack["chainId"],
|
|
205
|
-
"fromBlock": rolledBack["fromBlock"],
|
|
206
|
-
"toBlock": rolledBack["toBlock"],
|
|
207
|
-
"rollbackedEvents": rolledBack["rollbackedEvents"],
|
|
208
|
-
})
|
|
209
|
-
})
|
|
210
229
|
logger->Logging.childTrace({
|
|
211
230
|
"msg": "Rollback entity changes",
|
|
212
231
|
"deleted": diff["deletedEntities"],
|
|
@@ -217,7 +236,11 @@ and executeRollback = async (
|
|
|
217
236
|
~rollbackedProcessedEvents=rollbackedProcessedEvents.contents,
|
|
218
237
|
)
|
|
219
238
|
|
|
220
|
-
state->IndexerState.completeRollback(
|
|
239
|
+
state->IndexerState.completeRollback(
|
|
240
|
+
~eventsProcessedDiffByChain=progressDiffByChain->Utils.Dict.mapValues(diff =>
|
|
241
|
+
diff.eventsProcessed
|
|
242
|
+
),
|
|
243
|
+
)
|
|
221
244
|
scheduleFetch()
|
|
222
245
|
scheduleProcessing()
|
|
223
246
|
}
|
package/src/Rollback.res.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as Utils from "./Utils.res.mjs";
|
|
4
|
+
import * as Config from "./Config.res.mjs";
|
|
4
5
|
import * as Logging from "./Logging.res.mjs";
|
|
5
6
|
import * as Writing from "./Writing.res.mjs";
|
|
6
7
|
import * as ChainState from "./ChainState.res.mjs";
|
|
@@ -12,6 +13,7 @@ import * as InMemoryStore from "./InMemoryStore.res.mjs";
|
|
|
12
13
|
import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
|
|
13
14
|
import * as SourceManager from "./sources/SourceManager.res.mjs";
|
|
14
15
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
16
|
+
import * as RollbackFloors from "./db/RollbackFloors.res.mjs";
|
|
15
17
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
16
18
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
17
19
|
|
|
@@ -42,50 +44,67 @@ async function executeRollback(state, reorgChain, rollbackTargetBlockNumber, sch
|
|
|
42
44
|
await Writing.flush(state);
|
|
43
45
|
let checkpointId = await IndexerState.persistence(state).storage.getRollbackTargetCheckpoint(reorgChain, rollbackTargetBlockNumber);
|
|
44
46
|
let rollbackTargetCheckpointId = checkpointId !== undefined ? checkpointId : 0n;
|
|
45
|
-
let
|
|
46
|
-
let
|
|
47
|
+
let next = Config.isIsolatedMultichain(IndexerState.config(state)) ? RollbackFloors.isolated(reorgChain, rollbackTargetCheckpointId, rollbackTargetBlockNumber) : RollbackFloors.global(rollbackTargetCheckpointId, reorgChain, rollbackTargetBlockNumber);
|
|
48
|
+
let match = IndexerState.pendingRollback(state);
|
|
49
|
+
let floors = match !== undefined ? RollbackFloors.merge(match.floors, next) : next;
|
|
50
|
+
let progressDiffByChain = {};
|
|
47
51
|
let rollbackedProcessedEvents = 0;
|
|
48
|
-
let rollbackProgressDiff = await IndexerState.persistence(state).storage.getRollbackProgressDiff(
|
|
52
|
+
let rollbackProgressDiff = await IndexerState.persistence(state).storage.getRollbackProgressDiff(floors);
|
|
49
53
|
for (let idx = 0, idx_finish = rollbackProgressDiff.length; idx < idx_finish; ++idx) {
|
|
50
54
|
let diff = rollbackProgressDiff[idx];
|
|
51
|
-
let
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
let chainId = diff.chain_id;
|
|
56
|
+
let eventsProcessed = Stdlib_Option.getOrThrow(Stdlib_Float.fromString(diff.events_processed_diff), undefined);
|
|
57
|
+
rollbackedProcessedEvents = rollbackedProcessedEvents + eventsProcessed;
|
|
58
|
+
progressDiffByChain[chainId] = {
|
|
59
|
+
blockNumber: diff.new_progress_block_number,
|
|
60
|
+
eventsProcessed: eventsProcessed
|
|
61
|
+
};
|
|
55
62
|
}
|
|
56
63
|
let rolledBackChains = [];
|
|
57
64
|
let rolledBackAddresses = [];
|
|
58
65
|
Utils.Dict.forEach(IndexerState.chainStates(state), cs => {
|
|
59
66
|
let chainId = ChainState.chainConfig(cs).id;
|
|
60
67
|
let fromBlock = ChainState.committedProgressBlockNumber(cs);
|
|
61
|
-
let
|
|
68
|
+
let progressDiff = progressDiffByChain[chainId];
|
|
69
|
+
let match = RollbackFloors.forkBlockNumber(floors, chainId);
|
|
70
|
+
let killedAddresses = ChainState.rollback(cs, progressDiff !== undefined ? ({
|
|
71
|
+
TAG: "RecomputedProgress",
|
|
72
|
+
_0: {
|
|
73
|
+
blockNumber: Stdlib_Option.mapOr(match, progressDiff.blockNumber, forkBlockNumber => Primitive_int.min(progressDiff.blockNumber, forkBlockNumber)),
|
|
74
|
+
eventsProcessed: progressDiff.eventsProcessed
|
|
75
|
+
}
|
|
76
|
+
}) : (
|
|
77
|
+
match !== undefined ? ({
|
|
78
|
+
TAG: "ForkBlock",
|
|
79
|
+
_0: match
|
|
80
|
+
}) : "Untouched"
|
|
81
|
+
));
|
|
62
82
|
rolledBackAddresses.push(...killedAddresses);
|
|
63
83
|
let toBlock = ChainState.committedProgressBlockNumber(cs);
|
|
64
84
|
if (fromBlock !== toBlock) {
|
|
65
85
|
rolledBackChains.push({
|
|
86
|
+
chainId: chainId,
|
|
87
|
+
progressBlockNumber: toBlock,
|
|
88
|
+
sourceBlockNumber: ChainState.knownHeight(cs),
|
|
89
|
+
totalEventsProcessed: ChainState.numEventsProcessed(cs)
|
|
90
|
+
});
|
|
91
|
+
return Logging.childInfo(logger, {
|
|
92
|
+
msg: "Rollbacked",
|
|
66
93
|
chainId: chainId,
|
|
67
94
|
fromBlock: fromBlock,
|
|
68
95
|
toBlock: toBlock,
|
|
69
|
-
rollbackedEvents: Stdlib_Option.
|
|
96
|
+
rollbackedEvents: Stdlib_Option.mapOr(progressDiff, 0, diff => diff.eventsProcessed)
|
|
70
97
|
});
|
|
71
|
-
return;
|
|
72
98
|
}
|
|
73
99
|
});
|
|
74
|
-
let diff$1 = await InMemoryStore.prepareRollbackDiff(state,
|
|
75
|
-
rolledBackChains.forEach(rolledBack => Logging.childInfo(logger, {
|
|
76
|
-
msg: "Rollbacked",
|
|
77
|
-
chainId: rolledBack.chainId,
|
|
78
|
-
fromBlock: rolledBack.fromBlock,
|
|
79
|
-
toBlock: rolledBack.toBlock,
|
|
80
|
-
rollbackedEvents: rolledBack.rollbackedEvents
|
|
81
|
-
}));
|
|
100
|
+
let diff$1 = await InMemoryStore.prepareRollbackDiff(state, floors, IndexerState.committedCheckpointId(state) + 1n, rolledBackChains, rolledBackAddresses);
|
|
82
101
|
Logging.childTrace(logger, {
|
|
83
102
|
msg: "Rollback entity changes",
|
|
84
103
|
deleted: diff$1.deletedEntities,
|
|
85
104
|
upserted: diff$1.setEntities
|
|
86
105
|
});
|
|
87
106
|
IndexerState.recordRollbackSuccess(state, Performance.secondsSince(startTime), rollbackedProcessedEvents);
|
|
88
|
-
IndexerState.completeRollback(state,
|
|
107
|
+
IndexerState.completeRollback(state, Utils.Dict.mapValues(progressDiffByChain, diff => diff.eventsProcessed));
|
|
89
108
|
scheduleFetch();
|
|
90
109
|
return scheduleProcessing();
|
|
91
110
|
}
|
package/src/RollbackCommit.res
CHANGED
|
@@ -20,15 +20,13 @@ let register = (callback: callback) => {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
// Fired after a rollback diff is durably written, once per affected chain
|
|
24
|
-
//
|
|
25
|
-
// the
|
|
26
|
-
|
|
27
|
-
let
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
->Array.flatMap(((chainIdKey, rollbackToBlock)) => {
|
|
31
|
-
let args = {chainId: chainIdKey->ChainId.normalizeOrThrow, rollbackToBlock}
|
|
23
|
+
// Fired after a rollback diff is durably written, once per affected chain, with
|
|
24
|
+
// the last valid block the rollback left that chain at. A throwing callback
|
|
25
|
+
// bubbles to the write loop's onError, crashing the indexer like a failed write.
|
|
26
|
+
let fire = async (~progressedChains: array<InternalTable.Chains.progressedChain>) => {
|
|
27
|
+
let _ = await progressedChains
|
|
28
|
+
->Array.flatMap(({chainId, progressBlockNumber}) => {
|
|
29
|
+
let args = {chainId, rollbackToBlock: progressBlockNumber}
|
|
32
30
|
callbacks->Array.map(callback => callback(args))
|
|
33
31
|
})
|
|
34
32
|
->Promise.all
|