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.
Files changed (114) hide show
  1. package/README.md +40 -34
  2. package/evm.schema.json +3 -3
  3. package/fuel.schema.json +2 -2
  4. package/index.d.ts +3 -3
  5. package/licenses/README.md +1 -1
  6. package/package.json +8 -8
  7. package/src/Batch.res.mjs +5 -5
  8. package/src/BatchProcessing.res +1 -0
  9. package/src/BatchProcessing.res.mjs +1 -0
  10. package/src/ChainFetching.res +1 -6
  11. package/src/ChainFetching.res.mjs +1 -1
  12. package/src/ChainState.res +48 -7
  13. package/src/ChainState.res.mjs +21 -8
  14. package/src/Clock.res +57 -0
  15. package/src/Clock.res.mjs +72 -0
  16. package/src/Config.res +16 -4
  17. package/src/Config.res.mjs +5 -3
  18. package/src/Core.res +4 -40
  19. package/src/CrossChainState.res +10 -6
  20. package/src/CrossChainState.res.mjs +1 -1
  21. package/src/EffectState.res +10 -2
  22. package/src/EffectState.res.mjs +8 -4
  23. package/src/EffectState.resi +3 -1
  24. package/src/EntityId.res +2 -7
  25. package/src/EntityId.res.mjs +1 -8
  26. package/src/EntityTables.res +4 -1
  27. package/src/EntityTables.res.mjs +1 -1
  28. package/src/Env.res +4 -0
  29. package/src/Env.res.mjs +5 -0
  30. package/src/FetchState.res +157 -69
  31. package/src/FetchState.res.mjs +112 -47
  32. package/src/FinalizeBackfill.res +2 -4
  33. package/src/FinalizeBackfill.res.mjs +3 -4
  34. package/src/InMemoryTable.res +17 -16
  35. package/src/InMemoryTable.res.mjs +14 -15
  36. package/src/IndexerState.res +28 -4
  37. package/src/IndexerState.res.mjs +19 -6
  38. package/src/IndexerState.resi +4 -0
  39. package/src/Internal.res +0 -49
  40. package/src/Internal.res.mjs +1 -80
  41. package/src/LoadLayer.res +17 -10
  42. package/src/LoadLayer.res.mjs +11 -8
  43. package/src/Main.res.mjs +1 -1
  44. package/src/Persistence.res +3 -23
  45. package/src/Persistence.res.mjs +3 -3
  46. package/src/PreloadLookahead.res +42 -24
  47. package/src/PreloadLookahead.res.mjs +51 -30
  48. package/src/Rollback.res +0 -1
  49. package/src/Rollback.res.mjs +1 -1
  50. package/src/SafeCheckpointTracking.res +4 -4
  51. package/src/SafeCheckpointTracking.res.mjs +1 -1
  52. package/src/StagedRows.res +106 -0
  53. package/src/StagedRows.res.mjs +102 -0
  54. package/src/Staging.res +32 -14
  55. package/src/Staging.res.mjs +17 -20
  56. package/src/Storage.res +278 -973
  57. package/src/Storage.res.mjs +218 -720
  58. package/src/Supervisor.res +75 -56
  59. package/src/Supervisor.res.mjs +55 -26
  60. package/src/SyncWrites.res +24 -18
  61. package/src/SyncWrites.res.mjs +11 -11
  62. package/src/TestIndexer.res +22 -27
  63. package/src/TestIndexer.res.mjs +20 -17
  64. package/src/Throttler.res +15 -8
  65. package/src/Throttler.res.mjs +9 -6
  66. package/src/Throttler.resi +1 -1
  67. package/src/UserContext.res +13 -4
  68. package/src/UserContext.res.mjs +5 -5
  69. package/src/Utils.res +0 -6
  70. package/src/Utils.res.mjs +0 -5
  71. package/src/Writing.res +0 -1
  72. package/src/Writing.res.mjs +1 -1
  73. package/src/bindings/ClickHouse.res +0 -322
  74. package/src/bindings/ClickHouse.res.mjs +0 -338
  75. package/src/bindings/StorageClient.res +107 -174
  76. package/src/bindings/StorageClient.res.mjs +19 -8
  77. package/src/bindings/Vitest.res +0 -26
  78. package/src/bindings/Vitest.res.mjs +0 -3
  79. package/src/db/Table.res +24 -150
  80. package/src/db/Table.res.mjs +38 -272
  81. package/src/sources/ChainSources.res +2 -0
  82. package/src/sources/ChainSources.res.mjs +2 -2
  83. package/src/sources/EvmChain.res +3 -0
  84. package/src/sources/EvmChain.res.mjs +4 -2
  85. package/src/sources/EvmHyperSyncSource.res +2 -2
  86. package/src/sources/EvmHyperSyncSource.res.mjs +1 -1
  87. package/src/sources/EvmRpcClient.res +13 -3
  88. package/src/sources/EvmRpcClient.res.mjs +4 -3
  89. package/src/sources/EvmRpcWs.res +4 -3
  90. package/src/sources/EvmRpcWs.res.mjs +2 -2
  91. package/src/sources/HeightFeed.res +31 -10
  92. package/src/sources/HeightFeed.res.mjs +10 -6
  93. package/src/sources/HeightStream.res +32 -17
  94. package/src/sources/HeightStream.res.mjs +10 -10
  95. package/src/sources/HyperSyncSSE.res +4 -6
  96. package/src/sources/HyperSyncSSE.res.mjs +2 -2
  97. package/src/sources/RpcSource.res +10 -1
  98. package/src/sources/RpcSource.res.mjs +4 -3
  99. package/src/sources/Source.res +3 -0
  100. package/src/sources/SourceManager.res +85 -104
  101. package/src/sources/SourceManager.res.mjs +18 -37
  102. package/src/sources/SourceManager.resi +3 -5
  103. package/src/sources/StartBlockResolver.res +1 -0
  104. package/src/sources/StartBlockResolver.res.mjs +4 -3
  105. package/src/sources/SvmHyperSyncSource.res +2 -2
  106. package/src/sources/SvmHyperSyncSource.res.mjs +1 -1
  107. package/src/subgraph/graph-ts.ts +87 -28
  108. package/src/subgraph/runtime.ts +0 -1
  109. package/src/subgraph/scope.ts +0 -1
  110. package/svm.schema.json +3 -3
  111. package/src/PgWriting.res +0 -36
  112. package/src/PgWriting.res.mjs +0 -34
  113. package/src/db/IndexDefinition.res +0 -47
  114. package/src/db/IndexDefinition.res.mjs +0 -60
