envio 3.1.0-rc.1 → 3.1.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.
- package/index.d.ts +12 -0
- package/package.json +6 -6
- package/src/Batch.res +7 -1
- package/src/Batch.res.mjs +2 -1
- package/src/ChainManager.res +3 -2
- package/src/ChainManager.res.mjs +3 -3
- package/src/Env.res +6 -0
- package/src/Env.res.mjs +3 -0
- package/src/EventProcessing.res +24 -122
- package/src/EventProcessing.res.mjs +24 -88
- package/src/GlobalState.res +31 -52
- package/src/GlobalState.res.mjs +54 -33
- package/src/GlobalStateManager.res +1 -3
- package/src/InMemoryStore.res +408 -110
- package/src/InMemoryStore.res.mjs +335 -84
- package/src/InMemoryTable.res +49 -30
- package/src/InMemoryTable.res.mjs +31 -39
- package/src/Internal.res +3 -0
- package/src/LoadLayer.res +9 -7
- package/src/LoadLayer.res.mjs +4 -10
- package/src/Main.res +31 -2
- package/src/Main.res.mjs +19 -5
- package/src/Persistence.res +6 -1
- package/src/PgStorage.res +171 -68
- package/src/PgStorage.res.mjs +125 -39
- package/src/RollbackCommit.res +32 -0
- package/src/RollbackCommit.res.mjs +35 -0
- package/src/TestIndexerProxyStorage.res +1 -1
- package/src/TestIndexerProxyStorage.res.mjs +1 -1
- package/src/Throttler.res +22 -15
- package/src/Throttler.res.mjs +19 -14
- package/src/UserContext.res +1 -0
- package/src/UserContext.res.mjs +3 -1
- package/src/bindings/NodeJs.res +1 -0
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
+
import * as Env from "./Env.res.mjs";
|
|
3
4
|
import * as Utils from "./Utils.res.mjs";
|
|
4
5
|
import * as Config from "./Config.res.mjs";
|
|
6
|
+
import * as Logging from "./Logging.res.mjs";
|
|
5
7
|
import * as Internal from "./Internal.res.mjs";
|
|
8
|
+
import * as Throttler from "./Throttler.res.mjs";
|
|
6
9
|
import * as Prometheus from "./Prometheus.res.mjs";
|
|
7
10
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
8
11
|
import * as ErrorHandling from "./ErrorHandling.res.mjs";
|
|
9
12
|
import * as InMemoryTable from "./InMemoryTable.res.mjs";
|
|
13
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
14
|
+
import * as RollbackCommit from "./RollbackCommit.res.mjs";
|
|
10
15
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
16
|
+
import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
|
|
17
|
+
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
11
18
|
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
12
19
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
13
20
|
|
|
@@ -39,15 +46,30 @@ let EntityTables = {
|
|
|
39
46
|
get: get
|
|
40
47
|
};
|
|
41
48
|
|
|
42
|
-
function make$1(entities, committedCheckpointIdOpt) {
|
|
49
|
+
function make$1(entities, committedCheckpointIdOpt, persistence, config, onError) {
|
|
43
50
|
let committedCheckpointId = committedCheckpointIdOpt !== undefined ? committedCheckpointIdOpt : Internal.initialCheckpointId;
|
|
51
|
+
let intervalMillis = Env.ThrottleWrites.chainMetadataIntervalMillis;
|
|
52
|
+
let chainMetaThrottler = Throttler.make(intervalMillis, Logging.createChild({
|
|
53
|
+
context: "Throttler for chain metadata writes",
|
|
54
|
+
intervalMillis: intervalMillis
|
|
55
|
+
}));
|
|
44
56
|
return {
|
|
45
57
|
allEntities: entities,
|
|
46
|
-
rawEvents: [],
|
|
47
58
|
entities: make(entities),
|
|
48
59
|
effects: {},
|
|
49
60
|
rollback: undefined,
|
|
50
|
-
committedCheckpointId: committedCheckpointId
|
|
61
|
+
committedCheckpointId: committedCheckpointId,
|
|
62
|
+
processedCheckpointId: committedCheckpointId,
|
|
63
|
+
processedBatches: [],
|
|
64
|
+
writeFiber: undefined,
|
|
65
|
+
hasFailedWrite: false,
|
|
66
|
+
onError: onError,
|
|
67
|
+
commitWaiters: [],
|
|
68
|
+
persistence: persistence,
|
|
69
|
+
config: config,
|
|
70
|
+
chainMeta: {},
|
|
71
|
+
chainMetaDirty: false,
|
|
72
|
+
chainMetaThrottler: chainMetaThrottler
|
|
51
73
|
};
|
|
52
74
|
}
|
|
53
75
|
|
|
@@ -61,12 +83,65 @@ function getEffectInMemTable(inMemoryStore, effect) {
|
|
|
61
83
|
idsToStore: [],
|
|
62
84
|
invalidationsCount: 0,
|
|
63
85
|
dict: {},
|
|
86
|
+
changesCount: 0,
|
|
64
87
|
effect: effect
|
|
65
88
|
};
|
|
66
89
|
inMemoryStore.effects[key] = table$1;
|
|
67
90
|
return table$1;
|
|
68
91
|
}
|
|
69
92
|
|
|
93
|
+
function getEffectOutput(inMemTable, key) {
|
|
94
|
+
let match = inMemTable.dict[key];
|
|
95
|
+
if (match !== undefined && match.type === "SET") {
|
|
96
|
+
return Primitive_option.some(match.entity);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function setEffectOutput(inMemTable, checkpointId, cacheKey, output, shouldCache) {
|
|
101
|
+
let match = inMemTable.dict[cacheKey];
|
|
102
|
+
if (match !== undefined) {
|
|
103
|
+
|
|
104
|
+
} else {
|
|
105
|
+
inMemTable.changesCount = inMemTable.changesCount + 1;
|
|
106
|
+
}
|
|
107
|
+
inMemTable.dict[cacheKey] = {
|
|
108
|
+
type: "SET",
|
|
109
|
+
entityId: cacheKey,
|
|
110
|
+
entity: output,
|
|
111
|
+
checkpointId: checkpointId
|
|
112
|
+
};
|
|
113
|
+
if (shouldCache) {
|
|
114
|
+
inMemTable.idsToStore.push(cacheKey);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function initEffectOutputFromDb(inMemTable, cacheKey, output) {
|
|
120
|
+
if (Stdlib_Option.isNone(inMemTable.dict[cacheKey])) {
|
|
121
|
+
inMemTable.changesCount = inMemTable.changesCount + 1;
|
|
122
|
+
inMemTable.dict[cacheKey] = {
|
|
123
|
+
type: "SET",
|
|
124
|
+
entityId: cacheKey,
|
|
125
|
+
entity: output,
|
|
126
|
+
checkpointId: Internal.loadedFromDbCheckpointId
|
|
127
|
+
};
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function dropCommittedEffects(inMemTable, committedCheckpointId, keepLoadedFromDb) {
|
|
133
|
+
let keysToDelete = [];
|
|
134
|
+
Utils.Dict.forEachWithKey(inMemTable.dict, (change, key) => {
|
|
135
|
+
let checkpointId = change.checkpointId;
|
|
136
|
+
if (checkpointId <= committedCheckpointId && !(keepLoadedFromDb && checkpointId === Internal.loadedFromDbCheckpointId)) {
|
|
137
|
+
keysToDelete.push(key);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
keysToDelete.forEach(key => Utils.Dict.deleteInPlace(inMemTable.dict, key));
|
|
142
|
+
inMemTable.changesCount = inMemTable.changesCount - keysToDelete.length;
|
|
143
|
+
}
|
|
144
|
+
|
|
70
145
|
function getInMemTable(inMemoryStore, entityConfig) {
|
|
71
146
|
return get(inMemoryStore.entities, entityConfig.name);
|
|
72
147
|
}
|
|
@@ -75,33 +150,145 @@ function isRollingBack(inMemoryStore) {
|
|
|
75
150
|
return inMemoryStore.rollback !== undefined;
|
|
76
151
|
}
|
|
77
152
|
|
|
78
|
-
|
|
79
|
-
let
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
153
|
+
function getChangesCount(inMemoryStore) {
|
|
154
|
+
let total = {
|
|
155
|
+
contents: 0
|
|
156
|
+
};
|
|
157
|
+
inMemoryStore.allEntities.forEach(entityConfig => {
|
|
158
|
+
total.contents = total.contents + getInMemTable(inMemoryStore, entityConfig).changesCount;
|
|
159
|
+
});
|
|
160
|
+
Utils.Dict.forEach(inMemoryStore.effects, inMemTable => {
|
|
161
|
+
total.contents = total.contents + inMemTable.changesCount;
|
|
162
|
+
});
|
|
163
|
+
inMemoryStore.processedBatches.forEach(batch => {
|
|
164
|
+
total.contents = total.contents + batch.totalBatchSize;
|
|
165
|
+
});
|
|
166
|
+
return total.contents;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function wakeCommitWaiters(inMemoryStore) {
|
|
170
|
+
let waiters = inMemoryStore.commitWaiters;
|
|
171
|
+
inMemoryStore.commitWaiters = [];
|
|
172
|
+
waiters.forEach(resolve => resolve());
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function waitForCommit(inMemoryStore) {
|
|
176
|
+
return new Promise((resolve, param) => {
|
|
177
|
+
inMemoryStore.commitWaiters.push(resolve);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function drainBatchRun(inMemoryStore) {
|
|
182
|
+
let all = inMemoryStore.processedBatches;
|
|
183
|
+
let isInReorgThreshold = all[0].isInReorgThreshold;
|
|
184
|
+
let rest = [];
|
|
185
|
+
let progressedChainsById = {};
|
|
186
|
+
let totalBatchSize = {
|
|
89
187
|
contents: 0
|
|
90
188
|
};
|
|
91
|
-
|
|
92
|
-
|
|
189
|
+
let items = [];
|
|
190
|
+
let checkpointIds = [];
|
|
191
|
+
let checkpointChainIds = [];
|
|
192
|
+
let checkpointBlockNumbers = [];
|
|
193
|
+
let checkpointBlockHashes = [];
|
|
194
|
+
let checkpointEventsProcessed = [];
|
|
195
|
+
all.forEach(batch => {
|
|
196
|
+
if (Utils.$$Array.isEmpty(rest) && batch.isInReorgThreshold === isInReorgThreshold) {
|
|
197
|
+
Utils.Dict.forEachWithKey(batch.progressedChainsById, (chainAfterBatch, key) => {
|
|
198
|
+
progressedChainsById[key] = chainAfterBatch;
|
|
199
|
+
});
|
|
200
|
+
totalBatchSize.contents = totalBatchSize.contents + batch.totalBatchSize | 0;
|
|
201
|
+
items.push(...batch.items);
|
|
202
|
+
checkpointIds.push(...batch.checkpointIds);
|
|
203
|
+
checkpointChainIds.push(...batch.checkpointChainIds);
|
|
204
|
+
checkpointBlockNumbers.push(...batch.checkpointBlockNumbers);
|
|
205
|
+
checkpointBlockHashes.push(...batch.checkpointBlockHashes);
|
|
206
|
+
checkpointEventsProcessed.push(...batch.checkpointEventsProcessed);
|
|
207
|
+
} else {
|
|
208
|
+
rest.push(batch);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
inMemoryStore.processedBatches = rest;
|
|
212
|
+
return {
|
|
213
|
+
totalBatchSize: totalBatchSize.contents,
|
|
214
|
+
items: items,
|
|
215
|
+
progressedChainsById: progressedChainsById,
|
|
216
|
+
isInReorgThreshold: isInReorgThreshold,
|
|
217
|
+
checkpointIds: checkpointIds,
|
|
218
|
+
checkpointChainIds: checkpointChainIds,
|
|
219
|
+
checkpointBlockNumbers: checkpointBlockNumbers,
|
|
220
|
+
checkpointBlockHashes: checkpointBlockHashes,
|
|
221
|
+
checkpointEventsProcessed: checkpointEventsProcessed
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function snapshotEffects(inMemoryStore, cache) {
|
|
226
|
+
let acc = [];
|
|
227
|
+
Utils.Dict.forEach(inMemoryStore.effects, inMemTable => {
|
|
228
|
+
let idsToStore = inMemTable.idsToStore;
|
|
229
|
+
let invalidationsCount = inMemTable.invalidationsCount;
|
|
230
|
+
let dict = inMemTable.dict;
|
|
231
|
+
let effect = inMemTable.effect;
|
|
232
|
+
if (idsToStore.length !== 0) {
|
|
233
|
+
let items = Stdlib_Array.filterMap(idsToStore, id => {
|
|
234
|
+
let match = dict[id];
|
|
235
|
+
if (match.type === "SET") {
|
|
236
|
+
return {
|
|
237
|
+
id: id,
|
|
238
|
+
output: match.entity
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
let effectName = effect.name;
|
|
243
|
+
let c = cache[effectName];
|
|
244
|
+
let effectCacheRecord;
|
|
245
|
+
if (c !== undefined) {
|
|
246
|
+
effectCacheRecord = c;
|
|
247
|
+
} else {
|
|
248
|
+
let c$1 = {
|
|
249
|
+
effectName: effectName,
|
|
250
|
+
count: 0
|
|
251
|
+
};
|
|
252
|
+
cache[effectName] = c$1;
|
|
253
|
+
effectCacheRecord = c$1;
|
|
254
|
+
}
|
|
255
|
+
let shouldInitialize = effectCacheRecord.count === 0;
|
|
256
|
+
effectCacheRecord.count = (effectCacheRecord.count + items.length | 0) - invalidationsCount | 0;
|
|
257
|
+
Prometheus.EffectCacheCount.set(effectCacheRecord.count, effectName);
|
|
258
|
+
acc.push({
|
|
259
|
+
effect: effect,
|
|
260
|
+
items: items,
|
|
261
|
+
shouldInitialize: shouldInitialize
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
inMemTable.idsToStore = [];
|
|
265
|
+
inMemTable.invalidationsCount = 0;
|
|
93
266
|
});
|
|
94
|
-
|
|
267
|
+
return acc;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async function runOneWrite(inMemoryStore, persistence, config) {
|
|
271
|
+
let match = persistence.storageStatus;
|
|
272
|
+
let cache;
|
|
273
|
+
cache = typeof match !== "object" || match.TAG === "Initializing" ? Stdlib_JsError.throwWithMessage(`Failed to access the indexer storage. The Persistence layer is not initialized.`) : match._0.cache;
|
|
274
|
+
let chainMetaData = inMemoryStore.chainMetaDirty ? (inMemoryStore.chainMetaDirty = false, Utils.Dict.shallowCopy(inMemoryStore.chainMeta)) : undefined;
|
|
275
|
+
let match$1 = inMemoryStore.processedBatches;
|
|
276
|
+
if (match$1.length === 0) {
|
|
277
|
+
if (chainMetaData !== undefined) {
|
|
278
|
+
return await persistence.storage.setChainMeta(chainMetaData);
|
|
279
|
+
} else {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
let committedCheckpointId = inMemoryStore.committedCheckpointId;
|
|
284
|
+
let batch = drainBatchRun(inMemoryStore);
|
|
285
|
+
let checkpointId = Utils.$$Array.last(batch.checkpointIds);
|
|
286
|
+
let upToCheckpointId = checkpointId !== undefined ? checkpointId : committedCheckpointId;
|
|
287
|
+
let rollback = inMemoryStore.rollback;
|
|
288
|
+
inMemoryStore.rollback = undefined;
|
|
95
289
|
let updatedEntities = Stdlib_Array.filterMap(persistence.allEntities, entityConfig => {
|
|
96
290
|
let table = getInMemTable(inMemoryStore, entityConfig);
|
|
97
|
-
|
|
98
|
-
let changes = table.prevEntityChanges;
|
|
99
|
-
Utils.Dict.forEach(table.latestEntityChangeById, change => {
|
|
100
|
-
if (change.checkpointId > committedCheckpointId) {
|
|
101
|
-
changes.push(change);
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
});
|
|
291
|
+
let changes = InMemoryTable.Entity.snapshotChanges(table, committedCheckpointId, upToCheckpointId);
|
|
105
292
|
if (Utils.$$Array.isEmpty(changes)) {
|
|
106
293
|
return;
|
|
107
294
|
} else {
|
|
@@ -111,74 +298,120 @@ async function writeBatch(inMemoryStore, persistence, batch, config, isInReorgTh
|
|
|
111
298
|
};
|
|
112
299
|
}
|
|
113
300
|
});
|
|
114
|
-
let
|
|
115
|
-
await persistence.storage.writeBatch(batch,
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
301
|
+
let updatedEffectsCache = snapshotEffects(inMemoryStore, cache);
|
|
302
|
+
await persistence.storage.writeBatch(batch, rollback, batch.isInReorgThreshold, config, persistence.allEntities, updatedEffectsCache, updatedEntities, chainMetaData);
|
|
303
|
+
inMemoryStore.committedCheckpointId = upToCheckpointId;
|
|
304
|
+
if (rollback !== undefined && Utils.$$Array.notEmpty(RollbackCommit.callbacks)) {
|
|
305
|
+
return await RollbackCommit.fire(rollback.progressBlockNumberByChainId);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function hasPendingWrite(inMemoryStore) {
|
|
310
|
+
if (Utils.$$Array.notEmpty(inMemoryStore.processedBatches)) {
|
|
311
|
+
return true;
|
|
312
|
+
} else {
|
|
313
|
+
return inMemoryStore.chainMetaDirty;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
async function runWriteLoop(inMemoryStore) {
|
|
318
|
+
while (hasPendingWrite(inMemoryStore) && !inMemoryStore.hasFailedWrite) {
|
|
319
|
+
try {
|
|
320
|
+
await runOneWrite(inMemoryStore, inMemoryStore.persistence, inMemoryStore.config);
|
|
321
|
+
wakeCommitWaiters(inMemoryStore);
|
|
322
|
+
} catch (raw_exn) {
|
|
323
|
+
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
324
|
+
inMemoryStore.hasFailedWrite = true;
|
|
325
|
+
inMemoryStore.onError(exn);
|
|
122
326
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
327
|
+
};
|
|
328
|
+
inMemoryStore.writeFiber = undefined;
|
|
329
|
+
return wakeCommitWaiters(inMemoryStore);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function kick(inMemoryStore) {
|
|
333
|
+
if (Stdlib_Option.isNone(inMemoryStore.writeFiber) && !inMemoryStore.hasFailedWrite && hasPendingWrite(inMemoryStore)) {
|
|
334
|
+
inMemoryStore.writeFiber = runWriteLoop(inMemoryStore);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function metaFieldsEqual(a, b) {
|
|
340
|
+
if (Primitive_object.equal(a.first_event_block, b.first_event_block) && a.buffer_block === b.buffer_block && a._is_hyper_sync === b._is_hyper_sync) {
|
|
341
|
+
return Primitive_object.equal(Stdlib_Option.map(Primitive_option.fromNull(a.ready_at), prim => prim.getTime()), Stdlib_Option.map(Primitive_option.fromNull(b.ready_at), prim => prim.getTime()));
|
|
342
|
+
} else {
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function setChainMeta(inMemoryStore, chainsData) {
|
|
348
|
+
Utils.Dict.forEachWithKey(chainsData, (meta, chainId) => {
|
|
349
|
+
let prev = inMemoryStore.chainMeta[chainId];
|
|
350
|
+
let changed = prev !== undefined ? !metaFieldsEqual(meta, prev) : true;
|
|
351
|
+
if (changed) {
|
|
352
|
+
inMemoryStore.chainMeta[chainId] = meta;
|
|
353
|
+
inMemoryStore.chainMetaDirty = true;
|
|
354
|
+
return;
|
|
139
355
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
items: items,
|
|
146
|
-
shouldInitialize: shouldInitialize
|
|
356
|
+
});
|
|
357
|
+
if (inMemoryStore.chainMetaDirty) {
|
|
358
|
+
return Throttler.schedule(inMemoryStore.chainMetaThrottler, () => {
|
|
359
|
+
kick(inMemoryStore);
|
|
360
|
+
return Promise.resolve();
|
|
147
361
|
});
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function commitBatch(inMemoryStore, batch) {
|
|
366
|
+
inMemoryStore.processedBatches.push(batch);
|
|
152
367
|
let checkpointId = Utils.$$Array.last(batch.checkpointIds);
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
368
|
+
if (checkpointId !== undefined) {
|
|
369
|
+
inMemoryStore.processedCheckpointId = checkpointId;
|
|
370
|
+
}
|
|
371
|
+
kick(inMemoryStore);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function dropCommitted(inMemoryStore, keepLoadedFromDb) {
|
|
375
|
+
let committedCheckpointId = inMemoryStore.committedCheckpointId;
|
|
376
|
+
inMemoryStore.allEntities.forEach(entityConfig => InMemoryTable.Entity.dropCommittedChanges(getInMemTable(inMemoryStore, entityConfig), committedCheckpointId, keepLoadedFromDb));
|
|
377
|
+
Utils.Dict.forEach(inMemoryStore.effects, inMemTable => dropCommittedEffects(inMemTable, committedCheckpointId, keepLoadedFromDb));
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
async function awaitCapacity(inMemoryStore) {
|
|
381
|
+
if (!inMemoryStore.hasFailedWrite && getChangesCount(inMemoryStore) >= Env.inMemoryObjectsTarget) {
|
|
382
|
+
dropCommitted(inMemoryStore, true);
|
|
383
|
+
if (getChangesCount(inMemoryStore) >= Env.inMemoryObjectsTarget) {
|
|
384
|
+
dropCommitted(inMemoryStore, false);
|
|
385
|
+
}
|
|
386
|
+
if (getChangesCount(inMemoryStore) >= Env.inMemoryObjectsTarget && Utils.$$Array.notEmpty(inMemoryStore.processedBatches)) {
|
|
387
|
+
kick(inMemoryStore);
|
|
388
|
+
await waitForCommit(inMemoryStore);
|
|
389
|
+
return await awaitCapacity(inMemoryStore);
|
|
390
|
+
} else {
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
async function flush(inMemoryStore) {
|
|
397
|
+
if (inMemoryStore.hasFailedWrite) {
|
|
159
398
|
return;
|
|
160
399
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
return resetTable;
|
|
168
|
-
});
|
|
169
|
-
let dropEverything = loadedFromDbCount.contents >= 50000;
|
|
170
|
-
persistence.allEntities.forEach((entityConfig, idx) => {
|
|
171
|
-
inMemoryStore.entities[entityConfig.name] = dropEverything ? InMemoryTable.Entity.make() : resetTables[idx];
|
|
172
|
-
});
|
|
400
|
+
kick(inMemoryStore);
|
|
401
|
+
let fiber = inMemoryStore.writeFiber;
|
|
402
|
+
if (fiber !== undefined) {
|
|
403
|
+
await fiber;
|
|
404
|
+
return await flush(inMemoryStore);
|
|
405
|
+
}
|
|
173
406
|
}
|
|
174
407
|
|
|
175
|
-
async function prepareRollbackDiff(inMemoryStore, persistence, rollbackTargetCheckpointId, rollbackDiffCheckpointId) {
|
|
176
|
-
inMemoryStore.rawEvents = [];
|
|
408
|
+
async function prepareRollbackDiff(inMemoryStore, persistence, rollbackTargetCheckpointId, rollbackDiffCheckpointId, progressBlockNumberByChainId) {
|
|
177
409
|
inMemoryStore.entities = make(inMemoryStore.allEntities);
|
|
178
410
|
inMemoryStore.effects = {};
|
|
179
411
|
inMemoryStore.rollback = {
|
|
180
412
|
targetCheckpointId: rollbackTargetCheckpointId,
|
|
181
|
-
diffCheckpointId: rollbackDiffCheckpointId
|
|
413
|
+
diffCheckpointId: rollbackDiffCheckpointId,
|
|
414
|
+
progressBlockNumberByChainId: progressBlockNumberByChainId
|
|
182
415
|
};
|
|
183
416
|
let deletedEntities = {};
|
|
184
417
|
let setEntities = {};
|
|
@@ -247,17 +480,35 @@ function setBatchDcs(inMemoryStore, batch) {
|
|
|
247
480
|
}
|
|
248
481
|
}
|
|
249
482
|
|
|
250
|
-
let keepLatestChangesLimit =
|
|
483
|
+
let keepLatestChangesLimit = Env.inMemoryObjectsTarget;
|
|
251
484
|
|
|
252
485
|
export {
|
|
253
486
|
EntityTables,
|
|
254
487
|
make$1 as make,
|
|
255
488
|
keepLatestChangesLimit,
|
|
256
489
|
getEffectInMemTable,
|
|
490
|
+
getEffectOutput,
|
|
491
|
+
setEffectOutput,
|
|
492
|
+
initEffectOutputFromDb,
|
|
493
|
+
dropCommittedEffects,
|
|
257
494
|
getInMemTable,
|
|
258
495
|
isRollingBack,
|
|
259
|
-
|
|
496
|
+
getChangesCount,
|
|
497
|
+
wakeCommitWaiters,
|
|
498
|
+
waitForCommit,
|
|
499
|
+
drainBatchRun,
|
|
500
|
+
snapshotEffects,
|
|
501
|
+
runOneWrite,
|
|
502
|
+
hasPendingWrite,
|
|
503
|
+
runWriteLoop,
|
|
504
|
+
kick,
|
|
505
|
+
metaFieldsEqual,
|
|
506
|
+
setChainMeta,
|
|
507
|
+
commitBatch,
|
|
508
|
+
dropCommitted,
|
|
509
|
+
awaitCapacity,
|
|
510
|
+
flush,
|
|
260
511
|
prepareRollbackDiff,
|
|
261
512
|
setBatchDcs,
|
|
262
513
|
}
|
|
263
|
-
/*
|
|
514
|
+
/* Env Not a pure module */
|
package/src/InMemoryTable.res
CHANGED
|
@@ -9,13 +9,14 @@ module Entity = {
|
|
|
9
9
|
type entityIndices = Utils.Set.t<TableIndices.Index.t>
|
|
10
10
|
type t = {
|
|
11
11
|
latestEntityChangeById: dict<Change.t<Internal.entity>>,
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
// store size without scanning every dict.
|
|
12
|
+
// Recorded changes (new latest ids + prevEntityChanges pushes), tracked
|
|
13
|
+
// manually so InMemoryStore can gauge size without scanning every dict.
|
|
15
14
|
mutable changesCount: float,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
// Swapped out when a write starts so processing keeps appending while the
|
|
16
|
+
// previous changes persist in the background.
|
|
17
|
+
mutable prevEntityChanges: array<Change.t<Internal.entity>>,
|
|
18
|
+
mutable indicesByEntityId: dict<entityIndices>,
|
|
19
|
+
mutable fieldNameIndices: indexFieldNameToIndices,
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
// Helper to extract entity ID from any entity
|
|
@@ -55,33 +56,51 @@ module Entity = {
|
|
|
55
56
|
fieldNameIndices: Dict.make(),
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
let
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
59
|
+
// Changes to persist for checkpoints in (committedCheckpointId, upToCheckpointId].
|
|
60
|
+
// Those above upToCheckpointId stay in the table for a later write, while
|
|
61
|
+
// concurrent processing keeps accumulating.
|
|
62
|
+
let snapshotChanges = (self: t, ~committedCheckpointId, ~upToCheckpointId): array<
|
|
63
|
+
Change.t<Internal.entity>,
|
|
64
|
+
> => {
|
|
65
|
+
let changes = []
|
|
66
|
+
let keptPrev = []
|
|
67
|
+
self.prevEntityChanges->Array.forEach(change =>
|
|
68
|
+
if change->Change.getCheckpointId > upToCheckpointId {
|
|
69
|
+
keptPrev->Array.push(change)
|
|
70
|
+
} else {
|
|
71
|
+
changes->Array.push(change)
|
|
72
|
+
}
|
|
73
|
+
)
|
|
74
|
+
self.prevEntityChanges = keptPrev
|
|
75
|
+
self.changesCount = self.changesCount -. changes->Array.length->Int.toFloat
|
|
76
|
+
self.latestEntityChangeById->Utils.Dict.forEach(change => {
|
|
77
|
+
let checkpointId = change->Change.getCheckpointId
|
|
78
|
+
if checkpointId > committedCheckpointId && !(checkpointId > upToCheckpointId) {
|
|
79
|
+
changes->Array.push(change)
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
changes
|
|
66
83
|
}
|
|
67
84
|
|
|
68
|
-
//
|
|
69
|
-
// (
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
let
|
|
74
|
-
self.latestEntityChangeById->Utils.Dict.forEachWithKey((change, key) =>
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
85
|
+
// Frees committed changes: drops latest entries at or below committedCheckpointId
|
|
86
|
+
// (re-readable from the db) and clears the per-batch indices (rebuilt on the next
|
|
87
|
+
// getWhere). Uncommitted changes are kept. With keepLoadedFromDb, entries seeded
|
|
88
|
+
// from a db read are spared so the cheaper-to-re-derive writes are dropped first.
|
|
89
|
+
let dropCommittedChanges = (self: t, ~committedCheckpointId, ~keepLoadedFromDb) => {
|
|
90
|
+
let keysToDelete = []
|
|
91
|
+
self.latestEntityChangeById->Utils.Dict.forEachWithKey((change, key) => {
|
|
92
|
+
let checkpointId = change->Change.getCheckpointId
|
|
93
|
+
if (
|
|
94
|
+
!(checkpointId > committedCheckpointId) &&
|
|
95
|
+
!(keepLoadedFromDb && checkpointId == Internal.loadedFromDbCheckpointId)
|
|
96
|
+
) {
|
|
97
|
+
keysToDelete->Array.push(key)
|
|
78
98
|
}
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
99
|
+
})
|
|
100
|
+
keysToDelete->Array.forEach(key => self.latestEntityChangeById->Utils.Dict.deleteInPlace(key))
|
|
101
|
+
self.changesCount = self.changesCount -. keysToDelete->Array.length->Int.toFloat
|
|
102
|
+
self.indicesByEntityId = Dict.make()
|
|
103
|
+
self.fieldNameIndices = Dict.make()
|
|
85
104
|
}
|
|
86
105
|
|
|
87
106
|
let updateIndices = (self: t, ~entity: Internal.entity) => {
|