envio 3.3.0-alpha.11 → 3.3.0-alpha.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -6
- package/src/ChainFetching.res +21 -12
- package/src/ChainFetching.res.mjs +20 -9
- package/src/CrossChainState.res +48 -30
- package/src/CrossChainState.res.mjs +15 -7
- package/src/FetchState.res +100 -75
- package/src/FetchState.res.mjs +108 -103
- package/src/Rollback.res +32 -13
- package/src/Rollback.res.mjs +24 -11
- package/src/sources/SvmHyperSyncSource.res +10 -0
- package/src/sources/SvmHyperSyncSource.res.mjs +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "envio",
|
|
3
|
-
"version": "3.3.0-alpha.
|
|
3
|
+
"version": "3.3.0-alpha.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
|
|
6
6
|
"bin": "./bin.mjs",
|
|
@@ -70,10 +70,10 @@
|
|
|
70
70
|
"tsx": "4.21.0"
|
|
71
71
|
},
|
|
72
72
|
"optionalDependencies": {
|
|
73
|
-
"envio-linux-x64": "3.3.0-alpha.
|
|
74
|
-
"envio-linux-x64-musl": "3.3.0-alpha.
|
|
75
|
-
"envio-linux-arm64": "3.3.0-alpha.
|
|
76
|
-
"envio-darwin-x64": "3.3.0-alpha.
|
|
77
|
-
"envio-darwin-arm64": "3.3.0-alpha.
|
|
73
|
+
"envio-linux-x64": "3.3.0-alpha.12",
|
|
74
|
+
"envio-linux-x64-musl": "3.3.0-alpha.12",
|
|
75
|
+
"envio-linux-arm64": "3.3.0-alpha.12",
|
|
76
|
+
"envio-darwin-x64": "3.3.0-alpha.12",
|
|
77
|
+
"envio-darwin-arm64": "3.3.0-alpha.12"
|
|
78
78
|
}
|
|
79
79
|
}
|
package/src/ChainFetching.res
CHANGED
|
@@ -149,19 +149,28 @@ let rec onQueryResponse = async (
|
|
|
149
149
|
|
|
150
150
|
let numContractRegisterEvents = parsedQueueItems->Array.reduce(0, (count, item) => {
|
|
151
151
|
let eventItem = item->Internal.castUnsafeEventItem
|
|
152
|
-
eventItem.onEventRegistration.contractRegister !== None
|
|
153
|
-
? count + 1
|
|
154
|
-
: count
|
|
155
|
-
})
|
|
156
|
-
Logging.trace({
|
|
157
|
-
"msg": "Finished querying",
|
|
158
|
-
"chainId": chain->ChainMap.Chain.toChainId,
|
|
159
|
-
"partitionId": query.partitionId,
|
|
160
|
-
"fromBlock": fromBlockQueried,
|
|
161
|
-
"toBlock": latestFetchedBlockNumber,
|
|
162
|
-
"numEvents": parsedQueueItems->Array.length,
|
|
163
|
-
"numContractRegisterEvents": numContractRegisterEvents,
|
|
152
|
+
eventItem.onEventRegistration.contractRegister !== None ? count + 1 : count
|
|
164
153
|
})
|
|
154
|
+
if numContractRegisterEvents === 0 {
|
|
155
|
+
Logging.trace({
|
|
156
|
+
"msg": "Finished querying",
|
|
157
|
+
"chainId": chain->ChainMap.Chain.toChainId,
|
|
158
|
+
"partitionId": query.partitionId,
|
|
159
|
+
"fromBlock": fromBlockQueried,
|
|
160
|
+
"toBlock": latestFetchedBlockNumber,
|
|
161
|
+
"numEvents": parsedQueueItems->Array.length,
|
|
162
|
+
})
|
|
163
|
+
} else {
|
|
164
|
+
Logging.trace({
|
|
165
|
+
"msg": "Finished querying",
|
|
166
|
+
"chainId": chain->ChainMap.Chain.toChainId,
|
|
167
|
+
"partitionId": query.partitionId,
|
|
168
|
+
"fromBlock": fromBlockQueried,
|
|
169
|
+
"toBlock": latestFetchedBlockNumber,
|
|
170
|
+
"numEvents": parsedQueueItems->Array.length,
|
|
171
|
+
"numContractRegisterEvents": numContractRegisterEvents,
|
|
172
|
+
})
|
|
173
|
+
}
|
|
165
174
|
|
|
166
175
|
let reorgResult = chainState->ChainState.registerReorgGuard(~blockHashes, ~knownHeight)
|
|
167
176
|
|
|
@@ -111,15 +111,26 @@ async function onQueryResponse(state, param, stateId, scheduleFetch, schedulePro
|
|
|
111
111
|
return count;
|
|
112
112
|
}
|
|
113
113
|
});
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
114
|
+
if (numContractRegisterEvents === 0) {
|
|
115
|
+
Logging.trace({
|
|
116
|
+
msg: "Finished querying",
|
|
117
|
+
chainId: chain,
|
|
118
|
+
partitionId: query.partitionId,
|
|
119
|
+
fromBlock: fromBlockQueried,
|
|
120
|
+
toBlock: latestFetchedBlockNumber,
|
|
121
|
+
numEvents: parsedQueueItems.length
|
|
122
|
+
});
|
|
123
|
+
} else {
|
|
124
|
+
Logging.trace({
|
|
125
|
+
msg: "Finished querying",
|
|
126
|
+
chainId: chain,
|
|
127
|
+
partitionId: query.partitionId,
|
|
128
|
+
fromBlock: fromBlockQueried,
|
|
129
|
+
toBlock: latestFetchedBlockNumber,
|
|
130
|
+
numEvents: parsedQueueItems.length,
|
|
131
|
+
numContractRegisterEvents: numContractRegisterEvents
|
|
132
|
+
});
|
|
133
|
+
}
|
|
123
134
|
let reorgResult = ChainState.registerReorgGuard(chainState, response.blockHashes, knownHeight);
|
|
124
135
|
let rollbackWithReorgDetectedBlockNumber;
|
|
125
136
|
if (typeof reorgResult !== "object") {
|
package/src/CrossChainState.res
CHANGED
|
@@ -170,7 +170,7 @@ let applyBatchProgress = (crossChainState: t, ~batch: Batch.t, ~blockTimestampNa
|
|
|
170
170
|
// --- Fetch control. ---
|
|
171
171
|
|
|
172
172
|
// Chains ordered furthest-behind first, so the shared buffer pool goes to the
|
|
173
|
-
// chains with the most backfill work before the rest.
|
|
173
|
+
// chains with the most fetchable backfill work before the rest.
|
|
174
174
|
let priorityOrder = (crossChainState: t) =>
|
|
175
175
|
crossChainState.chainStates
|
|
176
176
|
->Dict.valuesToArray
|
|
@@ -198,29 +198,37 @@ let totalReservedSize = (crossChainState: t) => {
|
|
|
198
198
|
// chain-density-derived target block, then subtract what it actually used
|
|
199
199
|
// before moving to the next chain. So a chain that can only use a little
|
|
200
200
|
// (density too low, or already caught up) leaves the rest for the others
|
|
201
|
-
// automatically.
|
|
202
|
-
//
|
|
203
|
-
//
|
|
204
|
-
//
|
|
205
|
-
//
|
|
206
|
-
//
|
|
201
|
+
// automatically. Starting a new query requires at least 10% of the target pool
|
|
202
|
+
// to be free. A chain visited after the budget falls below that admission unit
|
|
203
|
+
// doesn't query this round — reservations release as responses land, so the
|
|
204
|
+
// next tick redistributes. Chains that do get budget are additionally capped
|
|
205
|
+
// at the first querying chain's target progress mapped onto their own range, so
|
|
206
|
+
// no chain runs further ahead than the chain currently using the pool. Waiting
|
|
207
|
+
// and idle chains do not establish this alignment line.
|
|
207
208
|
let checkAndFetch = async (
|
|
208
209
|
crossChainState: t,
|
|
209
210
|
~dispatchChain: (~chain: ChainMap.Chain.t, ~action: FetchState.nextQuery) => promise<unit>,
|
|
210
211
|
) => {
|
|
212
|
+
let targetBudget = crossChainState.targetBufferSize->Int.toFloat
|
|
211
213
|
let remaining = ref(
|
|
212
214
|
Pervasives.max(
|
|
213
215
|
0.,
|
|
214
|
-
|
|
216
|
+
targetBudget -.
|
|
215
217
|
crossChainState->totalReadyCount->Int.toFloat -.
|
|
216
218
|
crossChainState->totalReservedSize,
|
|
217
219
|
),
|
|
218
220
|
)
|
|
219
221
|
|
|
222
|
+
// New fetch work is admitted in units of 10% of the target pool. Waiting
|
|
223
|
+
// below this floor avoids spending the last few free items on undersized
|
|
224
|
+
// queries; response and batch completion schedule another tick after they
|
|
225
|
+
// release enough budget.
|
|
226
|
+
let minimumAdmissionBudget = targetBudget *. 0.1
|
|
227
|
+
|
|
220
228
|
// A chain with no density signal probes blind, so it only gets a bounded
|
|
221
229
|
// slice of the pool — one unknown chain shouldn't hold the whole budget
|
|
222
|
-
// while it takes its first measurements.
|
|
223
|
-
let coldChainBudget =
|
|
230
|
+
// while it takes its first measurements. Its probe is one admission unit.
|
|
231
|
+
let coldChainBudget = minimumAdmissionBudget
|
|
224
232
|
|
|
225
233
|
// Chunk reservations get headroom over the density estimate so a
|
|
226
234
|
// denser-than-expected range doesn't truncate at the server cap; realtime
|
|
@@ -233,30 +241,33 @@ let checkAndFetch = async (
|
|
|
233
241
|
->priorityOrder
|
|
234
242
|
->Array.forEach(cs => {
|
|
235
243
|
let chainId = (cs->ChainState.chainConfig).id
|
|
236
|
-
if
|
|
237
|
-
//
|
|
238
|
-
//
|
|
239
|
-
|
|
240
|
-
|
|
244
|
+
if remaining.contents < minimumAdmissionBudget {
|
|
245
|
+
// More than 90% of the target pool is ready or reserved. Do not attempt
|
|
246
|
+
// any chain action until a full admission unit becomes free.
|
|
247
|
+
actionByChain->Utils.Dict.setByInt(chainId, FetchState.NothingToQuery)
|
|
248
|
+
} else if cs->ChainState.knownHeight == 0 {
|
|
249
|
+
// Let getNextQuery own the waiting decision without trying to size work
|
|
250
|
+
// against an unknown height.
|
|
251
|
+
actionByChain->Utils.Dict.setByInt(
|
|
252
|
+
chainId,
|
|
253
|
+
cs->ChainState.getNextQuery(~chainTargetItems=0., ~chunkItemsMultiplier),
|
|
254
|
+
)
|
|
241
255
|
} else {
|
|
242
256
|
let isCold = cs->ChainState.effectiveDensity === None
|
|
243
257
|
let chainTargetItems =
|
|
244
258
|
(isCold ? Pervasives.min(remaining.contents, coldChainBudget) : remaining.contents) +.
|
|
245
259
|
cs->ChainState.pendingBudget
|
|
260
|
+
let candidateProgress = switch maxProgress.contents {
|
|
261
|
+
| None if !isCold =>
|
|
262
|
+
Some(
|
|
263
|
+
cs->ChainState.progressAtBlock(
|
|
264
|
+
~blockNumber=cs->ChainState.targetBlock(~chainTargetItems),
|
|
265
|
+
),
|
|
266
|
+
)
|
|
267
|
+
| _ => None
|
|
268
|
+
}
|
|
246
269
|
let maxTargetBlock = switch maxProgress.contents {
|
|
247
|
-
| None
|
|
248
|
-
// A cold chain's target is a guess — it doesn't set the alignment
|
|
249
|
-
// line; the first density-bearing chain after it does.
|
|
250
|
-
None
|
|
251
|
-
| None =>
|
|
252
|
-
// The most-behind chain sets the alignment line for everyone after it.
|
|
253
|
-
maxProgress :=
|
|
254
|
-
Some(
|
|
255
|
-
cs->ChainState.progressAtBlock(
|
|
256
|
-
~blockNumber=cs->ChainState.targetBlock(~chainTargetItems),
|
|
257
|
-
),
|
|
258
|
-
)
|
|
259
|
-
None
|
|
270
|
+
| None => None
|
|
260
271
|
// 5% margin past the leader's line: chains whose progress tracks the
|
|
261
272
|
// leader closely would otherwise flap in and out of the clamp as
|
|
262
273
|
// leadership alternates, stalling their pipeline every other tick.
|
|
@@ -274,8 +285,7 @@ let checkAndFetch = async (
|
|
|
274
285
|
// range to nothing — and has nothing else to wake it must keep polling
|
|
275
286
|
// for new blocks instead of going silent. NothingToQuery isn't
|
|
276
287
|
// dispatched, so such a chain would never be re-scheduled and its head
|
|
277
|
-
// tracking would freeze
|
|
278
|
-
// for the same reason). A chain is genuinely idle — and correctly left
|
|
288
|
+
// tracking would freeze. A chain is genuinely idle — and correctly left
|
|
279
289
|
// undispatched — when it is caught up to its head/endblock, still
|
|
280
290
|
// draining in-flight queries, or holding ready items that batch
|
|
281
291
|
// processing will drain and re-schedule from.
|
|
@@ -287,6 +297,14 @@ let checkAndFetch = async (
|
|
|
287
297
|
: FetchState.WaitingForNewBlock
|
|
288
298
|
actionByChain->Utils.Dict.setByInt(chainId, action)
|
|
289
299
|
| Ready(queries) => {
|
|
300
|
+
// A density-bearing chain establishes the alignment line only after
|
|
301
|
+
// proving that it can use the pool. Waiting and idle chains leave it
|
|
302
|
+
// open for the next chain; cold-chain targets remain guesses and do
|
|
303
|
+
// not lead, as before.
|
|
304
|
+
switch candidateProgress {
|
|
305
|
+
| Some(progress) => maxProgress := Some(progress)
|
|
306
|
+
| None => ()
|
|
307
|
+
}
|
|
290
308
|
let consumed =
|
|
291
309
|
queries->Array.reduce(0., (acc, query: FetchState.query) =>
|
|
292
310
|
acc +. query.itemsEst->Int.toFloat
|
|
@@ -134,10 +134,11 @@ function totalReservedSize(crossChainState) {
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
async function checkAndFetch(crossChainState, dispatchChain) {
|
|
137
|
+
let targetBudget = crossChainState.targetBufferSize;
|
|
137
138
|
let remaining = {
|
|
138
|
-
contents: Primitive_float.max(0,
|
|
139
|
+
contents: Primitive_float.max(0, targetBudget - totalReadyCount(crossChainState) - totalReservedSize(crossChainState))
|
|
139
140
|
};
|
|
140
|
-
let
|
|
141
|
+
let minimumAdmissionBudget = targetBudget * 0.1;
|
|
141
142
|
let chunkItemsMultiplier = crossChainState.isRealtime ? 3 : 1.5;
|
|
142
143
|
let actionByChain = {};
|
|
143
144
|
let maxProgress = {
|
|
@@ -145,18 +146,22 @@ async function checkAndFetch(crossChainState, dispatchChain) {
|
|
|
145
146
|
};
|
|
146
147
|
priorityOrder(crossChainState).forEach(cs => {
|
|
147
148
|
let chainId = ChainState.chainConfig(cs).id;
|
|
149
|
+
if (remaining.contents < minimumAdmissionBudget) {
|
|
150
|
+
actionByChain[chainId] = "NothingToQuery";
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
148
153
|
if (ChainState.knownHeight(cs) === 0) {
|
|
149
|
-
actionByChain[chainId] =
|
|
154
|
+
actionByChain[chainId] = ChainState.getNextQuery(cs, 0, chunkItemsMultiplier, undefined);
|
|
150
155
|
return;
|
|
151
156
|
}
|
|
152
157
|
let isCold = ChainState.effectiveDensity(cs) === undefined;
|
|
153
158
|
let chainTargetItems = (
|
|
154
|
-
isCold ? Primitive_float.min(remaining.contents,
|
|
159
|
+
isCold ? Primitive_float.min(remaining.contents, minimumAdmissionBudget) : remaining.contents
|
|
155
160
|
) + ChainState.pendingBudget(cs);
|
|
161
|
+
let match = maxProgress.contents;
|
|
162
|
+
let candidateProgress = match !== undefined || isCold ? undefined : ChainState.progressAtBlock(cs, ChainState.targetBlock(cs, chainTargetItems));
|
|
156
163
|
let progress = maxProgress.contents;
|
|
157
|
-
let maxTargetBlock = progress !== undefined ? ChainState.blockAtProgress(cs, progress + 0.05) :
|
|
158
|
-
isCold ? undefined : (maxProgress.contents = ChainState.progressAtBlock(cs, ChainState.targetBlock(cs, chainTargetItems)), undefined)
|
|
159
|
-
);
|
|
164
|
+
let maxTargetBlock = progress !== undefined ? ChainState.blockAtProgress(cs, progress + 0.05) : undefined;
|
|
160
165
|
let action = ChainState.getNextQuery(cs, chainTargetItems, chunkItemsMultiplier, maxTargetBlock);
|
|
161
166
|
if (typeof action !== "object") {
|
|
162
167
|
if (action === "WaitingForNewBlock") {
|
|
@@ -168,6 +173,9 @@ async function checkAndFetch(crossChainState, dispatchChain) {
|
|
|
168
173
|
return;
|
|
169
174
|
} else {
|
|
170
175
|
let queries = action._0;
|
|
176
|
+
if (candidateProgress !== undefined) {
|
|
177
|
+
maxProgress.contents = candidateProgress;
|
|
178
|
+
}
|
|
171
179
|
let consumed = Stdlib_Array.reduce(queries, 0, (acc, query) => acc + query.itemsEst);
|
|
172
180
|
let partitions = {};
|
|
173
181
|
queries.forEach(query => {
|
package/src/FetchState.res
CHANGED
|
@@ -47,17 +47,19 @@ type partition = {
|
|
|
47
47
|
dynamicContract: option<string>,
|
|
48
48
|
// Mutable array for SourceManager sync - queries exist only while being fetched
|
|
49
49
|
mutPendingQueries: array<pendingQuery>,
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
|
|
50
|
+
// The last two ranges that measured how many blocks the source could return.
|
|
51
|
+
// Both must be non-zero before the minimum is trusted for chunk sizing.
|
|
52
|
+
sourceRangeCapacity: int,
|
|
53
|
+
prevSourceRangeCapacity: int,
|
|
54
|
+
// Smoothed items/block observed in responses. This is independent from the
|
|
55
|
+
// source's range capacity: even a response truncated by our own itemsTarget
|
|
56
|
+
// cap is useful density evidence while saying nothing about source capacity.
|
|
57
|
+
// None distinguishes a new partition from a real zero-density observation.
|
|
58
|
+
eventDensity: option<float>,
|
|
57
59
|
// Tracks the latestFetchedBlock.blockNumber of the most recent response
|
|
58
|
-
// that updated
|
|
59
|
-
// when parallel query responses arrive out of order.
|
|
60
|
-
|
|
60
|
+
// that updated sourceRangeCapacity. Prevents degradation of the chunking
|
|
61
|
+
// heuristic when parallel query responses arrive out of order.
|
|
62
|
+
latestSourceRangeCapacityUpdateBlock: int,
|
|
61
63
|
}
|
|
62
64
|
|
|
63
65
|
type query = {
|
|
@@ -111,22 +113,17 @@ let densityItemsTarget = (~density, ~fromBlock, ~toBlock, ~chainTargetBlock) =>
|
|
|
111
113
|
)
|
|
112
114
|
}
|
|
113
115
|
|
|
114
|
-
// Calculate the chunk range from
|
|
116
|
+
// Calculate the chunk range from the last two source-capacity observations.
|
|
115
117
|
let getMinHistoryRange = (p: partition) => {
|
|
116
|
-
switch (p.
|
|
118
|
+
switch (p.sourceRangeCapacity, p.prevSourceRangeCapacity) {
|
|
117
119
|
| (0, _) | (_, 0) => None
|
|
118
120
|
| (a, b) => Some(a < b ? a : b)
|
|
119
121
|
}
|
|
120
122
|
}
|
|
121
123
|
|
|
122
|
-
// Density
|
|
123
|
-
//
|
|
124
|
-
let getTrustedDensity = (p: partition) =>
|
|
125
|
-
switch (p.prevQueryRange, p.prevPrevQueryRange) {
|
|
126
|
-
| (0, _) | (_, 0) => None
|
|
127
|
-
| (prevQueryRange, _) => Some(p.prevRangeSize->Int.toFloat /. prevQueryRange->Int.toFloat)
|
|
128
|
-
}
|
|
129
|
-
}
|
|
124
|
+
// Density has its own initialization signal and is useful independently from
|
|
125
|
+
// source-capacity history, including when an itemsTarget cap truncates a response.
|
|
126
|
+
let getTrustedDensity = (p: partition) => p.eventDensity
|
|
130
127
|
|
|
131
128
|
let getMinQueryRange = (partitions: array<partition>) => {
|
|
132
129
|
let min = ref(0)
|
|
@@ -134,8 +131,8 @@ let getMinQueryRange = (partitions: array<partition>) => {
|
|
|
134
131
|
let p = partitions->Array.getUnsafe(i)
|
|
135
132
|
|
|
136
133
|
// Even if it's fetching, set dynamicContract field
|
|
137
|
-
let a = p.
|
|
138
|
-
let b = p.
|
|
134
|
+
let a = p.sourceRangeCapacity
|
|
135
|
+
let b = p.prevSourceRangeCapacity
|
|
139
136
|
if a > 0 && (min.contents == 0 || a < min.contents) {
|
|
140
137
|
min := a
|
|
141
138
|
}
|
|
@@ -208,11 +205,14 @@ module OptimizedPartitions = {
|
|
|
208
205
|
nextPartitionIndexRef := nextPartitionIndexRef.contents + 1
|
|
209
206
|
let minRange = getMinQueryRange([p1, p2])
|
|
210
207
|
// The merged partition indexes both parents' addresses, so its expected
|
|
211
|
-
// event rate is the sum of their densities.
|
|
212
|
-
// density
|
|
213
|
-
//
|
|
214
|
-
let inheritedDensity =
|
|
215
|
-
|
|
208
|
+
// event rate is the sum of their densities. If neither parent has a
|
|
209
|
+
// trusted density, the merged partition probes for a fresh signal
|
|
210
|
+
// instead of treating zero as an observed density.
|
|
211
|
+
let inheritedDensity = switch (p1->getTrustedDensity, p2->getTrustedDensity) {
|
|
212
|
+
| (Some(p1Density), Some(p2Density)) => Some(p1Density +. p2Density)
|
|
213
|
+
| (Some(density), None) | (None, Some(density)) => Some(density)
|
|
214
|
+
| (None, None) => None
|
|
215
|
+
}
|
|
216
216
|
{
|
|
217
217
|
id: newId,
|
|
218
218
|
dynamicContract: Some(contractName),
|
|
@@ -221,10 +221,10 @@ module OptimizedPartitions = {
|
|
|
221
221
|
mergeBlock: None,
|
|
222
222
|
addressesByContractName: Dict.make(), // set below
|
|
223
223
|
mutPendingQueries: [],
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
224
|
+
sourceRangeCapacity: minRange,
|
|
225
|
+
prevSourceRangeCapacity: minRange,
|
|
226
|
+
eventDensity: inheritedDensity,
|
|
227
|
+
latestSourceRangeCapacityUpdateBlock: 0,
|
|
228
228
|
}
|
|
229
229
|
}
|
|
230
230
|
|
|
@@ -472,14 +472,28 @@ module OptimizedPartitions = {
|
|
|
472
472
|
pendingQuery.fetchedBlock = Some(latestFetchedBlock)
|
|
473
473
|
|
|
474
474
|
let blockRange = latestFetchedBlock.blockNumber - query.fromBlock + 1
|
|
475
|
-
//
|
|
475
|
+
// Update density for every response, independently from whether this range
|
|
476
|
+
// is valid evidence of source capacity. A cap hit is still useful density
|
|
477
|
+
// evidence because it reports items returned across the scanned range.
|
|
478
|
+
let observedEventDensity = itemsCount->Int.toFloat /. blockRange->Int.toFloat
|
|
479
|
+
// Seed from the first observation, then smooth every later observation
|
|
480
|
+
// with a 1:1 moving average. Keeping initialization explicit is important:
|
|
481
|
+
// zero is valid density evidence and must participate in the next blend.
|
|
482
|
+
let updatedEventDensity = switch p.eventDensity {
|
|
483
|
+
| None => Some(observedEventDensity)
|
|
484
|
+
| Some(eventDensity) => Some((eventDensity +. observedEventDensity) /. 2.)
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Skip updating source capacity if a later response already updated it.
|
|
476
488
|
// Prevents degradation of the chunking heuristic when parallel query
|
|
477
489
|
// responses arrive out of order (e.g. earlier query with smaller range
|
|
478
490
|
// arriving after a later query with bigger range).
|
|
479
|
-
let
|
|
480
|
-
latestFetchedBlock.blockNumber > p.
|
|
491
|
+
let shouldUpdateSourceRangeCapacity =
|
|
492
|
+
latestFetchedBlock.blockNumber > p.latestSourceRangeCapacityUpdateBlock &&
|
|
481
493
|
switch query.toBlock {
|
|
482
|
-
| None =>
|
|
494
|
+
| None =>
|
|
495
|
+
// Don't update source capacity when very close to the head.
|
|
496
|
+
latestFetchedBlock.blockNumber < knownHeight - 10
|
|
483
497
|
| Some(queryToBlock) =>
|
|
484
498
|
if latestFetchedBlock.blockNumber < queryToBlock {
|
|
485
499
|
// Partial response is direct capacity evidence — unless it was
|
|
@@ -497,9 +511,12 @@ module OptimizedPartitions = {
|
|
|
497
511
|
}
|
|
498
512
|
}
|
|
499
513
|
}
|
|
500
|
-
let
|
|
501
|
-
|
|
502
|
-
|
|
514
|
+
let updatedSourceRangeCapacity = shouldUpdateSourceRangeCapacity
|
|
515
|
+
? blockRange
|
|
516
|
+
: p.sourceRangeCapacity
|
|
517
|
+
let updatedPrevSourceRangeCapacity = shouldUpdateSourceRangeCapacity
|
|
518
|
+
? p.sourceRangeCapacity
|
|
519
|
+
: p.prevSourceRangeCapacity
|
|
503
520
|
|
|
504
521
|
// Process fetched queries from front of queue for main partition
|
|
505
522
|
let updatedLatestFetchedBlock = consumeFetchedQueries(
|
|
@@ -519,12 +536,12 @@ module OptimizedPartitions = {
|
|
|
519
536
|
let updatedMainPartition = {
|
|
520
537
|
...p,
|
|
521
538
|
latestFetchedBlock: updatedLatestFetchedBlock,
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
539
|
+
sourceRangeCapacity: updatedSourceRangeCapacity,
|
|
540
|
+
prevSourceRangeCapacity: updatedPrevSourceRangeCapacity,
|
|
541
|
+
eventDensity: updatedEventDensity,
|
|
542
|
+
latestSourceRangeCapacityUpdateBlock: shouldUpdateSourceRangeCapacity
|
|
526
543
|
? latestFetchedBlock.blockNumber
|
|
527
|
-
: p.
|
|
544
|
+
: p.latestSourceRangeCapacityUpdateBlock,
|
|
528
545
|
}
|
|
529
546
|
|
|
530
547
|
mutEntities->Dict.set(p.id, updatedMainPartition)
|
|
@@ -986,10 +1003,10 @@ OptimizedPartitions.t => {
|
|
|
986
1003
|
addressesByContractName,
|
|
987
1004
|
mergeBlock: None,
|
|
988
1005
|
mutPendingQueries: [],
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
1006
|
+
sourceRangeCapacity: 0,
|
|
1007
|
+
prevSourceRangeCapacity: 0,
|
|
1008
|
+
eventDensity: None,
|
|
1009
|
+
latestSourceRangeCapacityUpdateBlock: 0,
|
|
993
1010
|
})
|
|
994
1011
|
nextPartitionIndexRef := nextPartitionIndexRef.contents + 1
|
|
995
1012
|
}
|
|
@@ -1322,10 +1339,10 @@ let registerDynamicContracts = (
|
|
|
1322
1339
|
addressesByContractName,
|
|
1323
1340
|
mergeBlock: None,
|
|
1324
1341
|
mutPendingQueries: p.mutPendingQueries,
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1342
|
+
sourceRangeCapacity: p.sourceRangeCapacity,
|
|
1343
|
+
prevSourceRangeCapacity: p.prevSourceRangeCapacity,
|
|
1344
|
+
eventDensity: p.eventDensity,
|
|
1345
|
+
latestSourceRangeCapacityUpdateBlock: p.latestSourceRangeCapacityUpdateBlock,
|
|
1329
1346
|
})
|
|
1330
1347
|
}
|
|
1331
1348
|
}
|
|
@@ -1582,24 +1599,25 @@ type partitionFillState = {
|
|
|
1582
1599
|
// query with no other ceiling: the true hard bounds stay
|
|
1583
1600
|
// endBlock/mergeBlock/the lagged head.
|
|
1584
1601
|
//
|
|
1585
|
-
// The tick's budget is chainTargetItems minus what's already in flight.
|
|
1586
|
-
//
|
|
1587
|
-
//
|
|
1588
|
-
//
|
|
1589
|
-
//
|
|
1590
|
-
//
|
|
1602
|
+
// The tick's budget is chainTargetItems minus what's already in flight. A
|
|
1603
|
+
// non-positive budget only resolves the wait action and generates no query
|
|
1604
|
+
// candidates. With a positive budget, every candidate query — gap-fill holes,
|
|
1605
|
+
// plus each in-range partition's chunks or open-ended probe toward the target —
|
|
1606
|
+
// is generated with no budget check, then the candidates are sorted by
|
|
1607
|
+
// fromBlock and accepted in that order while the budget stays positive. The
|
|
1608
|
+
// query that tips it negative is still accepted (a single overshoot);
|
|
1609
|
+
// everything after it waits for a tick with more budget.
|
|
1591
1610
|
// Sorting by fromBlock spends the budget on the earliest blocks across all
|
|
1592
1611
|
// partitions first, so no partition is starved by generation order and the
|
|
1593
1612
|
// frontier advances evenly. In-flight reservations release as responses land,
|
|
1594
1613
|
// so acceptance redistributes across ticks.
|
|
1595
1614
|
//
|
|
1596
|
-
// A partition with a
|
|
1597
|
-
//
|
|
1598
|
-
//
|
|
1599
|
-
//
|
|
1600
|
-
//
|
|
1601
|
-
//
|
|
1602
|
-
// one budget, each scaled by how much of the range it still has to cover.
|
|
1615
|
+
// A partition with source-capacity history and a positive density generates
|
|
1616
|
+
// density-sized chunks toward the target. Any other partition (no signal, no
|
|
1617
|
+
// capacity history, or a density-0 estimate) generates one open-ended probe
|
|
1618
|
+
// sized to the events its range to the target is expected to hold —
|
|
1619
|
+
// rangeTargetDensity × (chainTargetBlock − fromBlock + 1) / inRangeCount — so
|
|
1620
|
+
// unknown-density partitions probe in parallel within one budget.
|
|
1603
1621
|
let getNextQuery = (
|
|
1604
1622
|
{optimizedPartitions, blockLag, latestOnBlockBlockNumber, knownHeight, endBlock}: t,
|
|
1605
1623
|
~chainTargetBlock: int,
|
|
@@ -1639,6 +1657,12 @@ let getNextQuery = (
|
|
|
1639
1657
|
}
|
|
1640
1658
|
}
|
|
1641
1659
|
|
|
1660
|
+
// A zero budget is an admission check: preserve the wait-state scan above,
|
|
1661
|
+
// but make every query-generation pass below empty. Caught-up chains also
|
|
1662
|
+
// skip those passes because their action is already known.
|
|
1663
|
+
let partitionsCount =
|
|
1664
|
+
chainTargetItems <= 0. || shouldWaitForNewBlock.contents ? 0 : partitionsCount
|
|
1665
|
+
|
|
1642
1666
|
// One bucket per partition, in idsInAscOrder order — gap-fill and the
|
|
1643
1667
|
// budget pass both push into a partition's own bucket, so flattening at
|
|
1644
1668
|
// the end (see below) reproduces idsInAscOrder without a sort.
|
|
@@ -1788,10 +1812,9 @@ let getNextQuery = (
|
|
|
1788
1812
|
inRangeCount > 0 && rangeToTarget > 0 ? freshBudget /. rangeToTarget->Int.toFloat : 0.
|
|
1789
1813
|
|
|
1790
1814
|
// Phase B: generate each in-range partition's candidates — strict chunks
|
|
1791
|
-
//
|
|
1792
|
-
//
|
|
1793
|
-
//
|
|
1794
|
-
// acceptance pass below decides which candidates make the cut.
|
|
1815
|
+
// when both source-capacity history and density are known, or an open-ended
|
|
1816
|
+
// budget probe otherwise. No budget check here; the acceptance pass below
|
|
1817
|
+
// decides which candidates make the cut.
|
|
1795
1818
|
//
|
|
1796
1819
|
// Chunks require a POSITIVE trusted density: density 0 prices every chunk at
|
|
1797
1820
|
// ~nothing, so an open-ended probe (full server scan range in one response)
|
|
@@ -2022,10 +2045,10 @@ let make = (
|
|
|
2022
2045
|
mergeBlock: None,
|
|
2023
2046
|
dynamicContract: None,
|
|
2024
2047
|
mutPendingQueries: [],
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2048
|
+
sourceRangeCapacity: 0,
|
|
2049
|
+
prevSourceRangeCapacity: 0,
|
|
2050
|
+
eventDensity: None,
|
|
2051
|
+
latestSourceRangeCapacityUpdateBlock: 0,
|
|
2029
2052
|
})
|
|
2030
2053
|
}
|
|
2031
2054
|
|
|
@@ -2345,13 +2368,15 @@ let isReadyToEnterReorgThreshold = ({endBlock, blockLag, buffer, knownHeight} as
|
|
|
2345
2368
|
buffer->Utils.Array.isEmpty
|
|
2346
2369
|
}
|
|
2347
2370
|
|
|
2348
|
-
// Lower progress percentage = further behind = higher priority.
|
|
2349
|
-
//
|
|
2371
|
+
// Lower progress percentage = further behind = higher priority. Progress is
|
|
2372
|
+
// relative to the head this chain can actually fetch, so a chain at its lagged
|
|
2373
|
+
// head does not look behind relative to unavailable blocks. Shared by the batch
|
|
2374
|
+
// ordering and the cross-chain fetch priority.
|
|
2350
2375
|
let getProgressPercentage = (fetchState: t) => {
|
|
2351
2376
|
switch fetchState.firstEventBlock {
|
|
2352
2377
|
| None => 0.
|
|
2353
2378
|
| Some(firstEventBlock) =>
|
|
2354
|
-
let totalRange = fetchState.knownHeight - firstEventBlock
|
|
2379
|
+
let totalRange = fetchState.knownHeight - fetchState.blockLag - firstEventBlock
|
|
2355
2380
|
if totalRange <= 0 {
|
|
2356
2381
|
0.
|
|
2357
2382
|
} else {
|
package/src/FetchState.res.mjs
CHANGED
|
@@ -28,27 +28,23 @@ function densityItemsTarget(density, fromBlock, toBlock, chainTargetBlock) {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
function getMinHistoryRange(p) {
|
|
31
|
-
let match = p.
|
|
32
|
-
let match$1 = p.
|
|
31
|
+
let match = p.sourceRangeCapacity;
|
|
32
|
+
let match$1 = p.prevSourceRangeCapacity;
|
|
33
33
|
if (match !== 0 && match$1 !== 0) {
|
|
34
34
|
return match < match$1 ? match : match$1;
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
function getTrustedDensity(p) {
|
|
39
|
-
|
|
40
|
-
let match$1 = p.prevPrevQueryRange;
|
|
41
|
-
if (match !== 0 && match$1 !== 0) {
|
|
42
|
-
return p.prevRangeSize / match;
|
|
43
|
-
}
|
|
39
|
+
return p.eventDensity;
|
|
44
40
|
}
|
|
45
41
|
|
|
46
42
|
function getMinQueryRange(partitions) {
|
|
47
43
|
let min = 0;
|
|
48
44
|
for (let i = 0, i_finish = partitions.length; i < i_finish; ++i) {
|
|
49
45
|
let p = partitions[i];
|
|
50
|
-
let a = p.
|
|
51
|
-
let b = p.
|
|
46
|
+
let a = p.sourceRangeCapacity;
|
|
47
|
+
let b = p.prevSourceRangeCapacity;
|
|
52
48
|
if (a > 0 && (min === 0 || a < min)) {
|
|
53
49
|
min = a;
|
|
54
50
|
}
|
|
@@ -88,10 +84,10 @@ function mergePartitionsAtBlock(p1, p2, potentialMergeBlock, contractName, maxAd
|
|
|
88
84
|
mergeBlock: potentialMergeBlock,
|
|
89
85
|
dynamicContract: p1.dynamicContract,
|
|
90
86
|
mutPendingQueries: p1.mutPendingQueries,
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
87
|
+
sourceRangeCapacity: p1.sourceRangeCapacity,
|
|
88
|
+
prevSourceRangeCapacity: p1.prevSourceRangeCapacity,
|
|
89
|
+
eventDensity: p1.eventDensity,
|
|
90
|
+
latestSourceRangeCapacityUpdateBlock: p1.latestSourceRangeCapacityUpdateBlock
|
|
95
91
|
});
|
|
96
92
|
completed.push({
|
|
97
93
|
id: p2.id,
|
|
@@ -101,10 +97,10 @@ function mergePartitionsAtBlock(p1, p2, potentialMergeBlock, contractName, maxAd
|
|
|
101
97
|
mergeBlock: potentialMergeBlock,
|
|
102
98
|
dynamicContract: p2.dynamicContract,
|
|
103
99
|
mutPendingQueries: p2.mutPendingQueries,
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
100
|
+
sourceRangeCapacity: p2.sourceRangeCapacity,
|
|
101
|
+
prevSourceRangeCapacity: p2.prevSourceRangeCapacity,
|
|
102
|
+
eventDensity: p2.eventDensity,
|
|
103
|
+
latestSourceRangeCapacityUpdateBlock: p2.latestSourceRangeCapacityUpdateBlock
|
|
108
104
|
});
|
|
109
105
|
let newId = nextPartitionIndexRef.contents.toString();
|
|
110
106
|
nextPartitionIndexRef.contents = nextPartitionIndexRef.contents + 1 | 0;
|
|
@@ -112,7 +108,13 @@ function mergePartitionsAtBlock(p1, p2, potentialMergeBlock, contractName, maxAd
|
|
|
112
108
|
p1,
|
|
113
109
|
p2
|
|
114
110
|
]);
|
|
115
|
-
let
|
|
111
|
+
let match = p1.eventDensity;
|
|
112
|
+
let match$1 = p2.eventDensity;
|
|
113
|
+
let inheritedDensity = match !== undefined ? (
|
|
114
|
+
match$1 !== undefined ? match + match$1 : match
|
|
115
|
+
) : (
|
|
116
|
+
match$1 !== undefined ? match$1 : undefined
|
|
117
|
+
);
|
|
116
118
|
continuingBase = {
|
|
117
119
|
id: newId,
|
|
118
120
|
latestFetchedBlock: {
|
|
@@ -124,10 +126,10 @@ function mergePartitionsAtBlock(p1, p2, potentialMergeBlock, contractName, maxAd
|
|
|
124
126
|
mergeBlock: undefined,
|
|
125
127
|
dynamicContract: contractName,
|
|
126
128
|
mutPendingQueries: [],
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
129
|
+
sourceRangeCapacity: minRange,
|
|
130
|
+
prevSourceRangeCapacity: minRange,
|
|
131
|
+
eventDensity: inheritedDensity,
|
|
132
|
+
latestSourceRangeCapacityUpdateBlock: 0
|
|
131
133
|
};
|
|
132
134
|
} else {
|
|
133
135
|
completed.push({
|
|
@@ -138,10 +140,10 @@ function mergePartitionsAtBlock(p1, p2, potentialMergeBlock, contractName, maxAd
|
|
|
138
140
|
mergeBlock: potentialMergeBlock,
|
|
139
141
|
dynamicContract: p1.dynamicContract,
|
|
140
142
|
mutPendingQueries: p1.mutPendingQueries,
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
143
|
+
sourceRangeCapacity: p1.sourceRangeCapacity,
|
|
144
|
+
prevSourceRangeCapacity: p1.prevSourceRangeCapacity,
|
|
145
|
+
eventDensity: p1.eventDensity,
|
|
146
|
+
latestSourceRangeCapacityUpdateBlock: p1.latestSourceRangeCapacityUpdateBlock
|
|
145
147
|
});
|
|
146
148
|
continuingBase = p2;
|
|
147
149
|
}
|
|
@@ -154,10 +156,10 @@ function mergePartitionsAtBlock(p1, p2, potentialMergeBlock, contractName, maxAd
|
|
|
154
156
|
mergeBlock: potentialMergeBlock,
|
|
155
157
|
dynamicContract: p2.dynamicContract,
|
|
156
158
|
mutPendingQueries: p2.mutPendingQueries,
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
159
|
+
sourceRangeCapacity: p2.sourceRangeCapacity,
|
|
160
|
+
prevSourceRangeCapacity: p2.prevSourceRangeCapacity,
|
|
161
|
+
eventDensity: p2.eventDensity,
|
|
162
|
+
latestSourceRangeCapacityUpdateBlock: p2.latestSourceRangeCapacityUpdateBlock
|
|
161
163
|
});
|
|
162
164
|
continuingBase = p1;
|
|
163
165
|
} else {
|
|
@@ -178,10 +180,10 @@ function mergePartitionsAtBlock(p1, p2, potentialMergeBlock, contractName, maxAd
|
|
|
178
180
|
mergeBlock: continuingBase.mergeBlock,
|
|
179
181
|
dynamicContract: continuingBase.dynamicContract,
|
|
180
182
|
mutPendingQueries: continuingBase.mutPendingQueries,
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
183
|
+
sourceRangeCapacity: continuingBase.sourceRangeCapacity,
|
|
184
|
+
prevSourceRangeCapacity: continuingBase.prevSourceRangeCapacity,
|
|
185
|
+
eventDensity: continuingBase.eventDensity,
|
|
186
|
+
latestSourceRangeCapacityUpdateBlock: continuingBase.latestSourceRangeCapacityUpdateBlock
|
|
185
187
|
});
|
|
186
188
|
let restId = nextPartitionIndexRef.contents.toString();
|
|
187
189
|
nextPartitionIndexRef.contents = nextPartitionIndexRef.contents + 1 | 0;
|
|
@@ -193,10 +195,10 @@ function mergePartitionsAtBlock(p1, p2, potentialMergeBlock, contractName, maxAd
|
|
|
193
195
|
mergeBlock: continuingBase.mergeBlock,
|
|
194
196
|
dynamicContract: continuingBase.dynamicContract,
|
|
195
197
|
mutPendingQueries: [],
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
198
|
+
sourceRangeCapacity: continuingBase.sourceRangeCapacity,
|
|
199
|
+
prevSourceRangeCapacity: continuingBase.prevSourceRangeCapacity,
|
|
200
|
+
eventDensity: continuingBase.eventDensity,
|
|
201
|
+
latestSourceRangeCapacityUpdateBlock: continuingBase.latestSourceRangeCapacityUpdateBlock
|
|
200
202
|
});
|
|
201
203
|
return completed;
|
|
202
204
|
}
|
|
@@ -210,10 +212,10 @@ function mergePartitionsAtBlock(p1, p2, potentialMergeBlock, contractName, maxAd
|
|
|
210
212
|
mergeBlock: continuingBase.mergeBlock,
|
|
211
213
|
dynamicContract: continuingBase.dynamicContract,
|
|
212
214
|
mutPendingQueries: continuingBase.mutPendingQueries,
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
215
|
+
sourceRangeCapacity: continuingBase.sourceRangeCapacity,
|
|
216
|
+
prevSourceRangeCapacity: continuingBase.prevSourceRangeCapacity,
|
|
217
|
+
eventDensity: continuingBase.eventDensity,
|
|
218
|
+
latestSourceRangeCapacityUpdateBlock: continuingBase.latestSourceRangeCapacityUpdateBlock
|
|
217
219
|
});
|
|
218
220
|
return completed;
|
|
219
221
|
}
|
|
@@ -358,8 +360,11 @@ function handleQueryResponse(optimizedPartitions, query, knownHeight, itemsCount
|
|
|
358
360
|
let pendingQuery = getPendingQueryOrThrow(p$1, query.fromBlock);
|
|
359
361
|
pendingQuery.fetchedBlock = latestFetchedBlock;
|
|
360
362
|
let blockRange = (latestFetchedBlock.blockNumber - query.fromBlock | 0) + 1 | 0;
|
|
361
|
-
let
|
|
362
|
-
|
|
363
|
+
let observedEventDensity = itemsCount / blockRange;
|
|
364
|
+
let eventDensity = p$1.eventDensity;
|
|
365
|
+
let updatedEventDensity = eventDensity !== undefined ? (eventDensity + observedEventDensity) / 2 : observedEventDensity;
|
|
366
|
+
let shouldUpdateSourceRangeCapacity = false;
|
|
367
|
+
if (latestFetchedBlock.blockNumber > p$1.latestSourceRangeCapacityUpdateBlock) {
|
|
363
368
|
let queryToBlock = query.toBlock;
|
|
364
369
|
let tmp;
|
|
365
370
|
if (queryToBlock !== undefined) {
|
|
@@ -372,11 +377,10 @@ function handleQueryResponse(optimizedPartitions, query, knownHeight, itemsCount
|
|
|
372
377
|
} else {
|
|
373
378
|
tmp = latestFetchedBlock.blockNumber < (knownHeight - 10 | 0);
|
|
374
379
|
}
|
|
375
|
-
|
|
380
|
+
shouldUpdateSourceRangeCapacity = tmp;
|
|
376
381
|
}
|
|
377
|
-
let
|
|
378
|
-
let
|
|
379
|
-
let updatedPrevRangeSize = shouldUpdateBlockRange ? itemsCount : p$1.prevRangeSize;
|
|
382
|
+
let updatedSourceRangeCapacity = shouldUpdateSourceRangeCapacity ? blockRange : p$1.sourceRangeCapacity;
|
|
383
|
+
let updatedPrevSourceRangeCapacity = shouldUpdateSourceRangeCapacity ? p$1.sourceRangeCapacity : p$1.prevSourceRangeCapacity;
|
|
380
384
|
let mutPendingQueries = p$1.mutPendingQueries;
|
|
381
385
|
let latestFetchedBlock$1 = p$1.latestFetchedBlock;
|
|
382
386
|
while ((() => {
|
|
@@ -402,7 +406,7 @@ function handleQueryResponse(optimizedPartitions, query, knownHeight, itemsCount
|
|
|
402
406
|
let updatedMainPartition_mergeBlock = p$1.mergeBlock;
|
|
403
407
|
let updatedMainPartition_dynamicContract = p$1.dynamicContract;
|
|
404
408
|
let updatedMainPartition_mutPendingQueries = p$1.mutPendingQueries;
|
|
405
|
-
let
|
|
409
|
+
let updatedMainPartition_latestSourceRangeCapacityUpdateBlock = shouldUpdateSourceRangeCapacity ? latestFetchedBlock.blockNumber : p$1.latestSourceRangeCapacityUpdateBlock;
|
|
406
410
|
let updatedMainPartition = {
|
|
407
411
|
id: updatedMainPartition_id,
|
|
408
412
|
latestFetchedBlock: updatedLatestFetchedBlock,
|
|
@@ -411,10 +415,10 @@ function handleQueryResponse(optimizedPartitions, query, knownHeight, itemsCount
|
|
|
411
415
|
mergeBlock: updatedMainPartition_mergeBlock,
|
|
412
416
|
dynamicContract: updatedMainPartition_dynamicContract,
|
|
413
417
|
mutPendingQueries: updatedMainPartition_mutPendingQueries,
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
+
sourceRangeCapacity: updatedSourceRangeCapacity,
|
|
419
|
+
prevSourceRangeCapacity: updatedPrevSourceRangeCapacity,
|
|
420
|
+
eventDensity: updatedEventDensity,
|
|
421
|
+
latestSourceRangeCapacityUpdateBlock: updatedMainPartition_latestSourceRangeCapacityUpdateBlock
|
|
418
422
|
};
|
|
419
423
|
mutEntities[p$1.id] = updatedMainPartition;
|
|
420
424
|
}
|
|
@@ -739,10 +743,10 @@ function createPartitionsFromIndexingAddresses(registeringContractsByContract, d
|
|
|
739
743
|
mergeBlock: undefined,
|
|
740
744
|
dynamicContract: isDynamic ? contractName : undefined,
|
|
741
745
|
mutPendingQueries: [],
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
+
sourceRangeCapacity: 0,
|
|
747
|
+
prevSourceRangeCapacity: 0,
|
|
748
|
+
eventDensity: undefined,
|
|
749
|
+
latestSourceRangeCapacityUpdateBlock: 0
|
|
746
750
|
});
|
|
747
751
|
nextPartitionIndexRef = nextPartitionIndexRef + 1 | 0;
|
|
748
752
|
};
|
|
@@ -790,10 +794,10 @@ function createPartitionsFromIndexingAddresses(registeringContractsByContract, d
|
|
|
790
794
|
mergeBlock: currentPBlock < nextPBlock ? nextPBlock : undefined,
|
|
791
795
|
dynamicContract: currentP.dynamicContract,
|
|
792
796
|
mutPendingQueries: currentP.mutPendingQueries,
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
+
sourceRangeCapacity: currentP.sourceRangeCapacity,
|
|
798
|
+
prevSourceRangeCapacity: currentP.prevSourceRangeCapacity,
|
|
799
|
+
eventDensity: currentP.eventDensity,
|
|
800
|
+
latestSourceRangeCapacityUpdateBlock: currentP.latestSourceRangeCapacityUpdateBlock
|
|
797
801
|
});
|
|
798
802
|
currentPRef = {
|
|
799
803
|
id: nextP.id,
|
|
@@ -803,10 +807,10 @@ function createPartitionsFromIndexingAddresses(registeringContractsByContract, d
|
|
|
803
807
|
mergeBlock: nextP.mergeBlock,
|
|
804
808
|
dynamicContract: nextP.dynamicContract,
|
|
805
809
|
mutPendingQueries: nextP.mutPendingQueries,
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
+
sourceRangeCapacity: nextP.sourceRangeCapacity,
|
|
811
|
+
prevSourceRangeCapacity: nextP.prevSourceRangeCapacity,
|
|
812
|
+
eventDensity: nextP.eventDensity,
|
|
813
|
+
latestSourceRangeCapacityUpdateBlock: nextP.latestSourceRangeCapacityUpdateBlock
|
|
810
814
|
};
|
|
811
815
|
} else {
|
|
812
816
|
currentPRef = {
|
|
@@ -817,10 +821,10 @@ function createPartitionsFromIndexingAddresses(registeringContractsByContract, d
|
|
|
817
821
|
mergeBlock: currentP.mergeBlock,
|
|
818
822
|
dynamicContract: currentP.dynamicContract,
|
|
819
823
|
mutPendingQueries: currentP.mutPendingQueries,
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
+
sourceRangeCapacity: currentP.sourceRangeCapacity,
|
|
825
|
+
prevSourceRangeCapacity: currentP.prevSourceRangeCapacity,
|
|
826
|
+
eventDensity: currentP.eventDensity,
|
|
827
|
+
latestSourceRangeCapacityUpdateBlock: currentP.latestSourceRangeCapacityUpdateBlock
|
|
824
828
|
};
|
|
825
829
|
}
|
|
826
830
|
}
|
|
@@ -967,10 +971,10 @@ function registerDynamicContracts(fetchState, indexingAddresses, items) {
|
|
|
967
971
|
mergeBlock: p.mergeBlock,
|
|
968
972
|
dynamicContract: p.dynamicContract,
|
|
969
973
|
mutPendingQueries: p.mutPendingQueries,
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
+
sourceRangeCapacity: p.sourceRangeCapacity,
|
|
975
|
+
prevSourceRangeCapacity: p.prevSourceRangeCapacity,
|
|
976
|
+
eventDensity: p.eventDensity,
|
|
977
|
+
latestSourceRangeCapacityUpdateBlock: p.latestSourceRangeCapacityUpdateBlock
|
|
974
978
|
};
|
|
975
979
|
let addressesByContractName = {};
|
|
976
980
|
addressesByContractName[contractName] = addresses;
|
|
@@ -982,10 +986,10 @@ function registerDynamicContracts(fetchState, indexingAddresses, items) {
|
|
|
982
986
|
mergeBlock: undefined,
|
|
983
987
|
dynamicContract: contractName,
|
|
984
988
|
mutPendingQueries: p.mutPendingQueries,
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
+
sourceRangeCapacity: p.sourceRangeCapacity,
|
|
990
|
+
prevSourceRangeCapacity: p.prevSourceRangeCapacity,
|
|
991
|
+
eventDensity: p.eventDensity,
|
|
992
|
+
latestSourceRangeCapacityUpdateBlock: p.latestSourceRangeCapacityUpdateBlock
|
|
989
993
|
});
|
|
990
994
|
}
|
|
991
995
|
} else {
|
|
@@ -997,10 +1001,10 @@ function registerDynamicContracts(fetchState, indexingAddresses, items) {
|
|
|
997
1001
|
mergeBlock: p.mergeBlock,
|
|
998
1002
|
dynamicContract: contractName,
|
|
999
1003
|
mutPendingQueries: p.mutPendingQueries,
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
+
sourceRangeCapacity: p.sourceRangeCapacity,
|
|
1005
|
+
prevSourceRangeCapacity: p.prevSourceRangeCapacity,
|
|
1006
|
+
eventDensity: p.eventDensity,
|
|
1007
|
+
latestSourceRangeCapacityUpdateBlock: p.latestSourceRangeCapacityUpdateBlock
|
|
1004
1008
|
};
|
|
1005
1009
|
}
|
|
1006
1010
|
}
|
|
@@ -1072,7 +1076,7 @@ function pushGapFillQueries(queries, partitionId, rangeFromBlock, rangeEndBlock,
|
|
|
1072
1076
|
exit = 1;
|
|
1073
1077
|
}
|
|
1074
1078
|
if (exit === 1) {
|
|
1075
|
-
let trustedDensity =
|
|
1079
|
+
let trustedDensity = partition.eventDensity;
|
|
1076
1080
|
let maxBlock = rangeEndBlock !== undefined ? rangeEndBlock : chainTargetBlock;
|
|
1077
1081
|
let pushSingleQuery = (density, isChunk) => {
|
|
1078
1082
|
let itemsTarget = densityItemsTarget(density * chunkItemsMultiplier, rangeFromBlock, rangeEndBlock, chainTargetBlock);
|
|
@@ -1150,7 +1154,8 @@ function getNextQuery(param, chainTargetBlock, chainTargetItems, $staropt$star)
|
|
|
1150
1154
|
shouldWaitForNewBlock = false;
|
|
1151
1155
|
}
|
|
1152
1156
|
}
|
|
1153
|
-
let
|
|
1157
|
+
let partitionsCount$1 = chainTargetItems <= 0 || shouldWaitForNewBlock ? 0 : partitionsCount;
|
|
1158
|
+
let queriesByPartitionIndex = Stdlib_Array.fromInitializer(partitionsCount$1, param => []);
|
|
1154
1159
|
let computeQueryEndBlock = p => {
|
|
1155
1160
|
let queryEndBlock = Utils.$$Math.minOptInt(endBlock, p.mergeBlock);
|
|
1156
1161
|
if (blockLag !== 0) {
|
|
@@ -1164,7 +1169,7 @@ function getNextQuery(param, chainTargetBlock, chainTargetItems, $staropt$star)
|
|
|
1164
1169
|
let partitionIndexById = {};
|
|
1165
1170
|
let candidates = [];
|
|
1166
1171
|
let fillStates = [];
|
|
1167
|
-
for (let idx$1 = 0; idx$1 < partitionsCount; ++idx$1) {
|
|
1172
|
+
for (let idx$1 = 0; idx$1 < partitionsCount$1; ++idx$1) {
|
|
1168
1173
|
let partitionId$1 = optimizedPartitions.idsInAscOrder[idx$1];
|
|
1169
1174
|
let p$1 = optimizedPartitions.entities[partitionId$1];
|
|
1170
1175
|
partitionIndexById[partitionId$1] = idx$1;
|
|
@@ -1189,7 +1194,7 @@ function getNextQuery(param, chainTargetBlock, chainTargetItems, $staropt$star)
|
|
|
1189
1194
|
let pq$1 = p$1.mutPendingQueries[pqIdx$1];
|
|
1190
1195
|
if (pq$1.fromBlock > cursor) {
|
|
1191
1196
|
let beforeLen = candidates.length;
|
|
1192
|
-
pushGapFillQueries(candidates, partitionId$1, cursor, Utils.$$Math.minOptInt(pq$1.fromBlock - 1 | 0, queryEndBlock), knownHeight, chainTargetBlock, maybeChunkRange, (12 - pendingCount | 0) - chunksUsedThisCall | 0, p$1, chainTargetItems / partitionsCount, chunkItemsMultiplier, p$1.selection, p$1.addressesByContractName);
|
|
1197
|
+
pushGapFillQueries(candidates, partitionId$1, cursor, Utils.$$Math.minOptInt(pq$1.fromBlock - 1 | 0, queryEndBlock), knownHeight, chainTargetBlock, maybeChunkRange, (12 - pendingCount | 0) - chunksUsedThisCall | 0, p$1, chainTargetItems / partitionsCount$1, chunkItemsMultiplier, p$1.selection, p$1.addressesByContractName);
|
|
1193
1198
|
chunksUsedThisCall = chunksUsedThisCall + (candidates.length - beforeLen | 0) | 0;
|
|
1194
1199
|
}
|
|
1195
1200
|
let toBlock = pq$1.toBlock;
|
|
@@ -1248,7 +1253,7 @@ function getNextQuery(param, chainTargetBlock, chainTargetItems, $staropt$star)
|
|
|
1248
1253
|
let eb = fs.queryEndBlock;
|
|
1249
1254
|
let maxBlock = eb !== undefined ? eb : chainTargetBlock;
|
|
1250
1255
|
let match = fs.maybeChunkRange;
|
|
1251
|
-
let match$1 =
|
|
1256
|
+
let match$1 = p.eventDensity;
|
|
1252
1257
|
if (match !== undefined && match$1 !== undefined && match$1 > 0) {
|
|
1253
1258
|
let chunkSize = Js_math.ceil_int(match * 1.8);
|
|
1254
1259
|
let maxChunksRemaining = (12 - fs.pendingCount | 0) - fs.chunksUsedThisCall | 0;
|
|
@@ -1416,10 +1421,10 @@ function make$1(startBlock, endBlock, onEventRegistrations, contractConfigs, add
|
|
|
1416
1421
|
mergeBlock: undefined,
|
|
1417
1422
|
dynamicContract: undefined,
|
|
1418
1423
|
mutPendingQueries: [],
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1424
|
+
sourceRangeCapacity: 0,
|
|
1425
|
+
prevSourceRangeCapacity: 0,
|
|
1426
|
+
eventDensity: undefined,
|
|
1427
|
+
latestSourceRangeCapacityUpdateBlock: 0
|
|
1423
1428
|
});
|
|
1424
1429
|
}
|
|
1425
1430
|
let normalSelection = {
|
|
@@ -1554,10 +1559,10 @@ function rollback(fetchState, indexingAddresses, targetBlockNumber) {
|
|
|
1554
1559
|
mergeBlock: mergeBlock$1,
|
|
1555
1560
|
dynamicContract: p.dynamicContract,
|
|
1556
1561
|
mutPendingQueries: rollbackPendingQueries(p.mutPendingQueries, targetBlockNumber),
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1562
|
+
sourceRangeCapacity: p.sourceRangeCapacity,
|
|
1563
|
+
prevSourceRangeCapacity: p.prevSourceRangeCapacity,
|
|
1564
|
+
eventDensity: p.eventDensity,
|
|
1565
|
+
latestSourceRangeCapacityUpdateBlock: p.latestSourceRangeCapacityUpdateBlock
|
|
1561
1566
|
});
|
|
1562
1567
|
}
|
|
1563
1568
|
}
|
|
@@ -1575,10 +1580,10 @@ function rollback(fetchState, indexingAddresses, targetBlockNumber) {
|
|
|
1575
1580
|
mergeBlock: p.mergeBlock,
|
|
1576
1581
|
dynamicContract: p.dynamicContract,
|
|
1577
1582
|
mutPendingQueries: rollbackPendingQueries(p.mutPendingQueries, targetBlockNumber),
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1583
|
+
sourceRangeCapacity: p.sourceRangeCapacity,
|
|
1584
|
+
prevSourceRangeCapacity: p.prevSourceRangeCapacity,
|
|
1585
|
+
eventDensity: p.eventDensity,
|
|
1586
|
+
latestSourceRangeCapacityUpdateBlock: p.latestSourceRangeCapacityUpdateBlock
|
|
1582
1587
|
});
|
|
1583
1588
|
}
|
|
1584
1589
|
}
|
|
@@ -1619,10 +1624,10 @@ function resetPendingQueries(fetchState) {
|
|
|
1619
1624
|
mergeBlock: partition.mergeBlock,
|
|
1620
1625
|
dynamicContract: partition.dynamicContract,
|
|
1621
1626
|
mutPendingQueries: kept,
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1627
|
+
sourceRangeCapacity: partition.sourceRangeCapacity,
|
|
1628
|
+
prevSourceRangeCapacity: partition.prevSourceRangeCapacity,
|
|
1629
|
+
eventDensity: partition.eventDensity,
|
|
1630
|
+
latestSourceRangeCapacityUpdateBlock: partition.latestSourceRangeCapacityUpdateBlock
|
|
1626
1631
|
};
|
|
1627
1632
|
}
|
|
1628
1633
|
}
|
|
@@ -1698,7 +1703,7 @@ function getProgressPercentage(fetchState) {
|
|
|
1698
1703
|
if (firstEventBlock === undefined) {
|
|
1699
1704
|
return 0;
|
|
1700
1705
|
}
|
|
1701
|
-
let totalRange = fetchState.knownHeight - firstEventBlock | 0;
|
|
1706
|
+
let totalRange = (fetchState.knownHeight - fetchState.blockLag | 0) - firstEventBlock | 0;
|
|
1702
1707
|
if (totalRange <= 0) {
|
|
1703
1708
|
return 0;
|
|
1704
1709
|
}
|
package/src/Rollback.res
CHANGED
|
@@ -72,7 +72,7 @@ let rec rollback = async (
|
|
|
72
72
|
// found yet. Wait for the ReorgDetected branch above to find it and re-kick.
|
|
73
73
|
| FindingReorgDepth => ()
|
|
74
74
|
| FoundReorgDepth(_) if state->IndexerState.isProcessing =>
|
|
75
|
-
Logging.
|
|
75
|
+
Logging.trace("Waiting for batch to finish processing before executing rollback")
|
|
76
76
|
| FoundReorgDepth({chain: reorgChain, rollbackTargetBlockNumber}) =>
|
|
77
77
|
await executeRollback(
|
|
78
78
|
state,
|
|
@@ -96,10 +96,10 @@ and executeRollback = async (
|
|
|
96
96
|
) => {
|
|
97
97
|
let startTime = Performance.now()
|
|
98
98
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
99
|
+
// Not derived from the reorg chain's logger: that would bind its chainId onto
|
|
100
|
+
// every line, colliding with the per-chain chainId on the "Rollbacked" logs.
|
|
101
|
+
// The reorg chain is identified by the reorgChain param instead.
|
|
102
|
+
let logger = Logging.createChild(
|
|
103
103
|
~params={
|
|
104
104
|
"action": "Rollback",
|
|
105
105
|
"reorgChain": reorgChain,
|
|
@@ -161,10 +161,12 @@ and executeRollback = async (
|
|
|
161
161
|
}
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
let rolledBackChains = []
|
|
164
165
|
state
|
|
165
166
|
->IndexerState.chainStates
|
|
166
167
|
->Utils.Dict.forEach(cs => {
|
|
167
168
|
let chainId = (cs->ChainState.chainConfig).id
|
|
169
|
+
let fromBlock = cs->ChainState.committedProgressBlockNumber
|
|
168
170
|
cs->ChainState.rollback(
|
|
169
171
|
~newProgressBlockNumber=newProgressBlockNumberPerChain->Utils.Dict.dangerouslyGetByIntNonOption(
|
|
170
172
|
chainId,
|
|
@@ -175,6 +177,19 @@ and executeRollback = async (
|
|
|
175
177
|
~rollbackTargetBlockNumber,
|
|
176
178
|
~isReorgChain=chainId === reorgChainId,
|
|
177
179
|
)
|
|
180
|
+
let toBlock = cs->ChainState.committedProgressBlockNumber
|
|
181
|
+
if fromBlock !== toBlock {
|
|
182
|
+
rolledBackChains
|
|
183
|
+
->Array.push({
|
|
184
|
+
"chainId": chainId,
|
|
185
|
+
"fromBlock": fromBlock,
|
|
186
|
+
"toBlock": toBlock,
|
|
187
|
+
"rollbackedEvents": eventsProcessedDiffByChain
|
|
188
|
+
->Utils.Dict.dangerouslyGetByIntNonOption(chainId)
|
|
189
|
+
->Option.getOr(0.),
|
|
190
|
+
})
|
|
191
|
+
->ignore
|
|
192
|
+
}
|
|
178
193
|
})
|
|
179
194
|
|
|
180
195
|
let diff = await state->InMemoryStore.prepareRollbackDiff(
|
|
@@ -183,15 +198,19 @@ and executeRollback = async (
|
|
|
183
198
|
~progressBlockNumberByChainId=newProgressBlockNumberPerChain,
|
|
184
199
|
)
|
|
185
200
|
|
|
201
|
+
rolledBackChains->Array.forEach(chain => {
|
|
202
|
+
logger->Logging.childInfo({
|
|
203
|
+
"msg": "Rollbacked",
|
|
204
|
+
"chainId": chain["chainId"],
|
|
205
|
+
"fromBlock": chain["fromBlock"],
|
|
206
|
+
"toBlock": chain["toBlock"],
|
|
207
|
+
"rollbackedEvents": chain["rollbackedEvents"],
|
|
208
|
+
})
|
|
209
|
+
})
|
|
186
210
|
logger->Logging.childTrace({
|
|
187
|
-
"msg": "
|
|
188
|
-
"
|
|
189
|
-
|
|
190
|
-
"upserted": diff["setEntities"],
|
|
191
|
-
},
|
|
192
|
-
"rollbackedEvents": rollbackedProcessedEvents.contents,
|
|
193
|
-
"beforeCheckpointId": state->IndexerState.committedCheckpointId,
|
|
194
|
-
"targetCheckpointId": rollbackTargetCheckpointId,
|
|
211
|
+
"msg": "Rollback entity changes",
|
|
212
|
+
"deleted": diff["deletedEntities"],
|
|
213
|
+
"upserted": diff["setEntities"],
|
|
195
214
|
})
|
|
196
215
|
Prometheus.RollbackSuccess.increment(
|
|
197
216
|
~timeSeconds=Performance.secondsSince(startTime),
|
package/src/Rollback.res.mjs
CHANGED
|
@@ -33,8 +33,7 @@ async function getLastKnownValidBlock(chainState, reorgBlockNumber, isRealtime)
|
|
|
33
33
|
|
|
34
34
|
async function executeRollback(state, reorgChain, rollbackTargetBlockNumber, scheduleFetch, scheduleProcessing) {
|
|
35
35
|
let startTime = Performance.now();
|
|
36
|
-
let
|
|
37
|
-
let logger = Logging.createChildFrom(ChainState.logger(chainState), {
|
|
36
|
+
let logger = Logging.createChild({
|
|
38
37
|
action: "Rollback",
|
|
39
38
|
reorgChain: reorgChain,
|
|
40
39
|
targetBlockNumber: rollbackTargetBlockNumber
|
|
@@ -55,20 +54,34 @@ async function executeRollback(state, reorgChain, rollbackTargetBlockNumber, sch
|
|
|
55
54
|
eventsProcessedDiffByChain[diff.chain_id] = eventsProcessedDiff;
|
|
56
55
|
newProgressBlockNumberPerChain[diff.chain_id] = rollbackTargetCheckpointId === 0n && diff.chain_id === reorgChain ? Primitive_int.min(diff.new_progress_block_number, rollbackTargetBlockNumber) : diff.new_progress_block_number;
|
|
57
56
|
}
|
|
57
|
+
let rolledBackChains = [];
|
|
58
58
|
Utils.Dict.forEach(IndexerState.chainStates(state), cs => {
|
|
59
59
|
let chainId = ChainState.chainConfig(cs).id;
|
|
60
|
+
let fromBlock = ChainState.committedProgressBlockNumber(cs);
|
|
60
61
|
ChainState.rollback(cs, newProgressBlockNumberPerChain[chainId], eventsProcessedDiffByChain[chainId], rollbackTargetBlockNumber, chainId === reorgChain);
|
|
62
|
+
let toBlock = ChainState.committedProgressBlockNumber(cs);
|
|
63
|
+
if (fromBlock !== toBlock) {
|
|
64
|
+
rolledBackChains.push({
|
|
65
|
+
chainId: chainId,
|
|
66
|
+
fromBlock: fromBlock,
|
|
67
|
+
toBlock: toBlock,
|
|
68
|
+
rollbackedEvents: Stdlib_Option.getOr(eventsProcessedDiffByChain[chainId], 0)
|
|
69
|
+
});
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
61
72
|
});
|
|
62
73
|
let diff$1 = await InMemoryStore.prepareRollbackDiff(state, rollbackTargetCheckpointId, IndexerState.committedCheckpointId(state) + 1n, newProgressBlockNumberPerChain);
|
|
74
|
+
rolledBackChains.forEach(chain => Logging.childInfo(logger, {
|
|
75
|
+
msg: "Rollbacked",
|
|
76
|
+
chainId: chain.chainId,
|
|
77
|
+
fromBlock: chain.fromBlock,
|
|
78
|
+
toBlock: chain.toBlock,
|
|
79
|
+
rollbackedEvents: chain.rollbackedEvents
|
|
80
|
+
}));
|
|
63
81
|
Logging.childTrace(logger, {
|
|
64
|
-
msg: "
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
upserted: diff$1.setEntities
|
|
68
|
-
},
|
|
69
|
-
rollbackedEvents: rollbackedProcessedEvents,
|
|
70
|
-
beforeCheckpointId: IndexerState.committedCheckpointId(state),
|
|
71
|
-
targetCheckpointId: rollbackTargetCheckpointId
|
|
82
|
+
msg: "Rollback entity changes",
|
|
83
|
+
deleted: diff$1.deletedEntities,
|
|
84
|
+
upserted: diff$1.setEntities
|
|
72
85
|
});
|
|
73
86
|
Prometheus.RollbackSuccess.increment(Performance.secondsSince(startTime), rollbackedProcessedEvents);
|
|
74
87
|
IndexerState.completeRollback(state, eventsProcessedDiffByChain);
|
|
@@ -97,7 +110,7 @@ async function rollback(state, scheduleFetch, scheduleProcessing, scheduleRollba
|
|
|
97
110
|
return scheduleRollback();
|
|
98
111
|
case "FoundReorgDepth" :
|
|
99
112
|
if (IndexerState.isProcessing(state)) {
|
|
100
|
-
return Logging.
|
|
113
|
+
return Logging.trace("Waiting for batch to finish processing before executing rollback");
|
|
101
114
|
} else {
|
|
102
115
|
return await executeRollback(state, match.chain, match.rollbackTargetBlockNumber, scheduleFetch, scheduleProcessing);
|
|
103
116
|
}
|
|
@@ -488,6 +488,16 @@ let make = (
|
|
|
488
488
|
|
|
489
489
|
switch maybeConfig {
|
|
490
490
|
| None => ()
|
|
491
|
+
// Exclude instructions from failed transactions. HyperSync has no
|
|
492
|
+
// server-side predicate to filter instructions by parent-transaction
|
|
493
|
+
// success (`InstructionSelection` only exposes `is_inner`, and
|
|
494
|
+
// instruction/transaction selections union at block level rather than
|
|
495
|
+
// joining), so we filter client-side on the `isCommitted` flag HyperSync
|
|
496
|
+
// already delivers on every instruction row (a required column, zero extra
|
|
497
|
+
// bandwidth). When HyperSync adds a server-side `is_committed` predicate the
|
|
498
|
+
// query can push this down and the client-side check becomes a redundant
|
|
499
|
+
// safety net.
|
|
500
|
+
| Some(_) if !instr.isCommitted => ()
|
|
491
501
|
| Some(onEventRegistration) =>
|
|
492
502
|
let eventConfig =
|
|
493
503
|
onEventRegistration.eventConfig->(
|
|
@@ -415,7 +415,7 @@ function make(param) {
|
|
|
415
415
|
let byteLengths = Stdlib_Option.getOr(orderingByProgram[instr.programId], []);
|
|
416
416
|
let contractAddress = instr.programId;
|
|
417
417
|
let maybeConfig = probeRouter(eventRouter, programId, instr, byteLengths, contractAddress, contractNameByAddress);
|
|
418
|
-
if (maybeConfig !== undefined) {
|
|
418
|
+
if (maybeConfig !== undefined && instr.isCommitted) {
|
|
419
419
|
let eventConfig = maybeConfig.eventConfig;
|
|
420
420
|
let logKey = instr.slot.toString() + ":" + instr.transactionIndex.toString() + ":" + serializeInstructionAddress(instr.instructionAddress);
|
|
421
421
|
let maybeLogs = Stdlib_Option.map(logsByKey[logKey], logs => logs.map(log => ({
|