envio 3.11.0 → 3.12.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 (89) hide show
  1. package/index.d.ts +3 -0
  2. package/package.json +6 -6
  3. package/src/Batch.res +111 -78
  4. package/src/Batch.res.mjs +53 -46
  5. package/src/BatchProcessing.res +1 -11
  6. package/src/BatchProcessing.res.mjs +1 -4
  7. package/src/ChainState.res +72 -19
  8. package/src/ChainState.res.mjs +50 -12
  9. package/src/ChainState.resi +5 -0
  10. package/src/Config.res +58 -37
  11. package/src/Config.res.mjs +63 -53
  12. package/src/CrossChainState.res +49 -52
  13. package/src/CrossChainState.res.mjs +25 -23
  14. package/src/CrossChainState.resi +6 -13
  15. package/src/EffectState.res +13 -4
  16. package/src/EffectState.resi +7 -1
  17. package/src/EventProcessing.res +6 -6
  18. package/src/EventProcessing.res.mjs +6 -6
  19. package/src/FetchState.res +14 -0
  20. package/src/FetchState.res.mjs +14 -0
  21. package/src/FinalizeBackfill.res +16 -12
  22. package/src/FinalizeBackfill.res.mjs +3 -3
  23. package/src/HistoryPolicy.res +46 -0
  24. package/src/HistoryPolicy.res.mjs +50 -0
  25. package/src/InMemoryStore.res +30 -32
  26. package/src/InMemoryStore.res.mjs +33 -28
  27. package/src/IndexerState.res +66 -45
  28. package/src/IndexerState.res.mjs +57 -29
  29. package/src/IndexerState.resi +8 -13
  30. package/src/Internal.res +4 -2
  31. package/src/LoadLayer.res +11 -4
  32. package/src/LoadLayer.res.mjs +8 -5
  33. package/src/Main.res +25 -24
  34. package/src/Main.res.mjs +24 -25
  35. package/src/Metrics.res +49 -1
  36. package/src/Metrics.res.mjs +26 -3
  37. package/src/Persistence.res +55 -16
  38. package/src/Persistence.res.mjs +18 -3
  39. package/src/PgStorage.res +194 -118
  40. package/src/PgStorage.res.mjs +132 -72
  41. package/src/PruneStaleHistory.res +9 -15
  42. package/src/PruneStaleHistory.res.mjs +11 -20
  43. package/src/Rollback.res +9 -14
  44. package/src/Rollback.res.mjs +5 -3
  45. package/src/Sink.res +7 -5
  46. package/src/Sink.res.mjs +4 -4
  47. package/src/Staging.res +298 -0
  48. package/src/Staging.res.mjs +257 -0
  49. package/src/TestIndexer.res +5 -5
  50. package/src/TestIndexer.res.mjs +7 -6
  51. package/src/UserContext.res +7 -4
  52. package/src/UserContext.res.mjs +5 -2
  53. package/src/Utils.res +9 -0
  54. package/src/Utils.res.mjs +9 -0
  55. package/src/Writing.res +34 -20
  56. package/src/Writing.res.mjs +15 -20
  57. package/src/bindings/ClickHouse.res +41 -34
  58. package/src/bindings/ClickHouse.res.mjs +36 -30
  59. package/src/bindings/ClickHouseSink.res +45 -186
  60. package/src/bindings/ClickHouseSink.res.mjs +21 -174
  61. package/src/db/CheckpointSequence.res +109 -0
  62. package/src/db/CheckpointSequence.res.mjs +99 -0
  63. package/src/db/EntityHistory.res +6 -5
  64. package/src/db/EntityHistory.res.mjs +5 -5
  65. package/src/db/Frontier.res +86 -0
  66. package/src/db/Frontier.res.mjs +126 -0
  67. package/src/db/InternalTable.res +145 -27
  68. package/src/db/InternalTable.res.mjs +115 -29
  69. package/src/db/RollbackFloors.res +34 -41
  70. package/src/db/RollbackFloors.res.mjs +27 -71
  71. package/src/sources/BlockStore.res +3 -0
  72. package/src/sources/EvmHyperSyncSource.res +3 -1
  73. package/src/sources/EvmHyperSyncSource.res.mjs +2 -1
  74. package/src/sources/HyperSync.res +2 -0
  75. package/src/sources/HyperSync.res.mjs +1 -0
  76. package/src/sources/HyperSync.resi +1 -0
  77. package/src/sources/HyperSyncClient.res +3 -0
  78. package/src/sources/RequestStat.res +8 -1
  79. package/src/sources/Source.res +4 -3
  80. package/src/sources/SourceManager.res +30 -10
  81. package/src/sources/SourceManager.res.mjs +24 -7
  82. package/src/sources/SourceManager.resi +2 -0
  83. package/src/tui/Tui.res +56 -143
  84. package/src/tui/Tui.res.mjs +70 -166
  85. package/src/tui/components/TuiData.res +61 -16
  86. package/src/tui/components/TuiData.res.mjs +53 -15
  87. package/svm.schema.json +0 -7
  88. package/src/db/CheckpointBounds.res +0 -53
  89. package/src/db/CheckpointBounds.res.mjs +0 -44
