envio 3.15.0 → 3.16.0-subgraph
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/README.md +40 -34
- package/evm.schema.json +11 -2
- package/fuel.schema.json +10 -1
- package/index.d.ts +6 -0
- package/licenses/README.md +1 -1
- package/package.json +10 -9
- package/src/Batch.res.mjs +5 -5
- package/src/BatchProcessing.res +1 -0
- package/src/BatchProcessing.res.mjs +1 -0
- package/src/Bin.res +7 -0
- package/src/Bin.res.mjs +8 -0
- package/src/ChainFetching.res +450 -16
- package/src/ChainFetching.res.mjs +403 -32
- package/src/ChainState.res +124 -7
- package/src/ChainState.res.mjs +128 -67
- package/src/ChainState.resi +26 -0
- package/src/Clock.res +57 -0
- package/src/Clock.res.mjs +72 -0
- package/src/Config.res +31 -3
- package/src/Config.res.mjs +15 -4
- package/src/ContractRegisterContext.res +3 -6
- package/src/ContractRegisterContext.res.mjs +4 -2
- package/src/Core.res +95 -36
- package/src/Core.res.mjs +90 -1
- package/src/CrossChainState.res +12 -2
- package/src/CrossChainState.res.mjs +3 -3
- package/src/Ecosystem.res +7 -4
- package/src/Ecosystem.res.mjs +8 -8
- package/src/EffectCacheLookup.res +98 -0
- package/src/EffectCacheLookup.res.mjs +116 -0
- package/src/EffectState.res +10 -2
- package/src/EffectState.res.mjs +8 -4
- package/src/EffectState.resi +3 -1
- package/src/EntityId.res +2 -7
- package/src/EntityId.res.mjs +1 -8
- package/src/EntityTables.res +4 -1
- package/src/EntityTables.res.mjs +1 -1
- package/src/Env.res +20 -0
- package/src/Env.res.mjs +16 -0
- package/src/EventProcessing.res +38 -1
- package/src/EventProcessing.res.mjs +28 -1
- package/src/FetchState.res +408 -73
- package/src/FetchState.res.mjs +361 -91
- package/src/FinalizeBackfill.res +2 -4
- package/src/FinalizeBackfill.res.mjs +3 -4
- package/src/HandlerLoader.res +39 -15
- package/src/HandlerLoader.res.mjs +27 -8
- package/src/HandlerRegister.res +4 -0
- package/src/HandlerRegister.res.mjs +5 -2
- package/src/HandlerRegister.resi +2 -0
- package/src/InMemoryTable.res +17 -16
- package/src/InMemoryTable.res.mjs +14 -15
- package/src/IndexerState.res +28 -4
- package/src/IndexerState.res.mjs +19 -6
- package/src/IndexerState.resi +4 -0
- package/src/Internal.res +61 -49
- package/src/Internal.res.mjs +36 -80
- package/src/LoadLayer.res +54 -15
- package/src/LoadLayer.res.mjs +75 -45
- package/src/LoadLayer.resi +26 -0
- package/src/Main.res +14 -1
- package/src/Main.res.mjs +4 -3
- package/src/Persistence.res +3 -23
- package/src/Persistence.res.mjs +3 -3
- package/src/PreloadLookahead.res +202 -0
- package/src/PreloadLookahead.res.mjs +218 -0
- package/src/Rollback.res +0 -1
- package/src/Rollback.res.mjs +1 -1
- package/src/RunScope.res +3 -0
- package/src/SafeCheckpointTracking.res +4 -4
- package/src/SafeCheckpointTracking.res.mjs +1 -1
- package/src/SimulateItems.res +42 -26
- package/src/SimulateItems.res.mjs +40 -16
- package/src/StagedRows.res +106 -0
- package/src/StagedRows.res.mjs +102 -0
- package/src/Staging.res +32 -14
- package/src/Staging.res.mjs +17 -20
- package/src/Storage.res +303 -948
- package/src/Storage.res.mjs +229 -708
- package/src/SubgraphLog.res +19 -0
- package/src/SubgraphLog.res.mjs +26 -0
- package/src/Supervisor.res +75 -56
- package/src/Supervisor.res.mjs +55 -26
- package/src/SyncWrites.res +107 -0
- package/src/SyncWrites.res.mjs +98 -0
- package/src/TestIndexer.res +22 -27
- package/src/TestIndexer.res.mjs +20 -17
- package/src/Throttler.res +15 -8
- package/src/Throttler.res.mjs +9 -6
- package/src/Throttler.resi +1 -1
- package/src/UserContext.res +419 -46
- package/src/UserContext.res.mjs +318 -30
- package/src/Utils.res +17 -6
- package/src/Utils.res.mjs +12 -6
- package/src/Writing.res +0 -1
- package/src/Writing.res.mjs +1 -1
- package/src/bindings/ClickHouse.res +0 -322
- package/src/bindings/ClickHouse.res.mjs +0 -338
- package/src/bindings/StorageClient.res +108 -174
- package/src/bindings/StorageClient.res.mjs +19 -8
- package/src/bindings/Vitest.res +0 -26
- package/src/bindings/Vitest.res.mjs +0 -3
- package/src/db/Table.res +24 -150
- package/src/db/Table.res.mjs +38 -272
- package/src/sources/AddressSet.res +2 -0
- package/src/sources/AddressStore.res +3 -0
- package/src/sources/CallSource.res +274 -0
- package/src/sources/CallSource.res.mjs +196 -0
- package/src/sources/ChainSources.res +2 -0
- package/src/sources/ChainSources.res.mjs +2 -2
- package/src/sources/EvmChain.res +10 -1
- package/src/sources/EvmChain.res.mjs +6 -3
- package/src/sources/EvmHyperSyncSource.res +2 -2
- package/src/sources/EvmHyperSyncSource.res.mjs +1 -1
- package/src/sources/EvmRpcClient.res +13 -3
- package/src/sources/EvmRpcClient.res.mjs +4 -3
- package/src/sources/EvmRpcWs.res +4 -3
- package/src/sources/EvmRpcWs.res.mjs +2 -2
- package/src/sources/HeightFeed.res +31 -10
- package/src/sources/HeightFeed.res.mjs +10 -6
- package/src/sources/HeightStream.res +32 -17
- package/src/sources/HeightStream.res.mjs +10 -10
- package/src/sources/HyperSync.res +2 -1
- package/src/sources/HyperSync.res.mjs +2 -1
- package/src/sources/HyperSyncSSE.res +4 -6
- package/src/sources/HyperSyncSSE.res.mjs +2 -2
- package/src/sources/RpcSource.res +10 -1
- package/src/sources/RpcSource.res.mjs +4 -3
- package/src/sources/Source.res +3 -0
- package/src/sources/SourceManager.res +84 -77
- package/src/sources/SourceManager.res.mjs +17 -20
- package/src/sources/SourceManager.resi +3 -4
- package/src/sources/StartBlockResolver.res +1 -0
- package/src/sources/StartBlockResolver.res.mjs +3 -2
- package/src/sources/SvmHyperSyncSource.res +2 -2
- package/src/sources/SvmHyperSyncSource.res.mjs +1 -1
- package/src/subgraph/abi-types.ts +84 -0
- package/src/subgraph/aggregations.ts +745 -0
- package/src/subgraph/as-package.ts +143 -0
- package/src/subgraph/assemblyscript-regex.ts +67 -0
- package/src/subgraph/assemblyscript.ts +731 -0
- package/src/subgraph/blocks.ts +398 -0
- package/src/subgraph/calls-source.ts +702 -0
- package/src/subgraph/calls.ts +258 -0
- package/src/subgraph/conformance.ts +106 -0
- package/src/subgraph/errors.ts +85 -0
- package/src/subgraph/graft.ts +73 -0
- package/src/subgraph/graph-cli-worker.mjs +35 -0
- package/src/subgraph/graph-cli.ts +900 -0
- package/src/subgraph/graph-node-decimal.ts +199 -0
- package/src/subgraph/graph-ts-enums.ts +28 -0
- package/src/subgraph/graph-ts-types/VERSION +2 -0
- package/src/subgraph/graph-ts-types/chain/arweave.d.ts +70 -0
- package/src/subgraph/graph-ts-types/chain/cosmos.d.ts +327 -0
- package/src/subgraph/graph-ts-types/chain/ethereum.d.ts +233 -0
- package/src/subgraph/graph-ts-types/chain/near.d.ts +253 -0
- package/src/subgraph/graph-ts-types/chain/starknet.d.ts +32 -0
- package/src/subgraph/graph-ts-types/common/collections.d.ts +136 -0
- package/src/subgraph/graph-ts-types/common/conversion.d.ts +11 -0
- package/src/subgraph/graph-ts-types/common/datasource.d.ts +30 -0
- package/src/subgraph/graph-ts-types/common/eager-offset.d.ts +0 -0
- package/src/subgraph/graph-ts-types/common/json.d.ts +17 -0
- package/src/subgraph/graph-ts-types/common/numbers.d.ts +120 -0
- package/src/subgraph/graph-ts-types/common/value.d.ts +120 -0
- package/src/subgraph/graph-ts-types/common/yaml.d.ts +90 -0
- package/src/subgraph/graph-ts-types/global/global.d.ts +194 -0
- package/src/subgraph/graph-ts-types/helper-functions.d.ts +22 -0
- package/src/subgraph/graph-ts-types/index.d.ts +102 -0
- package/src/subgraph/graph-ts.ts +4339 -0
- package/src/subgraph/hosts.ts +554 -0
- package/src/subgraph/log.ts +28 -0
- package/src/subgraph/receipts.ts +184 -0
- package/src/subgraph/register-slots.ts +93 -0
- package/src/subgraph/rollup.ts +109 -0
- package/src/subgraph/rpc.ts +337 -0
- package/src/subgraph/runtime.ts +2059 -0
- package/src/subgraph/scope.ts +176 -0
- package/src/subgraph/stats.ts +42 -0
- package/src/subgraph/template-instances.ts +66 -0
- package/src/subgraph/toolchain.ts +327 -0
- package/src/subgraph/transient.ts +176 -0
- package/svm.schema.json +11 -2
- package/src/PgWriting.res +0 -36
- package/src/PgWriting.res.mjs +0 -34
- package/src/db/IndexDefinition.res +0 -47
- package/src/db/IndexDefinition.res.mjs +0 -67
package/src/ChainState.res
CHANGED
|
@@ -26,6 +26,9 @@ type t = {
|
|
|
26
26
|
// scheduler doesn't re-sum pending queries on every tick. Incremented when
|
|
27
27
|
// queries are dispatched, decremented as their responses land.
|
|
28
28
|
mutable pendingBudget: float,
|
|
29
|
+
// Counts resets of pendingBudget: a hold taken before one has nothing left
|
|
30
|
+
// to release.
|
|
31
|
+
mutable pendingBudgetResets: int,
|
|
29
32
|
// Chain-wide events/block, used to turn a chain's item budget into a target
|
|
30
33
|
// block for query sizing. Seeded from cumulative progress on construction,
|
|
31
34
|
// then smoothed with an EMA on every batch (see applyBatchProgress). None
|
|
@@ -73,6 +76,12 @@ type t = {
|
|
|
73
76
|
// Timestamp of the committed progress block itself. See
|
|
74
77
|
// `Batch.chainAfterBatch.progressBlockTime`.
|
|
75
78
|
mutable committedProgressBlockTime: option<int>,
|
|
79
|
+
// Subgraph mode, with a block handler that has a register pass: the chain's
|
|
80
|
+
// register work runs one unit at a time, a response's passes or a run of
|
|
81
|
+
// block passes alone, so a pass never runs ahead of a block pass of an
|
|
82
|
+
// earlier block. Settles when the latest unit is applied.
|
|
83
|
+
mutable blockRegisterTail: promise<unit>,
|
|
84
|
+
mutable blockRegisterRuns: int,
|
|
76
85
|
}
|
|
77
86
|
|
|
78
87
|
// The chain's config-declared addresses as storage rows: what a clean run
|
|
@@ -167,6 +176,7 @@ let make = (
|
|
|
167
176
|
processingBlockNumber: committedProgressBlockNumber,
|
|
168
177
|
numEventsProcessed,
|
|
169
178
|
pendingBudget: 0.,
|
|
179
|
+
pendingBudgetResets: 0,
|
|
170
180
|
chainDensity,
|
|
171
181
|
safeCheckpointTracking,
|
|
172
182
|
shouldRollbackOnReorg,
|
|
@@ -187,6 +197,8 @@ let make = (
|
|
|
187
197
|
rollbackTargetBlock: None,
|
|
188
198
|
progressLatencyMs: None,
|
|
189
199
|
committedProgressBlockTime,
|
|
200
|
+
blockRegisterTail: Promise.resolve(),
|
|
201
|
+
blockRegisterRuns: 0,
|
|
190
202
|
}
|
|
191
203
|
}
|
|
192
204
|
|
|
@@ -308,13 +320,19 @@ let makeInternal = (
|
|
|
308
320
|
// FIXME: Shouldn't set with full history
|
|
309
321
|
// The resumed depth, not the config one: the pre-threshold lag must match
|
|
310
322
|
// the window reorg detection compares, or a reduced config depth would let
|
|
311
|
-
// fetching enter the stored rollback window without history.
|
|
323
|
+
// fetching enter the stored rollback window without history. One block
|
|
324
|
+
// more than the window, so the last pre-threshold page ends below it and
|
|
325
|
+
// the window's first block is read by a page that knows it is reorgable.
|
|
312
326
|
~blockLag=Pervasives.max(
|
|
313
|
-
!config.shouldRollbackOnReorg || isInReorgThreshold
|
|
327
|
+
!config.shouldRollbackOnReorg || isInReorgThreshold || maxReorgDepth == 0
|
|
328
|
+
? 0
|
|
329
|
+
: maxReorgDepth + 1,
|
|
314
330
|
chainConfig.blockLag,
|
|
315
331
|
),
|
|
332
|
+
~blockThrottle=chainConfig.blockThrottle,
|
|
316
333
|
~onBlockRegistrations,
|
|
317
334
|
~firstEventBlock,
|
|
335
|
+
~deferCreationBlockEvents=config.subgraph->Option.isSome,
|
|
318
336
|
// Fuel and SVM route through the address store like EVM does, so the
|
|
319
337
|
// client-side path works for them - but their address counts are nowhere
|
|
320
338
|
// near the threshold, so switching would only trade a server-side filter
|
|
@@ -349,6 +367,7 @@ let makeInternal = (
|
|
|
349
367
|
~blockStore,
|
|
350
368
|
~transactionStore,
|
|
351
369
|
~logger,
|
|
370
|
+
~maxAddressesPerRequest=config.maxAddrInPartition,
|
|
352
371
|
)
|
|
353
372
|
|
|
354
373
|
// Seed the stored reorg checkpoints (hash-only rows) so detection resumes
|
|
@@ -459,6 +478,7 @@ let logger = (cs: t) => cs.logger
|
|
|
459
478
|
let blockStore = (cs: t) => cs.blockStore
|
|
460
479
|
let transactionStore = (cs: t) => cs.transactionStore
|
|
461
480
|
let shouldChecksum = (cs: t) => cs.shouldChecksum
|
|
481
|
+
let fetchState = (cs: t) => cs.fetchState
|
|
462
482
|
let entities = (cs: t) => cs.entities
|
|
463
483
|
|
|
464
484
|
// Rollback discards every uncommitted change, so this chain's partition is
|
|
@@ -545,8 +565,70 @@ let startFetchingQueries = (cs: t, ~queries: array<FetchState.query>) => {
|
|
|
545
565
|
let resetPendingQueries = (cs: t) => {
|
|
546
566
|
cs.fetchState = cs.fetchState->FetchState.resetPendingQueries
|
|
547
567
|
cs.pendingBudget = 0.
|
|
568
|
+
cs.pendingBudgetResets = cs.pendingBudgetResets + 1
|
|
548
569
|
}
|
|
549
570
|
|
|
571
|
+
// A response that landed with more items than its query was priced at keeps the
|
|
572
|
+
// excess reserved while it waits for its register passes: until then its items
|
|
573
|
+
// are neither in the buffer nor in the query's estimate, and the pool would
|
|
574
|
+
// admit the next queries on top of them. Released when the response is applied.
|
|
575
|
+
type heldBudget = {items: int, resets: int}
|
|
576
|
+
|
|
577
|
+
let holdExtraBudget = (cs: t, ~items) => {
|
|
578
|
+
cs.pendingBudget = cs.pendingBudget +. items->Int.toFloat
|
|
579
|
+
{items, resets: cs.pendingBudgetResets}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
let releaseExtraBudget = (cs: t, held: heldBudget) =>
|
|
583
|
+
if held.resets === cs.pendingBudgetResets {
|
|
584
|
+
cs.pendingBudget = Pervasives.max(0., cs.pendingBudget -. held.items->Int.toFloat)
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
let hasBlockRegisters = (cs: t) => cs.fetchState.blockRegisterThrough !== None
|
|
588
|
+
let isBlockRegisterIdle = (cs: t) => cs.blockRegisterRuns === 0
|
|
589
|
+
|
|
590
|
+
// Queues a unit of register work behind the chain's latest one. Returns the
|
|
591
|
+
// promise to wait on before running it, and the callback that settles it,
|
|
592
|
+
// which must be called however the unit ends.
|
|
593
|
+
let queueBlockRegisterRun = (cs: t) => {
|
|
594
|
+
let previous = cs.blockRegisterTail
|
|
595
|
+
let settle = ref(() => ())
|
|
596
|
+
cs.blockRegisterTail = Promise.make((resolve, _) => settle := resolve)
|
|
597
|
+
cs.blockRegisterRuns = cs.blockRegisterRuns + 1
|
|
598
|
+
let finish = () => {
|
|
599
|
+
cs.blockRegisterRuns = cs.blockRegisterRuns - 1
|
|
600
|
+
settle.contents()
|
|
601
|
+
}
|
|
602
|
+
(previous, finish)
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
let canAdvanceBlockRegisters = (cs: t, ~through) =>
|
|
606
|
+
cs.fetchState->FetchState.canAdvanceBlockRegisters(~through)
|
|
607
|
+
let nextBlockRegisterItems = (cs: t, ~through, ~limit) =>
|
|
608
|
+
cs.fetchState->FetchState.nextBlockRegisterItems(~through, ~limit)
|
|
609
|
+
let blockRegisterItemsAfter = (cs: t, ~after, ~through, ~limit) =>
|
|
610
|
+
cs.fetchState->FetchState.blockRegisterItemsAfter(~after, ~through, ~limit)
|
|
611
|
+
let blockRegisterThrough = (cs: t) => cs.fetchState.blockRegisterThrough
|
|
612
|
+
let blockRegisterFrontier = (cs: t) => cs.fetchState->FetchState.blockRegisterFrontier
|
|
613
|
+
|
|
614
|
+
// Applies what a run of block passes alone registered, then lets block items
|
|
615
|
+
// through its last block be generated.
|
|
616
|
+
let applyBlockRegisters = (cs: t, ~registrations, ~through) => {
|
|
617
|
+
let fs = switch registrations {
|
|
618
|
+
| [] => cs.fetchState
|
|
619
|
+
| _ =>
|
|
620
|
+
cs.fetchState->FetchState.registerDynamicContracts(
|
|
621
|
+
~addressStore=cs.addressStore,
|
|
622
|
+
~onNotice=notice => cs.logger->logFetchStateNotice(notice),
|
|
623
|
+
registrations,
|
|
624
|
+
)
|
|
625
|
+
}
|
|
626
|
+
cs.fetchState = fs->FetchState.setBlockRegisterThrough(~through)
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
let markBlockRegistered = (cs: t, ~through) =>
|
|
630
|
+
cs.fetchState = cs.fetchState->FetchState.setBlockRegisterThrough(~through)
|
|
631
|
+
|
|
550
632
|
let isReady = (cs: t) => cs.timestampCaughtUpToHeadOrEndblock !== None
|
|
551
633
|
|
|
552
634
|
// Block span over which a batch fully replaces the chain density estimate;
|
|
@@ -776,10 +858,10 @@ let takeFinished = (cs: t) =>
|
|
|
776
858
|
}
|
|
777
859
|
}
|
|
778
860
|
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
861
|
+
// The block reorg detection compares from is itself in the window, so the
|
|
862
|
+
// highest block below it is the one before, where catch-up stops.
|
|
863
|
+
let getHighestBlockBelowThreshold = (cs: t): int =>
|
|
864
|
+
Pervasives.max(cs.fetchState.knownHeight - cs.maxReorgDepth - 1, 0)
|
|
783
865
|
|
|
784
866
|
// True once the fetch frontier has reached the head/endBlock for this chain.
|
|
785
867
|
let isFetchingAtHead = (cs: t) => cs.fetchState->FetchState.isFetchingAtHead
|
|
@@ -977,6 +1059,34 @@ let materializePageItems = async (
|
|
|
977
1059
|
))
|
|
978
1060
|
}
|
|
979
1061
|
|
|
1062
|
+
// Near the head: each new block in as few queries as possible, of a shape that
|
|
1063
|
+
// stays the same while contracts keep being registered. Once every partition is
|
|
1064
|
+
// close enough, the chain's dynamic contracts leave the server-side address
|
|
1065
|
+
// filter for the rest of the run.
|
|
1066
|
+
//
|
|
1067
|
+
// Judged only when the chain learns a newer head. An RPC response repeats the
|
|
1068
|
+
// height its query went out with, which can be hours old by the end of a
|
|
1069
|
+
// backfill: judged against it, every block produced since would be fetched
|
|
1070
|
+
// address-free.
|
|
1071
|
+
let switchDynamicContractsToClientSideFilteringNearHead = (cs: t) =>
|
|
1072
|
+
if (
|
|
1073
|
+
!cs.fetchState.clientFilterPolicy.allDynamicContracts &&
|
|
1074
|
+
cs.fetchState->FetchState.isWithinHeadClientFilteringDistance
|
|
1075
|
+
) {
|
|
1076
|
+
cs.fetchState =
|
|
1077
|
+
cs.fetchState->FetchState.switchDynamicContractsToClientSideFiltering(
|
|
1078
|
+
~addressStore=cs.addressStore,
|
|
1079
|
+
)
|
|
1080
|
+
if cs.onEventRegistrations->Array.some(registration => registration.contractRegister !== None) {
|
|
1081
|
+
cs.logger->Logger.trace(
|
|
1082
|
+
"Near the head: dynamic contracts are fetched without their addresses for the rest of the run, and filtered on our side.",
|
|
1083
|
+
~params={
|
|
1084
|
+
"contractNames": cs.fetchState.optimizedPartitions.dynamicContracts->Utils.Set.toArray,
|
|
1085
|
+
},
|
|
1086
|
+
)
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
|
|
980
1090
|
let handleQueryResult = (
|
|
981
1091
|
cs: t,
|
|
982
1092
|
~query: FetchState.query,
|
|
@@ -1000,6 +1110,7 @@ let handleQueryResult = (
|
|
|
1000
1110
|
)
|
|
1001
1111
|
}
|
|
1002
1112
|
|
|
1113
|
+
let previousKnownHeight = cs.fetchState.knownHeight
|
|
1003
1114
|
cs.fetchState =
|
|
1004
1115
|
fs
|
|
1005
1116
|
->FetchState.handleQueryResult(~query, ~latestFetchedBlock, ~newItems)
|
|
@@ -1007,6 +1118,9 @@ let handleQueryResult = (
|
|
|
1007
1118
|
|
|
1008
1119
|
// The query is no longer in flight, so release its reservation.
|
|
1009
1120
|
cs.pendingBudget = Pervasives.max(0., cs.pendingBudget -. query.itemsEst->Int.toFloat)
|
|
1121
|
+
if cs.fetchState.knownHeight > previousKnownHeight {
|
|
1122
|
+
cs->switchDynamicContractsToClientSideFilteringNearHead
|
|
1123
|
+
}
|
|
1010
1124
|
}
|
|
1011
1125
|
|
|
1012
1126
|
// Run reorg detection against a fetch response by merging its block-store page
|
|
@@ -1041,8 +1155,11 @@ let prepareReorg = (cs: t, ~eventsProcessedDiff) => {
|
|
|
1041
1155
|
cs->resetPendingQueries
|
|
1042
1156
|
}
|
|
1043
1157
|
|
|
1044
|
-
|
|
1158
|
+
// A chain waiting at its head goes address-free before querying the new block.
|
|
1159
|
+
let updateKnownHeight = (cs: t, ~knownHeight) => {
|
|
1045
1160
|
cs.fetchState = cs.fetchState->FetchState.updateKnownHeight(~knownHeight)
|
|
1161
|
+
cs->switchDynamicContractsToClientSideFilteringNearHead
|
|
1162
|
+
}
|
|
1046
1163
|
|
|
1047
1164
|
// In auto-exit mode, pin the endBlock to the earliest observed event block.
|
|
1048
1165
|
let setEndBlockToFirstEvent = (cs: t, ~blockNumber) =>
|
package/src/ChainState.res.mjs
CHANGED
|
@@ -83,6 +83,7 @@ function make(chainConfig, fetchState, onEventRegistrationsOpt, addressStore, so
|
|
|
83
83
|
processingBlockNumber: committedProgressBlockNumber,
|
|
84
84
|
numEventsProcessed: numEventsProcessed,
|
|
85
85
|
pendingBudget: 0,
|
|
86
|
+
pendingBudgetResets: 0,
|
|
86
87
|
chainDensity: chainDensity,
|
|
87
88
|
entities: EntityTables.make(perChainEntities),
|
|
88
89
|
safeCheckpointTracking: safeCheckpointTracking,
|
|
@@ -103,7 +104,9 @@ function make(chainConfig, fetchState, onEventRegistrationsOpt, addressStore, so
|
|
|
103
104
|
reorgDetectedBlock: undefined,
|
|
104
105
|
rollbackTargetBlock: undefined,
|
|
105
106
|
progressLatencyMs: undefined,
|
|
106
|
-
committedProgressBlockTime: committedProgressBlockTime
|
|
107
|
+
committedProgressBlockTime: committedProgressBlockTime,
|
|
108
|
+
blockRegisterTail: Promise.resolve(),
|
|
109
|
+
blockRegisterRuns: 0
|
|
107
110
|
};
|
|
108
111
|
}
|
|
109
112
|
|
|
@@ -159,16 +162,11 @@ function makeFromDbState(chainConfig, resumedChainState, reorgCheckpoints, isInR
|
|
|
159
162
|
onBlockRegistrations: []
|
|
160
163
|
});
|
|
161
164
|
let onEventRegistrations = match.onEventRegistrations;
|
|
162
|
-
let onBlockRegistrations = match.onBlockRegistrations.map(reg =>
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
endBlock: reg.endBlock,
|
|
168
|
-
interval: reg.interval,
|
|
169
|
-
handler: reg.handler,
|
|
170
|
-
blockFieldMask: reg.blockFieldMask
|
|
171
|
-
}));
|
|
165
|
+
let onBlockRegistrations = match.onBlockRegistrations.map(reg => {
|
|
166
|
+
let newrecord = {...reg};
|
|
167
|
+
newrecord.startBlock = Stdlib_Option.getOr(reg.startBlock, startBlock);
|
|
168
|
+
return newrecord;
|
|
169
|
+
});
|
|
172
170
|
chainConfig.contracts.forEach(contract => {
|
|
173
171
|
let contractStartBlock = contract.startBlock;
|
|
174
172
|
if (contractStartBlock !== undefined && contractStartBlock < startBlock) {
|
|
@@ -188,7 +186,7 @@ function makeFromDbState(chainConfig, resumedChainState, reorgCheckpoints, isInR
|
|
|
188
186
|
tmp = undefined;
|
|
189
187
|
break;
|
|
190
188
|
}
|
|
191
|
-
let fetchState = FetchState.make(startBlock, endBlock, onEventRegistrations, addressStore, addressRows, config.maxAddrInPartition, chainConfig.id, notice => logFetchStateNotice(logger$1, notice), (config.batchSize << 1), knownHeight, progressBlockNumber, onBlockRegistrations, Primitive_int.max(!config.shouldRollbackOnReorg || isInReorgThreshold ? 0 : maxReorgDepth, chainConfig.blockLag), Primitive_option.some(firstEventBlock), Primitive_option.some(tmp), isResumed);
|
|
189
|
+
let fetchState = FetchState.make(startBlock, endBlock, onEventRegistrations, addressStore, addressRows, config.maxAddrInPartition, chainConfig.id, notice => logFetchStateNotice(logger$1, notice), (config.batchSize << 1), knownHeight, progressBlockNumber, onBlockRegistrations, Primitive_int.max(!config.shouldRollbackOnReorg || isInReorgThreshold || maxReorgDepth === 0 ? 0 : maxReorgDepth + 1 | 0, chainConfig.blockLag), chainConfig.blockThrottle, Primitive_option.some(firstEventBlock), Primitive_option.some(tmp), isResumed, Stdlib_Option.isSome(config.subgraph));
|
|
192
190
|
let chainReorgCheckpoints = Stdlib_Array.filterMap(reorgCheckpoints, reorgCheckpoint => {
|
|
193
191
|
if (reorgCheckpoint.chain_id === chainConfig.id) {
|
|
194
192
|
return reorgCheckpoint;
|
|
@@ -196,7 +194,7 @@ function makeFromDbState(chainConfig, resumedChainState, reorgCheckpoints, isInR
|
|
|
196
194
|
});
|
|
197
195
|
let blockStore = BlockStore.make(config.ecosystem.name);
|
|
198
196
|
let transactionStore = TransactionStore.make(config.ecosystem.name);
|
|
199
|
-
let sources = ChainSources.make(chainConfig, onEventRegistrations, onBlockRegistrations, addressStore, lowercaseAddresses, blockStore, transactionStore, logger$1);
|
|
197
|
+
let sources = ChainSources.make(chainConfig, onEventRegistrations, onBlockRegistrations, addressStore, lowercaseAddresses, blockStore, transactionStore, logger$1, config.maxAddrInPartition);
|
|
200
198
|
if (Utils.$$Array.notEmpty(chainReorgCheckpoints)) {
|
|
201
199
|
let seedPage = BlockStore.fromJs(chainReorgCheckpoints.map(cp => ({
|
|
202
200
|
blockNumber: cp.block_number,
|
|
@@ -225,6 +223,10 @@ function shouldChecksum(cs) {
|
|
|
225
223
|
return cs.shouldChecksum;
|
|
226
224
|
}
|
|
227
225
|
|
|
226
|
+
function fetchState(cs) {
|
|
227
|
+
return cs.fetchState;
|
|
228
|
+
}
|
|
229
|
+
|
|
228
230
|
function entities(cs) {
|
|
229
231
|
return cs.entities;
|
|
230
232
|
}
|
|
@@ -338,6 +340,78 @@ function startFetchingQueries(cs, queries) {
|
|
|
338
340
|
function resetPendingQueries(cs) {
|
|
339
341
|
cs.fetchState = FetchState.resetPendingQueries(cs.fetchState);
|
|
340
342
|
cs.pendingBudget = 0;
|
|
343
|
+
cs.pendingBudgetResets = cs.pendingBudgetResets + 1 | 0;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function holdExtraBudget(cs, items) {
|
|
347
|
+
cs.pendingBudget = cs.pendingBudget + items;
|
|
348
|
+
return {
|
|
349
|
+
items: items,
|
|
350
|
+
resets: cs.pendingBudgetResets
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function releaseExtraBudget(cs, held) {
|
|
355
|
+
if (held.resets === cs.pendingBudgetResets) {
|
|
356
|
+
cs.pendingBudget = Primitive_float.max(0, cs.pendingBudget - held.items);
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function hasBlockRegisters(cs) {
|
|
362
|
+
return cs.fetchState.blockRegisterThrough !== undefined;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function isBlockRegisterIdle(cs) {
|
|
366
|
+
return cs.blockRegisterRuns === 0;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function queueBlockRegisterRun(cs) {
|
|
370
|
+
let previous = cs.blockRegisterTail;
|
|
371
|
+
let settle = {
|
|
372
|
+
contents: () => {}
|
|
373
|
+
};
|
|
374
|
+
cs.blockRegisterTail = new Promise((resolve, param) => {
|
|
375
|
+
settle.contents = resolve;
|
|
376
|
+
});
|
|
377
|
+
cs.blockRegisterRuns = cs.blockRegisterRuns + 1 | 0;
|
|
378
|
+
let finish = () => {
|
|
379
|
+
cs.blockRegisterRuns = cs.blockRegisterRuns - 1 | 0;
|
|
380
|
+
settle.contents();
|
|
381
|
+
};
|
|
382
|
+
return [
|
|
383
|
+
previous,
|
|
384
|
+
finish
|
|
385
|
+
];
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function canAdvanceBlockRegisters(cs, through) {
|
|
389
|
+
return FetchState.canAdvanceBlockRegisters(cs.fetchState, through);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function nextBlockRegisterItems(cs, through, limit) {
|
|
393
|
+
return FetchState.nextBlockRegisterItems(cs.fetchState, through, limit);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function blockRegisterItemsAfter(cs, after, through, limit) {
|
|
397
|
+
return FetchState.blockRegisterItemsAfter(cs.fetchState, after, through, limit);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function blockRegisterThrough(cs) {
|
|
401
|
+
return cs.fetchState.blockRegisterThrough;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function blockRegisterFrontier(cs) {
|
|
405
|
+
return FetchState.blockRegisterFrontier(cs.fetchState);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function applyBlockRegisters(cs, registrations, through) {
|
|
409
|
+
let fs = registrations.length !== 0 ? FetchState.registerDynamicContracts(cs.fetchState, cs.addressStore, notice => logFetchStateNotice(cs.logger, notice), undefined, registrations) : cs.fetchState;
|
|
410
|
+
cs.fetchState = FetchState.setBlockRegisterThrough(fs, through);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function markBlockRegistered(cs, through) {
|
|
414
|
+
cs.fetchState = FetchState.setBlockRegisterThrough(cs.fetchState, through);
|
|
341
415
|
}
|
|
342
416
|
|
|
343
417
|
function isReady(cs) {
|
|
@@ -494,12 +568,7 @@ function takeFinished(cs) {
|
|
|
494
568
|
}
|
|
495
569
|
|
|
496
570
|
function getHighestBlockBelowThreshold(cs) {
|
|
497
|
-
|
|
498
|
-
if (highestBlockBelowThreshold < 0) {
|
|
499
|
-
return 0;
|
|
500
|
-
} else {
|
|
501
|
-
return highestBlockBelowThreshold;
|
|
502
|
-
}
|
|
571
|
+
return Primitive_int.max((cs.fetchState.knownHeight - cs.maxReorgDepth | 0) - 1 | 0, 0);
|
|
503
572
|
}
|
|
504
573
|
|
|
505
574
|
function isFetchingAtHead(cs) {
|
|
@@ -647,10 +716,27 @@ async function materializePageItems(items, transactionStore, blockStore, shouldC
|
|
|
647
716
|
]);
|
|
648
717
|
}
|
|
649
718
|
|
|
719
|
+
function switchDynamicContractsToClientSideFilteringNearHead(cs) {
|
|
720
|
+
if (!cs.fetchState.clientFilterPolicy.allDynamicContracts && FetchState.isWithinHeadClientFilteringDistance(cs.fetchState)) {
|
|
721
|
+
cs.fetchState = FetchState.switchDynamicContractsToClientSideFiltering(cs.fetchState, cs.addressStore);
|
|
722
|
+
if (cs.onEventRegistrations.some(registration => registration.contractRegister !== undefined)) {
|
|
723
|
+
return Logger.trace(cs.logger, "Near the head: dynamic contracts are fetched without their addresses for the rest of the run, and filtered on our side.", {
|
|
724
|
+
contractNames: Array.from(cs.fetchState.optimizedPartitions.dynamicContracts)
|
|
725
|
+
});
|
|
726
|
+
} else {
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
650
732
|
function handleQueryResult(cs, query, newItems, newRegistrations, latestFetchedBlock, knownHeight) {
|
|
651
733
|
let fs = newRegistrations.length !== 0 ? FetchState.registerDynamicContracts(cs.fetchState, cs.addressStore, notice => logFetchStateNotice(cs.logger, notice), Primitive_int.max(knownHeight, latestFetchedBlock), newRegistrations) : cs.fetchState;
|
|
734
|
+
let previousKnownHeight = cs.fetchState.knownHeight;
|
|
652
735
|
cs.fetchState = FetchState.updateKnownHeight(FetchState.handleQueryResult(fs, query, latestFetchedBlock, newItems), knownHeight);
|
|
653
736
|
cs.pendingBudget = Primitive_float.max(0, cs.pendingBudget - query.itemsEst);
|
|
737
|
+
if (cs.fetchState.knownHeight > previousKnownHeight) {
|
|
738
|
+
return switchDynamicContractsToClientSideFilteringNearHead(cs);
|
|
739
|
+
}
|
|
654
740
|
}
|
|
655
741
|
|
|
656
742
|
function registerReorgGuard(cs, blockStore, knownHeight) {
|
|
@@ -683,6 +769,7 @@ function prepareReorg(cs, eventsProcessedDiff) {
|
|
|
683
769
|
|
|
684
770
|
function updateKnownHeight(cs, knownHeight) {
|
|
685
771
|
cs.fetchState = FetchState.updateKnownHeight(cs.fetchState, knownHeight);
|
|
772
|
+
switchDynamicContractsToClientSideFilteringNearHead(cs);
|
|
686
773
|
}
|
|
687
774
|
|
|
688
775
|
function setEndBlockToFirstEvent(cs, blockNumber) {
|
|
@@ -691,40 +778,14 @@ function setEndBlockToFirstEvent(cs, blockNumber) {
|
|
|
691
778
|
if (blockNumber >= currentEndBlock) {
|
|
692
779
|
return;
|
|
693
780
|
}
|
|
694
|
-
let
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
startBlock: init.startBlock,
|
|
698
|
-
endBlock: blockNumber,
|
|
699
|
-
normalSelection: init.normalSelection,
|
|
700
|
-
chainId: init.chainId,
|
|
701
|
-
latestOnBlockBlockNumber: init.latestOnBlockBlockNumber,
|
|
702
|
-
blockLag: init.blockLag,
|
|
703
|
-
buffer: init.buffer,
|
|
704
|
-
maxOnBlockBufferSize: init.maxOnBlockBufferSize,
|
|
705
|
-
onBlockRegistrations: init.onBlockRegistrations,
|
|
706
|
-
knownHeight: init.knownHeight,
|
|
707
|
-
firstEventBlock: init.firstEventBlock,
|
|
708
|
-
clientFilterAddressThreshold: init.clientFilterAddressThreshold
|
|
709
|
-
};
|
|
781
|
+
let newrecord = {...cs.fetchState};
|
|
782
|
+
newrecord.endBlock = blockNumber;
|
|
783
|
+
cs.fetchState = newrecord;
|
|
710
784
|
return;
|
|
711
785
|
}
|
|
712
|
-
let
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
startBlock: init$1.startBlock,
|
|
716
|
-
endBlock: blockNumber,
|
|
717
|
-
normalSelection: init$1.normalSelection,
|
|
718
|
-
chainId: init$1.chainId,
|
|
719
|
-
latestOnBlockBlockNumber: init$1.latestOnBlockBlockNumber,
|
|
720
|
-
blockLag: init$1.blockLag,
|
|
721
|
-
buffer: init$1.buffer,
|
|
722
|
-
maxOnBlockBufferSize: init$1.maxOnBlockBufferSize,
|
|
723
|
-
onBlockRegistrations: init$1.onBlockRegistrations,
|
|
724
|
-
knownHeight: init$1.knownHeight,
|
|
725
|
-
firstEventBlock: init$1.firstEventBlock,
|
|
726
|
-
clientFilterAddressThreshold: init$1.clientFilterAddressThreshold
|
|
727
|
-
};
|
|
786
|
+
let newrecord$1 = {...cs.fetchState};
|
|
787
|
+
newrecord$1.endBlock = blockNumber;
|
|
788
|
+
cs.fetchState = newrecord$1;
|
|
728
789
|
}
|
|
729
790
|
|
|
730
791
|
function enterReorgThreshold(cs) {
|
|
@@ -872,22 +933,9 @@ function applyBatchProgress(cs, batch, blockTimestampName) {
|
|
|
872
933
|
if (match === undefined) {
|
|
873
934
|
let firstEventBlock = Batch.findFirstEventBlockNumber(batch, chainId);
|
|
874
935
|
if (firstEventBlock !== undefined) {
|
|
875
|
-
let
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
startBlock: init.startBlock,
|
|
879
|
-
endBlock: init.endBlock,
|
|
880
|
-
normalSelection: init.normalSelection,
|
|
881
|
-
chainId: init.chainId,
|
|
882
|
-
latestOnBlockBlockNumber: init.latestOnBlockBlockNumber,
|
|
883
|
-
blockLag: init.blockLag,
|
|
884
|
-
buffer: init.buffer,
|
|
885
|
-
maxOnBlockBufferSize: init.maxOnBlockBufferSize,
|
|
886
|
-
onBlockRegistrations: init.onBlockRegistrations,
|
|
887
|
-
knownHeight: init.knownHeight,
|
|
888
|
-
firstEventBlock: firstEventBlock,
|
|
889
|
-
clientFilterAddressThreshold: init.clientFilterAddressThreshold
|
|
890
|
-
};
|
|
936
|
+
let newrecord = {...cs.fetchState};
|
|
937
|
+
newrecord.firstEventBlock = firstEventBlock;
|
|
938
|
+
cs.fetchState = newrecord;
|
|
891
939
|
}
|
|
892
940
|
}
|
|
893
941
|
let deltaBlocks = chainAfterBatch.progressBlockNumber - cs.committedProgressBlockNumber | 0;
|
|
@@ -990,6 +1038,7 @@ export {
|
|
|
990
1038
|
blockStore,
|
|
991
1039
|
transactionStore,
|
|
992
1040
|
shouldChecksum,
|
|
1041
|
+
fetchState,
|
|
993
1042
|
entities,
|
|
994
1043
|
resetEntities,
|
|
995
1044
|
sourceManager,
|
|
@@ -1002,6 +1051,18 @@ export {
|
|
|
1002
1051
|
committedProgressBlockTime,
|
|
1003
1052
|
numEventsProcessed,
|
|
1004
1053
|
pendingBudget,
|
|
1054
|
+
holdExtraBudget,
|
|
1055
|
+
releaseExtraBudget,
|
|
1056
|
+
hasBlockRegisters,
|
|
1057
|
+
isBlockRegisterIdle,
|
|
1058
|
+
queueBlockRegisterRun,
|
|
1059
|
+
canAdvanceBlockRegisters,
|
|
1060
|
+
nextBlockRegisterItems,
|
|
1061
|
+
blockRegisterItemsAfter,
|
|
1062
|
+
blockRegisterThrough,
|
|
1063
|
+
blockRegisterFrontier,
|
|
1064
|
+
applyBlockRegisters,
|
|
1065
|
+
markBlockRegistered,
|
|
1005
1066
|
startFetchingQueries,
|
|
1006
1067
|
timestampCaughtUpToHeadOrEndblock,
|
|
1007
1068
|
recordBlockRangeFetch,
|
package/src/ChainState.resi
CHANGED
|
@@ -58,6 +58,8 @@ let logger: t => Logger.t
|
|
|
58
58
|
let blockStore: t => BlockStore.t
|
|
59
59
|
let transactionStore: t => TransactionStore.t
|
|
60
60
|
let shouldChecksum: t => bool
|
|
61
|
+
// The chain's fetch state, read-only: for the round a registration belongs to.
|
|
62
|
+
let fetchState: t => FetchState.t
|
|
61
63
|
let entities: t => EntityTables.t
|
|
62
64
|
let resetEntities: (t, ~perChainEntities: array<Internal.entityConfig>) => unit
|
|
63
65
|
let sourceManager: t => SourceManager.t
|
|
@@ -75,6 +77,30 @@ let committedProgressBlockNumber: t => int
|
|
|
75
77
|
let committedProgressBlockTime: t => option<int>
|
|
76
78
|
let numEventsProcessed: t => float
|
|
77
79
|
let pendingBudget: t => float
|
|
80
|
+
type heldBudget
|
|
81
|
+
let holdExtraBudget: (t, ~items: int) => heldBudget
|
|
82
|
+
let releaseExtraBudget: (t, heldBudget) => unit
|
|
83
|
+
// Subgraph mode: the register passes of block handlers that create data
|
|
84
|
+
// sources. See `FetchState.blockRegisterThrough`.
|
|
85
|
+
let hasBlockRegisters: t => bool
|
|
86
|
+
let isBlockRegisterIdle: t => bool
|
|
87
|
+
let queueBlockRegisterRun: t => (promise<unit>, unit => unit)
|
|
88
|
+
let canAdvanceBlockRegisters: (t, ~through: int) => bool
|
|
89
|
+
let nextBlockRegisterItems: (t, ~through: int, ~limit: int) => option<(array<Internal.item>, int)>
|
|
90
|
+
let blockRegisterItemsAfter: (
|
|
91
|
+
t,
|
|
92
|
+
~after: int,
|
|
93
|
+
~through: int,
|
|
94
|
+
~limit: int,
|
|
95
|
+
) => option<(array<Internal.item>, int)>
|
|
96
|
+
let blockRegisterThrough: t => option<int>
|
|
97
|
+
let blockRegisterFrontier: t => int
|
|
98
|
+
let applyBlockRegisters: (
|
|
99
|
+
t,
|
|
100
|
+
~registrations: array<AddressStore.registration>,
|
|
101
|
+
~through: int,
|
|
102
|
+
) => unit
|
|
103
|
+
let markBlockRegistered: (t, ~through: int) => unit
|
|
78
104
|
let startFetchingQueries: (t, ~queries: array<FetchState.query>) => unit
|
|
79
105
|
let timestampCaughtUpToHeadOrEndblock: t => option<Date.t>
|
|
80
106
|
|
package/src/Clock.res
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// What the source layer reads time from and arms its timers on. A test can hand
|
|
2
|
+
// it a clock that only moves when the test moves it, which takes the scheduler
|
|
3
|
+
// out of every race between a timer and the step a test is taking. The label
|
|
4
|
+
// names a timer in that clock's timeline; the real clock ignores it.
|
|
5
|
+
type timer
|
|
6
|
+
|
|
7
|
+
type t = {
|
|
8
|
+
now: unit => float,
|
|
9
|
+
setTimeout: (~label: string, unit => unit, int) => timer,
|
|
10
|
+
clearTimeout: timer => unit,
|
|
11
|
+
random: unit => float,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Test-only. Set while a test drives a run on virtual time, so code that reaches
|
|
15
|
+
// past the clock it was given fails where it does, instead of arming a timer
|
|
16
|
+
// the test can neither see nor move.
|
|
17
|
+
let refuseReal = ref(false)
|
|
18
|
+
|
|
19
|
+
let refused = what =>
|
|
20
|
+
JsError.throwWithMessage(
|
|
21
|
+
`Clock.real ${what} on virtual time. Use the clock the code was given, so advanceTime can see and move it.`,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
// Monotonic: every reading is only ever compared with another, and a wall
|
|
25
|
+
// clock corrected mid-run would make a connection look like it served for
|
|
26
|
+
// minutes, or a benched source look recovered.
|
|
27
|
+
let real: t = {
|
|
28
|
+
now: () => refuseReal.contents ? refused("read the time") : Performance.now(),
|
|
29
|
+
// A zero delay is a turn of the event loop on either clock.
|
|
30
|
+
setTimeout: (~label, callback, millis) =>
|
|
31
|
+
refuseReal.contents && millis > 0
|
|
32
|
+
? refused(`armed "${label}"`)
|
|
33
|
+
: setTimeout(callback, millis)->(Utils.magic: timeoutId => timer),
|
|
34
|
+
clearTimeout: timer => clearTimeout(timer->(Utils.magic: timer => timeoutId)),
|
|
35
|
+
random: Math.random,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let delay = (clock: t, ~label, millis) =>
|
|
39
|
+
Promise.make((resolve, _reject) => clock.setTimeout(~label, () => resolve(), millis)->ignore)
|
|
40
|
+
|
|
41
|
+
// Half the delay fixed, half spread across it. Every indexer pointed at one
|
|
42
|
+
// provider loses its stream, or gives up on a quiet chain, in the same instant
|
|
43
|
+
// that provider blinks, and putting them all back on the same schedule is how a
|
|
44
|
+
// blip becomes a stampede.
|
|
45
|
+
let jitter = (clock: t, delay) =>
|
|
46
|
+
delay / 2 + (clock.random() *. (delay / 2)->Int.toFloat)->Float.toInt
|
|
47
|
+
|
|
48
|
+
// Clearing a pending timer and forgetting it are one action. A ref left holding
|
|
49
|
+
// a spent timer reads as one still pending, and the next clear looks like it
|
|
50
|
+
// did something.
|
|
51
|
+
let clearRef = (clock: t, timer: ref<option<timer>>) => {
|
|
52
|
+
switch timer.contents {
|
|
53
|
+
| Some(timer) => clock.clearTimeout(timer)
|
|
54
|
+
| None => ()
|
|
55
|
+
}
|
|
56
|
+
timer := None
|
|
57
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Performance from "./bindings/Performance.res.mjs";
|
|
4
|
+
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
5
|
+
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
6
|
+
|
|
7
|
+
let refuseReal = {
|
|
8
|
+
contents: false
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
function refused(what) {
|
|
12
|
+
return Stdlib_JsError.throwWithMessage(`Clock.real ` + what + ` on virtual time. Use the clock the code was given, so advanceTime can see and move it.`);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function real_now() {
|
|
16
|
+
if (refuseReal.contents) {
|
|
17
|
+
return refused("read the time");
|
|
18
|
+
} else {
|
|
19
|
+
return Performance.now();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function real_setTimeout(label, callback, millis) {
|
|
24
|
+
if (refuseReal.contents && millis > 0) {
|
|
25
|
+
return refused(`armed "` + label + `"`);
|
|
26
|
+
} else {
|
|
27
|
+
return setTimeout(callback, millis);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function real_clearTimeout(timer) {
|
|
32
|
+
clearTimeout(timer);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function real_random(prim) {
|
|
36
|
+
return Math.random();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let real = {
|
|
40
|
+
now: real_now,
|
|
41
|
+
setTimeout: real_setTimeout,
|
|
42
|
+
clearTimeout: real_clearTimeout,
|
|
43
|
+
random: real_random
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
function delay(clock, label, millis) {
|
|
47
|
+
return new Promise((resolve, _reject) => {
|
|
48
|
+
clock.setTimeout(label, () => resolve(), millis);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function jitter(clock, delay) {
|
|
53
|
+
return (delay / 2 | 0) + (clock.random() * (delay / 2 | 0) | 0) | 0;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function clearRef(clock, timer) {
|
|
57
|
+
let timer$1 = timer.contents;
|
|
58
|
+
if (timer$1 !== undefined) {
|
|
59
|
+
clock.clearTimeout(Primitive_option.valFromOption(timer$1));
|
|
60
|
+
}
|
|
61
|
+
timer.contents = undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export {
|
|
65
|
+
refuseReal,
|
|
66
|
+
refused,
|
|
67
|
+
real,
|
|
68
|
+
delay,
|
|
69
|
+
jitter,
|
|
70
|
+
clearRef,
|
|
71
|
+
}
|
|
72
|
+
/* No side effect */
|