envio 3.3.0-alpha.0 → 3.3.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -6
- package/src/Batch.res +0 -6
- package/src/Batch.res.mjs +0 -11
- package/src/BatchProcessing.res +2 -12
- package/src/BatchProcessing.res.mjs +2 -7
- package/src/ChainFetching.res +22 -13
- package/src/ChainFetching.res.mjs +13 -13
- package/src/ChainMetadata.res +1 -11
- package/src/ChainMetadata.res.mjs +1 -10
- package/src/ChainState.res +176 -2
- package/src/ChainState.res.mjs +153 -9
- package/src/ChainState.resi +53 -1
- package/src/Config.res +3 -6
- package/src/Config.res.mjs +2 -3
- package/src/Core.res +7 -0
- package/src/CrossChainState.res +99 -91
- package/src/CrossChainState.res.mjs +72 -57
- package/src/CrossChainState.resi +1 -9
- package/src/Ecosystem.res +6 -1
- package/src/Env.res +0 -7
- package/src/Env.res.mjs +0 -6
- package/src/Envio.res +3 -2
- package/src/EventConfigBuilder.res +19 -5
- package/src/EventConfigBuilder.res.mjs +6 -4
- package/src/EventProcessing.res +26 -0
- package/src/EventProcessing.res.mjs +20 -0
- package/src/FetchState.res +92 -22
- package/src/FetchState.res.mjs +87 -14
- package/src/IndexerLoop.res +2 -3
- package/src/IndexerLoop.res.mjs +1 -1
- package/src/IndexerState.res +0 -8
- package/src/IndexerState.res.mjs +4 -8
- package/src/IndexerState.resi +0 -4
- package/src/Internal.res +43 -4
- package/src/Internal.res.mjs +18 -0
- package/src/Main.res +3 -50
- package/src/Main.res.mjs +3 -24
- package/src/Prometheus.res +0 -11
- package/src/Prometheus.res.mjs +83 -98
- package/src/Rollback.res +1 -1
- package/src/Rollback.res.mjs +1 -1
- package/src/SimulateItems.res +3 -0
- package/src/SimulateItems.res.mjs +2 -1
- package/src/sources/Evm.res +52 -38
- package/src/sources/Evm.res.mjs +48 -36
- package/src/sources/Fuel.res +3 -1
- package/src/sources/Fuel.res.mjs +1 -0
- package/src/sources/HyperFuelSource.res +5 -0
- package/src/sources/HyperFuelSource.res.mjs +2 -0
- package/src/sources/HyperSync.res +4 -1
- package/src/sources/HyperSync.res.mjs +5 -3
- package/src/sources/HyperSync.resi +1 -0
- package/src/sources/HyperSyncClient.res +9 -78
- package/src/sources/HyperSyncClient.res.mjs +1 -22
- package/src/sources/HyperSyncSource.res +3 -4
- package/src/sources/HyperSyncSource.res.mjs +2 -1
- package/src/sources/RpcSource.res +7 -2
- package/src/sources/RpcSource.res.mjs +3 -1
- package/src/sources/SimulateSource.res +3 -1
- package/src/sources/SimulateSource.res.mjs +1 -0
- package/src/sources/Source.res +4 -0
- package/src/sources/SourceManager.res +9 -8
- package/src/sources/SourceManager.res.mjs +21 -26
- package/src/sources/SourceManager.resi +2 -3
- package/src/sources/Svm.res +25 -2
- package/src/sources/Svm.res.mjs +27 -2
- package/src/sources/SvmHyperSyncClient.res +3 -29
- package/src/sources/SvmHyperSyncSource.res +60 -84
- package/src/sources/SvmHyperSyncSource.res.mjs +59 -78
- package/src/sources/TransactionStore.res +111 -0
- package/src/sources/TransactionStore.res.mjs +77 -0
- package/src/tui/Tui.res +13 -16
- package/src/tui/Tui.res.mjs +12 -14
package/src/Config.res.mjs
CHANGED
|
@@ -111,9 +111,8 @@ let publicConfigChainSchema = S$RescriptSchema.schema(s => ({
|
|
|
111
111
|
let svmEventDescriptorSchema = S$RescriptSchema.schema(s => ({
|
|
112
112
|
discriminator: s.m(S$RescriptSchema.option(S$RescriptSchema.string)),
|
|
113
113
|
discriminatorByteLen: s.m(S$RescriptSchema.int),
|
|
114
|
-
|
|
114
|
+
transactionFields: s.m(S$RescriptSchema.array(Internal.svmTransactionFieldSchema)),
|
|
115
115
|
includeLogs: s.m(S$RescriptSchema.bool),
|
|
116
|
-
includeTokenBalances: s.m(S$RescriptSchema.bool),
|
|
117
116
|
accountFilters: s.m(S$RescriptSchema.option(S$RescriptSchema.array(S$RescriptSchema.schema(s => ({
|
|
118
117
|
position: s.m(S$RescriptSchema.int),
|
|
119
118
|
values: s.m(S$RescriptSchema.array(S$RescriptSchema.string))
|
|
@@ -518,7 +517,7 @@ function fromPublic(publicConfigJson) {
|
|
|
518
517
|
position: af.position,
|
|
519
518
|
values: af.values
|
|
520
519
|
})));
|
|
521
|
-
return EventConfigBuilder.buildSvmInstructionEventConfig(contractName, eventName, programId, svm.discriminator, svm.discriminatorByteLen, svm.
|
|
520
|
+
return EventConfigBuilder.buildSvmInstructionEventConfig(contractName, eventName, programId, svm.discriminator, svm.discriminatorByteLen, svm.includeLogs, svm.transactionFields, accountFilters, svm.isInner, false, undefined, undefined, Stdlib_Option.getOr(svm.accounts, []), Stdlib_Option.getOr(svm.args, null), svmDefinedTypes, startBlock);
|
|
522
521
|
}
|
|
523
522
|
});
|
|
524
523
|
} else {
|
package/src/Core.res
CHANGED
|
@@ -9,6 +9,7 @@ type evmRpcClientCtor
|
|
|
9
9
|
type evmDecoderCtor
|
|
10
10
|
type svmHypersyncClientCtor
|
|
11
11
|
type hyperfuelClientCtor
|
|
12
|
+
type transactionStoreCtor
|
|
12
13
|
|
|
13
14
|
type addon = {
|
|
14
15
|
getConfigJson: (~configPath: Null.t<string>, ~directory: Null.t<string>) => string,
|
|
@@ -23,6 +24,12 @@ type addon = {
|
|
|
23
24
|
svmHypersyncClient: svmHypersyncClientCtor,
|
|
24
25
|
@as("HyperfuelClient")
|
|
25
26
|
hyperfuelClient: hyperfuelClientCtor,
|
|
27
|
+
@as("TransactionStore")
|
|
28
|
+
transactionStore: transactionStoreCtor,
|
|
29
|
+
// Ordered transaction-field names exposed for the field-code contract test
|
|
30
|
+
// (the ReScript `transactionFields` arrays must match the Rust ordinals).
|
|
31
|
+
evmTransactionFieldNames: unit => array<string>,
|
|
32
|
+
svmTransactionFieldNames: unit => array<string>,
|
|
26
33
|
}
|
|
27
34
|
|
|
28
35
|
@module("node:module") external createRequire: string => {..} = "createRequire"
|
package/src/CrossChainState.res
CHANGED
|
@@ -11,10 +11,6 @@ type t = {
|
|
|
11
11
|
// True once every chain has caught up to head/endBlock. Monotonic during a run.
|
|
12
12
|
mutable isRealtime: bool,
|
|
13
13
|
mutable isInReorgThreshold: bool,
|
|
14
|
-
// Indexer-wide caps on concurrent data-source queries, shared across all
|
|
15
|
-
// chains. The realtime budget applies once every chain is at head.
|
|
16
|
-
maxBackfillConcurrency: int,
|
|
17
|
-
maxRealtimeConcurrency: int,
|
|
18
14
|
// Indexer-wide fetch buffer pool (item count), shared across all chains.
|
|
19
15
|
targetBufferSize: int,
|
|
20
16
|
}
|
|
@@ -26,18 +22,10 @@ let calculateTargetBufferSize = () =>
|
|
|
26
22
|
| None => 100_000
|
|
27
23
|
}
|
|
28
24
|
|
|
29
|
-
// The concurrency budget in force for the current phase.
|
|
30
|
-
let maxConcurrency = (crossChainState: t) =>
|
|
31
|
-
crossChainState.isRealtime
|
|
32
|
-
? crossChainState.maxRealtimeConcurrency
|
|
33
|
-
: crossChainState.maxBackfillConcurrency
|
|
34
|
-
|
|
35
25
|
let make = (
|
|
36
26
|
~chainStates,
|
|
37
27
|
~isInReorgThreshold,
|
|
38
28
|
~isRealtime,
|
|
39
|
-
~maxBackfillConcurrency=Env.maxBackfillConcurrency,
|
|
40
|
-
~maxRealtimeConcurrency=Env.maxRealtimeConcurrency,
|
|
41
29
|
~targetBufferSize=calculateTargetBufferSize(),
|
|
42
30
|
): t => {
|
|
43
31
|
let crossChainState = {
|
|
@@ -45,11 +33,8 @@ let make = (
|
|
|
45
33
|
chainIds: chainStates->Dict.valuesToArray->Array.map(cs => (cs->ChainState.chainConfig).id),
|
|
46
34
|
isRealtime,
|
|
47
35
|
isInReorgThreshold,
|
|
48
|
-
maxBackfillConcurrency,
|
|
49
|
-
maxRealtimeConcurrency,
|
|
50
36
|
targetBufferSize,
|
|
51
37
|
}
|
|
52
|
-
Prometheus.IndexingMaxConcurrency.set(~maxConcurrency=crossChainState->maxConcurrency)
|
|
53
38
|
Prometheus.IndexingTargetBufferSize.set(~targetBufferSize)
|
|
54
39
|
crossChainState
|
|
55
40
|
}
|
|
@@ -65,17 +50,6 @@ let chainStates = (crossChainState: t) => crossChainState.chainStates
|
|
|
65
50
|
let isRealtime = (crossChainState: t) => crossChainState.isRealtime
|
|
66
51
|
let isInReorgThreshold = (crossChainState: t) => crossChainState.isInReorgThreshold
|
|
67
52
|
|
|
68
|
-
// Partition queries in flight across every chain — the live draw against
|
|
69
|
-
// maxConcurrency.
|
|
70
|
-
let inFlight = (crossChainState: t) => {
|
|
71
|
-
let total = ref(0)
|
|
72
|
-
for i in 0 to crossChainState.chainIds->Array.length - 1 {
|
|
73
|
-
let cs = crossChainState->getChainState(crossChainState.chainIds->Array.getUnsafe(i))
|
|
74
|
-
total := total.contents + cs->ChainState.sourceManager->SourceManager.inFlightCount
|
|
75
|
-
}
|
|
76
|
-
total.contents
|
|
77
|
-
}
|
|
78
|
-
|
|
79
53
|
// Ready-to-process items across every chain — the live draw against
|
|
80
54
|
// targetBufferSize, which is a budget of processable events (items stuck behind
|
|
81
55
|
// a gap don't count toward the goal of keeping ~targetBufferSize ready).
|
|
@@ -83,7 +57,7 @@ let totalReadyCount = (crossChainState: t) => {
|
|
|
83
57
|
let total = ref(0)
|
|
84
58
|
for i in 0 to crossChainState.chainIds->Array.length - 1 {
|
|
85
59
|
let cs = crossChainState->getChainState(crossChainState.chainIds->Array.getUnsafe(i))
|
|
86
|
-
total := total.contents + cs->ChainState.
|
|
60
|
+
total := total.contents + cs->ChainState.bufferReadyCount
|
|
87
61
|
}
|
|
88
62
|
total.contents
|
|
89
63
|
}
|
|
@@ -91,9 +65,7 @@ let totalReadyCount = (crossChainState: t) => {
|
|
|
91
65
|
// --- Derived (pure). ---
|
|
92
66
|
|
|
93
67
|
let nextItemIsNone = (crossChainState: t): bool =>
|
|
94
|
-
!
|
|
95
|
-
crossChainState.chainStates->Dict.valuesToArray->Array.map(ChainState.fetchState),
|
|
96
|
-
)
|
|
68
|
+
!(crossChainState.chainStates->Dict.valuesToArray->Array.some(ChainState.hasReadyItem))
|
|
97
69
|
|
|
98
70
|
let getSafeCheckpointId = (crossChainState: t) => {
|
|
99
71
|
let result: ref<option<bigint>> = ref(None)
|
|
@@ -105,7 +77,7 @@ let getSafeCheckpointId = (crossChainState: t) => {
|
|
|
105
77
|
| Some(safeCheckpointTracking) =>
|
|
106
78
|
let safeCheckpointId =
|
|
107
79
|
safeCheckpointTracking->SafeCheckpointTracking.getSafeCheckpointId(
|
|
108
|
-
~sourceBlockNumber=
|
|
80
|
+
~sourceBlockNumber=cs->ChainState.knownHeight,
|
|
109
81
|
)
|
|
110
82
|
switch result.contents {
|
|
111
83
|
| None => result := Some(safeCheckpointId)
|
|
@@ -137,16 +109,9 @@ let createBatch = (
|
|
|
137
109
|
// in an append-only ClickHouse insert.
|
|
138
110
|
isRollback ? 1n : 0n,
|
|
139
111
|
),
|
|
140
|
-
~chainsBeforeBatch=crossChainState.chainStates->Utils.Dict.mapValues(
|
|
141
|
-
|
|
142
|
-
)
|
|
143
|
-
fetchState: cs->ChainState.fetchState,
|
|
144
|
-
progressBlockNumber: cs->ChainState.committedProgressBlockNumber,
|
|
145
|
-
totalEventsProcessed: cs->ChainState.numEventsProcessed,
|
|
146
|
-
sourceBlockNumber: (cs->ChainState.fetchState).knownHeight,
|
|
147
|
-
reorgDetection: cs->ChainState.reorgDetection,
|
|
148
|
-
chainConfig: cs->ChainState.chainConfig,
|
|
149
|
-
}),
|
|
112
|
+
~chainsBeforeBatch=crossChainState.chainStates->Utils.Dict.mapValues(
|
|
113
|
+
ChainState.toChainBeforeBatch,
|
|
114
|
+
),
|
|
150
115
|
~batchSizeTarget,
|
|
151
116
|
)
|
|
152
117
|
}
|
|
@@ -199,71 +164,114 @@ let applyBatchProgress = (crossChainState: t, ~batch: Batch.t) => {
|
|
|
199
164
|
Prometheus.ProgressReady.setAllReady()
|
|
200
165
|
}
|
|
201
166
|
|
|
202
|
-
let wasRealtime = crossChainState.isRealtime
|
|
203
167
|
crossChainState.isRealtime = crossChainState.isRealtime || allChainsReady.contents
|
|
204
|
-
if !wasRealtime && crossChainState.isRealtime {
|
|
205
|
-
// The realtime budget takes over now that every chain is at head.
|
|
206
|
-
Prometheus.IndexingMaxConcurrency.set(~maxConcurrency=crossChainState->maxConcurrency)
|
|
207
|
-
}
|
|
208
168
|
}
|
|
209
169
|
|
|
210
170
|
// --- Fetch control. ---
|
|
211
171
|
|
|
212
|
-
// Chains ordered furthest-behind first, so the shared
|
|
213
|
-
//
|
|
172
|
+
// Chains ordered furthest-behind first, so the shared buffer pool goes to the
|
|
173
|
+
// chains with the most backfill work before the rest.
|
|
214
174
|
let priorityOrder = (crossChainState: t) =>
|
|
215
175
|
crossChainState.chainStates
|
|
216
176
|
->Dict.valuesToArray
|
|
217
177
|
->Array.toSorted((a, b) =>
|
|
218
|
-
Float.compare(
|
|
219
|
-
a->ChainState.fetchState->FetchState.getProgressPercentage,
|
|
220
|
-
b->ChainState.fetchState->FetchState.getProgressPercentage,
|
|
221
|
-
)
|
|
178
|
+
Float.compare(a->ChainState.getProgressPercentage, b->ChainState.getProgressPercentage)
|
|
222
179
|
)
|
|
223
180
|
|
|
224
|
-
//
|
|
225
|
-
//
|
|
226
|
-
//
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
181
|
+
// In-flight estimated items across every chain — the live draw against
|
|
182
|
+
// targetBufferSize alongside totalReadyCount, so the pool isn't re-dispatched
|
|
183
|
+
// while queries are still being fetched.
|
|
184
|
+
let totalReservedSize = (crossChainState: t) => {
|
|
185
|
+
let total = ref(0.)
|
|
186
|
+
for i in 0 to crossChainState.chainIds->Array.length - 1 {
|
|
187
|
+
let cs = crossChainState->getChainState(crossChainState.chainIds->Array.getUnsafe(i))
|
|
188
|
+
total := total.contents +. cs->ChainState.pendingBudget
|
|
189
|
+
}
|
|
190
|
+
total.contents
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Furthest-behind first: order candidate queries by the chain progress % at
|
|
194
|
+
// their fromBlock, so the most behind ranges across all chains are admitted
|
|
195
|
+
// before the rest.
|
|
196
|
+
let compareByProgress = (a: FetchState.query, b: FetchState.query) =>
|
|
197
|
+
Float.compare(a.progress, b.progress)
|
|
198
|
+
|
|
199
|
+
// Dispatch a fetch tick across the whole indexer from one shared pool of
|
|
200
|
+
// ~targetBufferSize ready events. Every chain proposes its candidate queries
|
|
201
|
+
// (each carrying an estimated response size) against the full free budget; the
|
|
202
|
+
// candidates are then pooled, ordered by chain progress (furthest-behind first),
|
|
203
|
+
// and admitted until the budget is consumed. So the budget is split per query
|
|
204
|
+
// across chains rather than per chain — a chain that can only use a little
|
|
205
|
+
// leaves the rest for the others automatically.
|
|
233
206
|
let checkAndFetch = async (
|
|
234
207
|
crossChainState: t,
|
|
235
|
-
~
|
|
236
|
-
~chain: ChainMap.Chain.t,
|
|
237
|
-
~concurrencyLimit: int,
|
|
238
|
-
~bufferLimit: int,
|
|
239
|
-
) => promise<unit>,
|
|
208
|
+
~dispatchChain: (~chain: ChainMap.Chain.t, ~action: FetchState.nextQuery) => promise<unit>,
|
|
240
209
|
) => {
|
|
241
|
-
let
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
//
|
|
251
|
-
//
|
|
252
|
-
|
|
253
|
-
let
|
|
210
|
+
let remaining = Pervasives.max(
|
|
211
|
+
0,
|
|
212
|
+
crossChainState.targetBufferSize -
|
|
213
|
+
crossChainState->totalReadyCount -
|
|
214
|
+
crossChainState->totalReservedSize->Float.toInt,
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
let chainIds = crossChainState.chainIds
|
|
218
|
+
let actionByChain = Dict.make()
|
|
219
|
+
// Candidate queries from every chain. Each query carries its chain id and the
|
|
220
|
+
// chain progress % at its fromBlock (the admission sort key), set here so the
|
|
221
|
+
// pool can be ordered without a side tuple per query.
|
|
222
|
+
let candidates = []
|
|
223
|
+
for i in 0 to chainIds->Array.length - 1 {
|
|
224
|
+
let chainId = chainIds->Array.getUnsafe(i)
|
|
225
|
+
let cs = crossChainState->getChainState(chainId)
|
|
226
|
+
switch cs->ChainState.getNextQuery(~budget=remaining) {
|
|
227
|
+
| (WaitingForNewBlock | NothingToQuery) as action =>
|
|
228
|
+
actionByChain->Utils.Dict.setByInt(chainId, action)
|
|
229
|
+
| Ready(queries) =>
|
|
230
|
+
// Default to NothingToQuery; replaced below if any candidate is admitted.
|
|
231
|
+
actionByChain->Utils.Dict.setByInt(chainId, FetchState.NothingToQuery)
|
|
232
|
+
queries->Array.forEach(query => {
|
|
233
|
+
query.chainId = chainId
|
|
234
|
+
query.progress = cs->ChainState.getProgressPercentageAt(~blockNumber=query.fromBlock)
|
|
235
|
+
candidates->Array.push(query)
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
candidates->Array.sort(compareByProgress)
|
|
241
|
+
|
|
242
|
+
// Admit furthest-behind first until the budget runs out. The condition is
|
|
243
|
+
// checked before each query, so as long as there's any budget left we admit
|
|
244
|
+
// the next one even when its estimate alone exceeds the remainder — otherwise a
|
|
245
|
+
// chain whose only query is bigger than the budget would never make progress.
|
|
246
|
+
let admittedByChain = Dict.make()
|
|
247
|
+
let running = ref(0.)
|
|
248
|
+
let remainingF = remaining->Int.toFloat
|
|
249
|
+
let idx = ref(0)
|
|
250
|
+
while running.contents < remainingF && idx.contents < candidates->Array.length {
|
|
251
|
+
let query = candidates->Array.getUnsafe(idx.contents)
|
|
252
|
+
admittedByChain->Utils.Dict.push(query.chainId->Int.toString, query)
|
|
253
|
+
running := running.contents +. query.estResponseSize
|
|
254
|
+
idx := idx.contents + 1
|
|
255
|
+
}
|
|
256
|
+
admittedByChain->Dict.forEachWithKey((queries, chainId) => {
|
|
257
|
+
actionByChain->Dict.set(chainId, FetchState.Ready(queries))
|
|
258
|
+
// Mark the admitted queries in flight and reserve their size against the
|
|
259
|
+
// shared budget; released as each response lands in handleQueryResult.
|
|
260
|
+
crossChainState
|
|
261
|
+
->getChainState(chainId->Int.fromString->Option.getUnsafe)
|
|
262
|
+
->ChainState.startFetchingQueries(~queries)
|
|
263
|
+
})
|
|
264
|
+
|
|
254
265
|
let promises = []
|
|
255
|
-
for i in 0 to
|
|
256
|
-
let
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
(
|
|
263
|
-
|
|
264
|
-
let inFlightBefore = sourceManager->SourceManager.inFlightCount
|
|
265
|
-
promises->Array.push(fetchChain(~chain, ~concurrencyLimit, ~bufferLimit))
|
|
266
|
-
inFlight := inFlight.contents + (sourceManager->SourceManager.inFlightCount - inFlightBefore)
|
|
266
|
+
for i in 0 to chainIds->Array.length - 1 {
|
|
267
|
+
let chainId = chainIds->Array.getUnsafe(i)
|
|
268
|
+
switch actionByChain->Utils.Dict.dangerouslyGetByIntNonOption(chainId) {
|
|
269
|
+
| Some(NothingToQuery)
|
|
270
|
+
| None => ()
|
|
271
|
+
| Some(action) =>
|
|
272
|
+
let chain = ChainMap.Chain.makeUnsafe(~chainId)
|
|
273
|
+
promises->Array.push(dispatchChain(~chain, ~action))
|
|
274
|
+
}
|
|
267
275
|
}
|
|
268
276
|
let _ = await promises->Promise.all
|
|
269
277
|
}
|
|
@@ -6,10 +6,10 @@ import * as Utils from "./Utils.res.mjs";
|
|
|
6
6
|
import * as Logging from "./Logging.res.mjs";
|
|
7
7
|
import * as ChainMap from "./ChainMap.res.mjs";
|
|
8
8
|
import * as ChainState from "./ChainState.res.mjs";
|
|
9
|
-
import * as FetchState from "./FetchState.res.mjs";
|
|
10
9
|
import * as Prometheus from "./Prometheus.res.mjs";
|
|
10
|
+
import * as Stdlib_Int from "@rescript/runtime/lib/es6/Stdlib_Int.js";
|
|
11
|
+
import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
|
|
11
12
|
import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
|
|
12
|
-
import * as SourceManager from "./sources/SourceManager.res.mjs";
|
|
13
13
|
import * as Primitive_float from "@rescript/runtime/lib/es6/Primitive_float.js";
|
|
14
14
|
import * as SafeCheckpointTracking from "./SafeCheckpointTracking.res.mjs";
|
|
15
15
|
|
|
@@ -21,17 +21,7 @@ function calculateTargetBufferSize() {
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
function
|
|
25
|
-
if (crossChainState.isRealtime) {
|
|
26
|
-
return crossChainState.maxRealtimeConcurrency;
|
|
27
|
-
} else {
|
|
28
|
-
return crossChainState.maxBackfillConcurrency;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function make(chainStates, isInReorgThreshold, isRealtime, maxBackfillConcurrencyOpt, maxRealtimeConcurrencyOpt, targetBufferSizeOpt) {
|
|
33
|
-
let maxBackfillConcurrency = maxBackfillConcurrencyOpt !== undefined ? maxBackfillConcurrencyOpt : Env.maxBackfillConcurrency;
|
|
34
|
-
let maxRealtimeConcurrency = maxRealtimeConcurrencyOpt !== undefined ? maxRealtimeConcurrencyOpt : Env.maxRealtimeConcurrency;
|
|
24
|
+
function make(chainStates, isInReorgThreshold, isRealtime, targetBufferSizeOpt) {
|
|
35
25
|
let targetBufferSize = targetBufferSizeOpt !== undefined ? targetBufferSizeOpt : (
|
|
36
26
|
Env.targetBufferSize !== undefined ? Env.targetBufferSize : 100000
|
|
37
27
|
);
|
|
@@ -40,11 +30,8 @@ function make(chainStates, isInReorgThreshold, isRealtime, maxBackfillConcurrenc
|
|
|
40
30
|
chainIds: Object.values(chainStates).map(cs => ChainState.chainConfig(cs).id),
|
|
41
31
|
isRealtime: isRealtime,
|
|
42
32
|
isInReorgThreshold: isInReorgThreshold,
|
|
43
|
-
maxBackfillConcurrency: maxBackfillConcurrency,
|
|
44
|
-
maxRealtimeConcurrency: maxRealtimeConcurrency,
|
|
45
33
|
targetBufferSize: targetBufferSize
|
|
46
34
|
};
|
|
47
|
-
Prometheus.IndexingMaxConcurrency.set(maxConcurrency(crossChainState));
|
|
48
35
|
Prometheus.IndexingTargetBufferSize.set(targetBufferSize);
|
|
49
36
|
return crossChainState;
|
|
50
37
|
}
|
|
@@ -61,26 +48,17 @@ function isInReorgThreshold(crossChainState) {
|
|
|
61
48
|
return crossChainState.isInReorgThreshold;
|
|
62
49
|
}
|
|
63
50
|
|
|
64
|
-
function inFlight(crossChainState) {
|
|
65
|
-
let total = 0;
|
|
66
|
-
for (let i = 0, i_finish = crossChainState.chainIds.length; i < i_finish; ++i) {
|
|
67
|
-
let cs = crossChainState.chainStates[crossChainState.chainIds[i]];
|
|
68
|
-
total = total + SourceManager.inFlightCount(ChainState.sourceManager(cs)) | 0;
|
|
69
|
-
}
|
|
70
|
-
return total;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
51
|
function totalReadyCount(crossChainState) {
|
|
74
52
|
let total = 0;
|
|
75
53
|
for (let i = 0, i_finish = crossChainState.chainIds.length; i < i_finish; ++i) {
|
|
76
54
|
let cs = crossChainState.chainStates[crossChainState.chainIds[i]];
|
|
77
|
-
total = total +
|
|
55
|
+
total = total + ChainState.bufferReadyCount(cs) | 0;
|
|
78
56
|
}
|
|
79
57
|
return total;
|
|
80
58
|
}
|
|
81
59
|
|
|
82
60
|
function nextItemIsNone(crossChainState) {
|
|
83
|
-
return !
|
|
61
|
+
return !Object.values(crossChainState.chainStates).some(ChainState.hasReadyItem);
|
|
84
62
|
}
|
|
85
63
|
|
|
86
64
|
function getSafeCheckpointId(crossChainState) {
|
|
@@ -89,7 +67,7 @@ function getSafeCheckpointId(crossChainState) {
|
|
|
89
67
|
let cs = crossChainState.chainStates[crossChainState.chainIds[i]];
|
|
90
68
|
let safeCheckpointTracking = ChainState.safeCheckpointTracking(cs);
|
|
91
69
|
if (safeCheckpointTracking !== undefined) {
|
|
92
|
-
let safeCheckpointId = SafeCheckpointTracking.getSafeCheckpointId(safeCheckpointTracking, ChainState.
|
|
70
|
+
let safeCheckpointId = SafeCheckpointTracking.getSafeCheckpointId(safeCheckpointTracking, ChainState.knownHeight(cs));
|
|
93
71
|
let current = result;
|
|
94
72
|
if (!(current !== undefined && safeCheckpointId >= current)) {
|
|
95
73
|
result = safeCheckpointId;
|
|
@@ -105,14 +83,7 @@ function getSafeCheckpointId(crossChainState) {
|
|
|
105
83
|
function createBatch(crossChainState, processedCheckpointId, batchSizeTarget, isRollback) {
|
|
106
84
|
return Batch.make(processedCheckpointId + (
|
|
107
85
|
isRollback ? 1n : 0n
|
|
108
|
-
), Utils.Dict.mapValues(crossChainState.chainStates,
|
|
109
|
-
fetchState: ChainState.fetchState(cs),
|
|
110
|
-
reorgDetection: ChainState.reorgDetection(cs),
|
|
111
|
-
progressBlockNumber: ChainState.committedProgressBlockNumber(cs),
|
|
112
|
-
sourceBlockNumber: ChainState.fetchState(cs).knownHeight,
|
|
113
|
-
totalEventsProcessed: ChainState.numEventsProcessed(cs),
|
|
114
|
-
chainConfig: ChainState.chainConfig(cs)
|
|
115
|
-
})), batchSizeTarget, crossChainState.isInReorgThreshold);
|
|
86
|
+
), Utils.Dict.mapValues(crossChainState.chainStates, ChainState.toChainBeforeBatch), batchSizeTarget, crossChainState.isInReorgThreshold);
|
|
116
87
|
}
|
|
117
88
|
|
|
118
89
|
function enterReorgThreshold(crossChainState) {
|
|
@@ -148,32 +119,78 @@ function applyBatchProgress(crossChainState, batch) {
|
|
|
148
119
|
if (allChainsReady) {
|
|
149
120
|
Prometheus.ProgressReady.setAllReady();
|
|
150
121
|
}
|
|
151
|
-
let wasRealtime = crossChainState.isRealtime;
|
|
152
122
|
crossChainState.isRealtime = crossChainState.isRealtime || allChainsReady;
|
|
153
|
-
if (!wasRealtime && crossChainState.isRealtime) {
|
|
154
|
-
return Prometheus.IndexingMaxConcurrency.set(maxConcurrency(crossChainState));
|
|
155
|
-
}
|
|
156
123
|
}
|
|
157
124
|
|
|
158
125
|
function priorityOrder(crossChainState) {
|
|
159
|
-
return Object.values(crossChainState.chainStates).toSorted((a, b) => Primitive_float.compare(
|
|
126
|
+
return Object.values(crossChainState.chainStates).toSorted((a, b) => Primitive_float.compare(ChainState.getProgressPercentage(a), ChainState.getProgressPercentage(b)));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function totalReservedSize(crossChainState) {
|
|
130
|
+
let total = 0;
|
|
131
|
+
for (let i = 0, i_finish = crossChainState.chainIds.length; i < i_finish; ++i) {
|
|
132
|
+
let cs = crossChainState.chainStates[crossChainState.chainIds[i]];
|
|
133
|
+
total = total + ChainState.pendingBudget(cs);
|
|
134
|
+
}
|
|
135
|
+
return total;
|
|
160
136
|
}
|
|
161
137
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
138
|
+
function compareByProgress(a, b) {
|
|
139
|
+
return Primitive_float.compare(a.progress, b.progress);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function checkAndFetch(crossChainState, dispatchChain) {
|
|
143
|
+
let remaining = Primitive_int.max(0, (crossChainState.targetBufferSize - totalReadyCount(crossChainState) | 0) - (totalReservedSize(crossChainState) | 0) | 0);
|
|
144
|
+
let chainIds = crossChainState.chainIds;
|
|
145
|
+
let actionByChain = {};
|
|
146
|
+
let candidates = [];
|
|
147
|
+
for (let i = 0, i_finish = chainIds.length; i < i_finish; ++i) {
|
|
148
|
+
let chainId = chainIds[i];
|
|
149
|
+
let cs = crossChainState.chainStates[chainId];
|
|
150
|
+
let action = ChainState.getNextQuery(cs, remaining);
|
|
151
|
+
if (typeof action !== "object") {
|
|
152
|
+
actionByChain[chainId] = action;
|
|
153
|
+
} else {
|
|
154
|
+
actionByChain[chainId] = "NothingToQuery";
|
|
155
|
+
action._0.forEach(query => {
|
|
156
|
+
query.chainId = chainId;
|
|
157
|
+
query.progress = ChainState.getProgressPercentageAt(cs, query.fromBlock);
|
|
158
|
+
candidates.push(query);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
candidates.sort(compareByProgress);
|
|
163
|
+
let admittedByChain = {};
|
|
164
|
+
let running = 0;
|
|
165
|
+
let idx = 0;
|
|
166
|
+
while (running < remaining && idx < candidates.length) {
|
|
167
|
+
let query = candidates[idx];
|
|
168
|
+
Utils.Dict.push(admittedByChain, query.chainId.toString(), query);
|
|
169
|
+
running = running + query.estResponseSize;
|
|
170
|
+
idx = idx + 1 | 0;
|
|
171
|
+
};
|
|
172
|
+
Stdlib_Dict.forEachWithKey(admittedByChain, (queries, chainId) => {
|
|
173
|
+
actionByChain[chainId] = {
|
|
174
|
+
TAG: "Ready",
|
|
175
|
+
_0: queries
|
|
176
|
+
};
|
|
177
|
+
let chainId$1 = Stdlib_Int.fromString(chainId, undefined);
|
|
178
|
+
ChainState.startFetchingQueries(crossChainState.chainStates[chainId$1], queries);
|
|
179
|
+
});
|
|
167
180
|
let promises = [];
|
|
168
|
-
for (let i = 0, i_finish =
|
|
169
|
-
let
|
|
170
|
-
let
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
181
|
+
for (let i$1 = 0, i_finish$1 = chainIds.length; i$1 < i_finish$1; ++i$1) {
|
|
182
|
+
let chainId$1 = chainIds[i$1];
|
|
183
|
+
let action$1 = actionByChain[chainId$1];
|
|
184
|
+
if (action$1 !== undefined) {
|
|
185
|
+
let exit = 0;
|
|
186
|
+
if (typeof action$1 === "object" || action$1 === "WaitingForNewBlock") {
|
|
187
|
+
exit = 1;
|
|
188
|
+
}
|
|
189
|
+
if (exit === 1) {
|
|
190
|
+
let chain = ChainMap.Chain.makeUnsafe(chainId$1);
|
|
191
|
+
promises.push(dispatchChain(chain, action$1));
|
|
192
|
+
}
|
|
193
|
+
}
|
|
177
194
|
}
|
|
178
195
|
await Promise.all(promises);
|
|
179
196
|
}
|
|
@@ -184,8 +201,6 @@ export {
|
|
|
184
201
|
chainStates,
|
|
185
202
|
isRealtime,
|
|
186
203
|
isInReorgThreshold,
|
|
187
|
-
maxConcurrency,
|
|
188
|
-
inFlight,
|
|
189
204
|
nextItemIsNone,
|
|
190
205
|
getSafeCheckpointId,
|
|
191
206
|
createBatch,
|
package/src/CrossChainState.resi
CHANGED
|
@@ -9,8 +9,6 @@ let make: (
|
|
|
9
9
|
~chainStates: dict<ChainState.t>,
|
|
10
10
|
~isInReorgThreshold: bool,
|
|
11
11
|
~isRealtime: bool,
|
|
12
|
-
~maxBackfillConcurrency: int=?,
|
|
13
|
-
~maxRealtimeConcurrency: int=?,
|
|
14
12
|
~targetBufferSize: int=?,
|
|
15
13
|
) => t
|
|
16
14
|
|
|
@@ -18,8 +16,6 @@ let make: (
|
|
|
18
16
|
let chainStates: t => dict<ChainState.t>
|
|
19
17
|
let isRealtime: t => bool
|
|
20
18
|
let isInReorgThreshold: t => bool
|
|
21
|
-
let maxConcurrency: t => int
|
|
22
|
-
let inFlight: t => int
|
|
23
19
|
|
|
24
20
|
// Derived (pure).
|
|
25
21
|
let nextItemIsNone: t => bool
|
|
@@ -39,9 +35,5 @@ let applyBatchProgress: (t, ~batch: Batch.t) => unit
|
|
|
39
35
|
let priorityOrder: t => array<ChainState.t>
|
|
40
36
|
let checkAndFetch: (
|
|
41
37
|
t,
|
|
42
|
-
~
|
|
43
|
-
~chain: ChainMap.Chain.t,
|
|
44
|
-
~concurrencyLimit: int,
|
|
45
|
-
~bufferLimit: int,
|
|
46
|
-
) => promise<unit>,
|
|
38
|
+
~dispatchChain: (~chain: ChainMap.Chain.t, ~action: FetchState.nextQuery) => promise<unit>,
|
|
47
39
|
) => promise<unit>
|
package/src/Ecosystem.res
CHANGED
|
@@ -33,8 +33,13 @@ type t = {
|
|
|
33
33
|
loggers (see `getItemLogger`). */
|
|
34
34
|
logger: Pino.t,
|
|
35
35
|
/** Materialise the user-facing event handed to handlers and contract
|
|
36
|
-
registration from an item's opaque payload.
|
|
36
|
+
registration from an item's opaque payload. `event.transaction` is written
|
|
37
|
+
onto the payload at batch prep (HyperSync) or inline (RPC/simulate). */
|
|
37
38
|
toEvent: Internal.eventItem => Internal.event,
|
|
39
|
+
/** Bitmask (as a float) of the transaction fields selected across the chain's
|
|
40
|
+
events — the set the store materialises at batch prep. `0.` when the
|
|
41
|
+
ecosystem carries transactions inline. */
|
|
42
|
+
transactionFieldMask: array<Internal.eventConfig> => float,
|
|
38
43
|
/** Build the per-item child logger for an event item, with
|
|
39
44
|
ecosystem-specific log fields (EVM/Fuel: contract/event/address; SVM:
|
|
40
45
|
program/instruction/programId). Closes over the injected logger. */
|
package/src/Env.res
CHANGED
|
@@ -8,13 +8,6 @@ let updateSyncTimeOnRestart =
|
|
|
8
8
|
envSafe->EnvSafe.get("UPDATE_SYNC_TIME_ON_RESTART", S.bool, ~fallback=true)
|
|
9
9
|
let targetBufferSize = envSafe->EnvSafe.get("ENVIO_INDEXING_MAX_BUFFER_SIZE", S.option(S.int))
|
|
10
10
|
let maxAddrInPartition = envSafe->EnvSafe.get("MAX_PARTITION_SIZE", S.int, ~fallback=5_000)
|
|
11
|
-
// Global caps on concurrent data-source queries across the whole indexer (all
|
|
12
|
-
// chains share the budget), not per chain. Realtime allows far more since head
|
|
13
|
-
// queries are small and many chains follow the head at once.
|
|
14
|
-
let maxBackfillConcurrency =
|
|
15
|
-
envSafe->EnvSafe.get("ENVIO_MAX_BACKFILL_CONCURRENCY", S.int, ~fallback=30)
|
|
16
|
-
let maxRealtimeConcurrency =
|
|
17
|
-
envSafe->EnvSafe.get("ENVIO_MAX_REALTIME_CONCURRENCY", S.int, ~fallback=200)
|
|
18
11
|
|
|
19
12
|
// Target number of in-memory objects (uncommitted entity/effect changes plus
|
|
20
13
|
// unwritten batch items) the store holds before processing waits for the write
|
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/Envio.res
CHANGED
|
@@ -48,6 +48,7 @@ type svmTokenBalance = {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
type svmTransaction = {
|
|
51
|
+
transactionIndex?: int,
|
|
51
52
|
signatures: array<string>,
|
|
52
53
|
feePayer?: SvmTypes.Pubkey.t,
|
|
53
54
|
success?: bool,
|
|
@@ -104,8 +105,8 @@ type svmInstruction = {
|
|
|
104
105
|
d8?: string,
|
|
105
106
|
/** Borsh-decoded params view. See [[svmInstructionParams]]. */
|
|
106
107
|
params?: svmInstructionParams,
|
|
107
|
-
/** Parent transaction.
|
|
108
|
-
`
|
|
108
|
+
/** Parent transaction. Carries only the fields selected via
|
|
109
|
+
`field_selection.transaction_fields`; absent when none are selected. */
|
|
109
110
|
transaction?: svmTransaction,
|
|
110
111
|
/** Program log entries scoped to this instruction. Absent when the
|
|
111
112
|
per-instruction `include_logs` flag is `false`. */
|