envio 3.3.0-alpha.3 → 3.3.0-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/evm.schema.json +10 -0
- package/package.json +7 -7
- package/src/Batch.res.mjs +1 -1
- package/src/Bin.res +3 -0
- package/src/Bin.res.mjs +4 -0
- package/src/ChainFetching.res +1 -3
- package/src/ChainFetching.res.mjs +3 -3
- package/src/ChainState.res +44 -19
- package/src/ChainState.res.mjs +25 -23
- package/src/ChainState.resi +3 -3
- package/src/Config.res +3 -0
- package/src/Config.res.mjs +3 -1
- package/src/Core.res +0 -3
- package/src/Ecosystem.res +0 -4
- package/src/EventConfigBuilder.res +3 -0
- package/src/EventConfigBuilder.res.mjs +7 -1
- package/src/FetchState.res +73 -160
- package/src/FetchState.res.mjs +120 -222
- package/src/IndexingAddresses.res +105 -0
- package/src/IndexingAddresses.res.mjs +100 -0
- package/src/IndexingAddresses.resi +32 -0
- package/src/Internal.res +5 -0
- package/src/Main.res +21 -24
- package/src/Main.res.mjs +21 -20
- package/src/Metrics.res +33 -0
- package/src/Metrics.res.mjs +39 -0
- package/src/Prometheus.res +2 -20
- package/src/Prometheus.res.mjs +83 -95
- package/src/SimulateItems.res +4 -0
- package/src/TestIndexer.res +43 -17
- package/src/TestIndexer.res.mjs +16 -9
- package/src/bindings/Viem.res +0 -41
- package/src/bindings/Viem.res.mjs +1 -43
- package/src/sources/Evm.res +17 -39
- package/src/sources/Evm.res.mjs +8 -40
- package/src/sources/EvmChain.res +3 -1
- package/src/sources/EvmChain.res.mjs +2 -1
- package/src/sources/EvmRpcClient.res +36 -5
- package/src/sources/EvmRpcClient.res.mjs +7 -4
- package/src/sources/Fuel.res +0 -2
- package/src/sources/Fuel.res.mjs +0 -1
- package/src/sources/HyperSyncClient.res +0 -35
- package/src/sources/HyperSyncClient.res.mjs +1 -8
- package/src/sources/Rpc.res +15 -47
- package/src/sources/Rpc.res.mjs +25 -56
- package/src/sources/RpcSource.res +30 -73
- package/src/sources/RpcSource.res.mjs +24 -73
- package/src/sources/SourceManager.res +1 -1
- package/src/sources/SourceManager.res.mjs +1 -1
- package/src/sources/Svm.res +10 -17
- package/src/sources/Svm.res.mjs +6 -18
- package/src/sources/TransactionStore.res +98 -76
- package/src/sources/TransactionStore.res.mjs +49 -35
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
type indexingAddress = Internal.indexingContract
|
|
2
|
+
|
|
3
|
+
type contractConfig = {startBlock: option<int>}
|
|
4
|
+
|
|
5
|
+
type t = dict<indexingAddress>
|
|
6
|
+
|
|
7
|
+
let deriveEffectiveStartBlock = (~registrationBlock: int, ~contractStartBlock: option<int>) => {
|
|
8
|
+
Pervasives.max(Pervasives.max(registrationBlock, 0), contractStartBlock->Option.getOr(0))
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let makeContractConfigs = (~eventConfigs: array<Internal.eventConfig>): dict<contractConfig> => {
|
|
12
|
+
let contractConfigs: dict<contractConfig> = Dict.make()
|
|
13
|
+
eventConfigs->Array.forEach(ec => {
|
|
14
|
+
switch contractConfigs->Utils.Dict.dangerouslyGetNonOption(ec.contractName) {
|
|
15
|
+
| Some({startBlock}) =>
|
|
16
|
+
contractConfigs->Dict.set(
|
|
17
|
+
ec.contractName,
|
|
18
|
+
{
|
|
19
|
+
startBlock: switch (startBlock, ec.startBlock) {
|
|
20
|
+
| (Some(a), Some(b)) => Some(Pervasives.min(a, b))
|
|
21
|
+
| (Some(_) as s, None) | (None, Some(_) as s) => s
|
|
22
|
+
| (None, None) => None
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
)
|
|
26
|
+
| None =>
|
|
27
|
+
contractConfigs->Dict.set(
|
|
28
|
+
ec.contractName,
|
|
29
|
+
{
|
|
30
|
+
startBlock: ec.startBlock,
|
|
31
|
+
},
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
contractConfigs
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let makeIndexingAddress = (
|
|
39
|
+
~contract: Internal.indexingAddress,
|
|
40
|
+
~contractConfigs: dict<contractConfig>,
|
|
41
|
+
): indexingAddress => {
|
|
42
|
+
let contractStartBlock = switch contractConfigs->Utils.Dict.dangerouslyGetNonOption(
|
|
43
|
+
contract.contractName,
|
|
44
|
+
) {
|
|
45
|
+
| Some({startBlock}) => startBlock
|
|
46
|
+
| None => None
|
|
47
|
+
}
|
|
48
|
+
{
|
|
49
|
+
address: contract.address,
|
|
50
|
+
contractName: contract.contractName,
|
|
51
|
+
registrationBlock: contract.registrationBlock,
|
|
52
|
+
effectiveStartBlock: deriveEffectiveStartBlock(
|
|
53
|
+
~registrationBlock=contract.registrationBlock,
|
|
54
|
+
~contractStartBlock,
|
|
55
|
+
),
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let make = (
|
|
60
|
+
~contractConfigs: dict<contractConfig>,
|
|
61
|
+
~addresses: array<Internal.indexingAddress>,
|
|
62
|
+
): t => {
|
|
63
|
+
let indexingAddresses = Dict.make()
|
|
64
|
+
addresses->Array.forEach(contract => {
|
|
65
|
+
indexingAddresses->Dict.set(
|
|
66
|
+
contract.address->Address.toString,
|
|
67
|
+
makeIndexingAddress(~contract, ~contractConfigs),
|
|
68
|
+
)
|
|
69
|
+
})
|
|
70
|
+
indexingAddresses
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
let get = (indexingAddresses: t, address) =>
|
|
74
|
+
indexingAddresses->Utils.Dict.dangerouslyGetNonOption(address)
|
|
75
|
+
|
|
76
|
+
let size = (indexingAddresses: t) => indexingAddresses->Utils.Dict.size
|
|
77
|
+
|
|
78
|
+
let getContractAddresses = (indexingAddresses: t, ~contractName): array<Address.t> => {
|
|
79
|
+
let addresses = []
|
|
80
|
+
indexingAddresses->Utils.Dict.forEach(ia => {
|
|
81
|
+
if ia.contractName === contractName {
|
|
82
|
+
addresses->Array.push(ia.address)
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
addresses
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Underlying dict for the precompiled `clientAddressFilter` only — it does raw
|
|
89
|
+
// `indexingAddresses[srcAddress]` access in generated JS and can't take the opaque
|
|
90
|
+
// type. Don't reach for this elsewhere; use the domain accessors above.
|
|
91
|
+
let rawForFilter = (indexingAddresses: t): dict<indexingAddress> => indexingAddresses
|
|
92
|
+
|
|
93
|
+
let register = (indexingAddresses: t, additions: dict<indexingAddress>) => {
|
|
94
|
+
let _ = Utils.Dict.mergeInPlace(indexingAddresses, additions)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
let rollbackInPlace = (indexingAddresses: t, ~targetBlockNumber: int): unit => {
|
|
98
|
+
// forEachWithKey is a `for..in`, so deleting the key currently being visited is
|
|
99
|
+
// safe — it doesn't affect enumeration of the remaining keys.
|
|
100
|
+
indexingAddresses->Utils.Dict.forEachWithKey((indexingContract, address) => {
|
|
101
|
+
if indexingContract.registrationBlock > targetBlockNumber {
|
|
102
|
+
indexingAddresses->Utils.Dict.deleteInPlace(address)
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Utils from "./Utils.res.mjs";
|
|
4
|
+
import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
|
|
5
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
|
+
|
|
7
|
+
function deriveEffectiveStartBlock(registrationBlock, contractStartBlock) {
|
|
8
|
+
return Primitive_int.max(Primitive_int.max(registrationBlock, 0), Stdlib_Option.getOr(contractStartBlock, 0));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function makeContractConfigs(eventConfigs) {
|
|
12
|
+
let contractConfigs = {};
|
|
13
|
+
eventConfigs.forEach(ec => {
|
|
14
|
+
let match = contractConfigs[ec.contractName];
|
|
15
|
+
if (match !== undefined) {
|
|
16
|
+
let startBlock = match.startBlock;
|
|
17
|
+
let match$1 = ec.startBlock;
|
|
18
|
+
contractConfigs[ec.contractName] = {
|
|
19
|
+
startBlock: startBlock !== undefined ? (
|
|
20
|
+
match$1 !== undefined ? Primitive_int.min(startBlock, match$1) : startBlock
|
|
21
|
+
) : (
|
|
22
|
+
match$1 !== undefined ? match$1 : undefined
|
|
23
|
+
)
|
|
24
|
+
};
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
contractConfigs[ec.contractName] = {
|
|
28
|
+
startBlock: ec.startBlock
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
return contractConfigs;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function makeIndexingAddress(contract, contractConfigs) {
|
|
35
|
+
let match = contractConfigs[contract.contractName];
|
|
36
|
+
let contractStartBlock = match !== undefined ? match.startBlock : undefined;
|
|
37
|
+
return {
|
|
38
|
+
address: contract.address,
|
|
39
|
+
contractName: contract.contractName,
|
|
40
|
+
registrationBlock: contract.registrationBlock,
|
|
41
|
+
effectiveStartBlock: deriveEffectiveStartBlock(contract.registrationBlock, contractStartBlock)
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function make(contractConfigs, addresses) {
|
|
46
|
+
let indexingAddresses = {};
|
|
47
|
+
addresses.forEach(contract => {
|
|
48
|
+
indexingAddresses[contract.address] = makeIndexingAddress(contract, contractConfigs);
|
|
49
|
+
});
|
|
50
|
+
return indexingAddresses;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function get(indexingAddresses, address) {
|
|
54
|
+
return indexingAddresses[address];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function size(indexingAddresses) {
|
|
58
|
+
return Utils.Dict.size(indexingAddresses);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function getContractAddresses(indexingAddresses, contractName) {
|
|
62
|
+
let addresses = [];
|
|
63
|
+
Utils.Dict.forEach(indexingAddresses, ia => {
|
|
64
|
+
if (ia.contractName === contractName) {
|
|
65
|
+
addresses.push(ia.address);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
return addresses;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function rawForFilter(indexingAddresses) {
|
|
73
|
+
return indexingAddresses;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function register(indexingAddresses, additions) {
|
|
77
|
+
Object.assign(indexingAddresses, additions);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function rollbackInPlace(indexingAddresses, targetBlockNumber) {
|
|
81
|
+
Utils.Dict.forEachWithKey(indexingAddresses, (indexingContract, address) => {
|
|
82
|
+
if (indexingContract.registrationBlock > targetBlockNumber) {
|
|
83
|
+
return Utils.Dict.deleteInPlace(indexingAddresses, address);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export {
|
|
89
|
+
deriveEffectiveStartBlock,
|
|
90
|
+
makeContractConfigs,
|
|
91
|
+
makeIndexingAddress,
|
|
92
|
+
make,
|
|
93
|
+
get,
|
|
94
|
+
size,
|
|
95
|
+
getContractAddresses,
|
|
96
|
+
rawForFilter,
|
|
97
|
+
register,
|
|
98
|
+
rollbackInPlace,
|
|
99
|
+
}
|
|
100
|
+
/* Utils Not a pure module */
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
type indexingAddress = Internal.indexingContract
|
|
2
|
+
|
|
3
|
+
type contractConfig = {startBlock: option<int>}
|
|
4
|
+
|
|
5
|
+
type t
|
|
6
|
+
|
|
7
|
+
let deriveEffectiveStartBlock: (~registrationBlock: int, ~contractStartBlock: option<int>) => int
|
|
8
|
+
|
|
9
|
+
let makeContractConfigs: (~eventConfigs: array<Internal.eventConfig>) => dict<contractConfig>
|
|
10
|
+
|
|
11
|
+
let makeIndexingAddress: (
|
|
12
|
+
~contract: Internal.indexingAddress,
|
|
13
|
+
~contractConfigs: dict<contractConfig>,
|
|
14
|
+
) => indexingAddress
|
|
15
|
+
|
|
16
|
+
let make: (~contractConfigs: dict<contractConfig>, ~addresses: array<Internal.indexingAddress>) => t
|
|
17
|
+
|
|
18
|
+
let get: (t, string) => option<indexingAddress>
|
|
19
|
+
|
|
20
|
+
let size: t => int
|
|
21
|
+
|
|
22
|
+
let getContractAddresses: (t, ~contractName: string) => array<Address.t>
|
|
23
|
+
|
|
24
|
+
// Underlying dict for the precompiled `clientAddressFilter` only, which does raw
|
|
25
|
+
// `indexingAddresses[srcAddress]` access in generated JS. Don't use elsewhere —
|
|
26
|
+
// prefer the domain accessors so the opaque type stays enforced.
|
|
27
|
+
let rawForFilter: t => dict<indexingAddress>
|
|
28
|
+
|
|
29
|
+
let register: (t, dict<indexingAddress>) => unit
|
|
30
|
+
|
|
31
|
+
// Deletes addresses registered after `targetBlockNumber` from the index in place.
|
|
32
|
+
let rollbackInPlace: (t, ~targetBlockNumber: int) => unit
|
package/src/Internal.res
CHANGED
|
@@ -420,6 +420,11 @@ type eventConfig = private {
|
|
|
420
420
|
// string set so the shared mask logic is ecosystem-agnostic; sources recover
|
|
421
421
|
// the typed view where they need it.
|
|
422
422
|
selectedTransactionFields: Utils.Set.t<string>,
|
|
423
|
+
// `selectedTransactionFields` precompiled to the transaction-store selection
|
|
424
|
+
// bitmask (bit per ecosystem field code). Materialisation reads this per item
|
|
425
|
+
// so each transaction decodes only the fields its event selected. `0.` when
|
|
426
|
+
// nothing is selected or the ecosystem carries the transaction inline (Fuel).
|
|
427
|
+
transactionFieldMask: float,
|
|
423
428
|
}
|
|
424
429
|
|
|
425
430
|
type fuelEventKind =
|
package/src/Main.res
CHANGED
|
@@ -168,18 +168,7 @@ let buildChainsObject = (~config: Config.t) => {
|
|
|
168
168
|
| Some(state) => {
|
|
169
169
|
let chain = ChainMap.Chain.makeUnsafe(~chainId=chainConfig.id)
|
|
170
170
|
let chainState = state->IndexerState.getChainState(~chain)
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
// Collect all addresses for this contract name from indexingAddresses
|
|
174
|
-
let addresses = []
|
|
175
|
-
let values = indexingAddresses->Dict.valuesToArray
|
|
176
|
-
for idx in 0 to values->Array.length - 1 {
|
|
177
|
-
let indexingContract = values->Array.getUnsafe(idx)
|
|
178
|
-
if indexingContract.contractName === contract.name {
|
|
179
|
-
addresses->Array.push(indexingContract.address)->ignore
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
addresses
|
|
171
|
+
chainState->ChainState.contractAddresses(~contractName=contract.name)
|
|
183
172
|
}
|
|
184
173
|
// Before the global state is available (eg during handler
|
|
185
174
|
// module load after resume), combine static addresses from config
|
|
@@ -627,9 +616,9 @@ let startServer = (~getState, ~persistence: Persistence.t, ~isDevelopmentMode: b
|
|
|
627
616
|
app->get("/metrics", (_req, res) => {
|
|
628
617
|
res->set("Content-Type", PromClient.defaultRegister->PromClient.getContentType)
|
|
629
618
|
let _ =
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
619
|
+
Metrics.collect(~state=indexerStateRef.contents)->Promise.thenResolve(metrics =>
|
|
620
|
+
res->endWithData(metrics)
|
|
621
|
+
)
|
|
633
622
|
})
|
|
634
623
|
|
|
635
624
|
app->get("/metrics/runtime", (_req, res) => {
|
|
@@ -687,6 +676,10 @@ let dropSchema = async () => {
|
|
|
687
676
|
await persistence.storage.close()
|
|
688
677
|
}
|
|
689
678
|
|
|
679
|
+
// Rejection carried by `onError`: the failure is already logged with full
|
|
680
|
+
// context, so callers should act on it (exit / re-throw) without logging again.
|
|
681
|
+
exception FatalError(exn)
|
|
682
|
+
|
|
690
683
|
let start = async (
|
|
691
684
|
~persistence: option<Persistence.t>=?,
|
|
692
685
|
~reset=false,
|
|
@@ -742,17 +735,20 @@ let start = async (
|
|
|
742
735
|
| None => config
|
|
743
736
|
}
|
|
744
737
|
// The single fatal-error handler, invoked once via IndexerState.errorExit.
|
|
738
|
+
// It logs the failure once (with chain context) and rejects the run wrapped in
|
|
739
|
+
// `FatalError` so callers know it's already logged — `Bin.res` just exits, the
|
|
740
|
+
// test worker unwraps and re-throws it to the parent thread. `runUntilFatalError`
|
|
741
|
+
// only ever rejects: on a clean run it stays pending and the process exits via
|
|
742
|
+
// ExitOnCaughtUp / when the indexer loop drains.
|
|
743
|
+
let onErrorReject = ref(None)
|
|
744
|
+
let runUntilFatalError: promise<unit> = Promise.make((_resolve, reject) =>
|
|
745
|
+
onErrorReject := Some(reject)
|
|
746
|
+
)
|
|
747
|
+
// `onErrorReject` is filled synchronously by `Promise.make` above, before the
|
|
748
|
+
// indexer can run and call `onError`, so it's always present here.
|
|
745
749
|
let onError = (errHandler: ErrorHandling.t) => {
|
|
746
750
|
errHandler->ErrorHandling.log
|
|
747
|
-
|
|
748
|
-
// The TestIndexer runs the indexer in a worker thread and reads the
|
|
749
|
-
// failure off the worker 'error' event. Re-throw the original error
|
|
750
|
-
// outside the promise chain on the next tick so the test sees its real
|
|
751
|
-
// message instead of a generic non-zero exit code.
|
|
752
|
-
NodeJs.setImmediate(() => errHandler->ErrorHandling.raiseExn)
|
|
753
|
-
} else {
|
|
754
|
-
NodeJs.process->NodeJs.exitWithCode(Failure)
|
|
755
|
-
}
|
|
751
|
+
(onErrorReject.contents->Option.getUnsafe)(FatalError(errHandler.exn->Utils.prettifyExn))
|
|
756
752
|
}
|
|
757
753
|
let envioVersion = Utils.EnvioPackage.value.version
|
|
758
754
|
Prometheus.Info.set(~version=envioVersion)
|
|
@@ -793,4 +789,5 @@ let start = async (
|
|
|
793
789
|
}
|
|
794
790
|
indexerStateRef := Some(state)
|
|
795
791
|
state->IndexerLoop.start
|
|
792
|
+
await runUntilFatalError
|
|
796
793
|
}
|
package/src/Main.res.mjs
CHANGED
|
@@ -6,6 +6,7 @@ import * as Envio from "./Envio.res.mjs";
|
|
|
6
6
|
import * as Utils from "./Utils.res.mjs";
|
|
7
7
|
import * as Config from "./Config.res.mjs";
|
|
8
8
|
import * as Logging from "./Logging.res.mjs";
|
|
9
|
+
import * as Metrics from "./Metrics.res.mjs";
|
|
9
10
|
import Express from "express";
|
|
10
11
|
import * as Process from "process";
|
|
11
12
|
import * as ChainMap from "./ChainMap.res.mjs";
|
|
@@ -165,29 +166,20 @@ function buildChainsObject(config) {
|
|
|
165
166
|
if (state !== undefined) {
|
|
166
167
|
let chain = ChainMap.Chain.makeUnsafe(chainConfig.id);
|
|
167
168
|
let chainState = IndexerState.getChainState(Primitive_option.valFromOption(state), chain);
|
|
168
|
-
|
|
169
|
-
let addresses = [];
|
|
170
|
-
let values = Object.values(indexingAddresses);
|
|
171
|
-
for (let idx = 0, idx_finish = values.length; idx < idx_finish; ++idx) {
|
|
172
|
-
let indexingContract = values[idx];
|
|
173
|
-
if (indexingContract.contractName === contract.name) {
|
|
174
|
-
addresses.push(indexingContract.address);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
return addresses;
|
|
169
|
+
return ChainState.contractAddresses(chainState, contract.name);
|
|
178
170
|
}
|
|
179
171
|
let chainState$1 = getInitialChainState(chainConfig.id);
|
|
180
172
|
if (chainState$1 === undefined) {
|
|
181
173
|
return contract.addresses;
|
|
182
174
|
}
|
|
183
|
-
let addresses
|
|
175
|
+
let addresses = contract.addresses.slice();
|
|
184
176
|
chainState$1.indexingAddresses.forEach(dc => {
|
|
185
177
|
if (dc.contractName === contract.name) {
|
|
186
|
-
addresses
|
|
178
|
+
addresses.push(dc.address);
|
|
187
179
|
return;
|
|
188
180
|
}
|
|
189
181
|
});
|
|
190
|
-
return addresses
|
|
182
|
+
return addresses;
|
|
191
183
|
}
|
|
192
184
|
});
|
|
193
185
|
Object.defineProperty(chainObj, contract.name, {
|
|
@@ -486,7 +478,7 @@ function startServer(getState, persistence, isDevelopmentMode) {
|
|
|
486
478
|
});
|
|
487
479
|
app.get("/metrics", (_req, res) => {
|
|
488
480
|
res.set("Content-Type", PromClient.register.contentType);
|
|
489
|
-
|
|
481
|
+
Metrics.collect(indexerStateRef.contents).then(metrics => res.end(metrics));
|
|
490
482
|
});
|
|
491
483
|
app.get("/metrics/runtime", (_req, res) => {
|
|
492
484
|
res.set("Content-Type", runtimeRegistry.contentType);
|
|
@@ -522,6 +514,8 @@ async function dropSchema() {
|
|
|
522
514
|
return await persistence.storage.close();
|
|
523
515
|
}
|
|
524
516
|
|
|
517
|
+
let FatalError = /* @__PURE__ */Primitive_exceptions.create("Main.FatalError");
|
|
518
|
+
|
|
525
519
|
async function start(persistence, resetOpt, isTestOpt, exitAfterFirstEventBlockOpt, patchConfig) {
|
|
526
520
|
let reset = resetOpt !== undefined ? resetOpt : false;
|
|
527
521
|
let isTest = isTestOpt !== undefined ? isTestOpt : false;
|
|
@@ -562,13 +556,18 @@ async function start(persistence, resetOpt, isTestOpt, exitAfterFirstEventBlockO
|
|
|
562
556
|
allEnums: config.allEnums
|
|
563
557
|
}) : config;
|
|
564
558
|
let config$2 = patchConfig !== undefined ? patchConfig(config$1, registrations) : config$1;
|
|
559
|
+
let onErrorReject = {
|
|
560
|
+
contents: undefined
|
|
561
|
+
};
|
|
562
|
+
let runUntilFatalError = new Promise((_resolve, reject) => {
|
|
563
|
+
onErrorReject.contents = reject;
|
|
564
|
+
});
|
|
565
565
|
let onError = errHandler => {
|
|
566
566
|
ErrorHandling.log(errHandler);
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
}
|
|
567
|
+
onErrorReject.contents({
|
|
568
|
+
RE_EXN_ID: FatalError,
|
|
569
|
+
_1: Utils.prettifyExn(errHandler.exn)
|
|
570
|
+
});
|
|
572
571
|
};
|
|
573
572
|
let envioVersion = Utils.EnvioPackage.value.version;
|
|
574
573
|
Prometheus.Info.set(envioVersion);
|
|
@@ -599,7 +598,8 @@ async function start(persistence, resetOpt, isTestOpt, exitAfterFirstEventBlockO
|
|
|
599
598
|
Tui.start(() => state);
|
|
600
599
|
}
|
|
601
600
|
indexerStateRef.contents = Primitive_option.some(state);
|
|
602
|
-
|
|
601
|
+
IndexerLoop.start(state);
|
|
602
|
+
return await runUntilFatalError;
|
|
603
603
|
}
|
|
604
604
|
|
|
605
605
|
export {
|
|
@@ -616,6 +616,7 @@ export {
|
|
|
616
616
|
getEnvioInfo,
|
|
617
617
|
migrate,
|
|
618
618
|
dropSchema,
|
|
619
|
+
FatalError,
|
|
619
620
|
start,
|
|
620
621
|
}
|
|
621
622
|
/* chainDataSchema Not a pure module */
|
package/src/Metrics.res
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Pull-based metrics computed from live indexer state at scrape time, merged with
|
|
2
|
+
// the imperatively-updated prom-client default registry. Used because the address
|
|
3
|
+
// index is mutated in place (no single update site to fire a gauge from).
|
|
4
|
+
|
|
5
|
+
let indexingAddressesName = "envio_indexing_addresses"
|
|
6
|
+
let indexingAddressesHelp = "The number of addresses indexed on chain. Includes both static and dynamic addresses."
|
|
7
|
+
|
|
8
|
+
// Render a gauge straight from the per-chain dict, one sample per key, without
|
|
9
|
+
// materialising an intermediate samples array. Accumulate into one string rather
|
|
10
|
+
// than building a lines array to join: `++` compiles to JS `+=`, which V8 grows
|
|
11
|
+
// as a ConsString instead of recopying.
|
|
12
|
+
let renderGauge = (~name, ~help, ~chains: dict<'a>, ~value: 'a => int) => {
|
|
13
|
+
let out = ref(`# HELP ${name} ${help}\n# TYPE ${name} gauge`)
|
|
14
|
+
let prefix = `\n${name}{chainId="`
|
|
15
|
+
chains->Utils.Dict.forEachWithKey((chain, chainId) => {
|
|
16
|
+
out := out.contents ++ prefix ++ chainId ++ `"} ` ++ value(chain)->Int.toString
|
|
17
|
+
})
|
|
18
|
+
out.contents
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let collect = async (~state: option<IndexerState.t>) => {
|
|
22
|
+
let base = await PromClient.defaultRegister->PromClient.metrics
|
|
23
|
+
switch state {
|
|
24
|
+
| None => base
|
|
25
|
+
| Some(state) =>
|
|
26
|
+
`${base}${renderGauge(
|
|
27
|
+
~name=indexingAddressesName,
|
|
28
|
+
~help=indexingAddressesHelp,
|
|
29
|
+
~chains=state->IndexerState.chainStates,
|
|
30
|
+
~value=cs => (cs->ChainState.toChainData).numAddresses,
|
|
31
|
+
)}\n`
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Utils from "./Utils.res.mjs";
|
|
4
|
+
import * as ChainState from "./ChainState.res.mjs";
|
|
5
|
+
import * as PromClient from "prom-client";
|
|
6
|
+
import * as IndexerState from "./IndexerState.res.mjs";
|
|
7
|
+
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
8
|
+
|
|
9
|
+
let indexingAddressesName = "envio_indexing_addresses";
|
|
10
|
+
|
|
11
|
+
let indexingAddressesHelp = "The number of addresses indexed on chain. Includes both static and dynamic addresses.";
|
|
12
|
+
|
|
13
|
+
function renderGauge(name, help, chains, value) {
|
|
14
|
+
let out = {
|
|
15
|
+
contents: `# HELP ` + name + ` ` + help + `\n# TYPE ` + name + ` gauge`
|
|
16
|
+
};
|
|
17
|
+
let prefix = `\n` + name + `{chainId="`;
|
|
18
|
+
Utils.Dict.forEachWithKey(chains, (chain, chainId) => {
|
|
19
|
+
out.contents = out.contents + prefix + chainId + `"} ` + value(chain).toString();
|
|
20
|
+
});
|
|
21
|
+
return out.contents;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function collect(state) {
|
|
25
|
+
let base = await PromClient.register.metrics();
|
|
26
|
+
if (state !== undefined) {
|
|
27
|
+
return base + renderGauge(indexingAddressesName, indexingAddressesHelp, IndexerState.chainStates(Primitive_option.valFromOption(state)), cs => ChainState.toChainData(cs).numAddresses) + `\n`;
|
|
28
|
+
} else {
|
|
29
|
+
return base;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export {
|
|
34
|
+
indexingAddressesName,
|
|
35
|
+
indexingAddressesHelp,
|
|
36
|
+
renderGauge,
|
|
37
|
+
collect,
|
|
38
|
+
}
|
|
39
|
+
/* Utils Not a pure module */
|
package/src/Prometheus.res
CHANGED
|
@@ -246,10 +246,7 @@ module PreloadHandler = {
|
|
|
246
246
|
)
|
|
247
247
|
operations->Utils.Dict.deleteInPlace(key)
|
|
248
248
|
}
|
|
249
|
-
sumTimeCounter->SafeCounter.handleFloat(
|
|
250
|
-
~labels,
|
|
251
|
-
~value=timerRef->Performance.secondsSince,
|
|
252
|
-
)
|
|
249
|
+
sumTimeCounter->SafeCounter.handleFloat(~labels, ~value=timerRef->Performance.secondsSince)
|
|
253
250
|
count->SafeCounter.increment(~labels)
|
|
254
251
|
}
|
|
255
252
|
}
|
|
@@ -339,18 +336,6 @@ module ProcessStartTimeSeconds = {
|
|
|
339
336
|
}
|
|
340
337
|
}
|
|
341
338
|
|
|
342
|
-
module IndexingAddresses = {
|
|
343
|
-
let gauge = SafeGauge.makeOrThrow(
|
|
344
|
-
~name="envio_indexing_addresses",
|
|
345
|
-
~help="The number of addresses indexed on chain. Includes both static and dynamic addresses.",
|
|
346
|
-
~labelSchema=chainIdLabelsSchema,
|
|
347
|
-
)
|
|
348
|
-
|
|
349
|
-
let set = (~addressesCount, ~chainId) => {
|
|
350
|
-
gauge->SafeGauge.handleInt(~labels=chainId, ~value=addressesCount)
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
339
|
module IndexingConcurrency = {
|
|
355
340
|
let gauge = SafeGauge.makeOrThrow(
|
|
356
341
|
~name="envio_indexing_concurrency",
|
|
@@ -800,10 +785,7 @@ module StorageLoad = {
|
|
|
800
785
|
)
|
|
801
786
|
operations->Utils.Dict.deleteInPlace(key)
|
|
802
787
|
}
|
|
803
|
-
sumTimeCounter->SafeCounter.handleFloat(
|
|
804
|
-
~labels,
|
|
805
|
-
~value=timerRef->Performance.secondsSince,
|
|
806
|
-
)
|
|
788
|
+
sumTimeCounter->SafeCounter.handleFloat(~labels, ~value=timerRef->Performance.secondsSince)
|
|
807
789
|
counter->SafeCounter.increment(~labels)
|
|
808
790
|
whereSizeCounter->SafeCounter.handleInt(~labels, ~value=whereSize)
|
|
809
791
|
sizeCounter->SafeCounter.handleInt(~labels, ~value=size)
|