@@ -374,74 +374,93 @@ let releaseIfAtHead = group =>
374
374
  )
375
375
  }
376
376
 
377
- // The run's chains as an unsplit indexer reports them before it has fetched
378
- // anything: at their configured blocks, with nothing indexed. A display draws
379
- // these for a worker it hasn't heard from yet.
380
- let configuredChains = (config: Config.t): array<Metrics.chainMetrics> =>
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
- chainId: chain.id,
385
- poweredByHyperSync: switch chain.sourceConfig {
386
- | EvmSourceConfig({hypersync}) => hypersync->Option.isSome
387
- | FuelSourceConfig(_) | SvmSourceConfig(_) => true
388
- | SimulateSourceConfig(_) | CustomSources(_) => false
389
- },
390
- firstEventBlockNumber: None,
391
- latestProcessedBlock: None,
392
- timestampCaughtUpToHeadOrEndblock: None,
393
- numEventsProcessed: 0.,
394
- latestFetchedBlockNumber: 0,
395
- knownHeight: 0,
396
- numBatchesFetched: 0,
397
- // A chain resolves `start_block: latest` against its own head as it starts,
398
- // which is a worker's to do and no supervisor's to guess.
399
- startBlock: switch chain.startBlock {
400
- | Block(block) => block
401
- | Latest => 0
402
- },
403
- endBlock: chain.endBlock,
404
- numAddresses: 0,
405
- addressesByContract: [],
406
- isReady: false,
407
- sourceBlockNumber: 0,
408
- progressBlockNumber: -1,
409
- progressLatencyMs: None,
410
- progressBlockTime: None,
411
- concurrency: 0,
412
- partitionsCount: 0,
413
- bufferSize: 0,
414
- bufferBlockNumber: -1,
415
- idleSeconds: 0.,
416
- waitingForNewBlockSeconds: 0.,
417
- queryingSeconds: 0.,
418
- blockRangeFetchSeconds: 0.,
419
- blockRangeParseSeconds: 0.,
420
- blockRangeFetchCount: 0.,
421
- blockRangeFetchedEvents: 0.,
422
- blockRangeFetchedBlocks: 0.,
423
- reorgCount: 0,
424
- reorgDetectedBlock: None,
425
- rollbackTargetBlock: None,
426
- rateLimitTimeMs: 0.,
427
- rateLimitResetInMs: None,
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 = (running: array<running>, ~config, ~empty: Metrics.t) => {
434
- let configured = configuredChains(config)
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: configured->Array.filter(chain => r.worker.chainIds->Array.includes(chain.chainId)),
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
- (persistence->Persistence.getInitializedState).chains->Array.some(chain =>
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(~config, ~empty=[]->merge)->merge
557
+ group.running->displayedSnapshots(~resumedChains, ~empty=[]->merge)->merge
539
558
  )
540
559
  }
