envio 3.15.0-subgraph → 3.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) 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 +9 -10
  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 +0 -7
  11. package/src/Bin.res.mjs +0 -8
  12. package/src/ChainFetching.res +17 -456
  13. package/src/ChainFetching.res.mjs +33 -404
  14. package/src/ChainState.res +48 -83
  15. package/src/ChainState.res.mjs +83 -115
  16. package/src/ChainState.resi +0 -26
  17. package/src/Clock.res +57 -0
  18. package/src/Clock.res.mjs +72 -0
  19. package/src/Config.res +7 -16
  20. package/src/Config.res.mjs +4 -11
  21. package/src/ContractRegisterContext.res +6 -3
  22. package/src/ContractRegisterContext.res.mjs +2 -4
  23. package/src/Core.res +5 -136
  24. package/src/Core.res.mjs +1 -90
  25. package/src/CrossChainState.res +2 -8
  26. package/src/CrossChainState.res.mjs +3 -3
  27. package/src/Ecosystem.res +4 -7
  28. package/src/Ecosystem.res.mjs +8 -8
  29. package/src/EffectState.res +10 -2
  30. package/src/EffectState.res.mjs +8 -4
  31. package/src/EffectState.resi +3 -1
  32. package/src/EntityId.res +2 -7
  33. package/src/EntityId.res.mjs +1 -8
  34. package/src/EntityTables.res +4 -1
  35. package/src/EntityTables.res.mjs +1 -1
  36. package/src/Env.res +0 -16
  37. package/src/Env.res.mjs +0 -11
  38. package/src/EventProcessing.res +1 -38
  39. package/src/EventProcessing.res.mjs +1 -28
  40. package/src/FetchState.res +160 -319
  41. package/src/FetchState.res.mjs +182 -292
  42. package/src/FinalizeBackfill.res +2 -4
  43. package/src/FinalizeBackfill.res.mjs +3 -4
  44. package/src/HandlerLoader.res +15 -39
  45. package/src/HandlerLoader.res.mjs +8 -27
  46. package/src/HandlerRegister.res +0 -4
  47. package/src/HandlerRegister.res.mjs +2 -5
  48. package/src/HandlerRegister.resi +0 -2
  49. package/src/InMemoryTable.res +17 -16
  50. package/src/InMemoryTable.res.mjs +14 -15
  51. package/src/IndexerState.res +28 -4
  52. package/src/IndexerState.res.mjs +19 -6
  53. package/src/IndexerState.resi +4 -0
  54. package/src/Internal.res +0 -110
  55. package/src/Internal.res.mjs +1 -115
  56. package/src/LoadLayer.res +21 -47
  57. package/src/LoadLayer.res.mjs +47 -72
  58. package/src/LoadLayer.resi +0 -26
  59. package/src/Main.res +1 -14
  60. package/src/Main.res.mjs +3 -4
  61. package/src/Persistence.res +3 -23
  62. package/src/Persistence.res.mjs +3 -3
  63. package/src/Rollback.res +0 -1
  64. package/src/Rollback.res.mjs +1 -1
  65. package/src/RunScope.res +0 -3
  66. package/src/SafeCheckpointTracking.res +4 -4
  67. package/src/SafeCheckpointTracking.res.mjs +1 -1
  68. package/src/SimulateItems.res +26 -42
  69. package/src/SimulateItems.res.mjs +16 -40
  70. package/src/StagedRows.res +106 -0
  71. package/src/StagedRows.res.mjs +102 -0
  72. package/src/Staging.res +32 -14
  73. package/src/Staging.res.mjs +17 -20
  74. package/src/Storage.res +277 -998
  75. package/src/Storage.res.mjs +216 -731
  76. package/src/Supervisor.res +75 -56
  77. package/src/Supervisor.res.mjs +55 -26
  78. package/src/TestIndexer.res +22 -27
  79. package/src/TestIndexer.res.mjs +20 -17
  80. package/src/Throttler.res +15 -8
  81. package/src/Throttler.res.mjs +9 -6
  82. package/src/Throttler.resi +1 -1
  83. package/src/UserContext.res +46 -410
  84. package/src/UserContext.res.mjs +30 -318
  85. package/src/Utils.res +0 -23
  86. package/src/Utils.res.mjs +1 -17
  87. package/src/Writing.res +0 -1
  88. package/src/Writing.res.mjs +1 -1
  89. package/src/bindings/ClickHouse.res +0 -322
  90. package/src/bindings/ClickHouse.res.mjs +0 -338
  91. package/src/bindings/StorageClient.res +107 -175
  92. package/src/bindings/StorageClient.res.mjs +19 -8
  93. package/src/bindings/Vitest.res +0 -26
  94. package/src/bindings/Vitest.res.mjs +0 -3
  95. package/src/db/Table.res +24 -150
  96. package/src/db/Table.res.mjs +38 -272
  97. package/src/sources/AddressSet.res +0 -2
  98. package/src/sources/AddressStore.res +0 -3
  99. package/src/sources/ChainSources.res +2 -0
  100. package/src/sources/ChainSources.res.mjs +2 -2
  101. package/src/sources/EvmChain.res +4 -7
  102. package/src/sources/EvmChain.res.mjs +5 -4
  103. package/src/sources/EvmHyperSyncSource.res +2 -2
  104. package/src/sources/EvmHyperSyncSource.res.mjs +1 -1
  105. package/src/sources/EvmRpcClient.res +13 -3
  106. package/src/sources/EvmRpcClient.res.mjs +4 -3
  107. package/src/sources/EvmRpcWs.res +4 -3
  108. package/src/sources/EvmRpcWs.res.mjs +2 -2
  109. package/src/sources/HeightFeed.res +31 -10
  110. package/src/sources/HeightFeed.res.mjs +10 -6
  111. package/src/sources/HeightStream.res +32 -17
  112. package/src/sources/HeightStream.res.mjs +10 -10
  113. package/src/sources/HyperSync.res +1 -2
  114. package/src/sources/HyperSync.res.mjs +1 -2
  115. package/src/sources/HyperSyncSSE.res +4 -6
  116. package/src/sources/HyperSyncSSE.res.mjs +2 -2
  117. package/src/sources/RpcSource.res +10 -1
  118. package/src/sources/RpcSource.res.mjs +4 -3
  119. package/src/sources/Source.res +3 -0
  120. package/src/sources/SourceManager.res +85 -104
  121. package/src/sources/SourceManager.res.mjs +18 -37
  122. package/src/sources/SourceManager.resi +3 -5
  123. package/src/sources/StartBlockResolver.res +1 -0
  124. package/src/sources/StartBlockResolver.res.mjs +4 -3
  125. package/src/sources/SvmHyperSyncSource.res +2 -2
  126. package/src/sources/SvmHyperSyncSource.res.mjs +1 -1
  127. package/svm.schema.json +3 -3
  128. package/src/EffectCacheLookup.res +0 -98
  129. package/src/EffectCacheLookup.res.mjs +0 -116
  130. package/src/PgWriting.res +0 -36
  131. package/src/PgWriting.res.mjs +0 -34
  132. package/src/PreloadLookahead.res +0 -184
  133. package/src/PreloadLookahead.res.mjs +0 -197
  134. package/src/SubgraphLog.res +0 -19
  135. package/src/SubgraphLog.res.mjs +0 -26
  136. package/src/SyncWrites.res +0 -101
  137. package/src/SyncWrites.res.mjs +0 -98
  138. package/src/db/IndexDefinition.res +0 -47
  139. package/src/db/IndexDefinition.res.mjs +0 -60
  140. package/src/sources/CallSource.res +0 -274
  141. package/src/sources/CallSource.res.mjs +0 -196
  142. package/src/subgraph/abi-types.ts +0 -84
  143. package/src/subgraph/aggregations.ts +0 -745
  144. package/src/subgraph/as-package.ts +0 -143
  145. package/src/subgraph/assemblyscript-regex.ts +0 -67
  146. package/src/subgraph/assemblyscript.ts +0 -731
  147. package/src/subgraph/blocks.ts +0 -398
  148. package/src/subgraph/calls-source.ts +0 -702
  149. package/src/subgraph/calls.ts +0 -258
  150. package/src/subgraph/conformance.ts +0 -106
  151. package/src/subgraph/errors.ts +0 -85
  152. package/src/subgraph/graft.ts +0 -73
  153. package/src/subgraph/graph-cli-worker.mjs +0 -35
  154. package/src/subgraph/graph-cli.ts +0 -900
  155. package/src/subgraph/graph-node-decimal.ts +0 -199
  156. package/src/subgraph/graph-ts-enums.ts +0 -28
  157. package/src/subgraph/graph-ts-types/VERSION +0 -2
  158. package/src/subgraph/graph-ts-types/chain/arweave.d.ts +0 -70
  159. package/src/subgraph/graph-ts-types/chain/cosmos.d.ts +0 -327
  160. package/src/subgraph/graph-ts-types/chain/ethereum.d.ts +0 -233
  161. package/src/subgraph/graph-ts-types/chain/near.d.ts +0 -253
  162. package/src/subgraph/graph-ts-types/chain/starknet.d.ts +0 -32
  163. package/src/subgraph/graph-ts-types/common/collections.d.ts +0 -136
  164. package/src/subgraph/graph-ts-types/common/conversion.d.ts +0 -11
  165. package/src/subgraph/graph-ts-types/common/datasource.d.ts +0 -30
  166. package/src/subgraph/graph-ts-types/common/eager-offset.d.ts +0 -0
  167. package/src/subgraph/graph-ts-types/common/json.d.ts +0 -17
  168. package/src/subgraph/graph-ts-types/common/numbers.d.ts +0 -120
  169. package/src/subgraph/graph-ts-types/common/value.d.ts +0 -120
  170. package/src/subgraph/graph-ts-types/common/yaml.d.ts +0 -90
  171. package/src/subgraph/graph-ts-types/global/global.d.ts +0 -194
  172. package/src/subgraph/graph-ts-types/helper-functions.d.ts +0 -22
  173. package/src/subgraph/graph-ts-types/index.d.ts +0 -102
  174. package/src/subgraph/graph-ts.ts +0 -4280
  175. package/src/subgraph/hosts.ts +0 -554
  176. package/src/subgraph/log.ts +0 -28
  177. package/src/subgraph/receipts.ts +0 -184
  178. package/src/subgraph/register-slots.ts +0 -93
  179. package/src/subgraph/rollup.ts +0 -109
  180. package/src/subgraph/rpc.ts +0 -337
  181. package/src/subgraph/runtime.ts +0 -2060
  182. package/src/subgraph/scope.ts +0 -177
  183. package/src/subgraph/stats.ts +0 -42
  184. package/src/subgraph/template-instances.ts +0 -66
  185. package/src/subgraph/toolchain.ts +0 -327
  186. package/src/subgraph/transient.ts +0 -176
