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 +5 -5
- package/src/Batch.res +20 -0
- package/src/Batch.res.js +15 -0
- package/src/Envio.res +2 -1
- package/src/Envio.res.js +2 -1
- package/src/Internal.res +3 -1
- package/src/Persistence.res +1 -1
- package/src/Prometheus.res +35 -9
- package/src/Prometheus.res.js +30 -14
- package/src/ReorgDetection.res +6 -14
- package/src/ReorgDetection.res.js +3 -8
- package/src/sources/HyperFuelSource.res +3 -7
- package/src/sources/HyperFuelSource.res.js +2 -3
- package/src/sources/HyperSyncSource.res +3 -7
- package/src/sources/HyperSyncSource.res.js +2 -3
- package/src/sources/RpcSource.res +10 -8
- package/src/sources/RpcSource.res.js +12 -8
- package/src/sources/Source.res +1 -1
- package/src/sources/SourceManager.res +50 -11
- package/src/sources/SourceManager.res.js +91 -57
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "envio",
|
|
3
|
-
"version": "v2.
|
|
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.
|
|
29
|
-
"envio-linux-arm64": "v2.
|
|
30
|
-
"envio-darwin-x64": "v2.
|
|
31
|
-
"envio-darwin-arm64": "v2.
|
|
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
|
-
|
|
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
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
|
-
|
|
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>)
|
package/src/Persistence.res
CHANGED
package/src/Prometheus.res
CHANGED
|
@@ -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
|
|
588
|
-
let
|
|
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="
|
|
614
|
+
~help="Cumulative number of resolved Effect function calls during the indexing process.",
|
|
591
615
|
~labelSchema=effectLabelsSchema,
|
|
592
616
|
)
|
|
593
617
|
|
|
594
|
-
let
|
|
595
|
-
|
|
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
|
|
633
|
-
~name="
|
|
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
|
-
|
|
715
|
+
sumTimeCounter->SafeCounter.handleInt(
|
|
690
716
|
~labels={operation},
|
|
691
717
|
~value=timerRef->Hrtime.timeSince->Hrtime.toMillis->Hrtime.intFromMillis,
|
|
692
718
|
)
|
package/src/Prometheus.res.js
CHANGED
|
@@ -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
|
|
706
|
+
var timeCounter$2 = makeOrThrow("envio_effect_calls_time", "Processing time taken to call the Effect function. (milliseconds)", effectLabelsSchema);
|
|
696
707
|
|
|
697
|
-
function
|
|
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
|
|
702
|
-
|
|
703
|
-
|
|
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$
|
|
747
|
+
var timeCounter$3 = makeOrThrow("envio_storage_load_time", "Processing time taken to load data from storage. (milliseconds)", operationLabelsSchema);
|
|
733
748
|
|
|
734
|
-
var
|
|
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$
|
|
776
|
+
handleInt(timeCounter$3, operation, Hrtime.intFromMillis(Hrtime.toMillis(Hrtime.timeSince(operationRef.timerRef))));
|
|
762
777
|
Utils.Dict.deleteInPlace(operations, operation);
|
|
763
778
|
}
|
|
764
|
-
handleInt(
|
|
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$
|
|
773
|
-
|
|
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.
|
|
844
|
+
exports.EffectCalls = EffectCalls;
|
|
829
845
|
exports.EffectCacheCount = EffectCacheCount;
|
|
830
846
|
exports.EffectCacheInvalidationsCount = EffectCacheInvalidationsCount;
|
|
831
847
|
exports.StorageLoad = StorageLoad;
|
package/src/ReorgDetection.res
CHANGED
|
@@ -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
|
-
|
|
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 =
|
|
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->
|
|
221
|
-
|
|
|
222
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
262
|
-
message: `
|
|
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: "
|
|
217
|
-
message: "
|
|
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
|
-
|
|
292
|
-
message: `
|
|
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: "
|
|
218
|
-
message: "
|
|
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
|
-
|
|
679
|
-
message: "Failed to parse events using hypersync client decoder. Please double-check your ABI.",
|
|
678
|
+
FailedGettingItems({
|
|
680
679
|
exn,
|
|
681
|
-
|
|
682
|
-
|
|
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
|
-
|
|
813
|
-
message: "Failed to parse event with viem, please double-check your ABI.",
|
|
813
|
+
FailedGettingItems({
|
|
814
814
|
exn,
|
|
815
|
-
|
|
816
|
-
|
|
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: "
|
|
635
|
+
TAG: "FailedGettingItems",
|
|
636
636
|
exn: exn,
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
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: "
|
|
737
|
+
TAG: "FailedGettingItems",
|
|
736
738
|
exn: exn$1,
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
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
|
};
|
package/src/sources/Source.res
CHANGED
|
@@ -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 ===
|
|
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 =>
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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 ===
|
|
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
|
|
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 =
|
|
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
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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
|
-
|
|
325
|
-
|
|
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$
|
|
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$
|
|
375
|
+
source: nextSource$2.name
|
|
347
376
|
});
|
|
348
|
-
|
|
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
|
|