envio 2.32.3 → 2.32.7
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 +12 -11
- package/src/Utils.res +9 -0
- package/src/Utils.res.js +18 -0
- package/src/bindings/EventSource.res +20 -0
- package/src/bindings/EventSource.res.js +8 -0
- package/src/sources/HyperSync.res +10 -2
- package/src/sources/HyperSync.res.js +9 -4
- package/src/sources/HyperSyncClient.res +77 -20
- package/src/sources/HyperSyncClient.res.js +22 -6
- package/src/sources/HyperSyncHeightStream.res +105 -0
- package/src/sources/HyperSyncHeightStream.res.js +97 -0
- package/src/sources/HyperSyncSource.res +12 -1
- package/src/sources/HyperSyncSource.res.js +13 -3
- package/src/sources/Source.res +1 -0
- package/src/sources/SourceManager.res +172 -95
- package/src/sources/SourceManager.res.js +150 -87
- package/src/sources/SourceManager.resi +3 -3
|
@@ -19,6 +19,7 @@ var HyperSyncClient = require("./HyperSyncClient.res.js");
|
|
|
19
19
|
var Caml_splice_call = require("rescript/lib/js/caml_splice_call.js");
|
|
20
20
|
var HyperSyncJsonApi = require("./HyperSyncJsonApi.res.js");
|
|
21
21
|
var Caml_js_exceptions = require("rescript/lib/js/caml_js_exceptions.js");
|
|
22
|
+
var HyperSyncHeightStream = require("./HyperSyncHeightStream.res.js");
|
|
22
23
|
|
|
23
24
|
function getSelectionConfig(selection, chain) {
|
|
24
25
|
var nonOptionalBlockFieldNames = new Set();
|
|
@@ -52,7 +53,13 @@ function getSelectionConfig(selection, chain) {
|
|
|
52
53
|
Caml_splice_call.spliceObjApply(noAddressesTopicSelections, "push", [tmp]);
|
|
53
54
|
}));
|
|
54
55
|
var fieldSelection_block = Array.from(capitalizedBlockFields);
|
|
55
|
-
var fieldSelection_transaction = Array.from(capitalizedTransactionFields)
|
|
56
|
+
var fieldSelection_transaction = Belt_Array.map(Array.from(capitalizedTransactionFields), (function (field) {
|
|
57
|
+
if (field !== "Kind") {
|
|
58
|
+
return field;
|
|
59
|
+
} else {
|
|
60
|
+
return "Type";
|
|
61
|
+
}
|
|
62
|
+
}));
|
|
56
63
|
var fieldSelection_log = [
|
|
57
64
|
"Address",
|
|
58
65
|
"Data",
|
|
@@ -132,7 +139,7 @@ function make(param) {
|
|
|
132
139
|
var chain = param.chain;
|
|
133
140
|
var getSelectionConfig = memoGetSelectionConfig(chain);
|
|
134
141
|
var apiToken = Belt_Option.getWithDefault(param.apiToken, "3dc856dd-b0ea-494f-b27e-017b8b6b7e07");
|
|
135
|
-
var client = HyperSyncClient.make(endpointUrl, apiToken, param.clientTimeoutMillis, param.clientMaxRetries, !lowercaseAddresses);
|
|
142
|
+
var client = HyperSyncClient.make(endpointUrl, apiToken, param.clientTimeoutMillis, param.clientMaxRetries, !lowercaseAddresses, param.serializationFormat, param.enableQueryCaching, undefined, undefined, undefined);
|
|
136
143
|
var hscDecoder = {
|
|
137
144
|
contents: undefined
|
|
138
145
|
};
|
|
@@ -407,7 +414,10 @@ function make(param) {
|
|
|
407
414
|
return Js_exn.raiseError(height);
|
|
408
415
|
}
|
|
409
416
|
}),
|
|
410
|
-
getItemsOrThrow: getItemsOrThrow
|
|
417
|
+
getItemsOrThrow: getItemsOrThrow,
|
|
418
|
+
createHeightSubscription: (function (onHeight) {
|
|
419
|
+
return HyperSyncHeightStream.subscribe(endpointUrl, apiToken, onHeight);
|
|
420
|
+
})
|
|
411
421
|
};
|
|
412
422
|
}
|
|
413
423
|
|
package/src/sources/Source.res
CHANGED
|
@@ -2,13 +2,21 @@ open Belt
|
|
|
2
2
|
|
|
3
3
|
type sourceManagerStatus = Idle | WaitingForNewBlock | Querieng
|
|
4
4
|
|
|
5
|
+
type sourceState = {
|
|
6
|
+
source: Source.t,
|
|
7
|
+
mutable knownHeight: int,
|
|
8
|
+
mutable unsubscribe: option<unit => unit>,
|
|
9
|
+
mutable pendingHeightResolvers: array<int => unit>,
|
|
10
|
+
mutable disabled: bool,
|
|
11
|
+
}
|
|
12
|
+
|
|
5
13
|
// Ideally the ChainFetcher name suits this better
|
|
6
14
|
// But currently the ChainFetcher module is immutable
|
|
7
15
|
// and handles both processing and fetching.
|
|
8
16
|
// So this module is to encapsulate the fetching logic only
|
|
9
17
|
// with a mutable state for easier reasoning and testing.
|
|
10
18
|
type t = {
|
|
11
|
-
|
|
19
|
+
sourcesState: array<sourceState>,
|
|
12
20
|
mutable statusStart: Hrtime.timeRef,
|
|
13
21
|
mutable status: sourceManagerStatus,
|
|
14
22
|
maxPartitionConcurrency: int,
|
|
@@ -63,7 +71,13 @@ let make = (
|
|
|
63
71
|
)
|
|
64
72
|
{
|
|
65
73
|
maxPartitionConcurrency,
|
|
66
|
-
|
|
74
|
+
sourcesState: sources->Array.map(source => {
|
|
75
|
+
source,
|
|
76
|
+
knownHeight: 0,
|
|
77
|
+
unsubscribe: None,
|
|
78
|
+
pendingHeightResolvers: [],
|
|
79
|
+
disabled: false,
|
|
80
|
+
}),
|
|
67
81
|
activeSource: initialActiveSource,
|
|
68
82
|
waitingForNewBlockStateId: None,
|
|
69
83
|
fetchingPartitionsCount: 0,
|
|
@@ -100,13 +114,15 @@ let fetchNext = async (
|
|
|
100
114
|
) => {
|
|
101
115
|
let {maxPartitionConcurrency} = sourceManager
|
|
102
116
|
|
|
103
|
-
|
|
117
|
+
let nextQuery = fetchState->FetchState.getNextQuery(
|
|
104
118
|
~concurrencyLimit={
|
|
105
119
|
maxPartitionConcurrency - sourceManager.fetchingPartitionsCount
|
|
106
120
|
},
|
|
107
121
|
~currentBlockHeight,
|
|
108
122
|
~stateId,
|
|
109
|
-
)
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
switch nextQuery {
|
|
110
126
|
| ReachedMaxConcurrency
|
|
111
127
|
| NothingToQuery => ()
|
|
112
128
|
| WaitingForNewBlock =>
|
|
@@ -116,12 +132,12 @@ let fetchNext = async (
|
|
|
116
132
|
| None =>
|
|
117
133
|
sourceManager->trackNewStatus(~newStatus=WaitingForNewBlock)
|
|
118
134
|
sourceManager.waitingForNewBlockStateId = Some(stateId)
|
|
119
|
-
let
|
|
135
|
+
let knownHeight = await waitForNewBlock(~knownHeight=currentBlockHeight)
|
|
120
136
|
switch sourceManager.waitingForNewBlockStateId {
|
|
121
137
|
| Some(waitingStateId) if waitingStateId === stateId => {
|
|
122
138
|
sourceManager->trackNewStatus(~newStatus=Idle)
|
|
123
139
|
sourceManager.waitingForNewBlockStateId = None
|
|
124
|
-
onNewBlock(~
|
|
140
|
+
onNewBlock(~knownHeight)
|
|
125
141
|
}
|
|
126
142
|
| Some(_) // Don't reset it if we are waiting for another state
|
|
127
143
|
| None => ()
|
|
@@ -159,82 +175,135 @@ let fetchNext = async (
|
|
|
159
175
|
|
|
160
176
|
type status = Active | Stalled | Done
|
|
161
177
|
|
|
178
|
+
let disableSource = (sourceState: sourceState) => {
|
|
179
|
+
if !sourceState.disabled {
|
|
180
|
+
sourceState.disabled = true
|
|
181
|
+
switch sourceState.unsubscribe {
|
|
182
|
+
| Some(unsubscribe) => unsubscribe()
|
|
183
|
+
| None => ()
|
|
184
|
+
}
|
|
185
|
+
true
|
|
186
|
+
} else {
|
|
187
|
+
false
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
162
191
|
let getSourceNewHeight = async (
|
|
163
192
|
sourceManager,
|
|
164
|
-
~
|
|
165
|
-
~
|
|
193
|
+
~sourceState: sourceState,
|
|
194
|
+
~knownHeight,
|
|
166
195
|
~status: ref<status>,
|
|
167
196
|
~logger,
|
|
168
197
|
) => {
|
|
169
|
-
let
|
|
198
|
+
let source = sourceState.source
|
|
199
|
+
let initialHeight = sourceState.knownHeight
|
|
200
|
+
let newHeight = ref(initialHeight)
|
|
170
201
|
let retry = ref(0)
|
|
171
202
|
|
|
172
|
-
while newHeight.contents <=
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
203
|
+
while newHeight.contents <= knownHeight && status.contents !== Done {
|
|
204
|
+
// If subscription exists, wait for next height event
|
|
205
|
+
switch sourceState.unsubscribe {
|
|
206
|
+
| Some(_) =>
|
|
207
|
+
let height = await Promise.make((resolve, _reject) => {
|
|
208
|
+
sourceState.pendingHeightResolvers->Array.push(resolve)
|
|
178
209
|
})
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
source.
|
|
210
|
+
|
|
211
|
+
// Only accept heights greater than initialHeight
|
|
212
|
+
if height > initialHeight {
|
|
213
|
+
newHeight := height
|
|
214
|
+
}
|
|
215
|
+
| None =>
|
|
216
|
+
// No subscription, use REST polling
|
|
217
|
+
try {
|
|
218
|
+
// Use to detect if the source is taking too long to respond
|
|
219
|
+
let endTimer = Prometheus.SourceGetHeightDuration.startTimer({
|
|
220
|
+
"source": source.name,
|
|
221
|
+
"chainId": source.chain->ChainMap.Chain.toChainId,
|
|
222
|
+
})
|
|
223
|
+
let height = await source.getHeightOrThrow()
|
|
224
|
+
endTimer()
|
|
225
|
+
|
|
226
|
+
newHeight := height
|
|
227
|
+
if height <= knownHeight {
|
|
228
|
+
retry := 0
|
|
229
|
+
|
|
230
|
+
// If createHeightSubscription is available and height hasn't changed,
|
|
231
|
+
// create subscription instead of polling
|
|
232
|
+
switch source.createHeightSubscription {
|
|
233
|
+
| Some(createSubscription) =>
|
|
234
|
+
let unsubscribe = createSubscription(~onHeight=newHeight => {
|
|
235
|
+
sourceState.knownHeight = newHeight
|
|
236
|
+
// Resolve all pending height resolvers
|
|
237
|
+
let resolvers = sourceState.pendingHeightResolvers
|
|
238
|
+
sourceState.pendingHeightResolvers = []
|
|
239
|
+
resolvers->Array.forEach(resolve => resolve(newHeight))
|
|
240
|
+
})
|
|
241
|
+
sourceState.unsubscribe = Some(unsubscribe)
|
|
242
|
+
| None =>
|
|
243
|
+
// Slowdown polling when the chain isn't progressing
|
|
244
|
+
let pollingInterval = if status.contents === Stalled {
|
|
245
|
+
sourceManager.stalledPollingInterval
|
|
246
|
+
} else {
|
|
247
|
+
source.pollingInterval
|
|
248
|
+
}
|
|
249
|
+
await Utils.delay(pollingInterval)
|
|
250
|
+
}
|
|
190
251
|
}
|
|
191
|
-
|
|
252
|
+
} catch {
|
|
253
|
+
| exn =>
|
|
254
|
+
let retryInterval = sourceManager.getHeightRetryInterval(~retry=retry.contents)
|
|
255
|
+
logger->Logging.childTrace({
|
|
256
|
+
"msg": `Height retrieval from ${source.name} source failed. Retrying in ${retryInterval->Int.toString}ms.`,
|
|
257
|
+
"source": source.name,
|
|
258
|
+
"err": exn->Utils.prettifyExn,
|
|
259
|
+
})
|
|
260
|
+
retry := retry.contents + 1
|
|
261
|
+
await Utils.delay(retryInterval)
|
|
192
262
|
}
|
|
193
|
-
} catch {
|
|
194
|
-
| exn =>
|
|
195
|
-
let retryInterval = sourceManager.getHeightRetryInterval(~retry=retry.contents)
|
|
196
|
-
logger->Logging.childTrace({
|
|
197
|
-
"msg": `Height retrieval from ${source.name} source failed. Retrying in ${retryInterval->Int.toString}ms.`,
|
|
198
|
-
"source": source.name,
|
|
199
|
-
"err": exn->Utils.prettifyExn,
|
|
200
|
-
})
|
|
201
|
-
retry := retry.contents + 1
|
|
202
|
-
await Utils.delay(retryInterval)
|
|
203
263
|
}
|
|
204
264
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
265
|
+
|
|
266
|
+
// Update Prometheus only if height increased
|
|
267
|
+
if newHeight.contents > initialHeight {
|
|
268
|
+
Prometheus.SourceHeight.set(
|
|
269
|
+
~sourceName=source.name,
|
|
270
|
+
~chainId=source.chain->ChainMap.Chain.toChainId,
|
|
271
|
+
~blockNumber=newHeight.contents,
|
|
272
|
+
)
|
|
273
|
+
}
|
|
274
|
+
|
|
210
275
|
newHeight.contents
|
|
211
276
|
}
|
|
212
277
|
|
|
213
278
|
// Polls for a block height greater than the given block number to ensure a new block is available for indexing.
|
|
214
|
-
let waitForNewBlock = async (sourceManager: t, ~
|
|
215
|
-
let {
|
|
279
|
+
let waitForNewBlock = async (sourceManager: t, ~knownHeight) => {
|
|
280
|
+
let {sourcesState} = sourceManager
|
|
216
281
|
|
|
217
282
|
let logger = Logging.createChild(
|
|
218
283
|
~params={
|
|
219
284
|
"chainId": sourceManager.activeSource.chain->ChainMap.Chain.toChainId,
|
|
220
|
-
"
|
|
285
|
+
"knownHeight": knownHeight,
|
|
221
286
|
},
|
|
222
287
|
)
|
|
223
288
|
logger->Logging.childTrace("Initiating check for new blocks.")
|
|
224
289
|
|
|
225
290
|
let syncSources = []
|
|
226
291
|
let fallbackSources = []
|
|
227
|
-
|
|
228
|
-
|
|
292
|
+
sourcesState->Array.forEach(sourceState => {
|
|
293
|
+
let source = sourceState.source
|
|
294
|
+
if sourceState.disabled {
|
|
295
|
+
// Skip disabled sources
|
|
296
|
+
()
|
|
297
|
+
} else if (
|
|
229
298
|
source.sourceFor === Sync ||
|
|
230
299
|
// Even if the active source is a fallback, still include
|
|
231
300
|
// it to the list. So we don't wait for a timeout again
|
|
232
301
|
// if all main sync sources are still not valid
|
|
233
302
|
source === sourceManager.activeSource
|
|
234
303
|
) {
|
|
235
|
-
syncSources->Array.push(
|
|
304
|
+
syncSources->Array.push(sourceState)
|
|
236
305
|
} else {
|
|
237
|
-
fallbackSources->Array.push(
|
|
306
|
+
fallbackSources->Array.push(sourceState)
|
|
238
307
|
}
|
|
239
308
|
})
|
|
240
309
|
|
|
@@ -242,10 +311,10 @@ let waitForNewBlock = async (sourceManager: t, ~currentBlockHeight) => {
|
|
|
242
311
|
|
|
243
312
|
let (source, newBlockHeight) = await Promise.race(
|
|
244
313
|
syncSources
|
|
245
|
-
->Array.map(async
|
|
314
|
+
->Array.map(async sourceState => {
|
|
246
315
|
(
|
|
247
|
-
source,
|
|
248
|
-
await sourceManager->getSourceNewHeight(~
|
|
316
|
+
sourceState.source,
|
|
317
|
+
await sourceManager->getSourceNewHeight(~sourceState, ~knownHeight, ~status, ~logger),
|
|
249
318
|
)
|
|
250
319
|
})
|
|
251
320
|
->Array.concat([
|
|
@@ -269,15 +338,10 @@ let waitForNewBlock = async (sourceManager: t, ~currentBlockHeight) => {
|
|
|
269
338
|
// Promise.race will be forever pending if fallbackSources is empty
|
|
270
339
|
// which is good for this use case
|
|
271
340
|
Promise.race(
|
|
272
|
-
fallbackSources->Array.map(async
|
|
341
|
+
fallbackSources->Array.map(async sourceState => {
|
|
273
342
|
(
|
|
274
|
-
source,
|
|
275
|
-
await sourceManager->getSourceNewHeight(
|
|
276
|
-
~source,
|
|
277
|
-
~currentBlockHeight,
|
|
278
|
-
~status,
|
|
279
|
-
~logger,
|
|
280
|
-
),
|
|
343
|
+
sourceState.source,
|
|
344
|
+
await sourceManager->getSourceNewHeight(~sourceState, ~knownHeight, ~status, ~logger),
|
|
281
345
|
)
|
|
282
346
|
}),
|
|
283
347
|
)
|
|
@@ -300,11 +364,11 @@ let waitForNewBlock = async (sourceManager: t, ~currentBlockHeight) => {
|
|
|
300
364
|
newBlockHeight
|
|
301
365
|
}
|
|
302
366
|
|
|
303
|
-
let
|
|
367
|
+
let getNextSyncSourceState = (
|
|
304
368
|
sourceManager,
|
|
305
369
|
// This is needed to include the Fallback source to rotation
|
|
306
|
-
~
|
|
307
|
-
~
|
|
370
|
+
~initialSourceState: sourceState,
|
|
371
|
+
~currentSourceState: sourceState,
|
|
308
372
|
// After multiple failures start returning fallback sources as well
|
|
309
373
|
// But don't try it when main sync sources fail because of invalid configuration
|
|
310
374
|
// note: The logic might be changed in the future
|
|
@@ -315,16 +379,21 @@ let getNextSyncSource = (
|
|
|
315
379
|
|
|
316
380
|
let hasActive = ref(false)
|
|
317
381
|
|
|
318
|
-
sourceManager.
|
|
319
|
-
|
|
382
|
+
sourceManager.sourcesState->Array.forEach(sourceState => {
|
|
383
|
+
let source = sourceState.source
|
|
384
|
+
|
|
385
|
+
// Skip disabled sources
|
|
386
|
+
if sourceState.disabled {
|
|
387
|
+
()
|
|
388
|
+
} else if sourceState === currentSourceState {
|
|
320
389
|
hasActive := true
|
|
321
390
|
} else if (
|
|
322
391
|
switch source.sourceFor {
|
|
323
392
|
| Sync => true
|
|
324
|
-
| Fallback => attemptFallbacks ||
|
|
393
|
+
| Fallback => attemptFallbacks || sourceState === initialSourceState
|
|
325
394
|
}
|
|
326
395
|
) {
|
|
327
|
-
(hasActive.contents ? after : before)->Array.push(
|
|
396
|
+
(hasActive.contents ? after : before)->Array.push(sourceState)
|
|
328
397
|
}
|
|
329
398
|
})
|
|
330
399
|
|
|
@@ -333,7 +402,7 @@ let getNextSyncSource = (
|
|
|
333
402
|
| None =>
|
|
334
403
|
switch before->Array.get(0) {
|
|
335
404
|
| Some(s) => s
|
|
336
|
-
| None =>
|
|
405
|
+
| None => currentSourceState
|
|
337
406
|
}
|
|
338
407
|
}
|
|
339
408
|
}
|
|
@@ -349,12 +418,16 @@ let executeQuery = async (sourceManager: t, ~query: FetchState.query, ~currentBl
|
|
|
349
418
|
)
|
|
350
419
|
let responseRef = ref(None)
|
|
351
420
|
let retryRef = ref(0)
|
|
352
|
-
let
|
|
353
|
-
|
|
421
|
+
let initialSourceState =
|
|
422
|
+
sourceManager.sourcesState
|
|
423
|
+
->Js.Array2.find(s => s.source === sourceManager.activeSource)
|
|
424
|
+
->Option.getUnsafe
|
|
425
|
+
let sourceStateRef = ref(initialSourceState)
|
|
354
426
|
let shouldUpdateActiveSource = ref(false)
|
|
355
427
|
|
|
356
428
|
while responseRef.contents->Option.isNone {
|
|
357
|
-
let
|
|
429
|
+
let sourceState = sourceStateRef.contents
|
|
430
|
+
let source = sourceState.source
|
|
358
431
|
let toBlock = toBlockRef.contents
|
|
359
432
|
let retry = retryRef.contents
|
|
360
433
|
|
|
@@ -395,15 +468,19 @@ let executeQuery = async (sourceManager: t, ~query: FetchState.query, ~currentBl
|
|
|
395
468
|
switch error {
|
|
396
469
|
| UnsupportedSelection(_)
|
|
397
470
|
| FailedGettingFieldSelection(_) => {
|
|
398
|
-
let
|
|
471
|
+
let nextSourceState =
|
|
472
|
+
sourceManager->getNextSyncSourceState(
|
|
473
|
+
~initialSourceState,
|
|
474
|
+
~currentSourceState=sourceState,
|
|
475
|
+
)
|
|
399
476
|
|
|
400
|
-
// These errors are impossible to recover, so we
|
|
401
|
-
//
|
|
402
|
-
let
|
|
477
|
+
// These errors are impossible to recover, so we disable the source
|
|
478
|
+
// so it's not attempted anymore
|
|
479
|
+
let notAlreadyDisabled = disableSource(sourceState)
|
|
403
480
|
|
|
404
481
|
// In case there are multiple partitions
|
|
405
482
|
// failing at the same time. Log only once
|
|
406
|
-
if
|
|
483
|
+
if notAlreadyDisabled {
|
|
407
484
|
switch error {
|
|
408
485
|
| UnsupportedSelection({message}) => logger->Logging.childError(message)
|
|
409
486
|
| FailedGettingFieldSelection({exn, message, blockNumber, logIndex}) =>
|
|
@@ -417,7 +494,7 @@ let executeQuery = async (sourceManager: t, ~query: FetchState.query, ~currentBl
|
|
|
417
494
|
}
|
|
418
495
|
}
|
|
419
496
|
|
|
420
|
-
if
|
|
497
|
+
if nextSourceState === sourceState {
|
|
421
498
|
%raw(`null`)->ErrorHandling.mkLogAndRaise(
|
|
422
499
|
~logger,
|
|
423
500
|
~msg="The indexer doesn't have data-sources which can continue fetching. Please, check the error logs or reach out to the Envio team.",
|
|
@@ -425,9 +502,9 @@ let executeQuery = async (sourceManager: t, ~query: FetchState.query, ~currentBl
|
|
|
425
502
|
} else {
|
|
426
503
|
logger->Logging.childInfo({
|
|
427
504
|
"msg": "Switching to another data-source",
|
|
428
|
-
"source":
|
|
505
|
+
"source": nextSourceState.source.name,
|
|
429
506
|
})
|
|
430
|
-
|
|
507
|
+
sourceStateRef := nextSourceState
|
|
431
508
|
shouldUpdateActiveSource := true
|
|
432
509
|
retryRef := 0
|
|
433
510
|
}
|
|
@@ -441,14 +518,14 @@ let executeQuery = async (sourceManager: t, ~query: FetchState.query, ~currentBl
|
|
|
441
518
|
toBlockRef := Some(toBlock)
|
|
442
519
|
retryRef := 0
|
|
443
520
|
| FailedGettingItems({exn, attemptedToBlock, retry: ImpossibleForTheQuery({message})}) =>
|
|
444
|
-
let
|
|
445
|
-
sourceManager->
|
|
446
|
-
~
|
|
447
|
-
~
|
|
521
|
+
let nextSourceState =
|
|
522
|
+
sourceManager->getNextSyncSourceState(
|
|
523
|
+
~initialSourceState,
|
|
524
|
+
~currentSourceState=sourceState,
|
|
448
525
|
~attemptFallbacks=true,
|
|
449
526
|
)
|
|
450
527
|
|
|
451
|
-
let hasAnotherSource =
|
|
528
|
+
let hasAnotherSource = nextSourceState !== initialSourceState
|
|
452
529
|
|
|
453
530
|
logger->Logging.childWarn({
|
|
454
531
|
"msg": message ++ (hasAnotherSource ? " - Attempting to another source" : ""),
|
|
@@ -462,7 +539,7 @@ let executeQuery = async (sourceManager: t, ~query: FetchState.query, ~currentBl
|
|
|
462
539
|
~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
540
|
)
|
|
464
541
|
} else {
|
|
465
|
-
|
|
542
|
+
sourceStateRef := nextSourceState
|
|
466
543
|
shouldUpdateActiveSource := false
|
|
467
544
|
retryRef := 0
|
|
468
545
|
}
|
|
@@ -477,19 +554,19 @@ let executeQuery = async (sourceManager: t, ~query: FetchState.query, ~currentBl
|
|
|
477
554
|
// just keep the value high
|
|
478
555
|
let attemptFallbacks = retry >= 10
|
|
479
556
|
|
|
480
|
-
let
|
|
557
|
+
let nextSourceState = switch retry {
|
|
481
558
|
// Don't attempt a switch on first two failure
|
|
482
|
-
| 0 | 1 =>
|
|
559
|
+
| 0 | 1 => sourceState
|
|
483
560
|
| _ =>
|
|
484
561
|
// Then try to switch every second failure
|
|
485
562
|
if retry->mod(2) === 0 {
|
|
486
|
-
sourceManager->
|
|
487
|
-
~
|
|
563
|
+
sourceManager->getNextSyncSourceState(
|
|
564
|
+
~initialSourceState,
|
|
488
565
|
~attemptFallbacks,
|
|
489
|
-
~
|
|
566
|
+
~currentSourceState=sourceState,
|
|
490
567
|
)
|
|
491
568
|
} else {
|
|
492
|
-
|
|
569
|
+
sourceState
|
|
493
570
|
}
|
|
494
571
|
}
|
|
495
572
|
|
|
@@ -503,13 +580,13 @@ let executeQuery = async (sourceManager: t, ~query: FetchState.query, ~currentBl
|
|
|
503
580
|
"err": exn->Utils.prettifyExn,
|
|
504
581
|
})
|
|
505
582
|
|
|
506
|
-
let shouldSwitch =
|
|
583
|
+
let shouldSwitch = nextSourceState !== sourceState
|
|
507
584
|
if shouldSwitch {
|
|
508
585
|
logger->Logging.childInfo({
|
|
509
586
|
"msg": "Switching to another data-source",
|
|
510
|
-
"source":
|
|
587
|
+
"source": nextSourceState.source.name,
|
|
511
588
|
})
|
|
512
|
-
|
|
589
|
+
sourceStateRef := nextSourceState
|
|
513
590
|
shouldUpdateActiveSource := true
|
|
514
591
|
} else {
|
|
515
592
|
await Utils.delay(Pervasives.min(backoffMillis, 60_000))
|
|
@@ -523,7 +600,7 @@ let executeQuery = async (sourceManager: t, ~query: FetchState.query, ~currentBl
|
|
|
523
600
|
}
|
|
524
601
|
|
|
525
602
|
if shouldUpdateActiveSource.contents {
|
|
526
|
-
sourceManager.activeSource =
|
|
603
|
+
sourceManager.activeSource = sourceStateRef.contents.source
|
|
527
604
|
}
|
|
528
605
|
|
|
529
606
|
responseRef.contents->Option.getUnsafe
|