@@ -40,7 +40,7 @@ let runOnce = async (state: IndexerState.t) => {
40
40
  let storage = persistence->Persistence.getInitializedStorageOrThrow
41
41
  let readyAt = Date.make()
42
42
 
43
- await storage.finalizeBackfill(~entities=persistence.allEntities, ~chainIds, ~readyAt)
43
+ await storage.finalizeBackfill(~chainIds, ~readyAt)
44
44
 
45
45
  // Only after the commit: in-memory readiness must never run ahead of the
46
46
  // `ready_at` a restart would read back.
@@ -68,10 +68,8 @@ let run = (state: IndexerState.t) =>
68
68
  // Best-effort and not awaited by the loop: indexing is already live and correct
69
69
  // without the index, just slower.
70
70
  let repairSchemaIndexes = (state: IndexerState.t) => {
71
- let persistence = state->IndexerState.persistence
72
- let storage = persistence->Persistence.getInitializedStorageOrThrow
71
+ let storage = state->IndexerState.persistence->Persistence.getInitializedStorageOrThrow
73
72
  storage.ensureSchemaIndexes(
74
- ~entities=persistence.allEntities,
75
73
  ~chainIds=state->IndexerState.crossChainState->CrossChainState.chainIds,
76
74
  )
77
75
  }
@@ -27,7 +27,7 @@ async function runOnce(state) {
27
27
  let persistence = IndexerState.persistence(state);
28
28
  let storage = Persistence.getInitializedStorageOrThrow(persistence);
29
29
  let readyAt = new Date();
30
- await storage.finalizeBackfill(persistence.allEntities, chainIds, readyAt);
30
+ await storage.finalizeBackfill(chainIds, readyAt);
31
31
  return IndexerState.markReady(state, readyAt);
32
32
  }
33
33
 
@@ -42,9 +42,8 @@ function run(state) {
42
42
  }
43
43
 
44
44
  function repairSchemaIndexes(state) {
45
- let persistence = IndexerState.persistence(state);
46
- let storage = Persistence.getInitializedStorageOrThrow(persistence);
47
- return storage.ensureSchemaIndexes(persistence.allEntities, CrossChainState.chainIds(IndexerState.crossChainState(state)));
45
+ let storage = Persistence.getInitializedStorageOrThrow(IndexerState.persistence(state));
46
+ return storage.ensureSchemaIndexes(CrossChainState.chainIds(IndexerState.crossChainState(state)));
48
47
  }
49
48
 
50
49
  export {
@@ -54,8 +54,8 @@ let registerContractHandlers = async (~logger, ~contractName, ~handler: option<s
54
54
  }
55
55
  }
56
56
 
57
- let globAutoLoadFiles = async (~handlers) => {
58
- let srcPattern = `./${handlers}/**/*.{js,mjs,ts}`
57
+ let getAutoLoadFiles = async (~config: Config.t) => {
58
+ let srcPattern = `./${config.handlers}/**/*.{js,mjs,ts}`
59
59
  try {
60
60
  let iterator = globIterator(srcPattern)
61
61
  let files = await iterator->Utils.Array.fromAsyncIterator
@@ -77,14 +77,6 @@ let globAutoLoadFiles = async (~handlers) => {
77
77
  }
78
78
  }
79
79
 
80
- let getAutoLoadFiles = async (~config: Config.t) =>
81
- switch config.subgraph {
82
- // A subgraph project's `src/` holds AssemblyScript mappings, not envio
83
- // handlers: auto-loading or type-checking them as TypeScript would fail.
84
- | Some(_) => []
85
- | None => await globAutoLoadFiles(~handlers=config.handlers)
86
- }
87
-
88
80
  let typeCheck = async (~config: Config.t, ~logger, ~autoLoadFiles) => {
89
81
  // A contract's `handler:` may also be an auto-loaded file.
90
82
  let files =
@@ -111,16 +103,6 @@ let typeCheck = async (~config: Config.t, ~logger, ~autoLoadFiles) => {
111
103
  // `HandlerRegister.finishRegistration`. This loads the user handler files
112
104
  // (populating the global `HandlerRegister` registry as a side effect) and
113
105
  // returns the resulting per-chain registrations.
114
- // The subgraph runtime lives in this package but is never exported: it's
115
- // reached only from here, when the resolved config carries a translated
116
- // manifest.
117
- let registerSubgraph: (
118
- JSON.t,
119
- ~isDev: bool,
120
- ~logger: Logger.t,
121
- ) => promise<unit> = %raw(`(subgraph, isDev, logger) =>
122
- import("./subgraph/runtime.ts").then((m) => m.registerSubgraph({ ...subgraph, isDev }, logger))`)
123
-
124
106
  let registerAllHandlers = async (
125
107
  ~config: Config.t,
126
108
  ~logger,
@@ -129,27 +111,21 @@ let registerAllHandlers = async (
129
111
  HandlerRegister.startRegistration(~config, ~logger)
130
112
  registerTsHooks()
131
113
 
132
- switch config.subgraph {
133
- // Registered after the TypeScript hooks, so the subgraph runtime's own hooks
134
- // see a mapping first.
135
- | Some(subgraph) => await registerSubgraph(subgraph, ~isDev=config.isDev, ~logger)
136
- | None =>
137
- let _ = await autoLoadFiles
138
- ->Array.map(file => {
139
- importHandler(file)->Promise.catch(exn => {
140
- let cause = exn->Utils.prettifyExn->Obj.magic
141
- logger->Logger.errorWithExn(`Failed to auto-load handler file: ${file}`, exn)
142
- JsError.throwWithMessage(`Failed to auto-load handler file: ${file}. Cause: ${cause}`)
143
- })
114
+ let _ = await autoLoadFiles
115
+ ->Array.map(file => {
116
+ importHandler(file)->Promise.catch(exn => {
117
+ let cause = exn->Utils.prettifyExn->Obj.magic
118
+ logger->Logger.errorWithExn(`Failed to auto-load handler file: ${file}`, exn)
119
+ JsError.throwWithMessage(`Failed to auto-load handler file: ${file}. Cause: ${cause}`)
144
120
  })
145
- ->Promise.all
121
+ })
122
+ ->Promise.all
146
123
 
147
- let _ = await config.contractHandlers
148
- ->Array.map(({name, handler}) => {
149
- registerContractHandlers(~logger, ~contractName=name, ~handler)
150
- })
151
- ->Promise.all
152
- }
124
+ let _ = await config.contractHandlers
125
+ ->Array.map(({name, handler}) => {
126
+ registerContractHandlers(~logger, ~contractName=name, ~handler)
127
+ })
128
+ ->Promise.all
153
129
 
154
130
  HandlerRegister.finishRegistration(~config)
155
131
  }
@@ -58,8 +58,8 @@ async function registerContractHandlers(logger, contractName, handler) {
58
58
  }
59
59
  }
60
60
 
61
- async function globAutoLoadFiles(handlers) {
62
- let srcPattern = `./` + handlers + `/**/*.{js,mjs,ts}`;
61
+ async function getAutoLoadFiles(config) {
62
+ let srcPattern = `./` + config.handlers + `/**/*.{js,mjs,ts}`;
63
63
  try {
64
64
  let iterator = Promises.glob(srcPattern);
65
65
  let files = await Utils.$$Array.fromAsyncIterator(iterator);
@@ -70,15 +70,6 @@ async function globAutoLoadFiles(handlers) {
70
70
  }
71
71
  }
72
72
 
73
- async function getAutoLoadFiles(config) {
74
- let match = config.subgraph;
75
- if (match !== undefined) {
76
- return [];
77
- } else {
78
- return await globAutoLoadFiles(config.handlers);
79
- }
80
- }
81
-
82
73
  async function typeCheck(config, logger, autoLoadFiles) {
83
74
  let files = Array.from(new Set(autoLoadFiles.concat(Stdlib_Array.filterMap(config.contractHandlers, param => param.handler)).filter(isTypeScript).map(toAbsolutePath)));
84
75
  if (files.length === 0) {
@@ -94,23 +85,15 @@ async function typeCheck(config, logger, autoLoadFiles) {
94
85
  }
95
86
  }
96
87
 
97
- let registerSubgraph = ((subgraph, isDev, logger) =>
98
- import("./subgraph/runtime.ts").then((m) => m.registerSubgraph({ ...subgraph, isDev }, logger)));
99
-
100
88
  async function registerAllHandlers(config, logger, autoLoadFiles) {
101
89
  HandlerRegister.startRegistration(config, logger);
102
90
  registerTsHooks();
103
- let subgraph = config.subgraph;
104
- if (subgraph !== undefined) {
105
- await registerSubgraph(subgraph, config.isDev, logger);
106
- } else {
107
- await Promise.all(autoLoadFiles.map(file => Stdlib_Promise.$$catch(importHandler(file), exn => {
108
- let cause = Utils.prettifyExn(exn);
109
- Logger.errorWithExn(logger, `Failed to auto-load handler file: ` + file, exn);
110
- return Stdlib_JsError.throwWithMessage(`Failed to auto-load handler file: ` + file + `. Cause: ` + cause);
111
- })));
112
- await Promise.all(config.contractHandlers.map(param => registerContractHandlers(logger, param.name, param.handler)));
113
- }
91
+ await Promise.all(autoLoadFiles.map(file => Stdlib_Promise.$$catch(importHandler(file), exn => {
92
+ let cause = Utils.prettifyExn(exn);
93
+ Logger.errorWithExn(logger, `Failed to auto-load handler file: ` + file, exn);
94
+ return Stdlib_JsError.throwWithMessage(`Failed to auto-load handler file: ` + file + `. Cause: ` + cause);
95
+ })));
96
+ await Promise.all(config.contractHandlers.map(param => registerContractHandlers(logger, param.name, param.handler)));
114
97
  return HandlerRegister.finishRegistration(config);
115
98
  }
116
99
 
@@ -121,10 +104,8 @@ export {
121
104
  toAbsolutePath,
122
105
  importHandler,
123
106
  registerContractHandlers,
124
- globAutoLoadFiles,
125
107
  getAutoLoadFiles,
126
108
  typeCheck,
127
- registerSubgraph,
128
109
  registerAllHandlers,
129
110
  }
130
111
  /* url Not a pure module */
@@ -699,8 +699,6 @@ let registerOnBlock = (
699
699
  ~where: unknown,
700
700
  ~fields: unknown,
701
701
  ~handler: Internal.onBlockArgs => promise<unit>,
702
- ~runsFirst=false,
703
- ~contractRegister=?,
704
702
  ~getChainsObject: Config.t => dict<unknown>,
705
703
  ) => {
706
704
  withRegistration(registration => {
@@ -794,8 +792,6 @@ let registerOnBlock = (
794
792
  interval: range._every,
795
793
  chainId,
796
794
  handler,
797
- runsFirst,
798
- ?contractRegister,
799
795
  blockFieldMask,
800
796
  }: Internal.onBlockRegistration
801
797
  ),
@@ -440,8 +440,7 @@ function extractRange(filter, name, ecosystem) {
440
440
  }
441
441
  }
442
442
 
443
- function registerOnBlock(name, where, fields, handler, runsFirstOpt, contractRegister, getChainsObject) {
444
- let runsFirst = runsFirstOpt !== undefined ? runsFirstOpt : false;
443
+ function registerOnBlock(name, where, fields, handler, getChainsObject) {
445
444
  withRegistration(registration => {
446
445
  let config = registration.config;
447
446
  let ecosystem = config.ecosystem;
@@ -498,9 +497,7 @@ function registerOnBlock(name, where, fields, handler, runsFirstOpt, contractReg
498
497
  endBlock: range._lte,
499
498
  interval: range._every,
500
499
  handler: handler,
501
- blockFieldMask: blockFieldMask,
502
- runsFirst: runsFirst,
503
- contractRegister: contractRegister
500
+ blockFieldMask: blockFieldMask
504
501
  });
505
502
  });
506
503
  if (!matchedAny.contents) {
@@ -46,7 +46,5 @@ let registerOnBlock: (
46
46
  ~where: unknown,
47
47
  ~fields: unknown,
48
48
  ~handler: Internal.onBlockArgs => promise<unit>,
49
- ~runsFirst: bool=?,
50
- ~contractRegister: Internal.onBlockContractRegisterArgs => promise<unit>=?,
51
49
  ~getChainsObject: Config.t => dict<unknown>,
52
50
  ) => unit
@@ -22,6 +22,8 @@ module Entity = {
22
22
  }
23
23
 
24
24
  type t = {
25
+ // Keys every id dict and Set below; see Table.table.
26
+ entityIdToKey: EntityId.t => string,
25
27
  latestEntityChangeById: dict<Change.t<Internal.entity>>,
26
28
  // Recorded changes (new latest ids + prevEntityChanges pushes), tracked
27
29
  // manually so InMemoryStore can gauge size without scanning every dict.
@@ -45,12 +47,9 @@ module Entity = {
45
47
  mutable hasIndexes: bool,
46
48
  }
47
49
 
48
- // Helper to extract an entity's id as a dict key. The raw id may be a
49
- // string/int/bigint, so it's stringified to a stable key for in-memory
50
- // indexing.
51
- let getEntityIdUnsafe = (entity: Internal.entity): string =>
50
+ let getEntityKeyUnsafe = (self: t, entity: Internal.entity): string =>
52
51
  switch (entity->(Utils.magic: Internal.entity => {"id": option<EntityId.t>}))["id"] {
53
- | Some(id) => id->EntityId.toKey
52
+ | Some(id) => self.entityIdToKey(id)
54
53
  | None => JsError.throwWithMessage("Property 'id' does not exist on expected entity object")
55
54
  }
56
55
 
@@ -62,7 +61,8 @@ module Entity = {
62
61
  }
63
62
  }
64
63
 
65
- let make = (): t => {
64
+ let make = (~entityIdToKey): t => {
65
+ entityIdToKey,
66
66
  latestEntityChangeById: Dict.make(),
67
67
  changesCount: 0.,
68
68
  dirtyIds: Utils.Set.make(),
@@ -141,10 +141,8 @@ module Entity = {
141
141
  self.hasIndexes = false
142
142
  }
143
143
 
144
- let updateIndexes = (self: t, ~entity: Internal.entity) =>
144
+ let updateIndexes = (self: t, ~entityId: string, ~entity: Internal.entity) =>
145
145
  if self.hasIndexes {
146
- let entityId = entity->getEntityIdUnsafe
147
-
148
146
  switch self.indexesByEntityId->Utils.Dict.dangerouslyGetNonOption(entityId) {
149
147
  | None => ()
150
148
  | Some(indexes) =>
@@ -185,7 +183,7 @@ module Entity = {
185
183
  }
186
184
 
187
185
  let set = (inMemTable: t, ~committedCheckpointId, change: Change.t<Internal.entity>) => {
188
- let entityKey = change->Change.getEntityId->EntityId.toKey
186
+ let entityKey = inMemTable.entityIdToKey(change->Change.getEntityId)
189
187
  switch inMemTable.latestEntityChangeById->Utils.Dict.dangerouslyGetNonOption(entityKey) {
190
188
  | Some(prev) =>
191
189
  let prevCheckpointId = prev->Change.getCheckpointId
@@ -200,8 +198,8 @@ module Entity = {
200
198
  }
201
199
 
202
200
  switch change {
203
- | Set({entity}) => inMemTable->updateIndexes(~entity)
204
- | Delete({entityId}) => inMemTable->deleteEntityFromIndexes(~entityId=entityId->EntityId.toKey)
201
+ | Set({entity}) => inMemTable->updateIndexes(~entityId=entityKey, ~entity)
202
+ | Delete(_) => inMemTable->deleteEntityFromIndexes(~entityId=entityKey)
205
203
  }
206
204
  inMemTable.latestEntityChangeById->Dict.set(entityKey, change)
207
205
  if change->Change.getCheckpointId > committedCheckpointId {
@@ -214,11 +212,14 @@ module Entity = {
214
212
  let initValue = (
215
213
  inMemTable: t,
216
214
  ~committedCheckpointId,
217
- ~key: string,
215
+ ~entityId: EntityId.t,
218
216
  ~entity: option<Internal.entity>,
219
217
  ) =>
220
- if inMemTable.latestEntityChangeById->Utils.Dict.dangerouslyGetNonOption(key)->Option.isNone {
221
- let entityId = key->EntityId.unsafeOfString
218
+ if (
219
+ inMemTable.latestEntityChangeById
220
+ ->Utils.Dict.dangerouslyGetNonOption(inMemTable.entityIdToKey(entityId))
221
+ ->Option.isNone
222
+ ) {
222
223
  let change: Change.t<Internal.entity> = switch entity {
223
224
  | Some(entity) => Set({entityId, entity, checkpointId: Internal.loadedFromDbCheckpointId})
224
225
  | None => Delete({entityId, checkpointId: Internal.loadedFromDbCheckpointId})
@@ -312,7 +313,7 @@ module Entity = {
312
313
  switch change->mapChangeToEntity {
313
314
  | Some(entity) =>
314
315
  if index.matcher(entity) {
315
- inMemTable->addToIndex(~index, ~entityId=entity->getEntityIdUnsafe)
316
+ inMemTable->addToIndex(~index, ~entityId=inMemTable->getEntityKeyUnsafe(entity))
316
317
  }
317
318
  | None => ()
318
319
  }
@@ -1,7 +1,6 @@
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 Internal from "./Internal.res.mjs";
6
5
  import * as EntityFilter from "./db/EntityFilter.res.mjs";
7
6
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
@@ -9,10 +8,10 @@ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
9
8
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
10
9
  import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
11
10
 
12
- function getEntityIdUnsafe(entity) {
11
+ function getEntityKeyUnsafe(self, entity) {
13
12
  let id = entity.id;
14
13
  if (id !== undefined) {
15
- return EntityId.toKey(Primitive_option.valFromOption(id));
14
+ return self.entityIdToKey(Primitive_option.valFromOption(id));
16
15
  } else {
17
16
  return Stdlib_JsError.throwWithMessage("Property 'id' does not exist on expected entity object");
18
17
  }
@@ -28,8 +27,9 @@ function addToIndex(self, index, entityId) {
28
27
  }
29
28
  }
30
29
 
31
- function make() {
30
+ function make(entityIdToKey) {
32
31
  return {
32
+ entityIdToKey: entityIdToKey,
33
33
  latestEntityChangeById: {},
34
34
  changesCount: 0,
35
35
  dirtyIds: new Set(),
@@ -99,11 +99,10 @@ function dropCommittedChanges(self, committedCheckpointId, keepLoadedFromDb) {
99
99
  self.hasIndexes = false;
100
100
  }
101
101
 
102
- function updateIndexes(self, entity) {
102
+ function updateIndexes(self, entityId, entity) {
103
103
  if (!self.hasIndexes) {
104
104
  return;
105
105
  }
106
- let entityId = getEntityIdUnsafe(entity);
107
106
  let indexes = self.indexesByEntityId[entityId];
108
107
  if (indexes !== undefined) {
109
108
  let indexes$1 = Primitive_option.valFromOption(indexes);
@@ -144,7 +143,7 @@ function deleteEntityFromIndexes(self, entityId) {
144
143
  }
145
144
 
146
145
  function set(inMemTable, committedCheckpointId, change) {
147
- let entityKey = EntityId.toKey(change.entityId);
146
+ let entityKey = inMemTable.entityIdToKey(change.entityId);
148
147
  let prev = inMemTable.latestEntityChangeById[entityKey];
149
148
  if (prev !== undefined) {
150
149
  let prevCheckpointId = prev.checkpointId;
@@ -156,9 +155,9 @@ function set(inMemTable, committedCheckpointId, change) {
156
155
  inMemTable.changesCount = inMemTable.changesCount + 1;
157
156
  }
158
157
  if (change.type === "SET") {
159
- updateIndexes(inMemTable, change.entity);
158
+ updateIndexes(inMemTable, entityKey, change.entity);
160
159
  } else {
161
- deleteEntityFromIndexes(inMemTable, EntityId.toKey(change.entityId));
160
+ deleteEntityFromIndexes(inMemTable, entityKey);
162
161
  }
163
162
  inMemTable.latestEntityChangeById[entityKey] = change;
164
163
  if (change.checkpointId > committedCheckpointId) {
@@ -167,18 +166,18 @@ function set(inMemTable, committedCheckpointId, change) {
167
166
  }
168
167
  }
169
168
 
170
- function initValue(inMemTable, committedCheckpointId, key, entity) {
171
- if (!Stdlib_Option.isNone(inMemTable.latestEntityChangeById[key])) {
169
+ function initValue(inMemTable, committedCheckpointId, entityId, entity) {
170
+ if (!Stdlib_Option.isNone(inMemTable.latestEntityChangeById[inMemTable.entityIdToKey(entityId)])) {
172
171
  return;
173
172
  }
174
173
  let change = entity !== undefined ? ({
175
174
  type: "SET",
176
- entityId: key,
175
+ entityId: entityId,
177
176
  entity: entity,
178
177
  checkpointId: Internal.loadedFromDbCheckpointId
179
178
  }) : ({
180
179
  type: "DELETE",
181
- entityId: key,
180
+ entityId: entityId,
182
181
  checkpointId: Internal.loadedFromDbCheckpointId
183
182
  });
184
183
  set(inMemTable, committedCheckpointId, change);
@@ -285,7 +284,7 @@ function addEmptyIndex(inMemTable, filter, table) {
285
284
  Utils.Dict.forEach(inMemTable.latestEntityChangeById, change => {
286
285
  let entity = mapChangeToEntity(change);
287
286
  if (entity !== undefined && index_matcher(entity)) {
288
- return addToIndex(inMemTable, index, getEntityIdUnsafe(entity));
287
+ return addToIndex(inMemTable, index, getEntityKeyUnsafe(inMemTable, entity));
289
288
  }
290
289
  });
291
290
  }
@@ -321,7 +320,7 @@ function tryIndexFromLoadedValues(inMemTable, filter, table) {
321
320
  }
322
321
 
323
322
  let Entity = {
324
- getEntityIdUnsafe: getEntityIdUnsafe,
323
+ getEntityKeyUnsafe: getEntityKeyUnsafe,
325
324
  addToIndex: addToIndex,
326
325
  make: make,
327
326
  snapshotChanges: snapshotChanges,
@@ -108,6 +108,12 @@ type t = {
108
108
  // in-process test runner injects a callback that resolves its run promise
109
109
  // instead, so a caught-up run doesn't kill the test process.
110
110
  onExit: option<unit => unit>,
111
+ // Told, in the processing pass that gets it there, that a run holding its
112
+ // realtime transitions has gone as far as it can on its own. The test
113
+ // runner's stand-in supervisor releases from it, so the release lands at a
114
+ // point in the indexer's own progress rather than on a timer.
115
+ onArrivedAtHead: option<unit => unit>,
116
+ mutable reportedArrivalAtHead: bool,
111
117
  // Set once on any fatal error. Every loop checks it to stop iterating and
112
118
  // every launch skips when it's set, so a single failure quiesces the indexer.
113
119
  mutable isStopped: bool,
@@ -157,10 +163,13 @@ let make = (
157
163
  ~exitAfterFirstEventBlock=false,
158
164
  ~onError: ErrorHandling.t => unit,
159
165
  ~onExit=?,
166
+ ~onArrivedAtHead=?,
167
+ ~clock=Clock.real,
160
168
  ) => {
161
169
  let chainMetaThrottler = {
162
170
  let intervalMillis = Env.ThrottleWrites.chainMetadataIntervalMillis
163
171
  Throttler.make(
172
+ ~clock,
164
173
  ~intervalMillis,
165
174
  ~logger=logger->Logger.withFields({
166
175
  "context": "Throttler for chain metadata writes",
@@ -175,7 +184,7 @@ let make = (
175
184
  persistence,
176
185
  allEntities: persistence.allEntities,
177
186
  entities: EntityTables.make(persistence.allEntities->EntityTables.crossChain),
178
- effectState: EffectState.make(),
187
+ effectState: EffectState.make(~clock, ()),
179
188
  rollback: None,
180
189
  committedFrontier,
181
190
  processedFrontier: committedFrontier->Frontier.copy,
@@ -205,6 +214,8 @@ let make = (
205
214
  exitAfterFirstEventBlock,
206
215
  onError,
207
216
  onExit,
217
+ onArrivedAtHead,
218
+ reportedArrivalAtHead: false,
208
219
  isStopped: false,
209
220
  epoch: 0,
210
221
  simulateDeadInputTracker: SimulateDeadInputTracker.makeFromConfig(config),
@@ -225,12 +236,13 @@ let make = (
225
236
  }
226
237
 
227
238
  // Check if progress is past the reorg threshold (safe block).
228
- // A chain is in reorg threshold when progressBlockNumber > sourceBlockNumber - maxReorgDepth.
229
- // This matches the logic in InternalTable.Checkpoints.makeGetReorgCheckpointsQuery.
239
+ // A chain is in reorg threshold once its progress passes the safe block,
240
+ // sourceBlockNumber - maxReorgDepth - 1. This matches the reorg checkpoints
241
+ // query in packages/cli/src/postgres/internal.rs.
230
242
  let isProgressInReorgThreshold = (~progressBlockNumber, ~sourceBlockNumber, ~maxReorgDepth) => {
231
243
  maxReorgDepth > 0 &&
232
244
  sourceBlockNumber > 0 &&
233
- progressBlockNumber > sourceBlockNumber - maxReorgDepth
245
+ progressBlockNumber > sourceBlockNumber - maxReorgDepth - 1
234
246
  }
235
247
 
236
248
  let makeFromDbState = (
@@ -249,6 +261,7 @@ let makeFromDbState = (
249
261
  ~holdRealtime=false,
250
262
  ~onError,
251
263
  ~onExit=?,
264
+ ~onArrivedAtHead=?,
252
265
  ) => {
253
266
  let isInReorgThreshold = if initialState.cleanRun {
254
267
  false
@@ -303,6 +316,8 @@ let makeFromDbState = (
303
316
  ~exitAfterFirstEventBlock,
304
317
  ~onError,
305
318
  ~onExit?,
319
+ ~onArrivedAtHead?,
320
+ ~clock=?sourceTiming->Option.flatMap(timing => timing.clock),
306
321
  )
307
322
  state.crossChainState->CrossChainState.markCaughtUpOnResume
308
323
  initialState.cache->Utils.Dict.forEach(({effectName, count, scope}) => {
@@ -559,6 +574,15 @@ let isHoldingRealtime = (state: t) => state.crossChainState->CrossChainState.isH
559
574
 
560
575
  let hasArrivedAtHead = (state: t) => state.crossChainState->CrossChainState.hasArrivedAtHead
561
576
 
577
+ let reportArrivalAtHead = (state: t) =>
578
+ switch state.onArrivedAtHead {
579
+ | Some(onArrivedAtHead)
580
+ if !state.reportedArrivalAtHead && state->isHoldingRealtime && state->hasArrivedAtHead =>
581
+ state.reportedArrivalAtHead = true
582
+ onArrivedAtHead()
583
+ | _ => ()
584
+ }
585
+
562
586
  let releaseRealtime = (state: t) => {
563
587
  state.crossChainState->CrossChainState.releaseRealtime
564
588
  // Every chain is parked at the head with no batch coming, so nothing would
@@ -2,6 +2,7 @@
2
2
 
3
3
  import * as Env from "./Env.res.mjs";
4
4
  import * as Batch from "./Batch.res.mjs";
5
+ import * as Clock from "./Clock.res.mjs";
5
6
  import * as Utils from "./Utils.res.mjs";
6
7
  import * as Config from "./Config.res.mjs";
7
8
  import * as Logger from "./Logger.res.mjs";
@@ -26,25 +27,26 @@ import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js
26
27
  import * as CheckpointSequence from "./db/CheckpointSequence.res.mjs";
27
28
  import * as SimulateDeadInputTracker from "./SimulateDeadInputTracker.res.mjs";
28
29
 
29
- function make(config, logger, persistence, chainStates, isRealtime, targetBufferSizeOpt, holdRealtimeOpt, committedFrontierOpt, isDevelopmentModeOpt, shouldUseTuiOpt, exitAfterFirstEventBlockOpt, onError, onExit) {
30
+ function make(config, logger, persistence, chainStates, isRealtime, targetBufferSizeOpt, holdRealtimeOpt, committedFrontierOpt, isDevelopmentModeOpt, shouldUseTuiOpt, exitAfterFirstEventBlockOpt, onError, onExit, onArrivedAtHead, clockOpt) {
30
31
  let targetBufferSize = targetBufferSizeOpt !== undefined ? targetBufferSizeOpt : CrossChainState.calculateTargetBufferSize();
31
32
  let holdRealtime = holdRealtimeOpt !== undefined ? holdRealtimeOpt : false;
32
33
  let committedFrontier = committedFrontierOpt !== undefined ? committedFrontierOpt : Frontier.empty();
33
34
  let isDevelopmentMode = isDevelopmentModeOpt !== undefined ? isDevelopmentModeOpt : false;
34
35
  let shouldUseTui = shouldUseTuiOpt !== undefined ? shouldUseTuiOpt : false;
35
36
  let exitAfterFirstEventBlock = exitAfterFirstEventBlockOpt !== undefined ? exitAfterFirstEventBlockOpt : false;
37
+ let clock = clockOpt !== undefined ? clockOpt : Clock.real;
36
38
  let intervalMillis = Env.ThrottleWrites.chainMetadataIntervalMillis;
37
39
  let chainMetaThrottler = Throttler.make(intervalMillis, Logger.withFields(logger, {
38
40
  context: "Throttler for chain metadata writes",
39
41
  intervalMillis: intervalMillis
40
- }));
42
+ }), clock);
41
43
  return {
42
44
  config: config,
43
45
  logger: logger,
44
46
  persistence: persistence,
45
47
  allEntities: persistence.allEntities,
46
48
  entities: EntityTables.make(EntityTables.crossChain(persistence.allEntities)),
47
- effectState: EffectState.make(),
49
+ effectState: EffectState.make(clock, undefined),
48
50
  rollback: undefined,
49
51
  committedFrontier: committedFrontier,
50
52
  processedFrontier: Frontier.copy(committedFrontier),
@@ -68,6 +70,8 @@ function make(config, logger, persistence, chainStates, isRealtime, targetBuffer
68
70
  exitAfterFirstEventBlock: exitAfterFirstEventBlock,
69
71
  onError: onError,
70
72
  onExit: onExit,
73
+ onArrivedAtHead: onArrivedAtHead,
74
+ reportedArrivalAtHead: false,
71
75
  isStopped: false,
72
76
  epoch: 0,
73
77
  scheduleProcessing: () => {},
@@ -88,7 +92,7 @@ function make(config, logger, persistence, chainStates, isRealtime, targetBuffer
88
92
  };
89
93
  }
90
94
 
91
- function makeFromDbState(config, logger, persistence, initialState, registrationsByChainId, isDevelopmentModeOpt, shouldUseTuiOpt, exitAfterFirstEventBlockOpt, sourceTiming, targetBufferSizeOpt, holdRealtimeOpt, onError, onExit) {
95
+ function makeFromDbState(config, logger, persistence, initialState, registrationsByChainId, isDevelopmentModeOpt, shouldUseTuiOpt, exitAfterFirstEventBlockOpt, sourceTiming, targetBufferSizeOpt, holdRealtimeOpt, onError, onExit, onArrivedAtHead) {
92
96
  let isDevelopmentMode = isDevelopmentModeOpt !== undefined ? isDevelopmentModeOpt : false;
93
97
  let shouldUseTui = shouldUseTuiOpt !== undefined ? shouldUseTuiOpt : false;
94
98
  let exitAfterFirstEventBlock = exitAfterFirstEventBlockOpt !== undefined ? exitAfterFirstEventBlockOpt : false;
@@ -99,7 +103,7 @@ function makeFromDbState(config, logger, persistence, initialState, registration
99
103
  let sourceBlockNumber = resumedChainState.sourceBlockNumber;
100
104
  let maxReorgDepth = resumedChainState.maxReorgDepth;
101
105
  if (maxReorgDepth > 0 && sourceBlockNumber > 0) {
102
- return progressBlockNumber > (sourceBlockNumber - maxReorgDepth | 0);
106
+ return progressBlockNumber > ((sourceBlockNumber - maxReorgDepth | 0) - 1 | 0);
103
107
  } else {
104
108
  return false;
105
109
  }
@@ -111,7 +115,7 @@ function makeFromDbState(config, logger, persistence, initialState, registration
111
115
  let chainConfig = ChainMap.get(config.chainMap, chainId);
112
116
  chainStates[resumedChainState.id] = ChainState.makeFromDbState(chainConfig, resumedChainState, initialState.reorgCheckpoints, isInReorgThreshold, isRealtime, config, initialState.contractMapping, registrationsByChainId, logger, sourceTiming);
113
117
  });
114
- let state = make(config, logger, persistence, chainStates, isRealtime, targetBufferSize, holdRealtime, initialState.checkpointFrontier, isDevelopmentMode, shouldUseTui, exitAfterFirstEventBlock, onError, onExit);
118
+ let state = make(config, logger, persistence, chainStates, isRealtime, targetBufferSize, holdRealtime, initialState.checkpointFrontier, isDevelopmentMode, shouldUseTui, exitAfterFirstEventBlock, onError, onExit, onArrivedAtHead, Stdlib_Option.flatMap(sourceTiming, timing => timing.clock));
115
119
  CrossChainState.markCaughtUpOnResume(state.crossChainState);
116
120
  Utils.Dict.forEach(initialState.cache, param => {
117
121
  let count = param.count;
@@ -401,6 +405,14 @@ function hasArrivedAtHead(state) {
401
405
  return CrossChainState.hasArrivedAtHead(state.crossChainState);
402
406
  }
403
407
 
408
+ function reportArrivalAtHead(state) {
409
+ let onArrivedAtHead = state.onArrivedAtHead;
410
+ if (onArrivedAtHead !== undefined && !state.reportedArrivalAtHead && CrossChainState.isHoldingRealtime(state.crossChainState) && CrossChainState.hasArrivedAtHead(state.crossChainState)) {
411
+ state.reportedArrivalAtHead = true;
412
+ return onArrivedAtHead();
413
+ }
414
+ }
415
+
404
416
  function releaseRealtime(state) {
405
417
  CrossChainState.releaseRealtime(state.crossChainState);
406
418
  state.scheduleProcessing();
@@ -917,6 +929,7 @@ export {
917
929
  bindScheduleProcessing,
918
930
  isHoldingRealtime,
919
931
  hasArrivedAtHead,
932
+ reportArrivalAtHead,
920
933
  releaseRealtime,
921
934
  markReady,
922
935
  reportFinished,
@@ -24,6 +24,8 @@ let make: (
24
24
  ~exitAfterFirstEventBlock: bool=?,
25
25
  ~onError: ErrorHandling.t => unit,
26
26
  ~onExit: unit => unit=?,
27
+ ~onArrivedAtHead: unit => unit=?,
28
+ ~clock: Clock.t=?,
27
29
  ) => t
28
30
 
29
31
  let makeFromDbState: (
@@ -40,6 +42,7 @@ let makeFromDbState: (
40
42
  ~holdRealtime: bool=?,
41
43
  ~onError: ErrorHandling.t => unit,
42
44
  ~onExit: unit => unit=?,
45
+ ~onArrivedAtHead: unit => unit=?,
43
46
  ) => t
44
47
 
45
48
  let unexpectedErrorMsg: string
@@ -106,6 +109,7 @@ let shouldFinalizeIndexes: t => bool
106
109
  let bindScheduleProcessing: (t, unit => unit) => unit
107
110
  let isHoldingRealtime: t => bool
108
111
  let hasArrivedAtHead: t => bool
112
+ let reportArrivalAtHead: t => unit
109
113
  // The supervisor's go-ahead for a process driving part of a split run.
110
114
  let releaseRealtime: t => unit
111
115
  let markReady: (t, ~readyAt: Date.t) => unit