envio 3.3.0-alpha.7 → 3.3.0-alpha.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +23 -5
- package/package.json +6 -6
- package/src/Api.res +1 -1
- package/src/Api.res.mjs +1 -1
- package/src/ChainFetching.res +21 -10
- package/src/ChainFetching.res.mjs +11 -10
- package/src/ChainState.res +328 -165
- package/src/ChainState.res.mjs +205 -100
- package/src/ChainState.resi +16 -7
- package/src/Config.res +11 -27
- package/src/Config.res.mjs +8 -7
- package/src/Core.res +7 -0
- package/src/CrossChainState.res +53 -80
- package/src/CrossChainState.res.mjs +43 -59
- package/src/CrossChainState.resi +1 -1
- package/src/Ecosystem.res +3 -3
- package/src/Ecosystem.res.mjs +3 -3
- package/src/Envio.res +14 -9
- package/src/EventConfigBuilder.res +105 -56
- package/src/EventConfigBuilder.res.mjs +69 -33
- package/src/EventProcessing.res +19 -15
- package/src/EventProcessing.res.mjs +13 -12
- package/src/FetchState.res +375 -171
- package/src/FetchState.res.mjs +249 -251
- package/src/HandlerLoader.res +7 -112
- package/src/HandlerLoader.res.mjs +1 -87
- package/src/HandlerRegister.res +209 -6
- package/src/HandlerRegister.res.mjs +90 -5
- package/src/HandlerRegister.resi +13 -2
- package/src/IndexerState.res +6 -3
- package/src/IndexerState.res.mjs +3 -3
- package/src/IndexerState.resi +1 -1
- package/src/IndexingAddresses.res +10 -7
- package/src/IndexingAddresses.res.mjs +8 -7
- package/src/IndexingAddresses.resi +3 -1
- package/src/Internal.res +82 -36
- package/src/Internal.res.mjs +11 -1
- package/src/Main.res +22 -23
- package/src/Main.res.mjs +15 -17
- package/src/Metrics.res +43 -2
- package/src/Metrics.res.mjs +39 -3
- package/src/Prometheus.res +0 -35
- package/src/Prometheus.res.mjs +33 -68
- package/src/RawEvent.res +7 -2
- package/src/RawEvent.res.mjs +4 -4
- package/src/SimulateItems.res +20 -7
- package/src/SimulateItems.res.mjs +7 -11
- package/src/TestIndexer.res +1 -1
- package/src/TestIndexer.res.mjs +1 -1
- package/src/sources/BlockStore.res +46 -0
- package/src/sources/BlockStore.res.mjs +24 -0
- package/src/sources/EventRouter.res +19 -12
- package/src/sources/EventRouter.res.mjs +7 -5
- package/src/sources/Evm.res +54 -4
- package/src/sources/Evm.res.mjs +43 -4
- package/src/sources/EvmChain.res +14 -18
- package/src/sources/EvmChain.res.mjs +12 -13
- package/src/sources/FieldMask.res +39 -0
- package/src/sources/FieldMask.res.mjs +42 -0
- package/src/sources/Fuel.res +5 -2
- package/src/sources/Fuel.res.mjs +4 -3
- package/src/sources/HyperFuelSource.res +31 -40
- package/src/sources/HyperFuelSource.res.mjs +52 -44
- package/src/sources/HyperSync.res +31 -9
- package/src/sources/HyperSync.res.mjs +47 -31
- package/src/sources/HyperSync.resi +10 -3
- package/src/sources/HyperSyncClient.res +15 -4
- package/src/sources/HyperSyncHeightStream.res +1 -8
- package/src/sources/HyperSyncHeightStream.res.mjs +0 -2
- package/src/sources/HyperSyncSource.res +37 -58
- package/src/sources/HyperSyncSource.res.mjs +42 -42
- package/src/sources/RpcSource.res +91 -82
- package/src/sources/RpcSource.res.mjs +76 -45
- package/src/sources/RpcWebSocketHeightStream.res +0 -5
- package/src/sources/RpcWebSocketHeightStream.res.mjs +0 -2
- package/src/sources/SimulateSource.res +5 -3
- package/src/sources/SimulateSource.res.mjs +12 -4
- package/src/sources/Source.res +19 -5
- package/src/sources/SourceManager.res +62 -6
- package/src/sources/SourceManager.res.mjs +55 -8
- package/src/sources/SourceManager.resi +10 -0
- package/src/sources/Svm.res +14 -10
- package/src/sources/Svm.res.mjs +23 -6
- package/src/sources/SvmHyperSyncClient.res +5 -6
- package/src/sources/SvmHyperSyncSource.res +82 -41
- package/src/sources/SvmHyperSyncSource.res.mjs +86 -41
- package/src/sources/TransactionStore.res +6 -107
- package/src/sources/TransactionStore.res.mjs +5 -86
- package/svm.schema.json +19 -0
|
@@ -13,10 +13,16 @@ function make(items, endBlock, chain) {
|
|
|
13
13
|
poweredByHyperSync: false,
|
|
14
14
|
pollingInterval: 0,
|
|
15
15
|
getBlockHashes: (param, param$1) => Promise.resolve({
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
result: {
|
|
17
|
+
TAG: "Ok",
|
|
18
|
+
_0: []
|
|
19
|
+
},
|
|
20
|
+
requestStats: []
|
|
21
|
+
}),
|
|
22
|
+
getHeightOrThrow: () => Promise.resolve({
|
|
23
|
+
height: Primitive_int.max(endBlock, 1),
|
|
24
|
+
requestStats: []
|
|
18
25
|
}),
|
|
19
|
-
getHeightOrThrow: () => Promise.resolve(Primitive_int.max(endBlock, 1)),
|
|
20
26
|
getItemsOrThrow: (param, param$1, param$2, param$3, param$4, param$5, param$6, param$7, param$8, param$9) => {
|
|
21
27
|
let result = delivered.contents ? [] : (delivered.contents = true, items);
|
|
22
28
|
let reportedHeight = Primitive_int.max(endBlock, 1);
|
|
@@ -25,12 +31,14 @@ function make(items, endBlock, chain) {
|
|
|
25
31
|
blockHashes: [],
|
|
26
32
|
parsedQueueItems: result,
|
|
27
33
|
transactionStore: undefined,
|
|
34
|
+
blockStore: undefined,
|
|
28
35
|
fromBlockQueried: 0,
|
|
29
36
|
latestFetchedBlockNumber: reportedHeight,
|
|
30
37
|
latestFetchedBlockTimestamp: 0,
|
|
31
38
|
stats: {
|
|
32
39
|
"total time elapsed (s)": 0
|
|
33
|
-
}
|
|
40
|
+
},
|
|
41
|
+
requestStats: []
|
|
34
42
|
});
|
|
35
43
|
},
|
|
36
44
|
simulateItems: items
|
package/src/sources/Source.res
CHANGED
|
@@ -7,6 +7,11 @@ type blockRangeFetchStats = {
|
|
|
7
7
|
@as("page fetch time (s)") pageFetchTime?: float,
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
// A single backend request a source method actually made (cache/dedup hits
|
|
11
|
+
// aren't requests), with the time it took. SourceManager aggregates these
|
|
12
|
+
// per (source, method) into the envio_source_request_* metrics.
|
|
13
|
+
type requestStat = {method: string, seconds: float}
|
|
14
|
+
|
|
10
15
|
/**
|
|
11
16
|
Thes response returned from a block range fetch
|
|
12
17
|
*/
|
|
@@ -22,10 +27,22 @@ type blockRangeFetchResponse = {
|
|
|
22
27
|
// transactionIndex); merged into the chain's store on apply. `None` for
|
|
23
28
|
// sources that keep the transaction inline on the payload (RPC/Fuel/Simulate).
|
|
24
29
|
transactionStore: option<TransactionStore.t>,
|
|
30
|
+
// Page of blocks for this response's items, keyed by block number; merged into
|
|
31
|
+
// the chain's store on apply. `None` for sources that keep the block fully
|
|
32
|
+
// inline on the payload (RPC/Fuel/Simulate).
|
|
33
|
+
blockStore: option<BlockStore.t>,
|
|
25
34
|
fromBlockQueried: int,
|
|
26
35
|
latestFetchedBlockNumber: int,
|
|
27
36
|
latestFetchedBlockTimestamp: int,
|
|
28
37
|
stats: blockRangeFetchStats,
|
|
38
|
+
requestStats: array<requestStat>,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
type getHeightResponse = {height: int, requestStats: array<requestStat>}
|
|
42
|
+
|
|
43
|
+
type getBlockHashesResponse = {
|
|
44
|
+
result: result<array<ReorgDetection.blockDataWithTimestamp>, exn>,
|
|
45
|
+
requestStats: array<requestStat>,
|
|
29
46
|
}
|
|
30
47
|
|
|
31
48
|
type getItemsRetry =
|
|
@@ -51,11 +68,8 @@ type t = {
|
|
|
51
68
|
poweredByHyperSync: bool,
|
|
52
69
|
/* Frequency (in ms) used when polling for new events on this network. */
|
|
53
70
|
pollingInterval: int,
|
|
54
|
-
getBlockHashes: (
|
|
55
|
-
|
|
56
|
-
~logger: Pino.t,
|
|
57
|
-
) => promise<result<array<ReorgDetection.blockDataWithTimestamp>, exn>>,
|
|
58
|
-
getHeightOrThrow: unit => promise<int>,
|
|
71
|
+
getBlockHashes: (~blockNumbers: array<int>, ~logger: Pino.t) => promise<getBlockHashesResponse>,
|
|
72
|
+
getHeightOrThrow: unit => promise<getHeightResponse>,
|
|
59
73
|
getItemsOrThrow: (
|
|
60
74
|
~fromBlock: int,
|
|
61
75
|
~toBlock: option<int>,
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
type sourceManagerStatus = Idle | WaitingForNewBlock | Querieng
|
|
2
2
|
|
|
3
|
+
// Cumulative per-method request count/time for a source, aggregated from the
|
|
4
|
+
// requestStat arrays returned by its methods. Rendered into
|
|
5
|
+
// envio_source_request_* by Metrics.renderSourceRequests.
|
|
6
|
+
type requestStatAgg = {mutable count: int, mutable seconds: float}
|
|
7
|
+
|
|
3
8
|
type sourceState = {
|
|
4
9
|
source: Source.t,
|
|
5
10
|
mutable knownHeight: int,
|
|
@@ -9,6 +14,28 @@ type sourceState = {
|
|
|
9
14
|
// Timestamp (ms) when this source last failed during executeQuery.
|
|
10
15
|
// Used to decide when to attempt recovery to this source.
|
|
11
16
|
mutable lastFailedAt: option<float>,
|
|
17
|
+
requestStats: dict<requestStatAgg>,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let recordRequestStats = (sourceState: sourceState, requestStats: array<Source.requestStat>) => {
|
|
21
|
+
requestStats->Array.forEach(({method, seconds}) => {
|
|
22
|
+
switch sourceState.requestStats->Utils.Dict.dangerouslyGetNonOption(method) {
|
|
23
|
+
| Some(agg) =>
|
|
24
|
+
agg.count = agg.count + 1
|
|
25
|
+
agg.seconds = agg.seconds +. seconds
|
|
26
|
+
| None => sourceState.requestStats->Dict.set(method, {count: 1, seconds})
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Flattened (source, method) aggregates for Metrics.renderSourceRequests to
|
|
32
|
+
// inline into the /metrics response.
|
|
33
|
+
type requestStatSample = {
|
|
34
|
+
sourceName: string,
|
|
35
|
+
chainId: int,
|
|
36
|
+
method: string,
|
|
37
|
+
count: int,
|
|
38
|
+
seconds: float,
|
|
12
39
|
}
|
|
13
40
|
|
|
14
41
|
// Encapsulates the fetching logic for a chain's sources.
|
|
@@ -46,6 +73,25 @@ type t = {
|
|
|
46
73
|
|
|
47
74
|
let getActiveSource = sourceManager => sourceManager.activeSource
|
|
48
75
|
|
|
76
|
+
let getRequestStatSamples = (sourceManager: t): array<requestStatSample> => {
|
|
77
|
+
let samples = []
|
|
78
|
+
sourceManager.sourcesState->Array.forEach(sourceState => {
|
|
79
|
+
let chainId = sourceState.source.chain->ChainMap.Chain.toChainId
|
|
80
|
+
sourceState.requestStats->Utils.Dict.forEachWithKey((agg, method) => {
|
|
81
|
+
samples
|
|
82
|
+
->Array.push({
|
|
83
|
+
sourceName: sourceState.source.name,
|
|
84
|
+
chainId,
|
|
85
|
+
method,
|
|
86
|
+
count: agg.count,
|
|
87
|
+
seconds: agg.seconds,
|
|
88
|
+
})
|
|
89
|
+
->ignore
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
samples
|
|
93
|
+
}
|
|
94
|
+
|
|
49
95
|
// Partition queries currently in flight on this chain's sources. Summed across
|
|
50
96
|
// chains by CrossChainState to enforce the indexer-wide concurrency budget.
|
|
51
97
|
let inFlightCount = sourceManager => sourceManager.fetchingPartitionsCount
|
|
@@ -187,6 +233,7 @@ let make = (
|
|
|
187
233
|
pendingHeightResolvers: [],
|
|
188
234
|
disabled: false,
|
|
189
235
|
lastFailedAt: None,
|
|
236
|
+
requestStats: Dict.make(),
|
|
190
237
|
}),
|
|
191
238
|
activeSource: initialActiveSource,
|
|
192
239
|
waitingForNewBlockStateId: None,
|
|
@@ -344,7 +391,9 @@ let getSourceNewHeight = async (
|
|
|
344
391
|
let h = ref(initialHeight)
|
|
345
392
|
while h.contents <= knownHeight && !(newHeight.contents > initialHeight) {
|
|
346
393
|
try {
|
|
347
|
-
|
|
394
|
+
let res = await source.getHeightOrThrow()
|
|
395
|
+
sourceState->recordRequestStats(res.requestStats)
|
|
396
|
+
h := res.height
|
|
348
397
|
} catch {
|
|
349
398
|
| _ => ()
|
|
350
399
|
}
|
|
@@ -363,7 +412,9 @@ let getSourceNewHeight = async (
|
|
|
363
412
|
| None =>
|
|
364
413
|
// No subscription, use REST polling
|
|
365
414
|
try {
|
|
366
|
-
let
|
|
415
|
+
let res = await source.getHeightOrThrow()
|
|
416
|
+
sourceState->recordRequestStats(res.requestStats)
|
|
417
|
+
let height = res.height
|
|
367
418
|
|
|
368
419
|
newHeight := height
|
|
369
420
|
if height <= knownHeight {
|
|
@@ -385,6 +436,9 @@ let getSourceNewHeight = async (
|
|
|
385
436
|
}
|
|
386
437
|
})
|
|
387
438
|
sourceState.unsubscribe = Some(unsubscribe)
|
|
439
|
+
// Count a subscription (re)start rather than every pushed height —
|
|
440
|
+
// there's no request/response to time here.
|
|
441
|
+
sourceState->recordRequestStats([{Source.method: "heightSubscription", seconds: 0.}])
|
|
388
442
|
| _ =>
|
|
389
443
|
// Slowdown polling when the chain isn't progressing
|
|
390
444
|
let pollingInterval = if reducedPolling {
|
|
@@ -680,16 +734,17 @@ let executeQuery = async (
|
|
|
680
734
|
~partitionId=query.partitionId,
|
|
681
735
|
~knownHeight,
|
|
682
736
|
~selection=query.selection,
|
|
683
|
-
// Ceil (not truncate) so a sub-1
|
|
737
|
+
// Ceil (not truncate) so a sub-1 target from a sparse partition over a
|
|
684
738
|
// small range doesn't round down to 0 and ask the backend to cap the
|
|
685
739
|
// response at nothing — 0 items is indistinguishable from "no signal".
|
|
686
740
|
~itemsTarget={
|
|
687
|
-
let
|
|
688
|
-
|
|
741
|
+
let target = query.itemsTarget->Math.ceil->Float.toInt
|
|
742
|
+
target > 0 ? target : FetchState.minItemsTarget->Float.toInt
|
|
689
743
|
},
|
|
690
744
|
~retry,
|
|
691
745
|
~logger,
|
|
692
746
|
)
|
|
747
|
+
sourceState->recordRequestStats(response.requestStats)
|
|
693
748
|
sourceState.lastFailedAt = None
|
|
694
749
|
responseRef := Some(response)
|
|
695
750
|
} catch {
|
|
@@ -829,7 +884,8 @@ let getBlockHashes = async (sourceManager: t, ~blockNumbers: array<int>, ~isReal
|
|
|
829
884
|
|
|
830
885
|
try {
|
|
831
886
|
let res = await source.getBlockHashes(~blockNumbers, ~logger)
|
|
832
|
-
|
|
887
|
+
sourceState->recordRequestStats(res.requestStats)
|
|
888
|
+
switch res.result {
|
|
833
889
|
| Ok(data) =>
|
|
834
890
|
sourceState.lastFailedAt = None
|
|
835
891
|
responseRef := Some(data)
|
|
@@ -14,10 +14,44 @@ import * as Primitive_float from "@rescript/runtime/lib/es6/Primitive_float.js";
|
|
|
14
14
|
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
15
15
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
16
16
|
|
|
17
|
+
function recordRequestStats(sourceState, requestStats) {
|
|
18
|
+
requestStats.forEach(param => {
|
|
19
|
+
let seconds = param.seconds;
|
|
20
|
+
let method = param.method;
|
|
21
|
+
let agg = sourceState.requestStats[method];
|
|
22
|
+
if (agg !== undefined) {
|
|
23
|
+
agg.count = agg.count + 1 | 0;
|
|
24
|
+
agg.seconds = agg.seconds + seconds;
|
|
25
|
+
} else {
|
|
26
|
+
sourceState.requestStats[method] = {
|
|
27
|
+
count: 1,
|
|
28
|
+
seconds: seconds
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
17
34
|
function getActiveSource(sourceManager) {
|
|
18
35
|
return sourceManager.activeSource;
|
|
19
36
|
}
|
|
20
37
|
|
|
38
|
+
function getRequestStatSamples(sourceManager) {
|
|
39
|
+
let samples = [];
|
|
40
|
+
sourceManager.sourcesState.forEach(sourceState => {
|
|
41
|
+
let chainId = sourceState.source.chain;
|
|
42
|
+
Utils.Dict.forEachWithKey(sourceState.requestStats, (agg, method) => {
|
|
43
|
+
samples.push({
|
|
44
|
+
sourceName: sourceState.source.name,
|
|
45
|
+
chainId: chainId,
|
|
46
|
+
method: method,
|
|
47
|
+
count: agg.count,
|
|
48
|
+
seconds: agg.seconds
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
return samples;
|
|
53
|
+
}
|
|
54
|
+
|
|
21
55
|
function inFlightCount(sourceManager) {
|
|
22
56
|
return sourceManager.fetchingPartitionsCount;
|
|
23
57
|
}
|
|
@@ -137,7 +171,8 @@ function make(sources, isRealtime, newBlockStallTimeoutOpt, newBlockStallTimeout
|
|
|
137
171
|
unsubscribe: undefined,
|
|
138
172
|
pendingHeightResolvers: [],
|
|
139
173
|
disabled: false,
|
|
140
|
-
lastFailedAt: undefined
|
|
174
|
+
lastFailedAt: undefined,
|
|
175
|
+
requestStats: {}
|
|
141
176
|
})),
|
|
142
177
|
statusStart: Performance.now(),
|
|
143
178
|
status: "Idle",
|
|
@@ -263,7 +298,9 @@ async function getSourceNewHeight(sourceManager, sourceState, knownHeight, stall
|
|
|
263
298
|
let h = initialHeight;
|
|
264
299
|
while (h <= knownHeight && newHeight.contents <= initialHeight) {
|
|
265
300
|
try {
|
|
266
|
-
|
|
301
|
+
let res = await source.getHeightOrThrow();
|
|
302
|
+
recordRequestStats(sourceState, res.requestStats);
|
|
303
|
+
h = res.height;
|
|
267
304
|
} catch (exn) {
|
|
268
305
|
|
|
269
306
|
}
|
|
@@ -282,7 +319,9 @@ async function getSourceNewHeight(sourceManager, sourceState, knownHeight, stall
|
|
|
282
319
|
}
|
|
283
320
|
} else {
|
|
284
321
|
try {
|
|
285
|
-
let
|
|
322
|
+
let res = await source.getHeightOrThrow();
|
|
323
|
+
recordRequestStats(sourceState, res.requestStats);
|
|
324
|
+
let height$1 = res.height;
|
|
286
325
|
newHeight.contents = height$1;
|
|
287
326
|
if (height$1 <= knownHeight) {
|
|
288
327
|
retry = 0;
|
|
@@ -299,6 +338,10 @@ async function getSourceNewHeight(sourceManager, sourceState, knownHeight, stall
|
|
|
299
338
|
resolvers.forEach(resolve => resolve(newHeight));
|
|
300
339
|
});
|
|
301
340
|
sourceState.unsubscribe = unsubscribe;
|
|
341
|
+
recordRequestStats(sourceState, [{
|
|
342
|
+
method: "heightSubscription",
|
|
343
|
+
seconds: 0
|
|
344
|
+
}]);
|
|
302
345
|
} else {
|
|
303
346
|
exit = 1;
|
|
304
347
|
}
|
|
@@ -500,8 +543,9 @@ async function executeQuery(sourceManager, query, knownHeight, isRealtime) {
|
|
|
500
543
|
retry: retry
|
|
501
544
|
});
|
|
502
545
|
try {
|
|
503
|
-
let
|
|
504
|
-
let response = await source.getItemsOrThrow(query.fromBlock, toBlock, query.addressesByContractName, FetchState.deriveContractNameByAddress(query.addressesByContractName), knownHeight, query.partitionId, query.selection,
|
|
546
|
+
let target = Math.ceil(query.itemsTarget) | 0;
|
|
547
|
+
let response = await source.getItemsOrThrow(query.fromBlock, toBlock, query.addressesByContractName, FetchState.deriveContractNameByAddress(query.addressesByContractName), knownHeight, query.partitionId, query.selection, target > 0 ? target : FetchState.minItemsTarget | 0, retry, logger$2);
|
|
548
|
+
recordRequestStats(sourceState, response.requestStats);
|
|
505
549
|
sourceState.lastFailedAt = undefined;
|
|
506
550
|
responseRef = response;
|
|
507
551
|
} catch (raw_error) {
|
|
@@ -637,11 +681,13 @@ async function getBlockHashes(sourceManager, blockNumbers, isRealtime) {
|
|
|
637
681
|
});
|
|
638
682
|
try {
|
|
639
683
|
let res = await source.getBlockHashes(blockNumbers, logger$1);
|
|
640
|
-
|
|
684
|
+
recordRequestStats(sourceState, res.requestStats);
|
|
685
|
+
let data = res.result;
|
|
686
|
+
if (data.TAG === "Ok") {
|
|
641
687
|
sourceState.lastFailedAt = undefined;
|
|
642
|
-
responseRef =
|
|
688
|
+
responseRef = data._0;
|
|
643
689
|
} else {
|
|
644
|
-
throw
|
|
690
|
+
throw data._0;
|
|
645
691
|
}
|
|
646
692
|
} catch (raw_exn) {
|
|
647
693
|
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
@@ -673,6 +719,7 @@ export {
|
|
|
673
719
|
getSourceRole,
|
|
674
720
|
make,
|
|
675
721
|
getActiveSource,
|
|
722
|
+
getRequestStatSamples,
|
|
676
723
|
inFlightCount,
|
|
677
724
|
onReorg,
|
|
678
725
|
dispatch,
|
|
@@ -21,6 +21,16 @@ let make: (
|
|
|
21
21
|
|
|
22
22
|
let getActiveSource: t => Source.t
|
|
23
23
|
|
|
24
|
+
type requestStatSample = {
|
|
25
|
+
sourceName: string,
|
|
26
|
+
chainId: int,
|
|
27
|
+
method: string,
|
|
28
|
+
count: int,
|
|
29
|
+
seconds: float,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let getRequestStatSamples: t => array<requestStatSample>
|
|
33
|
+
|
|
24
34
|
let inFlightCount: t => int
|
|
25
35
|
|
|
26
36
|
let onReorg: (t, ~rollbackTargetBlock: int) => unit
|
package/src/sources/Svm.res
CHANGED
|
@@ -16,6 +16,17 @@ let transactionFields =
|
|
|
16
16
|
// Computed per event at config build and cached on the event config.
|
|
17
17
|
let eventTransactionFieldMask = TransactionStore.makeMaskFn(transactionFields)
|
|
18
18
|
|
|
19
|
+
// Ordered block field names. The index of each is the field code shared with the
|
|
20
|
+
// Rust store (`SvmBlockField`) — keep this order in sync.
|
|
21
|
+
let blockFields = ["slot", "time", "hash", "height", "parentSlot", "parentHash"]
|
|
22
|
+
|
|
23
|
+
// `slot`/`time`/`hash` are always included; every other block field is opt-in
|
|
24
|
+
// via `field_selection.block_fields`. All are materialised from the store.
|
|
25
|
+
//
|
|
26
|
+
// One instruction's selected block fields → store selection bitmask. Computed per
|
|
27
|
+
// event at config build and cached on the event config.
|
|
28
|
+
let eventBlockFieldMask = BlockStore.makeMaskFn(blockFields)
|
|
29
|
+
|
|
19
30
|
let make = (~logger: Pino.t): Ecosystem.t => {
|
|
20
31
|
name: Svm,
|
|
21
32
|
blockNumberName: "height",
|
|
@@ -37,8 +48,8 @@ let make = (~logger: Pino.t): Ecosystem.t => {
|
|
|
37
48
|
Logging.createChildFrom(
|
|
38
49
|
~logger,
|
|
39
50
|
~params={
|
|
40
|
-
"program": eventItem.eventConfig.contractName,
|
|
41
|
-
"instruction": eventItem.eventConfig.name,
|
|
51
|
+
"program": eventItem.onEventRegistration.eventConfig.contractName,
|
|
52
|
+
"instruction": eventItem.onEventRegistration.eventConfig.name,
|
|
42
53
|
"chainId": eventItem.chain->ChainMap.Chain.toChainId,
|
|
43
54
|
"slot": eventItem.blockNumber,
|
|
44
55
|
"programId": instruction.programId,
|
|
@@ -84,14 +95,7 @@ let makeRPCSource = (~chain, ~rpc: string, ~sourceFor: Source.sourceFor=Sync): S
|
|
|
84
95
|
let timerRef = Performance.now()
|
|
85
96
|
let height = await GetFinalizedSlot.route->Rest.fetch((), ~client)
|
|
86
97
|
let seconds = timerRef->Performance.secondsSince
|
|
87
|
-
|
|
88
|
-
Prometheus.SourceRequestCount.addSeconds(
|
|
89
|
-
~sourceName=name,
|
|
90
|
-
~chainId,
|
|
91
|
-
~method="getSlot",
|
|
92
|
-
~seconds,
|
|
93
|
-
)
|
|
94
|
-
height
|
|
98
|
+
{Source.height, requestStats: [{Source.method: "getSlot", seconds}]}
|
|
95
99
|
},
|
|
96
100
|
getItemsOrThrow: (
|
|
97
101
|
~fromBlock as _,
|
package/src/sources/Svm.res.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import * as Rest from "../vendored/Rest.res.mjs";
|
|
|
5
5
|
import * as Utils from "../Utils.res.mjs";
|
|
6
6
|
import * as Logging from "../Logging.res.mjs";
|
|
7
7
|
import * as Internal from "../Internal.res.mjs";
|
|
8
|
-
import * as
|
|
8
|
+
import * as BlockStore from "./BlockStore.res.mjs";
|
|
9
9
|
import * as Performance from "../bindings/Performance.res.mjs";
|
|
10
10
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
11
11
|
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
@@ -19,6 +19,17 @@ let cleanUpRawEventFieldsInPlace = (fields => {
|
|
|
19
19
|
|
|
20
20
|
let eventTransactionFieldMask = TransactionStore.makeMaskFn(Internal.allSvmTransactionFields);
|
|
21
21
|
|
|
22
|
+
let blockFields = [
|
|
23
|
+
"slot",
|
|
24
|
+
"time",
|
|
25
|
+
"hash",
|
|
26
|
+
"height",
|
|
27
|
+
"parentSlot",
|
|
28
|
+
"parentHash"
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
let eventBlockFieldMask = BlockStore.makeMaskFn(blockFields);
|
|
32
|
+
|
|
22
33
|
function make(logger) {
|
|
23
34
|
return {
|
|
24
35
|
name: "svm",
|
|
@@ -34,8 +45,8 @@ function make(logger) {
|
|
|
34
45
|
toEventLogger: eventItem => {
|
|
35
46
|
let instruction = eventItem.payload;
|
|
36
47
|
return Logging.createChildFrom(logger, {
|
|
37
|
-
program: eventItem.eventConfig.contractName,
|
|
38
|
-
instruction: eventItem.eventConfig.name,
|
|
48
|
+
program: eventItem.onEventRegistration.eventConfig.contractName,
|
|
49
|
+
instruction: eventItem.onEventRegistration.eventConfig.name,
|
|
39
50
|
chainId: eventItem.chain,
|
|
40
51
|
slot: eventItem.blockNumber,
|
|
41
52
|
programId: instruction.programId
|
|
@@ -72,9 +83,13 @@ function makeRPCSource(chain, rpc, sourceForOpt) {
|
|
|
72
83
|
let timerRef = Performance.now();
|
|
73
84
|
let height = await Rest.fetch(route, undefined, client);
|
|
74
85
|
let seconds = Performance.secondsSince(timerRef);
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
86
|
+
return {
|
|
87
|
+
height: height,
|
|
88
|
+
requestStats: [{
|
|
89
|
+
method: "getSlot",
|
|
90
|
+
seconds: seconds
|
|
91
|
+
}]
|
|
92
|
+
};
|
|
78
93
|
},
|
|
79
94
|
getItemsOrThrow: (param, param$1, param$2, param$3, param$4, param$5, param$6, param$7, param$8, param$9) => Stdlib_JsError.throwWithMessage("Svm does not support getting items")
|
|
80
95
|
};
|
|
@@ -86,6 +101,8 @@ export {
|
|
|
86
101
|
cleanUpRawEventFieldsInPlace,
|
|
87
102
|
transactionFields,
|
|
88
103
|
eventTransactionFieldMask,
|
|
104
|
+
blockFields,
|
|
105
|
+
eventBlockFieldMask,
|
|
89
106
|
make,
|
|
90
107
|
GetFinalizedSlot,
|
|
91
108
|
makeRPCSource,
|
|
@@ -138,13 +138,12 @@ module QueryTypes = {
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
module ResponseTypes = {
|
|
141
|
+
// Lean per-slot header for reorg detection and each item's slot/time; the
|
|
142
|
+
// selectable fields live in the block store and are materialised on demand.
|
|
141
143
|
type block = {
|
|
142
144
|
slot: int,
|
|
143
145
|
blockhash: string,
|
|
144
|
-
parentSlot?: int,
|
|
145
|
-
parentBlockhash?: string,
|
|
146
146
|
blockTime?: int,
|
|
147
|
-
blockHeight?: int,
|
|
148
147
|
}
|
|
149
148
|
|
|
150
149
|
/// Borsh-decoded view attached by the Rust client. `argsJson`/`accountsJson`
|
|
@@ -206,9 +205,9 @@ type queryResponse = ResponseTypes.queryResponse
|
|
|
206
205
|
|
|
207
206
|
type t = {
|
|
208
207
|
getHeight: unit => promise<int>,
|
|
209
|
-
// Returns the response plus
|
|
210
|
-
// keyed by (slot, transactionIndex), materialised at batch prep.
|
|
211
|
-
get: (~query: query) => promise<(queryResponse, TransactionStore.t)>,
|
|
208
|
+
// Returns the response plus pages of raw transactions and blocks (kept in
|
|
209
|
+
// Rust), keyed by (slot, transactionIndex) / slot, materialised at batch prep.
|
|
210
|
+
get: (~query: query) => promise<(queryResponse, TransactionStore.t, BlockStore.t)>,
|
|
212
211
|
}
|
|
213
212
|
|
|
214
213
|
@send
|