envio 3.15.0 → 3.16.0-subgraph

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/README.md +40 -34
  2. package/evm.schema.json +11 -2
  3. package/fuel.schema.json +10 -1
  4. package/index.d.ts +6 -0
  5. package/licenses/README.md +1 -1
  6. package/package.json +10 -9
  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/Bin.res +7 -0
  11. package/src/Bin.res.mjs +8 -0
  12. package/src/ChainFetching.res +450 -16
  13. package/src/ChainFetching.res.mjs +403 -32
  14. package/src/ChainState.res +124 -7
  15. package/src/ChainState.res.mjs +128 -67
  16. package/src/ChainState.resi +26 -0
  17. package/src/Clock.res +57 -0
  18. package/src/Clock.res.mjs +72 -0
  19. package/src/Config.res +31 -3
  20. package/src/Config.res.mjs +15 -4
  21. package/src/ContractRegisterContext.res +3 -6
  22. package/src/ContractRegisterContext.res.mjs +4 -2
  23. package/src/Core.res +95 -36
  24. package/src/Core.res.mjs +90 -1
  25. package/src/CrossChainState.res +12 -2
  26. package/src/CrossChainState.res.mjs +3 -3
  27. package/src/Ecosystem.res +7 -4
  28. package/src/Ecosystem.res.mjs +8 -8
  29. package/src/EffectCacheLookup.res +98 -0
  30. package/src/EffectCacheLookup.res.mjs +116 -0
  31. package/src/EffectState.res +10 -2
  32. package/src/EffectState.res.mjs +8 -4
  33. package/src/EffectState.resi +3 -1
  34. package/src/EntityId.res +2 -7
  35. package/src/EntityId.res.mjs +1 -8
  36. package/src/EntityTables.res +4 -1
  37. package/src/EntityTables.res.mjs +1 -1
  38. package/src/Env.res +20 -0
  39. package/src/Env.res.mjs +16 -0
  40. package/src/EventProcessing.res +38 -1
  41. package/src/EventProcessing.res.mjs +28 -1
  42. package/src/FetchState.res +408 -73
  43. package/src/FetchState.res.mjs +361 -91
  44. package/src/FinalizeBackfill.res +2 -4
  45. package/src/FinalizeBackfill.res.mjs +3 -4
  46. package/src/HandlerLoader.res +39 -15
  47. package/src/HandlerLoader.res.mjs +27 -8
  48. package/src/HandlerRegister.res +4 -0
  49. package/src/HandlerRegister.res.mjs +5 -2
  50. package/src/HandlerRegister.resi +2 -0
  51. package/src/InMemoryTable.res +17 -16
  52. package/src/InMemoryTable.res.mjs +14 -15
  53. package/src/IndexerState.res +28 -4
  54. package/src/IndexerState.res.mjs +19 -6
  55. package/src/IndexerState.resi +4 -0
  56. package/src/Internal.res +61 -49
  57. package/src/Internal.res.mjs +36 -80
  58. package/src/LoadLayer.res +54 -15
  59. package/src/LoadLayer.res.mjs +75 -45
  60. package/src/LoadLayer.resi +26 -0
  61. package/src/Main.res +14 -1
  62. package/src/Main.res.mjs +4 -3
  63. package/src/Persistence.res +3 -23
  64. package/src/Persistence.res.mjs +3 -3
  65. package/src/PreloadLookahead.res +202 -0
  66. package/src/PreloadLookahead.res.mjs +218 -0
  67. package/src/Rollback.res +0 -1
  68. package/src/Rollback.res.mjs +1 -1
  69. package/src/RunScope.res +3 -0
  70. package/src/SafeCheckpointTracking.res +4 -4
  71. package/src/SafeCheckpointTracking.res.mjs +1 -1
  72. package/src/SimulateItems.res +42 -26
  73. package/src/SimulateItems.res.mjs +40 -16
  74. package/src/StagedRows.res +106 -0
  75. package/src/StagedRows.res.mjs +102 -0
  76. package/src/Staging.res +32 -14
  77. package/src/Staging.res.mjs +17 -20
  78. package/src/Storage.res +303 -948
  79. package/src/Storage.res.mjs +229 -708
  80. package/src/SubgraphLog.res +19 -0
  81. package/src/SubgraphLog.res.mjs +26 -0
  82. package/src/Supervisor.res +75 -56
  83. package/src/Supervisor.res.mjs +55 -26
  84. package/src/SyncWrites.res +107 -0
  85. package/src/SyncWrites.res.mjs +98 -0
  86. package/src/TestIndexer.res +22 -27
  87. package/src/TestIndexer.res.mjs +20 -17
  88. package/src/Throttler.res +15 -8
  89. package/src/Throttler.res.mjs +9 -6
  90. package/src/Throttler.resi +1 -1
  91. package/src/UserContext.res +419 -46
  92. package/src/UserContext.res.mjs +318 -30
  93. package/src/Utils.res +17 -6
  94. package/src/Utils.res.mjs +12 -6
  95. package/src/Writing.res +0 -1
  96. package/src/Writing.res.mjs +1 -1
  97. package/src/bindings/ClickHouse.res +0 -322
  98. package/src/bindings/ClickHouse.res.mjs +0 -338
  99. package/src/bindings/StorageClient.res +108 -174
  100. package/src/bindings/StorageClient.res.mjs +19 -8
  101. package/src/bindings/Vitest.res +0 -26
  102. package/src/bindings/Vitest.res.mjs +0 -3
  103. package/src/db/Table.res +24 -150
  104. package/src/db/Table.res.mjs +38 -272
  105. package/src/sources/AddressSet.res +2 -0
  106. package/src/sources/AddressStore.res +3 -0
  107. package/src/sources/CallSource.res +274 -0
  108. package/src/sources/CallSource.res.mjs +196 -0
  109. package/src/sources/ChainSources.res +2 -0
  110. package/src/sources/ChainSources.res.mjs +2 -2
  111. package/src/sources/EvmChain.res +10 -1
  112. package/src/sources/EvmChain.res.mjs +6 -3
  113. package/src/sources/EvmHyperSyncSource.res +2 -2
  114. package/src/sources/EvmHyperSyncSource.res.mjs +1 -1
  115. package/src/sources/EvmRpcClient.res +13 -3
  116. package/src/sources/EvmRpcClient.res.mjs +4 -3
  117. package/src/sources/EvmRpcWs.res +4 -3
  118. package/src/sources/EvmRpcWs.res.mjs +2 -2
  119. package/src/sources/HeightFeed.res +31 -10
  120. package/src/sources/HeightFeed.res.mjs +10 -6
  121. package/src/sources/HeightStream.res +32 -17
  122. package/src/sources/HeightStream.res.mjs +10 -10
  123. package/src/sources/HyperSync.res +2 -1
  124. package/src/sources/HyperSync.res.mjs +2 -1
  125. package/src/sources/HyperSyncSSE.res +4 -6
  126. package/src/sources/HyperSyncSSE.res.mjs +2 -2
  127. package/src/sources/RpcSource.res +10 -1
  128. package/src/sources/RpcSource.res.mjs +4 -3
  129. package/src/sources/Source.res +3 -0
  130. package/src/sources/SourceManager.res +84 -77
  131. package/src/sources/SourceManager.res.mjs +17 -20
  132. package/src/sources/SourceManager.resi +3 -4
  133. package/src/sources/StartBlockResolver.res +1 -0
  134. package/src/sources/StartBlockResolver.res.mjs +3 -2
  135. package/src/sources/SvmHyperSyncSource.res +2 -2
  136. package/src/sources/SvmHyperSyncSource.res.mjs +1 -1
  137. package/src/subgraph/abi-types.ts +84 -0
  138. package/src/subgraph/aggregations.ts +745 -0
  139. package/src/subgraph/as-package.ts +143 -0
  140. package/src/subgraph/assemblyscript-regex.ts +67 -0
  141. package/src/subgraph/assemblyscript.ts +731 -0
  142. package/src/subgraph/blocks.ts +398 -0
  143. package/src/subgraph/calls-source.ts +702 -0
  144. package/src/subgraph/calls.ts +258 -0
  145. package/src/subgraph/conformance.ts +106 -0
  146. package/src/subgraph/errors.ts +85 -0
  147. package/src/subgraph/graft.ts +73 -0
  148. package/src/subgraph/graph-cli-worker.mjs +35 -0
  149. package/src/subgraph/graph-cli.ts +900 -0
  150. package/src/subgraph/graph-node-decimal.ts +199 -0
  151. package/src/subgraph/graph-ts-enums.ts +28 -0
  152. package/src/subgraph/graph-ts-types/VERSION +2 -0
  153. package/src/subgraph/graph-ts-types/chain/arweave.d.ts +70 -0
  154. package/src/subgraph/graph-ts-types/chain/cosmos.d.ts +327 -0
  155. package/src/subgraph/graph-ts-types/chain/ethereum.d.ts +233 -0
  156. package/src/subgraph/graph-ts-types/chain/near.d.ts +253 -0
  157. package/src/subgraph/graph-ts-types/chain/starknet.d.ts +32 -0
  158. package/src/subgraph/graph-ts-types/common/collections.d.ts +136 -0
  159. package/src/subgraph/graph-ts-types/common/conversion.d.ts +11 -0
  160. package/src/subgraph/graph-ts-types/common/datasource.d.ts +30 -0
  161. package/src/subgraph/graph-ts-types/common/eager-offset.d.ts +0 -0
  162. package/src/subgraph/graph-ts-types/common/json.d.ts +17 -0
  163. package/src/subgraph/graph-ts-types/common/numbers.d.ts +120 -0
  164. package/src/subgraph/graph-ts-types/common/value.d.ts +120 -0
  165. package/src/subgraph/graph-ts-types/common/yaml.d.ts +90 -0
  166. package/src/subgraph/graph-ts-types/global/global.d.ts +194 -0
  167. package/src/subgraph/graph-ts-types/helper-functions.d.ts +22 -0
  168. package/src/subgraph/graph-ts-types/index.d.ts +102 -0
  169. package/src/subgraph/graph-ts.ts +4339 -0
  170. package/src/subgraph/hosts.ts +554 -0
  171. package/src/subgraph/log.ts +28 -0
  172. package/src/subgraph/receipts.ts +184 -0
  173. package/src/subgraph/register-slots.ts +93 -0
  174. package/src/subgraph/rollup.ts +109 -0
  175. package/src/subgraph/rpc.ts +337 -0
  176. package/src/subgraph/runtime.ts +2059 -0
  177. package/src/subgraph/scope.ts +176 -0
  178. package/src/subgraph/stats.ts +42 -0
  179. package/src/subgraph/template-instances.ts +66 -0
  180. package/src/subgraph/toolchain.ts +327 -0
  181. package/src/subgraph/transient.ts +176 -0
  182. package/svm.schema.json +11 -2
  183. package/src/PgWriting.res +0 -36
  184. package/src/PgWriting.res.mjs +0 -34
  185. package/src/db/IndexDefinition.res +0 -47
  186. package/src/db/IndexDefinition.res.mjs +0 -67
