@@ -16,7 +16,13 @@ let getKnownBlock = (provider, blockNumber) =>
16
16
}
17
17
)
18
18
19
- let rec getKnownBlockWithBackoff = async (~provider , ~sourceName , ~chain , ~blockNumber , ~backoffMsOnFailure ) =>
19
+ let rec getKnownBlockWithBackoff = async (
20
+ ~provider ,
21
+ ~sourceName ,
22
+ ~chain ,
23
+ ~blockNumber ,
24
+ ~backoffMsOnFailure ,
25
+ ) =>
20
26
switch await getKnownBlock (provider , blockNumber ) {
21
27
| exception err =>
22
28
Logging .warn ({
@@ -88,19 +94,24 @@ let getSuggestedBlockIntervalFromExn = {
88
94
// - Optimism: "backend response too large" or "Block range is too large"
89
95
// - Arbitrum: "logs matched by query exceeds limit of 10000"
90
96
91
- exn =>
97
+ (exn ): option <(
98
+ // The suggested block range
99
+ int ,
100
+ // Whether it's the max range that the provider allows
101
+ bool ,
102
+ )> =>
92
103
switch exn {
93
104
| Js .Exn .Error (error ) =>
94
105
try {
95
106
let message : string = (error -> Obj .magic )["error" ]["message" ]
96
107
message -> S .assertOrThrow (S .string )
97
108
98
109
// Helper to extract block range from regex match
99
- let extractBlockRange = execResult =>
110
+ let extractBlockRange = ( execResult , ~ isMaxRange ) =>
100
111
switch execResult -> Js .Re .captures {
101
112
| [_ , Js .Nullable .Value (blockRangeLimit )] =>
102
113
switch blockRangeLimit -> Int .fromString {
103
- | Some (blockRangeLimit ) if blockRangeLimit > 0 => Some (blockRangeLimit )
114
+ | Some (blockRangeLimit ) if blockRangeLimit > 0 => Some (blockRangeLimit , isMaxRange )
104
115
| _ => None
105
116
}
106
117
| _ => None
@@ -113,48 +124,49 @@ let getSuggestedBlockIntervalFromExn = {
113
124
| [_ , Js .Nullable .Value (fromBlock ), Js .Nullable .Value (toBlock )] =>
114
125
switch (fromBlock -> Int .fromString , toBlock -> Int .fromString ) {
115
126
| (Some (fromBlock ), Some (toBlock )) if toBlock >= fromBlock =>
116
- Some (toBlock - fromBlock + 1 )
127
+ Some (toBlock - fromBlock + 1 , false )
117
128
| _ => None
118
129
}
119
130
| _ => None
120
131
}
121
132
| None =>
122
133
// Try each provider's specific error pattern
123
134
switch blockRangeLimitRegExp -> Js .Re .exec_ (message ) {
124
- | Some (execResult ) => extractBlockRange (execResult )
135
+ | Some (execResult ) => extractBlockRange (execResult , ~ isMaxRange = true )
125
136
| None =>
126
137
switch alchemyRangeRegExp -> Js .Re .exec_ (message ) {
127
- | Some (execResult ) => extractBlockRange (execResult )
138
+ | Some (execResult ) => extractBlockRange (execResult , ~ isMaxRange = true )
128
139
| None =>
129
140
switch cloudflareRangeRegExp -> Js .Re .exec_ (message ) {
130
- | Some (execResult ) => extractBlockRange (execResult )
141
+ | Some (execResult ) => extractBlockRange (execResult , ~ isMaxRange = true )
131
142
| None =>
132
143
switch thirdwebRangeRegExp -> Js .Re .exec_ (message ) {
133
- | Some (execResult ) => extractBlockRange (execResult )
144
+ | Some (execResult ) => extractBlockRange (execResult , ~ isMaxRange = true )
134
145
| None =>
135
146
switch blockpiRangeRegExp -> Js .Re .exec_ (message ) {
136
- | Some (execResult ) => extractBlockRange (execResult )
147
+ | Some (execResult ) => extractBlockRange (execResult , ~ isMaxRange = true )
137
148
| None =>
138
149
switch maxAllowedBlocksRegExp -> Js .Re .exec_ (message ) {
139
- | Some (execResult ) => extractBlockRange (execResult )
150
+ | Some (execResult ) => extractBlockRange (execResult , ~ isMaxRange = true )
140
151
| None =>
141
152
switch baseRangeRegExp -> Js .Re .exec_ (message ) {
142
- | Some (_ ) => Some (2000 )
153
+ | Some (_ ) => Some (2000 , true )
143
154
| None =>
144
- switch blastPaidRegExp -> Js .Re .exec_ (message ) {
145
- | Some (execResult ) => extractBlockRange (execResult )
155
+ switch blastPaidRegExp -> Js .Re .exec_ (message ) {
156
+ | Some (execResult ) => extractBlockRange (execResult , ~ isMaxRange = true )
146
157
| None =>
147
158
switch chainstackRegExp -> Js .Re .exec_ (message ) {
148
- | Some (_ ) => Some (10000 )
159
+ | Some (_ ) => Some (10000 , true )
149
160
| None =>
150
161
switch coinbaseRegExp -> Js .Re .exec_ (message ) {
151
- | Some (execResult ) => extractBlockRange (execResult )
162
+ | Some (execResult ) => extractBlockRange (execResult , ~ isMaxRange = true )
152
163
| None =>
153
164
switch publicNodeRegExp -> Js .Re .exec_ (message ) {
154
- | Some (execResult ) => extractBlockRange (execResult )
165
+ | Some (execResult ) => extractBlockRange (execResult , ~ isMaxRange = true )
155
166
| None =>
156
167
switch hyperliquidRegExp -> Js .Re .exec_ (message ) {
157
- | Some (execResult ) => extractBlockRange (execResult )
168
+ | Some (execResult ) =>
169
+ extractBlockRange (execResult , ~isMaxRange = true )
158
170
| None => None
159
171
}
160
172
}
@@ -181,15 +193,17 @@ type eventBatchQuery = {
181
193
latestFetchedBlock : Ethers .JsonRpcProvider .block ,
182
194
}
183
195
196
+ let maxSuggestedBlockIntervalKey = "max"
197
+
184
198
let getNextPage = (
185
199
~fromBlock ,
186
200
~toBlock ,
187
201
~addresses ,
188
202
~topicQuery ,
189
203
~loadBlock ,
190
- ~syncConfig as sc : Config . syncConfig ,
204
+ ~syncConfig as sc : InternalConfig . sourceSync ,
191
205
~provider ,
192
- ~suggestedBlockIntervals ,
206
+ ~mutSuggestedBlockIntervals ,
193
207
~partitionId ,
194
208
): promise <eventBatchQuery > => {
195
209
//If the query hangs for longer than this, reject this promise to reduce the block interval
@@ -224,8 +238,11 @@ let getNextPage = (
224
238
-> Promise .race
225
239
-> Promise .catch (err => {
226
240
switch getSuggestedBlockIntervalFromExn (err ) {
227
- | Some (nextBlockIntervalTry ) =>
228
- suggestedBlockIntervals -> Js .Dict .set (partitionId , nextBlockIntervalTry )
241
+ | Some ((nextBlockIntervalTry , isMaxRange )) =>
242
+ mutSuggestedBlockIntervals -> Js .Dict .set (
243
+ isMaxRange ? maxSuggestedBlockIntervalKey : partitionId ,
244
+ nextBlockIntervalTry ,
245
+ )
229
246
raise (
230
247
Source .GetItemsError (
231
248
FailedGettingItems ({
@@ -241,7 +258,7 @@ let getNextPage = (
241
258
let executedBlockInterval = toBlock - fromBlock + 1
242
259
let nextBlockIntervalTry =
243
260
(executedBlockInterval -> Belt .Int .toFloat *. sc .backoffMultiplicative )-> Belt .Int .fromFloat
244
- suggestedBlockIntervals -> Js .Dict .set (partitionId , nextBlockIntervalTry )
261
+ mutSuggestedBlockIntervals -> Js .Dict .set (partitionId , nextBlockIntervalTry )
245
262
raise (
246
263
Source .GetItemsError (
247
264
Source .FailedGettingItems ({
@@ -351,11 +368,8 @@ let memoGetSelectionConfig = (~chain) => {
351
368
}
352
369
353
370
let makeThrowingGetEventBlock = (~getBlock ) => {
354
- // The block fields type is a subset of Ethers.JsonRpcProvider.block so we can safely cast
355
- let blockFieldsFromBlock : Ethers .JsonRpcProvider .block => Internal .eventBlock = Utils .magic
356
-
357
- async (log : Ethers .log ): Internal .eventBlock => {
358
- (await getBlock (log .blockNumber ))-> blockFieldsFromBlock
371
+ async (log : Ethers .log ) => {
372
+ await getBlock (log .blockNumber )
359
373
}
360
374
}
361
375
@@ -434,7 +448,7 @@ let sanitizeUrl = (url: string) => {
434
448
435
449
type options = {
436
450
sourceFor : Source .sourceFor ,
437
- syncConfig : Config . syncConfig ,
451
+ syncConfig : InternalConfig . sourceSync ,
438
452
url : string ,
439
453
chain : ChainMap .Chain .t ,
440
454
contracts : array <Internal .evmContractConfig >,
@@ -455,7 +469,7 @@ let make = ({sourceFor, syncConfig, url, chain, contracts, eventRouter}: options
455
469
456
470
let getSelectionConfig = memoGetSelectionConfig (~chain )
457
471
458
- let suggestedBlockIntervals = Js .Dict .empty ()
472
+ let mutSuggestedBlockIntervals = Js .Dict .empty ()
459
473
460
474
let transactionLoader = LazyLoader .make (
461
475
~loaderFn = transactionHash => provider -> Ethers .JsonRpcProvider .getTransaction (~transactionHash ),
@@ -478,7 +492,13 @@ let make = ({sourceFor, syncConfig, url, chain, contracts, eventRouter}: options
478
492
479
493
let blockLoader = LazyLoader .make (
480
494
~loaderFn = blockNumber =>
481
- getKnownBlockWithBackoff (~provider , ~sourceName = name , ~chain , ~backoffMsOnFailure = 1000 , ~blockNumber ),
495
+ getKnownBlockWithBackoff (
496
+ ~provider ,
497
+ ~sourceName = name ,
498
+ ~chain ,
499
+ ~backoffMsOnFailure = 1000 ,
500
+ ~blockNumber ,
501
+ ),
482
502
~onError = (am , ~exn ) => {
483
503
Logging .error ({
484
504
"err" : exn ,
@@ -523,10 +543,15 @@ let make = ({sourceFor, syncConfig, url, chain, contracts, eventRouter}: options
523
543
) => {
524
544
let startFetchingBatchTimeRef = Hrtime .makeTimer ()
525
545
526
- let suggestedBlockInterval =
527
- suggestedBlockIntervals
546
+ let suggestedBlockInterval = switch mutSuggestedBlockIntervals -> Utils .Dict .dangerouslyGetNonOption (
547
+ maxSuggestedBlockIntervalKey ,
548
+ ) {
549
+ | Some (maxSuggestedBlockInterval ) => maxSuggestedBlockInterval
550
+ | None =>
551
+ mutSuggestedBlockIntervals
528
552
-> Utils .Dict .dangerouslyGetNonOption (partitionId )
529
553
-> Belt .Option .getWithDefault (syncConfig .initialBlockInterval )
554
+ }
530
555
531
556
// Always have a toBlock for an RPC worker
532
557
let toBlock = switch toBlock {
@@ -554,18 +579,22 @@ let make = ({sourceFor, syncConfig, url, chain, contracts, eventRouter}: options
554
579
~loadBlock = blockNumber => blockLoader -> LazyLoader .get (blockNumber ),
555
580
~syncConfig ,
556
581
~provider ,
557
- ~suggestedBlockIntervals ,
582
+ ~mutSuggestedBlockIntervals ,
558
583
~partitionId ,
559
584
)
560
585
561
586
let executedBlockInterval = suggestedToBlock - fromBlock + 1
562
587
563
588
// Increase the suggested block interval only when it was actually applied
564
589
// and we didn't query to a hard toBlock
565
- if executedBlockInterval >= suggestedBlockInterval {
590
+ // We also don't care about it when we have a hard max block interval
591
+ if (
592
+ executedBlockInterval >= suggestedBlockInterval &&
593
+ ! (mutSuggestedBlockIntervals -> Utils .Dict .has (maxSuggestedBlockIntervalKey ))
594
+ ) {
566
595
// Increase batch size going forward, but do not increase past a configured maximum
567
596
// See: https://en.wikipedia.org/wiki/Additive_increase/multiplicative_decrease
568
- suggestedBlockIntervals -> Js .Dict .set (
597
+ mutSuggestedBlockIntervals -> Js .Dict .set (
569
598
partitionId ,
570
599
Pervasives .min (
571
600
executedBlockInterval + syncConfig .accelerationAdditive ,
@@ -634,15 +663,19 @@ let make = ({sourceFor, syncConfig, url, chain, contracts, eventRouter}: options
634
663
(
635
664
{
636
665
eventConfig : (eventConfig :> Internal .eventConfig ),
637
- timestamp : block -> Types .Block .getTimestamp ,
666
+ timestamp : block .timestamp ,
667
+ blockNumber : block .number ,
638
668
chain ,
639
- blockNumber : block -> Types .Block .getNumber ,
640
669
logIndex : log .logIndex ,
641
670
event : {
642
671
chainId : chain -> ChainMap .Chain .toChainId ,
643
672
params : decodedEvent .args ,
644
673
transaction ,
645
- block ,
674
+ // Unreliably expect that the Ethers block fields match the types in HyperIndex
675
+ // I assume this is wrong in some cases, so we need to fix it in the future
676
+ block : block -> (
677
+ Utils .magic : Ethers .JsonRpcProvider .block => Internal .eventBlock
678
+ ),
646
679
srcAddress : log .address ,
647
680
logIndex : log .logIndex ,
648
681
}-> Internal .fromGenericEvent ,
0 commit comments