envio 3.15.0 → 3.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/README.md +40 -34
  2. package/evm.schema.json +11 -2
  3. package/fuel.schema.json +10 -1
  4. package/index.d.ts +6 -0
  5. package/licenses/README.md +1 -1
  6. package/package.json +8 -8
  7. package/src/Batch.res.mjs +5 -5
  8. package/src/BatchProcessing.res +1 -0
  9. package/src/BatchProcessing.res.mjs +1 -0
  10. package/src/ChainState.res +48 -7
  11. package/src/ChainState.res.mjs +27 -11
  12. package/src/Clock.res +57 -0
  13. package/src/Clock.res.mjs +72 -0
  14. package/src/Config.res +10 -3
  15. package/src/Config.res.mjs +4 -2
  16. package/src/Core.res +4 -40
  17. package/src/EffectState.res +10 -2
  18. package/src/EffectState.res.mjs +8 -4
  19. package/src/EffectState.resi +3 -1
  20. package/src/EntityId.res +2 -7
  21. package/src/EntityId.res.mjs +1 -8
  22. package/src/EntityTables.res +4 -1
  23. package/src/EntityTables.res.mjs +1 -1
  24. package/src/FetchState.res +157 -69
  25. package/src/FetchState.res.mjs +144 -49
  26. package/src/FinalizeBackfill.res +2 -4
  27. package/src/FinalizeBackfill.res.mjs +3 -4
  28. package/src/InMemoryTable.res +17 -16
  29. package/src/InMemoryTable.res.mjs +14 -15
  30. package/src/IndexerState.res +28 -4
  31. package/src/IndexerState.res.mjs +19 -6
  32. package/src/IndexerState.resi +4 -0
  33. package/src/Internal.res +0 -49
  34. package/src/Internal.res.mjs +1 -80
  35. package/src/LoadLayer.res +14 -8
  36. package/src/LoadLayer.res.mjs +8 -6
  37. package/src/Main.res.mjs +1 -1
  38. package/src/Persistence.res +3 -23
  39. package/src/Persistence.res.mjs +3 -3
  40. package/src/Rollback.res +0 -1
  41. package/src/Rollback.res.mjs +1 -1
  42. package/src/SafeCheckpointTracking.res +4 -4
  43. package/src/SafeCheckpointTracking.res.mjs +1 -1
  44. package/src/StagedRows.res +106 -0
  45. package/src/StagedRows.res.mjs +102 -0
  46. package/src/Staging.res +32 -14
  47. package/src/Staging.res.mjs +17 -20
  48. package/src/Storage.res +277 -948
  49. package/src/Storage.res.mjs +215 -707
  50. package/src/Supervisor.res +75 -56
  51. package/src/Supervisor.res.mjs +55 -26
  52. package/src/TestIndexer.res +22 -27
  53. package/src/TestIndexer.res.mjs +20 -17
  54. package/src/Throttler.res +15 -8
  55. package/src/Throttler.res.mjs +9 -6
  56. package/src/Throttler.resi +1 -1
  57. package/src/Utils.res +0 -6
  58. package/src/Utils.res.mjs +0 -5
  59. package/src/Writing.res +0 -1
  60. package/src/Writing.res.mjs +1 -1
  61. package/src/bindings/ClickHouse.res +0 -322
  62. package/src/bindings/ClickHouse.res.mjs +0 -338
  63. package/src/bindings/StorageClient.res +107 -174
  64. package/src/bindings/StorageClient.res.mjs +19 -8
  65. package/src/bindings/Vitest.res +0 -26
  66. package/src/bindings/Vitest.res.mjs +0 -3
  67. package/src/db/Table.res +24 -150
  68. package/src/db/Table.res.mjs +38 -272
  69. package/src/sources/ChainSources.res +2 -0
  70. package/src/sources/ChainSources.res.mjs +2 -2
  71. package/src/sources/EvmChain.res +3 -0
  72. package/src/sources/EvmChain.res.mjs +4 -2
  73. package/src/sources/EvmHyperSyncSource.res +2 -2
  74. package/src/sources/EvmHyperSyncSource.res.mjs +1 -1
  75. package/src/sources/EvmRpcClient.res +13 -3
  76. package/src/sources/EvmRpcClient.res.mjs +4 -3
  77. package/src/sources/EvmRpcWs.res +4 -3
  78. package/src/sources/EvmRpcWs.res.mjs +2 -2
  79. package/src/sources/HeightFeed.res +31 -10
  80. package/src/sources/HeightFeed.res.mjs +10 -6
  81. package/src/sources/HeightStream.res +32 -17
  82. package/src/sources/HeightStream.res.mjs +10 -10
  83. package/src/sources/HyperSyncSSE.res +4 -6
  84. package/src/sources/HyperSyncSSE.res.mjs +2 -2
  85. package/src/sources/RpcSource.res +10 -1
  86. package/src/sources/RpcSource.res.mjs +4 -3
  87. package/src/sources/Source.res +3 -0
  88. package/src/sources/SourceManager.res +84 -77
  89. package/src/sources/SourceManager.res.mjs +17 -20
  90. package/src/sources/SourceManager.resi +3 -4
  91. package/src/sources/StartBlockResolver.res +1 -0
  92. package/src/sources/StartBlockResolver.res.mjs +3 -2
  93. package/src/sources/SvmHyperSyncSource.res +2 -2
  94. package/src/sources/SvmHyperSyncSource.res.mjs +1 -1
  95. package/svm.schema.json +11 -2
  96. package/src/PgWriting.res +0 -36
  97. package/src/PgWriting.res.mjs +0 -34
  98. package/src/db/IndexDefinition.res +0 -47
  99. package/src/db/IndexDefinition.res.mjs +0 -67