@@ -26,6 +26,9 @@ type t = {
26
26
  // scheduler doesn't re-sum pending queries on every tick. Incremented when
27
27
  // queries are dispatched, decremented as their responses land.
28
28
  mutable pendingBudget: float,
29
+ // Counts resets of pendingBudget: a hold taken before one has nothing left
30
+ // to release.
31
+ mutable pendingBudgetResets: int,
29
32
  // Chain-wide events/block, used to turn a chain's item budget into a target
30
33
  // block for query sizing. Seeded from cumulative progress on construction,
31
34
  // then smoothed with an EMA on every batch (see applyBatchProgress). None
@@ -73,6 +76,12 @@ type t = {
73
76
  // Timestamp of the committed progress block itself. See
74
77
  // `Batch.chainAfterBatch.progressBlockTime`.
75
78
  mutable committedProgressBlockTime: option<int>,
79
+ // Subgraph mode, with a block handler that has a register pass: the chain's
80
+ // register work runs one unit at a time, a response's passes or a run of
81
+ // block passes alone, so a pass never runs ahead of a block pass of an
82
+ // earlier block. Settles when the latest unit is applied.
83
+ mutable blockRegisterTail: promise<unit>,
84
+ mutable blockRegisterRuns: int,
76
85
  }
77
86
 
78
87
  // The chain's config-declared addresses as storage rows: what a clean run
@@ -167,6 +176,7 @@ let make = (
167
176
  processingBlockNumber: committedProgressBlockNumber,
168
177
  numEventsProcessed,
169
178
  pendingBudget: 0.,
179
+ pendingBudgetResets: 0,
170
180
  chainDensity,
171
181
  safeCheckpointTracking,
172
182
  shouldRollbackOnReorg,
@@ -187,6 +197,8 @@ let make = (
187
197
  rollbackTargetBlock: None,
188
198
  progressLatencyMs: None,
189
199
  committedProgressBlockTime,
200
+ blockRegisterTail: Promise.resolve(),
201
+ blockRegisterRuns: 0,
190
202
  }
191
203
  }
192
204
 
@@ -308,13 +320,19 @@ let makeInternal = (
308
320
  // FIXME: Shouldn't set with full history
309
321
  // The resumed depth, not the config one: the pre-threshold lag must match
310
322
  // the window reorg detection compares, or a reduced config depth would let
311
- // fetching enter the stored rollback window without history.
323
+ // fetching enter the stored rollback window without history. One block
324
+ // more than the window, so the last pre-threshold page ends below it and
325
+ // the window's first block is read by a page that knows it is reorgable.
312
326
  ~blockLag=Pervasives.max(
313
- !config.shouldRollbackOnReorg || isInReorgThreshold ? 0 : maxReorgDepth,
327
+ !config.shouldRollbackOnReorg || isInReorgThreshold || maxReorgDepth == 0
328
+ ? 0
329
+ : maxReorgDepth + 1,
314
330
  chainConfig.blockLag,
315
331
  ),
332
+ ~blockThrottle=chainConfig.blockThrottle,
316
333
  ~onBlockRegistrations,
317
334
  ~firstEventBlock,
335
+ ~deferCreationBlockEvents=config.subgraph->Option.isSome,
318
336
  // Fuel and SVM route through the address store like EVM does, so the
319
337
  // client-side path works for them - but their address counts are nowhere
320
338
  // near the threshold, so switching would only trade a server-side filter
@@ -349,6 +367,7 @@ let makeInternal = (
349
367
  ~blockStore,
350
368
  ~transactionStore,
351
369
  ~logger,
370
+ ~maxAddressesPerRequest=config.maxAddrInPartition,
352
371
  )
353
372
 
354
373
  // Seed the stored reorg checkpoints (hash-only rows) so detection resumes
@@ -459,6 +478,7 @@ let logger = (cs: t) => cs.logger
459
478
  let blockStore = (cs: t) => cs.blockStore
460
479
  let transactionStore = (cs: t) => cs.transactionStore
461
480
  let shouldChecksum = (cs: t) => cs.shouldChecksum
481
+ let fetchState = (cs: t) => cs.fetchState
462
482
  let entities = (cs: t) => cs.entities
463
483
 
464
484
  // Rollback discards every uncommitted change, so this chain's partition is
@@ -545,8 +565,70 @@ let startFetchingQueries = (cs: t, ~queries: array<FetchState.query>) => {
545
565
  let resetPendingQueries = (cs: t) => {
546
566
  cs.fetchState = cs.fetchState->FetchState.resetPendingQueries
547
567
  cs.pendingBudget = 0.
568
+ cs.pendingBudgetResets = cs.pendingBudgetResets + 1
548
569
  }
549
570
 
571
+ // A response that landed with more items than its query was priced at keeps the
572
+ // excess reserved while it waits for its register passes: until then its items
573
+ // are neither in the buffer nor in the query's estimate, and the pool would
574
+ // admit the next queries on top of them. Released when the response is applied.
575
+ type heldBudget = {items: int, resets: int}
576
+
577
+ let holdExtraBudget = (cs: t, ~items) => {
578
+ cs.pendingBudget = cs.pendingBudget +. items->Int.toFloat
579
+ {items, resets: cs.pendingBudgetResets}
580
+ }
581
+
582
+ let releaseExtraBudget = (cs: t, held: heldBudget) =>
583
+ if held.resets === cs.pendingBudgetResets {
584
+ cs.pendingBudget = Pervasives.max(0., cs.pendingBudget -. held.items->Int.toFloat)
585
+ }
586
+
587
+ let hasBlockRegisters = (cs: t) => cs.fetchState.blockRegisterThrough !== None
588
+ let isBlockRegisterIdle = (cs: t) => cs.blockRegisterRuns === 0
589
+
590
+ // Queues a unit of register work behind the chain's latest one. Returns the
591
+ // promise to wait on before running it, and the callback that settles it,
592
+ // which must be called however the unit ends.
593
+ let queueBlockRegisterRun = (cs: t) => {
594
+ let previous = cs.blockRegisterTail
595
+ let settle = ref(() => ())
596
+ cs.blockRegisterTail = Promise.make((resolve, _) => settle := resolve)
597
+ cs.blockRegisterRuns = cs.blockRegisterRuns + 1
598
+ let finish = () => {
599
+ cs.blockRegisterRuns = cs.blockRegisterRuns - 1
600
+ settle.contents()
601
+ }
602
+ (previous, finish)
603
+ }
604
+
605
+ let canAdvanceBlockRegisters = (cs: t, ~through) =>
606
+ cs.fetchState->FetchState.canAdvanceBlockRegisters(~through)
607
+ let nextBlockRegisterItems = (cs: t, ~through, ~limit) =>
608
+ cs.fetchState->FetchState.nextBlockRegisterItems(~through, ~limit)
609
+ let blockRegisterItemsAfter = (cs: t, ~after, ~through, ~limit) =>
610
+ cs.fetchState->FetchState.blockRegisterItemsAfter(~after, ~through, ~limit)
611
+ let blockRegisterThrough = (cs: t) => cs.fetchState.blockRegisterThrough
612
+ let blockRegisterFrontier = (cs: t) => cs.fetchState->FetchState.blockRegisterFrontier
613
+
614
+ // Applies what a run of block passes alone registered, then lets block items
615
+ // through its last block be generated.
616
+ let applyBlockRegisters = (cs: t, ~registrations, ~through) => {
617
+ let fs = switch registrations {
618
+ | [] => cs.fetchState
619
+ | _ =>
620
+ cs.fetchState->FetchState.registerDynamicContracts(
621
+ ~addressStore=cs.addressStore,
622
+ ~onNotice=notice => cs.logger->logFetchStateNotice(notice),
623
+ registrations,
624
+ )
625
+ }
626
+ cs.fetchState = fs->FetchState.setBlockRegisterThrough(~through)
627
+ }
628
+
629
+ let markBlockRegistered = (cs: t, ~through) =>
630
+ cs.fetchState = cs.fetchState->FetchState.setBlockRegisterThrough(~through)
631
+
550
632
  let isReady = (cs: t) => cs.timestampCaughtUpToHeadOrEndblock !== None
551
633
 
552
634
  // Block span over which a batch fully replaces the chain density estimate;
@@ -776,10 +858,10 @@ let takeFinished = (cs: t) =>
776
858
  }
777
859
  }
778
860
 
779
- let getHighestBlockBelowThreshold = (cs: t): int => {
780
- let highestBlockBelowThreshold = cs.fetchState.knownHeight - cs.maxReorgDepth
781
- highestBlockBelowThreshold < 0 ? 0 : highestBlockBelowThreshold
782
- }
861
+ // The block reorg detection compares from is itself in the window, so the
862
+ // highest block below it is the one before, where catch-up stops.
863
+ let getHighestBlockBelowThreshold = (cs: t): int =>
864
+ Pervasives.max(cs.fetchState.knownHeight - cs.maxReorgDepth - 1, 0)
783
865
 
784
866
  // True once the fetch frontier has reached the head/endBlock for this chain.
785
867
  let isFetchingAtHead = (cs: t) => cs.fetchState->FetchState.isFetchingAtHead
@@ -977,6 +1059,34 @@ let materializePageItems = async (
977
1059
  ))
