envio 3.15.0-subgraph → 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 +3 -3
- package/fuel.schema.json +2 -2
- package/index.d.ts +3 -3
- package/licenses/README.md +1 -1
- package/package.json +8 -8
- package/src/Batch.res.mjs +5 -5
- package/src/BatchProcessing.res +1 -0
- package/src/BatchProcessing.res.mjs +1 -0
- package/src/ChainFetching.res +1 -6
- package/src/ChainFetching.res.mjs +1 -1
- package/src/ChainState.res +48 -7
- package/src/ChainState.res.mjs +21 -8
- package/src/Clock.res +57 -0
- package/src/Clock.res.mjs +72 -0
- package/src/Config.res +16 -4
- package/src/Config.res.mjs +5 -3
- package/src/Core.res +4 -40
- package/src/CrossChainState.res +10 -6
- package/src/CrossChainState.res.mjs +1 -1
- 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 +4 -0
- package/src/Env.res.mjs +5 -0
- package/src/FetchState.res +157 -69
- package/src/FetchState.res.mjs +112 -47
- package/src/FinalizeBackfill.res +2 -4
- package/src/FinalizeBackfill.res.mjs +3 -4
- 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 +0 -49
- package/src/Internal.res.mjs +1 -80
- package/src/LoadLayer.res +17 -10
- package/src/LoadLayer.res.mjs +11 -8
- package/src/Main.res.mjs +1 -1
- package/src/Persistence.res +3 -23
- package/src/Persistence.res.mjs +3 -3
- package/src/PreloadLookahead.res +42 -24
- package/src/PreloadLookahead.res.mjs +51 -30
- package/src/Rollback.res +0 -1
- package/src/Rollback.res.mjs +1 -1
- package/src/SafeCheckpointTracking.res +4 -4
- package/src/SafeCheckpointTracking.res.mjs +1 -1
- 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 +278 -973
- package/src/Storage.res.mjs +218 -720
- package/src/Supervisor.res +75 -56
- package/src/Supervisor.res.mjs +55 -26
- package/src/SyncWrites.res +24 -18
- package/src/SyncWrites.res.mjs +11 -11
- 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 +13 -4
- package/src/UserContext.res.mjs +5 -5
- package/src/Utils.res +0 -6
- package/src/Utils.res.mjs +0 -5
- 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 +107 -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/ChainSources.res +2 -0
- package/src/sources/ChainSources.res.mjs +2 -2
- package/src/sources/EvmChain.res +3 -0
- package/src/sources/EvmChain.res.mjs +4 -2
- 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/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 +85 -104
- package/src/sources/SourceManager.res.mjs +18 -37
- package/src/sources/SourceManager.resi +3 -5
- package/src/sources/StartBlockResolver.res +1 -0
- package/src/sources/StartBlockResolver.res.mjs +4 -3
- package/src/sources/SvmHyperSyncSource.res +2 -2
- package/src/sources/SvmHyperSyncSource.res.mjs +1 -1
- package/src/subgraph/graph-ts.ts +87 -28
- package/src/subgraph/runtime.ts +0 -1
- package/src/subgraph/scope.ts +0 -1
- package/svm.schema.json +3 -3
- 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 -60
package/src/Supervisor.res
CHANGED
|
@@ -374,74 +374,93 @@ let releaseIfAtHead = group =>
|
|
|
374
374
|
)
|
|
375
375
|
}
|
|
376
376
|
|
|
377
|
-
// The run's chains as
|
|
378
|
-
//
|
|
379
|
-
//
|
|
380
|
-
|
|
377
|
+
// The run's chains as a worker reports them before it has fetched anything:
|
|
378
|
+
// where `ChainState.makeFromDbState` resumes them. A display draws these for a
|
|
379
|
+
// worker it hasn't heard from yet, and its first sight of a chain is what it
|
|
380
|
+
// measures the sync pace from, so a resumed chain drawn from its start block
|
|
381
|
+
// would pass the earlier runs' progress off as this one's.
|
|
382
|
+
let resumedChains = (config: Config.t, ~resumed: array<Persistence.initialChainState>): array<
|
|
383
|
+
Metrics.chainMetrics,
|
|
384
|
+
> =>
|
|
381
385
|
config.chainMap
|
|
382
386
|
->ChainMap.values
|
|
383
387
|
->Array.map((chain): Metrics.chainMetrics => {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
timestampCaughtUpToHeadOrEndblock
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
388
|
+
let row = resumed->Array.find(row => row.id == chain.id)
|
|
389
|
+
let startBlock = row->Option.mapOr(chain->Config.startBlockOrZero, row => row.startBlock)
|
|
390
|
+
let endBlock = row->Option.mapOr(chain.endBlock, row => row.endBlock)
|
|
391
|
+
let progressBlockNumber = switch row {
|
|
392
|
+
| Some({progressBlockNumber}) if progressBlockNumber >= 0 => progressBlockNumber
|
|
393
|
+
| _ => startBlock - 1
|
|
394
|
+
}
|
|
395
|
+
let sourceBlockNumber = row->Option.mapOr(0, row => row.sourceBlockNumber)
|
|
396
|
+
let timestampCaughtUpToHeadOrEndblock =
|
|
397
|
+
row->Option.flatMap(row => row.timestampCaughtUpToHeadOrEndblock)
|
|
398
|
+
let metrics: Metrics.chainMetrics = {
|
|
399
|
+
chainId: chain.id,
|
|
400
|
+
poweredByHyperSync: switch chain.sourceConfig {
|
|
401
|
+
| EvmSourceConfig({hypersync}) => hypersync->Option.isSome
|
|
402
|
+
| FuelSourceConfig(_) | SvmSourceConfig(_) => true
|
|
403
|
+
| SimulateSourceConfig(_) | CustomSources(_) => false
|
|
404
|
+
},
|
|
405
|
+
firstEventBlockNumber: row->Option.flatMap(row => row.firstEventBlockNumber),
|
|
406
|
+
latestProcessedBlock: progressBlockNumber === -1 ? None : Some(progressBlockNumber),
|
|
407
|
+
timestampCaughtUpToHeadOrEndblock,
|
|
408
|
+
numEventsProcessed: row->Option.mapOr(0., row => row.numEventsProcessed),
|
|
409
|
+
latestFetchedBlockNumber: Pervasives.max(progressBlockNumber, 0),
|
|
410
|
+
knownHeight: sourceBlockNumber,
|
|
411
|
+
numBatchesFetched: 0,
|
|
412
|
+
startBlock,
|
|
413
|
+
endBlock,
|
|
414
|
+
numAddresses: 0,
|
|
415
|
+
addressesByContract: [],
|
|
416
|
+
isReady: timestampCaughtUpToHeadOrEndblock->Option.isSome,
|
|
417
|
+
sourceBlockNumber,
|
|
418
|
+
progressBlockNumber,
|
|
419
|
+
progressLatencyMs: None,
|
|
420
|
+
progressBlockTime: row->Option.flatMap(row => row.progressBlockTime),
|
|
421
|
+
concurrency: 0,
|
|
422
|
+
partitionsCount: 0,
|
|
423
|
+
bufferSize: 0,
|
|
424
|
+
bufferBlockNumber: progressBlockNumber,
|
|
425
|
+
idleSeconds: 0.,
|
|
426
|
+
waitingForNewBlockSeconds: 0.,
|
|
427
|
+
queryingSeconds: 0.,
|
|
428
|
+
blockRangeFetchSeconds: 0.,
|
|
429
|
+
blockRangeParseSeconds: 0.,
|
|
430
|
+
blockRangeFetchCount: 0.,
|
|
431
|
+
blockRangeFetchedEvents: 0.,
|
|
432
|
+
blockRangeFetchedBlocks: 0.,
|
|
433
|
+
reorgCount: 0,
|
|
434
|
+
reorgDetectedBlock: None,
|
|
435
|
+
rollbackTargetBlock: None,
|
|
436
|
+
rateLimitTimeMs: 0.,
|
|
437
|
+
rateLimitResetInMs: None,
|
|
438
|
+
}
|
|
439
|
+
switch (metrics->Metrics.hasProcessedToEndblock, endBlock) {
|
|
440
|
+
| (true, Some(endBlock)) => {...metrics, knownHeight: endBlock}
|
|
441
|
+
| _ => metrics
|
|
442
|
+
}
|
|
428
443
|
})
|
|
429
444
|
|
|
430
445
|
// Per worker rather than all or nothing: a worker done within its first report
|
|
431
446
|
// interval is heard from only as it exits, and a display holding just its
|
|
432
447
|
// siblings' finished chains would read as synced while it still indexes.
|
|
433
|
-
let displayedSnapshots = (
|
|
434
|
-
|
|
448
|
+
let displayedSnapshots = (
|
|
449
|
+
running: array<running>,
|
|
450
|
+
~resumedChains: array<Metrics.chainMetrics>,
|
|
451
|
+
~empty: Metrics.t,
|
|
452
|
+
) =>
|
|
435
453
|
running->Array.map(r =>
|
|
436
454
|
switch r.snapshot {
|
|
437
455
|
| Some(snapshot) => snapshot
|
|
438
456
|
| None => {
|
|
439
457
|
...empty,
|
|
440
|
-
chains:
|
|
458
|
+
chains: resumedChains->Array.filter(chain =>
|
|
459
|
+
r.worker.chainIds->Array.includes(chain.chainId)
|
|
460
|
+
),
|
|
441
461
|
}
|
|
442
462
|
}
|
|
443
463
|
)
|
|
444
|
-
}
|
|
445
464
|
|
|
446
465
|
// Runs the group: creates the schema for every chain, forks a worker per plan
|
|
447
466
|
// entry, and serves the run's metrics, console and display from what they
|
|
@@ -481,10 +500,9 @@ let run = async (~config: Config.t, ~logger, ~workers: array<worker>, ~reset) =>
|
|
|
481
500
|
let shouldUseTui = Tui.shouldUse()
|
|
482
501
|
// A run that resumed with every chain already caught up owes nobody a wait:
|
|
483
502
|
// its workers start realtime and there is no barrier to open.
|
|
503
|
+
let resumed = (persistence->Persistence.getInitializedState).chains
|
|
484
504
|
let holdRealtime =
|
|
485
|
-
|
|
486
|
-
chain.timestampCaughtUpToHeadOrEndblock->Option.isNone
|
|
487
|
-
)
|
|
505
|
+
resumed->Array.some(chain => chain.timestampCaughtUpToHeadOrEndblock->Option.isNone)
|
|
488
506
|
let group = {running: [], stopping: false, holdingRealtime: holdRealtime}
|
|
489
507
|
group.running =
|
|
490
508
|
workers->Array.mapWithIndex((worker, workerIndex) =>
|
|
@@ -534,8 +552,9 @@ let run = async (~config: Config.t, ~logger, ~workers: array<worker>, ~reset) =>
|
|
|
534
552
|
)
|
|
535
553
|
|
|
536
554
|
if shouldUseTui {
|
|
555
|
+
let resumedChains = resumedChains(config, ~resumed)
|
|
537
556
|
Tui.start(~config, ~logger, ~getMetrics=() =>
|
|
538
|
-
group.running->displayedSnapshots(~
|
|
557
|
+
group.running->displayedSnapshots(~resumedChains, ~empty=[]->merge)->merge
|
|
539
558
|
)
|
|
540
559
|
}
|
|
541
560
|
|
package/src/Supervisor.res.mjs
CHANGED
|
@@ -22,6 +22,7 @@ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
|
22
22
|
import * as Child_process from "child_process";
|
|
23
23
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
24
24
|
import * as CrossChainState from "./CrossChainState.res.mjs";
|
|
25
|
+
import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
|
|
25
26
|
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
26
27
|
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
27
28
|
|
|
@@ -313,8 +314,20 @@ function releaseIfAtHead(group) {
|
|
|
313
314
|
}
|
|
314
315
|
}
|
|
315
316
|
|
|
316
|
-
function
|
|
317
|
+
function resumedChains(config, resumed) {
|
|
317
318
|
return ChainMap.values(config.chainMap).map(chain => {
|
|
319
|
+
let row = resumed.find(row => Primitive_object.equal(row.id, chain.id));
|
|
320
|
+
let startBlock = Stdlib_Option.mapOr(row, Config.startBlockOrZero(chain), row => row.startBlock);
|
|
321
|
+
let endBlock = Stdlib_Option.mapOr(row, chain.endBlock, row => row.endBlock);
|
|
322
|
+
let progressBlockNumber;
|
|
323
|
+
if (row !== undefined) {
|
|
324
|
+
let progressBlockNumber$1 = row.progressBlockNumber;
|
|
325
|
+
progressBlockNumber = progressBlockNumber$1 >= 0 ? progressBlockNumber$1 : startBlock - 1 | 0;
|
|
326
|
+
} else {
|
|
327
|
+
progressBlockNumber = startBlock - 1 | 0;
|
|
328
|
+
}
|
|
329
|
+
let sourceBlockNumber = Stdlib_Option.mapOr(row, 0, row => row.sourceBlockNumber);
|
|
330
|
+
let timestampCaughtUpToHeadOrEndblock = Stdlib_Option.flatMap(row, row => row.timestampCaughtUpToHeadOrEndblock);
|
|
318
331
|
let match = chain.sourceConfig;
|
|
319
332
|
let tmp;
|
|
320
333
|
switch (match.TAG) {
|
|
@@ -330,32 +343,37 @@ function configuredChains(config) {
|
|
|
330
343
|
tmp = false;
|
|
331
344
|
break;
|
|
332
345
|
}
|
|
333
|
-
let
|
|
334
|
-
let
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
346
|
+
let metrics_chainId = chain.id;
|
|
347
|
+
let metrics_firstEventBlockNumber = Stdlib_Option.flatMap(row, row => row.firstEventBlockNumber);
|
|
348
|
+
let metrics_latestProcessedBlock = progressBlockNumber === -1 ? undefined : progressBlockNumber;
|
|
349
|
+
let metrics_numEventsProcessed = Stdlib_Option.mapOr(row, 0, row => row.numEventsProcessed);
|
|
350
|
+
let metrics_latestFetchedBlockNumber = Primitive_int.max(progressBlockNumber, 0);
|
|
351
|
+
let metrics_addressesByContract = [];
|
|
352
|
+
let metrics_isReady = Stdlib_Option.isSome(timestampCaughtUpToHeadOrEndblock);
|
|
353
|
+
let metrics_progressBlockTime = Stdlib_Option.flatMap(row, row => row.progressBlockTime);
|
|
354
|
+
let metrics = {
|
|
355
|
+
chainId: metrics_chainId,
|
|
338
356
|
poweredByHyperSync: tmp,
|
|
339
|
-
firstEventBlockNumber:
|
|
340
|
-
latestProcessedBlock:
|
|
341
|
-
timestampCaughtUpToHeadOrEndblock:
|
|
342
|
-
numEventsProcessed:
|
|
343
|
-
latestFetchedBlockNumber:
|
|
344
|
-
knownHeight:
|
|
357
|
+
firstEventBlockNumber: metrics_firstEventBlockNumber,
|
|
358
|
+
latestProcessedBlock: metrics_latestProcessedBlock,
|
|
359
|
+
timestampCaughtUpToHeadOrEndblock: timestampCaughtUpToHeadOrEndblock,
|
|
360
|
+
numEventsProcessed: metrics_numEventsProcessed,
|
|
361
|
+
latestFetchedBlockNumber: metrics_latestFetchedBlockNumber,
|
|
362
|
+
knownHeight: sourceBlockNumber,
|
|
345
363
|
numBatchesFetched: 0,
|
|
346
|
-
startBlock:
|
|
347
|
-
endBlock:
|
|
364
|
+
startBlock: startBlock,
|
|
365
|
+
endBlock: endBlock,
|
|
348
366
|
numAddresses: 0,
|
|
349
|
-
addressesByContract:
|
|
350
|
-
isReady:
|
|
351
|
-
sourceBlockNumber:
|
|
352
|
-
progressBlockNumber:
|
|
367
|
+
addressesByContract: metrics_addressesByContract,
|
|
368
|
+
isReady: metrics_isReady,
|
|
369
|
+
sourceBlockNumber: sourceBlockNumber,
|
|
370
|
+
progressBlockNumber: progressBlockNumber,
|
|
353
371
|
progressLatencyMs: undefined,
|
|
354
|
-
progressBlockTime:
|
|
372
|
+
progressBlockTime: metrics_progressBlockTime,
|
|
355
373
|
concurrency: 0,
|
|
356
374
|
partitionsCount: 0,
|
|
357
375
|
bufferSize: 0,
|
|
358
|
-
bufferBlockNumber:
|
|
376
|
+
bufferBlockNumber: progressBlockNumber,
|
|
359
377
|
idleSeconds: 0,
|
|
360
378
|
waitingForNewBlockSeconds: 0,
|
|
361
379
|
queryingSeconds: 0,
|
|
@@ -370,18 +388,27 @@ function configuredChains(config) {
|
|
|
370
388
|
rateLimitTimeMs: 0,
|
|
371
389
|
rateLimitResetInMs: undefined
|
|
372
390
|
};
|
|
391
|
+
let match$1 = Metrics.hasProcessedToEndblock(metrics);
|
|
392
|
+
if (!match$1) {
|
|
393
|
+
return metrics;
|
|
394
|
+
}
|
|
395
|
+
if (endBlock === undefined) {
|
|
396
|
+
return metrics;
|
|
397
|
+
}
|
|
398
|
+
let newrecord = {...metrics};
|
|
399
|
+
newrecord.knownHeight = endBlock;
|
|
400
|
+
return newrecord;
|
|
373
401
|
});
|
|
374
402
|
}
|
|
375
403
|
|
|
376
|
-
function displayedSnapshots(running,
|
|
377
|
-
let configured = configuredChains(config);
|
|
404
|
+
function displayedSnapshots(running, resumedChains, empty) {
|
|
378
405
|
return running.map(r => {
|
|
379
406
|
let snapshot = r.snapshot;
|
|
380
407
|
if (snapshot !== undefined) {
|
|
381
408
|
return snapshot;
|
|
382
409
|
}
|
|
383
410
|
let newrecord = {...empty};
|
|
384
|
-
newrecord.chains =
|
|
411
|
+
newrecord.chains = resumedChains.filter(chain => r.worker.chainIds.includes(chain.chainId));
|
|
385
412
|
return newrecord;
|
|
386
413
|
});
|
|
387
414
|
}
|
|
@@ -402,7 +429,8 @@ async function run(config, logger, workers, reset) {
|
|
|
402
429
|
maxConnections: Env.Db.maxConnections
|
|
403
430
|
});
|
|
404
431
|
let shouldUseTui = Tui.shouldUse(undefined, undefined);
|
|
405
|
-
let
|
|
432
|
+
let resumed = Persistence.getInitializedState(persistence).chains;
|
|
433
|
+
let holdRealtime = resumed.some(chain => Stdlib_Option.isNone(chain.timestampCaughtUpToHeadOrEndblock));
|
|
406
434
|
let group = {
|
|
407
435
|
running: [],
|
|
408
436
|
stopping: false,
|
|
@@ -421,7 +449,8 @@ async function run(config, logger, workers, reset) {
|
|
|
421
449
|
runtime
|
|
422
450
|
]))), config.isDev);
|
|
423
451
|
if (shouldUseTui) {
|
|
424
|
-
|
|
452
|
+
let resumedChains$1 = resumedChains(config, resumed);
|
|
453
|
+
Tui.start(config, logger, () => merge(displayedSnapshots(group.running, resumedChains$1, merge([]))));
|
|
425
454
|
}
|
|
426
455
|
process.on("SIGTERM", () => stop(group));
|
|
427
456
|
process.on("SIGINT", () => stop(group));
|
|
@@ -465,7 +494,7 @@ export {
|
|
|
465
494
|
awaitExit,
|
|
466
495
|
isRunAtHead,
|
|
467
496
|
releaseIfAtHead,
|
|
468
|
-
|
|
497
|
+
resumedChains,
|
|
469
498
|
displayedSnapshots,
|
|
470
499
|
run,
|
|
471
500
|
}
|
package/src/SyncWrites.res
CHANGED
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
// applied: the replay has to see the store exactly as the first round did, or a
|
|
4
4
|
// read-modify-write lands once per round.
|
|
5
5
|
|
|
6
|
+
// A Map rather than a dict: every round makes a fresh one keyed by ids no
|
|
7
|
+
// other round shares, and a dict would give each of them its own hidden class.
|
|
6
8
|
type table = {
|
|
7
9
|
entityConfig: Internal.entityConfig,
|
|
8
10
|
scope: Internal.chainScope,
|
|
9
|
-
changes:
|
|
11
|
+
changes: Map.t<string, Change.t<Internal.entity>>,
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
// A handler runs on one chain, so an entity's scope is fixed for the whole
|
|
@@ -19,11 +21,11 @@ let record = (writes: t, ~entityConfig: Internal.entityConfig, ~scope, change) =
|
|
|
19
21
|
let table = switch writes->Utils.Dict.dangerouslyGetNonOption(entityConfig.name) {
|
|
20
22
|
| Some(table) => table
|
|
21
23
|
| None =>
|
|
22
|
-
let table = {entityConfig, scope, changes:
|
|
24
|
+
let table = {entityConfig, scope, changes: Map.make()}
|
|
23
25
|
writes->Dict.set(entityConfig.name, table)
|
|
24
26
|
table
|
|
25
27
|
}
|
|
26
|
-
table.changes->
|
|
28
|
+
table.changes->Map.set(entityConfig.table.entityIdToKey(change->Change.getEntityId), change)
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
let changesOf = (writes: t, ~entityConfig: Internal.entityConfig) =>
|
|
@@ -33,11 +35,11 @@ let changesOf = (writes: t, ~entityConfig: Internal.entityConfig) =>
|
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
// `Some(None)` is a delete this round made: the entity is known not to exist.
|
|
36
|
-
let getById = (writes: t, ~entityConfig, ~entityId: string) =>
|
|
38
|
+
let getById = (writes: t, ~entityConfig: Internal.entityConfig, ~entityId: string) =>
|
|
37
39
|
switch writes->changesOf(~entityConfig) {
|
|
38
40
|
| Some(changes) =>
|
|
39
41
|
changes
|
|
40
|
-
->
|
|
42
|
+
->Map.get(entityConfig.table.entityIdToKey(entityId->EntityId.unsafeOfString))
|
|
41
43
|
->Option.map(InMemoryTable.Entity.mapChangeToEntity)
|
|
42
44
|
| None => None
|
|
43
45
|
}
|
|
@@ -56,17 +58,21 @@ let overlayFilter = (
|
|
|
56
58
|
let matches = filter->EntityFilter.makeMatcher(~table=entityConfig.table)
|
|
57
59
|
let untouched =
|
|
58
60
|
entities->Array.filter(entity =>
|
|
59
|
-
!(
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
| Some(entity) if matches(entity) => Some(entity)
|
|
67
|
-
| _ => None
|
|
68
|
-
}
|
|
61
|
+
!(
|
|
62
|
+
changes->Map.has(
|
|
63
|
+
entityConfig.table.entityIdToKey(
|
|
64
|
+
(entity->(Utils.magic: Internal.entity => {"id": EntityId.t}))["id"],
|
|
65
|
+
),
|
|
66
|
+
)
|
|
67
|
+
)
|
|
69
68
|
)
|
|
69
|
+
let written = []
|
|
70
|
+
changes->Map.forEach(change =>
|
|
71
|
+
switch change->InMemoryTable.Entity.mapChangeToEntity {
|
|
72
|
+
| Some(entity) if matches(entity) => written->Array.push(entity)->ignore
|
|
73
|
+
| _ => ()
|
|
74
|
+
}
|
|
75
|
+
)
|
|
70
76
|
untouched->Array.concat(written)
|
|
71
77
|
}
|
|
72
78
|
|
|
@@ -78,7 +84,7 @@ let write = (indexerState, ~entityConfig: Internal.entityConfig, ~scope, change)
|
|
|
78
84
|
PreloadLookahead.onSet(
|
|
79
85
|
~entityName=entityConfig.name,
|
|
80
86
|
~scope,
|
|
81
|
-
~entityId=entityId
|
|
87
|
+
~entityId=entityConfig.table.entityIdToKey(entityId),
|
|
82
88
|
)
|
|
83
89
|
| Delete(_) => ()
|
|
84
90
|
}
|
|
@@ -92,10 +98,10 @@ let write = (indexerState, ~entityConfig: Internal.entityConfig, ~scope, change)
|
|
|
92
98
|
|
|
93
99
|
let forEachChange = (writes: t, fn) =>
|
|
94
100
|
writes->Utils.Dict.forEach(({entityConfig, scope, changes}) =>
|
|
95
|
-
changes->
|
|
101
|
+
changes->Map.forEach(change => fn(~entityConfig, ~scope, change))
|
|
96
102
|
)
|
|
97
103
|
|
|
98
104
|
let commit = (writes: t, ~indexerState) =>
|
|
99
105
|
writes->Utils.Dict.forEach(({entityConfig, scope, changes}) =>
|
|
100
|
-
changes->
|
|
106
|
+
changes->Map.forEach(change => indexerState->write(~entityConfig, ~scope, change))
|
|
101
107
|
)
|
package/src/SyncWrites.res.mjs
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as Utils from "./Utils.res.mjs";
|
|
4
|
-
import * as EntityId from "./EntityId.res.mjs";
|
|
5
4
|
import * as EntityFilter from "./db/EntityFilter.res.mjs";
|
|
6
5
|
import * as IndexerState from "./IndexerState.res.mjs";
|
|
7
|
-
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
8
6
|
import * as InMemoryStore from "./InMemoryStore.res.mjs";
|
|
9
7
|
import * as InMemoryTable from "./InMemoryTable.res.mjs";
|
|
10
8
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
@@ -20,7 +18,7 @@ function record(writes, entityConfig, scope, change) {
|
|
|
20
18
|
if (table !== undefined) {
|
|
21
19
|
table$1 = table;
|
|
22
20
|
} else {
|
|
23
|
-
let table_changes =
|
|
21
|
+
let table_changes = new Map();
|
|
24
22
|
let table$2 = {
|
|
25
23
|
entityConfig: entityConfig,
|
|
26
24
|
scope: scope,
|
|
@@ -29,7 +27,7 @@ function record(writes, entityConfig, scope, change) {
|
|
|
29
27
|
writes[entityConfig.name] = table$2;
|
|
30
28
|
table$1 = table$2;
|
|
31
29
|
}
|
|
32
|
-
table$1.changes
|
|
30
|
+
table$1.changes.set(entityConfig.table.entityIdToKey(change.entityId), change);
|
|
33
31
|
}
|
|
34
32
|
|
|
35
33
|
function changesOf(writes, entityConfig) {
|
|
@@ -42,7 +40,7 @@ function changesOf(writes, entityConfig) {
|
|
|
42
40
|
function getById(writes, entityConfig, entityId) {
|
|
43
41
|
let changes = changesOf(writes, entityConfig);
|
|
44
42
|
if (changes !== undefined) {
|
|
45
|
-
return Stdlib_Option.map(changes
|
|
43
|
+
return Stdlib_Option.map(changes.get(entityConfig.table.entityIdToKey(entityId)), InMemoryTable.Entity.mapChangeToEntity);
|
|
46
44
|
}
|
|
47
45
|
}
|
|
48
46
|
|
|
@@ -52,11 +50,13 @@ function overlayFilter(writes, entities, entityConfig, filter) {
|
|
|
52
50
|
return entities;
|
|
53
51
|
}
|
|
54
52
|
let matches = EntityFilter.makeMatcher(filter, entityConfig.table);
|
|
55
|
-
let untouched = entities.filter(entity => !(
|
|
56
|
-
let written =
|
|
53
|
+
let untouched = entities.filter(entity => !changes.has(entityConfig.table.entityIdToKey(entity.id)));
|
|
54
|
+
let written = [];
|
|
55
|
+
changes.forEach(change => {
|
|
57
56
|
let entity = InMemoryTable.Entity.mapChangeToEntity(change);
|
|
58
57
|
if (entity !== undefined && matches(entity)) {
|
|
59
|
-
|
|
58
|
+
written.push(entity);
|
|
59
|
+
return;
|
|
60
60
|
}
|
|
61
61
|
});
|
|
62
62
|
return untouched.concat(written);
|
|
@@ -64,7 +64,7 @@ function overlayFilter(writes, entities, entityConfig, filter) {
|
|
|
64
64
|
|
|
65
65
|
function write(indexerState, entityConfig, scope, change) {
|
|
66
66
|
if (change.type === "SET") {
|
|
67
|
-
PreloadLookahead.onSet(entityConfig.name, scope,
|
|
67
|
+
PreloadLookahead.onSet(entityConfig.name, scope, entityConfig.table.entityIdToKey(change.entityId));
|
|
68
68
|
}
|
|
69
69
|
InMemoryTable.Entity.set(InMemoryStore.getInMemTable(indexerState, entityConfig, scope), IndexerState.committedCheckpointIdFor(indexerState, scope), change);
|
|
70
70
|
}
|
|
@@ -73,7 +73,7 @@ function forEachChange(writes, fn) {
|
|
|
73
73
|
Utils.Dict.forEach(writes, param => {
|
|
74
74
|
let scope = param.scope;
|
|
75
75
|
let entityConfig = param.entityConfig;
|
|
76
|
-
|
|
76
|
+
param.changes.forEach(change => fn(entityConfig, scope, change));
|
|
77
77
|
});
|
|
78
78
|
}
|
|
79
79
|
|
|
@@ -81,7 +81,7 @@ function commit(writes, indexerState) {
|
|
|
81
81
|
Utils.Dict.forEach(writes, param => {
|
|
82
82
|
let scope = param.scope;
|
|
83
83
|
let entityConfig = param.entityConfig;
|
|
84
|
-
|
|
84
|
+
param.changes.forEach(change => write(indexerState, entityConfig, scope, change));
|
|
85
85
|
});
|
|
86
86
|
}
|
|
87
87
|
|
package/src/TestIndexer.res
CHANGED
|
@@ -53,11 +53,13 @@ let renderRows = (rows: array<AddressRows.row>, ~config: Config.t) =>
|
|
|
53
53
|
|
|
54
54
|
// Rows of a per-chain entity are keyed per (chain, id): the same id exists
|
|
55
55
|
// independently on every chain.
|
|
56
|
-
let rowKey = (~scope: Internal.chainScope, ~entityId: EntityId.t) =>
|
|
56
|
+
let rowKey = (~table: Table.table, ~scope: Internal.chainScope, ~entityId: EntityId.t) => {
|
|
57
|
+
let entityKey = table.entityIdToKey(entityId)
|
|
57
58
|
switch scope {
|
|
58
|
-
| CrossChain =>
|
|
59
|
-
| Chain(chainId) => `${chainId->ChainId.toString}|${
|
|
59
|
+
| CrossChain => entityKey
|
|
60
|
+
| Chain(chainId) => `${chainId->ChainId.toString}|${entityKey}`
|
|
60
61
|
}
|
|
62
|
+
}
|
|
61
63
|
|
|
62
64
|
let readChainId = (entity: Internal.entity, ~field: Table.field): option<ChainId.t> =>
|
|
63
65
|
entity
|
|
@@ -158,19 +160,18 @@ let handleWriteBatch = (
|
|
|
158
160
|
switch change {
|
|
159
161
|
| Set({entityId, entity, checkpointId}) =>
|
|
160
162
|
// The store keeps decoded entities so load comparisons (bigint /
|
|
161
|
-
// BigDecimal) work on real values.
|
|
162
|
-
// since they may be string/int/bigint.
|
|
163
|
+
// BigDecimal) work on real values.
|
|
163
164
|
let storedEntity = switch (chainIdField, scope->Internal.chainScopeChainId) {
|
|
164
165
|
| (Some(field), Some(chainId)) =>
|
|
165
166
|
entity->Internal.stampChainId(~fieldName=field.fieldName, ~chainId)
|
|
166
167
|
| _ => entity
|
|
167
168
|
}
|
|
168
|
-
entityDict->Dict.set(rowKey(~scope, ~entityId), storedEntity)
|
|
169
|
+
entityDict->Dict.set(rowKey(~table=entityConfig.table, ~scope, ~entityId), storedEntity)
|
|
169
170
|
// The change already carries the checkpoint's chainId, so the entity
|
|
170
171
|
// inside it stays unstamped.
|
|
171
172
|
entityChangeFor(checkpointId).sets->Array.push(entity->Utils.magic)->ignore
|
|
172
173
|
| Delete({entityId, checkpointId}) =>
|
|
173
|
-
Dict.delete(entityDict->Obj.magic, rowKey(~scope, ~entityId))
|
|
174
|
+
Dict.delete(entityDict->Obj.magic, rowKey(~table=entityConfig.table, ~scope, ~entityId))
|
|
174
175
|
entityChangeFor(checkpointId).deleted->Array.push(entityId)->ignore
|
|
175
176
|
}
|
|
176
177
|
}
|
|
@@ -433,10 +434,15 @@ let getEntityFromState = (
|
|
|
433
434
|
)
|
|
434
435
|
}
|
|
435
436
|
let entityDict = state.entities->Dict.get(entityConfig.name)->Option.getOr(Dict.make())
|
|
437
|
+
let {entityIdToKey} = entityConfig.table
|
|
438
|
+
let key = entityIdToKey(entityId->EntityId.unsafeOfString)
|
|
436
439
|
switch entityConfig.table->Table.getChainIdField {
|
|
437
|
-
| None => entityDict->Dict.get(
|
|
440
|
+
| None => entityDict->Dict.get(key)->Option.map(copyEntity)
|
|
438
441
|
| Some(field) =>
|
|
439
|
-
let matches =
|
|
442
|
+
let matches =
|
|
443
|
+
entityDict
|
|
444
|
+
->Dict.valuesToArray
|
|
445
|
+
->Array.filter(entity => entityIdToKey(entity.id->EntityId.unsafeOfString) === key)
|
|
440
446
|
switch matches {
|
|
441
447
|
| [] => None
|
|
442
448
|
| [entity] => Some(copyEntity(entity))
|
|
@@ -508,7 +514,7 @@ let makeEntitySet = (~state: testIndexerState, ~entityConfig: Internal.entityCon
|
|
|
508
514
|
}
|
|
509
515
|
}
|
|
510
516
|
entityDict->Dict.set(
|
|
511
|
-
rowKey(~scope, ~entityId=entity.id->EntityId.unsafeOfString),
|
|
517
|
+
rowKey(~table=entityConfig.table, ~scope, ~entityId=entity.id->EntityId.unsafeOfString),
|
|
512
518
|
copyEntity(entity),
|
|
513
519
|
)
|
|
514
520
|
}
|
|
@@ -568,13 +574,7 @@ let makeInMemoryStorage = (~state: testIndexerState): Persistence.storage => {
|
|
|
568
574
|
// The runner injects the config-derived initial state by setting
|
|
569
575
|
// `persistence.storageStatus = Ready(...)` directly, bypassing `Persistence.init`,
|
|
570
576
|
// so none of these is reached.
|
|
571
|
-
initialize: async (
|
|
572
|
-
~chainConfigs as _=?,
|
|
573
|
-
~entities as _=?,
|
|
574
|
-
~enums as _=?,
|
|
575
|
-
~contractMapping as _,
|
|
576
|
-
~envioInfo as _,
|
|
577
|
-
) =>
|
|
577
|
+
initialize: async (~chainConfigs as _=?, ~contractMapping as _, ~envioInfo as _) =>
|
|
578
578
|
JsError.throwWithMessage(
|
|
579
579
|
"TestIndexer: initialize should not be called; the initial state is derived from config.",
|
|
580
580
|
),
|
|
@@ -582,11 +582,11 @@ let makeInMemoryStorage = (~state: testIndexerState): Persistence.storage => {
|
|
|
582
582
|
JsError.throwWithMessage(
|
|
583
583
|
"TestIndexer: readStoredConfig should not be called; the initial state is derived from config.",
|
|
584
584
|
),
|
|
585
|
-
addChain: async (~chainConfig as _, ~
|
|
585
|
+
addChain: async (~chainConfig as _, ~contractMapping as _) =>
|
|
586
586
|
JsError.throwWithMessage(
|
|
587
587
|
"TestIndexer: addChain should not be called; the initial state is derived from config.",
|
|
588
588
|
),
|
|
589
|
-
resumeInitialState: async (~
|
|
589
|
+
resumeInitialState: async (~chainIds as _, ~contractMapping as _) =>
|
|
590
590
|
JsError.throwWithMessage(
|
|
591
591
|
"TestIndexer: resumeInitialState should not be called; the initial state is derived from config.",
|
|
592
592
|
),
|
|
@@ -596,13 +596,12 @@ let makeInMemoryStorage = (~state: testIndexerState): Persistence.storage => {
|
|
|
596
596
|
->(Utils.magic: array<Internal.entity> => array<unknown>),
|
|
597
597
|
// The in-memory storage has no indexes to build, and it's always ready.
|
|
598
598
|
ensureQueryIndexes: async (~entityConfig as _, ~scope as _, ~filters as _) => (),
|
|
599
|
-
ensureSchemaIndexes: async (~
|
|
600
|
-
finalizeBackfill: async (~
|
|
599
|
+
ensureSchemaIndexes: async (~chainIds as _) => (),
|
|
600
|
+
finalizeBackfill: async (~chainIds as _, ~readyAt as _) => (),
|
|
601
601
|
writeBatch: async (
|
|
602
602
|
~batch,
|
|
603
603
|
~rollback as _,
|
|
604
604
|
~config,
|
|
605
|
-
~allEntities as _,
|
|
606
605
|
~updatedEffectsCache as _,
|
|
607
606
|
~updatedEntities,
|
|
608
607
|
~registeredAddresses,
|
|
@@ -623,11 +622,7 @@ let makeInMemoryStorage = (~state: testIndexerState): Persistence.storage => {
|
|
|
623
622
|
setChainMeta: async _ => Obj.magic(),
|
|
624
623
|
pruneStaleCheckpoints: async (~safeCheckpoints as _) => (),
|
|
625
624
|
pruneStaleEntityHistory: async (~entityConfig as _, ~safeCheckpoints as _) => (),
|
|
626
|
-
getRollbackTargetCheckpoint: async (
|
|
627
|
-
~reorgChainId as _,
|
|
628
|
-
~lastKnownValidBlockNumber as _,
|
|
629
|
-
~sequence as _,
|
|
630
|
-
) =>
|
|
625
|
+
getRollbackTargetCheckpoint: async (~reorgChainId as _, ~lastKnownValidBlockNumber as _) =>
|
|
631
626
|
JsError.throwWithMessage(
|
|
632
627
|
"TestIndexer: Rollback is not supported. The runner forces rollbackOnReorg off, so this should be unreachable.",
|
|
633
628
|
),
|