envio 3.14.0-subgraph → 3.14.1
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/evm.schema.json +1 -1
- package/package.json +6 -6
- package/src/Config.res +0 -9
- package/src/Config.res.mjs +2 -6
- package/src/Core.res +0 -21
- package/src/Core.res.mjs +0 -17
- package/src/EventProcessing.res +6 -6
- package/src/EventProcessing.res.mjs +6 -8
- package/src/HandlerLoader.res +22 -48
- package/src/HandlerLoader.res.mjs +15 -41
- package/src/HandlerTypeCheck.res +207 -0
- package/src/HandlerTypeCheck.res.mjs +262 -0
- package/src/LoadLayer.res +7 -39
- package/src/LoadLayer.res.mjs +38 -65
- package/src/LoadLayer.resi +0 -28
- package/src/PgStorage.res +5 -24
- package/src/PgStorage.res.mjs +1 -18
- package/src/UserContext.res +62 -377
- package/src/UserContext.res.mjs +39 -297
- package/src/bindings/NodeJs.res +31 -0
- package/src/bindings/NodeJs.res.mjs +3 -0
- package/src/db/Table.res +0 -10
- package/src/db/Table.res.mjs +11 -22
- package/src/sources/EvmRpcClient.res +2 -0
- package/src/sources/RpcSource.res +17 -4
- package/src/sources/RpcSource.res.mjs +23 -5
- package/src/sources/SourceManager.res +63 -40
- package/src/sources/SourceManager.res.mjs +32 -20
- package/src/HandlerTypeCheck.mjs +0 -14
- package/src/HandlerTypeCheckWorker.mjs +0 -68
- package/src/SyncWrites.res +0 -86
- package/src/SyncWrites.res.mjs +0 -85
- package/src/subgraph/abi-types.ts +0 -63
- package/src/subgraph/assemblyscript.ts +0 -85
- package/src/subgraph/blocks.ts +0 -183
- package/src/subgraph/calls.ts +0 -149
- package/src/subgraph/conformance.ts +0 -83
- package/src/subgraph/errors.ts +0 -96
- package/src/subgraph/graph-cli-worker.mjs +0 -26
- package/src/subgraph/graph-cli.ts +0 -190
- package/src/subgraph/graph-ts-enums.ts +0 -28
- package/src/subgraph/graph-ts-types/VERSION +0 -2
- package/src/subgraph/graph-ts-types/chain/arweave.d.ts +0 -70
- package/src/subgraph/graph-ts-types/chain/cosmos.d.ts +0 -327
- package/src/subgraph/graph-ts-types/chain/ethereum.d.ts +0 -233
- package/src/subgraph/graph-ts-types/chain/near.d.ts +0 -253
- package/src/subgraph/graph-ts-types/chain/starknet.d.ts +0 -32
- package/src/subgraph/graph-ts-types/common/collections.d.ts +0 -136
- package/src/subgraph/graph-ts-types/common/conversion.d.ts +0 -11
- package/src/subgraph/graph-ts-types/common/datasource.d.ts +0 -30
- package/src/subgraph/graph-ts-types/common/eager-offset.d.ts +0 -0
- package/src/subgraph/graph-ts-types/common/json.d.ts +0 -17
- package/src/subgraph/graph-ts-types/common/numbers.d.ts +0 -120
- package/src/subgraph/graph-ts-types/common/value.d.ts +0 -120
- package/src/subgraph/graph-ts-types/common/yaml.d.ts +0 -90
- package/src/subgraph/graph-ts-types/global/global.d.ts +0 -194
- package/src/subgraph/graph-ts-types/helper-functions.d.ts +0 -22
- package/src/subgraph/graph-ts-types/index.d.ts +0 -102
- package/src/subgraph/graph-ts.ts +0 -2285
- package/src/subgraph/hosts.ts +0 -158
- package/src/subgraph/rpc.ts +0 -65
- package/src/subgraph/runtime.ts +0 -595
- package/src/subgraph/scope.ts +0 -68
|
@@ -93,6 +93,14 @@ function make(param) {
|
|
|
93
93
|
},
|
|
94
94
|
Error: new Error()
|
|
95
95
|
};
|
|
96
|
+
case "BehindHead" :
|
|
97
|
+
let blockNumber$1 = result.blockNumber;
|
|
98
|
+
throw {
|
|
99
|
+
RE_EXN_ID: Source.SourceBehindHead,
|
|
100
|
+
blockNumber: blockNumber$1 !== undefined ? blockNumber$1 : missing("blockNumber"),
|
|
101
|
+
requestStats: result.requestStats,
|
|
102
|
+
Error: new Error()
|
|
103
|
+
};
|
|
96
104
|
}
|
|
97
105
|
let parsingTimeRef = Performance.now();
|
|
98
106
|
let parsedQueueItems = EvmEventItem.toInternalItems(result.items, onEventRegistrations, chainId);
|
|
@@ -114,12 +122,22 @@ function make(param) {
|
|
|
114
122
|
let getBlockHashes = async (blockNumbers, param) => {
|
|
115
123
|
let match = await rpcClient.getBlockHashes(blockNumbers);
|
|
116
124
|
let result = match[0];
|
|
117
|
-
let
|
|
125
|
+
let match$1 = result.message;
|
|
126
|
+
let match$2 = result.behindHeadBlock;
|
|
118
127
|
return {
|
|
119
|
-
result:
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
128
|
+
result: match$1 !== undefined ? (
|
|
129
|
+
match$2 !== undefined ? ({
|
|
130
|
+
TAG: "Error",
|
|
131
|
+
_0: {
|
|
132
|
+
RE_EXN_ID: Source.SourceBehindHead,
|
|
133
|
+
blockNumber: match$2,
|
|
134
|
+
requestStats: result.requestStats
|
|
135
|
+
}
|
|
136
|
+
}) : ({
|
|
137
|
+
TAG: "Error",
|
|
138
|
+
_0: Primitive_exceptions.internalToException(new Error(match$1))
|
|
139
|
+
})
|
|
140
|
+
) : ({
|
|
123
141
|
TAG: "Ok",
|
|
124
142
|
_0: match[1]
|
|
125
143
|
}),
|
|
@@ -24,7 +24,7 @@ let recordStatsInto = (
|
|
|
24
24
|
aggregates: dict<requestStatAgg>,
|
|
25
25
|
requestStats: array<Source.requestStat>,
|
|
26
26
|
) => {
|
|
27
|
-
requestStats->Array.forEach(({method, seconds,
|
|
27
|
+
requestStats->Array.forEach(({method, seconds, ?responseBlocks}) => {
|
|
28
28
|
let agg = switch aggregates->Utils.Dict.dangerouslyGetNonOption(method) {
|
|
29
29
|
| Some(agg) => agg
|
|
30
30
|
| None =>
|
|
@@ -577,11 +577,14 @@ let backoffBeforeRetry = async (
|
|
|
577
577
|
~isRealtime,
|
|
578
578
|
~backoffMillis,
|
|
579
579
|
~minBackoffMillis=0,
|
|
580
|
+
~failoverAfterRetries=2,
|
|
580
581
|
~excludedSources=?,
|
|
581
582
|
) => {
|
|
582
|
-
// Give the source
|
|
583
|
+
// Give the source a few attempts before demoting it, then re-try a failover
|
|
583
584
|
// every second attempt.
|
|
584
|
-
let switchedToWorkingSource = if
|
|
585
|
+
let switchedToWorkingSource = if (
|
|
586
|
+
retry >= failoverAfterRetries && (retry - failoverAfterRetries)->mod(2) === 0
|
|
587
|
+
) {
|
|
585
588
|
let now = Date.now()
|
|
586
589
|
sourceState.lastFailedAt = Some(now)
|
|
587
590
|
switch sourceManager->getNextSource(~isRealtime, ~excludedSources?) {
|
|
@@ -608,6 +611,25 @@ let backoffBeforeRetry = async (
|
|
|
608
611
|
}
|
|
609
612
|
}
|
|
610
613
|
|
|
614
|
+
// A replica behind its siblings usually catches up within seconds, and failing
|
|
615
|
+
// over demotes an otherwise healthy source for the whole recovery timeout. So
|
|
616
|
+
// the source keeps the query for about 10s (100ms doubling, capped at 1s per
|
|
617
|
+
// attempt) before a failover is tried. Past that window the source is behind
|
|
618
|
+
// for real, and with nowhere to fail over to it is polled less and less often,
|
|
619
|
+
// doubling from 1s up to the shared cap.
|
|
620
|
+
let behindHeadFailoverRetries = 12
|
|
621
|
+
|
|
622
|
+
let behindHeadBackoffMillis = retry =>
|
|
623
|
+
if retry < behindHeadFailoverRetries {
|
|
624
|
+
Utils.expBackoff(~base=100, ~exp=retry, ~maxMillis=1_000)
|
|
625
|
+
} else {
|
|
626
|
+
Utils.expBackoff(
|
|
627
|
+
~base=1_000,
|
|
628
|
+
~exp=retry - behindHeadFailoverRetries,
|
|
629
|
+
~maxMillis=maxRetryBackoffMillis,
|
|
630
|
+
)
|
|
631
|
+
}
|
|
632
|
+
|
|
611
633
|
// The queried block hasn't reached the backend instance that served the
|
|
612
634
|
// request. Expected around the head of a load-balanced backend, so early
|
|
613
635
|
// attempts stay quiet and short; a source that stays behind fails over like any
|
|
@@ -619,18 +641,28 @@ let retryBehindHead = async (
|
|
|
619
641
|
~isRealtime,
|
|
620
642
|
~logger: Pino.t,
|
|
621
643
|
~blockNumber: int,
|
|
622
|
-
~method: string,
|
|
623
|
-
~err: exn,
|
|
624
644
|
~excludedSources=?,
|
|
625
645
|
) => {
|
|
626
|
-
let backoffMillis = retry->
|
|
627
|
-
let
|
|
646
|
+
let backoffMillis = retry->behindHeadBackoffMillis
|
|
647
|
+
let source = sourceState.source.name
|
|
648
|
+
let block = blockNumber->Int.toString
|
|
649
|
+
// The condition is routine and resolves without the user doing anything, so
|
|
650
|
+
// the wording says so; the error object would only add internals.
|
|
651
|
+
let (log, msg) = if retry < behindHeadFailoverRetries {
|
|
652
|
+
(
|
|
653
|
+
Logging.childTrace,
|
|
654
|
+
`${source} hasn't reached block ${block} on all of its servers yet. This is normal near the latest block and resolves by itself - retrying shortly.`,
|
|
655
|
+
)
|
|
656
|
+
} else {
|
|
657
|
+
(
|
|
658
|
+
Logging.childWarn,
|
|
659
|
+
`${source} is still catching up to block ${block}. This is a delay on the provider's side; indexing will continue automatically, using another data source if one is configured.`,
|
|
660
|
+
)
|
|
661
|
+
}
|
|
628
662
|
logger->log({
|
|
629
|
-
"msg":
|
|
630
|
-
"method": method,
|
|
663
|
+
"msg": msg,
|
|
631
664
|
"retry": retry,
|
|
632
665
|
"backOffMilliseconds": backoffMillis,
|
|
633
|
-
"err": err->Utils.prettifyExn,
|
|
634
666
|
})
|
|
635
667
|
await sourceManager->backoffBeforeRetry(
|
|
636
668
|
sourceState,
|
|
@@ -638,6 +670,7 @@ let retryBehindHead = async (
|
|
|
638
670
|
~isRealtime,
|
|
639
671
|
~backoffMillis,
|
|
640
672
|
~minBackoffMillis=minRecoverableBackoffMillis,
|
|
673
|
+
~failoverAfterRetries=behindHeadFailoverRetries,
|
|
641
674
|
~excludedSources?,
|
|
642
675
|
)
|
|
643
676
|
}
|
|
@@ -861,8 +894,7 @@ let waitForNewBlock = (sourceManager: t, ~knownHeight, ~isRealtime, ~reducedPoll
|
|
|
861
894
|
// Re-armed because distrusting a stream is a one-shot — the next height it
|
|
862
895
|
// delivers takes it back at its word — and the silence that earned it can
|
|
863
896
|
// come straight back.
|
|
864
|
-
let rec armPokeTimeout = () =>
|
|
865
|
-
pokeTimeoutId := Some(setTimeout(() => {
|
|
897
|
+
let rec armPokeTimeout = () => pokeTimeoutId := Some(setTimeout(() => {
|
|
866
898
|
pokeTimeoutId := None
|
|
867
899
|
if !settled.contents {
|
|
868
900
|
watched->Array.forEach(subscription => subscription.distrustStream())
|
|
@@ -879,8 +911,7 @@ let waitForNewBlock = (sourceManager: t, ~knownHeight, ~isRealtime, ~reducedPoll
|
|
|
879
911
|
// operator about when they hear about a quiet chain, and spreading that
|
|
880
912
|
// would report it early. Fires once — the warning is worth saying once per
|
|
881
913
|
// wait, and a fallback stays recruited.
|
|
882
|
-
let armStallTimeout = () =>
|
|
883
|
-
stallTimeoutId := Some(setTimeout(() => {
|
|
914
|
+
let armStallTimeout = () => stallTimeoutId := Some(setTimeout(() => {
|
|
884
915
|
stallTimeoutId := None
|
|
885
916
|
if !settled.contents {
|
|
886
917
|
stalled := true
|
|
@@ -917,9 +948,7 @@ let waitForNewBlock = (sourceManager: t, ~knownHeight, ~isRealtime, ~reducedPoll
|
|
|
917
948
|
}
|
|
918
949
|
|
|
919
950
|
// Recruited to poll, not to stream: see `watch`.
|
|
920
|
-
fallbackSources->Array.forEach(
|
|
921
|
-
sourceState => sourceState->watch(~withStream=false),
|
|
922
|
-
)
|
|
951
|
+
fallbackSources->Array.forEach(sourceState => sourceState->watch(~withStream=false))
|
|
923
952
|
|
|
924
953
|
// A fallback recruited here can still be holding a stream from an
|
|
925
954
|
// earlier wait that made it a primary, and a live stream is not
|
|
@@ -940,7 +969,6 @@ let waitForNewBlock = (sourceManager: t, ~knownHeight, ~isRealtime, ~reducedPoll
|
|
|
940
969
|
})
|
|
941
970
|
}
|
|
942
971
|
|
|
943
|
-
|
|
944
972
|
let executeQuery = async (
|
|
945
973
|
sourceManager: t,
|
|
946
974
|
~query: FetchState.query,
|
|
@@ -949,11 +977,12 @@ let executeQuery = async (
|
|
|
949
977
|
) => {
|
|
950
978
|
// Read when the manager actually runs out of sources, not on the way in: the
|
|
951
979
|
// reason is recorded by the failure that disabled the last one.
|
|
952
|
-
let noSourcesError = () =>
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
980
|
+
let noSourcesError = () =>
|
|
981
|
+
switch sourceManager.disableReason {
|
|
982
|
+
| Some(reason) =>
|
|
983
|
+
`The indexer doesn't have data-sources which can continue fetching. The last one was disabled because ${reason}`
|
|
984
|
+
| None => "The indexer doesn't have data-sources which can continue fetching. Please, check the error logs or reach out to the Envio team."
|
|
985
|
+
}
|
|
957
986
|
|
|
958
987
|
// Sources where the query is impossible - lazily allocated, excluded for the duration of this query
|
|
959
988
|
let excludedSourcesRef = ref(None)
|
|
@@ -1030,7 +1059,7 @@ let executeQuery = async (
|
|
|
1030
1059
|
await sourceManager->waitForRateLimitReset(~resetMs, ~retry, ~logger)
|
|
1031
1060
|
retryRef := retryRef.contents + 1
|
|
1032
1061
|
|
|
1033
|
-
| Source.SourceBehindHead({blockNumber, requestStats})
|
|
1062
|
+
| Source.SourceBehindHead({blockNumber, requestStats}) =>
|
|
1034
1063
|
sourceState->recordRequestStats(requestStats)
|
|
1035
1064
|
await sourceManager->retryBehindHead(
|
|
1036
1065
|
sourceState,
|
|
@@ -1038,8 +1067,6 @@ let executeQuery = async (
|
|
|
1038
1067
|
~isRealtime,
|
|
1039
1068
|
~logger,
|
|
1040
1069
|
~blockNumber,
|
|
1041
|
-
~method="getItems",
|
|
1042
|
-
~err,
|
|
1043
1070
|
~excludedSources=?excludedSourcesRef.contents,
|
|
1044
1071
|
)
|
|
1045
1072
|
retryRef := retryRef.contents + 1
|
|
@@ -1135,7 +1162,11 @@ let executeQuery = async (
|
|
|
1135
1162
|
})
|
|
1136
1163
|
retryRef := 0
|
|
1137
1164
|
|
|
1138
|
-
| FailedGettingItems({
|
|
1165
|
+
| FailedGettingItems({
|
|
1166
|
+
?exn,
|
|
1167
|
+
attemptedToBlock,
|
|
1168
|
+
retry: WithBackoff({message, backoffMillis}),
|
|
1169
|
+
}) =>
|
|
1139
1170
|
await sourceManager->retryFailedPage(
|
|
1140
1171
|
sourceState,
|
|
1141
1172
|
~retry,
|
|
@@ -1200,22 +1231,14 @@ let getBlockHashes = async (sourceManager: t, ~blockNumbers: array<int>, ~isReal
|
|
|
1200
1231
|
| Error(exn) => throw(exn)
|
|
1201
1232
|
}
|
|
1202
1233
|
} catch {
|
|
1203
|
-
|
|
1204
|
-
|
|
1234
|
+
// A failure arrives as `res.result`, whose `res.requestStats` is already
|
|
1235
|
+
// recorded above, so the stats the error carries are not recorded again.
|
|
1236
|
+
| Source.RateLimited({resetMs}) =>
|
|
1205
1237
|
await sourceManager->waitForRateLimitReset(~resetMs, ~retry, ~logger)
|
|
1206
1238
|
retryRef := retryRef.contents + 1
|
|
1207
1239
|
|
|
1208
|
-
| Source.SourceBehindHead({blockNumber
|
|
1209
|
-
|
|
1210
|
-
await sourceManager->retryBehindHead(
|
|
1211
|
-
sourceState,
|
|
1212
|
-
~retry,
|
|
1213
|
-
~isRealtime,
|
|
1214
|
-
~logger,
|
|
1215
|
-
~blockNumber,
|
|
1216
|
-
~method="getBlockHashes",
|
|
1217
|
-
~err,
|
|
1218
|
-
)
|
|
1240
|
+
| Source.SourceBehindHead({blockNumber}) =>
|
|
1241
|
+
await sourceManager->retryBehindHead(sourceState, ~retry, ~isRealtime, ~logger, ~blockNumber)
|
|
1219
1242
|
retryRef := retryRef.contents + 1
|
|
1220
1243
|
|
|
1221
1244
|
| Source.InconsistentResponse(_) as err =>
|
|
@@ -470,10 +470,11 @@ function getNextSource(sourceManager, isRealtime, excludedSources) {
|
|
|
470
470
|
}
|
|
471
471
|
}
|
|
472
472
|
|
|
473
|
-
async function backoffBeforeRetry(sourceManager, sourceState, retry, isRealtime, backoffMillis, minBackoffMillisOpt, excludedSources) {
|
|
473
|
+
async function backoffBeforeRetry(sourceManager, sourceState, retry, isRealtime, backoffMillis, minBackoffMillisOpt, failoverAfterRetriesOpt, excludedSources) {
|
|
474
474
|
let minBackoffMillis = minBackoffMillisOpt !== undefined ? minBackoffMillisOpt : 0;
|
|
475
|
+
let failoverAfterRetries = failoverAfterRetriesOpt !== undefined ? failoverAfterRetriesOpt : 2;
|
|
475
476
|
let switchedToWorkingSource;
|
|
476
|
-
if (retry >=
|
|
477
|
+
if (retry >= failoverAfterRetries && (retry - failoverAfterRetries | 0) % 2 === 0) {
|
|
477
478
|
let now = Date.now();
|
|
478
479
|
sourceState.lastFailedAt = now;
|
|
479
480
|
let next = getNextSource(sourceManager, isRealtime, excludedSources);
|
|
@@ -497,17 +498,31 @@ async function backoffBeforeRetry(sourceManager, sourceState, retry, isRealtime,
|
|
|
497
498
|
}
|
|
498
499
|
}
|
|
499
500
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
501
|
+
function behindHeadBackoffMillis(retry) {
|
|
502
|
+
if (retry < 12) {
|
|
503
|
+
return Utils.expBackoff(100, retry, 1000);
|
|
504
|
+
} else {
|
|
505
|
+
return Utils.expBackoff(1000, retry - 12 | 0, 60000);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
async function retryBehindHead(sourceManager, sourceState, retry, isRealtime, logger, blockNumber, excludedSources) {
|
|
510
|
+
let backoffMillis = behindHeadBackoffMillis(retry);
|
|
511
|
+
let source = sourceState.source.name;
|
|
512
|
+
let block = blockNumber.toString();
|
|
513
|
+
let match = retry < 12 ? [
|
|
514
|
+
Logging.childTrace,
|
|
515
|
+
source + ` hasn't reached block ` + block + ` on all of its servers yet. This is normal near the latest block and resolves by itself - retrying shortly.`
|
|
516
|
+
] : [
|
|
517
|
+
Logging.childWarn,
|
|
518
|
+
source + ` is still catching up to block ` + block + `. This is a delay on the provider's side; indexing will continue automatically, using another data source if one is configured.`
|
|
519
|
+
];
|
|
520
|
+
match[0](logger, {
|
|
521
|
+
msg: match[1],
|
|
506
522
|
retry: retry,
|
|
507
|
-
backOffMilliseconds: backoffMillis
|
|
508
|
-
err: Utils.prettifyExn(err)
|
|
523
|
+
backOffMilliseconds: backoffMillis
|
|
509
524
|
});
|
|
510
|
-
return await backoffBeforeRetry(sourceManager, sourceState, retry, isRealtime, backoffMillis, 50, excludedSources);
|
|
525
|
+
return await backoffBeforeRetry(sourceManager, sourceState, retry, isRealtime, backoffMillis, 50, 12, excludedSources);
|
|
511
526
|
}
|
|
512
527
|
|
|
513
528
|
function stalledLog(retry, warnAfter, msg, stalled) {
|
|
@@ -536,7 +551,7 @@ async function retryInconsistentResponse(sourceManager, sourceState, retry, isRe
|
|
|
536
551
|
err: Utils.prettifyExn(err)
|
|
537
552
|
});
|
|
538
553
|
Stdlib_Option.forEach(sourceState.source.onReorg, cb => cb());
|
|
539
|
-
return await backoffBeforeRetry(sourceManager, sourceState, retry, isRealtime, backoffMillis, 50, excludedSources);
|
|
554
|
+
return await backoffBeforeRetry(sourceManager, sourceState, retry, isRealtime, backoffMillis, 50, undefined, excludedSources);
|
|
540
555
|
}
|
|
541
556
|
|
|
542
557
|
async function retryFailedPage(sourceManager, sourceState, retry, isRealtime, logger, msg, attemptedToBlock, backoffMillis, minBackoffMillisOpt, err, excludedSources) {
|
|
@@ -549,7 +564,7 @@ async function retryFailedPage(sourceManager, sourceState, retry, isRealtime, lo
|
|
|
549
564
|
retry: retry,
|
|
550
565
|
err: Stdlib_Option.map(err, Utils.prettifyExn)
|
|
551
566
|
});
|
|
552
|
-
return await backoffBeforeRetry(sourceManager, sourceState, retry, isRealtime, backoffMillis, minBackoffMillis, excludedSources);
|
|
567
|
+
return await backoffBeforeRetry(sourceManager, sourceState, retry, isRealtime, backoffMillis, minBackoffMillis, undefined, excludedSources);
|
|
553
568
|
}
|
|
554
569
|
|
|
555
570
|
function waitForNewBlock(sourceManager, knownHeight, isRealtime, reducedPolling) {
|
|
@@ -736,7 +751,7 @@ async function executeQuery(sourceManager, query, knownHeight, isRealtime) {
|
|
|
736
751
|
retryRef = retryRef + 1 | 0;
|
|
737
752
|
} else if (err.RE_EXN_ID === Source.SourceBehindHead) {
|
|
738
753
|
recordStatsInto(sourceState.requestStats, err.requestStats);
|
|
739
|
-
await retryBehindHead(sourceManager, sourceState, retry, isRealtime, logger$2, err.blockNumber,
|
|
754
|
+
await retryBehindHead(sourceManager, sourceState, retry, isRealtime, logger$2, err.blockNumber, excludedSourcesRef);
|
|
740
755
|
retryRef = retryRef + 1 | 0;
|
|
741
756
|
} else if (err.RE_EXN_ID === Source.InconsistentResponse) {
|
|
742
757
|
await retryInconsistentResponse(sourceManager, sourceState, retry, isRealtime, logger$2, "getItems", err, excludedSourcesRef);
|
|
@@ -866,13 +881,10 @@ async function getBlockHashes(sourceManager, blockNumbers, isRealtime) {
|
|
|
866
881
|
} catch (raw_err) {
|
|
867
882
|
let err = Primitive_exceptions.internalToException(raw_err);
|
|
868
883
|
if (err.RE_EXN_ID === Source.RateLimited) {
|
|
869
|
-
|
|
870
|
-
recordStatsInto(sourceState.requestStats, match.requestStats);
|
|
871
|
-
await waitForRateLimitReset(sourceManager, match.resetMs, retry, logger$1);
|
|
884
|
+
await waitForRateLimitReset(sourceManager, err._1.resetMs, retry, logger$1);
|
|
872
885
|
retryRef = retryRef + 1 | 0;
|
|
873
886
|
} else if (err.RE_EXN_ID === Source.SourceBehindHead) {
|
|
874
|
-
|
|
875
|
-
await retryBehindHead(sourceManager, sourceState, retry, isRealtime, logger$1, err.blockNumber, "getBlockHashes", err, undefined);
|
|
887
|
+
await retryBehindHead(sourceManager, sourceState, retry, isRealtime, logger$1, err.blockNumber, undefined);
|
|
876
888
|
retryRef = retryRef + 1 | 0;
|
|
877
889
|
} else if (err.RE_EXN_ID === Source.InconsistentResponse) {
|
|
878
890
|
await retryInconsistentResponse(sourceManager, sourceState, retry, isRealtime, logger$1, "getBlockHashes", err, undefined);
|
|
@@ -886,7 +898,7 @@ async function getBlockHashes(sourceManager, blockNumbers, isRealtime) {
|
|
|
886
898
|
backOffMilliseconds: backoffMillis,
|
|
887
899
|
err: Utils.prettifyExn(err)
|
|
888
900
|
});
|
|
889
|
-
await backoffBeforeRetry(sourceManager, sourceState, retry, isRealtime, backoffMillis, 50, undefined);
|
|
901
|
+
await backoffBeforeRetry(sourceManager, sourceState, retry, isRealtime, backoffMillis, 50, undefined, undefined);
|
|
890
902
|
retryRef = retryRef + 1 | 0;
|
|
891
903
|
}
|
|
892
904
|
}
|
package/src/HandlerTypeCheck.mjs
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { Worker } from "node:worker_threads";
|
|
2
|
-
|
|
3
|
-
// The compiler blocks the thread it runs on, so it gets its own and the
|
|
4
|
-
// database setup carries on meanwhile.
|
|
5
|
-
export const check = (cwd, files) =>
|
|
6
|
-
new Promise((resolve, reject) => {
|
|
7
|
-
const worker = new Worker(new URL("./HandlerTypeCheckWorker.mjs", import.meta.url), {
|
|
8
|
-
workerData: { cwd, files },
|
|
9
|
-
});
|
|
10
|
-
worker.once("message", resolve);
|
|
11
|
-
worker.once("error", reject);
|
|
12
|
-
// Settles nothing once the result has arrived.
|
|
13
|
-
worker.once("exit", () => reject(new Error("The handler type check exited without a result.")));
|
|
14
|
-
});
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import { createRequire } from "node:module";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { stripVTControlCharacters } from "node:util";
|
|
5
|
-
import { parentPort, workerData } from "node:worker_threads";
|
|
6
|
-
|
|
7
|
-
const check = ({ cwd, files }) => {
|
|
8
|
-
// The ReScript compiler checks a ReScript project's handlers, so it has no
|
|
9
|
-
// reason to set up TypeScript, and the warning would only be noise there.
|
|
10
|
-
// `rescript.json` at the root is what makes codegen treat it as ReScript.
|
|
11
|
-
const skip = (warning) => (existsSync(path.join(cwd, "rescript.json")) ? {} : { skipped: warning });
|
|
12
|
-
|
|
13
|
-
let ts;
|
|
14
|
-
try {
|
|
15
|
-
// The project's own compiler, so handlers are checked exactly as the
|
|
16
|
-
// user's editor and `tsc` check them.
|
|
17
|
-
ts = createRequire(path.join(cwd, "package.json"))("typescript");
|
|
18
|
-
} catch {
|
|
19
|
-
return skip("Skipped the handler type check: the project doesn't depend on typescript.");
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const configPath = ts.findConfigFile(cwd, ts.sys.fileExists);
|
|
23
|
-
if (configPath === undefined) {
|
|
24
|
-
return skip(
|
|
25
|
-
"Skipped the handler type check: no tsconfig.json found. Add one to type-check handlers on start, like the one envio init creates."
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const host = {
|
|
30
|
-
getCanonicalFileName: (file) => file,
|
|
31
|
-
getCurrentDirectory: () => cwd,
|
|
32
|
-
getNewLine: () => "\n",
|
|
33
|
-
};
|
|
34
|
-
const format = (diagnostics) =>
|
|
35
|
-
stripVTControlCharacters(ts.formatDiagnosticsWithColorAndContext(diagnostics, host)).trimEnd();
|
|
36
|
-
|
|
37
|
-
const parsed = ts.getParsedCommandLineOfConfigFile(
|
|
38
|
-
configPath,
|
|
39
|
-
{ noEmit: true },
|
|
40
|
-
{
|
|
41
|
-
...ts.sys,
|
|
42
|
-
onUnRecoverableConfigFileDiagnostic: (diagnostic) => {
|
|
43
|
-
throw new Error(format([diagnostic]));
|
|
44
|
-
},
|
|
45
|
-
}
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
// The tsconfig's files are roots, as tsc makes them, since any of them can
|
|
49
|
-
// declare globals a handler relies on. Only the handlers' errors count.
|
|
50
|
-
const handlerFiles = files.map((file) => path.resolve(cwd, file));
|
|
51
|
-
const program = ts.createProgram({
|
|
52
|
-
rootNames: [...parsed.fileNames, ...handlerFiles],
|
|
53
|
-
options: parsed.options,
|
|
54
|
-
projectReferences: parsed.projectReferences,
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
// Every file is a root, so each has a source file. Without one,
|
|
58
|
-
// `getSemanticDiagnostics` would check the whole program.
|
|
59
|
-
const diagnostics = handlerFiles
|
|
60
|
-
.map((file) => program.getSourceFile(file))
|
|
61
|
-
.flatMap((sourceFile) => [
|
|
62
|
-
...program.getSyntacticDiagnostics(sourceFile),
|
|
63
|
-
...program.getSemanticDiagnostics(sourceFile),
|
|
64
|
-
]);
|
|
65
|
-
return diagnostics.length === 0 ? {} : { errors: format(diagnostics) };
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
parentPort.postMessage(check(workerData));
|
package/src/SyncWrites.res
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
// The writes one round of a synchronous handler has made. A round that
|
|
2
|
-
// suspends is replayed from the top, so its writes are held here rather than
|
|
3
|
-
// applied: the replay has to see the store exactly as the first round did, or a
|
|
4
|
-
// read-modify-write lands once per round.
|
|
5
|
-
|
|
6
|
-
type table = {
|
|
7
|
-
entityConfig: Internal.entityConfig,
|
|
8
|
-
scope: Internal.chainScope,
|
|
9
|
-
changes: dict<Change.t<Internal.entity>>,
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
// A handler runs on one chain, so an entity's scope is fixed for the whole
|
|
13
|
-
// round and the entity name is enough to key its table.
|
|
14
|
-
type t = dict<table>
|
|
15
|
-
|
|
16
|
-
let make = (): t => Dict.make()
|
|
17
|
-
|
|
18
|
-
let record = (writes: t, ~entityConfig: Internal.entityConfig, ~scope, change) => {
|
|
19
|
-
let table = switch writes->Utils.Dict.dangerouslyGetNonOption(entityConfig.name) {
|
|
20
|
-
| Some(table) => table
|
|
21
|
-
| None =>
|
|
22
|
-
let table = {entityConfig, scope, changes: Dict.make()}
|
|
23
|
-
writes->Dict.set(entityConfig.name, table)
|
|
24
|
-
table
|
|
25
|
-
}
|
|
26
|
-
table.changes->Dict.set(change->Change.getEntityId->EntityId.toKey, change)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
let changesOf = (writes: t, ~entityConfig: Internal.entityConfig) =>
|
|
30
|
-
switch writes->Utils.Dict.dangerouslyGetNonOption(entityConfig.name) {
|
|
31
|
-
| Some({changes}) => Some(changes)
|
|
32
|
-
| None => None
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// `Some(None)` is a delete this round made: the entity is known not to exist.
|
|
36
|
-
let getById = (writes: t, ~entityConfig, ~entityId: string) =>
|
|
37
|
-
switch writes->changesOf(~entityConfig) {
|
|
38
|
-
| Some(changes) =>
|
|
39
|
-
changes
|
|
40
|
-
->Utils.Dict.dangerouslyGetNonOption(entityId)
|
|
41
|
-
->Option.map(InMemoryTable.Entity.mapChangeToEntity)
|
|
42
|
-
| None => None
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// What the store answered, as it reads once this round's writes land: a row
|
|
46
|
-
// the round rewrote is re-tested against the filter, and one it created joins.
|
|
47
|
-
let overlayFilter = (
|
|
48
|
-
writes: t,
|
|
49
|
-
entities: array<Internal.entity>,
|
|
50
|
-
~entityConfig: Internal.entityConfig,
|
|
51
|
-
~filter: EntityFilter.t,
|
|
52
|
-
) =>
|
|
53
|
-
switch writes->changesOf(~entityConfig) {
|
|
54
|
-
| None => entities
|
|
55
|
-
| Some(changes) =>
|
|
56
|
-
let matches = filter->EntityFilter.makeMatcher(~table=entityConfig.table)
|
|
57
|
-
let untouched =
|
|
58
|
-
entities->Array.filter(entity =>
|
|
59
|
-
!(changes->Dict.has(entity->InMemoryTable.Entity.getEntityIdUnsafe))
|
|
60
|
-
)
|
|
61
|
-
let written =
|
|
62
|
-
changes
|
|
63
|
-
->Dict.valuesToArray
|
|
64
|
-
->Array.filterMap(change =>
|
|
65
|
-
switch change->InMemoryTable.Entity.mapChangeToEntity {
|
|
66
|
-
| Some(entity) if matches(entity) => Some(entity)
|
|
67
|
-
| _ => None
|
|
68
|
-
}
|
|
69
|
-
)
|
|
70
|
-
untouched->Array.concat(written)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// How a change lands in the in-memory store, whether written directly or
|
|
74
|
-
// committed at the end of a round.
|
|
75
|
-
let write = (indexerState, ~entityConfig, ~scope, change) =>
|
|
76
|
-
indexerState
|
|
77
|
-
->InMemoryStore.getInMemTable(~entityConfig, ~scope)
|
|
78
|
-
->InMemoryTable.Entity.set(
|
|
79
|
-
~committedCheckpointId=indexerState->IndexerState.committedCheckpointIdFor(~scope),
|
|
80
|
-
change,
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
let commit = (writes: t, ~indexerState) =>
|
|
84
|
-
writes->Utils.Dict.forEach(({entityConfig, scope, changes}) =>
|
|
85
|
-
changes->Utils.Dict.forEach(change => indexerState->write(~entityConfig, ~scope, change))
|
|
86
|
-
)
|
package/src/SyncWrites.res.mjs
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
-
|
|
3
|
-
import * as Utils from "./Utils.res.mjs";
|
|
4
|
-
import * as EntityId from "./EntityId.res.mjs";
|
|
5
|
-
import * as EntityFilter from "./db/EntityFilter.res.mjs";
|
|
6
|
-
import * as IndexerState from "./IndexerState.res.mjs";
|
|
7
|
-
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
8
|
-
import * as InMemoryStore from "./InMemoryStore.res.mjs";
|
|
9
|
-
import * as InMemoryTable from "./InMemoryTable.res.mjs";
|
|
10
|
-
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
11
|
-
|
|
12
|
-
function make() {
|
|
13
|
-
return {};
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function record(writes, entityConfig, scope, change) {
|
|
17
|
-
let table = writes[entityConfig.name];
|
|
18
|
-
let table$1;
|
|
19
|
-
if (table !== undefined) {
|
|
20
|
-
table$1 = table;
|
|
21
|
-
} else {
|
|
22
|
-
let table_changes = {};
|
|
23
|
-
let table$2 = {
|
|
24
|
-
entityConfig: entityConfig,
|
|
25
|
-
scope: scope,
|
|
26
|
-
changes: table_changes
|
|
27
|
-
};
|
|
28
|
-
writes[entityConfig.name] = table$2;
|
|
29
|
-
table$1 = table$2;
|
|
30
|
-
}
|
|
31
|
-
table$1.changes[EntityId.toKey(change.entityId)] = change;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function changesOf(writes, entityConfig) {
|
|
35
|
-
let match = writes[entityConfig.name];
|
|
36
|
-
if (match !== undefined) {
|
|
37
|
-
return match.changes;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function getById(writes, entityConfig, entityId) {
|
|
42
|
-
let changes = changesOf(writes, entityConfig);
|
|
43
|
-
if (changes !== undefined) {
|
|
44
|
-
return Stdlib_Option.map(changes[entityId], InMemoryTable.Entity.mapChangeToEntity);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function overlayFilter(writes, entities, entityConfig, filter) {
|
|
49
|
-
let changes = changesOf(writes, entityConfig);
|
|
50
|
-
if (changes === undefined) {
|
|
51
|
-
return entities;
|
|
52
|
-
}
|
|
53
|
-
let matches = EntityFilter.makeMatcher(filter, entityConfig.table);
|
|
54
|
-
let untouched = entities.filter(entity => !(InMemoryTable.Entity.getEntityIdUnsafe(entity) in changes));
|
|
55
|
-
let written = Stdlib_Array.filterMap(Object.values(changes), change => {
|
|
56
|
-
let entity = InMemoryTable.Entity.mapChangeToEntity(change);
|
|
57
|
-
if (entity !== undefined && matches(entity)) {
|
|
58
|
-
return entity;
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
return untouched.concat(written);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function write(indexerState, entityConfig, scope, change) {
|
|
65
|
-
InMemoryTable.Entity.set(InMemoryStore.getInMemTable(indexerState, entityConfig, scope), IndexerState.committedCheckpointIdFor(indexerState, scope), change);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function commit(writes, indexerState) {
|
|
69
|
-
Utils.Dict.forEach(writes, param => {
|
|
70
|
-
let scope = param.scope;
|
|
71
|
-
let entityConfig = param.entityConfig;
|
|
72
|
-
Utils.Dict.forEach(param.changes, change => write(indexerState, entityConfig, scope, change));
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export {
|
|
77
|
-
make,
|
|
78
|
-
record,
|
|
79
|
-
changesOf,
|
|
80
|
-
getById,
|
|
81
|
-
overlayFilter,
|
|
82
|
-
write,
|
|
83
|
-
commit,
|
|
84
|
-
}
|
|
85
|
-
/* Utils Not a pure module */
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Contract-call signatures and ABI type lists as graph codegen writes them.
|
|
3
|
-
* Free of envio imports: the graph-ts shim reads them too.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export type ParsedSignature = {
|
|
7
|
-
name: string;
|
|
8
|
-
inputs: string;
|
|
9
|
-
outputs: string;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* graph codegen emits `"balanceOf(address):(uint256)"`. Types only, no names —
|
|
14
|
-
* which is all `parseAbiParameters` needs.
|
|
15
|
-
*/
|
|
16
|
-
export function parseSignature(signature: string): ParsedSignature {
|
|
17
|
-
const open = signature.indexOf("(");
|
|
18
|
-
if (open === -1) {
|
|
19
|
-
throw new Error(`Unreadable contract call signature "${signature}"`);
|
|
20
|
-
}
|
|
21
|
-
const name = signature.slice(0, open);
|
|
22
|
-
|
|
23
|
-
let depth = 0;
|
|
24
|
-
let close = -1;
|
|
25
|
-
for (let index = open; index < signature.length; index++) {
|
|
26
|
-
const char = signature[index];
|
|
27
|
-
if (char === "(") depth++;
|
|
28
|
-
if (char === ")") {
|
|
29
|
-
depth--;
|
|
30
|
-
if (depth === 0) {
|
|
31
|
-
close = index;
|
|
32
|
-
break;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
if (close === -1) {
|
|
37
|
-
throw new Error(`Unreadable contract call signature "${signature}"`);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const inputs = signature.slice(open + 1, close);
|
|
41
|
-
const rest = signature.slice(close + 1).replace(/^:/, "");
|
|
42
|
-
const outputs = rest.startsWith("(") ? rest.slice(1, -1) : rest;
|
|
43
|
-
|
|
44
|
-
return { name, inputs, outputs };
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/** `address,(uint256,bytes)` → `["address", "(uint256,bytes)"]`. */
|
|
48
|
-
export function splitTypes(list: string): string[] {
|
|
49
|
-
const types: string[] = [];
|
|
50
|
-
let depth = 0;
|
|
51
|
-
let start = 0;
|
|
52
|
-
for (let index = 0; index < list.length; index++) {
|
|
53
|
-
const char = list[index];
|
|
54
|
-
if (char === "(") depth++;
|
|
55
|
-
else if (char === ")") depth--;
|
|
56
|
-
else if (char === "," && depth === 0) {
|
|
57
|
-
types.push(list.slice(start, index));
|
|
58
|
-
start = index + 1;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
if (list.length > 0) types.push(list.slice(start));
|
|
62
|
-
return types;
|
|
63
|
-
}
|