envio 3.5.0-alpha.2 → 3.5.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/package.json +6 -6
  2. package/src/Batch.res +8 -8
  3. package/src/Batch.res.mjs +4 -3
  4. package/src/BatchProcessing.res +24 -0
  5. package/src/BatchProcessing.res.mjs +12 -0
  6. package/src/ChainFetching.res +20 -19
  7. package/src/ChainFetching.res.mjs +14 -14
  8. package/src/ChainId.res +58 -0
  9. package/src/ChainId.res.mjs +49 -0
  10. package/src/ChainId.resi +27 -0
  11. package/src/ChainMap.res +12 -26
  12. package/src/ChainMap.res.mjs +5 -41
  13. package/src/ChainMap.resi +5 -20
  14. package/src/ChainMetadata.res +1 -1
  15. package/src/ChainMetadata.res.mjs +3 -2
  16. package/src/ChainState.res +45 -24
  17. package/src/ChainState.res.mjs +40 -16
  18. package/src/ChainState.resi +3 -1
  19. package/src/Config.res +43 -26
  20. package/src/Config.res.mjs +19 -16
  21. package/src/ContractRegisterContext.res +1 -1
  22. package/src/ContractRegisterContext.res.mjs +1 -1
  23. package/src/Core.res +1 -0
  24. package/src/CrossChainState.res +90 -22
  25. package/src/CrossChainState.res.mjs +55 -16
  26. package/src/CrossChainState.resi +5 -1
  27. package/src/Env.res +0 -3
  28. package/src/Env.res.mjs +0 -3
  29. package/src/EventConfigBuilder.res +1 -1
  30. package/src/EventProcessing.res +4 -4
  31. package/src/EventProcessing.res.mjs +5 -5
  32. package/src/FetchState.res +489 -153
  33. package/src/FetchState.res.mjs +399 -129
  34. package/src/FinalizeBackfill.res +61 -0
  35. package/src/FinalizeBackfill.res.mjs +44 -0
  36. package/src/HandlerRegister.res +8 -8
  37. package/src/HandlerRegister.res.mjs +5 -4
  38. package/src/HandlerRegister.resi +1 -1
  39. package/src/InMemoryStore.res +4 -0
  40. package/src/InMemoryStore.res.mjs +1 -1
  41. package/src/InMemoryTable.res +15 -15
  42. package/src/InMemoryTable.res.mjs +14 -14
  43. package/src/IndexerLoop.res +9 -2
  44. package/src/IndexerLoop.res.mjs +8 -1
  45. package/src/IndexerState.res +52 -23
  46. package/src/IndexerState.res.mjs +52 -14
  47. package/src/IndexerState.resi +11 -6
  48. package/src/Internal.res +19 -17
  49. package/src/Internal.res.mjs +18 -9
  50. package/src/LoadLayer.res +10 -2
  51. package/src/LoadLayer.res.mjs +3 -1
  52. package/src/LogSelection.res +2 -2
  53. package/src/Main.res +7 -11
  54. package/src/Main.res.mjs +4 -6
  55. package/src/Metrics.res +6 -6
  56. package/src/Metrics.res.mjs +4 -3
  57. package/src/Persistence.res +23 -4
  58. package/src/PgStorage.res +447 -100
  59. package/src/PgStorage.res.mjs +285 -99
  60. package/src/RawEvent.res +1 -2
  61. package/src/RawEvent.res.mjs +1 -1
  62. package/src/Rollback.res +18 -20
  63. package/src/Rollback.res.mjs +9 -9
  64. package/src/RollbackCommit.res +2 -2
  65. package/src/RollbackCommit.res.mjs +3 -3
  66. package/src/SafeCheckpointTracking.res +2 -2
  67. package/src/SimulateDeadInputTracker.res +17 -21
  68. package/src/SimulateDeadInputTracker.res.mjs +9 -8
  69. package/src/SimulateItems.res +8 -10
  70. package/src/SimulateItems.res.mjs +9 -10
  71. package/src/Sink.res +2 -2
  72. package/src/Sink.res.mjs +3 -2
  73. package/src/TestIndexer.res +14 -14
  74. package/src/TestIndexer.res.mjs +16 -10
  75. package/src/UserContext.res +2 -2
  76. package/src/UserContext.res.mjs +2 -1
  77. package/src/Utils.res +0 -4
  78. package/src/Utils.res.mjs +1 -6
  79. package/src/bindings/ClickHouse.res +20 -2
  80. package/src/bindings/ClickHouse.res.mjs +25 -14
  81. package/src/db/EntityFilter.res +0 -4
  82. package/src/db/EntityFilter.res.mjs +1 -8
  83. package/src/db/EntityHistory.res +1 -1
  84. package/src/db/IndexCatalog.res +248 -0
  85. package/src/db/IndexCatalog.res.mjs +222 -0
  86. package/src/db/IndexDefinition.res +131 -0
  87. package/src/db/IndexDefinition.res.mjs +138 -0
  88. package/src/db/IndexManager.res +145 -0
  89. package/src/db/IndexManager.res.mjs +130 -0
  90. package/src/db/InternalTable.res +54 -23
  91. package/src/db/InternalTable.res.mjs +38 -23
  92. package/src/db/Table.res +24 -14
  93. package/src/db/Table.res.mjs +21 -16
  94. package/src/sources/AddressSet.res +5 -4
  95. package/src/sources/AddressStore.res +25 -14
  96. package/src/sources/AddressStore.res.mjs +10 -12
  97. package/src/sources/Evm.res +2 -2
  98. package/src/sources/Evm.res.mjs +1 -1
  99. package/src/sources/EvmChain.res +3 -3
  100. package/src/sources/EvmChain.res.mjs +3 -3
  101. package/src/sources/EvmHyperSyncSource.res +7 -7
  102. package/src/sources/EvmHyperSyncSource.res.mjs +6 -6
  103. package/src/sources/Fuel.res +2 -2
  104. package/src/sources/Fuel.res.mjs +1 -1
  105. package/src/sources/FuelHyperSyncClient.res +3 -0
  106. package/src/sources/FuelHyperSyncClient.res.mjs +1 -0
  107. package/src/sources/FuelHyperSyncSource.res +5 -7
  108. package/src/sources/FuelHyperSyncSource.res.mjs +5 -5
  109. package/src/sources/HyperSync.resi +2 -2
  110. package/src/sources/HyperSyncClient.res +5 -0
  111. package/src/sources/HyperSyncClient.res.mjs +1 -0
  112. package/src/sources/RpcSource.res +13 -14
  113. package/src/sources/RpcSource.res.mjs +13 -12
  114. package/src/sources/SimulateSource.res +42 -15
  115. package/src/sources/SimulateSource.res.mjs +27 -9
  116. package/src/sources/Source.res +1 -5
  117. package/src/sources/SourceManager.res +12 -12
  118. package/src/sources/SourceManager.res.mjs +11 -10
  119. package/src/sources/SourceManager.resi +2 -2
  120. package/src/sources/Svm.res +4 -5
  121. package/src/sources/Svm.res.mjs +5 -4
  122. package/src/sources/SvmHyperSyncClient.res +3 -0
  123. package/src/sources/SvmHyperSyncClient.res.mjs +1 -0
  124. package/src/sources/SvmHyperSyncSource.res +4 -4
  125. package/src/sources/SvmHyperSyncSource.res.mjs +3 -3
  126. package/src/tui/Tui.res +1 -1
  127. package/src/tui/Tui.res.mjs +2 -1
  128. package/src/tui/components/CustomHooks.res +5 -4
  129. package/src/tui/components/CustomHooks.res.mjs +4 -2