978
1060
  }
979
1061
 
1062
+ // Near the head: each new block in as few queries as possible, of a shape that
1063
+ // stays the same while contracts keep being registered. Once every partition is
1064
+ // close enough, the chain's dynamic contracts leave the server-side address
1065
+ // filter for the rest of the run.
1066
+ //
1067
+ // Judged only when the chain learns a newer head. An RPC response repeats the
1068
+ // height its query went out with, which can be hours old by the end of a
1069
+ // backfill: judged against it, every block produced since would be fetched
1070
+ // address-free.
1071
+ let switchDynamicContractsToClientSideFilteringNearHead = (cs: t) =>
1072
+ if (
1073
+ !cs.fetchState.clientFilterPolicy.allDynamicContracts &&
1074
+ cs.fetchState->FetchState.isWithinHeadClientFilteringDistance
1075
+ ) {
1076
+ cs.fetchState =
1077
+ cs.fetchState->FetchState.switchDynamicContractsToClientSideFiltering(
1078
+ ~addressStore=cs.addressStore,
1079
+ )
1080
+ if cs.onEventRegistrations->Array.some(registration => registration.contractRegister !== None) {
1081
+ cs.logger->Logger.trace(
1082
+ "Near the head: dynamic contracts are fetched without their addresses for the rest of the run, and filtered on our side.",
1083
+ ~params={
1084
+ "contractNames": cs.fetchState.optimizedPartitions.dynamicContracts->Utils.Set.toArray,
1085
+ },
1086
+ )
1087
+ }
1088
+ }
1089
+
980
1090
  let handleQueryResult = (
981
1091
  cs: t,
982
1092
  ~query: FetchState.query,
@@ -1000,6 +1110,7 @@ let handleQueryResult = (
1000
1110
  )
1001
1111
  }