@@ -941,26 +941,47 @@ function updateInternal(fetchState, optimizedPartitionsOpt, mutItems, mutItemsSo
941
941
  chainId: fetchState.chainId,
942
942
  latestOnBlockBlockNumber: latestOnBlockBlockNumber,
943
943
  blockLag: blockLag,
944
+ blockThrottle: fetchState.blockThrottle,
944
945
  buffer: blockItems.length !== 0 ? mergeIntoBuffer(base, blockItems) : base,
945
946
  maxOnBlockBufferSize: fetchState.maxOnBlockBufferSize,
946
947
  onBlockRegistrations: fetchState.onBlockRegistrations,
947
948
  knownHeight: knownHeight,
948
949
  firstEventBlock: fetchState.firstEventBlock,
949
- clientFilterAddressThreshold: fetchState.clientFilterAddressThreshold
950
+ clientFilterPolicy: fetchState.clientFilterPolicy
950
951
  };
951
952
  }
952
953
 
953
- function addClientFilteredContract(clientFilteredContracts, contractName, onNotice, addressCount, threshold, isAlreadyReportedOpt) {
954
+ function switchToClientSideFiltering(clientFilteredContracts, contractNames, policy, dynamicContracts, addressStore, onNotice, isAlreadyReportedOpt) {
954
955
  let isAlreadyReported = isAlreadyReportedOpt !== undefined ? isAlreadyReportedOpt : false;
955
- clientFilteredContracts.add(contractName);
956
- if (!isAlreadyReported) {
957
- return onNotice({
958
- TAG: "SwitchedToClientSideFiltering",
959
- contractName: contractName,
960
- addressCount: addressCount,
961
- threshold: threshold
962
- });
963
- }
956
+ let switched = new Set(Array.from(clientFilteredContracts));
957
+ contractNames.forEach(contractName => {
958
+ if (switched.has(contractName)) {
959
+ return;
960
+ }
961
+ if (policy.allDynamicContracts && dynamicContracts.has(contractName)) {
962
+ switched.add(contractName);
963
+ return;
964
+ }
965
+ let threshold = policy.addressThreshold;
966
+ if (threshold === undefined) {
967
+ return;
968
+ }
969
+ let addressCount = addressStore.contractCount(contractName);
970
+ if (addressCount > threshold) {
971
+ switched.add(contractName);
972
+ if (!isAlreadyReported) {
973
+ return onNotice({
974
+ TAG: "SwitchedToClientSideFiltering",
975
+ contractName: contractName,
976
+ addressCount: addressCount,
977
+ threshold: threshold
978
+ });
979
+ } else {
980
+ return;
981
+ }
982
+ }
983
+ });
984
+ return switched;
964
985
  }
965
986
 
966
987
  function claimedFetchedBlock(p, knownHeight) {
@@ -1362,20 +1383,7 @@ function registerDynamicContracts(fetchState, addressStore, onNotice, claimCeili
1362
1383
  }
1363
1384
  }
1364
1385
  }
1365
- let threshold = fetchState.clientFilterAddressThreshold;
1366
- let clientFilteredContracts;
1367
- if (threshold !== undefined) {
1368
- let clientFilteredContracts$1 = new Set(Array.from(fetchState.optimizedPartitions.clientFilteredContracts));
1369
- Array.from(dynamicContractsRef).forEach(contractName => {
1370
- let addressCount = addressStore.contractCount(contractName);
1371
- if (!clientFilteredContracts$1.has(contractName) && addressCount > threshold) {
1372
- return addClientFilteredContract(clientFilteredContracts$1, contractName, onNotice, addressCount, threshold, undefined);
1373
- }
1374
- });
1375
- clientFilteredContracts = clientFilteredContracts$1;
1376
- } else {
1377
- clientFilteredContracts = fetchState.optimizedPartitions.clientFilteredContracts;
1378
- }
1386
+ let clientFilteredContracts = switchToClientSideFiltering(fetchState.optimizedPartitions.clientFilteredContracts, Array.from(dynamicContractsRef), fetchState.clientFilterPolicy, dynamicContractsRef, addressStore, onNotice, undefined);
1379
1387
  let registeringSetsByContract = {};
