envio 3.15.0 → 3.16.0-subgraph
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -34
- package/evm.schema.json +11 -2
- package/fuel.schema.json +10 -1
- package/index.d.ts +6 -0
- package/licenses/README.md +1 -1
- package/package.json +10 -9
- package/src/Batch.res.mjs +5 -5
- package/src/BatchProcessing.res +1 -0
- package/src/BatchProcessing.res.mjs +1 -0
- package/src/Bin.res +7 -0
- package/src/Bin.res.mjs +8 -0
- package/src/ChainFetching.res +450 -16
- package/src/ChainFetching.res.mjs +403 -32
- package/src/ChainState.res +124 -7
- package/src/ChainState.res.mjs +128 -67
- package/src/ChainState.resi +26 -0
- package/src/Clock.res +57 -0
- package/src/Clock.res.mjs +72 -0
- package/src/Config.res +31 -3
- package/src/Config.res.mjs +15 -4
- package/src/ContractRegisterContext.res +3 -6
- package/src/ContractRegisterContext.res.mjs +4 -2
- package/src/Core.res +95 -36
- package/src/Core.res.mjs +90 -1
- package/src/CrossChainState.res +12 -2
- package/src/CrossChainState.res.mjs +3 -3
- package/src/Ecosystem.res +7 -4
- package/src/Ecosystem.res.mjs +8 -8
- package/src/EffectCacheLookup.res +98 -0
- package/src/EffectCacheLookup.res.mjs +116 -0
- package/src/EffectState.res +10 -2
- package/src/EffectState.res.mjs +8 -4
- package/src/EffectState.resi +3 -1
- package/src/EntityId.res +2 -7
- package/src/EntityId.res.mjs +1 -8
- package/src/EntityTables.res +4 -1
- package/src/EntityTables.res.mjs +1 -1
- package/src/Env.res +20 -0
- package/src/Env.res.mjs +16 -0
- package/src/EventProcessing.res +38 -1
- package/src/EventProcessing.res.mjs +28 -1
- package/src/FetchState.res +408 -73
- package/src/FetchState.res.mjs +361 -91
- package/src/FinalizeBackfill.res +2 -4
- package/src/FinalizeBackfill.res.mjs +3 -4
- package/src/HandlerLoader.res +39 -15
- package/src/HandlerLoader.res.mjs +27 -8
- package/src/HandlerRegister.res +4 -0
- package/src/HandlerRegister.res.mjs +5 -2
- package/src/HandlerRegister.resi +2 -0
- package/src/InMemoryTable.res +17 -16
- package/src/InMemoryTable.res.mjs +14 -15
- package/src/IndexerState.res +28 -4
- package/src/IndexerState.res.mjs +19 -6
- package/src/IndexerState.resi +4 -0
- package/src/Internal.res +61 -49
- package/src/Internal.res.mjs +36 -80
- package/src/LoadLayer.res +54 -15
- package/src/LoadLayer.res.mjs +75 -45
- package/src/LoadLayer.resi +26 -0
- package/src/Main.res +14 -1
- package/src/Main.res.mjs +4 -3
- package/src/Persistence.res +3 -23
- package/src/Persistence.res.mjs +3 -3
- package/src/PreloadLookahead.res +202 -0
- package/src/PreloadLookahead.res.mjs +218 -0
- package/src/Rollback.res +0 -1
- package/src/Rollback.res.mjs +1 -1
- package/src/RunScope.res +3 -0
- package/src/SafeCheckpointTracking.res +4 -4
- package/src/SafeCheckpointTracking.res.mjs +1 -1
- package/src/SimulateItems.res +42 -26
- package/src/SimulateItems.res.mjs +40 -16
- package/src/StagedRows.res +106 -0
- package/src/StagedRows.res.mjs +102 -0
- package/src/Staging.res +32 -14
- package/src/Staging.res.mjs +17 -20
- package/src/Storage.res +303 -948
- package/src/Storage.res.mjs +229 -708
- package/src/SubgraphLog.res +19 -0
- package/src/SubgraphLog.res.mjs +26 -0
- package/src/Supervisor.res +75 -56
- package/src/Supervisor.res.mjs +55 -26
- package/src/SyncWrites.res +107 -0
- package/src/SyncWrites.res.mjs +98 -0
- package/src/TestIndexer.res +22 -27
- package/src/TestIndexer.res.mjs +20 -17
- package/src/Throttler.res +15 -8
- package/src/Throttler.res.mjs +9 -6
- package/src/Throttler.resi +1 -1
- package/src/UserContext.res +419 -46
- package/src/UserContext.res.mjs +318 -30
- package/src/Utils.res +17 -6
- package/src/Utils.res.mjs +12 -6
- package/src/Writing.res +0 -1
- package/src/Writing.res.mjs +1 -1
- package/src/bindings/ClickHouse.res +0 -322
- package/src/bindings/ClickHouse.res.mjs +0 -338
- package/src/bindings/StorageClient.res +108 -174
- package/src/bindings/StorageClient.res.mjs +19 -8
- package/src/bindings/Vitest.res +0 -26
- package/src/bindings/Vitest.res.mjs +0 -3
- package/src/db/Table.res +24 -150
- package/src/db/Table.res.mjs +38 -272
- package/src/sources/AddressSet.res +2 -0
- package/src/sources/AddressStore.res +3 -0
- package/src/sources/CallSource.res +274 -0
- package/src/sources/CallSource.res.mjs +196 -0
- package/src/sources/ChainSources.res +2 -0
- package/src/sources/ChainSources.res.mjs +2 -2
- package/src/sources/EvmChain.res +10 -1
- package/src/sources/EvmChain.res.mjs +6 -3
- package/src/sources/EvmHyperSyncSource.res +2 -2
- package/src/sources/EvmHyperSyncSource.res.mjs +1 -1
- package/src/sources/EvmRpcClient.res +13 -3
- package/src/sources/EvmRpcClient.res.mjs +4 -3
- package/src/sources/EvmRpcWs.res +4 -3
- package/src/sources/EvmRpcWs.res.mjs +2 -2
- package/src/sources/HeightFeed.res +31 -10
- package/src/sources/HeightFeed.res.mjs +10 -6
- package/src/sources/HeightStream.res +32 -17
- package/src/sources/HeightStream.res.mjs +10 -10
- package/src/sources/HyperSync.res +2 -1
- package/src/sources/HyperSync.res.mjs +2 -1
- package/src/sources/HyperSyncSSE.res +4 -6
- package/src/sources/HyperSyncSSE.res.mjs +2 -2
- package/src/sources/RpcSource.res +10 -1
- package/src/sources/RpcSource.res.mjs +4 -3
- package/src/sources/Source.res +3 -0
- package/src/sources/SourceManager.res +84 -77
- package/src/sources/SourceManager.res.mjs +17 -20
- package/src/sources/SourceManager.resi +3 -4
- package/src/sources/StartBlockResolver.res +1 -0
- package/src/sources/StartBlockResolver.res.mjs +3 -2
- package/src/sources/SvmHyperSyncSource.res +2 -2
- package/src/sources/SvmHyperSyncSource.res.mjs +1 -1
- package/src/subgraph/abi-types.ts +84 -0
- package/src/subgraph/aggregations.ts +745 -0
- package/src/subgraph/as-package.ts +143 -0
- package/src/subgraph/assemblyscript-regex.ts +67 -0
- package/src/subgraph/assemblyscript.ts +731 -0
- package/src/subgraph/blocks.ts +398 -0
- package/src/subgraph/calls-source.ts +702 -0
- package/src/subgraph/calls.ts +258 -0
- package/src/subgraph/conformance.ts +106 -0
- package/src/subgraph/errors.ts +85 -0
- package/src/subgraph/graft.ts +73 -0
- package/src/subgraph/graph-cli-worker.mjs +35 -0
- package/src/subgraph/graph-cli.ts +900 -0
- package/src/subgraph/graph-node-decimal.ts +199 -0
- package/src/subgraph/graph-ts-enums.ts +28 -0
- package/src/subgraph/graph-ts-types/VERSION +2 -0
- package/src/subgraph/graph-ts-types/chain/arweave.d.ts +70 -0
- package/src/subgraph/graph-ts-types/chain/cosmos.d.ts +327 -0
- package/src/subgraph/graph-ts-types/chain/ethereum.d.ts +233 -0
- package/src/subgraph/graph-ts-types/chain/near.d.ts +253 -0
- package/src/subgraph/graph-ts-types/chain/starknet.d.ts +32 -0
- package/src/subgraph/graph-ts-types/common/collections.d.ts +136 -0
- package/src/subgraph/graph-ts-types/common/conversion.d.ts +11 -0
- package/src/subgraph/graph-ts-types/common/datasource.d.ts +30 -0
- package/src/subgraph/graph-ts-types/common/eager-offset.d.ts +0 -0
- package/src/subgraph/graph-ts-types/common/json.d.ts +17 -0
- package/src/subgraph/graph-ts-types/common/numbers.d.ts +120 -0
- package/src/subgraph/graph-ts-types/common/value.d.ts +120 -0
- package/src/subgraph/graph-ts-types/common/yaml.d.ts +90 -0
- package/src/subgraph/graph-ts-types/global/global.d.ts +194 -0
- package/src/subgraph/graph-ts-types/helper-functions.d.ts +22 -0
- package/src/subgraph/graph-ts-types/index.d.ts +102 -0
- package/src/subgraph/graph-ts.ts +4339 -0
- package/src/subgraph/hosts.ts +554 -0
- package/src/subgraph/log.ts +28 -0
- package/src/subgraph/receipts.ts +184 -0
- package/src/subgraph/register-slots.ts +93 -0
- package/src/subgraph/rollup.ts +109 -0
- package/src/subgraph/rpc.ts +337 -0
- package/src/subgraph/runtime.ts +2059 -0
- package/src/subgraph/scope.ts +176 -0
- package/src/subgraph/stats.ts +42 -0
- package/src/subgraph/template-instances.ts +66 -0
- package/src/subgraph/toolchain.ts +327 -0
- package/src/subgraph/transient.ts +176 -0
- package/svm.schema.json +11 -2
- package/src/PgWriting.res +0 -36
- package/src/PgWriting.res.mjs +0 -34
- package/src/db/IndexDefinition.res +0 -47
- package/src/db/IndexDefinition.res.mjs +0 -67
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// A cached effect's stored outputs, read straight from storage. For the
|
|
2
|
+
// subgraph register pass: it runs at fetch time, outside any handler context,
|
|
3
|
+
// so its contract calls can't go through the effect machinery, and after
|
|
4
|
+
// `envio start -r` (which keeps the subgraph call caches) it would otherwise ask
|
|
5
|
+
// the RPC again for every call it makes.
|
|
6
|
+
//
|
|
7
|
+
// Only a chain-scoped effect's table that held rows when the run started is
|
|
8
|
+
// read: rows this run writes are already in the register pass's own cache.
|
|
9
|
+
// The calls of one turn share one storage query.
|
|
10
|
+
|
|
11
|
+
type batch = {
|
|
12
|
+
effect: Internal.effect,
|
|
13
|
+
table: Table.table,
|
|
14
|
+
keys: array<string>,
|
|
15
|
+
resolvers: array<option<Internal.effectOutput> => unit>,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let startupTables: ref<option<Utils.Set.t<string>>> = ref(None)
|
|
19
|
+
|
|
20
|
+
// Reset between test indexers, which each bring their own storage.
|
|
21
|
+
let resetStartupTables = () => startupTables := None
|
|
22
|
+
|
|
23
|
+
let tablesWithRowsAtStart = (cache: dict<Persistence.effectCacheRecord>) =>
|
|
24
|
+
switch startupTables.contents {
|
|
25
|
+
| Some(tables) => tables
|
|
26
|
+
| None =>
|
|
27
|
+
let tables = Utils.Set.make()
|
|
28
|
+
cache
|
|
29
|
+
->Dict.valuesToArray
|
|
30
|
+
->Array.forEach(record =>
|
|
31
|
+
if record.count > 0 {
|
|
32
|
+
tables->Utils.Set.add(record.tableName)->ignore
|
|
33
|
+
}
|
|
34
|
+
)
|
|
35
|
+
startupTables := Some(tables)
|
|
36
|
+
tables
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let pending: dict<batch> = Dict.make()
|
|
40
|
+
|
|
41
|
+
let load = async (storage: Persistence.storage, batch: batch) => {
|
|
42
|
+
let rows =
|
|
43
|
+
(await storage.loadOrThrow(~table=batch.table, ~filter=EntityFilter.byIds(batch.keys)))->(
|
|
44
|
+
Utils.magic: array<unknown> => array<Internal.effectCacheItem>
|
|
45
|
+
)
|
|
46
|
+
let byId = Dict.make()
|
|
47
|
+
rows->Array.forEach(row =>
|
|
48
|
+
switch row.output->S.parseOrThrow(batch.effect.storageMeta.outputSchema) {
|
|
49
|
+
| output => byId->Dict.set(row.id, output)
|
|
50
|
+
| exception _ => ()
|
|
51
|
+
}
|
|
52
|
+
)
|
|
53
|
+
batch.keys->Array.map(key => byId->Utils.Dict.dangerouslyGetNonOption(key))
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let flush = async (storage: Persistence.storage, tableName) => {
|
|
57
|
+
let batch = pending->Dict.getUnsafe(tableName)
|
|
58
|
+
pending->Utils.Dict.deleteInPlace(tableName)
|
|
59
|
+
let outputs = switch await load(storage, batch) {
|
|
60
|
+
| outputs => outputs
|
|
61
|
+
| exception _ => batch.keys->Array.map(_ => None)
|
|
62
|
+
}
|
|
63
|
+
batch.resolvers->Array.forEachWithIndex((resolve, idx) => resolve(outputs->Array.getUnsafe(idx)))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let cached = (effect: Internal.effect, chainId: int, input: Internal.effectInput): promise<
|
|
67
|
+
option<Internal.effectOutput>,
|
|
68
|
+
> =>
|
|
69
|
+
switch (
|
|
70
|
+
effect.crossChain,
|
|
71
|
+
EnvioGlobal.value.persistence->(Utils.magic: option<unknown> => option<Persistence.t>),
|
|
72
|
+
) {
|
|
73
|
+
| (Some(false), Some({storageStatus: Ready({cache}), storage})) =>
|
|
74
|
+
let table = Internal.makeCacheTable(
|
|
75
|
+
~effectName=effect.name,
|
|
76
|
+
~scope=Chain(chainId->ChainId.fromInt),
|
|
77
|
+
)
|
|
78
|
+
if cache->tablesWithRowsAtStart->Utils.Set.has(table.tableName) {
|
|
79
|
+
let key =
|
|
80
|
+
input
|
|
81
|
+
->S.reverseConvertOrThrow(effect.input)
|
|
82
|
+
->Utils.Hash.makeOrThrow
|
|
83
|
+
->Utils.Hash.boundCacheKey
|
|
84
|
+
Promise.make((resolve, _) => {
|
|
85
|
+
switch pending->Utils.Dict.dangerouslyGetNonOption(table.tableName) {
|
|
86
|
+
| Some(batch) =>
|
|
87
|
+
batch.keys->Array.push(key)
|
|
88
|
+
batch.resolvers->Array.push(resolve)
|
|
89
|
+
| None =>
|
|
90
|
+
pending->Dict.set(table.tableName, {effect, table, keys: [key], resolvers: [resolve]})
|
|
91
|
+
let _ = setTimeout(() => storage->flush(table.tableName)->ignore, 0)
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
} else {
|
|
95
|
+
Promise.resolve(None)
|
|
96
|
+
}
|
|
97
|
+
| _ => Promise.resolve(None)
|
|
98
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Utils from "./Utils.res.mjs";
|
|
4
|
+
import * as Internal from "./Internal.res.mjs";
|
|
5
|
+
import * as EnvioGlobal from "./EnvioGlobal.res.mjs";
|
|
6
|
+
import * as EntityFilter from "./db/EntityFilter.res.mjs";
|
|
7
|
+
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
8
|
+
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
9
|
+
|
|
10
|
+
let startupTables = {
|
|
11
|
+
contents: undefined
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
function resetStartupTables() {
|
|
15
|
+
startupTables.contents = undefined;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function tablesWithRowsAtStart(cache) {
|
|
19
|
+
let tables = startupTables.contents;
|
|
20
|
+
if (tables !== undefined) {
|
|
21
|
+
return Primitive_option.valFromOption(tables);
|
|
22
|
+
}
|
|
23
|
+
let tables$1 = new Set();
|
|
24
|
+
Object.values(cache).forEach(record => {
|
|
25
|
+
if (record.count > 0) {
|
|
26
|
+
tables$1.add(record.tableName);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
startupTables.contents = Primitive_option.some(tables$1);
|
|
31
|
+
return tables$1;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let pending = {};
|
|
35
|
+
|
|
36
|
+
async function load(storage, batch) {
|
|
37
|
+
let rows = await storage.loadOrThrow(EntityFilter.byIds(batch.keys), batch.table);
|
|
38
|
+
let byId = {};
|
|
39
|
+
rows.forEach(row => {
|
|
40
|
+
let output;
|
|
41
|
+
try {
|
|
42
|
+
output = S$RescriptSchema.parseOrThrow(row.output, batch.effect.storageMeta.outputSchema);
|
|
43
|
+
} catch (exn) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
byId[row.id] = output;
|
|
47
|
+
});
|
|
48
|
+
return batch.keys.map(key => byId[key]);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function flush(storage, tableName) {
|
|
52
|
+
let batch = pending[tableName];
|
|
53
|
+
Utils.Dict.deleteInPlace(pending, tableName);
|
|
54
|
+
let outputs;
|
|
55
|
+
try {
|
|
56
|
+
outputs = await load(storage, batch);
|
|
57
|
+
} catch (exn) {
|
|
58
|
+
outputs = batch.keys.map(param => {});
|
|
59
|
+
}
|
|
60
|
+
batch.resolvers.forEach((resolve, idx) => resolve(outputs[idx]));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function cached(effect, chainId, input) {
|
|
64
|
+
let match = effect.crossChain;
|
|
65
|
+
let match$1 = EnvioGlobal.value.persistence;
|
|
66
|
+
if (match === undefined) {
|
|
67
|
+
return Promise.resolve(undefined);
|
|
68
|
+
}
|
|
69
|
+
if (match) {
|
|
70
|
+
return Promise.resolve(undefined);
|
|
71
|
+
}
|
|
72
|
+
if (match$1 === undefined) {
|
|
73
|
+
return Promise.resolve(undefined);
|
|
74
|
+
}
|
|
75
|
+
let match$2 = match$1.storageStatus;
|
|
76
|
+
if (typeof match$2 !== "object") {
|
|
77
|
+
return Promise.resolve(undefined);
|
|
78
|
+
}
|
|
79
|
+
if (match$2.TAG === "Initializing") {
|
|
80
|
+
return Promise.resolve(undefined);
|
|
81
|
+
}
|
|
82
|
+
let storage = match$1.storage;
|
|
83
|
+
let table = Internal.makeCacheTable(effect.name, chainId);
|
|
84
|
+
if (!tablesWithRowsAtStart(match$2._0.cache).has(table.tableName)) {
|
|
85
|
+
return Promise.resolve(undefined);
|
|
86
|
+
}
|
|
87
|
+
let key = Utils.Hash.boundCacheKey(Utils.Hash.makeOrThrow(S$RescriptSchema.reverseConvertOrThrow(input, effect.input)));
|
|
88
|
+
return new Promise((resolve, param) => {
|
|
89
|
+
let batch = pending[table.tableName];
|
|
90
|
+
if (batch !== undefined) {
|
|
91
|
+
batch.keys.push(key);
|
|
92
|
+
batch.resolvers.push(resolve);
|
|
93
|
+
} else {
|
|
94
|
+
pending[table.tableName] = {
|
|
95
|
+
effect: effect,
|
|
96
|
+
table: table,
|
|
97
|
+
keys: [key],
|
|
98
|
+
resolvers: [resolve]
|
|
99
|
+
};
|
|
100
|
+
setTimeout(() => {
|
|
101
|
+
flush(storage, table.tableName);
|
|
102
|
+
}, 0);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export {
|
|
108
|
+
startupTables,
|
|
109
|
+
resetStartupTables,
|
|
110
|
+
tablesWithRowsAtStart,
|
|
111
|
+
pending,
|
|
112
|
+
load,
|
|
113
|
+
flush,
|
|
114
|
+
cached,
|
|
115
|
+
}
|
|
116
|
+
/* Utils Not a pure module */
|
package/src/EffectState.res
CHANGED
|
@@ -11,6 +11,8 @@ type effectRateLimitState = {
|
|
|
11
11
|
mutable availableCalls: int,
|
|
12
12
|
mutable windowStartTime: float,
|
|
13
13
|
mutable nextWindowPromise: option<promise<unit>>,
|
|
14
|
+
// The run's clock: a test on virtual time opens the next window by advancing.
|
|
15
|
+
clock: Clock.t,
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
// Per-(effect, scope) counters rendered into the envio_effect_* metrics. Live
|
|
@@ -80,9 +82,14 @@ type t = {
|
|
|
80
82
|
// rollback; stats and the rate-limit window survive because the table does.
|
|
81
83
|
tables: dict<effectCacheInMemTable>,
|
|
82
84
|
unregisteredCacheCounts: dict<unregisteredCacheCount>,
|
|
85
|
+
clock: Clock.t,
|
|
83
86
|
}
|
|
84
87
|
|
|
85
|
-
let make = (): t => {
|
|
88
|
+
let make = (~clock=Clock.real, ()): t => {
|
|
89
|
+
tables: Dict.make(),
|
|
90
|
+
unregisteredCacheCounts: Dict.make(),
|
|
91
|
+
clock,
|
|
92
|
+
}
|
|
86
93
|
|
|
87
94
|
let setUnregisteredCacheCount = (self: t, ~effectName, ~scope, ~count) => {
|
|
88
95
|
let tableName = Internal.EffectCache.toTableName(~effectName, ~scope)
|
|
@@ -214,8 +221,9 @@ let getTable = (self: t, ~effect: Internal.effect, ~scope: Internal.chainScope)
|
|
|
214
221
|
callsPerDuration,
|
|
215
222
|
durationMs,
|
|
216
223
|
availableCalls: callsPerDuration,
|
|
217
|
-
windowStartTime:
|
|
224
|
+
windowStartTime: self.clock.now(),
|
|
218
225
|
nextWindowPromise: None,
|
|
226
|
+
clock: self.clock,
|
|
219
227
|
})
|
|
220
228
|
}
|
|
221
229
|
let inMemTable: effectCacheInMemTable = {
|
package/src/EffectState.res.mjs
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
+
import * as Clock from "./Clock.res.mjs";
|
|
3
4
|
import * as Utils from "./Utils.res.mjs";
|
|
4
5
|
import * as Internal from "./Internal.res.mjs";
|
|
5
6
|
import * as Performance from "./bindings/Performance.res.mjs";
|
|
6
7
|
|
|
7
|
-
function make() {
|
|
8
|
+
function make(clockOpt, param) {
|
|
9
|
+
let clock = clockOpt !== undefined ? clockOpt : Clock.real;
|
|
8
10
|
return {
|
|
9
11
|
tables: {},
|
|
10
|
-
unregisteredCacheCounts: {}
|
|
12
|
+
unregisteredCacheCounts: {},
|
|
13
|
+
clock: clock
|
|
11
14
|
};
|
|
12
15
|
}
|
|
13
16
|
|
|
@@ -130,8 +133,9 @@ function getTable(self, effect, scope) {
|
|
|
130
133
|
callsPerDuration: callsPerDuration,
|
|
131
134
|
durationMs: match$1.durationMs,
|
|
132
135
|
availableCalls: callsPerDuration,
|
|
133
|
-
windowStartTime:
|
|
134
|
-
nextWindowPromise: undefined
|
|
136
|
+
windowStartTime: self.clock.now(),
|
|
137
|
+
nextWindowPromise: undefined,
|
|
138
|
+
clock: self.clock
|
|
135
139
|
};
|
|
136
140
|
} else {
|
|
137
141
|
rateLimitState = undefined;
|
package/src/EffectState.resi
CHANGED
|
@@ -4,6 +4,8 @@ type effectRateLimitState = {
|
|
|
4
4
|
mutable availableCalls: int,
|
|
5
5
|
mutable windowStartTime: float,
|
|
6
6
|
mutable nextWindowPromise: option<promise<unit>>,
|
|
7
|
+
// The run's clock: a test on virtual time opens the next window by advancing.
|
|
8
|
+
clock: Clock.t,
|
|
7
9
|
}
|
|
8
10
|
|
|
9
11
|
// Per-(effect, scope) metric counters. Survive rollback so they stay monotonic.
|
|
@@ -32,7 +34,7 @@ type effectCacheInMemTable = {
|
|
|
32
34
|
// Opaque store of all per-(effect, scope) runtime state.
|
|
33
35
|
type t
|
|
34
36
|
|
|
35
|
-
let make: unit => t
|
|
37
|
+
let make: (~clock: Clock.t=?, unit) => t
|
|
36
38
|
// Get, or lazily create, the in-mem table for an (effect, scope). Reuses the
|
|
37
39
|
// surviving rate-limit window when the table is recreated after a rollback.
|
|
38
40
|
let getTable: (t, ~effect: Internal.effect, ~scope: Internal.chainScope) => effectCacheInMemTable
|
package/src/EntityId.res
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
// Opaque representation of an entity id. The generated per-entity types expose
|
|
2
|
-
// the real scalar (string / int / bigint); the generic storage and in-memory
|
|
2
|
+
// the real scalar (string / int / bigint / Uint8Array); the generic storage and in-memory
|
|
3
3
|
// layers work with any entity's id through this type without knowing which
|
|
4
4
|
// scalar backs it. The runtime value is always the real id, never a stringified
|
|
5
|
-
// form — `
|
|
5
|
+
// form — a table's `entityIdToKey` derives the string only where a dict or Set key is needed.
|
|
6
6
|
type t
|
|
7
7
|
|
|
8
8
|
external unsafeOfAny: 'a => t = "%identity"
|
|
9
9
|
external unsafeToAny: t => 'a = "%identity"
|
|
10
10
|
external unsafeOfString: string => t = "%identity"
|
|
11
|
-
|
|
12
|
-
// Stringified id used as a JS object/dict key. `String` matches how JS coerces
|
|
13
|
-
// a value used as an object key, so an id indexes the same whether the raw
|
|
14
|
-
// value or its key form is used for lookup, across string/int/bigint.
|
|
15
|
-
let toKey: t => string = %raw(`String`)
|
package/src/EntityId.res.mjs
CHANGED
package/src/EntityTables.res
CHANGED
|
@@ -6,7 +6,10 @@ type t = dict<InMemoryTable.Entity.t>
|
|
|
6
6
|
let make = (entities: array<Internal.entityConfig>): t => {
|
|
7
7
|
let init = Dict.make()
|
|
8
8
|
entities->Array.forEach(entityConfig => {
|
|
9
|
-
init->Dict.set(
|
|
9
|
+
init->Dict.set(
|
|
10
|
+
(entityConfig.name :> string),
|
|
11
|
+
InMemoryTable.Entity.make(~entityIdToKey=entityConfig.table.entityIdToKey),
|
|
12
|
+
)
|
|
10
13
|
})
|
|
11
14
|
init
|
|
12
15
|
}
|
package/src/EntityTables.res.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
|
6
6
|
function make(entities) {
|
|
7
7
|
let init = {};
|
|
8
8
|
entities.forEach(entityConfig => {
|
|
9
|
-
init[entityConfig.name] = InMemoryTable.Entity.make();
|
|
9
|
+
init[entityConfig.name] = InMemoryTable.Entity.make(entityConfig.table.entityIdToKey);
|
|
10
10
|
});
|
|
11
11
|
return init;
|
|
12
12
|
}
|
package/src/Env.res
CHANGED
|
@@ -4,6 +4,26 @@
|
|
|
4
4
|
%%private(let envSafe = EnvSafe.make())
|
|
5
5
|
|
|
6
6
|
let targetBufferSize = envSafe->EnvSafe.get("ENVIO_INDEXING_MAX_BUFFER_SIZE", S.option(S.int))
|
|
7
|
+
|
|
8
|
+
// Set once a subgraph-mode config is loaded. A subgraph's mappings run per
|
|
9
|
+
// event, and every fetched event that can create a data source also holds a
|
|
10
|
+
// live register pass and its contract calls until the response is merged, so
|
|
11
|
+
// the pool of events ready to process is sized for that (CrossChainState).
|
|
12
|
+
let subgraphMode = ref(false)
|
|
13
|
+
|
|
14
|
+
// Set with subgraphMode when the manifest declares templates: without one no
|
|
15
|
+
// event can create a data source, so none holds a register pass.
|
|
16
|
+
let subgraphHasTemplates = ref(false)
|
|
17
|
+
|
|
18
|
+
// Most events with a register pass one subgraph response may hold before it is
|
|
19
|
+
// cut after a block and the rest fetched again. 0 turns the cut off; unset, it
|
|
20
|
+
// follows what the register passes cost (ChainFetching).
|
|
21
|
+
let subgraphRegisterChunk = envSafe->EnvSafe.get("ENVIO_SUBGRAPH_REGISTER_CHUNK", S.option(S.int))
|
|
22
|
+
|
|
23
|
+
// How many items ahead of the one executing a subgraph handler is preloaded
|
|
24
|
+
// again once an entity it read as absent is created (PreloadLookahead). 0 = off.
|
|
25
|
+
let subgraphPreloadLookahead =
|
|
26
|
+
envSafe->EnvSafe.get("ENVIO_SUBGRAPH_PRELOAD_LOOKAHEAD", S.int, ~fallback=256)
|
|
7
27
|
let maxAddrInPartition = envSafe->EnvSafe.get("MAX_PARTITION_SIZE", S.int, ~fallback=5_000)
|
|
8
28
|
|
|
9
29
|
// Most parallel in-flight queries a single chain may have at once, across all
|
package/src/Env.res.mjs
CHANGED
|
@@ -13,6 +13,18 @@ let envSafe = EnvSafe.make(undefined);
|
|
|
13
13
|
|
|
14
14
|
let targetBufferSize = EnvSafe.get(envSafe, "ENVIO_INDEXING_MAX_BUFFER_SIZE", S$RescriptSchema.option(S$RescriptSchema.int), undefined, undefined, undefined, undefined);
|
|
15
15
|
|
|
16
|
+
let subgraphMode = {
|
|
17
|
+
contents: false
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
let subgraphHasTemplates = {
|
|
21
|
+
contents: false
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
let subgraphRegisterChunk = EnvSafe.get(envSafe, "ENVIO_SUBGRAPH_REGISTER_CHUNK", S$RescriptSchema.option(S$RescriptSchema.int), undefined, undefined, undefined, undefined);
|
|
25
|
+
|
|
26
|
+
let subgraphPreloadLookahead = EnvSafe.get(envSafe, "ENVIO_SUBGRAPH_PRELOAD_LOOKAHEAD", S$RescriptSchema.int, undefined, 256, undefined, undefined);
|
|
27
|
+
|
|
16
28
|
let maxAddrInPartition = EnvSafe.get(envSafe, "MAX_PARTITION_SIZE", S$RescriptSchema.int, undefined, 5000, undefined, undefined);
|
|
17
29
|
|
|
18
30
|
let clientFilterAddressThreshold = EnvSafe.get(envSafe, "ENVIO_CLIENT_FILTER_ADDRESS_THRESHOLD", S$RescriptSchema.int, undefined, (maxAddrInPartition * 100 | 0) / 5 | 0, undefined, undefined);
|
|
@@ -216,6 +228,10 @@ let maxChainConcurrency = 100;
|
|
|
216
228
|
|
|
217
229
|
export {
|
|
218
230
|
targetBufferSize,
|
|
231
|
+
subgraphMode,
|
|
232
|
+
subgraphHasTemplates,
|
|
233
|
+
subgraphRegisterChunk,
|
|
234
|
+
subgraphPreloadLookahead,
|
|
219
235
|
maxAddrInPartition,
|
|
220
236
|
maxChainConcurrency,
|
|
221
237
|
clientFilterAddressThreshold,
|
package/src/EventProcessing.res
CHANGED
|
@@ -125,17 +125,52 @@ let preloadBatchOrThrow = async (batch: Batch.t, ~runtime) => {
|
|
|
125
125
|
|
|
126
126
|
let runBatchHandlersOrThrow = async (batch: Batch.t, ~runtime) => {
|
|
127
127
|
let itemIdx = ref(0)
|
|
128
|
+
let lookahead = PreloadLookahead.active.contents
|
|
129
|
+
// Settled before the batch ends: one finishing after a rollback would put a
|
|
130
|
+
// value read before it back into memory.
|
|
131
|
+
let lookaheadPreloads = []
|
|
132
|
+
// Only read by the lookahead, which preloads items out of checkpoint order.
|
|
133
|
+
let checkpointIdOf = switch lookahead {
|
|
134
|
+
| Some(_) =>
|
|
135
|
+
let ids = []
|
|
136
|
+
batch.checkpointIds->Array.forEachWithIndex((checkpointId, checkpointIdx) => {
|
|
137
|
+
for _ in 1 to batch.checkpointItemsCount->Array.getUnsafe(checkpointIdx) {
|
|
138
|
+
ids->Array.push(checkpointId)
|
|
139
|
+
}
|
|
140
|
+
})
|
|
141
|
+
ids
|
|
142
|
+
| None => []
|
|
143
|
+
}
|
|
128
144
|
|
|
129
145
|
for checkpointIdx in 0 to batch.checkpointIds->Array.length - 1 {
|
|
130
146
|
let checkpointId = batch.checkpointIds->Array.getUnsafe(checkpointIdx)
|
|
131
147
|
let checkpointItemsCount = batch.checkpointItemsCount->Array.getUnsafe(checkpointIdx)
|
|
132
148
|
|
|
133
149
|
for idx in 0 to checkpointItemsCount - 1 {
|
|
134
|
-
let
|
|
150
|
+
let index = itemIdx.contents + idx
|
|
151
|
+
let item = batch.items->Array.getUnsafe(index)
|
|
135
152
|
await runHandlerOrThrow(item, ~checkpointId, ~runtime)
|
|
153
|
+
|
|
154
|
+
switch lookahead {
|
|
155
|
+
| Some(lookahead) =>
|
|
156
|
+
lookahead->PreloadLookahead.afterItem(~index, ~preload=(ahead, aheadIdx) =>
|
|
157
|
+
if ahead->hasHandler {
|
|
158
|
+
lookaheadPreloads
|
|
159
|
+
->Array.push(
|
|
160
|
+
runtime->preload(
|
|
161
|
+
~item=ahead,
|
|
162
|
+
~checkpointId=checkpointIdOf->Array.getUnsafe(aheadIdx),
|
|
163
|
+
),
|
|
164
|
+
)
|
|
165
|
+
->ignore
|
|
166
|
+
}
|
|
167
|
+
)
|
|
168
|
+
| None => ()
|
|
169
|
+
}
|
|
136
170
|
}
|
|
137
171
|
itemIdx := itemIdx.contents + checkpointItemsCount
|
|
138
172
|
}
|
|
173
|
+
let _ = await Promise.all(lookaheadPreloads)
|
|
139
174
|
}
|
|
140
175
|
|
|
141
176
|
let registerProcessEventBatchMetrics = (
|
|
@@ -219,6 +254,7 @@ let processEventBatch = async (~batch: Batch.t, ~runtime: HandlerRuntime.t) => {
|
|
|
219
254
|
// Materialise store-backed transactions onto payloads before any handler
|
|
220
255
|
// (preload or execute) reads them.
|
|
221
256
|
await materializeBatchEvents(batch, ~chainStates)
|
|
257
|
+
PreloadLookahead.start(~items=batch.items)
|
|
222
258
|
await batch->preloadBatchOrThrow(~runtime)
|
|
223
259
|
}
|
|
224
260
|
|
|
@@ -227,6 +263,7 @@ let processEventBatch = async (~batch: Batch.t, ~runtime: HandlerRuntime.t) => {
|
|
|
227
263
|
if batch.items->Utils.Array.notEmpty {
|
|
228
264
|
await batch->runBatchHandlersOrThrow(~runtime)
|
|
229
265
|
}
|
|
266
|
+
PreloadLookahead.stop()
|
|
230
267
|
|
|
231
268
|
let elapsedTimeAfterProcessing = timeRef->Performance.secondsSince
|
|
232
269
|
|
|
@@ -14,6 +14,7 @@ import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
|
|
|
14
14
|
import * as IndexerState from "./IndexerState.res.mjs";
|
|
15
15
|
import * as ErrorHandling from "./ErrorHandling.res.mjs";
|
|
16
16
|
import * as HandlerRuntime from "./HandlerRuntime.res.mjs";
|
|
17
|
+
import * as PreloadLookahead from "./PreloadLookahead.res.mjs";
|
|
17
18
|
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
18
19
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
19
20
|
|
|
@@ -138,15 +139,39 @@ async function preloadBatchOrThrow(batch, runtime) {
|
|
|
138
139
|
|
|
139
140
|
async function runBatchHandlersOrThrow(batch, runtime) {
|
|
140
141
|
let itemIdx = 0;
|
|
142
|
+
let lookahead = PreloadLookahead.active.contents;
|
|
143
|
+
let lookaheadPreloads = [];
|
|
144
|
+
let checkpointIdOf;
|
|
145
|
+
if (lookahead !== undefined) {
|
|
146
|
+
let ids = [];
|
|
147
|
+
batch.checkpointIds.forEach((checkpointId, checkpointIdx) => {
|
|
148
|
+
for (let _for = 1, _for_finish = batch.checkpointItemsCount[checkpointIdx]; _for <= _for_finish; ++_for) {
|
|
149
|
+
ids.push(checkpointId);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
checkpointIdOf = ids;
|
|
153
|
+
} else {
|
|
154
|
+
checkpointIdOf = [];
|
|
155
|
+
}
|
|
141
156
|
for (let checkpointIdx = 0, checkpointIdx_finish = batch.checkpointIds.length; checkpointIdx < checkpointIdx_finish; ++checkpointIdx) {
|
|
142
157
|
let checkpointId = batch.checkpointIds[checkpointIdx];
|
|
143
158
|
let checkpointItemsCount = batch.checkpointItemsCount[checkpointIdx];
|
|
144
159
|
for (let idx = 0; idx < checkpointItemsCount; ++idx) {
|
|
145
|
-
let
|
|
160
|
+
let index = itemIdx + idx | 0;
|
|
161
|
+
let item = batch.items[index];
|
|
146
162
|
await runHandlerOrThrow(item, checkpointId, runtime);
|
|
163
|
+
if (lookahead !== undefined) {
|
|
164
|
+
PreloadLookahead.afterItem(lookahead, index, (ahead, aheadIdx) => {
|
|
165
|
+
if (hasHandler(ahead)) {
|
|
166
|
+
lookaheadPreloads.push(preload(runtime, ahead, checkpointIdOf[aheadIdx]));
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
}
|
|
147
171
|
}
|
|
148
172
|
itemIdx = itemIdx + checkpointItemsCount | 0;
|
|
149
173
|
}
|
|
174
|
+
await Promise.all(lookaheadPreloads);
|
|
150
175
|
}
|
|
151
176
|
|
|
152
177
|
function registerProcessEventBatchMetrics(logger, batch, indexerState, loadDuration, handlerDuration) {
|
|
@@ -195,12 +220,14 @@ async function processEventBatch(batch, runtime) {
|
|
|
195
220
|
let timeRef = Performance.now();
|
|
196
221
|
if (Utils.$$Array.notEmpty(batch.items)) {
|
|
197
222
|
await materializeBatchEvents(batch, chainStates);
|
|
223
|
+
PreloadLookahead.start(batch.items);
|
|
198
224
|
await preloadBatchOrThrow(batch, runtime);
|
|
199
225
|
}
|
|
200
226
|
let elapsedTimeAfterLoaders = Performance.secondsSince(timeRef);
|
|
201
227
|
if (Utils.$$Array.notEmpty(batch.items)) {
|
|
202
228
|
await runBatchHandlersOrThrow(batch, runtime);
|
|
203
229
|
}
|
|
230
|
+
PreloadLookahead.stop();
|
|
204
231
|
let elapsedTimeAfterProcessing = Performance.secondsSince(timeRef);
|
|
205
232
|
Writing.commitBatch(indexerState, batch);
|
|
206
233
|
let handlerDuration = elapsedTimeAfterProcessing - elapsedTimeAfterLoaders;
|