envio 3.3.0-alpha.9 → 3.3.0-rc.0
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 +19 -0
- package/licenses/CLA.md +35 -0
- package/licenses/EULA.md +67 -0
- package/licenses/LICENSE.md +45 -0
- package/licenses/README.md +35 -0
- package/package.json +9 -8
- package/src/Batch.res.mjs +1 -1
- package/src/BatchProcessing.res +0 -7
- package/src/BatchProcessing.res.mjs +1 -8
- package/src/ChainFetching.res +30 -18
- package/src/ChainFetching.res.mjs +23 -13
- package/src/ChainState.res +134 -55
- package/src/ChainState.res.mjs +78 -36
- package/src/ChainState.resi +14 -2
- package/src/Config.res +9 -5
- package/src/Config.res.mjs +2 -2
- package/src/Core.res +20 -0
- package/src/Core.res.mjs +12 -0
- package/src/CrossChainState.res +115 -34
- package/src/CrossChainState.res.mjs +37 -14
- package/src/EffectState.res +100 -0
- package/src/EffectState.res.mjs +74 -0
- package/src/EffectState.resi +32 -0
- package/src/Envio.res +25 -7
- package/src/Envio.res.mjs +15 -19
- package/src/EventConfigBuilder.res +21 -46
- package/src/EventConfigBuilder.res.mjs +18 -25
- package/src/EventProcessing.res +8 -5
- package/src/EventProcessing.res.mjs +2 -1
- package/src/FetchState.res +814 -467
- package/src/FetchState.res.mjs +541 -369
- package/src/HandlerRegister.res +3 -1
- package/src/HandlerRegister.res.mjs +3 -2
- package/src/InMemoryStore.res +14 -26
- package/src/InMemoryStore.res.mjs +6 -19
- package/src/IndexerState.res +15 -39
- package/src/IndexerState.res.mjs +18 -29
- package/src/IndexerState.resi +2 -10
- package/src/Internal.res +98 -14
- package/src/Internal.res.mjs +96 -2
- package/src/LoadLayer.res +44 -24
- package/src/LoadLayer.res.mjs +43 -20
- package/src/LoadLayer.resi +1 -0
- package/src/LogSelection.res +0 -62
- package/src/LogSelection.res.mjs +0 -74
- package/src/Metrics.res +1 -1
- package/src/Metrics.res.mjs +1 -1
- package/src/Persistence.res +12 -3
- package/src/PgStorage.res +155 -82
- package/src/PgStorage.res.mjs +130 -77
- package/src/Prometheus.res +20 -10
- package/src/Prometheus.res.mjs +22 -10
- package/src/PruneStaleHistory.res +146 -35
- package/src/PruneStaleHistory.res.mjs +114 -20
- package/src/RawEvent.res +2 -2
- package/src/Rollback.res +32 -13
- package/src/Rollback.res.mjs +24 -11
- package/src/SimulateDeadInputTracker.res +1 -1
- package/src/SimulateItems.res +38 -6
- package/src/SimulateItems.res.mjs +28 -9
- package/src/TestIndexer.res +2 -2
- package/src/TestIndexer.res.mjs +2 -2
- package/src/TopicFilter.res +1 -25
- package/src/TopicFilter.res.mjs +0 -74
- package/src/UserContext.res +62 -23
- package/src/UserContext.res.mjs +26 -6
- package/src/Writing.res +60 -21
- package/src/Writing.res.mjs +27 -9
- package/src/bindings/NodeJs.res +5 -0
- package/src/bindings/Viem.res +0 -5
- package/src/sources/EventRouter.res +0 -21
- package/src/sources/EventRouter.res.mjs +0 -12
- package/src/sources/EvmChain.res +2 -27
- package/src/sources/EvmChain.res.mjs +2 -23
- package/src/sources/EvmRpcClient.res +58 -23
- package/src/sources/EvmRpcClient.res.mjs +11 -5
- package/src/sources/HyperSync.res +9 -38
- package/src/sources/HyperSync.res.mjs +16 -28
- package/src/sources/HyperSync.resi +2 -2
- package/src/sources/HyperSyncClient.res +88 -11
- package/src/sources/HyperSyncClient.res.mjs +39 -6
- package/src/sources/HyperSyncSource.res +18 -199
- package/src/sources/HyperSyncSource.res.mjs +9 -128
- package/src/sources/Rpc.res +0 -32
- package/src/sources/Rpc.res.mjs +1 -46
- package/src/sources/RpcSource.res +129 -522
- package/src/sources/RpcSource.res.mjs +161 -366
- package/src/sources/SimulateSource.res +37 -19
- package/src/sources/SimulateSource.res.mjs +27 -10
- package/src/sources/SourceManager.res +3 -7
- package/src/sources/SourceManager.res.mjs +2 -7
- package/src/sources/SourceManager.resi +0 -2
- package/src/sources/SvmHyperSyncSource.res +13 -3
- package/src/sources/SvmHyperSyncSource.res.mjs +1 -1
package/src/SimulateItems.res
CHANGED
|
@@ -240,9 +240,12 @@ let deriveSrcAddress = (
|
|
|
240
240
|
}
|
|
241
241
|
}
|
|
242
242
|
|
|
243
|
-
let parse = (
|
|
244
|
-
|
|
245
|
-
|
|
243
|
+
let parse = (
|
|
244
|
+
~simulateItems: array<JSON.t>,
|
|
245
|
+
~config: Config.t,
|
|
246
|
+
~chainConfig: Config.chain,
|
|
247
|
+
~onEventRegistrations: array<Internal.onEventRegistration>,
|
|
248
|
+
): array<Internal.item> => {
|
|
246
249
|
let chain = ChainMap.Chain.makeUnsafe(~chainId=chainConfig.id)
|
|
247
250
|
let chainId = chainConfig.id
|
|
248
251
|
let startBlock = chainConfig.startBlock
|
|
@@ -345,6 +348,11 @@ let parse = (~simulateItems: array<JSON.t>, ~config: Config.t, ~chainConfig: Con
|
|
|
345
348
|
~chainId,
|
|
346
349
|
~eventConfig,
|
|
347
350
|
)
|
|
351
|
+
// Append into the registration array that the chain state will own and
|
|
352
|
+
// put that same registration object directly on the simulated item.
|
|
353
|
+
let onEventRegistrationIndex = onEventRegistrations->Array.length
|
|
354
|
+
let onEventRegistration = {...onEventRegistration, index: onEventRegistrationIndex}
|
|
355
|
+
onEventRegistrations->Array.push(onEventRegistration)->ignore
|
|
348
356
|
|
|
349
357
|
items
|
|
350
358
|
->Array.push(
|
|
@@ -382,7 +390,11 @@ let parse = (~simulateItems: array<JSON.t>, ~config: Config.t, ~chainConfig: Con
|
|
|
382
390
|
|
|
383
391
|
// Apply simulate source config from processConfig JSON to a Config.t
|
|
384
392
|
// This patches chainMap entries that have simulate items with CustomSources
|
|
385
|
-
let patchConfig = (
|
|
393
|
+
let patchConfig = (
|
|
394
|
+
~config: Config.t,
|
|
395
|
+
~processConfig: JSON.t,
|
|
396
|
+
~registrationsByChainId: HandlerRegister.registrationsByChainId,
|
|
397
|
+
): Config.t => {
|
|
386
398
|
let processChains: option<dict<JSON.t>> =
|
|
387
399
|
(processConfig->(Utils.magic: JSON.t => {..}))["chains"]->Nullable.toOption
|
|
388
400
|
switch processChains {
|
|
@@ -395,11 +407,31 @@ let patchConfig = (~config: Config.t, ~processConfig: JSON.t): Config.t => {
|
|
|
395
407
|
let simulateRaw: option<array<JSON.t>> = raw["simulate"]->Nullable.toOption
|
|
396
408
|
switch simulateRaw {
|
|
397
409
|
| Some(simulateItems) =>
|
|
398
|
-
let
|
|
410
|
+
let chainRegistrations = switch registrationsByChainId->Utils.Dict.dangerouslyGetNonOption(
|
|
411
|
+
chainIdStr,
|
|
412
|
+
) {
|
|
413
|
+
| Some(registrations) => registrations
|
|
414
|
+
| None =>
|
|
415
|
+
let registrations: HandlerRegister.chainRegistrations = {
|
|
416
|
+
onEventRegistrations: [],
|
|
417
|
+
onBlockRegistrations: [],
|
|
418
|
+
}
|
|
419
|
+
registrationsByChainId->Dict.set(chainIdStr, registrations)
|
|
420
|
+
registrations
|
|
421
|
+
}
|
|
399
422
|
let startBlock: int = raw["startBlock"]->(Utils.magic: 'a => int)
|
|
400
423
|
let endBlock: int = raw["endBlock"]->(Utils.magic: 'a => int)
|
|
424
|
+
// Parse with the process's startBlock so items default into the range
|
|
425
|
+
// the source will be queried over; the source now filters by range.
|
|
426
|
+
let chainConfig = {...chainConfig, startBlock, endBlock}
|
|
427
|
+
let items = parse(
|
|
428
|
+
~simulateItems,
|
|
429
|
+
~config,
|
|
430
|
+
~chainConfig,
|
|
431
|
+
~onEventRegistrations=chainRegistrations.onEventRegistrations,
|
|
432
|
+
)
|
|
401
433
|
let source = SimulateSource.make(~items, ~endBlock, ~chain)
|
|
402
|
-
{...chainConfig,
|
|
434
|
+
{...chainConfig, sourceConfig: Config.CustomSources([source])}
|
|
403
435
|
| None => chainConfig
|
|
404
436
|
}
|
|
405
437
|
| None => chainConfig
|
|
@@ -167,7 +167,7 @@ function deriveSrcAddress(providedSrcAddress, eventConfig, chainConfig, config)
|
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
function parse(simulateItems, config, chainConfig) {
|
|
170
|
+
function parse(simulateItems, config, chainConfig, onEventRegistrations) {
|
|
171
171
|
let chain = ChainMap.Chain.makeUnsafe(chainConfig.id);
|
|
172
172
|
let chainId = chainConfig.id;
|
|
173
173
|
let startBlock = chainConfig.startBlock;
|
|
@@ -254,9 +254,13 @@ function parse(simulateItems, config, chainConfig) {
|
|
|
254
254
|
seenCoordinates[coordinate] = itemIndex;
|
|
255
255
|
}
|
|
256
256
|
let onEventRegistration = HandlerRegister.buildOnEventRegistration(config, chainId, eventConfig, undefined);
|
|
257
|
+
let onEventRegistrationIndex = onEventRegistrations.length;
|
|
258
|
+
let newrecord = {...onEventRegistration};
|
|
259
|
+
newrecord.index = onEventRegistrationIndex;
|
|
260
|
+
onEventRegistrations.push(newrecord);
|
|
257
261
|
items.push({
|
|
258
262
|
kind: 0,
|
|
259
|
-
onEventRegistration:
|
|
263
|
+
onEventRegistration: newrecord,
|
|
260
264
|
chain: chain,
|
|
261
265
|
blockNumber: blockNumber,
|
|
262
266
|
logIndex: logIndex,
|
|
@@ -276,7 +280,7 @@ function parse(simulateItems, config, chainConfig) {
|
|
|
276
280
|
return items;
|
|
277
281
|
}
|
|
278
282
|
|
|
279
|
-
function patchConfig(config, processConfig) {
|
|
283
|
+
function patchConfig(config, processConfig, registrationsByChainId) {
|
|
280
284
|
let processChains = processConfig.chains;
|
|
281
285
|
if (processChains == null) {
|
|
282
286
|
return config;
|
|
@@ -291,18 +295,33 @@ function patchConfig(config, processConfig) {
|
|
|
291
295
|
if (simulateRaw == null) {
|
|
292
296
|
return chainConfig;
|
|
293
297
|
}
|
|
294
|
-
let
|
|
298
|
+
let registrations = registrationsByChainId[chainIdStr];
|
|
299
|
+
let chainRegistrations;
|
|
300
|
+
if (registrations !== undefined) {
|
|
301
|
+
chainRegistrations = registrations;
|
|
302
|
+
} else {
|
|
303
|
+
let registrations_onEventRegistrations = [];
|
|
304
|
+
let registrations_onBlockRegistrations = [];
|
|
305
|
+
let registrations$1 = {
|
|
306
|
+
onEventRegistrations: registrations_onEventRegistrations,
|
|
307
|
+
onBlockRegistrations: registrations_onBlockRegistrations
|
|
308
|
+
};
|
|
309
|
+
registrationsByChainId[chainIdStr] = registrations$1;
|
|
310
|
+
chainRegistrations = registrations$1;
|
|
311
|
+
}
|
|
295
312
|
let startBlock = processChainJson.startBlock;
|
|
296
313
|
let endBlock = processChainJson.endBlock;
|
|
297
|
-
let source = SimulateSource.make(items, endBlock, chain);
|
|
298
314
|
let newrecord = {...chainConfig};
|
|
299
|
-
newrecord.
|
|
315
|
+
newrecord.endBlock = endBlock;
|
|
316
|
+
newrecord.startBlock = startBlock;
|
|
317
|
+
let items = parse(simulateRaw, config, newrecord, chainRegistrations.onEventRegistrations);
|
|
318
|
+
let source = SimulateSource.make(items, endBlock, chain);
|
|
319
|
+
let newrecord$1 = {...newrecord};
|
|
320
|
+
newrecord$1.sourceConfig = {
|
|
300
321
|
TAG: "CustomSources",
|
|
301
322
|
_0: [source]
|
|
302
323
|
};
|
|
303
|
-
newrecord
|
|
304
|
-
newrecord.startBlock = startBlock;
|
|
305
|
-
return newrecord;
|
|
324
|
+
return newrecord$1;
|
|
306
325
|
});
|
|
307
326
|
return {
|
|
308
327
|
name: config.name,
|
package/src/TestIndexer.res
CHANGED
|
@@ -863,8 +863,8 @@ let initTestWorker = () => {
|
|
|
863
863
|
| Some(_) => ()
|
|
864
864
|
}
|
|
865
865
|
|
|
866
|
-
let patchConfig = (config: Config.t,
|
|
867
|
-
let config = SimulateItems.patchConfig(~config, ~processConfig)
|
|
866
|
+
let patchConfig = (config: Config.t, registrationsByChainId) => {
|
|
867
|
+
let config = SimulateItems.patchConfig(~config, ~processConfig, ~registrationsByChainId)
|
|
868
868
|
|
|
869
869
|
// In auto-exit mode, set batchSize=1 to process one block checkpoint at a time
|
|
870
870
|
if exitAfterFirstEventBlock {
|
package/src/TestIndexer.res.mjs
CHANGED
|
@@ -612,8 +612,8 @@ function initTestWorker() {
|
|
|
612
612
|
} else {
|
|
613
613
|
Logging.setLogLevel("silent");
|
|
614
614
|
}
|
|
615
|
-
let patchConfig = (config,
|
|
616
|
-
let config$1 = SimulateItems.patchConfig(config, processConfig);
|
|
615
|
+
let patchConfig = (config, registrationsByChainId) => {
|
|
616
|
+
let config$1 = SimulateItems.patchConfig(config, processConfig, registrationsByChainId);
|
|
617
617
|
if (exitAfterFirstEventBlock) {
|
|
618
618
|
return {
|
|
619
619
|
name: config$1.name,
|
package/src/TopicFilter.res
CHANGED
|
@@ -1,27 +1,3 @@
|
|
|
1
|
-
let toTwosComplement = (num: bigint, ~bytesLen: int) => {
|
|
2
|
-
let maxValue = 1n->BigInt.shiftLeft(BigInt.fromInt(bytesLen * 8))
|
|
3
|
-
let mask = maxValue->BigInt.sub(1n)
|
|
4
|
-
num->BigInt.add(maxValue)->BigInt.bitwiseAnd(mask)
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
let fromSignedBigInt = val => {
|
|
8
|
-
let bytesLen = 32
|
|
9
|
-
let val = val >= 0n ? val : val->toTwosComplement(~bytesLen)
|
|
10
|
-
val->Viem.bigintToHex(~options={size: bytesLen})
|
|
11
|
-
}
|
|
12
|
-
|
|
13
1
|
type hex = EvmTypes.Hex.t
|
|
14
|
-
|
|
15
|
-
type bytesHex = string
|
|
16
|
-
let keccak256 = Viem.keccak256
|
|
17
|
-
let bytesToHex = Viem.bytesToHex
|
|
18
|
-
let concat = Viem.concat
|
|
19
|
-
let castToHexUnsafe: 'a => hex = val => val->(Utils.magic: 'a => hex)
|
|
20
|
-
let fromBigInt: bigint => hex = val => val->Viem.bigintToHex(~options={size: 32})
|
|
21
|
-
let fromDynamicString: string => hex = val => val->(Utils.magic: string => hex)->keccak256
|
|
22
|
-
let fromString: string => hex = val => val->Viem.stringToHex(~options={size: 32})
|
|
2
|
+
|
|
23
3
|
let fromAddress: Address.t => hex = addr => addr->(Utils.magic: Address.t => hex)->Viem.pad
|
|
24
|
-
let fromDynamicBytes: bytesHex => hex = bytes => bytes->(Utils.magic: bytesHex => hex)->keccak256
|
|
25
|
-
let fromBytes: bytesHex => hex = bytes =>
|
|
26
|
-
bytes->(Utils.magic: bytesHex => Uint8Array.t)->Viem.bytesToHex(~options={size: 32})
|
|
27
|
-
let fromBool: bool => hex = b => b->Viem.boolToHex(~options={size: 32})
|
package/src/TopicFilter.res.mjs
CHANGED
|
@@ -1,86 +1,12 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as Viem from "viem";
|
|
4
|
-
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
5
|
-
|
|
6
|
-
function toTwosComplement(num, bytesLen) {
|
|
7
|
-
let maxValue = (1n << BigInt((bytesLen << 3)));
|
|
8
|
-
let mask = maxValue - 1n;
|
|
9
|
-
return num + maxValue & mask;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function fromSignedBigInt(val) {
|
|
13
|
-
let val$1 = val >= 0n ? val : toTwosComplement(val, 32);
|
|
14
|
-
return Viem.numberToHex(val$1, {
|
|
15
|
-
size: 32
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function keccak256(prim) {
|
|
20
|
-
return Viem.keccak256(prim);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function bytesToHex(prim0, prim1) {
|
|
24
|
-
return Viem.bytesToHex(prim0, prim1 !== undefined ? Primitive_option.valFromOption(prim1) : undefined);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function concat(prim) {
|
|
28
|
-
return Viem.concat(prim);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function castToHexUnsafe(val) {
|
|
32
|
-
return val;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function fromBigInt(val) {
|
|
36
|
-
return Viem.numberToHex(val, {
|
|
37
|
-
size: 32
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function fromDynamicString(val) {
|
|
42
|
-
return Viem.keccak256(val);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function fromString(val) {
|
|
46
|
-
return Viem.stringToHex(val, {
|
|
47
|
-
size: 32
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
4
|
|
|
51
5
|
function fromAddress(addr) {
|
|
52
6
|
return Viem.pad(addr);
|
|
53
7
|
}
|
|
54
8
|
|
|
55
|
-
function fromDynamicBytes(bytes) {
|
|
56
|
-
return Viem.keccak256(bytes);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function fromBytes(bytes) {
|
|
60
|
-
return Viem.bytesToHex(bytes, {
|
|
61
|
-
size: 32
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function fromBool(b) {
|
|
66
|
-
return Viem.boolToHex(b, {
|
|
67
|
-
size: 32
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
|
|
71
9
|
export {
|
|
72
|
-
toTwosComplement,
|
|
73
|
-
fromSignedBigInt,
|
|
74
|
-
keccak256,
|
|
75
|
-
bytesToHex,
|
|
76
|
-
concat,
|
|
77
|
-
castToHexUnsafe,
|
|
78
|
-
fromBigInt,
|
|
79
|
-
fromDynamicString,
|
|
80
|
-
fromString,
|
|
81
10
|
fromAddress,
|
|
82
|
-
fromDynamicBytes,
|
|
83
|
-
fromBytes,
|
|
84
|
-
fromBool,
|
|
85
11
|
}
|
|
86
12
|
/* viem Not a pure module */
|
package/src/UserContext.res
CHANGED
|
@@ -28,10 +28,23 @@ Utils.Object.defineProperty(
|
|
|
28
28
|
},
|
|
29
29
|
)
|
|
30
30
|
%%raw(`
|
|
31
|
-
|
|
31
|
+
// Top-level so the getter is created once, not per context. \`this\` is the
|
|
32
|
+
// EffectContext instance; only cross-chain contexts install it.
|
|
33
|
+
function throwCrossChainChainAccess() {
|
|
34
|
+
throw new Error('context.chain is not available on the cross-chain effect "' + this._effectName + '". Set \`crossChain: false\` in its options to scope the effect to a single chain, then read context.chain.id.');
|
|
35
|
+
}
|
|
36
|
+
var crossChainChainDescriptor = { get: throwCrossChainChainAccess, enumerable: true };
|
|
37
|
+
var EffectContext = function(params, chainId, effectName, defaultShouldCache, callEffect) {
|
|
32
38
|
paramsByThis.set(this, params);
|
|
33
39
|
this.effect = callEffect;
|
|
34
40
|
this.cache = defaultShouldCache;
|
|
41
|
+
if (chainId === undefined) {
|
|
42
|
+
// Cross-chain: reading context.chain throws, via the shared getter.
|
|
43
|
+
Object.defineProperty(this, "_effectName", { value: effectName });
|
|
44
|
+
Object.defineProperty(this, "chain", crossChainChainDescriptor);
|
|
45
|
+
} else {
|
|
46
|
+
this.chain = { id: chainId };
|
|
47
|
+
}
|
|
35
48
|
};
|
|
36
49
|
EffectContext.prototype = effectContextPrototype;
|
|
37
50
|
`)
|
|
@@ -39,35 +52,61 @@ EffectContext.prototype = effectContextPrototype;
|
|
|
39
52
|
@new
|
|
40
53
|
external makeEffectContext: (
|
|
41
54
|
contextParams,
|
|
55
|
+
~chainId: option<int>,
|
|
56
|
+
~effectName: string,
|
|
42
57
|
~defaultShouldCache: bool,
|
|
43
58
|
~callEffect: (Internal.effect, Internal.effectInput) => promise<Internal.effectOutput>,
|
|
44
59
|
) => Internal.effectContext = "EffectContext"
|
|
45
60
|
|
|
46
61
|
let initEffect = (params: contextParams) => {
|
|
47
|
-
let
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
62
|
+
let handlerChainId = params.item->Internal.getItemChainId
|
|
63
|
+
// A chain-scoped effect always resolves against the chain of the handler that
|
|
64
|
+
// triggered the call, even several effects deep, so the chain id is captured
|
|
65
|
+
// once from the item and reused for the whole nested-call tree.
|
|
66
|
+
let rec makeCaller = (~caller: option<Internal.effect>) => {
|
|
67
|
+
(effect: Internal.effect, input: Internal.effectInput) => {
|
|
68
|
+
let scope = effect.crossChain ? Internal.CrossChain : Internal.Chain(handlerChainId)
|
|
69
|
+
|
|
70
|
+
switch caller {
|
|
71
|
+
| Some(callerEffect) if callerEffect.crossChain && !effect.crossChain =>
|
|
72
|
+
// A cross-chain effect isn't tied to a single chain, so it has no chain
|
|
73
|
+
// to resolve a chain-scoped child against. Reject before any cache work.
|
|
74
|
+
JsError.throwWithMessage(
|
|
75
|
+
`The cross-chain effect "${callerEffect.name}" cannot call the chain-scoped effect "${effect.name}", because a cross-chain effect isn't tied to a single chain. Make "${effect.name}" cross-chain (\`crossChain: true\`), or make "${callerEffect.name}" chain-scoped (\`crossChain: false\`).`,
|
|
76
|
+
)
|
|
77
|
+
| _ => ()
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let effectContext = makeEffectContext(
|
|
81
|
+
params,
|
|
82
|
+
~chainId=switch scope {
|
|
83
|
+
| Internal.Chain(chainId) => Some(chainId)
|
|
84
|
+
| Internal.CrossChain => None
|
|
85
|
+
},
|
|
86
|
+
~effectName=effect.name,
|
|
87
|
+
~defaultShouldCache=effect.defaultShouldCache,
|
|
88
|
+
~callEffect=makeCaller(~caller=Some(effect)),
|
|
89
|
+
)
|
|
90
|
+
let effectArgs: Internal.effectArgs = {
|
|
91
|
+
input,
|
|
92
|
+
context: effectContext,
|
|
93
|
+
cacheKey: input->S.reverseConvertOrThrow(effect.input)->Utils.Hash.makeOrThrow,
|
|
94
|
+
checkpointId: params.checkpointId,
|
|
95
|
+
}
|
|
96
|
+
LoadLayer.loadEffect(
|
|
97
|
+
~loadManager=params.loadManager,
|
|
98
|
+
~persistence=params.persistence,
|
|
99
|
+
~effect,
|
|
100
|
+
~effectArgs,
|
|
101
|
+
~scope,
|
|
102
|
+
~indexerState=params.indexerState,
|
|
103
|
+
~shouldGroup=params.isPreload,
|
|
104
|
+
~item=params.item,
|
|
105
|
+
~ecosystem=params.config.ecosystem,
|
|
106
|
+
)
|
|
58
107
|
}
|
|
59
|
-
LoadLayer.loadEffect(
|
|
60
|
-
~loadManager=params.loadManager,
|
|
61
|
-
~persistence=params.persistence,
|
|
62
|
-
~effect,
|
|
63
|
-
~effectArgs,
|
|
64
|
-
~indexerState=params.indexerState,
|
|
65
|
-
~shouldGroup=params.isPreload,
|
|
66
|
-
~item=params.item,
|
|
67
|
-
~ecosystem=params.config.ecosystem,
|
|
68
|
-
)
|
|
69
108
|
}
|
|
70
|
-
|
|
109
|
+
makeCaller(~caller=None)
|
|
71
110
|
}
|
|
72
111
|
|
|
73
112
|
type entityContextParams = {
|
package/src/UserContext.res.mjs
CHANGED
|
@@ -25,17 +25,37 @@ Object.defineProperty(effectContextPrototype, "log", {
|
|
|
25
25
|
}
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
// Top-level so the getter is created once, not per context. \`this\` is the
|
|
29
|
+
// EffectContext instance; only cross-chain contexts install it.
|
|
30
|
+
function throwCrossChainChainAccess() {
|
|
31
|
+
throw new Error('context.chain is not available on the cross-chain effect "' + this._effectName + '". Set \`crossChain: false\` in its options to scope the effect to a single chain, then read context.chain.id.');
|
|
32
|
+
}
|
|
33
|
+
var crossChainChainDescriptor = { get: throwCrossChainChainAccess, enumerable: true };
|
|
34
|
+
var EffectContext = function(params, chainId, effectName, defaultShouldCache, callEffect) {
|
|
29
35
|
paramsByThis.set(this, params);
|
|
30
36
|
this.effect = callEffect;
|
|
31
37
|
this.cache = defaultShouldCache;
|
|
38
|
+
if (chainId === undefined) {
|
|
39
|
+
// Cross-chain: reading context.chain throws, via the shared getter.
|
|
40
|
+
Object.defineProperty(this, "_effectName", { value: effectName });
|
|
41
|
+
Object.defineProperty(this, "chain", crossChainChainDescriptor);
|
|
42
|
+
} else {
|
|
43
|
+
this.chain = { id: chainId };
|
|
44
|
+
}
|
|
32
45
|
};
|
|
33
46
|
EffectContext.prototype = effectContextPrototype;
|
|
34
47
|
;
|
|
35
48
|
|
|
36
49
|
function initEffect(params) {
|
|
37
|
-
let
|
|
38
|
-
|
|
50
|
+
let handlerChainId = Internal.getItemChainId(params.item);
|
|
51
|
+
let makeCaller = caller => ((effect, input) => {
|
|
52
|
+
let scope = effect.crossChain ? "crossChain" : handlerChainId;
|
|
53
|
+
if (caller !== undefined && caller.crossChain && !effect.crossChain) {
|
|
54
|
+
Stdlib_JsError.throwWithMessage(`The cross-chain effect "` + caller.name + `" cannot call the chain-scoped effect "` + effect.name + `", because a cross-chain effect isn't tied to a single chain. Make "` + effect.name + `" cross-chain (\`crossChain: true\`), or make "` + caller.name + `" chain-scoped (\`crossChain: false\`).`);
|
|
55
|
+
}
|
|
56
|
+
let tmp;
|
|
57
|
+
tmp = scope === "crossChain" ? undefined : scope;
|
|
58
|
+
let effectContext = new EffectContext(params, tmp, effect.name, effect.defaultShouldCache, makeCaller(effect));
|
|
39
59
|
let effectArgs_cacheKey = Utils.Hash.makeOrThrow(S$RescriptSchema.reverseConvertOrThrow(input, effect.input));
|
|
40
60
|
let effectArgs_checkpointId = params.checkpointId;
|
|
41
61
|
let effectArgs = {
|
|
@@ -44,9 +64,9 @@ function initEffect(params) {
|
|
|
44
64
|
cacheKey: effectArgs_cacheKey,
|
|
45
65
|
checkpointId: effectArgs_checkpointId
|
|
46
66
|
};
|
|
47
|
-
return LoadLayer.loadEffect(params.loadManager, params.persistence, effect, effectArgs, params.indexerState, params.isPreload, params.item, params.config.ecosystem);
|
|
48
|
-
};
|
|
49
|
-
return
|
|
67
|
+
return LoadLayer.loadEffect(params.loadManager, params.persistence, effect, effectArgs, scope, params.indexerState, params.isPreload, params.item, params.config.ecosystem);
|
|
68
|
+
});
|
|
69
|
+
return makeCaller(undefined);
|
|
50
70
|
}
|
|
51
71
|
|
|
52
72
|
function getWhereHandler(params, filter) {
|
package/src/Writing.res
CHANGED
|
@@ -16,8 +16,8 @@ let getChangesCount = (state: IndexerState.t) => {
|
|
|
16
16
|
total := total.contents +. (state->InMemoryStore.getInMemTable(~entityConfig)).changesCount
|
|
17
17
|
})
|
|
18
18
|
state
|
|
19
|
-
->IndexerState.
|
|
20
|
-
->
|
|
19
|
+
->IndexerState.effectState
|
|
20
|
+
->EffectState.forEach(inMemTable => {
|
|
21
21
|
total := total.contents +. inMemTable.changesCount
|
|
22
22
|
})
|
|
23
23
|
state
|
|
@@ -38,9 +38,9 @@ let waitForCommit = (state: IndexerState.t): promise<unit> =>
|
|
|
38
38
|
let snapshotEffects = (state: IndexerState.t, ~cache): array<Persistence.updatedEffectCache> => {
|
|
39
39
|
let acc = []
|
|
40
40
|
state
|
|
41
|
-
->IndexerState.
|
|
42
|
-
->
|
|
43
|
-
let {idsToStore, dict, effect, invalidationsCount} = inMemTable
|
|
41
|
+
->IndexerState.effectState
|
|
42
|
+
->EffectState.forEach(inMemTable => {
|
|
43
|
+
let {idsToStore, dict, effect, invalidationsCount, scope, table} = inMemTable
|
|
44
44
|
switch idsToStore {
|
|
45
45
|
| [] => ()
|
|
46
46
|
| ids =>
|
|
@@ -51,17 +51,33 @@ let snapshotEffects = (state: IndexerState.t, ~cache): array<Persistence.updated
|
|
|
51
51
|
}
|
|
52
52
|
)
|
|
53
53
|
let effectName = effect.name
|
|
54
|
-
let
|
|
54
|
+
let tableName = table.tableName
|
|
55
|
+
let effectCacheRecord = switch cache->Utils.Dict.dangerouslyGetNonOption(tableName) {
|
|
55
56
|
| Some(c) => c
|
|
56
57
|
| None =>
|
|
57
|
-
let c: Persistence.effectCacheRecord = {effectName, count: 0}
|
|
58
|
-
cache->Dict.set(
|
|
58
|
+
let c: Persistence.effectCacheRecord = {effectName, scope, tableName, count: 0}
|
|
59
|
+
cache->Dict.set(tableName, c)
|
|
59
60
|
c
|
|
60
61
|
}
|
|
61
62
|
let shouldInitialize = effectCacheRecord.count === 0
|
|
62
63
|
effectCacheRecord.count = effectCacheRecord.count + items->Array.length - invalidationsCount
|
|
63
|
-
Prometheus.EffectCacheCount.set(
|
|
64
|
-
|
|
64
|
+
Prometheus.EffectCacheCount.set(
|
|
65
|
+
~count=effectCacheRecord.count,
|
|
66
|
+
~effectName,
|
|
67
|
+
~scope=scope->Internal.EffectCache.scopeToString,
|
|
68
|
+
)
|
|
69
|
+
acc
|
|
70
|
+
->Array.push(
|
|
71
|
+
(
|
|
72
|
+
{
|
|
73
|
+
table,
|
|
74
|
+
itemSchema: effect.storageMeta.itemSchema,
|
|
75
|
+
items,
|
|
76
|
+
shouldInitialize,
|
|
77
|
+
}: Persistence.updatedEffectCache
|
|
78
|
+
),
|
|
79
|
+
)
|
|
80
|
+
->ignore
|
|
65
81
|
}
|
|
66
82
|
inMemTable.idsToStore = []
|
|
67
83
|
inMemTable.invalidationsCount = 0
|
|
@@ -113,17 +129,36 @@ let runOneWrite = async (state: IndexerState.t) => {
|
|
|
113
129
|
})
|
|
114
130
|
let updatedEffectsCache = snapshotEffects(state, ~cache)
|
|
115
131
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
~
|
|
123
|
-
~
|
|
124
|
-
~chainMetaData,
|
|
132
|
+
let writtenEntityNames = Utils.Set.make()
|
|
133
|
+
updatedEntities->Array.forEach(({entityConfig}) =>
|
|
134
|
+
writtenEntityNames->Utils.Set.add(entityConfig.name)->ignore
|
|
135
|
+
)
|
|
136
|
+
let pruneTargets = PruneStaleHistory.select(
|
|
137
|
+
state,
|
|
138
|
+
~writtenEntityNames,
|
|
139
|
+
~isRollback=rollback->Option.isSome,
|
|
125
140
|
)
|
|
126
141
|
|
|
142
|
+
// The prune runs concurrently with the batch write, but only for entities
|
|
143
|
+
// absent from it, so they never touch the same history table. Both must be
|
|
144
|
+
// awaited before the next write starts, otherwise a still-running prune
|
|
145
|
+
// could overlap the next batch's history writes and lose an anchor (which
|
|
146
|
+
// breaks a later rollback). Rollback writes touch every history table, so
|
|
147
|
+
// they get no concurrent prune at all.
|
|
148
|
+
let _ = await Promise.all2((
|
|
149
|
+
persistence.storage.writeBatch(
|
|
150
|
+
~batch,
|
|
151
|
+
~rollback,
|
|
152
|
+
~isInReorgThreshold=batch.isInReorgThreshold,
|
|
153
|
+
~config,
|
|
154
|
+
~allEntities=persistence.allEntities,
|
|
155
|
+
~updatedEntities,
|
|
156
|
+
~updatedEffectsCache,
|
|
157
|
+
~chainMetaData,
|
|
158
|
+
),
|
|
159
|
+
PruneStaleHistory.runConcurrent(state, ~targets=pruneTargets),
|
|
160
|
+
))
|
|
161
|
+
|
|
127
162
|
state->IndexerState.markCommitted(~upToCheckpointId)
|
|
128
163
|
|
|
129
164
|
switch rollback {
|
|
@@ -131,6 +166,10 @@ let runOneWrite = async (state: IndexerState.t) => {
|
|
|
131
166
|
await RollbackCommit.fire(~progressBlockNumberByChainId)
|
|
132
167
|
| _ => ()
|
|
133
168
|
}
|
|
169
|
+
|
|
170
|
+
// Entities starved of the concurrent prune (eg written in every batch) are
|
|
171
|
+
// pruned here with no other pg writer running.
|
|
172
|
+
await PruneStaleHistory.runForced(state, ~targets=pruneTargets)
|
|
134
173
|
}
|
|
135
174
|
}
|
|
136
175
|
|
|
@@ -192,8 +231,8 @@ let dropCommitted = (state: IndexerState.t, ~keepLoadedFromDb) => {
|
|
|
192
231
|
->InMemoryTable.Entity.dropCommittedChanges(~committedCheckpointId, ~keepLoadedFromDb)
|
|
193
232
|
)
|
|
194
233
|
state
|
|
195
|
-
->IndexerState.
|
|
196
|
-
->
|
|
234
|
+
->IndexerState.effectState
|
|
235
|
+
->EffectState.forEach(inMemTable =>
|
|
197
236
|
inMemTable->InMemoryStore.dropCommittedEffects(~committedCheckpointId, ~keepLoadedFromDb)
|
|
198
237
|
)
|
|
199
238
|
}
|