envio 2.31.1-rc.0 → 2.32.0-alpha.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "envio",
3
- "version": "v2.31.1-rc.0",
3
+ "version": "v2.32.0-alpha.0",
4
4
  "description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
5
5
  "bin": "./bin.js",
6
6
  "main": "./index.js",
@@ -25,10 +25,10 @@
25
25
  },
26
26
  "homepage": "https://envio.dev",
27
27
  "optionalDependencies": {
28
- "envio-linux-x64": "v2.31.1-rc.0",
29
- "envio-linux-arm64": "v2.31.1-rc.0",
30
- "envio-darwin-x64": "v2.31.1-rc.0",
31
- "envio-darwin-arm64": "v2.31.1-rc.0"
28
+ "envio-linux-x64": "v2.32.0-alpha.0",
29
+ "envio-linux-arm64": "v2.32.0-alpha.0",
30
+ "envio-darwin-x64": "v2.32.0-alpha.0",
31
+ "envio-darwin-arm64": "v2.32.0-alpha.0"
32
32
  },
33
33
  "dependencies": {
34
34
  "@envio-dev/hypersync-client": "0.6.6",
package/src/Batch.res CHANGED
@@ -528,3 +528,23 @@ let findFirstEventBlockNumber = (batch: t, ~chainId) => {
528
528
  }
529
529
  result.contents
530
530
  }
531
+
532
+ let findLastEventItem = (batch: t, ~chainId) => {
533
+ let idx = ref(batch.items->Array.length - 1)
534
+ let result = ref(None)
535
+ while idx.contents >= 0 && result.contents === None {
536
+ let item = batch.items->Array.getUnsafe(idx.contents)
537
+ switch item {
538
+ | Internal.Event(_) as eventItem => {
539
+ let eventItem = eventItem->Internal.castUnsafeEventItem
540
+ if eventItem.chain->ChainMap.Chain.toChainId === chainId {
541
+ result := Some(eventItem)
542
+ } else {
543
+ idx := idx.contents - 1
544
+ }
545
+ }
546
+ | Internal.Block(_) => idx := idx.contents - 1
547
+ }
548
+ }
549
+ result.contents
550
+ }
package/src/Batch.res.js CHANGED
@@ -361,6 +361,20 @@ function findFirstEventBlockNumber(batch, chainId) {
361
361
  return result;
362
362
  }
363
363
 
364
+ function findLastEventItem(batch, chainId) {
365
+ var idx = batch.items.length - 1;
366
+ var result;
367
+ while(idx >= 0 && result === undefined) {
368
+ var item = batch.items[idx];
369
+ if (item.kind === 0 && item.chain === chainId) {
370
+ result = item;
371
+ } else {
372
+ idx = idx - 1;
373
+ }
374
+ };
375
+ return result;
376
+ }
377
+
364
378
  exports.getOrderedNextChain = getOrderedNextChain;
365
379
  exports.immutableEmptyBatchSizePerChain = immutableEmptyBatchSizePerChain;
366
380
  exports.hasOrderedReadyItem = hasOrderedReadyItem;
@@ -372,4 +386,5 @@ exports.prepareOrderedBatch = prepareOrderedBatch;
372
386
  exports.prepareUnorderedBatch = prepareUnorderedBatch;
373
387
  exports.make = make;
374
388
  exports.findFirstEventBlockNumber = findFirstEventBlockNumber;
389
+ exports.findLastEventItem = findLastEventItem;
375
390
  /* Utils Not a pure module */
package/src/Envio.res CHANGED
@@ -68,7 +68,8 @@ let experimental_createEffect = (
68
68
  Internal.effectOutput,
69
69
  >
70
70
  ),
71
- callsCount: 0,
71
+ activeCallsCount: 0,
72
+ prevCallStartTimerRef: %raw(`null`),
72
73
  // This is the way to make the createEffect API
73
74
  // work without the need for users to call S.schema themselves,
74
75
  // but simply pass the desired object/tuple/etc.
package/src/Envio.res.js CHANGED
@@ -33,7 +33,8 @@ function experimental_createEffect(options, handler) {
33
33
  input: S$RescriptSchema.schema(function (param) {
34
34
  return options.input;
35
35
  }),
36
- callsCount: 0
36
+ activeCallsCount: 0,
37
+ prevCallStartTimerRef: null
37
38
  };
38
39
  }
39
40
 
package/src/Internal.res CHANGED
@@ -299,7 +299,9 @@ type effect = {
299
299
  cache: option<effectCacheMeta>,
300
300
  output: S.t<effectOutput>,
301
301
  input: S.t<effectInput>,
302
- mutable callsCount: int,
302
+ // The number of functions that are currently running.
303
+ mutable activeCallsCount: int,
304
+ mutable prevCallStartTimerRef: Hrtime.timeRef,
303
305
  }
304
306
  let cacheTablePrefix = "envio_effect_"
305
307
  let cacheOutputSchema = S.json(~validate=false)->(Utils.magic: S.t<Js.Json.t> => S.t<effectOutput>)
@@ -34,7 +34,7 @@ type initialState = {
34
34
  reorgCheckpoints: array<Internal.reorgCheckpoint>,
35
35
  }
36
36
 