1380
1388
  registeringContractNames.forEach(contractName => {
1381
1389
  registeringSetsByContract[contractName] = AddressStore.makeSet(addressStore, contractName, {
@@ -1386,6 +1394,55 @@ function registerDynamicContracts(fetchState, addressStore, onNotice, claimCeili
1386
1394
  return updateInternal(fetchState, optimizedPartitions, undefined, undefined, undefined, undefined);
1387
1395
  }
1388
1396
 
1397
+ function switchDynamicContractsToClientSideFiltering(fetchState, addressStore) {
1398
+ if (fetchState.clientFilterPolicy.allDynamicContracts) {
1399
+ return fetchState;
1400
+ }
1401
+ let init = fetchState.clientFilterPolicy;
1402
+ let fetchState_optimizedPartitions = fetchState.optimizedPartitions;
1403
+ let fetchState_startBlock = fetchState.startBlock;
1404
+ let fetchState_endBlock = fetchState.endBlock;
1405
+ let fetchState_normalSelection = fetchState.normalSelection;
1406
+ let fetchState_chainId = fetchState.chainId;
1407
+ let fetchState_latestOnBlockBlockNumber = fetchState.latestOnBlockBlockNumber;
1408
+ let fetchState_blockLag = fetchState.blockLag;
1409
+ let fetchState_blockThrottle = fetchState.blockThrottle;
1410
+ let fetchState_buffer = fetchState.buffer;
1411
+ let fetchState_maxOnBlockBufferSize = fetchState.maxOnBlockBufferSize;
1412
+ let fetchState_onBlockRegistrations = fetchState.onBlockRegistrations;
1413
+ let fetchState_knownHeight = fetchState.knownHeight;
1414
+ let fetchState_firstEventBlock = fetchState.firstEventBlock;
1415
+ let fetchState_clientFilterPolicy = {
1416
+ addressThreshold: init.addressThreshold,
1417
+ allDynamicContracts: true
1418
+ };
1419
+ let fetchState$1 = {
1420
+ optimizedPartitions: fetchState_optimizedPartitions,
1421
+ startBlock: fetchState_startBlock,
1422
+ endBlock: fetchState_endBlock,
1423
+ normalSelection: fetchState_normalSelection,
1424
+ chainId: fetchState_chainId,
1425
+ latestOnBlockBlockNumber: fetchState_latestOnBlockBlockNumber,
1426
+ blockLag: fetchState_blockLag,
1427
+ blockThrottle: fetchState_blockThrottle,
1428
+ buffer: fetchState_buffer,
1429
+ maxOnBlockBufferSize: fetchState_maxOnBlockBufferSize,
1430
+ onBlockRegistrations: fetchState_onBlockRegistrations,
1431
+ knownHeight: fetchState_knownHeight,
1432
+ firstEventBlock: fetchState_firstEventBlock,
1433
+ clientFilterPolicy: fetchState_clientFilterPolicy
1434
+ };
1435
+ let match = fetchState_optimizedPartitions;
1436
+ let clientFilteredContracts = match.clientFilteredContracts;
1437
+ let dynamicContracts = match.dynamicContracts;
1438
+ let switched = new Set(Array.from(clientFilteredContracts).concat(Array.from(dynamicContracts)));
1439
+ if (switched.size === clientFilteredContracts.size) {
1440
+ return fetchState$1;
1441
+ } else {
1442
+ return updateInternal(fetchState$1, createPartitions({}, addressStore, dynamicContracts, switched, fetchState_normalSelection, match.maxAddrInPartition, match.nextPartitionIndex, Object.values(fetchState_optimizedPartitions.entities), 0, fetchState_knownHeight), undefined, undefined, undefined, undefined);
1443
+ }
1444
+ }
1445
+
1389
1446
  function handleQueryResult(fetchState, query, latestFetchedBlock, newItems) {
1390
1447
  return updateInternal(fetchState, handleQueryResponse(fetchState.optimizedPartitions, query, fetchState.knownHeight, newItems.length, latestFetchedBlock), newItems.length !== 0 ? mergeIntoBuffer(fetchState.buffer, newItems) : undefined, true, undefined, undefined);
1391
1448
  }
@@ -1624,15 +1681,30 @@ function acceptCandidates(candidates, reservations, chainTargetItems, partitionI
1624
1681
  };
1625
1682
  }
1626
1683
 
1627
- function getNextQuery(param, chainTargetBlock, chainTargetItems) {
1628
- let blockLag = param.blockLag;
1684
+ function blocksBehindHeadAllowedByThrottle(param) {
1685
+ let blockThrottle = param.blockThrottle;
1629
1686
  let endBlock = param.endBlock;
1630
- let optimizedPartitions = param.optimizedPartitions;
1631
- let headBlockNumber = param.knownHeight - blockLag | 0;
1687
+ if (endBlock !== undefined && (param.knownHeight - param.blockLag | 0) >= endBlock) {
1688
+ return 0;
1689
+ } else {
1690
+ return Primitive_int.max(0, blockThrottle - 1 | 0);
1691
+ }
1692
+ }
1693
+
1694
+ function headFetchTarget(fetchState) {
1695
+ return (fetchState.knownHeight - fetchState.blockLag | 0) - blocksBehindHeadAllowedByThrottle(fetchState) | 0;
1696
+ }
1697
+
1698
+ function getNextQuery(fetchState, chainTargetBlock, chainTargetItems) {
1699
+ let blockLag = fetchState.blockLag;
1700
+ let endBlock = fetchState.endBlock;
1701
+ let optimizedPartitions = fetchState.optimizedPartitions;
1702
+ let headBlockNumber = fetchState.knownHeight - blockLag | 0;
1632
1703
  if (headBlockNumber <= 0) {
1633
1704
  return "WaitingForNewBlock";
1634
1705
  }
1635
- let isOnBlockBehindTheHead = param.latestOnBlockBlockNumber < headBlockNumber;
1706
+ let caughtUpBlockNumber = headBlockNumber - blocksBehindHeadAllowedByThrottle(fetchState) | 0;
1707
+ let isOnBlockBehindTheHead = fetchState.latestOnBlockBlockNumber < caughtUpBlockNumber;
1636
1708
  let shouldWaitForNewBlock = (
1637
1709
  endBlock !== undefined ? headBlockNumber < endBlock : true
1638
1710
  ) && !isOnBlockBehindTheHead;
@@ -1658,7 +1730,7 @@ function getNextQuery(param, chainTargetBlock, chainTargetItems) {
1658
1730
  }
1659
1731
  }
1660
1732
  inFlightCounts[idx] = inFlightCount;
1661
- if (p.mutPendingQueries.length !== 0 || p.latestFetchedBlock < headBlockNumber) {
1733
+ if (p.mutPendingQueries.length !== 0 || p.latestFetchedBlock < caughtUpBlockNumber) {
1662
1734
  shouldWaitForNewBlock = false;
1663
1735
  }
1664
1736
  }
@@ -1749,10 +1821,11 @@ function getReadyItemsCount(fetchState, targetSize, fromItem) {
1749
1821
  return acc;
1750
1822
  }
1751
1823
 
1752
- function make$1(startBlock, endBlock, onEventRegistrations, addressStore, addressRows, maxAddrInPartition, chainId, onNotice, maxOnBlockBufferSize, knownHeight, progressBlockNumberOpt, onBlockRegistrationsOpt, blockLagOpt, firstEventBlockOpt, clientFilterAddressThresholdOpt, isResumedOpt) {
1824
+ function make$1(startBlock, endBlock, onEventRegistrations, addressStore, addressRows, maxAddrInPartition, chainId, onNotice, maxOnBlockBufferSize, knownHeight, progressBlockNumberOpt, onBlockRegistrationsOpt, blockLagOpt, blockThrottleOpt, firstEventBlockOpt, clientFilterAddressThresholdOpt, isResumedOpt) {
1753
1825
  let progressBlockNumber = progressBlockNumberOpt !== undefined ? progressBlockNumberOpt : startBlock - 1 | 0;
1754
1826
  let onBlockRegistrations = onBlockRegistrationsOpt !== undefined ? onBlockRegistrationsOpt : [];
1755
1827
  let blockLag = blockLagOpt !== undefined ? blockLagOpt : 0;
1828
+ let blockThrottle = blockThrottleOpt !== undefined ? blockThrottleOpt : 0;
1756
1829
  let firstEventBlock = firstEventBlockOpt !== undefined ? Primitive_option.valFromOption(firstEventBlockOpt) : undefined;
1757
1830
  let clientFilterAddressThreshold = clientFilterAddressThresholdOpt !== undefined ? Primitive_option.valFromOption(clientFilterAddressThresholdOpt) : undefined;
1758
1831
  let isResumed = isResumedOpt !== undefined ? isResumedOpt : false;
@@ -1801,7 +1874,6 @@ function make$1(startBlock, endBlock, onEventRegistrations, addressStore, addres
1801
1874
  requestedFromBlock: rejected.effectiveStartBlock
1802
1875
  }));
1803
1876
  let dynamicContracts = new Set();
1804
- let clientFilteredContracts = new Set();
1805
1877
  let registeringSetsByContract = {};
1806
1878
  Array.from(contractNamesWithNormalEvents).forEach(contractName => {
1807
1879
  if (addressStore.contractCount(contractName) > 0) {
@@ -1815,14 +1887,11 @@ function make$1(startBlock, endBlock, onEventRegistrations, addressStore, addres
1815
1887
  return;
1816
1888
  }
1817
1889
  });
1818
- if (clientFilterAddressThreshold !== undefined) {
1819
- Utils.Dict.forEachWithKey(registeringSetsByContract, (set, contractName) => {
1820
- let addressCount = set.size();
1821
- if (addressCount > clientFilterAddressThreshold) {
1822
- return addClientFilteredContract(clientFilteredContracts, contractName, onNotice, addressCount, clientFilterAddressThreshold, isResumed);
1823
- }
1824
- });
1825
- }
1890
+ let clientFilterPolicy = {
1891
+ addressThreshold: clientFilterAddressThreshold,
1892
+ allDynamicContracts: false
1893
+ };
1894
+ let clientFilteredContracts = switchToClientSideFiltering(new Set(), Object.keys(registeringSetsByContract), clientFilterPolicy, dynamicContracts, addressStore, onNotice, isResumed);
1826
1895
  let optimizedPartitions = createPartitions(registeringSetsByContract, addressStore, dynamicContracts, clientFilteredContracts, normalSelection, maxAddrInPartition, partitions.length, partitions, progressBlockNumber, knownHeight);
1827
1896
  if (optimizedPartitions.idsInAscOrder.length === 0 && Utils.$$Array.isEmpty(onBlockRegistrations$1)) {
1828
1897
  Stdlib_JsError.throwWithMessage(`Invalid configuration: Nothing to fetch on chain ` + ChainId.toString(chainId) + `. ` + (`addresses=` + addressRows.addresses.length.toString() + `, `) + (`onEventRegistrations=` + onEventRegistrations.length.toString() + `, `) + (`normalRegistrations=` + normalRegistrations.length.toString() + `. `) + `Make sure that you provided at least one contract address to index, or have events with Wildcard mode enabled, or have onBlock handlers.`);
@@ -1853,12 +1922,13 @@ function make$1(startBlock, endBlock, onEventRegistrations, addressStore, addres
1853
1922
  chainId: chainId,
1854
1923
  latestOnBlockBlockNumber: latestOnBlockBlockNumber,
1855
1924
  blockLag: blockLag,
1925
+ blockThrottle: blockThrottle,
1856
1926
  buffer: buffer,
1857
1927
  maxOnBlockBufferSize: maxOnBlockBufferSize,
1858
1928
  onBlockRegistrations: onBlockRegistrations$1,
1859
1929
  knownHeight: knownHeight,
1860
1930
  firstEventBlock: firstEventBlock,
1861
- clientFilterAddressThreshold: clientFilterAddressThreshold
1931
+ clientFilterPolicy: clientFilterPolicy
1862
1932
  };
1863
1933
  }
1864
1934
 
@@ -1966,12 +2036,13 @@ function rollback(fetchState, rolledBackAddressStore, targetBlockNumber) {
1966
2036
  chainId: fetchState.chainId,
1967
2037
  latestOnBlockBlockNumber: Primitive_int.min(fetchState.latestOnBlockBlockNumber, targetBlockNumber),
1968
2038
  blockLag: fetchState.blockLag,
2039
+ blockThrottle: fetchState.blockThrottle,
1969
2040
  buffer: fetchState.buffer,
1970
2041
  maxOnBlockBufferSize: fetchState.maxOnBlockBufferSize,
1971
2042
  onBlockRegistrations: fetchState.onBlockRegistrations,
1972
2043
  knownHeight: fetchState.knownHeight,
1973
2044
  firstEventBlock: fetchState.firstEventBlock,
1974
- clientFilterAddressThreshold: fetchState.clientFilterAddressThreshold
2045
+ clientFilterPolicy: fetchState.clientFilterPolicy
1975
2046
  }, optimizedPartitions, fetchState.buffer.filter(item => {
1976
2047
  let tmp;
1977
2048
  tmp = item.kind === 0 ? item.blockNumber : item.blockNumber;
@@ -2021,12 +2092,13 @@ function resetPendingQueries(fetchState) {
2021
2092
  chainId: fetchState.chainId,
2022
2093
  latestOnBlockBlockNumber: fetchState.latestOnBlockBlockNumber,
2023
2094
  blockLag: fetchState.blockLag,
2095
+ blockThrottle: fetchState.blockThrottle,
2024
2096
  buffer: fetchState.buffer,
2025
2097
  maxOnBlockBufferSize: fetchState.maxOnBlockBufferSize,
2026
2098
  onBlockRegistrations: fetchState.onBlockRegistrations,
2027
2099
  knownHeight: fetchState.knownHeight,
2028
2100
  firstEventBlock: fetchState.firstEventBlock,
2029
- clientFilterAddressThreshold: fetchState.clientFilterAddressThreshold
2101
+ clientFilterPolicy: fetchState.clientFilterPolicy
2030
2102
  };
2031
2103
  }
2032
2104
 
@@ -2044,21 +2116,37 @@ function isActivelyIndexing(fetchState) {
2044
2116
  }
2045
2117
 
2046
2118
  function isFetchingAtHead(fetchState) {
2047
- let knownHeight = fetchState.knownHeight;
2048
- let blockLag = fetchState.blockLag;
2049
2119
  let endBlock = fetchState.endBlock;
2050
2120
  let bufferBlockNumber$1 = bufferBlockNumber(fetchState);
2051
- if (knownHeight !== 0) {
2121
+ if (fetchState.knownHeight !== 0) {
2052
2122
  if (endBlock !== undefined && bufferBlockNumber$1 >= endBlock) {
2053
2123
  return true;
2054
2124
  } else {
2055
- return bufferBlockNumber$1 >= (knownHeight - blockLag | 0);
2125
+ return bufferBlockNumber$1 >= headFetchTarget(fetchState);
2056
2126
  }
2057
2127
  } else {
2058
2128
  return false;
2059
2129
  }
2060
2130
  }
2061
2131
 
2132
+ function isWithinHeadClientFilteringDistance(fetchState) {
2133
+ let knownHeight = fetchState.knownHeight;
2134
+ let endBlock = fetchState.endBlock;
2135
+ let optimizedPartitions = fetchState.optimizedPartitions;
2136
+ if (!(knownHeight !== 0 && (
2137
+ endBlock !== undefined ? endBlock > (knownHeight - fetchState.blockLag | 0) : true
2138
+ ))) {
2139
+ return false;
2140
+ }
2141
+ let id = optimizedPartitions.idsInAscOrder[0];
2142
+ let latestFetchedBlock = id !== undefined ? optimizedPartitions.entities[id].latestFetchedBlock : undefined;
2143
+ if (latestFetchedBlock !== undefined) {
2144
+ return (headFetchTarget(fetchState) - latestFetchedBlock | 0) <= 200;
2145
+ } else {
2146
+ return true;
2147
+ }
2148
+ }
2149
+
2062
2150
  function isReadyToEnterReorgThreshold(tolerance, fetchState) {
2063
2151
  let knownHeight = fetchState.knownHeight;
2064
2152
  let endBlock = fetchState.endBlock;
@@ -2145,6 +2233,8 @@ let maxChainConcurrency = Env.maxChainConcurrency;
2145
2233
 
2146
2234
  let chunkRangeGrowthFactor = 1.8;
2147
2235
 
2236
+ let headClientFilteringDistance = 200;
2237
+
2148
2238
  export {
2149
2239
  deriveSelectionStartBlock,
2150
2240
  makeSelection,
@@ -2168,12 +2258,13 @@ export {
2168
2258
  mergeIntoBuffer,
2169
2259
  appendOnBlockItems,
2170
2260
  updateInternal,
2171
- addClientFilteredContract,
2261
+ switchToClientSideFiltering,
2172
2262
  claimedFetchedBlock,
2173
2263
  collapseClientFilteredContracts,
2174
2264
  reportRejectedRegistration,
2175
2265
  createPartitions,
2176
2266
  registerDynamicContracts,
2267
+ switchDynamicContractsToClientSideFiltering,
2177
2268
  handleQueryResult,
2178
2269
  startFetchingQueries,
2179
2270
  maxInFlightChunksPerPartition,
@@ -2184,6 +2275,8 @@ export {
2184
2275
  walkPartitionPending,
2185
2276
  pushForwardCandidates,
2186
2277
  acceptCandidates,
2278
+ blocksBehindHeadAllowedByThrottle,
2279
+ headFetchTarget,
2187
2280
  getNextQuery,
2188
2281
  hasReadyItem,
2189
2282
  getReadyItemsCount,
@@ -2195,6 +2288,8 @@ export {
2195
2288
  resetPendingQueries,
2196
2289
  isActivelyIndexing,
2197
2290
  isFetchingAtHead,
2291
+ headClientFilteringDistance,
2292
+ isWithinHeadClientFilteringDistance,
2198
2293
  isReadyToEnterReorgThreshold,
2199
2294
  getProgressPercentage,
2200
2295
  sortForBatch,
@@ -40,7 +40,7 @@ let runOnce = async (state: IndexerState.t) => {
40
40
  let storage = persistence->Persistence.getInitializedStorageOrThrow
41
41
  let readyAt = Date.make()
42
42
 
43
- await storage.finalizeBackfill(~entities=persistence.allEntities, ~chainIds, ~readyAt)
43
+ await storage.finalizeBackfill(~chainIds, ~readyAt)
44
44
 
45
45
  // Only after the commit: in-memory readiness must never run ahead of the
46
46
  // `ready_at` a restart would read back.
@@ -68,10 +68,8 @@ let run = (state: IndexerState.t) =>
68
68
  // Best-effort and not awaited by the loop: indexing is already live and correct
69
69
  // without the index, just slower.
70
70
  let repairSchemaIndexes = (state: IndexerState.t) => {
71
- let persistence = state->IndexerState.persistence
72
- let storage = persistence->Persistence.getInitializedStorageOrThrow
71
+ let storage = state->IndexerState.persistence->Persistence.getInitializedStorageOrThrow
73
72
  storage.ensureSchemaIndexes(
74
- ~entities=persistence.allEntities,
75
73
  ~chainIds=state->IndexerState.crossChainState->CrossChainState.chainIds,
76
74
  )
77
75
  }
@@ -27,7 +27,7 @@ async function runOnce(state) {
27
27
  let persistence = IndexerState.persistence(state);
28
28
  let storage = Persistence.getInitializedStorageOrThrow(persistence);
29
29
  let readyAt = new Date();
30
- await storage.finalizeBackfill(persistence.allEntities, chainIds, readyAt);
30
+ await storage.finalizeBackfill(chainIds, readyAt);
31
31
  return IndexerState.markReady(state, readyAt);
32
32
  }
33
33
 
@@ -42,9 +42,8 @@ function run(state) {
42
42
  }
43
43
 
44
44
  function repairSchemaIndexes(state) {
45
- let persistence = IndexerState.persistence(state);
46
- let storage = Persistence.getInitializedStorageOrThrow(persistence);
47
- return storage.ensureSchemaIndexes(persistence.allEntities, CrossChainState.chainIds(IndexerState.crossChainState(state)));
45
+ let storage = Persistence.getInitializedStorageOrThrow(IndexerState.persistence(state));
46
+ return storage.ensureSchemaIndexes(CrossChainState.chainIds(IndexerState.crossChainState(state)));
48
47
  }
49
48
 
50
49
  export {
@@ -22,6 +22,8 @@ module Entity = {
22
22
  }
23
23
 
24
24
  type t = {
25
+ // Keys every id dict and Set below; see Table.table.
26
+ entityIdToKey: EntityId.t => string,
25
27
  latestEntityChangeById: dict<Change.t<Internal.entity>>,
26
28
  // Recorded changes (new latest ids + prevEntityChanges pushes), tracked
27
29
  // manually so InMemoryStore can gauge size without scanning every dict.
@@ -45,12 +47,9 @@ module Entity = {
45
47
  mutable hasIndexes: bool,
46
48
  }
47
49
 
48
- // Helper to extract an entity's id as a dict key. The raw id may be a
49
- // string/int/bigint, so it's stringified to a stable key for in-memory
50
- // indexing.
51
- let getEntityIdUnsafe = (entity: Internal.entity): string =>
50
+ let getEntityKeyUnsafe = (self: t, entity: Internal.entity): string =>
52
51
  switch (entity->(Utils.magic: Internal.entity => {"id": option<EntityId.t>}))["id"] {
53
- | Some(id) => id->EntityId.toKey
52
+ | Some(id) => self.entityIdToKey(id)
54
53
  | None => JsError.throwWithMessage("Property 'id' does not exist on expected entity object")
55
54
  }
56
55
 
@@ -62,7 +61,8 @@ module Entity = {
62
61
  }
63
62
  }
64
63
 
65
- let make = (): t => {
64
+ let make = (~entityIdToKey): t => {
65
+ entityIdToKey,
66
66
  latestEntityChangeById: Dict.make(),
67
67
  changesCount: 0.,
68
68
  dirtyIds: Utils.Set.make(),
@@ -141,10 +141,8 @@ module Entity = {
141
141
  self.hasIndexes = false
142
142
  }
143
143
 
144
- let updateIndexes = (self: t, ~entity: Internal.entity) =>
144
+ let updateIndexes = (self: t, ~entityId: string, ~entity: Internal.entity) =>
145
145
  if self.hasIndexes {
146
- let entityId = entity->getEntityIdUnsafe
147
-
148
146
  switch self.indexesByEntityId->Utils.Dict.dangerouslyGetNonOption(entityId) {
149
147
  | None => ()
150
148
  | Some(indexes) =>
@@ -185,7 +183,7 @@ module Entity = {
185
183
  }
186
184
 
187
185
  let set = (inMemTable: t, ~committedCheckpointId, change: Change.t<Internal.entity>) => {
188
- let entityKey = change->Change.getEntityId->EntityId.toKey
186
+ let entityKey = inMemTable.entityIdToKey(change->Change.getEntityId)
189
187
  switch inMemTable.latestEntityChangeById->Utils.Dict.dangerouslyGetNonOption(entityKey) {
190
188
  | Some(prev) =>
191
189
  let prevCheckpointId = prev->Change.getCheckpointId
@@ -200,8 +198,8 @@ module Entity = {
200
198
  }
201
199
 
202
200
  switch change {
203
- | Set({entity}) => inMemTable->updateIndexes(~entity)
204
- | Delete({entityId}) => inMemTable->deleteEntityFromIndexes(~entityId=entityId->EntityId.toKey)
201
+ | Set({entity}) => inMemTable->updateIndexes(~entityId=entityKey, ~entity)
202
+ | Delete(_) => inMemTable->deleteEntityFromIndexes(~entityId=entityKey)
205
203
  }
206
204
  inMemTable.latestEntityChangeById->Dict.set(entityKey, change)
207
205
  if change->Change.getCheckpointId > committedCheckpointId {
@@ -214,11 +212,14 @@ module Entity = {
214
212
  let initValue = (
215
213
  inMemTable: t,
216
214
  ~committedCheckpointId,
217
- ~key: string,
215
+ ~entityId: EntityId.t,
218
216
  ~entity: option<Internal.entity>,
219
217
  ) =>
220
- if inMemTable.latestEntityChangeById->Utils.Dict.dangerouslyGetNonOption(key)->Option.isNone {
221
- let entityId = key->EntityId.unsafeOfString
218
+ if (
219
+ inMemTable.latestEntityChangeById
220
+ ->Utils.Dict.dangerouslyGetNonOption(inMemTable.entityIdToKey(entityId))
221
+ ->Option.isNone
222
+ ) {
222
223
  let change: Change.t<Internal.entity> = switch entity {
223
224
  | Some(entity) => Set({entityId, entity, checkpointId: Internal.loadedFromDbCheckpointId})
224
225
  | None => Delete({entityId, checkpointId: Internal.loadedFromDbCheckpointId})
@@ -312,7 +313,7 @@ module Entity = {
312
313
  switch change->mapChangeToEntity {
313
314
  | Some(entity) =>
314
315
  if index.matcher(entity) {
315
- inMemTable->addToIndex(~index, ~entityId=entity->getEntityIdUnsafe)
316
+ inMemTable->addToIndex(~index, ~entityId=inMemTable->getEntityKeyUnsafe(entity))
316
317
  }
317
318
  | None => ()
318
319
  }
@@ -1,7 +1,6 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
3
  import * as Utils from "./Utils.res.mjs";
4
- import * as EntityId from "./EntityId.res.mjs";
5
4
  import * as Internal from "./Internal.res.mjs";
6
5
  import * as EntityFilter from "./db/EntityFilter.res.mjs";
7
6
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
@@ -9,10 +8,10 @@ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
9
8
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
10
9
  import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
11
10
 
12
- function getEntityIdUnsafe(entity) {
11
+ function getEntityKeyUnsafe(self, entity) {
13
12
  let id = entity.id;
14
13
  if (id !== undefined) {
15
- return EntityId.toKey(Primitive_option.valFromOption(id));
14
+ return self.entityIdToKey(Primitive_option.valFromOption(id));
16
15
  } else {
17
16
  return Stdlib_JsError.throwWithMessage("Property 'id' does not exist on expected entity object");
18
17
  }
@@ -28,8 +27,9 @@ function addToIndex(self, index, entityId) {
28
27
  }
29
28
  }
30
29
 
31
- function make() {
30
+ function make(entityIdToKey) {
32
31
  return {
32
+ entityIdToKey: entityIdToKey,
33
33
  latestEntityChangeById: {},
34
34
  changesCount: 0,
35
35
  dirtyIds: new Set(),
@@ -99,11 +99,10 @@ function dropCommittedChanges(self, committedCheckpointId, keepLoadedFromDb) {
99
99
  self.hasIndexes = false;
100
100
  }
101
101
 
102
- function updateIndexes(self, entity) {
102
+ function updateIndexes(self, entityId, entity) {
103
103
  if (!self.hasIndexes) {
104
104
  return;
105
105
  }
106
- let entityId = getEntityIdUnsafe(entity);
107
106
  let indexes = self.indexesByEntityId[entityId];
108
107
  if (indexes !== undefined) {
109
108
  let indexes$1 = Primitive_option.valFromOption(indexes);
@@ -144,7 +143,7 @@ function deleteEntityFromIndexes(self, entityId) {
144
143
  }
145
144
 
146
145
  function set(inMemTable, committedCheckpointId, change) {
147
- let entityKey = EntityId.toKey(change.entityId);
146
+ let entityKey = inMemTable.entityIdToKey(change.entityId);
148
147
  let prev = inMemTable.latestEntityChangeById[entityKey];
149
148
  if (prev !== undefined) {
150
149
  let prevCheckpointId = prev.checkpointId;
@@ -156,9 +155,9 @@ function set(inMemTable, committedCheckpointId, change) {
156
155
  inMemTable.changesCount = inMemTable.changesCount + 1;
157
156
  }
158
157
  if (change.type === "SET") {
159
- updateIndexes(inMemTable, change.entity);
158
+ updateIndexes(inMemTable, entityKey, change.entity);
160
159
  } else {
161
- deleteEntityFromIndexes(inMemTable, EntityId.toKey(change.entityId));
160
+ deleteEntityFromIndexes(inMemTable, entityKey);
162
161
  }
163
162
  inMemTable.latestEntityChangeById[entityKey] = change;
164
163
  if (change.checkpointId > committedCheckpointId) {
@@ -167,18 +166,18 @@ function set(inMemTable, committedCheckpointId, change) {
167
166
  }
168
167
  }
169
168
 
170
- function initValue(inMemTable, committedCheckpointId, key, entity) {
171
- if (!Stdlib_Option.isNone(inMemTable.latestEntityChangeById[key])) {
169
+ function initValue(inMemTable, committedCheckpointId, entityId, entity) {
170
+ if (!Stdlib_Option.isNone(inMemTable.latestEntityChangeById[inMemTable.entityIdToKey(entityId)])) {
172
171
  return;
173
172
  }
174
173
  let change = entity !== undefined ? ({
175
174
  type: "SET",
176
- entityId: key,
175
+ entityId: entityId,
177
176
  entity: entity,
178
177
  checkpointId: Internal.loadedFromDbCheckpointId
179
178
  }) : ({
180
179
  type: "DELETE",
181
- entityId: key,
180
+ entityId: entityId,
182
181
  checkpointId: Internal.loadedFromDbCheckpointId
183
182
  });
184
183
  set(inMemTable, committedCheckpointId, change);
@@ -285,7 +284,7 @@ function addEmptyIndex(inMemTable, filter, table) {
285
284
  Utils.Dict.forEach(inMemTable.latestEntityChangeById, change => {
286
285
  let entity = mapChangeToEntity(change);
287
286
  if (entity !== undefined && index_matcher(entity)) {
288
- return addToIndex(inMemTable, index, getEntityIdUnsafe(entity));
287
+ return addToIndex(inMemTable, index, getEntityKeyUnsafe(inMemTable, entity));
289
288
  }
290
289
  });
291
290
  }
@@ -321,7 +320,7 @@ function tryIndexFromLoadedValues(inMemTable, filter, table) {
321
320
  }
322
321
 
323
322
  let Entity = {
324
- getEntityIdUnsafe: getEntityIdUnsafe,
323
+ getEntityKeyUnsafe: getEntityKeyUnsafe,
325
324
  addToIndex: addToIndex,
326
325
  make: make,
327
326
  snapshotChanges: snapshotChanges,