@@ -0,0 +1,61 @@
1
+ // The FinalizingIndexes phase. Reached from the processing loop when every
2
+ // chain has caught up: processing is already paused (the loop awaits this),
3
+ // pending writes are flushed, then storage builds every missing schema-defined
4
+ // index and, once they all verify, commits `ready_at`. A failure part-way
5
+ // leaves the indexes built so far in place and reaches the processing loop's
6
+ // error boundary; the retry only owes what's left. Either way the indexer never
7
+ // reports ready with an index the schema promised still missing.
8
+
9
+ let runOnce = async (state: IndexerState.t) => {
10
+ Logging.info(
11
+ "All chains are caught up. Finalizing the indexer before switching to realtime: flushing pending writes, then creating the indexes the schema promises.",
12
+ )
13
+
14
+ await Writing.flush(state)
15
+
16
+ // A failed write already surfaced through onError; committing ready_at on top
17
+ // of an incomplete write would claim progress that isn't durable.
18
+ if !(state->IndexerState.hasFailedWrite) {
19
+ let persistence = state->IndexerState.persistence
20
+ let storage = persistence->Persistence.getInitializedStorageOrThrow
21
+ let readyAt = Date.make()
22
+
23
+ await storage.finalizeBackfill(
24
+ ~entities=persistence.allEntities,
25
+ ~chainIds=state
26
+ ->IndexerState.chainStates
27
+ ->Dict.valuesToArray
28
+ ->Array.map(cs => (cs->ChainState.chainConfig).id),
29
+ ~readyAt,
30
+ )
31
+
32
+ // Only after the commit: in-memory readiness must never run ahead of the
33
+ // `ready_at` a restart would read back.
34
+ state->IndexerState.markReady(~readyAt)
35
+ Logging.info("The indexer is ready. Switching to realtime indexing.")
36
+ }
37
+ }
38
+
39
+ // Several paths reach the phase — a processed batch, a tick that progressed
40
+ // nothing — and a height update can bring another one round while the first is
41
+ // still building. They all join the in-flight run rather than starting a second
42
+ // pass over the same indexes.
43
+ let run = (state: IndexerState.t) =>
44
+ switch state->IndexerState.finalizeFiber {
45
+ | Some(fiber) => fiber
46
+ | None =>
47
+ let fiber = runOnce(state)->Promise.finally(() => state->IndexerState.endFinalizeFiber)
48
+ state->IndexerState.beginFinalizeFiber(fiber)
49
+ fiber
50
+ }
51
+
52
+ // An indexer resumed already ready never reaches `run`, so nothing above would
53
+ // notice an index the schema promises being dropped or invalidated while it was
54
+ // down — `ensureQueryIndexes` only covers what a getWhere actually asks for.
55
+ // Best-effort and not awaited by the loop: indexing is already live and correct
56
+ // without the index, just slower.
57
+ let repairSchemaIndexes = (state: IndexerState.t) => {
58
+ let persistence = state->IndexerState.persistence
59
+ let storage = persistence->Persistence.getInitializedStorageOrThrow
60
+ storage.ensureSchemaIndexes(~entities=persistence.allEntities)
61
+ }
@@ -0,0 +1,44 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Logging from "./Logging.res.mjs";
4
+ import * as Writing from "./Writing.res.mjs";
5
+ import * as ChainState from "./ChainState.res.mjs";
6
+ import * as Persistence from "./Persistence.res.mjs";
7
+ import * as IndexerState from "./IndexerState.res.mjs";
8
+
9
+ async function runOnce(state) {
10
+ Logging.info("All chains are caught up. Finalizing the indexer before switching to realtime: flushing pending writes, then creating the indexes the schema promises.");
11
+ await Writing.flush(state);
12
+ if (IndexerState.hasFailedWrite(state)) {
13
+ return;
14
+ }
15
+ let persistence = IndexerState.persistence(state);
16
+ let storage = Persistence.getInitializedStorageOrThrow(persistence);
17
+ let readyAt = new Date();
18
+ await storage.finalizeBackfill(persistence.allEntities, Object.values(IndexerState.chainStates(state)).map(cs => ChainState.chainConfig(cs).id), readyAt);
19
+ IndexerState.markReady(state, readyAt);
20
+ return Logging.info("The indexer is ready. Switching to realtime indexing.");
21
+ }
22
+
23
+ function run(state) {
24
+ let fiber = IndexerState.finalizeFiber(state);
25
+ if (fiber !== undefined) {
26
+ return fiber;
27
+ }
28
+ let fiber$1 = runOnce(state).finally(() => IndexerState.endFinalizeFiber(state));
29
+ IndexerState.beginFinalizeFiber(state, fiber$1);
30
+ return fiber$1;
31
+ }
32
+
33
+ function repairSchemaIndexes(state) {
34
+ let persistence = IndexerState.persistence(state);
35
+ let storage = Persistence.getInitializedStorageOrThrow(persistence);
36
+ return storage.ensureSchemaIndexes(persistence.allEntities);
37
+ }
38
+
39
+ export {
40
+ runOnce,
41
+ run,
42
+ repairSchemaIndexes,
43
+ }
44
+ /* Logging Not a pure module */
@@ -82,8 +82,8 @@ let startRegistration = (~config: Config.t) => {
82
82
  }