1002
1112
 
1113
+ let previousKnownHeight = cs.fetchState.knownHeight
1003
1114
  cs.fetchState =
1004
1115
  fs
1005
1116
  ->FetchState.handleQueryResult(~query, ~latestFetchedBlock, ~newItems)
@@ -1007,6 +1118,9 @@ let handleQueryResult = (
1007
1118
 
1008
1119
  // The query is no longer in flight, so release its reservation.
1009
1120
  cs.pendingBudget = Pervasives.max(0., cs.pendingBudget -. query.itemsEst->Int.toFloat)
1121
+ if cs.fetchState.knownHeight > previousKnownHeight {
1122
+ cs->switchDynamicContractsToClientSideFilteringNearHead
1123
+ }
1010
1124
  }
1011
1125
 
1012
1126
  // Run reorg detection against a fetch response by merging its block-store page
@@ -1041,8 +1155,11 @@ let prepareReorg = (cs: t, ~eventsProcessedDiff) => {
1041
1155
  cs->resetPendingQueries
1042
1156
  }
1043
1157
 
1044
- let updateKnownHeight = (cs: t, ~knownHeight) =>
1158
+ // A chain waiting at its head goes address-free before querying the new block.
1159
+ let updateKnownHeight = (cs: t, ~knownHeight) => {
1045
1160
  cs.fetchState = cs.fetchState->FetchState.updateKnownHeight(~knownHeight)
1161
+ cs->switchDynamicContractsToClientSideFilteringNearHead
1162
+ }
1046
1163
 
1047
1164
  // In auto-exit mode, pin the endBlock to the earliest observed event block.