541
560
 
@@ -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 configuredChains(config) {
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 block = chain.startBlock;
334
- let tmp$1;
335
- tmp$1 = block === "latest" ? 0 : block;
336
- return {
337
- chainId: chain.id,
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: undefined,
340
- latestProcessedBlock: undefined,
341
- timestampCaughtUpToHeadOrEndblock: undefined,
342
- numEventsProcessed: 0,
343
- latestFetchedBlockNumber: 0,
344
- knownHeight: 0,
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: tmp$1,
347
- endBlock: chain.endBlock,
364
+ startBlock: startBlock,
365
+ endBlock: endBlock,
348
366
  numAddresses: 0,
349
- addressesByContract: [],
350
- isReady: false,
351
- sourceBlockNumber: 0,
352
- progressBlockNumber: -1,
367
+ addressesByContract: metrics_addressesByContract,
368
+ isReady: metrics_isReady,
369
+ sourceBlockNumber: sourceBlockNumber,
370
+ progressBlockNumber: progressBlockNumber,
353
371
  progressLatencyMs: undefined,
354
- progressBlockTime: undefined,
372
+ progressBlockTime: metrics_progressBlockTime,
355
373
  concurrency: 0,
356
374
  partitionsCount: 0,
357
375
  bufferSize: 0,
358
- bufferBlockNumber: -1,
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, config, empty) {
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 = configured.filter(chain => r.worker.chainIds.includes(chain.chainId));
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 holdRealtime = Persistence.getInitializedState(persistence).chains.some(chain => Stdlib_Option.isNone(chain.timestampCaughtUpToHeadOrEndblock));
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
- Tui.start(config, logger, () => merge(displayedSnapshots(group.running, config, merge([]))));
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
- configuredChains,
497
+ resumedChains,
469
498
  displayedSnapshots,
470
499
  run,
471
500
  }
@@ -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: dict<Change.t<Internal.entity>>,
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: Dict.make()}
24
+ let table = {entityConfig, scope, changes: Map.make()}
23
25
  writes->Dict.set(entityConfig.name, table)
24
26
  table
25
27
  }
26
- table.changes->Dict.set(change->Change.getEntityId->EntityId.toKey, change)
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
- ->Utils.Dict.dangerouslyGetNonOption(entityId)
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
- !(changes->Dict.has(entity->InMemoryTable.Entity.getEntityIdUnsafe))
60
- )
61
- let written =
62
- changes
63
- ->Dict.valuesToArray
64
- ->Array.filterMap(change =>
65
- switch change->InMemoryTable.Entity.mapChangeToEntity {
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->EntityId.toKey,
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->Utils.Dict.forEach(change => fn(~entityConfig, ~scope, change))
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->Utils.Dict.forEach(change => indexerState->write(~entityConfig, ~scope, change))
106
+ changes->Map.forEach(change => indexerState->write(~entityConfig, ~scope, change))
101
107
  )
@@ -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[EntityId.toKey(change.entityId)] = change;
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[entityId], InMemoryTable.Entity.mapChangeToEntity);
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 => !(InMemoryTable.Entity.getEntityIdUnsafe(entity) in changes));
56
- let written = Stdlib_Array.filterMap(Object.values(changes), change => {
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
- return entity;
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, EntityId.toKey(change.entityId));
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
- Utils.Dict.forEach(param.changes, change => fn(entityConfig, scope, change));
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
- Utils.Dict.forEach(param.changes, change => write(indexerState, entityConfig, scope, change));
84
+ param.changes.forEach(change => write(indexerState, entityConfig, scope, change));
85
85
  });
86
86
  }
87
87
 
@@ -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 => entityId->EntityId.toKey
59
- | Chain(chainId) => `${chainId->ChainId.toString}|${entityId->EntityId.toKey}`
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. Ids are keyed by their string form
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(entityId)->Option.map(copyEntity)
440
+ | None => entityDict->Dict.get(key)->Option.map(copyEntity)
438
441
  | Some(field) =>
439
- let matches = entityDict->Dict.valuesToArray->Array.filter(entity => entity.id === entityId)
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 _, ~entities as _, ~contractMapping 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 (~entities as _, ~chainIds as _, ~contractMapping as _) =>
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 (~entities as _, ~chainIds as _) => (),
600
- finalizeBackfill: async (~entities as _, ~chainIds as _, ~readyAt as _) => (),
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
  ),