83
83
  }
84
84
 
85
- let getChainRegistrations = (r: activeRegistration, ~chainId: int): chainRegistrations => {
86
- let key = chainId->Int.toString
85
+ let getChainRegistrations = (r: activeRegistration, ~chainId: ChainId.t): chainRegistrations => {
86
+ let key = chainId->ChainId.toString
87
87
  switch r.registrationsByChainId->Utils.Dict.dangerouslyGetNonOption(key) {
88
88
  | Some(existing) => existing
89
89
  | None =>
@@ -98,7 +98,7 @@ let getChainRegistrations = (r: activeRegistration, ~chainId: int): chainRegistr
98
98
 
99
99
  let buildOnEventRegistrationWith = (
100
100
  ~config: Config.t,
101
- ~chainId: int,
101
+ ~chainId: ChainId.t,
102
102
  ~eventConfig: Internal.eventConfig,
103
103
  ~isWildcard: bool,
104
104
  ~handler: option<Internal.handler>,
@@ -342,10 +342,10 @@ let setContractRegister = (~contractName, ~eventName, contractRegister, ~eventOp
342
342
  }
343
343
 
344
344
  // Raw onEvent registrations stored for a chain (empty if the chain has none).
345
- let storedOnEventRegistrations = (r: activeRegistration, ~chainId: int): array<
345
+ let storedOnEventRegistrations = (r: activeRegistration, ~chainId: ChainId.t): array<
346
346
  Internal.onEventRegistration,
347
347
  > =>
348
- switch r.registrationsByChainId->Utils.Dict.dangerouslyGetNonOption(chainId->Int.toString) {
348
+ switch r.registrationsByChainId->Utils.Dict.dangerouslyGetNonOption(chainId->ChainId.toString) {
349
349
  | Some(chainRegs) => chainRegs.onEventRegistrations
350
350
  | None => []
351
351
  }
@@ -373,7 +373,7 @@ let isWildcard = (~contractName, ~eventName) =>
373
373
  // item still produces an item to run.
374
374
  let getSimulateOnEventRegistrations = (
375
375
  ~config: Config.t,
376
- ~chainId: int,
376
+ ~chainId: ChainId.t,
377
377
  ~eventConfig: Internal.eventConfig,
378
378
  ): array<Internal.onEventRegistration> => {
379
379
  let stored = switch getActiveRegistration() {
@@ -412,7 +412,7 @@ let finishRegistration = (~config: Config.t): registrationsByChainId => {
412
412
  ->ChainMap.values
413
413
  ->Array.forEach(chainConfig => {
414
414
  let chainId = chainConfig.id
415
- let key = chainId->Int.toString
415
+ let key = chainId->ChainId.toString
416
416
 
417
417
  let builtRegs = mergeRegistrations(r->storedOnEventRegistrations(~chainId), ~config)
418
418
  let registeredKeys = Utils.Set.make()
@@ -638,7 +638,7 @@ let registerOnBlock = (
638
638
  ->ChainMap.values
639
639
  ->Array.forEach(chainConfig => {
640
640
  let chainId = chainConfig.id
641
- let chainObj = chainsDict->Dict.getUnsafe(chainId->Int.toString)
641
+ let chainObj = chainsDict->Dict.getUnsafe(chainId->ChainId.toString)
642
642
 
643
643
  // Predicate returns `true` → match with no filter; `false` → skip;
644
644
  // any plain object → structured filter. `undefined`/`null` returns
@@ -1,6 +1,7 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
3
  import * as Utils from "./Utils.res.mjs";
4
+ import * as ChainId from "./ChainId.res.mjs";
4
5
  import * as Logging from "./Logging.res.mjs";
5
6
  import * as ChainMap from "./ChainMap.res.mjs";
6
7
  import * as EnvioGlobal from "./EnvioGlobal.res.mjs";
@@ -54,7 +55,7 @@ function startRegistration(config) {
54
55
  }
55
56
 
56
57
  function getChainRegistrations(r, chainId) {
57
- let key = chainId.toString();
58
+ let key = ChainId.toString(chainId);
58
59
  let existing = r.registrationsByChainId[key];
59
60
  if (existing !== undefined) {
60
61
  return existing;
@@ -220,7 +221,7 @@ function setContractRegister(contractName, eventName, contractRegister, eventOpt
220
221
  }
221
222
 
222
223
  function storedOnEventRegistrations(r, chainId) {
223
- let chainRegs = r.registrationsByChainId[chainId.toString()];
224
+ let chainRegs = r.registrationsByChainId[ChainId.toString(chainId)];
224
225
  if (chainRegs !== undefined) {
225
226
  return chainRegs.onEventRegistrations;
226
227
  } else {
@@ -270,7 +271,7 @@ function finishRegistration(config) {
270
271
  let registrationsByChainId = {};
271
272
  ChainMap.values(config.chainMap).forEach(chainConfig => {
272
273
  let chainId = chainConfig.id;
273
- let key = chainId.toString();
274
+ let key = ChainId.toString(chainId);
274
275
  let builtRegs = mergeRegistrations(storedOnEventRegistrations(r, chainId), config);
275
276
  let registeredKeys = new Set();
276
277
  builtRegs.forEach(reg => {
@@ -385,7 +386,7 @@ function registerOnBlock(name, where, handler, getChainsObject) {
385
386
  };
386
387
  ChainMap.values(config.chainMap).forEach(chainConfig => {
387
388
  let chainId = chainConfig.id;
388
- let chainObj = chainsDict[chainId.toString()];
389
+ let chainObj = chainsDict[ChainId.toString(chainId)];
389
390
  let result = where$1 !== undefined ? where$1({
390
391
  chain: chainObj
391
392
  }) : true;
@@ -26,7 +26,7 @@ let isWildcard: (~contractName: string, ~eventName: string) => bool
26
26
  let isDroppedByWhere: (~config: Config.t, Internal.onEventRegistration) => bool
27
27
  let getSimulateOnEventRegistrations: (
28
28
  ~config: Config.t,
29
- ~chainId: int,
29
+ ~chainId: ChainId.t,
30
30
  ~eventConfig: Internal.eventConfig,
31
31
  ) => array<Internal.onEventRegistration>
32
32
 
@@ -108,7 +108,11 @@ let prepareRollbackDiff = async (
108
108
  let deletedEntities = Dict.make()
109
109
  let setEntities = Dict.make()
110
110
 
111
+ // Rollback data comes from Postgres entity history, which is kept only for
112
+ // Postgres-backed entities. ClickHouse-only entities have no history to
113
+ // restore from, so a reorg leaves them un-reverted in the sink.
111
114
  let _ = await persistence.allEntities
115
+ ->Array.filter(entityConfig => entityConfig.storage.postgres)
112
116
  ->Array.map(async entityConfig => {
113
117
  let entityTable = state->getInMemTable(~entityConfig)
114
118
 
@@ -86,7 +86,7 @@ async function prepareRollbackDiff(state, rollbackTargetCheckpointId, rollbackDi
86
86
  let committedCheckpointId = IndexerState.committedCheckpointId(state);
87
87
  let deletedEntities = {};
88
88
  let setEntities = {};
89
- await Promise.all(persistence.allEntities.map(async entityConfig => {
89
+ await Promise.all(persistence.allEntities.filter(entityConfig => entityConfig.storage.postgres).map(async entityConfig => {
90
90
  let entityTable = getInMemTable(state, entityConfig);
91
91
  let match = await persistence.storage.getRollbackData(entityConfig, rollbackTargetCheckpointId);
92
92
  match[0].forEach(entityId => {
@@ -2,7 +2,7 @@ module Entity = {
2
2
  type relatedEntityId = string
3
3
  type filterWithRelatedIds = (EntityFilter.t, Utils.Set.t<relatedEntityId>)
4
4
  // Keyed by EntityFilter.toString
5
- type filterIndices = dict<filterWithRelatedIds>
5
+ type filterIndexes = dict<filterWithRelatedIds>
6
6
 
7
7
  type entityFilters = Utils.Set.t<EntityFilter.t>
8
8
  type t = {
@@ -14,7 +14,7 @@ module Entity = {
14
14
  // previous changes persist in the background.
15
15
  mutable prevEntityChanges: array<Change.t<Internal.entity>>,
16
16
  mutable filtersByEntityId: dict<entityFilters>,
17
- mutable filterIndices: filterIndices,
17
+ mutable filterIndexes: filterIndexes,
18
18
  }
19
19
 
20
20
  // Helper to extract an entity's id as a dict key. The raw id may be a
@@ -44,7 +44,7 @@ module Entity = {
44
44
  changesCount: 0.,
45
45
  prevEntityChanges: [],
46
46
  filtersByEntityId: Dict.make(),
47
- filterIndices: Dict.make(),
47
+ filterIndexes: Dict.make(),
48
48
  }
49
49
 
50
50
  // Changes to persist for checkpoints in (committedCheckpointId, upToCheckpointId].
@@ -81,7 +81,7 @@ module Entity = {
81
81
  }
82
82
 
83
83
  // Frees committed changes: drops latest entries at or below committedCheckpointId
84
- // (re-readable from the db) and clears the per-batch indices (rebuilt on the next
84
+ // (re-readable from the db) and clears the per-batch indexes (rebuilt on the next
85
85
  // getWhere). Uncommitted changes are kept. With keepLoadedFromDb, entries seeded
86
86
  // from a db read are spared so the cheaper-to-re-derive writes are dropped first.
87
87
  let dropCommittedChanges = (self: t, ~committedCheckpointId, ~keepLoadedFromDb) => {
@@ -98,10 +98,10 @@ module Entity = {
98
98
  keysToDelete->Array.forEach(key => self.latestEntityChangeById->Utils.Dict.deleteInPlace(key))
99
99
  self.changesCount = self.changesCount -. keysToDelete->Array.length->Int.toFloat
100
100
  self.filtersByEntityId = Dict.make()
101
- self.filterIndices = Dict.make()
101
+ self.filterIndexes = Dict.make()
102
102
  }
103
103
 
104
- let updateIndices = (self: t, ~entity: Internal.entity) => {
104
+ let updateIndexes = (self: t, ~entity: Internal.entity) => {
105
105
  let entityId = entity->getEntityIdUnsafe
106
106
  let entityAsDict = entity->(Utils.magic: Internal.entity => dict<EntityFilter.FieldValue.t>)
107
107
 
@@ -116,7 +116,7 @@ module Entity = {
116
116
  })
117
117
  }
118
118
 
119
- self.filterIndices->Utils.Dict.forEach(((filter, relatedEntityIds)) => {
119
+ self.filterIndexes->Utils.Dict.forEach(((filter, relatedEntityIds)) => {
120
120
  if filter->EntityFilter.matches(~entity=entityAsDict) {
121
121
  //Add entity id to the filter index and the filter to entity filters
122
122
  relatedEntityIds->Utils.Set.add(entityId)->ignore
@@ -127,12 +127,12 @@ module Entity = {
127
127
  })
128
128
  }
129
129
 
130
- let deleteEntityFromIndices = (self: t, ~entityId: string) =>
130
+ let deleteEntityFromIndexes = (self: t, ~entityId: string) =>
131
131
  switch self.filtersByEntityId->Utils.Dict.dangerouslyGetNonOption(entityId) {
132
132
  | None => ()
133
133
  | Some(entityFilters) =>
134
134
  entityFilters->Utils.Set.forEach(filter => {
135
- switch self.filterIndices->Utils.Dict.dangerouslyGetNonOption(
135
+ switch self.filterIndexes->Utils.Dict.dangerouslyGetNonOption(
136
136
  filter->EntityFilter.toString,
137
137
  ) {
138
138
  | Some((_filter, relatedEntityIds)) =>
@@ -159,8 +159,8 @@ module Entity = {
159
159
  }
160
160
 
161
161
  switch change {
162
- | Set({entity}) => inMemTable->updateIndices(~entity)
163
- | Delete({entityId}) => inMemTable->deleteEntityFromIndices(~entityId=entityId->EntityId.toKey)
162
+ | Set({entity}) => inMemTable->updateIndexes(~entity)
163
+ | Delete({entityId}) => inMemTable->deleteEntityFromIndexes(~entityId=entityId->EntityId.toKey)
164
164
  }
165
165
  inMemTable.latestEntityChangeById->Dict.set(entityKey, change)
166
166
  }
@@ -200,12 +200,12 @@ module Entity = {
200
200
 
201
201
  let hasIndex = (inMemTable: t) =>
202
202
  (filterKey: string) =>
203
- inMemTable.filterIndices->Utils.Dict.dangerouslyGetNonOption(filterKey) !== None
203
+ inMemTable.filterIndexes->Utils.Dict.dangerouslyGetNonOption(filterKey) !== None
204
204
 
205
205
  let getUnsafeOnIndex = (inMemTable: t) => {
206
206
  let getEntity = inMemTable->getUnsafe
207
207
  (filterKey: string) => {
208
- switch inMemTable.filterIndices->Utils.Dict.dangerouslyGetNonOption(filterKey) {
208
+ switch inMemTable.filterIndexes->Utils.Dict.dangerouslyGetNonOption(filterKey) {
209
209
  | None =>
210
210
  JsError.throwWithMessage(`Unexpected error. Must have an index for the filter ${filterKey}`)
211
211
  | Some((_filter, relatedEntityIds)) =>
@@ -223,7 +223,7 @@ module Entity = {
223
223
 
224
224
  let addEmptyIndex = (inMemTable: t, ~filter: EntityFilter.t) => {
225
225
  let filterKey = filter->EntityFilter.toString
226
- switch inMemTable.filterIndices->Utils.Dict.dangerouslyGetNonOption(filterKey) {
226
+ switch inMemTable.filterIndexes->Utils.Dict.dangerouslyGetNonOption(filterKey) {
227
227
  | Some(_) => () //Should not happen, this means the index already exists
228
228
  | None =>
229
229
  let relatedEntityIds = Utils.Set.make()
@@ -240,7 +240,7 @@ module Entity = {
240
240
  | None => ()
241
241
  }
242
242
  })
243
- inMemTable.filterIndices->Dict.set(filterKey, (filter, relatedEntityIds))
243
+ inMemTable.filterIndexes->Dict.set(filterKey, (filter, relatedEntityIds))
244
244
  }
245
245
  }
246
246
  }
@@ -40,7 +40,7 @@ function make() {
40
40
  changesCount: 0,
41
41
  prevEntityChanges: [],
42
42
  filtersByEntityId: {},
43
- filterIndices: {}
43
+ filterIndexes: {}
44
44
  };
45
45
  }
46
46
 
@@ -84,10 +84,10 @@ function dropCommittedChanges(self, committedCheckpointId, keepLoadedFromDb) {
84
84
  keysToDelete.forEach(key => Utils.Dict.deleteInPlace(self.latestEntityChangeById, key));
85
85
  self.changesCount = self.changesCount - keysToDelete.length;
86
86
  self.filtersByEntityId = {};
87
- self.filterIndices = {};
87
+ self.filterIndexes = {};
88
88
  }
89
89
 
90
- function updateIndices(self, entity) {
90
+ function updateIndexes(self, entity) {
91
91
  let entityId = getEntityIdUnsafe(entity);
92
92
  let entityFilters = self.filtersByEntityId[entityId];
93
93
  if (entityFilters !== undefined) {
@@ -99,7 +99,7 @@ function updateIndices(self, entity) {
99
99
  }
100
100
  });
101
101
  }
102
- Utils.Dict.forEach(self.filterIndices, param => {
102
+ Utils.Dict.forEach(self.filterIndexes, param => {
103
103
  let relatedEntityIds = param[1];
104
104
  let filter = param[0];
105
105
  if (EntityFilter.matches(filter, entity)) {
@@ -111,14 +111,14 @@ function updateIndices(self, entity) {
111
111
  });
112
112
  }
113
113
 
114
- function deleteEntityFromIndices(self, entityId) {
114
+ function deleteEntityFromIndexes(self, entityId) {
115
115
  let entityFilters = self.filtersByEntityId[entityId];
116
116
  if (entityFilters === undefined) {
117
117
  return;
118
118
  }
119
119
  let entityFilters$1 = Primitive_option.valFromOption(entityFilters);
120
120
  entityFilters$1.forEach(filter => {
121
- let match = self.filterIndices[EntityFilter.toString(filter)];
121
+ let match = self.filterIndexes[EntityFilter.toString(filter)];
122
122
  if (match !== undefined) {
123
123
  match[1].delete(entityId);
124
124
  }
@@ -139,9 +139,9 @@ function set(inMemTable, committedCheckpointId, change) {
139
139
  inMemTable.changesCount = inMemTable.changesCount + 1;
140
140
  }
141
141
  if (change.type === "SET") {
142
- updateIndices(inMemTable, change.entity);
142
+ updateIndexes(inMemTable, change.entity);
143
143
  } else {
144
- deleteEntityFromIndices(inMemTable, EntityId.toKey(change.entityId));
144
+ deleteEntityFromIndexes(inMemTable, EntityId.toKey(change.entityId));
145
145
  }
146
146
  inMemTable.latestEntityChangeById[entityKey] = change;
147
147
  }
@@ -174,13 +174,13 @@ function getUnsafe(inMemTable) {
174
174
  }
175
175
 
176
176
  function hasIndex(inMemTable) {
177
- return filterKey => inMemTable.filterIndices[filterKey] !== undefined;
177
+ return filterKey => inMemTable.filterIndexes[filterKey] !== undefined;
178
178
  }
179
179
 
180
180
  function getUnsafeOnIndex(inMemTable) {
181
181
  let getEntity = getUnsafe(inMemTable);
182
182
  return filterKey => {
183
- let match = inMemTable.filterIndices[filterKey];
183
+ let match = inMemTable.filterIndexes[filterKey];
184
184
  if (match !== undefined) {
185
185
  return Stdlib_Array.filterMap(Array.from(match[1]), entityId => {
186
186
  if (entityId in inMemTable.latestEntityChangeById) {
@@ -195,7 +195,7 @@ function getUnsafeOnIndex(inMemTable) {
195
195
 
196
196
  function addEmptyIndex(inMemTable, filter) {
197
197
  let filterKey = EntityFilter.toString(filter);
198
- let match = inMemTable.filterIndices[filterKey];
198
+ let match = inMemTable.filterIndexes[filterKey];
199
199
  if (match !== undefined) {
200
200
  return;
201
201
  }
@@ -212,7 +212,7 @@ function addEmptyIndex(inMemTable, filter) {
212
212
  getOrCreateEntityFilters(inMemTable, entityId).add(filter);
213
213
  relatedEntityIds.add(entityId);
214
214
  });
215
- inMemTable.filterIndices[filterKey] = [
215
+ inMemTable.filterIndexes[filterKey] = [
216
216
  filter,
217
217
  relatedEntityIds
218
218
  ];
@@ -225,8 +225,8 @@ let Entity = {
225
225
  make: make,
226
226
  snapshotChanges: snapshotChanges,
227
227
  dropCommittedChanges: dropCommittedChanges,
228
- updateIndices: updateIndices,
229
- deleteEntityFromIndices: deleteEntityFromIndices,
228
+ updateIndexes: updateIndexes,
229
+ deleteEntityFromIndexes: deleteEntityFromIndexes,
230
230
  set: set,
231
231
  initValue: initValue,
232
232
  mapChangeToEntity: mapChangeToEntity,
@@ -19,10 +19,10 @@ let start = (state: IndexerState.t) => {
19
19
  launch(state, () =>
20
20
  state
21
21
  ->IndexerState.crossChainState
22
- ->CrossChainState.checkAndFetch(~dispatchChain=(~chain, ~action) =>
22
+ ->CrossChainState.checkAndFetch(~dispatchChain=(~chainId, ~action) =>
23
23
  ChainFetching.fetchChain(
24
24
  state,
25
- chain,
25
+ chainId,
26
26
  ~action,
27
27
  ~stateId=state->IndexerState.epoch,
28
28
  ~scheduleFetch,
@@ -38,6 +38,13 @@ let start = (state: IndexerState.t) => {
38
38
  Rollback.rollback(state, ~scheduleFetch, ~scheduleProcessing, ~scheduleRollback)
39
39
  )
40
40
 
41
+ // Resuming already ready means the FinalizingIndexes phase is behind us and
42
+ // won't run again, so this is the only pass that can restore an index the
43
+ // database lost while the indexer was down.
44
+ if state->IndexerState.isRealtime {
45
+ launch(state, () => FinalizeBackfill.repairSchemaIndexes(state))
46
+ }
47
+
41
48
  scheduleFetch()
42
49
  scheduleProcessing()
43
50
  }
@@ -5,6 +5,7 @@ import * as IndexerState from "./IndexerState.res.mjs";
5
5
  import * as ChainFetching from "./ChainFetching.res.mjs";
6
6
  import * as BatchProcessing from "./BatchProcessing.res.mjs";
7
7
  import * as CrossChainState from "./CrossChainState.res.mjs";
8
+ import * as FinalizeBackfill from "./FinalizeBackfill.res.mjs";
8
9
 
9
10
  function launch(state, work) {
10
11
  if (!IndexerState.isStopped(state)) {
@@ -15,7 +16,7 @@ function launch(state, work) {
15
16
 
16
17
  function start(state) {
17
18
  let scheduleFetch = () => {
18
- let work = () => CrossChainState.checkAndFetch(IndexerState.crossChainState(state), (chain, action) => ChainFetching.fetchChain(state, chain, action, IndexerState.epoch(state), scheduleFetch, scheduleProcessing, scheduleRollback));
19
+ let work = () => CrossChainState.checkAndFetch(IndexerState.crossChainState(state), (chainId, action) => ChainFetching.fetchChain(state, chainId, action, IndexerState.epoch(state), scheduleFetch, scheduleProcessing, scheduleRollback));
19
20
  if (!IndexerState.isStopped(state)) {
20
21
  work();
21
22
  return;
@@ -35,6 +36,12 @@ function start(state) {
35
36
  return;
36
37
  }
37
38
  };
39
+ if (IndexerState.isRealtime(state)) {
40
+ let work = () => FinalizeBackfill.repairSchemaIndexes(state);
41
+ if (!IndexerState.isStopped(state)) {
42
+ work();
43
+ }
44
+ }
38
45
  scheduleFetch();
39
46
  scheduleProcessing();
40
47
  }