1048
1165
  let setEndBlockToFirstEvent = (cs: t, ~blockNumber) =>
@@ -83,6 +83,7 @@ function make(chainConfig, fetchState, onEventRegistrationsOpt, addressStore, so
83
83
  processingBlockNumber: committedProgressBlockNumber,
84
84
  numEventsProcessed: numEventsProcessed,
85
85
  pendingBudget: 0,
86
+ pendingBudgetResets: 0,
86
87
  chainDensity: chainDensity,
87
88
  entities: EntityTables.make(perChainEntities),
88
89
  safeCheckpointTracking: safeCheckpointTracking,
@@ -103,7 +104,9 @@ function make(chainConfig, fetchState, onEventRegistrationsOpt, addressStore, so
103
104
  reorgDetectedBlock: undefined,
104
105
  rollbackTargetBlock: undefined,
105
106
  progressLatencyMs: undefined,
106
- committedProgressBlockTime: committedProgressBlockTime
107
+ committedProgressBlockTime: committedProgressBlockTime,
108
+ blockRegisterTail: Promise.resolve(),
109
+ blockRegisterRuns: 0
107
110
  };
108
111
  }
109
112
 
@@ -159,16 +162,11 @@ function makeFromDbState(chainConfig, resumedChainState, reorgCheckpoints, isInR
159
162
  onBlockRegistrations: []
160
163
  });
161
164
  let onEventRegistrations = match.onEventRegistrations;