@@ -110,8 +110,8 @@ async function preloadBatchOrThrow(batch, loadManager, persistence, config, inde
110
110
  let itemIdx = 0;
111
111
  for (let checkpointIdx = 0, checkpointIdx_finish = batch.checkpointIds.length; checkpointIdx < checkpointIdx_finish; ++checkpointIdx) {
112
112
  let checkpointId = batch.checkpointIds[checkpointIdx];
113
- let checkpointEventsProcessed = batch.checkpointEventsProcessed[checkpointIdx];
114
- for (let idx = 0; idx < checkpointEventsProcessed; ++idx) {
113
+ let checkpointItemsCount = batch.checkpointItemsCount[checkpointIdx];
114
+ for (let idx = 0; idx < checkpointItemsCount; ++idx) {
115
115
  let item = batch.items[itemIdx + idx | 0];
116
116
  if (item.kind === 0) {
117
117
  let match = item.onEventRegistration;
@@ -158,7 +158,7 @@ async function preloadBatchOrThrow(batch, loadManager, persistence, config, inde
158
158
  }
159
159
  }
160
160
  }
161
- itemIdx = itemIdx + checkpointEventsProcessed | 0;
161
+ itemIdx = itemIdx + checkpointItemsCount | 0;
162
162
  }
163
163
  await Promise.all(promises);
164
164
  }
@@ -167,12 +167,12 @@ async function runBatchHandlersOrThrow(batch, indexerState, loadManager, persist
167
167
  let itemIdx = 0;
168
168
  for (let checkpointIdx = 0, checkpointIdx_finish = batch.checkpointIds.length; checkpointIdx < checkpointIdx_finish; ++checkpointIdx) {
169
169
  let checkpointId = batch.checkpointIds[checkpointIdx];
170
- let checkpointEventsProcessed = batch.checkpointEventsProcessed[checkpointIdx];
171
- for (let idx = 0; idx < checkpointEventsProcessed; ++idx) {
170
+ let checkpointItemsCount = batch.checkpointItemsCount[checkpointIdx];
171
+ for (let idx = 0; idx < checkpointItemsCount; ++idx) {
172
172
  let item = batch.items[itemIdx + idx | 0];
173
173
  await runHandlerOrThrow(item, checkpointId, indexerState, loadManager, persistence, config, chains);
174
174
  }
175
- itemIdx = itemIdx + checkpointEventsProcessed | 0;
175
+ itemIdx = itemIdx + checkpointItemsCount | 0;
176
176
  }
177
177
  }
178
178
 
@@ -979,6 +979,20 @@ let compareBufferItem = (a: Internal.item, b: Internal.item): int => {
979
979
  }
980
980
  }
981
981
 
982
+ // Whether two adjacent buffer items came from one log routed to two
983
+ // registrations: everything `compareBufferItem` orders on except the
984
+ // registration index is equal. Only meaningful on neighbours of a sorted
985
+ // buffer, where such items sit next to each other.
986
+ let isSameLog = (a: Internal.item, b: Internal.item): bool =>
987
+ a->Internal.getItemKind === 0 &&
988
+ b->Internal.getItemKind === 0 &&
989
+ a->Internal.getItemBlockNumber === b->Internal.getItemBlockNumber &&
990
+ a->Internal.getItemLogIndex === b->Internal.getItemLogIndex &&
991
+ switch (a->Internal.getItemOrderPath, b->Internal.getItemOrderPath) {
992
+ | (Value(pa), Value(pb)) => comparePath(pa, pb) === 0
993
+ | _ => true
994
+ }
995
+
982
996
  // Merge a maybe-unsorted `newItems` run into the already-sorted, already-deduped
983
997
  // `buffer`, dropping items equal on every component of `compareBufferItem`.
984
998
  // Single linear pass over both runs after ordering `newItems` in place; every
@@ -795,6 +795,19 @@ function compareBufferItem(a, b) {
795
795
  }
796
796
  }
