envio 3.5.0-alpha.2 → 3.5.0-alpha.3
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/package.json +6 -6
- package/src/BatchProcessing.res +14 -0
- package/src/BatchProcessing.res.mjs +8 -0
- package/src/ChainState.res +10 -4
- package/src/ChainState.res.mjs +7 -6
- package/src/ChainState.resi +1 -1
- package/src/Config.res +2 -2
- package/src/Config.res.mjs +2 -2
- package/src/CrossChainState.res +33 -10
- package/src/CrossChainState.res.mjs +21 -6
- package/src/CrossChainState.resi +3 -0
- package/src/FetchState.res +230 -66
- package/src/FetchState.res.mjs +213 -67
- package/src/FinalizeBackfill.res +34 -0
- package/src/FinalizeBackfill.res.mjs +26 -0
- package/src/InMemoryTable.res +15 -15
- package/src/InMemoryTable.res.mjs +14 -14
- package/src/IndexerState.res +12 -0
- package/src/IndexerState.res.mjs +19 -0
- package/src/IndexerState.resi +3 -0
- package/src/LoadLayer.res +9 -1
- package/src/LoadLayer.res.mjs +1 -0
- package/src/Persistence.res +11 -0
- package/src/PgStorage.res +356 -35
- package/src/PgStorage.res.mjs +237 -24
- package/src/TestIndexer.res +3 -0
- package/src/TestIndexer.res.mjs +2 -0
- package/src/db/EntityFilter.res +0 -4
- package/src/db/EntityFilter.res.mjs +1 -8
- package/src/db/IndexRegistry.res +219 -0
- package/src/db/IndexRegistry.res.mjs +195 -0
- package/src/db/InternalTable.res +7 -0
- package/src/db/InternalTable.res.mjs +7 -0
- package/src/db/Table.res +13 -13
- package/src/db/Table.res.mjs +12 -12
package/src/FetchState.res.mjs
CHANGED
|
@@ -200,16 +200,92 @@ function ascSortFn(a, b) {
|
|
|
200
200
|
return Primitive_int.compare(a.latestFetchedBlock.blockNumber, b.latestFetchedBlock.blockNumber);
|
|
201
201
|
}
|
|
202
202
|
|
|
203
|
+
function anchoredContracts(partitions) {
|
|
204
|
+
let set = new Set();
|
|
205
|
+
partitions.forEach(p => {
|
|
206
|
+
let match = p.mergeBlock;
|
|
207
|
+
let match$1 = p.selection.clientFilteredContracts;
|
|
208
|
+
if (match !== undefined || match$1 === undefined) {
|
|
209
|
+
return;
|
|
210
|
+
} else {
|
|
211
|
+
match$1.forEach(name => {
|
|
212
|
+
set.add(name);
|
|
213
|
+
});
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
return set;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function getAnchorSafeBlock(p) {
|
|
221
|
+
let safeRef = {
|
|
222
|
+
contents: p.latestFetchedBlock.blockNumber
|
|
223
|
+
};
|
|
224
|
+
p.mutPendingQueries.forEach(pq => {
|
|
225
|
+
let match = safeRef.contents;
|
|
226
|
+
if (match === undefined) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
let toBlock = pq.toBlock;
|
|
230
|
+
let match$1 = pq.fetchedBlock;
|
|
231
|
+
if (match$1 === undefined) {
|
|
232
|
+
if (toBlock !== undefined) {
|
|
233
|
+
if (toBlock > match) {
|
|
234
|
+
safeRef.contents = toBlock;
|
|
235
|
+
return;
|
|
236
|
+
} else {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
} else {
|
|
240
|
+
safeRef.contents = undefined;
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
let blockNumber = match$1.blockNumber;
|
|
245
|
+
if (blockNumber > match) {
|
|
246
|
+
safeRef.contents = blockNumber;
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
return safeRef.contents;
|
|
251
|
+
}
|
|
252
|
+
|
|
203
253
|
function make(partitions, maxAddrInPartition, nextPartitionIndex, dynamicContracts, clientFilteredContracts) {
|
|
204
254
|
let newPartitions = [];
|
|
205
255
|
let mergingPartitions = {};
|
|
206
256
|
let nextPartitionIndexRef = {
|
|
207
257
|
contents: nextPartitionIndex
|
|
208
258
|
};
|
|
259
|
+
let anchorSafeBlocks = {};
|
|
260
|
+
let anchoredContractsSet = new Set();
|
|
261
|
+
if (clientFilteredContracts.size > 0) {
|
|
262
|
+
partitions.forEach(p => {
|
|
263
|
+
let match = p.mergeBlock;
|
|
264
|
+
let match$1 = p.selection.clientFilteredContracts;
|
|
265
|
+
if (match !== undefined) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
if (match$1 === undefined) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
let safeBlock = getAnchorSafeBlock(p);
|
|
272
|
+
match$1.forEach(name => {
|
|
273
|
+
anchoredContractsSet.add(name);
|
|
274
|
+
if (safeBlock !== undefined) {
|
|
275
|
+
anchorSafeBlocks[name] = safeBlock;
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
}
|
|
209
281
|
for (let idx = 0, idx_finish = partitions.length; idx < idx_finish; ++idx) {
|
|
210
282
|
let p = partitions[idx];
|
|
211
|
-
let
|
|
212
|
-
if (
|
|
283
|
+
let mergeBlock = p.mergeBlock;
|
|
284
|
+
if (mergeBlock !== undefined) {
|
|
285
|
+
if (p.latestFetchedBlock.blockNumber < mergeBlock) {
|
|
286
|
+
newPartitions.push(p);
|
|
287
|
+
}
|
|
288
|
+
} else if (p.dynamicContract !== undefined && p.selection.dependsOnAddresses) {
|
|
213
289
|
let contractName = p.dynamicContract;
|
|
214
290
|
let pAddressesCount = p.addresses.countFor(contractName);
|
|
215
291
|
let match = Utils.$$Array.last(p.mutPendingQueries);
|
|
@@ -236,9 +312,6 @@ function make(partitions, maxAddrInPartition, nextPartitionIndex, dynamicContrac
|
|
|
236
312
|
newPartitions.push(p);
|
|
237
313
|
}
|
|
238
314
|
} else {
|
|
239
|
-
exit = 1;
|
|
240
|
-
}
|
|
241
|
-
if (exit === 1) {
|
|
242
315
|
newPartitions.push(p);
|
|
243
316
|
}
|
|
244
317
|
}
|
|
@@ -276,12 +349,57 @@ function make(partitions, maxAddrInPartition, nextPartitionIndex, dynamicContrac
|
|
|
276
349
|
};
|
|
277
350
|
newPartitions.push(currentPRef);
|
|
278
351
|
}
|
|
279
|
-
|
|
280
|
-
|
|
352
|
+
let finalPartitions;
|
|
353
|
+
if (anchoredContractsSet.size === 0) {
|
|
354
|
+
finalPartitions = newPartitions;
|
|
355
|
+
} else {
|
|
356
|
+
let anchored = [];
|
|
357
|
+
newPartitions.forEach(p => {
|
|
358
|
+
if (p.mergeBlock !== undefined) {
|
|
359
|
+
anchored.push(p);
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
let contractName = p.dynamicContract;
|
|
363
|
+
if (contractName !== undefined) {
|
|
364
|
+
if (anchoredContractsSet.has(contractName)) {
|
|
365
|
+
let anchorSafeBlock = anchorSafeBlocks[contractName];
|
|
366
|
+
if (anchorSafeBlock !== undefined) {
|
|
367
|
+
if (p.latestFetchedBlock.blockNumber < anchorSafeBlock) {
|
|
368
|
+
anchored.push({
|
|
369
|
+
id: p.id,
|
|
370
|
+
latestFetchedBlock: p.latestFetchedBlock,
|
|
371
|
+
selection: p.selection,
|
|
372
|
+
addresses: p.addresses,
|
|
373
|
+
mergeBlock: anchorSafeBlock,
|
|
374
|
+
dynamicContract: p.dynamicContract,
|
|
375
|
+
mutPendingQueries: p.mutPendingQueries,
|
|
376
|
+
sourceRangeCapacity: p.sourceRangeCapacity,
|
|
377
|
+
prevSourceRangeCapacity: p.prevSourceRangeCapacity,
|
|
378
|
+
eventDensity: p.eventDensity,
|
|
379
|
+
latestSourceRangeCapacityUpdateBlock: p.latestSourceRangeCapacityUpdateBlock
|
|
380
|
+
});
|
|
381
|
+
return;
|
|
382
|
+
} else {
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
} else {
|
|
386
|
+
anchored.push(p);
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
anchored.push(p);
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
anchored.push(p);
|
|
394
|
+
});
|
|
395
|
+
finalPartitions = anchored;
|
|
396
|
+
}
|
|
397
|
+
finalPartitions.sort(ascSortFn);
|
|
398
|
+
let partitionsCount = finalPartitions.length;
|
|
281
399
|
let idsInAscOrder = Array(partitionsCount);
|
|
282
400
|
let entities = {};
|
|
283
401
|
for (let idx$2 = 0; idx$2 < partitionsCount; ++idx$2) {
|
|
284
|
-
let p$1 =
|
|
402
|
+
let p$1 = finalPartitions[idx$2];
|
|
285
403
|
idsInAscOrder[idx$2] = p$1.id;
|
|
286
404
|
entities[p$1.id] = p$1;
|
|
287
405
|
}
|
|
@@ -475,6 +593,8 @@ let OptimizedPartitions = {
|
|
|
475
593
|
mergePartitionsAtBlock: mergePartitionsAtBlock,
|
|
476
594
|
tooFarBlockRange: 20000,
|
|
477
595
|
ascSortFn: ascSortFn,
|
|
596
|
+
anchoredContracts: anchoredContracts,
|
|
597
|
+
getAnchorSafeBlock: getAnchorSafeBlock,
|
|
478
598
|
make: make,
|
|
479
599
|
consumeFetchedQueries: consumeFetchedQueries,
|
|
480
600
|
getPendingQueryOrThrow: getPendingQueryOrThrow,
|
|
@@ -683,14 +803,17 @@ function updateInternal(fetchState, optimizedPartitionsOpt, mutItems, mutItemsSo
|
|
|
683
803
|
};
|
|
684
804
|
}
|
|
685
805
|
|
|
686
|
-
function addClientFilteredContract(clientFilteredContracts, contractName, chainId, addressCount, threshold) {
|
|
806
|
+
function addClientFilteredContract(clientFilteredContracts, contractName, chainId, addressCount, threshold, shouldLogOpt) {
|
|
807
|
+
let shouldLog = shouldLogOpt !== undefined ? shouldLogOpt : true;
|
|
687
808
|
clientFilteredContracts.add(contractName);
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
809
|
+
if (shouldLog) {
|
|
810
|
+
return Logging.childTrace(Logging.createChild({
|
|
811
|
+
chainId: chainId,
|
|
812
|
+
contractName: contractName,
|
|
813
|
+
addressCount: addressCount,
|
|
814
|
+
threshold: threshold
|
|
815
|
+
}), "Switching contract to client-side address filtering: registered address count crossed the server-side threshold.");
|
|
816
|
+
}
|
|
694
817
|
}
|
|
695
818
|
|
|
696
819
|
function claimedFetchedBlock(p, knownHeight) {
|
|
@@ -700,7 +823,8 @@ function claimedFetchedBlock(p, knownHeight) {
|
|
|
700
823
|
});
|
|
701
824
|
}
|
|
702
825
|
|
|
703
|
-
function collapseClientFilteredContracts(partitions, clientFilteredContracts, normalSelection, nextPartitionIndexRef, addressStore, knownHeight) {
|
|
826
|
+
function collapseClientFilteredContracts(partitions, clientFilteredContracts, normalSelection, nextPartitionIndexRef, addressStore, clientFilteredFrontiersOpt, knownHeight) {
|
|
827
|
+
let clientFilteredFrontiers = clientFilteredFrontiersOpt !== undefined ? clientFilteredFrontiersOpt : [];
|
|
704
828
|
if (clientFilteredContracts.size === 0) {
|
|
705
829
|
return partitions;
|
|
706
830
|
}
|
|
@@ -711,6 +835,7 @@ function collapseClientFilteredContracts(partitions, clientFilteredContracts, no
|
|
|
711
835
|
let backfills = [];
|
|
712
836
|
let absorbedPartitions = [];
|
|
713
837
|
let strippedFrontiers = [];
|
|
838
|
+
let anchoredContracts$1 = anchoredContracts(partitions);
|
|
714
839
|
partitions.forEach(p => {
|
|
715
840
|
if (!p.selection.dependsOnAddresses) {
|
|
716
841
|
if (p.mergeBlock !== undefined) {
|
|
@@ -726,6 +851,11 @@ function collapseClientFilteredContracts(partitions, clientFilteredContracts, no
|
|
|
726
851
|
let serverSideNames = contractNames.filter(c => !clientFilteredContracts.has(c));
|
|
727
852
|
if (serverSideNames.length === contractNames.length) {
|
|
728
853
|
kept.push(p);
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
let contractName = p.dynamicContract;
|
|
857
|
+
if (contractName !== undefined ? anchoredContracts$1.has(contractName) : false) {
|
|
858
|
+
kept.push(p);
|
|
729
859
|
} else if (Utils.$$Array.isEmpty(serverSideNames)) {
|
|
730
860
|
absorbedPartitions.push(p);
|
|
731
861
|
} else {
|
|
@@ -735,7 +865,7 @@ function collapseClientFilteredContracts(partitions, clientFilteredContracts, no
|
|
|
735
865
|
});
|
|
736
866
|
let standing = standingRef.contents;
|
|
737
867
|
let selectionChanged = standing !== undefined ? Stdlib_Option.mapOr(standing.selection.clientFilteredContracts, 0, prim => prim.length) !== clientFilteredContracts.size : true;
|
|
738
|
-
if (Utils.$$Array.isEmpty(absorbedPartitions) && Utils.$$Array.isEmpty(strippedFrontiers) && !selectionChanged) {
|
|
868
|
+
if (Utils.$$Array.isEmpty(absorbedPartitions) && Utils.$$Array.isEmpty(strippedFrontiers) && Utils.$$Array.isEmpty(clientFilteredFrontiers) && !selectionChanged) {
|
|
739
869
|
return partitions;
|
|
740
870
|
}
|
|
741
871
|
let minFrontierRef = {
|
|
@@ -753,6 +883,7 @@ function collapseClientFilteredContracts(partitions, clientFilteredContracts, no
|
|
|
753
883
|
absorbedPartitions.forEach(p => considerFrontier(p.latestFetchedBlock));
|
|
754
884
|
backfills.forEach(p => considerFrontier(p.latestFetchedBlock));
|
|
755
885
|
strippedFrontiers.forEach(considerFrontier);
|
|
886
|
+
clientFilteredFrontiers.forEach(considerFrontier);
|
|
756
887
|
let regByIndex = {};
|
|
757
888
|
let addRegs = regs => {
|
|
758
889
|
regs.forEach(reg => {
|
|
@@ -849,59 +980,72 @@ function createPartitions(registeringSetsByContract, addressStore, dynamicContra
|
|
|
849
980
|
};
|
|
850
981
|
let dynamicPartitions = [];
|
|
851
982
|
let nonDynamicPartitions = [];
|
|
983
|
+
let clientFilteredFrontiers = [];
|
|
984
|
+
let anchoredContracts$1 = anchoredContracts(existingPartitions);
|
|
852
985
|
let contractNames = Object.keys(registeringSetsByContract);
|
|
853
986
|
for (let cIdx = 0, cIdx_finish = contractNames.length; cIdx < cIdx_finish; ++cIdx) {
|
|
854
987
|
let contractName = contractNames[cIdx];
|
|
855
988
|
let contractSet = registeringSetsByContract[contractName];
|
|
856
|
-
let
|
|
989
|
+
let isAnchored = anchoredContracts$1.has(contractName);
|
|
990
|
+
let isDynamic = dynamicContracts.has(contractName) || isAnchored;
|
|
857
991
|
let partitions = isDynamic ? dynamicPartitions : nonDynamicPartitions;
|
|
858
992
|
let groups = contractSet.startBlockGroups();
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
let joining = true;
|
|
866
|
-
while (joining && nextIdx < groups.length) {
|
|
867
|
-
let group = groups[nextIdx];
|
|
868
|
-
if ((group.startBlock - startBlock | 0) < 20000) {
|
|
869
|
-
countRef = countRef + group.count | 0;
|
|
870
|
-
nextIdx = nextIdx + 1 | 0;
|
|
871
|
-
} else {
|
|
872
|
-
joining = false;
|
|
873
|
-
}
|
|
874
|
-
};
|
|
875
|
-
let latestFetchedBlock_blockNumber = Primitive_int.max(startBlock - 1 | 0, progressBlockNumber);
|
|
876
|
-
let latestFetchedBlock = {
|
|
877
|
-
blockNumber: latestFetchedBlock_blockNumber,
|
|
878
|
-
blockTimestamp: 0
|
|
879
|
-
};
|
|
880
|
-
let remainingRef = countRef;
|
|
881
|
-
let chunkOffsetRef = offsetRef;
|
|
882
|
-
while (remainingRef > 0) {
|
|
883
|
-
let take = Primitive_int.min(remainingRef, maxAddrInPartition);
|
|
884
|
-
let pAddresses = contractSet.slice(chunkOffsetRef, take);
|
|
885
|
-
partitions.push({
|
|
886
|
-
id: nextPartitionIndexRef.contents.toString(),
|
|
887
|
-
latestFetchedBlock: latestFetchedBlock,
|
|
888
|
-
selection: normalSelection,
|
|
889
|
-
addresses: pAddresses,
|
|
890
|
-
mergeBlock: undefined,
|
|
891
|
-
dynamicContract: isDynamic ? contractName : undefined,
|
|
892
|
-
mutPendingQueries: [],
|
|
893
|
-
sourceRangeCapacity: 0,
|
|
894
|
-
prevSourceRangeCapacity: 0,
|
|
895
|
-
eventDensity: undefined,
|
|
896
|
-
latestSourceRangeCapacityUpdateBlock: 0
|
|
993
|
+
if (clientFilteredContracts.has(contractName) && !isAnchored) {
|
|
994
|
+
let match = groups[0];
|
|
995
|
+
if (match !== undefined) {
|
|
996
|
+
clientFilteredFrontiers.push({
|
|
997
|
+
blockNumber: Primitive_int.max(match.startBlock - 1 | 0, progressBlockNumber),
|
|
998
|
+
blockTimestamp: 0
|
|
897
999
|
});
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
1000
|
+
}
|
|
1001
|
+
} else {
|
|
1002
|
+
let offsetRef = 0;
|
|
1003
|
+
let groupIdx = 0;
|
|
1004
|
+
while (groupIdx < groups.length) {
|
|
1005
|
+
let startBlock = groups[groupIdx].startBlock;
|
|
1006
|
+
let countRef = 0;
|
|
1007
|
+
let nextIdx = groupIdx;
|
|
1008
|
+
let joining = true;
|
|
1009
|
+
while (joining && nextIdx < groups.length) {
|
|
1010
|
+
let group = groups[nextIdx];
|
|
1011
|
+
if ((group.startBlock - startBlock | 0) < 20000) {
|
|
1012
|
+
countRef = countRef + group.count | 0;
|
|
1013
|
+
nextIdx = nextIdx + 1 | 0;
|
|
1014
|
+
} else {
|
|
1015
|
+
joining = false;
|
|
1016
|
+
}
|
|
1017
|
+
};
|
|
1018
|
+
let latestFetchedBlock_blockNumber = Primitive_int.max(startBlock - 1 | 0, progressBlockNumber);
|
|
1019
|
+
let latestFetchedBlock = {
|
|
1020
|
+
blockNumber: latestFetchedBlock_blockNumber,
|
|
1021
|
+
blockTimestamp: 0
|
|
1022
|
+
};
|
|
1023
|
+
let remainingRef = countRef;
|
|
1024
|
+
let chunkOffsetRef = offsetRef;
|
|
1025
|
+
while (remainingRef > 0) {
|
|
1026
|
+
let take = Primitive_int.min(remainingRef, maxAddrInPartition);
|
|
1027
|
+
let pAddresses = contractSet.slice(chunkOffsetRef, take);
|
|
1028
|
+
partitions.push({
|
|
1029
|
+
id: nextPartitionIndexRef.contents.toString(),
|
|
1030
|
+
latestFetchedBlock: latestFetchedBlock,
|
|
1031
|
+
selection: normalSelection,
|
|
1032
|
+
addresses: pAddresses,
|
|
1033
|
+
mergeBlock: undefined,
|
|
1034
|
+
dynamicContract: isDynamic ? contractName : undefined,
|
|
1035
|
+
mutPendingQueries: [],
|
|
1036
|
+
sourceRangeCapacity: 0,
|
|
1037
|
+
prevSourceRangeCapacity: 0,
|
|
1038
|
+
eventDensity: undefined,
|
|
1039
|
+
latestSourceRangeCapacityUpdateBlock: 0
|
|
1040
|
+
});
|
|
1041
|
+
nextPartitionIndexRef.contents = nextPartitionIndexRef.contents + 1 | 0;
|
|
1042
|
+
chunkOffsetRef = chunkOffsetRef + take | 0;
|
|
1043
|
+
remainingRef = remainingRef - take | 0;
|
|
1044
|
+
};
|
|
1045
|
+
offsetRef = offsetRef + countRef | 0;
|
|
1046
|
+
groupIdx = nextIdx;
|
|
901
1047
|
};
|
|
902
|
-
|
|
903
|
-
groupIdx = nextIdx;
|
|
904
|
-
};
|
|
1048
|
+
}
|
|
905
1049
|
}
|
|
906
1050
|
let mergedNonDynamic = [];
|
|
907
1051
|
if (nonDynamicPartitions.length !== 0) {
|
|
@@ -944,7 +1088,7 @@ function createPartitions(registeringSetsByContract, addressStore, dynamicContra
|
|
|
944
1088
|
mergedNonDynamic.push(currentPRef);
|
|
945
1089
|
}
|
|
946
1090
|
let mergedPartitions = mergedNonDynamic.concat(dynamicPartitions);
|
|
947
|
-
let allPartitions = collapseClientFilteredContracts(existingPartitions.concat(mergedPartitions), clientFilteredContracts, normalSelection, nextPartitionIndexRef, addressStore, knownHeight);
|
|
1091
|
+
let allPartitions = collapseClientFilteredContracts(existingPartitions.concat(mergedPartitions), clientFilteredContracts, normalSelection, nextPartitionIndexRef, addressStore, clientFilteredFrontiers, knownHeight);
|
|
948
1092
|
return make(allPartitions, maxAddrInPartition, nextPartitionIndexRef.contents, dynamicContracts, clientFilteredContracts);
|
|
949
1093
|
}
|
|
950
1094
|
|
|
@@ -1097,7 +1241,7 @@ function registerDynamicContracts(fetchState, addressStore, claimCeilingOpt, ite
|
|
|
1097
1241
|
Array.from(dynamicContractsRef).forEach(contractName => {
|
|
1098
1242
|
let addressCount = addressStore.contractCount(contractName);
|
|
1099
1243
|
if (!clientFilteredContracts$1.has(contractName) && addressCount > threshold) {
|
|
1100
|
-
return addClientFilteredContract(clientFilteredContracts$1, contractName, fetchState.chainId, addressCount, threshold);
|
|
1244
|
+
return addClientFilteredContract(clientFilteredContracts$1, contractName, fetchState.chainId, addressCount, threshold, undefined);
|
|
1101
1245
|
}
|
|
1102
1246
|
});
|
|
1103
1247
|
clientFilteredContracts = clientFilteredContracts$1;
|
|
@@ -1482,12 +1626,13 @@ function getReadyItemsCount(fetchState, targetSize, fromItem) {
|
|
|
1482
1626
|
return acc;
|
|
1483
1627
|
}
|
|
1484
1628
|
|
|
1485
|
-
function make$1(startBlock, endBlock, onEventRegistrations, addressStore, addresses, maxAddrInPartition, chainId, maxOnBlockBufferSize, knownHeight, progressBlockNumberOpt, onBlockRegistrationsOpt, blockLagOpt, firstEventBlockOpt, clientFilterAddressThresholdOpt) {
|
|
1629
|
+
function make$1(startBlock, endBlock, onEventRegistrations, addressStore, addresses, maxAddrInPartition, chainId, maxOnBlockBufferSize, knownHeight, progressBlockNumberOpt, onBlockRegistrationsOpt, blockLagOpt, firstEventBlockOpt, clientFilterAddressThresholdOpt, isResumedOpt) {
|
|
1486
1630
|
let progressBlockNumber = progressBlockNumberOpt !== undefined ? progressBlockNumberOpt : startBlock - 1 | 0;
|
|
1487
1631
|
let onBlockRegistrations = onBlockRegistrationsOpt !== undefined ? onBlockRegistrationsOpt : [];
|
|
1488
1632
|
let blockLag = blockLagOpt !== undefined ? blockLagOpt : 0;
|
|
1489
1633
|
let firstEventBlock = firstEventBlockOpt !== undefined ? Primitive_option.valFromOption(firstEventBlockOpt) : undefined;
|
|
1490
1634
|
let clientFilterAddressThreshold = clientFilterAddressThresholdOpt !== undefined ? Primitive_option.valFromOption(clientFilterAddressThresholdOpt) : undefined;
|
|
1635
|
+
let isResumed = isResumedOpt !== undefined ? isResumedOpt : false;
|
|
1491
1636
|
let latestFetchedBlock = {
|
|
1492
1637
|
blockNumber: progressBlockNumber,
|
|
1493
1638
|
blockTimestamp: 0
|
|
@@ -1552,7 +1697,7 @@ function make$1(startBlock, endBlock, onEventRegistrations, addressStore, addres
|
|
|
1552
1697
|
Utils.Dict.forEachWithKey(registeringSetsByContract, (set, contractName) => {
|
|
1553
1698
|
let addressCount = set.size();
|
|
1554
1699
|
if (addressCount > clientFilterAddressThreshold) {
|
|
1555
|
-
return addClientFilteredContract(clientFilteredContracts, contractName, chainId, addressCount, clientFilterAddressThreshold);
|
|
1700
|
+
return addClientFilteredContract(clientFilteredContracts, contractName, chainId, addressCount, clientFilterAddressThreshold, !isResumed);
|
|
1556
1701
|
}
|
|
1557
1702
|
});
|
|
1558
1703
|
}
|
|
@@ -1669,6 +1814,7 @@ function rollback(fetchState, addressStore, targetBlockNumber) {
|
|
|
1669
1814
|
} else {
|
|
1670
1815
|
let id$1 = nextKeptIdRef.toString();
|
|
1671
1816
|
nextKeptIdRef = nextKeptIdRef + 1 | 0;
|
|
1817
|
+
let mergeBlock$2 = p.mergeBlock;
|
|
1672
1818
|
keptPartitions.push({
|
|
1673
1819
|
id: id$1,
|
|
1674
1820
|
latestFetchedBlock: p.latestFetchedBlock.blockNumber > targetBlockNumber ? ({
|
|
@@ -1677,7 +1823,7 @@ function rollback(fetchState, addressStore, targetBlockNumber) {
|
|
|
1677
1823
|
}) : p.latestFetchedBlock,
|
|
1678
1824
|
selection: p.selection,
|
|
1679
1825
|
addresses: p.addresses,
|
|
1680
|
-
mergeBlock:
|
|
1826
|
+
mergeBlock: mergeBlock$2 !== undefined && mergeBlock$2 > targetBlockNumber ? targetBlockNumber : mergeBlock$2,
|
|
1681
1827
|
dynamicContract: p.dynamicContract,
|
|
1682
1828
|
mutPendingQueries: rollbackPendingQueries(p.mutPendingQueries, targetBlockNumber),
|
|
1683
1829
|
sourceRangeCapacity: p.sourceRangeCapacity,
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// The FinalizingIndexes phase. Runs once, 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 creates every missing schema-defined
|
|
4
|
+
// index and stamps `ready_at` in one transaction. A failure rolls back both, so
|
|
5
|
+
// the next batch simply retries — the indexer never reports ready with an index
|
|
6
|
+
// the schema promised still missing.
|
|
7
|
+
|
|
8
|
+
let run = async (state: IndexerState.t) => {
|
|
9
|
+
Logging.info(
|
|
10
|
+
"All chains are caught up. Finalizing the indexer before switching to realtime: flushing pending writes, then creating the indexes the schema promises.",
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
await Writing.flush(state)
|
|
14
|
+
|
|
15
|
+
// A failed write already surfaced through onError; committing ready_at on top
|
|
16
|
+
// of an incomplete write would claim progress that isn't durable.
|
|
17
|
+
if !(state->IndexerState.hasFailedWrite) {
|
|
18
|
+
let persistence = state->IndexerState.persistence
|
|
19
|
+
let storage = persistence->Persistence.getInitializedStorageOrThrow
|
|
20
|
+
let readyAt = Date.make()
|
|
21
|
+
|
|
22
|
+
await storage.finalizeBackfill(
|
|
23
|
+
~entities=persistence.allEntities,
|
|
24
|
+
~chainIds=state
|
|
25
|
+
->IndexerState.chainStates
|
|
26
|
+
->Dict.valuesToArray
|
|
27
|
+
->Array.map(cs => (cs->ChainState.chainConfig).id),
|
|
28
|
+
~readyAt,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
state->IndexerState.markReady(~readyAt)
|
|
32
|
+
Logging.info("The indexer is ready. Switching to realtime indexing.")
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
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 run(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
|
+
export {
|
|
24
|
+
run,
|
|
25
|
+
}
|
|
26
|
+
/* Logging Not a pure module */
|
package/src/InMemoryTable.res
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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
|
|
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.
|
|
101
|
+
self.filterIndexes = Dict.make()
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
let
|
|
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.
|
|
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
|
|
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.
|
|
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->
|
|
163
|
-
| Delete({entityId}) => inMemTable->
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
243
|
+
inMemTable.filterIndexes->Dict.set(filterKey, (filter, relatedEntityIds))
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
246
|
}
|