162
- let onBlockRegistrations = match.onBlockRegistrations.map(reg => ({
163
- index: reg.index,
164
- name: reg.name,
165
- chainId: reg.chainId,
166
- startBlock: Stdlib_Option.getOr(reg.startBlock, startBlock),
167
- endBlock: reg.endBlock,
168
- interval: reg.interval,
169
- handler: reg.handler,
170
- blockFieldMask: reg.blockFieldMask
171
- }));
165
+ let onBlockRegistrations = match.onBlockRegistrations.map(reg => {
166
+ let newrecord = {...reg};
167
+ newrecord.startBlock = Stdlib_Option.getOr(reg.startBlock, startBlock);
168
+ return newrecord;
169
+ });
172
170
  chainConfig.contracts.forEach(contract => {
173
171
  let contractStartBlock = contract.startBlock;
174
172
  if (contractStartBlock !== undefined && contractStartBlock < startBlock) {
@@ -188,7 +186,7 @@ function makeFromDbState(chainConfig, resumedChainState, reorgCheckpoints, isInR
188
186
  tmp = undefined;
189
187
  break;
190
188
  }
191
- let fetchState = FetchState.make(startBlock, endBlock, onEventRegistrations, addressStore, addressRows, config.maxAddrInPartition, chainConfig.id, notice => logFetchStateNotice(logger$1, notice), (config.batchSize << 1), knownHeight, progressBlockNumber, onBlockRegistrations, Primitive_int.max(!config.shouldRollbackOnReorg || isInReorgThreshold ? 0 : maxReorgDepth, chainConfig.blockLag), Primitive_option.some(firstEventBlock), Primitive_option.some(tmp), isResumed);
189
+ let fetchState = FetchState.make(startBlock, endBlock, onEventRegistrations, addressStore, addressRows, config.maxAddrInPartition, chainConfig.id, notice => logFetchStateNotice(logger$1, notice), (config.batchSize << 1), knownHeight, progressBlockNumber, onBlockRegistrations, Primitive_int.max(!config.shouldRollbackOnReorg || isInReorgThreshold || maxReorgDepth === 0 ? 0 : maxReorgDepth + 1 | 0, chainConfig.blockLag), chainConfig.blockThrottle, Primitive_option.some(firstEventBlock), Primitive_option.some(tmp), isResumed, Stdlib_Option.isSome(config.subgraph));
192
190
  let chainReorgCheckpoints = Stdlib_Array.filterMap(reorgCheckpoints, reorgCheckpoint => {
193
191
  if (reorgCheckpoint.chain_id === chainConfig.id) {
194
192
  return reorgCheckpoint;
@@ -196,7 +194,7 @@ function makeFromDbState(chainConfig, resumedChainState, reorgCheckpoints, isInR
196
194
  });
197
195
  let blockStore = BlockStore.make(config.ecosystem.name);
198
196
  let transactionStore = TransactionStore.make(config.ecosystem.name);
199
- let sources = ChainSources.make(chainConfig, onEventRegistrations, onBlockRegistrations, addressStore, lowercaseAddresses, blockStore, transactionStore, logger$1);
197
+ let sources = ChainSources.make(chainConfig, onEventRegistrations, onBlockRegistrations, addressStore, lowercaseAddresses, blockStore, transactionStore, logger$1, config.maxAddrInPartition);
200
198
  if (Utils.$$Array.notEmpty(chainReorgCheckpoints)) {
201
199
  let seedPage = BlockStore.fromJs(chainReorgCheckpoints.map(cp => ({
202
200
  blockNumber: cp.block_number,
@@ -225,6 +223,10 @@ function shouldChecksum(cs) {
225
223
  return cs.shouldChecksum;
226
224
  }
227
225
 
226
+ function fetchState(cs) {
227
+ return cs.fetchState;
228
+ }
229
+
228
230
  function entities(cs) {
229
231
  return cs.entities;
230
232
  }
@@ -338,6 +340,78 @@ function startFetchingQueries(cs, queries) {
338
340
  function resetPendingQueries(cs) {
339
341
  cs.fetchState = FetchState.resetPendingQueries(cs.fetchState);
340
342
  cs.pendingBudget = 0;
343
+ cs.pendingBudgetResets = cs.pendingBudgetResets + 1 | 0;
344
+ }
345
+
346
+ function holdExtraBudget(cs, items) {
347
+ cs.pendingBudget = cs.pendingBudget + items;
348
+ return {
349
+ items: items,
350
+ resets: cs.pendingBudgetResets
351
+ };
352
+ }
353
+
354
+ function releaseExtraBudget(cs, held) {
355
+ if (held.resets === cs.pendingBudgetResets) {
356
+ cs.pendingBudget = Primitive_float.max(0, cs.pendingBudget - held.items);
357
+ return;
358
+ }
359
+ }
360
+
361
+ function hasBlockRegisters(cs) {
362
+ return cs.fetchState.blockRegisterThrough !== undefined;
363
+ }
364
+
365
+ function isBlockRegisterIdle(cs) {
366
+ return cs.blockRegisterRuns === 0;
367
+ }
368
+
369
+ function queueBlockRegisterRun(cs) {
370
+ let previous = cs.blockRegisterTail;
371
+ let settle = {
372
+ contents: () => {}
373
+ };
374
+ cs.blockRegisterTail = new Promise((resolve, param) => {
375
+ settle.contents = resolve;
376
+ });
377
+ cs.blockRegisterRuns = cs.blockRegisterRuns + 1 | 0;
378
+ let finish = () => {
379
+ cs.blockRegisterRuns = cs.blockRegisterRuns - 1 | 0;
380
+ settle.contents();
381
+ };
382
+ return [
383
+ previous,
384
+ finish
385
+ ];
386
+ }
387
+
388
+ function canAdvanceBlockRegisters(cs, through) {
389
+ return FetchState.canAdvanceBlockRegisters(cs.fetchState, through);
390
+ }
391
+
392
+ function nextBlockRegisterItems(cs, through, limit) {
393
+ return FetchState.nextBlockRegisterItems(cs.fetchState, through, limit);
394
+ }
395
+
396
+ function blockRegisterItemsAfter(cs, after, through, limit) {
397
+ return FetchState.blockRegisterItemsAfter(cs.fetchState, after, through, limit);
398
+ }
399
+
400
+ function blockRegisterThrough(cs) {
401
+ return cs.fetchState.blockRegisterThrough;
402
+ }
403
+
404
+ function blockRegisterFrontier(cs) {
405
+ return FetchState.blockRegisterFrontier(cs.fetchState);
406
+ }
407
+
408
+ function applyBlockRegisters(cs, registrations, through) {
409
+ let fs = registrations.length !== 0 ? FetchState.registerDynamicContracts(cs.fetchState, cs.addressStore, notice => logFetchStateNotice(cs.logger, notice), undefined, registrations) : cs.fetchState;
410
+ cs.fetchState = FetchState.setBlockRegisterThrough(fs, through);
411
+ }
412
+
413
+ function markBlockRegistered(cs, through) {
414
+ cs.fetchState = FetchState.setBlockRegisterThrough(cs.fetchState, through);
341
415
  }
342
416
 
343
417
  function isReady(cs) {
@@ -494,12 +568,7 @@ function takeFinished(cs) {
494
568
  }
495
569
 
496
570
  function getHighestBlockBelowThreshold(cs) {
497
- let highestBlockBelowThreshold = cs.fetchState.knownHeight - cs.maxReorgDepth | 0;
498
- if (highestBlockBelowThreshold < 0) {
499
- return 0;
500
- } else {
501
- return highestBlockBelowThreshold;
502
- }
571
+ return Primitive_int.max((cs.fetchState.knownHeight - cs.maxReorgDepth | 0) - 1 | 0, 0);
503
572
  }
504
573
 
505
574
  function isFetchingAtHead(cs) {
@@ -647,10 +716,27 @@ async function materializePageItems(items, transactionStore, blockStore, shouldC
647
716
  ]);
648
717
  }
649
718
 
719
+ function switchDynamicContractsToClientSideFilteringNearHead(cs) {
720
+ if (!cs.fetchState.clientFilterPolicy.allDynamicContracts && FetchState.isWithinHeadClientFilteringDistance(cs.fetchState)) {
721
+ cs.fetchState = FetchState.switchDynamicContractsToClientSideFiltering(cs.fetchState, cs.addressStore);
722
+ if (cs.onEventRegistrations.some(registration => registration.contractRegister !== undefined)) {
723
+ return Logger.trace(cs.logger, "Near the head: dynamic contracts are fetched without their addresses for the rest of the run, and filtered on our side.", {
724
+ contractNames: Array.from(cs.fetchState.optimizedPartitions.dynamicContracts)
725
+ });
726
+ } else {
727
+ return;
728
+ }
729
+ }
730
+ }
731
+
650
732
  function handleQueryResult(cs, query, newItems, newRegistrations, latestFetchedBlock, knownHeight) {
651
733
  let fs = newRegistrations.length !== 0 ? FetchState.registerDynamicContracts(cs.fetchState, cs.addressStore, notice => logFetchStateNotice(cs.logger, notice), Primitive_int.max(knownHeight, latestFetchedBlock), newRegistrations) : cs.fetchState;
734
+ let previousKnownHeight = cs.fetchState.knownHeight;
652
735
  cs.fetchState = FetchState.updateKnownHeight(FetchState.handleQueryResult(fs, query, latestFetchedBlock, newItems), knownHeight);
653
736
  cs.pendingBudget = Primitive_float.max(0, cs.pendingBudget - query.itemsEst);
737
+ if (cs.fetchState.knownHeight > previousKnownHeight) {
738
+ return switchDynamicContractsToClientSideFilteringNearHead(cs);
739
+ }
654
740
  }
655
741
 
656
742
  function registerReorgGuard(cs, blockStore, knownHeight) {
@@ -683,6 +769,7 @@ function prepareReorg(cs, eventsProcessedDiff) {
683
769
 
684
770
  function updateKnownHeight(cs, knownHeight) {
685
771
  cs.fetchState = FetchState.updateKnownHeight(cs.fetchState, knownHeight);
772
+ switchDynamicContractsToClientSideFilteringNearHead(cs);
686
773
  }
687
774
 
688
775
  function setEndBlockToFirstEvent(cs, blockNumber) {
@@ -691,40 +778,14 @@ function setEndBlockToFirstEvent(cs, blockNumber) {
691
778
  if (blockNumber >= currentEndBlock) {
692
779
  return;
693
780
  }
694
- let init = cs.fetchState;
695
- cs.fetchState = {
696
- optimizedPartitions: init.optimizedPartitions,
697
- startBlock: init.startBlock,
698
- endBlock: blockNumber,
699
- normalSelection: init.normalSelection,
700
- chainId: init.chainId,
701
- latestOnBlockBlockNumber: init.latestOnBlockBlockNumber,
702
- blockLag: init.blockLag,
703
- buffer: init.buffer,
704
- maxOnBlockBufferSize: init.maxOnBlockBufferSize,
705
- onBlockRegistrations: init.onBlockRegistrations,
706
- knownHeight: init.knownHeight,
707
- firstEventBlock: init.firstEventBlock,
708
- clientFilterAddressThreshold: init.clientFilterAddressThreshold
709
- };
781
+ let newrecord = {...cs.fetchState};
782
+ newrecord.endBlock = blockNumber;
783
+ cs.fetchState = newrecord;
710
784
  return;
711
785
  }
712
- let init$1 = cs.fetchState;
713
- cs.fetchState = {
714
- optimizedPartitions: init$1.optimizedPartitions,
715
- startBlock: init$1.startBlock,
716
- endBlock: blockNumber,
717
- normalSelection: init$1.normalSelection,
718
- chainId: init$1.chainId,
719
- latestOnBlockBlockNumber: init$1.latestOnBlockBlockNumber,
720
- blockLag: init$1.blockLag,
721
- buffer: init$1.buffer,
722
- maxOnBlockBufferSize: init$1.maxOnBlockBufferSize,
723
- onBlockRegistrations: init$1.onBlockRegistrations,
724
- knownHeight: init$1.knownHeight,
725
- firstEventBlock: init$1.firstEventBlock,
726
- clientFilterAddressThreshold: init$1.clientFilterAddressThreshold
727
- };
786
+ let newrecord$1 = {...cs.fetchState};
787
+ newrecord$1.endBlock = blockNumber;
788
+ cs.fetchState = newrecord$1;
728
789
  }
729
790
 
730
791
  function enterReorgThreshold(cs) {
@@ -872,22 +933,9 @@ function applyBatchProgress(cs, batch, blockTimestampName) {
872
933
  if (match === undefined) {
873
934
  let firstEventBlock = Batch.findFirstEventBlockNumber(batch, chainId);
874
935
  if (firstEventBlock !== undefined) {
875
- let init = cs.fetchState;
876
- cs.fetchState = {
877
- optimizedPartitions: init.optimizedPartitions,
878
- startBlock: init.startBlock,
879
- endBlock: init.endBlock,
880
- normalSelection: init.normalSelection,
881
- chainId: init.chainId,
882
- latestOnBlockBlockNumber: init.latestOnBlockBlockNumber,
883
- blockLag: init.blockLag,
884
- buffer: init.buffer,
885
- maxOnBlockBufferSize: init.maxOnBlockBufferSize,
886
- onBlockRegistrations: init.onBlockRegistrations,
887
- knownHeight: init.knownHeight,
888
- firstEventBlock: firstEventBlock,
889
- clientFilterAddressThreshold: init.clientFilterAddressThreshold
890
- };
936
+ let newrecord = {...cs.fetchState};
937
+ newrecord.firstEventBlock = firstEventBlock;
938
+ cs.fetchState = newrecord;
891
939
  }
892
940
  }
893
941
  let deltaBlocks = chainAfterBatch.progressBlockNumber - cs.committedProgressBlockNumber | 0;
@@ -990,6 +1038,7 @@ export {
990
1038
  blockStore,
991
1039
  transactionStore,
992
1040
  shouldChecksum,
1041
+ fetchState,
993
1042
  entities,
994
1043
  resetEntities,
995
1044
  sourceManager,
@@ -1002,6 +1051,18 @@ export {
1002
1051
  committedProgressBlockTime,
1003
1052
  numEventsProcessed,
1004
1053
  pendingBudget,
1054
+ holdExtraBudget,
1055
+ releaseExtraBudget,
1056
+ hasBlockRegisters,
1057
+ isBlockRegisterIdle,
1058
+ queueBlockRegisterRun,
1059
+ canAdvanceBlockRegisters,
1060
+ nextBlockRegisterItems,
1061
+ blockRegisterItemsAfter,
1062
+ blockRegisterThrough,
1063
+ blockRegisterFrontier,
1064
+ applyBlockRegisters,
1065
+ markBlockRegistered,
1005
1066
  startFetchingQueries,
1006
1067
  timestampCaughtUpToHeadOrEndblock,
1007
1068
  recordBlockRangeFetch,
@@ -58,6 +58,8 @@ let logger: t => Logger.t
58
58
  let blockStore: t => BlockStore.t
59
59
  let transactionStore: t => TransactionStore.t
60
60
  let shouldChecksum: t => bool
61
+ // The chain's fetch state, read-only: for the round a registration belongs to.
62
+ let fetchState: t => FetchState.t
61
63
  let entities: t => EntityTables.t
62
64
  let resetEntities: (t, ~perChainEntities: array<Internal.entityConfig>) => unit
63
65
  let sourceManager: t => SourceManager.t
@@ -75,6 +77,30 @@ let committedProgressBlockNumber: t => int
75
77
  let committedProgressBlockTime: t => option<int>
76
78
  let numEventsProcessed: t => float
77
79
  let pendingBudget: t => float
80
+ type heldBudget
81
+ let holdExtraBudget: (t, ~items: int) => heldBudget
82
+ let releaseExtraBudget: (t, heldBudget) => unit
83
+ // Subgraph mode: the register passes of block handlers that create data
84
+ // sources. See `FetchState.blockRegisterThrough`.
85
+ let hasBlockRegisters: t => bool
86
+ let isBlockRegisterIdle: t => bool
87
+ let queueBlockRegisterRun: t => (promise<unit>, unit => unit)
88
+ let canAdvanceBlockRegisters: (t, ~through: int) => bool
89
+ let nextBlockRegisterItems: (t, ~through: int, ~limit: int) => option<(array<Internal.item>, int)>
90
+ let blockRegisterItemsAfter: (
91
+ t,
92
+ ~after: int,
93
+ ~through: int,
94
+ ~limit: int,
95
+ ) => option<(array<Internal.item>, int)>
96
+ let blockRegisterThrough: t => option<int>
97
+ let blockRegisterFrontier: t => int
98
+ let applyBlockRegisters: (
99
+ t,
100
+ ~registrations: array<AddressStore.registration>,
101
+ ~through: int,
102
+ ) => unit
103
+ let markBlockRegistered: (t, ~through: int) => unit
78
104
  let startFetchingQueries: (t, ~queries: array<FetchState.query>) => unit
79
105
  let timestampCaughtUpToHeadOrEndblock: t => option<Date.t>
80
106
 
package/src/Clock.res ADDED
@@ -0,0 +1,57 @@
1
+ // What the source layer reads time from and arms its timers on. A test can hand
2
+ // it a clock that only moves when the test moves it, which takes the scheduler
3
+ // out of every race between a timer and the step a test is taking. The label
4
+ // names a timer in that clock's timeline; the real clock ignores it.
5
+ type timer
6
+
7
+ type t = {
8
+ now: unit => float,
9
+ setTimeout: (~label: string, unit => unit, int) => timer,
10
+ clearTimeout: timer => unit,
11
+ random: unit => float,
12
+ }
13
+
14
+ // Test-only. Set while a test drives a run on virtual time, so code that reaches
15
+ // past the clock it was given fails where it does, instead of arming a timer
16
+ // the test can neither see nor move.
17
+ let refuseReal = ref(false)
18
+
19
+ let refused = what =>
20
+ JsError.throwWithMessage(
21
+ `Clock.real ${what} on virtual time. Use the clock the code was given, so advanceTime can see and move it.`,
22
+ )
23
+
24
+ // Monotonic: every reading is only ever compared with another, and a wall
25
+ // clock corrected mid-run would make a connection look like it served for
26
+ // minutes, or a benched source look recovered.
27
+ let real: t = {
28
+ now: () => refuseReal.contents ? refused("read the time") : Performance.now(),
29
+ // A zero delay is a turn of the event loop on either clock.
30
+ setTimeout: (~label, callback, millis) =>
31
+ refuseReal.contents && millis > 0
32
+ ? refused(`armed "${label}"`)
33
+ : setTimeout(callback, millis)->(Utils.magic: timeoutId => timer),
34
+ clearTimeout: timer => clearTimeout(timer->(Utils.magic: timer => timeoutId)),
35
+ random: Math.random,
36
+ }
37
+
38
+ let delay = (clock: t, ~label, millis) =>
39
+ Promise.make((resolve, _reject) => clock.setTimeout(~label, () => resolve(), millis)->ignore)
40
+
41
+ // Half the delay fixed, half spread across it. Every indexer pointed at one
42
+ // provider loses its stream, or gives up on a quiet chain, in the same instant
43
+ // that provider blinks, and putting them all back on the same schedule is how a
44
+ // blip becomes a stampede.
45
+ let jitter = (clock: t, delay) =>
46
+ delay / 2 + (clock.random() *. (delay / 2)->Int.toFloat)->Float.toInt
47
+
48
+ // Clearing a pending timer and forgetting it are one action. A ref left holding
49
+ // a spent timer reads as one still pending, and the next clear looks like it
50
+ // did something.
51
+ let clearRef = (clock: t, timer: ref<option<timer>>) => {
52
+ switch timer.contents {
53
+ | Some(timer) => clock.clearTimeout(timer)
54
+ | None => ()
55
+ }
56
+ timer := None
57
+ }
@@ -0,0 +1,72 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Performance from "./bindings/Performance.res.mjs";
4
+ import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
5
+ import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
6
+
7
+ let refuseReal = {
8
+ contents: false
9
+ };
10
+
11
+ function refused(what) {
12
+ return Stdlib_JsError.throwWithMessage(`Clock.real ` + what + ` on virtual time. Use the clock the code was given, so advanceTime can see and move it.`);
13
+ }
14
+
15
+ function real_now() {
16
+ if (refuseReal.contents) {
17
+ return refused("read the time");
18
+ } else {
19
+ return Performance.now();
20
+ }
21
+ }
22
+
23
+ function real_setTimeout(label, callback, millis) {
24
+ if (refuseReal.contents && millis > 0) {
25
+ return refused(`armed "` + label + `"`);
26
+ } else {
27
+ return setTimeout(callback, millis);
28
+ }
29
+ }
30
+
31
+ function real_clearTimeout(timer) {
32
+ clearTimeout(timer);
33
+ }
34
+
35
+ function real_random(prim) {
36
+ return Math.random();
37
+ }
38
+
39
+ let real = {
40
+ now: real_now,
41
+ setTimeout: real_setTimeout,
42
+ clearTimeout: real_clearTimeout,
43
+ random: real_random
44
+ };
45
+
46
+ function delay(clock, label, millis) {
47
+ return new Promise((resolve, _reject) => {
48
+ clock.setTimeout(label, () => resolve(), millis);
49
+ });
50
+ }
51
+
52
+ function jitter(clock, delay) {
53
+ return (delay / 2 | 0) + (clock.random() * (delay / 2 | 0) | 0) | 0;
54
+ }
55
+
56
+ function clearRef(clock, timer) {
57
+ let timer$1 = timer.contents;
58
+ if (timer$1 !== undefined) {
59
+ clock.clearTimeout(Primitive_option.valFromOption(timer$1));
60
+ }
61
+ timer.contents = undefined;
62
+ }
63
+
64
+ export {
65
+ refuseReal,
66
+ refused,
67
+ real,
68
+ delay,
69
+ jitter,
70
+ clearRef,
71
+ }
72
+ /* No side effect */