797
797
 
798
+ function isSameLog(a, b) {
799
+ if (a.kind !== 0 || b.kind !== 0 || a.blockNumber !== b.blockNumber || a.logIndex !== b.logIndex) {
800
+ return false;
801
+ }
802
+ let match = a.orderPath;
803
+ let match$1 = b.orderPath;
804
+ if ((match == null) || (match$1 == null)) {
805
+ return true;
806
+ } else {
807
+ return comparePath(match, match$1) === 0;
808
+ }
809
+ }
810
+
798
811
  function mergeIntoBuffer(buffer, newItems) {
799
812
  let n = newItems.length;
800
813
  for (let i = 1; i < n; ++i) {
@@ -2113,6 +2126,7 @@ export {
2113
2126
  comparePath,
2114
2127
  compareTiebreak,
2115
2128
  compareBufferItem,
2129
+ isSameLog,
2116
2130
  mergeIntoBuffer,
2117
2131
  appendOnBlockItems,
2118
2132
  updateInternal,
@@ -1,10 +1,14 @@
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.
1
+ // The FinalizingIndexes phase. Reached from the processing loop when the chains
2
+ // this process drives have caught up: processing is already paused (the loop
3
+ // awaits this), pending writes are flushed, then storage builds every index the
4
+ // schema promises for those chains and, once they all verify, commits their
5
+ // `ready_at`. A failure part-way leaves the indexes built so far in place and
6
+ // reaches the processing loop's error boundary; the retry only owes what's left.
7
+ // Either way a chain never reports ready with an index the schema promised for
8
+ // it still missing.
9
+ //
10
+ // A chain's rows live in a partition of its own, so this touches nothing another
11
+ // `envio start --chain` process is indexing and never waits on one.
8
12
 
9
13
  let runOnce = async (state: IndexerState.t) => {
10
14
  Logging.info(
@@ -22,10 +26,7 @@ let runOnce = async (state: IndexerState.t) => {
22
26
 
23
27
  await storage.finalizeBackfill(
24
28
  ~entities=persistence.allEntities,
25
- ~chainIds=state
26
- ->IndexerState.chainStates
27
- ->Dict.valuesToArray
28
- ->Array.map(cs => (cs->ChainState.chainConfig).id),
29
+ ~chainIds=state->IndexerState.crossChainState->CrossChainState.chainIds,
29
30
  ~readyAt,
30
31
  )
31
32
 
@@ -57,5 +58,8 @@ let run = (state: IndexerState.t) =>
57
58
  let repairSchemaIndexes = (state: IndexerState.t) => {
58
59
  let persistence = state->IndexerState.persistence
59
60
  let storage = persistence->Persistence.getInitializedStorageOrThrow
60
- storage.ensureSchemaIndexes(~entities=persistence.allEntities)
61
+ storage.ensureSchemaIndexes(
62
+ ~entities=persistence.allEntities,
63
+ ~chainIds=state->IndexerState.crossChainState->CrossChainState.chainIds,
64
+ )
61
65
  }
@@ -2,9 +2,9 @@
2
2
 
3
3
  import * as Logging from "./Logging.res.mjs";
4
4
  import * as Writing from "./Writing.res.mjs";
5
- import * as ChainState from "./ChainState.res.mjs";
6
5
  import * as Persistence from "./Persistence.res.mjs";
7
6
  import * as IndexerState from "./IndexerState.res.mjs";
7
+ import * as CrossChainState from "./CrossChainState.res.mjs";
8
8
 
9
9
  async function runOnce(state) {
10
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.");
@@ -15,7 +15,7 @@ async function runOnce(state) {
15
15
  let persistence = IndexerState.persistence(state);
16
16
  let storage = Persistence.getInitializedStorageOrThrow(persistence);
17
17
  let readyAt = new Date();
18
- await storage.finalizeBackfill(persistence.allEntities, Object.values(IndexerState.chainStates(state)).map(cs => ChainState.chainConfig(cs).id), readyAt);
18
+ await storage.finalizeBackfill(persistence.allEntities, CrossChainState.chainIds(IndexerState.crossChainState(state)), readyAt);
19
19
  IndexerState.markReady(state, readyAt);
20
20
  return Logging.info("The indexer is ready. Switching to realtime indexing.");
21
21
  }
@@ -33,7 +33,7 @@ function run(state) {
33
33
  function repairSchemaIndexes(state) {
34
34
  let persistence = IndexerState.persistence(state);
35
35
  let storage = Persistence.getInitializedStorageOrThrow(persistence);
36
- return storage.ensureSchemaIndexes(persistence.allEntities);
36
+ return storage.ensureSchemaIndexes(persistence.allEntities, CrossChainState.chainIds(IndexerState.crossChainState(state)));
37
37
  }
38
38
 
39
39
  export {
@@ -0,0 +1,46 @@
1
+ // Whether each chain's rows get history on a write, keyed by chain id. History
2
+ // is only ever kept for what a rollback could reach: under one shared sequence
3
+ // any chain's rollback reaches every chain's rows, so the run decides together;
4
+ // with a counter per chain only the group's own chain can reach its rows.
5
+ type t = dict<bool>
6
+
7
+ %%private(
8
+ let anyChainSaves = (shouldSaveHistory: dict<bool>) =>
9
+ shouldSaveHistory->Dict.valuesToArray->Array.some(saves => saves)
10
+ )
11
+
12
+ let decide = (config: Config.t, ~shouldSaveHistory: dict<bool>): t =>
13
+ if config.shouldSaveFullHistory {
14
+ shouldSaveHistory->Utils.Dict.mapValues(_ => true)
15
+ } else {
16
+ switch config.checkpointSequence {
17
+ | SharedAcrossChains =>
18
+ let saves = shouldSaveHistory->anyChainSaves
19
+ shouldSaveHistory->Utils.Dict.mapValues(_ => saves)
20
+ | PerChain => shouldSaveHistory
21
+ }
22
+ }
23
+
24
+ // Every chain the run indexes was decided for, so an absent one is a bug —
25
+ // answering `false` for it would silently leave the chain with nothing to roll
26
+ // back to.
27
+ let forChain = (t: t, chainId: ChainId.t): bool =>
28
+ switch t->ChainId.Dict.dangerouslyGetNonOption(chainId) {
29
+ | Some(saves) => saves
30
+ | None =>
31
+ JsError.throwWithMessage(
32
+ `Internal error: no history decision for chain ${chainId->ChainId.toString}. The policy is decided for every chain the run indexes.`,
33
+ )
34
+ }
35
+
36
+ // A cross-chain group's rows are reachable by any chain's rollback.
37
+ let forScope = (t: t, ~scope: Internal.chainScope): bool =>
38
+ switch scope {
39
+ | Chain(chainId) => t->forChain(chainId)
40
+ | CrossChain => t->anyChainSaves
41
+ }
42
+
43
+ // Whether a run has stale history to prune at all: history it keeps but doesn't
44
+ // keep forever.
45
+ let mayPrune = (config: Config.t, ~shouldSaveHistory: dict<bool>) =>
46
+ !config.shouldSaveFullHistory && shouldSaveHistory->anyChainSaves
@@ -0,0 +1,50 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Utils from "./Utils.res.mjs";
4
+ import * as ChainId from "./ChainId.res.mjs";
5
+ import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
6
+
7
+ function decide(config, shouldSaveHistory) {
8
+ if (config.shouldSaveFullHistory) {
9
+ return Utils.Dict.mapValues(shouldSaveHistory, param => true);
10
+ }
11
+ let match = config.checkpointSequence;
12
+ if (match !== "SharedAcrossChains") {
13
+ return shouldSaveHistory;
14
+ }
15
+ let saves = Object.values(shouldSaveHistory).some(saves => saves);
16
+ return Utils.Dict.mapValues(shouldSaveHistory, param => saves);
17
+ }
18
+
19
+ function forChain(t, chainId) {
20
+ let saves = t[chainId];
21
+ if (saves !== undefined) {
22
+ return saves;
23
+ } else {
24
+ return Stdlib_JsError.throwWithMessage(`Internal error: no history decision for chain ` + ChainId.toString(chainId) + `. The policy is decided for every chain the run indexes.`);
25
+ }
26
+ }
27
+
28
+ function forScope(t, scope) {
29
+ if (scope === "crossChain") {
30
+ return Object.values(t).some(saves => saves);
31
+ } else {
32
+ return forChain(t, scope);
33
+ }
34
+ }
35
+
36
+ function mayPrune(config, shouldSaveHistory) {
37
+ if (config.shouldSaveFullHistory) {
38
+ return false;
39
+ } else {
40
+ return Object.values(shouldSaveHistory).some(saves => saves);
41
+ }
42
+ }
43
+
44
+ export {
45
+ decide,
46
+ forChain,
47
+ forScope,
48
+ mayPrune,
49
+ }
50
+ /* Utils Not a pure module */
@@ -49,10 +49,7 @@ let getEffectInMemTable = (
49
49
  ) => state->IndexerState.effectState->EffectState.getTable(~effect, ~scope)
50
50
 
51
51
  let hasEffectOutput = (inMemTable: EffectState.effectCacheInMemTable, key) =>
52
- switch inMemTable.dict->Utils.Dict.dangerouslyGetNonOption(key) {
53
- | Some(Set(_)) => true
54
- | Some(Delete(_)) | None => false
55
- }
52
+ inMemTable.dict->Utils.Dict.dangerouslyGetNonOption(key)->Option.isSome
56
53
 
57
54
  // Returns the raw output. The output is itself an option for effects with an
58
55
  // optional output, so it must never be wrapped in another option here: Some(None)
@@ -62,14 +59,15 @@ let getEffectOutputUnsafe = (
62
59
  key,
63
60
  ): Internal.effectOutput =>
64
61
  switch inMemTable.dict->Utils.Dict.dangerouslyGetNonOption(key) {
65
- | Some(Set({entity: output})) => output
66
- | Some(Delete(_)) | None => %raw(`undefined`)
62
+ | Some({output}) => output
63
+ | None => %raw(`undefined`)
67
64
  }
68
65
 
69
66
  // Records a handler output. Persisted on the next write only when shouldCache;
70
67
  // otherwise kept in memory (re-run on a later miss) but never written to the db.
71
68
  let setEffectOutput = (
72
69
  inMemTable: EffectState.effectCacheInMemTable,
70
+ ~chainId,
73
71
  ~checkpointId,
74
72
  ~cacheKey,
75
73
  ~output,
@@ -79,10 +77,7 @@ let setEffectOutput = (
79
77
  | Some(_) => ()
80
78
  | None => inMemTable.changesCount = inMemTable.changesCount +. 1.
81
79
  }
82
- inMemTable.dict->Dict.set(
83
- cacheKey,
84
- Set({entityId: cacheKey->EntityId.unsafeOfString, entity: output, checkpointId}),
85
- )
80
+ inMemTable.dict->Dict.set(cacheKey, {output, chainId, checkpointId})
86
81
  if shouldCache {
87
82
  inMemTable.idsToStore->Array.push(cacheKey)->ignore
88
83
  }
@@ -90,16 +85,17 @@ let setEffectOutput = (
90
85
 
91
86
  // Seeds an entry from a db read. Stamped with loadedFromDbCheckpointId so it's
92
87
  // always droppable (re-readable from the db) and never re-persisted.
93
- let initEffectOutputFromDb = (inMemTable: EffectState.effectCacheInMemTable, ~cacheKey, ~output) =>
88
+ let initEffectOutputFromDb = (
89
+ inMemTable: EffectState.effectCacheInMemTable,
90
+ ~chainId,
91
+ ~cacheKey,
92
+ ~output,
93
+ ) =>
94
94
  if inMemTable.dict->Utils.Dict.dangerouslyGetNonOption(cacheKey)->Option.isNone {
95
95
  inMemTable.changesCount = inMemTable.changesCount +. 1.
96
96
  inMemTable.dict->Dict.set(
97
97
  cacheKey,
98
- Set({
99
- entityId: cacheKey->EntityId.unsafeOfString,
100
- entity: output,
101
- checkpointId: Internal.loadedFromDbCheckpointId,
102
- }),
98
+ {output, chainId, checkpointId: Internal.loadedFromDbCheckpointId},
103
99
  )
104
100
  }
105
101
 
@@ -108,14 +104,13 @@ let initEffectOutputFromDb = (inMemTable: EffectState.effectCacheInMemTable, ~ca
108
104
  // seeded from a db read are spared. Mirrors entity dropCommittedChanges.
109
105
  let dropCommittedEffects = (
110
106
  inMemTable: EffectState.effectCacheInMemTable,
111
- ~committedCheckpointId,
107
+ ~committedFrontier: Frontier.t,
112
108
  ~keepLoadedFromDb,
113
109
  ) => {
114
110
  let keysToDelete = []
115
- inMemTable.dict->Utils.Dict.forEachWithKey((change, key) => {
116
- let checkpointId = change->Change.getCheckpointId
111
+ inMemTable.dict->Utils.Dict.forEachWithKey(({chainId, checkpointId}, key) => {
117
112
  if (
118
- !(checkpointId > committedCheckpointId) &&
113
+ !(checkpointId > committedFrontier->Frontier.get(chainId)) &&
119
114
  !(keepLoadedFromDb && checkpointId == Internal.loadedFromDbCheckpointId)
120
115
  ) {
121
116
  keysToDelete->Array.push(key)
@@ -128,18 +123,21 @@ let dropCommittedEffects = (
128
123
  let prepareRollbackDiff = async (
129
124
  state: IndexerState.t,
130
125
  ~floors: RollbackFloors.t,
131
- ~rollbackDiffCheckpointId,
132
126
  ~progressedChains,
133
127
  ~rolledBackAddresses,
134
128
  ) => {
135
- state->IndexerState.beginRollbackDiff(
136
- ~diffCheckpointId=rollbackDiffCheckpointId,
137
- ~floors,
138
- ~progressedChains,
139
- ~rolledBackAddresses,
140
- )
129
+ let diffFrontier =
130
+ state->IndexerState.beginRollbackDiff(~floors, ~progressedChains, ~rolledBackAddresses)
141
131
  let persistence = state->IndexerState.persistence
142
- let committedCheckpointId = state->IndexerState.committedCheckpointId
132
+ let sequence = (state->IndexerState.config).checkpointSequence
133
+ let diffCheckpointId = scope =>
134
+ switch sequence->CheckpointSequence.findForScope(diffFrontier, ~scope) {
135
+ | Some(checkpointId) => checkpointId
136
+ | None =>
137
+ JsError.throwWithMessage(
138
+ "Internal error: the rollback returned rows for a chain its diff never moved.",
139
+ )
140
+ }
143
141
 
144
142
  let deletedEntities = Dict.make()
145
143
  let setEntities = Dict.make()
@@ -160,10 +158,10 @@ let prepareRollbackDiff = async (
160
158
  state
161
159
  ->getInMemTable(~entityConfig, ~scope)
162
160
  ->InMemoryTable.Entity.set(
163
- ~committedCheckpointId,
161
+ ~committedCheckpointId=state->IndexerState.committedCheckpointIdFor(~scope),
164
162
  Delete({
165
163
  entityId,
166
- checkpointId: rollbackDiffCheckpointId,
164
+ checkpointId: diffCheckpointId(scope),
167
165
  }),
168
166
  )
169
167
  })
@@ -174,10 +172,10 @@ let prepareRollbackDiff = async (
174
172
  state
175
173
  ->getInMemTable(~entityConfig, ~scope)
176
174
  ->InMemoryTable.Entity.set(
177
- ~committedCheckpointId,
175
+ ~committedCheckpointId=state->IndexerState.committedCheckpointIdFor(~scope),
178
176
  Set({
179
177
  entityId: entity.id->EntityId.unsafeOfString,
180
- checkpointId: rollbackDiffCheckpointId,
178
+ checkpointId: diffCheckpointId(scope),
181
179
  entity,
182
180
  }),
183
181
  )
@@ -2,6 +2,7 @@
2
2
 
3
3
  import * as Table from "./db/Table.res.mjs";
4
4
  import * as Utils from "./Utils.res.mjs";
5
+ import * as Frontier from "./db/Frontier.res.mjs";
5
6
  import * as Internal from "./Internal.res.mjs";
6
7
  import * as ChainState from "./ChainState.res.mjs";
7
8
  import * as EffectState from "./EffectState.res.mjs";
@@ -11,6 +12,7 @@ import * as InMemoryTable from "./InMemoryTable.res.mjs";
11
12
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
12
13
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
13
14
  import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
15
+ import * as CheckpointSequence from "./db/CheckpointSequence.res.mjs";
14
16
 
15
17
  function getInMemTable(state, entityConfig, scope) {
16
18
  let tmp;
@@ -45,23 +47,19 @@ function getEffectInMemTable(state, effect, scope) {
45
47
  }
46
48
 
47
49
  function hasEffectOutput(inMemTable, key) {
48
- let match = inMemTable.dict[key];
49
- if (match !== undefined) {
50
- return match.type === "SET";
51
- } else {
52
- return false;
53
- }
50
+ return Stdlib_Option.isSome(inMemTable.dict[key]);
54
51
  }
55
52
 
56
53
  function getEffectOutputUnsafe(inMemTable, key) {
57
54
  let match = inMemTable.dict[key];
58
- if (match !== undefined && match.type === "SET") {
59
- return match.entity;
55
+ if (match !== undefined) {
56
+ return match.output;
57
+ } else {
58
+ return undefined;
60
59
  }
61
- return undefined;
62
60
  }
63
61
 
64
- function setEffectOutput(inMemTable, checkpointId, cacheKey, output, shouldCache) {
62
+ function setEffectOutput(inMemTable, chainId, checkpointId, cacheKey, output, shouldCache) {
65
63
  let match = inMemTable.dict[cacheKey];
66
64
  if (match !== undefined) {
67
65
 
@@ -69,9 +67,8 @@ function setEffectOutput(inMemTable, checkpointId, cacheKey, output, shouldCache
69
67
  inMemTable.changesCount = inMemTable.changesCount + 1;
70
68
  }
71
69
  inMemTable.dict[cacheKey] = {
72
- type: "SET",
73
- entityId: cacheKey,
74
- entity: output,
70
+ output: output,
71
+ chainId: chainId,
75
72
  checkpointId: checkpointId
76
73
  };
77
74
  if (shouldCache) {
@@ -80,24 +77,23 @@ function setEffectOutput(inMemTable, checkpointId, cacheKey, output, shouldCache
80
77
  }
81
78
  }
82
79
 
83
- function initEffectOutputFromDb(inMemTable, cacheKey, output) {
80
+ function initEffectOutputFromDb(inMemTable, chainId, cacheKey, output) {
84
81
  if (Stdlib_Option.isNone(inMemTable.dict[cacheKey])) {
85
82
  inMemTable.changesCount = inMemTable.changesCount + 1;
86
83
  inMemTable.dict[cacheKey] = {
87
- type: "SET",
88
- entityId: cacheKey,
89
- entity: output,
84
+ output: output,
85
+ chainId: chainId,
90
86
  checkpointId: Internal.loadedFromDbCheckpointId
91
87
  };
92
88
  return;
93
89
  }
94
90
  }
95
91
 
96
- function dropCommittedEffects(inMemTable, committedCheckpointId, keepLoadedFromDb) {
92
+ function dropCommittedEffects(inMemTable, committedFrontier, keepLoadedFromDb) {
97
93
  let keysToDelete = [];
98
- Utils.Dict.forEachWithKey(inMemTable.dict, (change, key) => {
99
- let checkpointId = change.checkpointId;
100
- if (checkpointId <= committedCheckpointId && !(keepLoadedFromDb && checkpointId === Internal.loadedFromDbCheckpointId)) {
94
+ Utils.Dict.forEachWithKey(inMemTable.dict, (param, key) => {
95
+ let checkpointId = param.checkpointId;
96
+ if (checkpointId <= Frontier.get(committedFrontier, param.chainId) && !(keepLoadedFromDb && checkpointId === Internal.loadedFromDbCheckpointId)) {
101
97
  keysToDelete.push(key);
102
98
  return;
103
99
  }
@@ -106,31 +102,40 @@ function dropCommittedEffects(inMemTable, committedCheckpointId, keepLoadedFromD
106
102
  inMemTable.changesCount = inMemTable.changesCount - keysToDelete.length;
107
103
  }
108
104
 
109
- async function prepareRollbackDiff(state, floors, rollbackDiffCheckpointId, progressedChains, rolledBackAddresses) {
110
- IndexerState.beginRollbackDiff(state, rollbackDiffCheckpointId, floors, progressedChains, rolledBackAddresses);
105
+ async function prepareRollbackDiff(state, floors, progressedChains, rolledBackAddresses) {
106
+ let diffFrontier = IndexerState.beginRollbackDiff(state, floors, progressedChains, rolledBackAddresses);
111
107
  let persistence = IndexerState.persistence(state);
112
- let committedCheckpointId = IndexerState.committedCheckpointId(state);
108
+ let sequence = IndexerState.config(state).checkpointSequence;
109
+ let diffCheckpointId = scope => {
110
+ let checkpointId = CheckpointSequence.findForScope(sequence, diffFrontier, scope);
111
+ if (checkpointId !== undefined) {
112
+ return checkpointId;
113
+ } else {
114
+ return Stdlib_JsError.throwWithMessage("Internal error: the rollback returned rows for a chain its diff never moved.");
115
+ }
116
+ };
113
117
  let deletedEntities = {};
114
118
  let setEntities = {};
115
119
  await Promise.all(persistence.allEntities.filter(entityConfig => entityConfig.storage.postgres).map(async entityConfig => {
116
120
  let match = await persistence.storage.getRollbackData(entityConfig, floors);
117
121
  match[0].forEach(param => {
122
+ let scope = param.scope;
118
123
  let entityId = param.entityId;
119
124
  Utils.Dict.push(deletedEntities, entityConfig.name, entityId);
120
- InMemoryTable.Entity.set(getInMemTable(state, entityConfig, param.scope), committedCheckpointId, {
125
+ InMemoryTable.Entity.set(getInMemTable(state, entityConfig, scope), IndexerState.committedCheckpointIdFor(state, scope), {
121
126
  type: "DELETE",
122
127
  entityId: entityId,
123
- checkpointId: rollbackDiffCheckpointId
128
+ checkpointId: diffCheckpointId(scope)
124
129
  });
125
130
  });
126
131
  match[1].forEach(entity => {
127
132
  let scope = takeRowScope(entity, entityConfig);
128
133
  Utils.Dict.push(setEntities, entityConfig.name, entity.id);
129
- InMemoryTable.Entity.set(getInMemTable(state, entityConfig, scope), committedCheckpointId, {
134
+ InMemoryTable.Entity.set(getInMemTable(state, entityConfig, scope), IndexerState.committedCheckpointIdFor(state, scope), {
130
135
  type: "SET",
131
136
  entityId: entity.id,
132
137
  entity: entity,
133
- checkpointId: rollbackDiffCheckpointId
138
+ checkpointId: diffCheckpointId(scope)
134
139
  });
135
140
  });
136
141
  }));