37
- type operator = [#">" | #"="]
37
+ type operator = [#">" | #"=" | #"<"]
38
38
 
39
39
  type storage = {
40
40
  // Should return true if we already have persisted data
@@ -580,20 +580,46 @@ module ProgressBatchCount = {
580
580
  }
581
581
  }
582
582
 
583
+ module ProgressLatency = {
584
+ let gauge = SafeGauge.makeOrThrow(
585
+ ~name="envio_progress_latency",
586
+ ~help="The latency in milliseconds between the latest processed event creation and the time it was written to storage.",
587
+ ~labelSchema=chainIdLabelsSchema,
588
+ )
589
+
590
+ let set = (~latencyMs, ~chainId) => {
591
+ gauge->SafeGauge.handleInt(~labels=chainId, ~value=latencyMs)
592
+ }
593
+ }
594
+
583
595
  let effectLabelsSchema = S.object(s => {
584
596
  s.field("effect", S.string)
585
597
  })
586
598
 
587
- module EffectCallsCount = {
588
- let gauge = SafeGauge.makeOrThrow(
599
+ module EffectCalls = {
600
+ let timeCounter = SafeCounter.makeOrThrow(
601
+ ~name="envio_effect_calls_time",
602
+ ~help="Processing time taken to call the Effect function. (milliseconds)",
603
+ ~labelSchema=effectLabelsSchema,
604
+ )
605
+
606
+ let sumTimeCounter = SafeCounter.makeOrThrow(
607
+ ~name="envio_effect_calls_sum_time",
608
+ ~help="Cumulative time spent calling the Effect function during the indexing process. (milliseconds)",
609
+ ~labelSchema=effectLabelsSchema,
610
+ )
611
+
612
+ let totalCallsCount = SafeCounter.makeOrThrow(
589
613
  ~name="envio_effect_calls_count",
590
- ~help="The number of calls to the effect. Including both handler execution and cache hits.",
614
+ ~help="Cumulative number of resolved Effect function calls during the indexing process.",
591
615
  ~labelSchema=effectLabelsSchema,
592
616
  )
593
617
 
594
- let set = (~callsCount, ~effectName) => {
595
- gauge->SafeGauge.handleInt(~labels=effectName, ~value=callsCount)
596
- }
618
+ let activeCallsCount = SafeGauge.makeOrThrow(
619
+ ~name="envio_effect_active_calls_count",
620
+ ~help="The number of Effect function calls that are currently running.",
621
+ ~labelSchema=effectLabelsSchema,
622
+ )
597
623
  }
598
624
 
599
625
  module EffectCacheCount = {
@@ -629,8 +655,8 @@ module StorageLoad = {
629
655
  ~labelSchema=operationLabelsSchema,
630
656
  )
631
657
 
632
- let totalTimeCounter = SafeCounter.makeOrThrow(
633
- ~name="envio_storage_load_total_time",
658
+ let sumTimeCounter = SafeCounter.makeOrThrow(
659
+ ~name="envio_storage_load_sum_time",
634
660
  ~help="Cumulative time spent loading data from storage during the indexing process. (milliseconds)",
635
661
  ~labelSchema=operationLabelsSchema,
636
662
  )
@@ -686,7 +712,7 @@ module StorageLoad = {
686
712
  )
687
713
  operations->Utils.Dict.deleteInPlace(operation)
688
714
  }
689
- totalTimeCounter->SafeCounter.handleInt(
715
+ sumTimeCounter->SafeCounter.handleInt(
690
716
  ~labels={operation},
691
717
  ~value=timerRef->Hrtime.timeSince->Hrtime.toMillis->Hrtime.intFromMillis,
692
718
  )
@@ -688,19 +688,34 @@ var ProgressBatchCount = {
688
688
  increment: increment$5
689
689
  };
690
690
 
691
+ var gauge$20 = makeOrThrow$1("envio_progress_latency", "The latency in milliseconds between the latest processed event creation and the time it was written to storage.", chainIdLabelsSchema);
692
+
693
+ function set$19(latencyMs, chainId) {
694
+ handleInt$1(gauge$20, chainId, latencyMs);
695
+ }
696
+
697
+ var ProgressLatency = {
698
+ gauge: gauge$20,
699
+ set: set$19
700
+ };
701
+
691
702
  var effectLabelsSchema = S$RescriptSchema.object(function (s) {
692
703
  return s.f("effect", S$RescriptSchema.string);
693
704
  });
694
705
 
695
- var gauge$20 = makeOrThrow$1("envio_effect_calls_count", "The number of calls to the effect. Including both handler execution and cache hits.", effectLabelsSchema);
706
+ var timeCounter$2 = makeOrThrow("envio_effect_calls_time", "Processing time taken to call the Effect function. (milliseconds)", effectLabelsSchema);
696
707
 
697
- function set$19(callsCount, effectName) {
698
- handleInt$1(gauge$20, effectName, callsCount);
699
- }
708
+ var sumTimeCounter = makeOrThrow("envio_effect_calls_sum_time", "Cumulative time spent calling the Effect function during the indexing process. (milliseconds)", effectLabelsSchema);
700
709
 
701
- var EffectCallsCount = {
702
- gauge: gauge$20,
703
- set: set$19
710
+ var totalCallsCount = makeOrThrow("envio_effect_calls_count", "Cumulative number of resolved Effect function calls during the indexing process.", effectLabelsSchema);
711
+
712
+ var activeCallsCount = makeOrThrow$1("envio_effect_active_calls_count", "The number of Effect function calls that are currently running.", effectLabelsSchema);
713
+
714
+ var EffectCalls = {
715
+ timeCounter: timeCounter$2,
716
+ sumTimeCounter: sumTimeCounter,
717
+ totalCallsCount: totalCallsCount,
718
+ activeCallsCount: activeCallsCount
704
719
  };
705
720
 
706
721
  var gauge$21 = makeOrThrow$1("envio_effect_cache_count", "The number of items in the effect cache.", effectLabelsSchema);
@@ -729,9 +744,9 @@ var operationLabelsSchema = S$RescriptSchema.object(function (s) {
729
744
  return s.f("operation", S$RescriptSchema.string);
730
745
  });
731
746
 
732
- var timeCounter$2 = makeOrThrow("envio_storage_load_time", "Processing time taken to load data from storage. (milliseconds)", operationLabelsSchema);
747
+ var timeCounter$3 = makeOrThrow("envio_storage_load_time", "Processing time taken to load data from storage. (milliseconds)", operationLabelsSchema);
733
748
 
734
- var totalTimeCounter = makeOrThrow("envio_storage_load_total_time", "Cumulative time spent loading data from storage during the indexing process. (milliseconds)", operationLabelsSchema);
749
+ var sumTimeCounter$1 = makeOrThrow("envio_storage_load_sum_time", "Cumulative time spent loading data from storage during the indexing process. (milliseconds)", operationLabelsSchema);
735
750
 
736
751
  var counter$7 = makeOrThrow("envio_storage_load_count", "Cumulative number of successful storage load operations during the indexing process.", operationLabelsSchema);
737
752
 
@@ -758,10 +773,10 @@ function endOperation(timerRef, operation, whereSize, size) {
758
773
  var operationRef = operations[operation];
759
774
  operationRef.pendingCount = operationRef.pendingCount - 1 | 0;
760
775
  if (operationRef.pendingCount === 0) {
761
- handleInt(timeCounter$2, operation, Hrtime.intFromMillis(Hrtime.toMillis(Hrtime.timeSince(operationRef.timerRef))));
776
+ handleInt(timeCounter$3, operation, Hrtime.intFromMillis(Hrtime.toMillis(Hrtime.timeSince(operationRef.timerRef))));
762
777
  Utils.Dict.deleteInPlace(operations, operation);
763
778
  }
764
- handleInt(totalTimeCounter, operation, Hrtime.intFromMillis(Hrtime.toMillis(Hrtime.timeSince(timerRef))));
779
+ handleInt(sumTimeCounter$1, operation, Hrtime.intFromMillis(Hrtime.toMillis(Hrtime.timeSince(timerRef))));
765
780
  increment(counter$7, operation);
766
781
  handleInt(whereSizeCounter, operation, whereSize);
767
782
  handleInt(sizeCounter, operation, size);
@@ -769,8 +784,8 @@ function endOperation(timerRef, operation, whereSize, size) {
769
784
 
770
785
  var StorageLoad = {
771
786
  operationLabelsSchema: operationLabelsSchema,
772
- timeCounter: timeCounter$2,
773
- totalTimeCounter: totalTimeCounter,
787
+ timeCounter: timeCounter$3,
788
+ sumTimeCounter: sumTimeCounter$1,
774
789
  counter: counter$7,
775
790
  whereSizeCounter: whereSizeCounter,
776
791
  sizeCounter: sizeCounter,
@@ -824,8 +839,9 @@ exports.ProcessingMaxBatchSize = ProcessingMaxBatchSize;
824
839
  exports.ProgressBlockNumber = ProgressBlockNumber;
825
840
  exports.ProgressEventsCount = ProgressEventsCount;
826
841
  exports.ProgressBatchCount = ProgressBatchCount;
842
+ exports.ProgressLatency = ProgressLatency;
827
843
  exports.effectLabelsSchema = effectLabelsSchema;
828
- exports.EffectCallsCount = EffectCallsCount;
844
+ exports.EffectCalls = EffectCalls;
829
845
  exports.EffectCacheCount = EffectCacheCount;
830
846
  exports.EffectCacheInvalidationsCount = EffectCacheInvalidationsCount;
831
847
  exports.StorageLoad = StorageLoad;
@@ -188,19 +188,16 @@ Returns the latest block number which matches block number and hashes in the pro
188
188
  If it doesn't exist in the reorg threshold it returns NotFound
189
189
  */
190
190
  let getLatestValidScannedBlock = (
191
- self: t,
191
+ reorgDetection: t,
192
192
  ~blockNumbersAndHashes: array<blockDataWithTimestamp>,
193
- ~currentBlockHeight,
194
193
  ) => {
195
194
  let verifiedDataByBlockNumber = Js.Dict.empty()
196
195
  for idx in 0 to blockNumbersAndHashes->Array.length - 1 {
197
196
  let blockData = blockNumbersAndHashes->Array.getUnsafe(idx)
198
197
  verifiedDataByBlockNumber->Js.Dict.set(blockData.blockNumber->Int.toString, blockData)
199
198
  }
200
-
201
- let dataByBlockNumber = self->getDataByBlockNumberCopyInThreshold(~currentBlockHeight)
202
199
  // Js engine automatically orders numeric object keys
203
- let ascBlockNumberKeys = dataByBlockNumber->Js.Dict.keys
200
+ let ascBlockNumberKeys = verifiedDataByBlockNumber->Js.Dict.keys
204
201
 
205
202
  let getPrevScannedBlockNumber = idx =>
206
203
  ascBlockNumberKeys
@@ -216,15 +213,10 @@ let getLatestValidScannedBlock = (
216
213
  let rec loop = idx => {
217
214
  switch ascBlockNumberKeys->Belt.Array.get(idx) {
218
215
  | Some(blockNumberKey) =>
219
- let scannedBlock = dataByBlockNumber->Js.Dict.unsafeGet(blockNumberKey)
220
- switch verifiedDataByBlockNumber->Utils.Dict.dangerouslyGetNonOption(blockNumberKey) {
221
- | None =>
222
- Js.Exn.raiseError(
223
- `Unexpected case. Couldn't find verified hash for block number ${blockNumberKey}`,
224
- )
225
- | Some(verifiedBlockData) if verifiedBlockData.blockHash === scannedBlock.blockHash =>
226
- loop(idx + 1)
227
- | Some(_) => getPrevScannedBlockNumber(idx)
216
+ let scannedBlock = reorgDetection.dataByBlockNumber->Js.Dict.unsafeGet(blockNumberKey)
217
+ switch verifiedDataByBlockNumber->Js.Dict.unsafeGet(blockNumberKey) {
218
+ | verifiedBlockData if verifiedBlockData.blockHash === scannedBlock.blockHash => loop(idx + 1)
219
+ | _ => getPrevScannedBlockNumber(idx)
228
220
  }
229
221
  | None => getPrevScannedBlockNumber(idx)
230
222
  }
@@ -1,7 +1,6 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
  'use strict';
3
3
 
4
- var Js_exn = require("rescript/lib/js/js_exn.js");
5
4
  var Belt_Array = require("rescript/lib/js/belt_Array.js");
6
5
  var Belt_Option = require("rescript/lib/js/belt_Option.js");
7
6
 
@@ -108,14 +107,13 @@ function registerReorgGuard(self, reorgGuard, currentBlockHeight) {
108
107
  }
109
108
  }
110
109
 
111
- function getLatestValidScannedBlock(self, blockNumbersAndHashes, currentBlockHeight) {
110
+ function getLatestValidScannedBlock(reorgDetection, blockNumbersAndHashes) {
112
111
  var verifiedDataByBlockNumber = {};
113
112
  for(var idx = 0 ,idx_finish = blockNumbersAndHashes.length; idx < idx_finish; ++idx){
114
113
  var blockData = blockNumbersAndHashes[idx];
115
114
  verifiedDataByBlockNumber[String(blockData.blockNumber)] = blockData;
116
115
  }
117
- var dataByBlockNumber = getDataByBlockNumberCopyInThreshold(self, currentBlockHeight);
118
- var ascBlockNumberKeys = Object.keys(dataByBlockNumber);
116
+ var ascBlockNumberKeys = Object.keys(verifiedDataByBlockNumber);
119
117
  var getPrevScannedBlockNumber = function (idx) {
120
118
  return Belt_Option.flatMap(Belt_Array.get(ascBlockNumberKeys, idx - 1 | 0), (function (key) {
121
119
  var v = verifiedDataByBlockNumber[key];
@@ -132,11 +130,8 @@ function getLatestValidScannedBlock(self, blockNumbersAndHashes, currentBlockHei
132
130
  if (blockNumberKey === undefined) {
133
131
  return getPrevScannedBlockNumber(idx$1);
134
132
  }
135
- var scannedBlock = dataByBlockNumber[blockNumberKey];
133
+ var scannedBlock = reorgDetection.dataByBlockNumber[blockNumberKey];
136
134
  var verifiedBlockData = verifiedDataByBlockNumber[blockNumberKey];
137
- if (verifiedBlockData === undefined) {
138
- return Js_exn.raiseError("Unexpected case. Couldn't find verified hash for block number " + blockNumberKey);
139
- }
140
135
  if (verifiedBlockData.blockHash !== scannedBlock.blockHash) {
141
136
  return getPrevScannedBlockNumber(idx$1);
142
137
  }
@@ -258,14 +258,10 @@ let make = ({chain, endpointUrl}: options): t => {
258
258
  backoffMillis,
259
259
  })
260
260
  | UnexpectedMissingParams({missingParams}) =>
261
- WithBackoff({
262
- message: `Received page response with invalid data. Attempt a retry. Missing params: ${missingParams->Js.Array2.joinWith(
263
- ",",
261
+ ImpossibleForTheQuery({
262
+ message: `Source returned invalid data with missing required fields: ${missingParams->Js.Array2.joinWith(
263
+ ", ",
264
264
  )}`,
265
- backoffMillis: switch retry {
266
- | 0 => 1000
267
- | _ => 4000 * retry
268
- },
269
265
  })
270
266
  },
271
267
  }),
@@ -213,9 +213,8 @@ function make(param) {
213
213
  };
214
214
  } else {
215
215
  tmp = {
216
- TAG: "WithBackoff",
217
- message: "Received page response with invalid data. Attempt a retry. Missing params: " + error$1.missingParams.join(","),
218
- backoffMillis: retry !== 0 ? Math.imul(4000, retry) : 1000
216
+ TAG: "ImpossibleForTheQuery",
217
+ message: "Source returned invalid data with missing required fields: " + error$1.missingParams.join(", ")
219
218
  };
220
219
  }
221
220
  throw {
@@ -288,14 +288,10 @@ let make = (
288
288
  backoffMillis,
289
289
  })
290
290
  | UnexpectedMissingParams({missingParams}) =>
291
- WithBackoff({
292
- message: `Received page response with invalid data. Attempt a retry. Missing params: ${missingParams->Js.Array2.joinWith(
293
- ",",
291
+ ImpossibleForTheQuery({
292
+ message: `Source returned invalid data with missing required fields: ${missingParams->Js.Array2.joinWith(
293
+ ", ",
294
294
  )}`,
295
- backoffMillis: switch retry {
296
- | 0 => 1000
297
- | _ => 4000 * retry
298
- },
299
295
  })
300
296
  },
301
297
  }),
@@ -214,9 +214,8 @@ function make(param) {
214
214
  };
215
215
  } else {
216
216
  tmp = {
217
- TAG: "WithBackoff",
218
- message: "Received page response with invalid data. Attempt a retry. Missing params: " + error$1.missingParams.join(","),
219
- backoffMillis: retry !== 0 ? Math.imul(4000, retry) : 1000
217
+ TAG: "ImpossibleForTheQuery",
218
+ message: "Source returned invalid data with missing required fields: " + error$1.missingParams.join(", ")
220
219
  };
221
220
  }
222
221
  throw {
@@ -675,11 +675,12 @@ let make = (
675
675
  | exn =>
676
676
  raise(
677
677
  Source.GetItemsError(
678
- FailedParsingItems({
679
- message: "Failed to parse events using hypersync client decoder. Please double-check your ABI.",
678
+ FailedGettingItems({
680
679
  exn,
681
- blockNumber: fromBlock,
682
- logIndex: 0,
680
+ attemptedToBlock: toBlock,
681
+ retry: ImpossibleForTheQuery({
682
+ message: "Failed to parse events using hypersync client decoder. Please double-check your ABI.",
683
+ }),
683
684
  }),
684
685
  ),
685
686
  )
@@ -809,11 +810,12 @@ let make = (
809
810
  | exn =>
810
811
  raise(
811
812
  Source.GetItemsError(
812
- FailedParsingItems({
813
- message: "Failed to parse event with viem, please double-check your ABI.",
813
+ FailedGettingItems({
814
814
  exn,
815
- blockNumber,
816
- logIndex,
815
+ attemptedToBlock: toBlock,
816
+ retry: ImpossibleForTheQuery({
817
+ message: `Failed to parse event with viem, please double-check your ABI. Block number: ${blockNumber->Int.toString}, log index: ${logIndex->Int.toString}`,
818
+ }),
817
819
  }),
818
820
  ),
819
821
  )
@@ -632,11 +632,13 @@ function make(param) {
632
632
  throw {
633
633
  RE_EXN_ID: Source.GetItemsError,
634
634
  _1: {
635
- TAG: "FailedParsingItems",
635
+ TAG: "FailedGettingItems",
636
636
  exn: exn,
637
- blockNumber: fromBlock,
638
- logIndex: 0,
639
- message: "Failed to parse events using hypersync client decoder. Please double-check your ABI."
637
+ attemptedToBlock: toBlock$1,
638
+ retry: {
639
+ TAG: "ImpossibleForTheQuery",
640
+ message: "Failed to parse events using hypersync client decoder. Please double-check your ABI."
641
+ }
640
642
  },
641
643
  Error: new Error()
642
644
  };
@@ -732,11 +734,13 @@ function make(param) {
732
734
  throw {
733
735
  RE_EXN_ID: Source.GetItemsError,
734
736
  _1: {
735
- TAG: "FailedParsingItems",
737
+ TAG: "FailedGettingItems",
736
738
  exn: exn$1,
737
- blockNumber: blockNumber,
738
- logIndex: logIndex,
739
- message: "Failed to parse event with viem, please double-check your ABI."
739
+ attemptedToBlock: toBlock$1,
740
+ retry: {
741
+ TAG: "ImpossibleForTheQuery",
742
+ message: "Failed to parse event with viem, please double-check your ABI. Block number: " + String(blockNumber) + ", log index: " + String(logIndex)
743
+ }
740
744
  },
741
745
  Error: new Error()
742
746
  };
@@ -23,11 +23,11 @@ type blockRangeFetchResponse = {
23
23
  type getItemsRetry =
24
24
  | WithSuggestedToBlock({toBlock: int})
25
25
  | WithBackoff({message: string, backoffMillis: int})
26
+ | ImpossibleForTheQuery({message: string})
26
27
 
27
28
  type getItemsError =
28
29
  | UnsupportedSelection({message: string})
29
30
  | FailedGettingFieldSelection({exn: exn, blockNumber: int, logIndex: int, message: string})
30
- | FailedParsingItems({exn: exn, blockNumber: int, logIndex: int, message: string})
31
31
  | FailedGettingItems({exn: exn, attemptedToBlock: int, retry: getItemsRetry})
32
32
 
33
33
  exception GetItemsError(getItemsError)
@@ -304,6 +304,7 @@ let getNextSyncSource = (
304
304
  sourceManager,
305
305
  // This is needed to include the Fallback source to rotation
306
306
  ~initialSource,
307
+ ~currentSource,
307
308
  // After multiple failures start returning fallback sources as well
308
309
  // But don't try it when main sync sources fail because of invalid configuration
309
310
  // note: The logic might be changed in the future
@@ -315,7 +316,7 @@ let getNextSyncSource = (
315
316
  let hasActive = ref(false)
316
317
 
317
318
  sourceManager.sources->Utils.Set.forEach(source => {
318
- if source === sourceManager.activeSource {
319
+ if source === currentSource {
319
320
  hasActive := true
320
321
  } else if (
321
322
  switch source.sourceFor {
@@ -332,7 +333,7 @@ let getNextSyncSource = (
332
333
  | None =>
333
334
  switch before->Array.get(0) {
334
335
  | Some(s) => s
335
- | None => sourceManager.activeSource
336
+ | None => currentSource
336
337
  }
337
338
  }
338
339
  }
@@ -349,9 +350,11 @@ let executeQuery = async (sourceManager: t, ~query: FetchState.query, ~currentBl
349
350
  let responseRef = ref(None)
350
351
  let retryRef = ref(0)
351
352
  let initialSource = sourceManager.activeSource
353
+ let sourceRef = ref(initialSource)
354
+ let shouldUpdateActiveSource = ref(false)
352
355
 
353
356
  while responseRef.contents->Option.isNone {
354
- let source = sourceManager.activeSource
357
+ let source = sourceRef.contents
355
358
  let toBlock = toBlockRef.contents
356
359
  let retry = retryRef.contents
357
360
 
@@ -391,9 +394,8 @@ let executeQuery = async (sourceManager: t, ~query: FetchState.query, ~currentBl
391
394
  | Source.GetItemsError(error) =>
392
395
  switch error {
393
396
  | UnsupportedSelection(_)
394
- | FailedGettingFieldSelection(_)
395
- | FailedParsingItems(_) => {
396
- let nextSource = sourceManager->getNextSyncSource(~initialSource)
397
+ | FailedGettingFieldSelection(_) => {
398
+ let nextSource = sourceManager->getNextSyncSource(~initialSource, ~currentSource=source)
397
399
 
398
400
  // These errors are impossible to recover, so we delete the source
399
401
  // from sourceManager so it's not attempted anymore
@@ -404,8 +406,7 @@ let executeQuery = async (sourceManager: t, ~query: FetchState.query, ~currentBl
404
406
  if notAlreadyDeleted {
405
407
  switch error {
406
408
  | UnsupportedSelection({message}) => logger->Logging.childError(message)
407
- | FailedGettingFieldSelection({exn, message, blockNumber, logIndex})
408
- | FailedParsingItems({exn, message, blockNumber, logIndex}) =>
409
+ | FailedGettingFieldSelection({exn, message, blockNumber, logIndex}) =>
409
410
  logger->Logging.childError({
410
411
  "msg": message,
411
412
  "err": exn->Utils.prettifyExn,
@@ -426,7 +427,8 @@ let executeQuery = async (sourceManager: t, ~query: FetchState.query, ~currentBl
426
427
  "msg": "Switching to another data-source",
427
428
  "source": nextSource.name,
428
429
  })
429
- sourceManager.activeSource = nextSource
430
+ sourceRef := nextSource
431
+ shouldUpdateActiveSource := true
430
432
  retryRef := 0
431
433
  }
432
434
  }
@@ -438,6 +440,33 @@ let executeQuery = async (sourceManager: t, ~query: FetchState.query, ~currentBl
438
440
  })
439
441
  toBlockRef := Some(toBlock)
440
442
  retryRef := 0
443
+ | FailedGettingItems({exn, attemptedToBlock, retry: ImpossibleForTheQuery({message})}) =>
444
+ let nextSource =
445
+ sourceManager->getNextSyncSource(
446
+ ~initialSource,
447
+ ~currentSource=source,
448
+ ~attemptFallbacks=true,
449
+ )
450
+
451
+ let hasAnotherSource = nextSource !== initialSource
452
+
453
+ logger->Logging.childWarn({
454
+ "msg": message ++ (hasAnotherSource ? " - Attempting to another source" : ""),
455
+ "toBlock": attemptedToBlock,
456
+ "err": exn->Utils.prettifyExn,
457
+ })
458
+
459
+ if !hasAnotherSource {
460
+ %raw(`null`)->ErrorHandling.mkLogAndRaise(
461
+ ~logger,
462
+ ~msg="The indexer doesn't have data-sources which can continue fetching. Please, check the error logs or reach out to the Envio team.",
463
+ )
464
+ } else {
465
+ sourceRef := nextSource
466
+ shouldUpdateActiveSource := false
467
+ retryRef := 0
468
+ }
469
+
441
470
  | FailedGettingItems({exn, attemptedToBlock, retry: WithBackoff({message, backoffMillis})}) =>
442
471
  // Starting from the 11th failure (retry=10)
443
472
  // include fallback sources for switch
@@ -454,7 +483,11 @@ let executeQuery = async (sourceManager: t, ~query: FetchState.query, ~currentBl
454
483
  | _ =>
455
484
  // Then try to switch every second failure
456
485
  if retry->mod(2) === 0 {
457
- sourceManager->getNextSyncSource(~initialSource, ~attemptFallbacks)
486
+ sourceManager->getNextSyncSource(
487
+ ~initialSource,
488
+ ~attemptFallbacks,
489
+ ~currentSource=source,
490
+ )
458
491
  } else {
459
492
  source
460
493
  }
@@ -476,16 +509,22 @@ let executeQuery = async (sourceManager: t, ~query: FetchState.query, ~currentBl
476
509
  "msg": "Switching to another data-source",
477
510
  "source": nextSource.name,
478
511
  })
479
- sourceManager.activeSource = nextSource
512
+ sourceRef := nextSource
513
+ shouldUpdateActiveSource := true
480
514
  } else {
481
515
  await Utils.delay(Pervasives.min(backoffMillis, 60_000))
482
516
  }
483
517
  retryRef := retryRef.contents + 1
484
518
  }
519
+
485
520
  // TODO: Handle more error cases and hang/retry instead of throwing
486
521
  | exn => exn->ErrorHandling.mkLogAndRaise(~logger, ~msg="Failed to fetch block Range")
487
522
  }
488
523
  }
489
524
 
525
+ if shouldUpdateActiveSource.contents {
526
+ sourceManager.activeSource = sourceRef.contents
527
+ }
528
+
490
529
  responseRef.contents->Option.getUnsafe
491
530
  }
@@ -203,7 +203,7 @@ async function waitForNewBlock(sourceManager, currentBlockHeight) {
203
203
  return newBlockHeight;
204
204
  }
205
205
 
206
- function getNextSyncSource(sourceManager, initialSource, attemptFallbacksOpt) {
206
+ function getNextSyncSource(sourceManager, initialSource, currentSource, attemptFallbacksOpt) {
207
207
  var attemptFallbacks = attemptFallbacksOpt !== undefined ? attemptFallbacksOpt : false;
208
208
  var before = [];
209
209
  var after = [];
@@ -211,7 +211,7 @@ function getNextSyncSource(sourceManager, initialSource, attemptFallbacksOpt) {
211
211
  contents: false
212
212
  };
213
213
  sourceManager.sources.forEach(function (source) {
214
- if (source === sourceManager.activeSource) {
214
+ if (source === currentSource) {
215
215
  hasActive.contents = true;
216
216
  return ;
217
217
  }
@@ -234,7 +234,7 @@ function getNextSyncSource(sourceManager, initialSource, attemptFallbacksOpt) {
234
234
  if (s$1 !== undefined) {
235
235
  return s$1;
236
236
  } else {
237
- return sourceManager.activeSource;
237
+ return currentSource;
238
238
  }
239
239
  }
240
240
 
@@ -247,8 +247,10 @@ async function executeQuery(sourceManager, query, currentBlockHeight) {
247
247
  var responseRef;
248
248
  var retryRef = 0;
249
249
  var initialSource = sourceManager.activeSource;
250
+ var sourceRef = initialSource;
251
+ var shouldUpdateActiveSource = false;
250
252
  while(Belt_Option.isNone(responseRef)) {
251
- var source = sourceManager.activeSource;
253
+ var source = sourceRef;
252
254
  var toBlock = toBlockRef;
253
255
  var retry = retryRef;
254
256
  var logger = Logging.createChild({
@@ -275,85 +277,117 @@ async function executeQuery(sourceManager, query, currentBlockHeight) {
275
277
  var error = Caml_js_exceptions.internalToOCamlException(raw_error);
276
278
  if (error.RE_EXN_ID === Source.GetItemsError) {
277
279
  var error$1 = error._1;
278
- if (error$1.TAG === "FailedGettingItems") {
279
- var match$1 = error$1.retry;
280
- var attemptedToBlock = error$1.attemptedToBlock;
281
- if (match$1.TAG === "WithSuggestedToBlock") {
282
- var toBlock$1 = match$1.toBlock;
283
- Logging.childTrace(logger, {
284
- msg: "Failed getting data for the block range. Immediately retrying with the suggested block range from response.",
285
- toBlock: attemptedToBlock,
286
- suggestedToBlock: toBlock$1
287
- });
288
- toBlockRef = toBlock$1;
289
- retryRef = 0;
290
- } else {
291
- var backoffMillis = match$1.backoffMillis;
292
- var attemptFallbacks = retry >= 10;
293
- var nextSource = !(retry === 0 || retry === 1) && retry % 2 === 0 ? getNextSyncSource(sourceManager, initialSource, attemptFallbacks) : source;
294
- var log = retry >= 4 ? Logging.childWarn : Logging.childTrace;
295
- log(logger, {
296
- msg: match$1.message,
297
- toBlock: attemptedToBlock,
298
- backOffMilliseconds: backoffMillis,
299
- retry: retry,
300
- err: Utils.prettifyExn(error$1.exn)
301
- });
302
- var shouldSwitch = nextSource !== source;
303
- if (shouldSwitch) {
304
- Logging.childInfo(logger, {
305
- msg: "Switching to another data-source",
306
- source: nextSource.name
307
- });
308
- sourceManager.activeSource = nextSource;
309
- } else {
310
- await Utils.delay(backoffMillis < 60000 ? backoffMillis : 60000);
311
- }
312
- retryRef = retryRef + 1 | 0;
313
- }
314
- } else {
315
- var nextSource$1 = getNextSyncSource(sourceManager, initialSource, undefined);
280
+ var exit = 0;
281
+ switch (error$1.TAG) {
282
+ case "UnsupportedSelection" :
283
+ case "FailedGettingFieldSelection" :
284
+ exit = 1;
285
+ break;
286
+ case "FailedGettingItems" :
287
+ var match$1 = error$1.retry;
288
+ var attemptedToBlock = error$1.attemptedToBlock;
289
+ var exn = error$1.exn;
290
+ switch (match$1.TAG) {
291
+ case "WithSuggestedToBlock" :
292
+ var toBlock$1 = match$1.toBlock;
293
+ Logging.childTrace(logger, {
294
+ msg: "Failed getting data for the block range. Immediately retrying with the suggested block range from response.",
295
+ toBlock: attemptedToBlock,
296
+ suggestedToBlock: toBlock$1
297
+ });
298
+ toBlockRef = toBlock$1;
299
+ retryRef = 0;
300
+ break;
301
+ case "WithBackoff" :
302
+ var backoffMillis = match$1.backoffMillis;
303
+ var attemptFallbacks = retry >= 10;
304
+ var nextSource = !(retry === 0 || retry === 1) && retry % 2 === 0 ? getNextSyncSource(sourceManager, initialSource, source, attemptFallbacks) : source;
305
+ var log = retry >= 4 ? Logging.childWarn : Logging.childTrace;
306
+ log(logger, {
307
+ msg: match$1.message,
308
+ toBlock: attemptedToBlock,
309
+ backOffMilliseconds: backoffMillis,
310
+ retry: retry,
311
+ err: Utils.prettifyExn(exn)
312
+ });
313
+ var shouldSwitch = nextSource !== source;
314
+ if (shouldSwitch) {
315
+ Logging.childInfo(logger, {
316
+ msg: "Switching to another data-source",
317
+ source: nextSource.name
318
+ });
319
+ sourceRef = nextSource;
320
+ shouldUpdateActiveSource = true;
321
+ } else {
322
+ await Utils.delay(backoffMillis < 60000 ? backoffMillis : 60000);
323
+ }
324
+ retryRef = retryRef + 1 | 0;
325
+ break;
326
+ case "ImpossibleForTheQuery" :
327
+ var nextSource$1 = getNextSyncSource(sourceManager, initialSource, source, true);
328
+ var hasAnotherSource = nextSource$1 !== initialSource;
329
+ Logging.childWarn(logger, {
330
+ msg: match$1.message + (
331
+ hasAnotherSource ? " - Attempting to another source" : ""
332
+ ),
333
+ toBlock: attemptedToBlock,
334
+ err: Utils.prettifyExn(exn)
335
+ });
336
+ if (hasAnotherSource) {
337
+ sourceRef = nextSource$1;
338
+ shouldUpdateActiveSource = false;
339
+ retryRef = 0;
340
+ } else {
341
+ ErrorHandling.mkLogAndRaise(logger, "The indexer doesn't have data-sources which can continue fetching. Please, check the error logs or reach out to the Envio team.", null);
342
+ }
343
+ break;
344
+
345
+ }
346
+ break;
347
+
348
+ }
349
+ if (exit === 1) {
350
+ var nextSource$2 = getNextSyncSource(sourceManager, initialSource, source, undefined);
316
351
  var notAlreadyDeleted = sourceManager.sources.delete(source);
317
352
  if (notAlreadyDeleted) {
318
- var exit = 0;
319
353
  switch (error$1.TAG) {
320
354
  case "UnsupportedSelection" :
321
355
  Logging.childError(logger, error$1.message);
322
356
  break;
323
357
  case "FailedGettingFieldSelection" :
324
- case "FailedParsingItems" :
325
- exit = 1;
358
+ Logging.childError(logger, {
359
+ msg: error$1.message,
360
+ err: Utils.prettifyExn(error$1.exn),
361
+ blockNumber: error$1.blockNumber,
362
+ logIndex: error$1.logIndex
363
+ });
326
364
  break;
327
365
  case "FailedGettingItems" :
328
366
  break;
329
367
 
330
368
  }
331
- if (exit === 1) {
332
- Logging.childError(logger, {
333
- msg: error$1.message,
334
- err: Utils.prettifyExn(error$1.exn),
335
- blockNumber: error$1.blockNumber,
336
- logIndex: error$1.logIndex
337
- });
338
- }
339
-
340
369
  }
341
- if (nextSource$1 === source) {
370
+ if (nextSource$2 === source) {
342
371
  ErrorHandling.mkLogAndRaise(logger, "The indexer doesn't have data-sources which can continue fetching. Please, check the error logs or reach out to the Envio team.", null);
343
372
  } else {
344
373
  Logging.childInfo(logger, {
345
374
  msg: "Switching to another data-source",
346
- source: nextSource$1.name
375
+ source: nextSource$2.name
347
376
  });
348
- sourceManager.activeSource = nextSource$1;
377
+ sourceRef = nextSource$2;
378
+ shouldUpdateActiveSource = true;
349
379
  retryRef = 0;
350
380
  }
351
381
  }
382
+
352
383
  } else {
353
384
  ErrorHandling.mkLogAndRaise(logger, "Failed to fetch block Range", error);
354
385
  }
355
386
  }
356
387
  };
388
+ if (shouldUpdateActiveSource) {
389
+ sourceManager.activeSource = sourceRef;
390
+ }
357
391
  return responseRef;
358
392
  }
359
393