envio 3.6.1 → 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +197 -9
- package/package.json +6 -6
- package/src/Batch.res +64 -25
- package/src/Batch.res.mjs +69 -46
- package/src/ChainFetching.res +13 -17
- package/src/ChainFetching.res.mjs +9 -14
- package/src/ChainState.res +160 -119
- package/src/ChainState.res.mjs +75 -46
- package/src/ChainState.resi +15 -12
- package/src/Config.res +9 -2
- package/src/Config.res.mjs +4 -4
- package/src/Core.res +5 -0
- package/src/Ecosystem.res +0 -1
- package/src/Envio.res +16 -3
- package/src/EventConfigBuilder.res +155 -40
- package/src/EventConfigBuilder.res.mjs +76 -24
- package/src/EventProcessing.res +4 -8
- package/src/EventProcessing.res.mjs +4 -4
- package/src/EventUtils.res +3 -47
- package/src/FetchState.res +124 -104
- package/src/FetchState.res.mjs +116 -100
- package/src/HandlerRegister.res +108 -2
- package/src/HandlerRegister.res.mjs +59 -10
- package/src/HandlerRegister.resi +4 -0
- package/src/Internal.res +84 -24
- package/src/Internal.res.mjs +33 -2
- package/src/LazyLoader.res +12 -11
- package/src/LazyLoader.res.mjs +13 -7
- package/src/Main.res +22 -9
- package/src/Main.res.mjs +18 -5
- package/src/MemoryStorage.res +724 -0
- package/src/MemoryStorage.res.mjs +601 -0
- package/src/PgStorage.res +18 -7
- package/src/PgStorage.res.mjs +14 -11
- package/src/ReorgDetection.res +0 -222
- package/src/ReorgDetection.res.mjs +0 -163
- package/src/Rollback.res +14 -15
- package/src/Rollback.res.mjs +4 -5
- package/src/SimulateItems.res +2 -2
- package/src/Utils.res +3 -0
- package/src/bindings/ClickHouse.res +16 -11
- package/src/bindings/ClickHouse.res.mjs +10 -10
- package/src/bindings/Vitest.res +1 -1
- package/src/sources/BlockStore.res +115 -5
- package/src/sources/BlockStore.res.mjs +25 -0
- package/src/sources/Evm.res +0 -1
- package/src/sources/Evm.res.mjs +0 -1
- package/src/sources/EvmHyperSyncSource.res +19 -84
- package/src/sources/EvmHyperSyncSource.res.mjs +24 -60
- package/src/sources/Fuel.res +24 -4
- package/src/sources/Fuel.res.mjs +23 -3
- package/src/sources/FuelHyperSync.res +8 -3
- package/src/sources/FuelHyperSync.res.mjs +5 -4
- package/src/sources/FuelHyperSync.resi +3 -1
- package/src/sources/FuelHyperSyncClient.res +7 -10
- package/src/sources/FuelHyperSyncSource.res +18 -45
- package/src/sources/FuelHyperSyncSource.res.mjs +18 -35
- package/src/sources/HyperSync.res +49 -229
- package/src/sources/HyperSync.res.mjs +74 -191
- package/src/sources/HyperSync.resi +11 -35
- package/src/sources/HyperSyncClient.res +9 -79
- package/src/sources/HyperSyncClient.res.mjs +2 -6
- package/src/sources/RequestStat.res +5 -0
- package/src/sources/RequestStat.res.mjs +2 -0
- package/src/sources/RpcSource.res +149 -43
- package/src/sources/RpcSource.res.mjs +104 -41
- package/src/sources/SimulateSource.res +8 -5
- package/src/sources/SimulateSource.res.mjs +6 -6
- package/src/sources/Source.res +89 -16
- package/src/sources/Source.res.mjs +50 -1
- package/src/sources/SourceManager.res +255 -50
- package/src/sources/SourceManager.res.mjs +149 -55
- package/src/sources/SourceManager.resi +2 -6
- package/src/sources/Svm.res +0 -7
- package/src/sources/Svm.res.mjs +0 -8
- package/src/sources/SvmHyperSyncClient.res +21 -16
- package/src/sources/SvmHyperSyncClient.res.mjs +3 -4
- package/src/sources/SvmHyperSyncSource.res +25 -117
- package/src/sources/SvmHyperSyncSource.res.mjs +8 -121
- package/svm.schema.json +3 -2
|
@@ -199,7 +199,7 @@ let waitForRateLimitReset = async (sourceManager: t, ~resetMs, ~retry, ~logger)
|
|
|
199
199
|
let waitMs = Pervasives.min(resetMs, 300_000)
|
|
200
200
|
let log = retry >= 2 ? Logging.childWarn : Logging.childTrace
|
|
201
201
|
logger->log({
|
|
202
|
-
"msg": `HyperSync source is rate-limited
|
|
202
|
+
"msg": `HyperSync source is rate-limited - not critical, the indexer will retry in ${(waitMs / 1000)
|
|
203
203
|
->Int.toString}s. For higher limits upgrade your plan at https://envio.dev/app/api-tokens.`,
|
|
204
204
|
"retry": retry,
|
|
205
205
|
"waitMs": waitMs,
|
|
@@ -209,10 +209,45 @@ let waitForRateLimitReset = async (sourceManager: t, ~resetMs, ~retry, ~logger)
|
|
|
209
209
|
sourceManager->stopRateLimitTimeout
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
|
|
212
|
+
// A response is trusted only after its BlockStore has proved that it is
|
|
213
|
+
// internally coherent. `requiredBlockNumbers` is used by getBlockHashes to
|
|
214
|
+
// reject a response that simply omitted one of the requested hashes.
|
|
215
|
+
let validateResponseBlockStore = (
|
|
216
|
+
~method: string,
|
|
217
|
+
~blockStore: BlockStore.t,
|
|
218
|
+
~requiredBlockNumbers: array<int>=[],
|
|
219
|
+
) => {
|
|
220
|
+
switch blockStore->BlockStore.responseConflict->Null.toOption {
|
|
221
|
+
| Some({blockNumber, storedHash, receivedHash}) =>
|
|
222
|
+
throw(
|
|
223
|
+
Source.InconsistentResponse({
|
|
224
|
+
method,
|
|
225
|
+
blockNumber: Some(blockNumber),
|
|
226
|
+
storedHash: Some(storedHash),
|
|
227
|
+
receivedHash: Some(receivedHash),
|
|
228
|
+
missingBlockNumbers: [],
|
|
229
|
+
}),
|
|
230
|
+
)
|
|
231
|
+
| None =>
|
|
232
|
+
let missingBlockNumbers = blockStore->BlockStore.missingHashes(requiredBlockNumbers)
|
|
233
|
+
if missingBlockNumbers->Array.length > 0 {
|
|
234
|
+
throw(
|
|
235
|
+
Source.InconsistentResponse({
|
|
236
|
+
method,
|
|
237
|
+
blockNumber: None,
|
|
238
|
+
storedHash: None,
|
|
239
|
+
receivedHash: None,
|
|
240
|
+
missingBlockNumbers,
|
|
241
|
+
}),
|
|
242
|
+
)
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
let onReorg = (sourceManager: t) => {
|
|
213
248
|
sourceManager.sourcesState->Array.forEach(({source}) => {
|
|
214
249
|
switch source.onReorg {
|
|
215
|
-
| Some(cb) => cb(
|
|
250
|
+
| Some(cb) => cb()
|
|
216
251
|
| None => ()
|
|
217
252
|
}
|
|
218
253
|
})
|
|
@@ -558,7 +593,7 @@ let getNextSources = (sourceManager, ~isRealtime, ~excludedSources=?) => {
|
|
|
558
593
|
} else if workingSecondarySources->Array.length > 0 {
|
|
559
594
|
workingSecondarySources
|
|
560
595
|
} else {
|
|
561
|
-
// All primaries in recovery
|
|
596
|
+
// All primaries in recovery - sort by oldest lastFailedAt (closest to recovery first)
|
|
562
597
|
allPrimarySources->Array.sort(compareByOldestFailure)
|
|
563
598
|
allPrimarySources
|
|
564
599
|
}
|
|
@@ -579,6 +614,145 @@ let getNextSource = (sourceManager, ~isRealtime, ~excludedSources=?) => {
|
|
|
579
614
|
}
|
|
580
615
|
}
|
|
581
616
|
|
|
617
|
+
let maxRetryBackoffMillis = 60_000
|
|
618
|
+
|
|
619
|
+
// One schedule for every retry of the same request, whatever made it fail:
|
|
620
|
+
// 100ms doubling up to the cap. `backoffBeforeRetry` still applies the caller's
|
|
621
|
+
// floor and the cap on top.
|
|
622
|
+
let retryBackoffMillis = retry =>
|
|
623
|
+
Pervasives.min(100. *. 2. ** retry->Int.toFloat, maxRetryBackoffMillis->Int.toFloat)->Float.toInt
|
|
624
|
+
|
|
625
|
+
// Floor for the retries driven by a condition the source reported itself
|
|
626
|
+
// (behind the head, inconsistent response). Unlike a caller-supplied backoff of
|
|
627
|
+
// 0, these must never busy-loop when there is no other source to move to.
|
|
628
|
+
let minRecoverableBackoffMillis = 50
|
|
629
|
+
|
|
630
|
+
// Back off before retrying the same request, failing the source over first when
|
|
631
|
+
// another one can actually take over. Marking `lastFailedAt` only demotes this
|
|
632
|
+
// source in the selection order - with no working alternative the next attempt
|
|
633
|
+
// lands right back here, so `minBackoffMillis` is what keeps a retry loop that
|
|
634
|
+
// never changes source from spinning at full speed.
|
|
635
|
+
let backoffBeforeRetry = async (
|
|
636
|
+
sourceManager: t,
|
|
637
|
+
sourceState: sourceState,
|
|
638
|
+
~retry,
|
|
639
|
+
~isRealtime,
|
|
640
|
+
~backoffMillis,
|
|
641
|
+
~minBackoffMillis=0,
|
|
642
|
+
~excludedSources=?,
|
|
643
|
+
) => {
|
|
644
|
+
// Give the source two attempts before demoting it, then re-try a failover
|
|
645
|
+
// every second attempt.
|
|
646
|
+
let switchedToWorkingSource = if retry >= 2 && retry->mod(2) === 0 {
|
|
647
|
+
let now = Date.now()
|
|
648
|
+
sourceState.lastFailedAt = Some(now)
|
|
649
|
+
switch sourceManager->getNextSource(~isRealtime, ~excludedSources?) {
|
|
650
|
+
| Some(next) =>
|
|
651
|
+
switch next.lastFailedAt {
|
|
652
|
+
| None => true
|
|
653
|
+
| Some(failedAt) => now -. failedAt >= sourceManager.recoveryTimeout
|
|
654
|
+
}
|
|
655
|
+
| None => false
|
|
656
|
+
}
|
|
657
|
+
} else {
|
|
658
|
+
false
|
|
659
|
+
}
|
|
660
|
+
if switchedToWorkingSource {
|
|
661
|
+
// The next attempt goes to a different source, which is progress on its
|
|
662
|
+
// own - only pace it when the caller asked for a floor.
|
|
663
|
+
if minBackoffMillis > 0 {
|
|
664
|
+
await Utils.delay(minBackoffMillis)
|
|
665
|
+
}
|
|
666
|
+
} else {
|
|
667
|
+
await Utils.delay(
|
|
668
|
+
backoffMillis->Pervasives.max(minBackoffMillis)->Pervasives.min(maxRetryBackoffMillis),
|
|
669
|
+
)
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// The queried block hasn't reached the backend instance that served the
|
|
674
|
+
// request. Expected around the head of a load-balanced backend, so early
|
|
675
|
+
// attempts stay quiet and short; a source that stays behind fails over like any
|
|
676
|
+
// other. Shared by every ecosystem's getItems and getBlockHashes.
|
|
677
|
+
let retryBehindHead = async (
|
|
678
|
+
sourceManager: t,
|
|
679
|
+
sourceState: sourceState,
|
|
680
|
+
~retry,
|
|
681
|
+
~isRealtime,
|
|
682
|
+
~logger: Pino.t,
|
|
683
|
+
~blockNumber: int,
|
|
684
|
+
~method: string,
|
|
685
|
+
~err: exn,
|
|
686
|
+
~excludedSources=?,
|
|
687
|
+
) => {
|
|
688
|
+
let backoffMillis = retry->retryBackoffMillis
|
|
689
|
+
let log = retry >= 4 ? Logging.childWarn : Logging.childTrace
|
|
690
|
+
logger->log({
|
|
691
|
+
"msg": `Block #${blockNumber->Int.toString} is not available on the ${sourceState.source.name} source yet. Instances of a load-balanced backend drift slightly around the head, so this is expected - indexing continues after an automatic retry.`,
|
|
692
|
+
"method": method,
|
|
693
|
+
"retry": retry,
|
|
694
|
+
"backOffMilliseconds": backoffMillis,
|
|
695
|
+
"err": err->Utils.prettifyExn,
|
|
696
|
+
})
|
|
697
|
+
await sourceManager->backoffBeforeRetry(
|
|
698
|
+
sourceState,
|
|
699
|
+
~retry,
|
|
700
|
+
~isRealtime,
|
|
701
|
+
~backoffMillis,
|
|
702
|
+
~minBackoffMillis=minRecoverableBackoffMillis,
|
|
703
|
+
~excludedSources?,
|
|
704
|
+
)
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// A source that keeps contradicting itself is not mid-reorg, it is broken, and
|
|
708
|
+
// no amount of retrying moves the chain forward. Roughly five minutes of the
|
|
709
|
+
// backoff schedule below.
|
|
710
|
+
let inconsistentResponseStallRetries = 13
|
|
711
|
+
|
|
712
|
+
// The response contradicted itself (the same block twice with different hashes,
|
|
713
|
+
// or a requested hash missing). It may be a reorg mid-request, so refetch before
|
|
714
|
+
// concluding anything about the chain.
|
|
715
|
+
let retryInconsistentResponse = async (
|
|
716
|
+
sourceManager: t,
|
|
717
|
+
sourceState: sourceState,
|
|
718
|
+
~retry,
|
|
719
|
+
~isRealtime,
|
|
720
|
+
~logger: Pino.t,
|
|
721
|
+
~method: string,
|
|
722
|
+
~err: exn,
|
|
723
|
+
~excludedSources=?,
|
|
724
|
+
) => {
|
|
725
|
+
let backoffMillis = retry->retryBackoffMillis
|
|
726
|
+
let msg = `Received a partial indicator of a possible reorg from the ${sourceState.source.name} source while fetching ${method}. Retrying the request to better identify whether a reorg happened.`
|
|
727
|
+
let (log, msg) = if retry >= inconsistentResponseStallRetries {
|
|
728
|
+
(
|
|
729
|
+
Logging.childError,
|
|
730
|
+
msg ++ " It has disagreed with itself on every attempt for several minutes now, so this chain has stopped making progress - the endpoint is likely serving blocks and logs from nodes on different chains.",
|
|
731
|
+
)
|
|
732
|
+
} else {
|
|
733
|
+
(retry >= 2 ? Logging.childWarn : Logging.childTrace, msg)
|
|
734
|
+
}
|
|
735
|
+
logger->log({
|
|
736
|
+
"msg": msg,
|
|
737
|
+
"method": method,
|
|
738
|
+
"retry": retry,
|
|
739
|
+
"backOffMilliseconds": backoffMillis,
|
|
740
|
+
"err": err->Utils.prettifyExn,
|
|
741
|
+
})
|
|
742
|
+
// Before the backoff, not after: local state may point at an orphaned chain,
|
|
743
|
+
// and a sibling query on this source would keep reading it for as long as the
|
|
744
|
+
// wait lasts.
|
|
745
|
+
sourceState.source.onReorg->Option.forEach(cb => cb())
|
|
746
|
+
await sourceManager->backoffBeforeRetry(
|
|
747
|
+
sourceState,
|
|
748
|
+
~retry,
|
|
749
|
+
~isRealtime,
|
|
750
|
+
~backoffMillis,
|
|
751
|
+
~minBackoffMillis=minRecoverableBackoffMillis,
|
|
752
|
+
~excludedSources?,
|
|
753
|
+
)
|
|
754
|
+
}
|
|
755
|
+
|
|
582
756
|
// Polls for a block height greater than the given block number to ensure a new block is available for indexing.
|
|
583
757
|
let waitForNewBlock = async (sourceManager: t, ~knownHeight, ~isRealtime, ~reducedPolling) => {
|
|
584
758
|
let {sourcesState} = sourceManager
|
|
@@ -709,7 +883,7 @@ let executeQuery = async (
|
|
|
709
883
|
) => {
|
|
710
884
|
let noSourcesError = "The indexer doesn't have data-sources which can continue fetching. Please, check the error logs or reach out to the Envio team."
|
|
711
885
|
|
|
712
|
-
// Sources where the query is impossible
|
|
886
|
+
// Sources where the query is impossible - lazily allocated, excluded for the duration of this query
|
|
713
887
|
let excludedSourcesRef = ref(None)
|
|
714
888
|
|
|
715
889
|
let toBlockRef = ref(query.toBlock)
|
|
@@ -768,6 +942,7 @@ let executeQuery = async (
|
|
|
768
942
|
~logger,
|
|
769
943
|
)
|
|
770
944
|
sourceState->recordRequestStats(response.requestStats)
|
|
945
|
+
validateResponseBlockStore(~method="getItems", ~blockStore=response.blockStore)
|
|
771
946
|
sourceState.lastFailedAt = None
|
|
772
947
|
|
|
773
948
|
// The response carries a fresh height for exactly this source, so during a
|
|
@@ -778,10 +953,37 @@ let executeQuery = async (
|
|
|
778
953
|
}
|
|
779
954
|
responseRef := Some(response)
|
|
780
955
|
} catch {
|
|
781
|
-
| Source.RateLimited({resetMs}) =>
|
|
956
|
+
| Source.RateLimited({resetMs, requestStats}) =>
|
|
957
|
+
sourceState->recordRequestStats(requestStats)
|
|
782
958
|
await sourceManager->waitForRateLimitReset(~resetMs, ~retry, ~logger)
|
|
783
959
|
retryRef := retryRef.contents + 1
|
|
784
960
|
|
|
961
|
+
| Source.SourceBehindHead({blockNumber, requestStats}) as err =>
|
|
962
|
+
sourceState->recordRequestStats(requestStats)
|
|
963
|
+
await sourceManager->retryBehindHead(
|
|
964
|
+
sourceState,
|
|
965
|
+
~retry,
|
|
966
|
+
~isRealtime,
|
|
967
|
+
~logger,
|
|
968
|
+
~blockNumber,
|
|
969
|
+
~method="getItems",
|
|
970
|
+
~err,
|
|
971
|
+
~excludedSources=?excludedSourcesRef.contents,
|
|
972
|
+
)
|
|
973
|
+
retryRef := retryRef.contents + 1
|
|
974
|
+
|
|
975
|
+
| Source.InconsistentResponse(_) as err =>
|
|
976
|
+
await sourceManager->retryInconsistentResponse(
|
|
977
|
+
sourceState,
|
|
978
|
+
~retry,
|
|
979
|
+
~isRealtime,
|
|
980
|
+
~logger,
|
|
981
|
+
~method="getItems",
|
|
982
|
+
~err,
|
|
983
|
+
~excludedSources=?excludedSourcesRef.contents,
|
|
984
|
+
)
|
|
985
|
+
retryRef := retryRef.contents + 1
|
|
986
|
+
|
|
785
987
|
| Source.GetItemsError(error) =>
|
|
786
988
|
switch error {
|
|
787
989
|
| UnsupportedSelection(_)
|
|
@@ -817,7 +1019,7 @@ let executeQuery = async (
|
|
|
817
1019
|
toBlockRef := Some(toBlock)
|
|
818
1020
|
retryRef := 0
|
|
819
1021
|
| FailedGettingItems({exn, attemptedToBlock, retry: ImpossibleForTheQuery({message})}) =>
|
|
820
|
-
// Don't set lastFailedAt
|
|
1022
|
+
// Don't set lastFailedAt - the source isn't broken, the query just can't work on it
|
|
821
1023
|
let excludedSources = switch excludedSourcesRef.contents {
|
|
822
1024
|
| Some(s) => s
|
|
823
1025
|
| None =>
|
|
@@ -844,34 +1046,13 @@ let executeQuery = async (
|
|
|
844
1046
|
"retry": retry,
|
|
845
1047
|
"err": exn->Utils.prettifyExn,
|
|
846
1048
|
})
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
if shouldSwitch {
|
|
856
|
-
let now = Date.now()
|
|
857
|
-
sourceState.lastFailedAt = Some(now)
|
|
858
|
-
// Check if there's a working (recovered) source to switch to immediately
|
|
859
|
-
let nextSource =
|
|
860
|
-
sourceManager->getNextSource(~isRealtime, ~excludedSources=?excludedSourcesRef.contents)
|
|
861
|
-
let hasWorkingAlternative = switch nextSource {
|
|
862
|
-
| Some(s) =>
|
|
863
|
-
switch s.lastFailedAt {
|
|
864
|
-
| None => true
|
|
865
|
-
| Some(failedAt) => now -. failedAt >= sourceManager.recoveryTimeout
|
|
866
|
-
}
|
|
867
|
-
| None => false
|
|
868
|
-
}
|
|
869
|
-
if !hasWorkingAlternative {
|
|
870
|
-
await Utils.delay(Pervasives.min(backoffMillis, 60_000))
|
|
871
|
-
}
|
|
872
|
-
} else {
|
|
873
|
-
await Utils.delay(Pervasives.min(backoffMillis, 60_000))
|
|
874
|
-
}
|
|
1049
|
+
await sourceManager->backoffBeforeRetry(
|
|
1050
|
+
sourceState,
|
|
1051
|
+
~retry,
|
|
1052
|
+
~isRealtime,
|
|
1053
|
+
~backoffMillis,
|
|
1054
|
+
~excludedSources=?excludedSourcesRef.contents,
|
|
1055
|
+
)
|
|
875
1056
|
retryRef := retryRef.contents + 1
|
|
876
1057
|
}
|
|
877
1058
|
|
|
@@ -915,20 +1096,47 @@ let getBlockHashes = async (sourceManager: t, ~blockNumbers: array<int>, ~isReal
|
|
|
915
1096
|
sourceState->recordRequestStats(res.requestStats)
|
|
916
1097
|
switch res.result {
|
|
917
1098
|
| Ok(data) =>
|
|
1099
|
+
validateResponseBlockStore(
|
|
1100
|
+
~method="getBlockHashes",
|
|
1101
|
+
~blockStore=data,
|
|
1102
|
+
~requiredBlockNumbers=blockNumbers,
|
|
1103
|
+
)
|
|
918
1104
|
sourceState.lastFailedAt = None
|
|
919
1105
|
responseRef := Some(data)
|
|
920
1106
|
| Error(exn) => throw(exn)
|
|
921
1107
|
}
|
|
922
1108
|
} catch {
|
|
923
|
-
| Source.RateLimited({resetMs}) =>
|
|
1109
|
+
| Source.RateLimited({resetMs, requestStats}) =>
|
|
1110
|
+
sourceState->recordRequestStats(requestStats)
|
|
924
1111
|
await sourceManager->waitForRateLimitReset(~resetMs, ~retry, ~logger)
|
|
925
1112
|
retryRef := retryRef.contents + 1
|
|
926
1113
|
|
|
1114
|
+
| Source.SourceBehindHead({blockNumber, requestStats}) as err =>
|
|
1115
|
+
sourceState->recordRequestStats(requestStats)
|
|
1116
|
+
await sourceManager->retryBehindHead(
|
|
1117
|
+
sourceState,
|
|
1118
|
+
~retry,
|
|
1119
|
+
~isRealtime,
|
|
1120
|
+
~logger,
|
|
1121
|
+
~blockNumber,
|
|
1122
|
+
~method="getBlockHashes",
|
|
1123
|
+
~err,
|
|
1124
|
+
)
|
|
1125
|
+
retryRef := retryRef.contents + 1
|
|
1126
|
+
|
|
1127
|
+
| Source.InconsistentResponse(_) as err =>
|
|
1128
|
+
await sourceManager->retryInconsistentResponse(
|
|
1129
|
+
sourceState,
|
|
1130
|
+
~retry,
|
|
1131
|
+
~isRealtime,
|
|
1132
|
+
~logger,
|
|
1133
|
+
~method="getBlockHashes",
|
|
1134
|
+
~err,
|
|
1135
|
+
)
|
|
1136
|
+
retryRef := retryRef.contents + 1
|
|
1137
|
+
|
|
927
1138
|
| exn =>
|
|
928
|
-
let backoffMillis =
|
|
929
|
-
| 0 => 500
|
|
930
|
-
| _ => 1000 * retry
|
|
931
|
-
}
|
|
1139
|
+
let backoffMillis = retry->retryBackoffMillis
|
|
932
1140
|
let log = retry >= 4 ? Logging.childWarn : Logging.childTrace
|
|
933
1141
|
logger->log({
|
|
934
1142
|
"msg": "Failed to fetch block hashes. Retrying.",
|
|
@@ -936,16 +1144,13 @@ let getBlockHashes = async (sourceManager: t, ~blockNumbers: array<int>, ~isReal
|
|
|
936
1144
|
"backOffMilliseconds": backoffMillis,
|
|
937
1145
|
"err": exn->Utils.prettifyExn,
|
|
938
1146
|
})
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
sourceState.lastFailedAt = Some(Date.now())
|
|
947
|
-
}
|
|
948
|
-
await Utils.delay(Pervasives.min(backoffMillis, 60_000))
|
|
1147
|
+
await sourceManager->backoffBeforeRetry(
|
|
1148
|
+
sourceState,
|
|
1149
|
+
~retry,
|
|
1150
|
+
~isRealtime,
|
|
1151
|
+
~backoffMillis,
|
|
1152
|
+
~minBackoffMillis=minRecoverableBackoffMillis,
|
|
1153
|
+
)
|
|
949
1154
|
retryRef := retryRef.contents + 1
|
|
950
1155
|
}
|
|
951
1156
|
}
|