envio 3.15.0-subgraph → 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.
- package/README.md +40 -34
- package/evm.schema.json +3 -3
- package/fuel.schema.json +2 -2
- package/index.d.ts +3 -3
- package/licenses/README.md +1 -1
- package/package.json +9 -10
- package/src/Batch.res.mjs +5 -5
- package/src/BatchProcessing.res +1 -0
- package/src/BatchProcessing.res.mjs +1 -0
- package/src/Bin.res +0 -7
- package/src/Bin.res.mjs +0 -8
- package/src/ChainFetching.res +17 -456
- package/src/ChainFetching.res.mjs +33 -404
- package/src/ChainState.res +48 -83
- package/src/ChainState.res.mjs +83 -115
- package/src/ChainState.resi +0 -26
- package/src/Clock.res +57 -0
- package/src/Clock.res.mjs +72 -0
- package/src/Config.res +7 -16
- package/src/Config.res.mjs +4 -11
- package/src/ContractRegisterContext.res +6 -3
- package/src/ContractRegisterContext.res.mjs +2 -4
- package/src/Core.res +5 -136
- package/src/Core.res.mjs +1 -90
- package/src/CrossChainState.res +2 -8
- package/src/CrossChainState.res.mjs +3 -3
- package/src/Ecosystem.res +4 -7
- package/src/Ecosystem.res.mjs +8 -8
- 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 +0 -16
- package/src/Env.res.mjs +0 -11
- package/src/EventProcessing.res +1 -38
- package/src/EventProcessing.res.mjs +1 -28
- package/src/FetchState.res +160 -319
- package/src/FetchState.res.mjs +182 -292
- package/src/FinalizeBackfill.res +2 -4
- package/src/FinalizeBackfill.res.mjs +3 -4
- package/src/HandlerLoader.res +15 -39
- package/src/HandlerLoader.res.mjs +8 -27
- package/src/HandlerRegister.res +0 -4
- package/src/HandlerRegister.res.mjs +2 -5
- package/src/HandlerRegister.resi +0 -2
- 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 +0 -110
- package/src/Internal.res.mjs +1 -115
- package/src/LoadLayer.res +21 -47
- package/src/LoadLayer.res.mjs +47 -72
- package/src/LoadLayer.resi +0 -26
- package/src/Main.res +1 -14
- package/src/Main.res.mjs +3 -4
- package/src/Persistence.res +3 -23
- package/src/Persistence.res.mjs +3 -3
- package/src/Rollback.res +0 -1
- package/src/Rollback.res.mjs +1 -1
- package/src/RunScope.res +0 -3
- package/src/SafeCheckpointTracking.res +4 -4
- package/src/SafeCheckpointTracking.res.mjs +1 -1
- package/src/SimulateItems.res +26 -42
- package/src/SimulateItems.res.mjs +16 -40
- 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 +277 -998
- package/src/Storage.res.mjs +216 -731
- package/src/Supervisor.res +75 -56
- package/src/Supervisor.res.mjs +55 -26
- 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 +46 -410
- package/src/UserContext.res.mjs +30 -318
- package/src/Utils.res +0 -23
- package/src/Utils.res.mjs +1 -17
- 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 +107 -175
- 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 +0 -2
- package/src/sources/AddressStore.res +0 -3
- package/src/sources/ChainSources.res +2 -0
- package/src/sources/ChainSources.res.mjs +2 -2
- package/src/sources/EvmChain.res +4 -7
- package/src/sources/EvmChain.res.mjs +5 -4
- 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 +1 -2
- package/src/sources/HyperSync.res.mjs +1 -2
- 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 +85 -104
- package/src/sources/SourceManager.res.mjs +18 -37
- package/src/sources/SourceManager.resi +3 -5
- package/src/sources/StartBlockResolver.res +1 -0
- package/src/sources/StartBlockResolver.res.mjs +4 -3
- package/src/sources/SvmHyperSyncSource.res +2 -2
- package/src/sources/SvmHyperSyncSource.res.mjs +1 -1
- package/svm.schema.json +3 -3
- package/src/EffectCacheLookup.res +0 -98
- package/src/EffectCacheLookup.res.mjs +0 -116
- package/src/PgWriting.res +0 -36
- package/src/PgWriting.res.mjs +0 -34
- package/src/PreloadLookahead.res +0 -184
- package/src/PreloadLookahead.res.mjs +0 -197
- package/src/SubgraphLog.res +0 -19
- package/src/SubgraphLog.res.mjs +0 -26
- package/src/SyncWrites.res +0 -101
- package/src/SyncWrites.res.mjs +0 -98
- package/src/db/IndexDefinition.res +0 -47
- package/src/db/IndexDefinition.res.mjs +0 -60
- package/src/sources/CallSource.res +0 -274
- package/src/sources/CallSource.res.mjs +0 -196
- package/src/subgraph/abi-types.ts +0 -84
- package/src/subgraph/aggregations.ts +0 -745
- package/src/subgraph/as-package.ts +0 -143
- package/src/subgraph/assemblyscript-regex.ts +0 -67
- package/src/subgraph/assemblyscript.ts +0 -731
- package/src/subgraph/blocks.ts +0 -398
- package/src/subgraph/calls-source.ts +0 -702
- package/src/subgraph/calls.ts +0 -258
- package/src/subgraph/conformance.ts +0 -106
- package/src/subgraph/errors.ts +0 -85
- package/src/subgraph/graft.ts +0 -73
- package/src/subgraph/graph-cli-worker.mjs +0 -35
- package/src/subgraph/graph-cli.ts +0 -900
- package/src/subgraph/graph-node-decimal.ts +0 -199
- package/src/subgraph/graph-ts-enums.ts +0 -28
- package/src/subgraph/graph-ts-types/VERSION +0 -2
- package/src/subgraph/graph-ts-types/chain/arweave.d.ts +0 -70
- package/src/subgraph/graph-ts-types/chain/cosmos.d.ts +0 -327
- package/src/subgraph/graph-ts-types/chain/ethereum.d.ts +0 -233
- package/src/subgraph/graph-ts-types/chain/near.d.ts +0 -253
- package/src/subgraph/graph-ts-types/chain/starknet.d.ts +0 -32
- package/src/subgraph/graph-ts-types/common/collections.d.ts +0 -136
- package/src/subgraph/graph-ts-types/common/conversion.d.ts +0 -11
- package/src/subgraph/graph-ts-types/common/datasource.d.ts +0 -30
- package/src/subgraph/graph-ts-types/common/eager-offset.d.ts +0 -0
- package/src/subgraph/graph-ts-types/common/json.d.ts +0 -17
- package/src/subgraph/graph-ts-types/common/numbers.d.ts +0 -120
- package/src/subgraph/graph-ts-types/common/value.d.ts +0 -120
- package/src/subgraph/graph-ts-types/common/yaml.d.ts +0 -90
- package/src/subgraph/graph-ts-types/global/global.d.ts +0 -194
- package/src/subgraph/graph-ts-types/helper-functions.d.ts +0 -22
- package/src/subgraph/graph-ts-types/index.d.ts +0 -102
- package/src/subgraph/graph-ts.ts +0 -4280
- package/src/subgraph/hosts.ts +0 -554
- package/src/subgraph/log.ts +0 -28
- package/src/subgraph/receipts.ts +0 -184
- package/src/subgraph/register-slots.ts +0 -93
- package/src/subgraph/rollup.ts +0 -109
- package/src/subgraph/rpc.ts +0 -337
- package/src/subgraph/runtime.ts +0 -2060
- package/src/subgraph/scope.ts +0 -177
- package/src/subgraph/stats.ts +0 -42
- package/src/subgraph/template-instances.ts +0 -66
- package/src/subgraph/toolchain.ts +0 -327
- package/src/subgraph/transient.ts +0 -176
package/src/ChainState.res
CHANGED
|
@@ -26,9 +26,6 @@ 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,
|
|
32
29
|
// Chain-wide events/block, used to turn a chain's item budget into a target
|
|
33
30
|
// block for query sizing. Seeded from cumulative progress on construction,
|
|
34
31
|
// then smoothed with an EMA on every batch (see applyBatchProgress). None
|
|
@@ -76,12 +73,6 @@ type t = {
|
|
|
76
73
|
// Timestamp of the committed progress block itself. See
|
|
77
74
|
// `Batch.chainAfterBatch.progressBlockTime`.
|
|
78
75
|
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,
|
|
85
76
|
}
|
|
86
77
|
|
|
87
78
|
// The chain's config-declared addresses as storage rows: what a clean run
|
|
@@ -176,7 +167,6 @@ let make = (
|
|
|
176
167
|
processingBlockNumber: committedProgressBlockNumber,
|
|
177
168
|
numEventsProcessed,
|
|
178
169
|
pendingBudget: 0.,
|
|
179
|
-
pendingBudgetResets: 0,
|
|
180
170
|
chainDensity,
|
|
181
171
|
safeCheckpointTracking,
|
|
182
172
|
shouldRollbackOnReorg,
|
|
@@ -197,8 +187,6 @@ let make = (
|
|
|
197
187
|
rollbackTargetBlock: None,
|
|
198
188
|
progressLatencyMs: None,
|
|
199
189
|
committedProgressBlockTime,
|
|
200
|
-
blockRegisterTail: Promise.resolve(),
|
|
201
|
-
blockRegisterRuns: 0,
|
|
202
190
|
}
|
|
203
191
|
}
|
|
204
192
|
|
|
@@ -320,14 +308,18 @@ let makeInternal = (
|
|
|
320
308
|
// FIXME: Shouldn't set with full history
|
|
321
309
|
// The resumed depth, not the config one: the pre-threshold lag must match
|
|
322
310
|
// the window reorg detection compares, or a reduced config depth would let
|
|
323
|
-
// fetching enter the stored rollback window without history.
|
|
311
|
+
// fetching enter the stored rollback window without history. One block
|
|
312
|
+
// more than the window, so the last pre-threshold page ends below it and
|
|
313
|
+
// the window's first block is read by a page that knows it is reorgable.
|
|
324
314
|
~blockLag=Pervasives.max(
|
|
325
|
-
!config.shouldRollbackOnReorg || isInReorgThreshold
|
|
315
|
+
!config.shouldRollbackOnReorg || isInReorgThreshold || maxReorgDepth == 0
|
|
316
|
+
? 0
|
|
317
|
+
: maxReorgDepth + 1,
|
|
326
318
|
chainConfig.blockLag,
|
|
327
319
|
),
|
|
320
|
+
~blockThrottle=chainConfig.blockThrottle,
|
|
328
321
|
~onBlockRegistrations,
|
|
329
322
|
~firstEventBlock,
|
|
330
|
-
~deferCreationBlockEvents=config.subgraph->Option.isSome,
|
|
331
323
|
// Fuel and SVM route through the address store like EVM does, so the
|
|
332
324
|
// client-side path works for them - but their address counts are nowhere
|
|
333
325
|
// near the threshold, so switching would only trade a server-side filter
|
|
@@ -362,6 +354,7 @@ let makeInternal = (
|
|
|
362
354
|
~blockStore,
|
|
363
355
|
~transactionStore,
|
|
364
356
|
~logger,
|
|
357
|
+
~maxAddressesPerRequest=config.maxAddrInPartition,
|
|
365
358
|
)
|
|
366
359
|
|
|
367
360
|
// Seed the stored reorg checkpoints (hash-only rows) so detection resumes
|
|
@@ -472,7 +465,6 @@ let logger = (cs: t) => cs.logger
|
|
|
472
465
|
let blockStore = (cs: t) => cs.blockStore
|
|
473
466
|
let transactionStore = (cs: t) => cs.transactionStore
|
|
474
467
|
let shouldChecksum = (cs: t) => cs.shouldChecksum
|
|
475
|
-
let fetchState = (cs: t) => cs.fetchState
|
|
476
468
|
let entities = (cs: t) => cs.entities
|
|
477
469
|
|
|
478
470
|
// Rollback discards every uncommitted change, so this chain's partition is
|
|
@@ -559,70 +551,8 @@ let startFetchingQueries = (cs: t, ~queries: array<FetchState.query>) => {
|
|
|
559
551
|
let resetPendingQueries = (cs: t) => {
|
|
560
552
|
cs.fetchState = cs.fetchState->FetchState.resetPendingQueries
|
|
561
553
|
cs.pendingBudget = 0.
|
|
562
|
-
cs.pendingBudgetResets = cs.pendingBudgetResets + 1
|
|
563
554
|
}
|
|
564
555
|
|
|
565
|
-
// A response that landed with more items than its query was priced at keeps the
|
|
566
|
-
// excess reserved while it waits for its register passes: until then its items
|
|
567
|
-
// are neither in the buffer nor in the query's estimate, and the pool would
|
|
568
|
-
// admit the next queries on top of them. Released when the response is applied.
|
|
569
|
-
type heldBudget = {items: int, resets: int}
|
|
570
|
-
|
|
571
|
-
let holdExtraBudget = (cs: t, ~items) => {
|
|
572
|
-
cs.pendingBudget = cs.pendingBudget +. items->Int.toFloat
|
|
573
|
-
{items, resets: cs.pendingBudgetResets}
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
let releaseExtraBudget = (cs: t, held: heldBudget) =>
|
|
577
|
-
if held.resets === cs.pendingBudgetResets {
|
|
578
|
-
cs.pendingBudget = Pervasives.max(0., cs.pendingBudget -. held.items->Int.toFloat)
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
let hasBlockRegisters = (cs: t) => cs.fetchState.blockRegisterThrough !== None
|
|
582
|
-
let isBlockRegisterIdle = (cs: t) => cs.blockRegisterRuns === 0
|
|
583
|
-
|
|
584
|
-
// Queues a unit of register work behind the chain's latest one. Returns the
|
|
585
|
-
// promise to wait on before running it, and the callback that settles it,
|
|
586
|
-
// which must be called however the unit ends.
|
|
587
|
-
let queueBlockRegisterRun = (cs: t) => {
|
|
588
|
-
let previous = cs.blockRegisterTail
|
|
589
|
-
let settle = ref(() => ())
|
|
590
|
-
cs.blockRegisterTail = Promise.make((resolve, _) => settle := resolve)
|
|
591
|
-
cs.blockRegisterRuns = cs.blockRegisterRuns + 1
|
|
592
|
-
let finish = () => {
|
|
593
|
-
cs.blockRegisterRuns = cs.blockRegisterRuns - 1
|
|
594
|
-
settle.contents()
|
|
595
|
-
}
|
|
596
|
-
(previous, finish)
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
let canAdvanceBlockRegisters = (cs: t, ~through) =>
|
|
600
|
-
cs.fetchState->FetchState.canAdvanceBlockRegisters(~through)
|
|
601
|
-
let nextBlockRegisterItems = (cs: t, ~through, ~limit) =>
|
|
602
|
-
cs.fetchState->FetchState.nextBlockRegisterItems(~through, ~limit)
|
|
603
|
-
let blockRegisterItemsAfter = (cs: t, ~after, ~through, ~limit) =>
|
|
604
|
-
cs.fetchState->FetchState.blockRegisterItemsAfter(~after, ~through, ~limit)
|
|
605
|
-
let blockRegisterThrough = (cs: t) => cs.fetchState.blockRegisterThrough
|
|
606
|
-
let blockRegisterFrontier = (cs: t) => cs.fetchState->FetchState.blockRegisterFrontier
|
|
607
|
-
|
|
608
|
-
// Applies what a run of block passes alone registered, then lets block items
|
|
609
|
-
// through its last block be generated.
|
|
610
|
-
let applyBlockRegisters = (cs: t, ~registrations, ~through) => {
|
|
611
|
-
let fs = switch registrations {
|
|
612
|
-
| [] => cs.fetchState
|
|
613
|
-
| _ =>
|
|
614
|
-
cs.fetchState->FetchState.registerDynamicContracts(
|
|
615
|
-
~addressStore=cs.addressStore,
|
|
616
|
-
~onNotice=notice => cs.logger->logFetchStateNotice(notice),
|
|
617
|
-
registrations,
|
|
618
|
-
)
|
|
619
|
-
}
|
|
620
|
-
cs.fetchState = fs->FetchState.setBlockRegisterThrough(~through)
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
let markBlockRegistered = (cs: t, ~through) =>
|
|
624
|
-
cs.fetchState = cs.fetchState->FetchState.setBlockRegisterThrough(~through)
|
|
625
|
-
|
|
626
556
|
let isReady = (cs: t) => cs.timestampCaughtUpToHeadOrEndblock !== None
|
|
627
557
|
|
|
628
558
|
// Block span over which a batch fully replaces the chain density estimate;
|
|
@@ -852,10 +782,10 @@ let takeFinished = (cs: t) =>
|
|
|
852
782
|
}
|
|
853
783
|
}
|
|
854
784
|
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
785
|
+
// The block reorg detection compares from is itself in the window, so the
|
|
786
|
+
// highest block below it is the one before, where catch-up stops.
|
|
787
|
+
let getHighestBlockBelowThreshold = (cs: t): int =>
|
|
788
|
+
Pervasives.max(cs.fetchState.knownHeight - cs.maxReorgDepth - 1, 0)
|
|
859
789
|
|
|
860
790
|
// True once the fetch frontier has reached the head/endBlock for this chain.
|
|
861
791
|
let isFetchingAtHead = (cs: t) => cs.fetchState->FetchState.isFetchingAtHead
|
|
@@ -1053,6 +983,34 @@ let materializePageItems = async (
|
|
|
1053
983
|
))
|
|
1054
984
|
}
|
|
1055
985
|
|
|
986
|
+
// Near the head: each new block in as few queries as possible, of a shape that
|
|
987
|
+
// stays the same while contracts keep being registered. Once every partition is
|
|
988
|
+
// close enough, the chain's dynamic contracts leave the server-side address
|
|
989
|
+
// filter for the rest of the run.
|
|
990
|
+
//
|
|
991
|
+
// Judged only when the chain learns a newer head. An RPC response repeats the
|
|
992
|
+
// height its query went out with, which can be hours old by the end of a
|
|
993
|
+
// backfill: judged against it, every block produced since would be fetched
|
|
994
|
+
// address-free.
|
|
995
|
+
let switchDynamicContractsToClientSideFilteringNearHead = (cs: t) =>
|
|
996
|
+
if (
|
|
997
|
+
!cs.fetchState.clientFilterPolicy.allDynamicContracts &&
|
|
998
|
+
cs.fetchState->FetchState.isWithinHeadClientFilteringDistance
|
|
999
|
+
) {
|
|
1000
|
+
cs.fetchState =
|
|
1001
|
+
cs.fetchState->FetchState.switchDynamicContractsToClientSideFiltering(
|
|
1002
|
+
~addressStore=cs.addressStore,
|
|
1003
|
+
)
|
|
1004
|
+
if cs.onEventRegistrations->Array.some(registration => registration.contractRegister !== None) {
|
|
1005
|
+
cs.logger->Logger.trace(
|
|
1006
|
+
"Near the head: dynamic contracts are fetched without their addresses for the rest of the run, and filtered on our side.",
|
|
1007
|
+
~params={
|
|
1008
|
+
"contractNames": cs.fetchState.optimizedPartitions.dynamicContracts->Utils.Set.toArray,
|
|
1009
|
+
},
|
|
1010
|
+
)
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1056
1014
|
let handleQueryResult = (
|
|
1057
1015
|
cs: t,
|
|
1058
1016
|
~query: FetchState.query,
|
|
@@ -1076,6 +1034,7 @@ let handleQueryResult = (
|
|
|
1076
1034
|
)
|
|
1077
1035
|
}
|
|
1078
1036
|
|
|
1037
|
+
let previousKnownHeight = cs.fetchState.knownHeight
|
|
1079
1038
|
cs.fetchState =
|
|
1080
1039
|
fs
|
|
1081
1040
|
->FetchState.handleQueryResult(~query, ~latestFetchedBlock, ~newItems)
|
|
@@ -1083,6 +1042,9 @@ let handleQueryResult = (
|
|
|
1083
1042
|
|
|
1084
1043
|
// The query is no longer in flight, so release its reservation.
|
|
1085
1044
|
cs.pendingBudget = Pervasives.max(0., cs.pendingBudget -. query.itemsEst->Int.toFloat)
|
|
1045
|
+
if cs.fetchState.knownHeight > previousKnownHeight {
|
|
1046
|
+
cs->switchDynamicContractsToClientSideFilteringNearHead
|
|
1047
|
+
}
|
|
1086
1048
|
}
|
|
1087
1049
|
|
|
1088
1050
|
// Run reorg detection against a fetch response by merging its block-store page
|
|
@@ -1117,8 +1079,11 @@ let prepareReorg = (cs: t, ~eventsProcessedDiff) => {
|
|
|
1117
1079
|
cs->resetPendingQueries
|
|
1118
1080
|
}
|
|
1119
1081
|
|
|
1120
|
-
|
|
1082
|
+
// A chain waiting at its head goes address-free before querying the new block.
|
|
1083
|
+
let updateKnownHeight = (cs: t, ~knownHeight) => {
|
|
1121
1084
|
cs.fetchState = cs.fetchState->FetchState.updateKnownHeight(~knownHeight)
|
|
1085
|
+
cs->switchDynamicContractsToClientSideFilteringNearHead
|
|
1086
|
+
}
|
|
1122
1087
|
|
|
1123
1088
|
// In auto-exit mode, pin the endBlock to the earliest observed event block.
|
|
1124
1089
|
let setEndBlockToFirstEvent = (cs: t, ~blockNumber) =>
|
package/src/ChainState.res.mjs
CHANGED
|
@@ -83,7 +83,6 @@ function make(chainConfig, fetchState, onEventRegistrationsOpt, addressStore, so
|
|
|
83
83
|
processingBlockNumber: committedProgressBlockNumber,
|
|
84
84
|
numEventsProcessed: numEventsProcessed,
|
|
85
85
|
pendingBudget: 0,
|
|
86
|
-
pendingBudgetResets: 0,
|
|
87
86
|
chainDensity: chainDensity,
|
|
88
87
|
entities: EntityTables.make(perChainEntities),
|
|
89
88
|
safeCheckpointTracking: safeCheckpointTracking,
|
|
@@ -104,9 +103,7 @@ function make(chainConfig, fetchState, onEventRegistrationsOpt, addressStore, so
|
|
|
104
103
|
reorgDetectedBlock: undefined,
|
|
105
104
|
rollbackTargetBlock: undefined,
|
|
106
105
|
progressLatencyMs: undefined,
|
|
107
|
-
committedProgressBlockTime: committedProgressBlockTime
|
|
108
|
-
blockRegisterTail: Promise.resolve(),
|
|
109
|
-
blockRegisterRuns: 0
|
|
106
|
+
committedProgressBlockTime: committedProgressBlockTime
|
|
110
107
|
};
|
|
111
108
|
}
|
|
112
109
|
|
|
@@ -162,11 +159,16 @@ function makeFromDbState(chainConfig, resumedChainState, reorgCheckpoints, isInR
|
|
|
162
159
|
onBlockRegistrations: []
|
|
163
160
|
});
|
|
164
161
|
let onEventRegistrations = match.onEventRegistrations;
|
|
165
|
-
let onBlockRegistrations = match.onBlockRegistrations.map(reg => {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
162
|
+
let onBlockRegistrations = match.onBlockRegistrations.map(reg => ({
|
|
163
|
+
index: reg.index,
|
|
164
|
+
name: reg.name,
|
|
165
|
+
chainId: reg.chainId,
|
|
166
|
+
startBlock: Stdlib_Option.getOr(reg.startBlock, startBlock),
|
|
167
|
+
endBlock: reg.endBlock,
|
|
168
|
+
interval: reg.interval,
|
|
169
|
+
handler: reg.handler,
|
|
170
|
+
blockFieldMask: reg.blockFieldMask
|
|
171
|
+
}));
|
|
170
172
|
chainConfig.contracts.forEach(contract => {
|
|
171
173
|
let contractStartBlock = contract.startBlock;
|
|
172
174
|
if (contractStartBlock !== undefined && contractStartBlock < startBlock) {
|
|
@@ -186,7 +188,7 @@ function makeFromDbState(chainConfig, resumedChainState, reorgCheckpoints, isInR
|
|
|
186
188
|
tmp = undefined;
|
|
187
189
|
break;
|
|
188
190
|
}
|
|
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 ? 0 : maxReorgDepth, chainConfig.blockLag), Primitive_option.some(firstEventBlock), Primitive_option.some(tmp), isResumed
|
|
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 || maxReorgDepth === 0 ? 0 : maxReorgDepth + 1 | 0, chainConfig.blockLag), chainConfig.blockThrottle, Primitive_option.some(firstEventBlock), Primitive_option.some(tmp), isResumed);
|
|
190
192
|
let chainReorgCheckpoints = Stdlib_Array.filterMap(reorgCheckpoints, reorgCheckpoint => {
|
|
191
193
|
if (reorgCheckpoint.chain_id === chainConfig.id) {
|
|
192
194
|
return reorgCheckpoint;
|
|
@@ -194,7 +196,7 @@ function makeFromDbState(chainConfig, resumedChainState, reorgCheckpoints, isInR
|
|
|
194
196
|
});
|
|
195
197
|
let blockStore = BlockStore.make(config.ecosystem.name);
|
|
196
198
|
let transactionStore = TransactionStore.make(config.ecosystem.name);
|
|
197
|
-
let sources = ChainSources.make(chainConfig, onEventRegistrations, onBlockRegistrations, addressStore, lowercaseAddresses, blockStore, transactionStore, logger$1);
|
|
199
|
+
let sources = ChainSources.make(chainConfig, onEventRegistrations, onBlockRegistrations, addressStore, lowercaseAddresses, blockStore, transactionStore, logger$1, config.maxAddrInPartition);
|
|
198
200
|
if (Utils.$$Array.notEmpty(chainReorgCheckpoints)) {
|
|
199
201
|
let seedPage = BlockStore.fromJs(chainReorgCheckpoints.map(cp => ({
|
|
200
202
|
blockNumber: cp.block_number,
|
|
@@ -223,10 +225,6 @@ function shouldChecksum(cs) {
|
|
|
223
225
|
return cs.shouldChecksum;
|
|
224
226
|
}
|
|
225
227
|
|
|
226
|
-
function fetchState(cs) {
|
|
227
|
-
return cs.fetchState;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
228
|
function entities(cs) {
|
|
231
229
|
return cs.entities;
|
|
232
230
|
}
|
|
@@ -340,78 +338,6 @@ function startFetchingQueries(cs, queries) {
|
|
|
340
338
|
function resetPendingQueries(cs) {
|
|
341
339
|
cs.fetchState = FetchState.resetPendingQueries(cs.fetchState);
|
|
342
340
|
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);
|
|
415
341
|
}
|
|
416
342
|
|
|
417
343
|
function isReady(cs) {
|
|
@@ -568,12 +494,7 @@ function takeFinished(cs) {
|
|
|
568
494
|
}
|
|
569
495
|
|
|
570
496
|
function getHighestBlockBelowThreshold(cs) {
|
|
571
|
-
|
|
572
|
-
if (highestBlockBelowThreshold < 0) {
|
|
573
|
-
return 0;
|
|
574
|
-
} else {
|
|
575
|
-
return highestBlockBelowThreshold;
|
|
576
|
-
}
|
|
497
|
+
return Primitive_int.max((cs.fetchState.knownHeight - cs.maxReorgDepth | 0) - 1 | 0, 0);
|
|
577
498
|
}
|
|
578
499
|
|
|
579
500
|
function isFetchingAtHead(cs) {
|
|
@@ -721,10 +642,27 @@ async function materializePageItems(items, transactionStore, blockStore, shouldC
|
|
|
721
642
|
]);
|
|
722
643
|
}
|
|
723
644
|
|
|
645
|
+
function switchDynamicContractsToClientSideFilteringNearHead(cs) {
|
|
646
|
+
if (!cs.fetchState.clientFilterPolicy.allDynamicContracts && FetchState.isWithinHeadClientFilteringDistance(cs.fetchState)) {
|
|
647
|
+
cs.fetchState = FetchState.switchDynamicContractsToClientSideFiltering(cs.fetchState, cs.addressStore);
|
|
648
|
+
if (cs.onEventRegistrations.some(registration => registration.contractRegister !== undefined)) {
|
|
649
|
+
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.", {
|
|
650
|
+
contractNames: Array.from(cs.fetchState.optimizedPartitions.dynamicContracts)
|
|
651
|
+
});
|
|
652
|
+
} else {
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
724
658
|
function handleQueryResult(cs, query, newItems, newRegistrations, latestFetchedBlock, knownHeight) {
|
|
725
659
|
let fs = newRegistrations.length !== 0 ? FetchState.registerDynamicContracts(cs.fetchState, cs.addressStore, notice => logFetchStateNotice(cs.logger, notice), Primitive_int.max(knownHeight, latestFetchedBlock), newRegistrations) : cs.fetchState;
|
|
660
|
+
let previousKnownHeight = cs.fetchState.knownHeight;
|
|
726
661
|
cs.fetchState = FetchState.updateKnownHeight(FetchState.handleQueryResult(fs, query, latestFetchedBlock, newItems), knownHeight);
|
|
727
662
|
cs.pendingBudget = Primitive_float.max(0, cs.pendingBudget - query.itemsEst);
|
|
663
|
+
if (cs.fetchState.knownHeight > previousKnownHeight) {
|
|
664
|
+
return switchDynamicContractsToClientSideFilteringNearHead(cs);
|
|
665
|
+
}
|
|
728
666
|
}
|
|
729
667
|
|
|
730
668
|
function registerReorgGuard(cs, blockStore, knownHeight) {
|
|
@@ -757,6 +695,7 @@ function prepareReorg(cs, eventsProcessedDiff) {
|
|
|
757
695
|
|
|
758
696
|
function updateKnownHeight(cs, knownHeight) {
|
|
759
697
|
cs.fetchState = FetchState.updateKnownHeight(cs.fetchState, knownHeight);
|
|
698
|
+
switchDynamicContractsToClientSideFilteringNearHead(cs);
|
|
760
699
|
}
|
|
761
700
|
|
|
762
701
|
function setEndBlockToFirstEvent(cs, blockNumber) {
|
|
@@ -765,14 +704,42 @@ function setEndBlockToFirstEvent(cs, blockNumber) {
|
|
|
765
704
|
if (blockNumber >= currentEndBlock) {
|
|
766
705
|
return;
|
|
767
706
|
}
|
|
768
|
-
let
|
|
769
|
-
|
|
770
|
-
|
|
707
|
+
let init = cs.fetchState;
|
|
708
|
+
cs.fetchState = {
|
|
709
|
+
optimizedPartitions: init.optimizedPartitions,
|
|
710
|
+
startBlock: init.startBlock,
|
|
711
|
+
endBlock: blockNumber,
|
|
712
|
+
normalSelection: init.normalSelection,
|
|
713
|
+
chainId: init.chainId,
|
|
714
|
+
latestOnBlockBlockNumber: init.latestOnBlockBlockNumber,
|
|
715
|
+
blockLag: init.blockLag,
|
|
716
|
+
blockThrottle: init.blockThrottle,
|
|
717
|
+
buffer: init.buffer,
|
|
718
|
+
maxOnBlockBufferSize: init.maxOnBlockBufferSize,
|
|
719
|
+
onBlockRegistrations: init.onBlockRegistrations,
|
|
720
|
+
knownHeight: init.knownHeight,
|
|
721
|
+
firstEventBlock: init.firstEventBlock,
|
|
722
|
+
clientFilterPolicy: init.clientFilterPolicy
|
|
723
|
+
};
|
|
771
724
|
return;
|
|
772
725
|
}
|
|
773
|
-
let
|
|
774
|
-
|
|
775
|
-
|
|
726
|
+
let init$1 = cs.fetchState;
|
|
727
|
+
cs.fetchState = {
|
|
728
|
+
optimizedPartitions: init$1.optimizedPartitions,
|
|
729
|
+
startBlock: init$1.startBlock,
|
|
730
|
+
endBlock: blockNumber,
|
|
731
|
+
normalSelection: init$1.normalSelection,
|
|
732
|
+
chainId: init$1.chainId,
|
|
733
|
+
latestOnBlockBlockNumber: init$1.latestOnBlockBlockNumber,
|
|
734
|
+
blockLag: init$1.blockLag,
|
|
735
|
+
blockThrottle: init$1.blockThrottle,
|
|
736
|
+
buffer: init$1.buffer,
|
|
737
|
+
maxOnBlockBufferSize: init$1.maxOnBlockBufferSize,
|
|
738
|
+
onBlockRegistrations: init$1.onBlockRegistrations,
|
|
739
|
+
knownHeight: init$1.knownHeight,
|
|
740
|
+
firstEventBlock: init$1.firstEventBlock,
|
|
741
|
+
clientFilterPolicy: init$1.clientFilterPolicy
|
|
742
|
+
};
|
|
776
743
|
}
|
|
777
744
|
|
|
778
745
|
function enterReorgThreshold(cs) {
|
|
@@ -920,9 +887,23 @@ function applyBatchProgress(cs, batch, blockTimestampName) {
|
|
|
920
887
|
if (match === undefined) {
|
|
921
888
|
let firstEventBlock = Batch.findFirstEventBlockNumber(batch, chainId);
|
|
922
889
|
if (firstEventBlock !== undefined) {
|
|
923
|
-
let
|
|
924
|
-
|
|
925
|
-
|
|
890
|
+
let init = cs.fetchState;
|
|
891
|
+
cs.fetchState = {
|
|
892
|
+
optimizedPartitions: init.optimizedPartitions,
|
|
893
|
+
startBlock: init.startBlock,
|
|
894
|
+
endBlock: init.endBlock,
|
|
895
|
+
normalSelection: init.normalSelection,
|
|
896
|
+
chainId: init.chainId,
|
|
897
|
+
latestOnBlockBlockNumber: init.latestOnBlockBlockNumber,
|
|
898
|
+
blockLag: init.blockLag,
|
|
899
|
+
blockThrottle: init.blockThrottle,
|
|
900
|
+
buffer: init.buffer,
|
|
901
|
+
maxOnBlockBufferSize: init.maxOnBlockBufferSize,
|
|
902
|
+
onBlockRegistrations: init.onBlockRegistrations,
|
|
903
|
+
knownHeight: init.knownHeight,
|
|
904
|
+
firstEventBlock: firstEventBlock,
|
|
905
|
+
clientFilterPolicy: init.clientFilterPolicy
|
|
906
|
+
};
|
|
926
907
|
}
|
|
927
908
|
}
|
|
928
909
|
let deltaBlocks = chainAfterBatch.progressBlockNumber - cs.committedProgressBlockNumber | 0;
|
|
@@ -1025,7 +1006,6 @@ export {
|
|
|
1025
1006
|
blockStore,
|
|
1026
1007
|
transactionStore,
|
|
1027
1008
|
shouldChecksum,
|
|
1028
|
-
fetchState,
|
|
1029
1009
|
entities,
|
|
1030
1010
|
resetEntities,
|
|
1031
1011
|
sourceManager,
|
|
@@ -1038,18 +1018,6 @@ export {
|
|
|
1038
1018
|
committedProgressBlockTime,
|
|
1039
1019
|
numEventsProcessed,
|
|
1040
1020
|
pendingBudget,
|
|
1041
|
-
holdExtraBudget,
|
|
1042
|
-
releaseExtraBudget,
|
|
1043
|
-
hasBlockRegisters,
|
|
1044
|
-
isBlockRegisterIdle,
|
|
1045
|
-
queueBlockRegisterRun,
|
|
1046
|
-
canAdvanceBlockRegisters,
|
|
1047
|
-
nextBlockRegisterItems,
|
|
1048
|
-
blockRegisterItemsAfter,
|
|
1049
|
-
blockRegisterThrough,
|
|
1050
|
-
blockRegisterFrontier,
|
|
1051
|
-
applyBlockRegisters,
|
|
1052
|
-
markBlockRegistered,
|
|
1053
1021
|
startFetchingQueries,
|
|
1054
1022
|
timestampCaughtUpToHeadOrEndblock,
|
|
1055
1023
|
recordBlockRangeFetch,
|
package/src/ChainState.resi
CHANGED
|
@@ -58,8 +58,6 @@ 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
|
|
63
61
|
let entities: t => EntityTables.t
|
|
64
62
|
let resetEntities: (t, ~perChainEntities: array<Internal.entityConfig>) => unit
|
|
65
63
|
let sourceManager: t => SourceManager.t
|
|
@@ -77,30 +75,6 @@ let committedProgressBlockNumber: t => int
|
|
|
77
75
|
let committedProgressBlockTime: t => option<int>
|
|
78
76
|
let numEventsProcessed: t => float
|
|
79
77
|
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
|
|
104
78
|
let startFetchingQueries: (t, ~queries: array<FetchState.query>) => unit
|
|
105
79
|
let timestampCaughtUpToHeadOrEndblock: t => option<Date.t>
|
|
106
80
|
|
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
|
+
}
|