@fluidframework/odsp-driver 1.2.1 → 2.0.0-internal.1.0.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/dist/compactSnapshotParser.d.ts.map +1 -1
- package/dist/compactSnapshotParser.js +4 -1
- package/dist/compactSnapshotParser.js.map +1 -1
- package/dist/compactSnapshotWriter.d.ts.map +1 -1
- package/dist/compactSnapshotWriter.js +6 -3
- package/dist/compactSnapshotWriter.js.map +1 -1
- package/dist/epochTracker.d.ts +1 -0
- package/dist/epochTracker.d.ts.map +1 -1
- package/dist/epochTracker.js +24 -5
- package/dist/epochTracker.js.map +1 -1
- package/dist/fetchSnapshot.d.ts.map +1 -1
- package/dist/fetchSnapshot.js +15 -12
- package/dist/fetchSnapshot.js.map +1 -1
- package/dist/getFileLink.d.ts +3 -6
- package/dist/getFileLink.d.ts.map +1 -1
- package/dist/getFileLink.js +27 -38
- package/dist/getFileLink.js.map +1 -1
- package/dist/odspDeltaStorageService.d.ts +2 -1
- package/dist/odspDeltaStorageService.d.ts.map +1 -1
- package/dist/odspDeltaStorageService.js +5 -4
- package/dist/odspDeltaStorageService.js.map +1 -1
- package/dist/odspDocumentService.d.ts +1 -0
- package/dist/odspDocumentService.d.ts.map +1 -1
- package/dist/odspDocumentService.js +11 -5
- package/dist/odspDocumentService.js.map +1 -1
- package/dist/odspDocumentStorageManager.d.ts +4 -3
- package/dist/odspDocumentStorageManager.d.ts.map +1 -1
- package/dist/odspDocumentStorageManager.js +67 -60
- package/dist/odspDocumentStorageManager.js.map +1 -1
- package/dist/odspDriverUrlResolverForShareLink.d.ts.map +1 -1
- package/dist/odspDriverUrlResolverForShareLink.js +4 -4
- package/dist/odspDriverUrlResolverForShareLink.js.map +1 -1
- package/dist/odspLocationRedirection.d.ts +14 -0
- package/dist/odspLocationRedirection.d.ts.map +1 -0
- package/dist/odspLocationRedirection.js +24 -0
- package/dist/odspLocationRedirection.js.map +1 -0
- package/dist/odspSummaryUploadManager.d.ts +2 -1
- package/dist/odspSummaryUploadManager.d.ts.map +1 -1
- package/dist/odspSummaryUploadManager.js +3 -4
- package/dist/odspSummaryUploadManager.js.map +1 -1
- package/dist/odspUrlHelper.js +2 -1
- package/dist/odspUrlHelper.js.map +1 -1
- package/dist/odspUtils.d.ts.map +1 -1
- package/dist/odspUtils.js +10 -2
- package/dist/odspUtils.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/retryUtils.d.ts.map +1 -1
- package/dist/retryUtils.js +8 -4
- package/dist/retryUtils.js.map +1 -1
- package/lib/compactSnapshotParser.d.ts.map +1 -1
- package/lib/compactSnapshotParser.js +4 -1
- package/lib/compactSnapshotParser.js.map +1 -1
- package/lib/compactSnapshotWriter.d.ts.map +1 -1
- package/lib/compactSnapshotWriter.js +6 -3
- package/lib/compactSnapshotWriter.js.map +1 -1
- package/lib/epochTracker.d.ts +1 -0
- package/lib/epochTracker.d.ts.map +1 -1
- package/lib/epochTracker.js +26 -7
- package/lib/epochTracker.js.map +1 -1
- package/lib/fetchSnapshot.d.ts.map +1 -1
- package/lib/fetchSnapshot.js +15 -12
- package/lib/fetchSnapshot.js.map +1 -1
- package/lib/getFileLink.d.ts +3 -6
- package/lib/getFileLink.d.ts.map +1 -1
- package/lib/getFileLink.js +29 -40
- package/lib/getFileLink.js.map +1 -1
- package/lib/odspDeltaStorageService.d.ts +2 -1
- package/lib/odspDeltaStorageService.d.ts.map +1 -1
- package/lib/odspDeltaStorageService.js +5 -4
- package/lib/odspDeltaStorageService.js.map +1 -1
- package/lib/odspDocumentService.d.ts +1 -0
- package/lib/odspDocumentService.d.ts.map +1 -1
- package/lib/odspDocumentService.js +13 -7
- package/lib/odspDocumentService.js.map +1 -1
- package/lib/odspDocumentStorageManager.d.ts +4 -3
- package/lib/odspDocumentStorageManager.d.ts.map +1 -1
- package/lib/odspDocumentStorageManager.js +68 -61
- package/lib/odspDocumentStorageManager.js.map +1 -1
- package/lib/odspDriverUrlResolverForShareLink.d.ts.map +1 -1
- package/lib/odspDriverUrlResolverForShareLink.js +4 -4
- package/lib/odspDriverUrlResolverForShareLink.js.map +1 -1
- package/lib/odspLocationRedirection.d.ts +14 -0
- package/lib/odspLocationRedirection.d.ts.map +1 -0
- package/lib/odspLocationRedirection.js +20 -0
- package/lib/odspLocationRedirection.js.map +1 -0
- package/lib/odspSummaryUploadManager.d.ts +2 -1
- package/lib/odspSummaryUploadManager.d.ts.map +1 -1
- package/lib/odspSummaryUploadManager.js +3 -4
- package/lib/odspSummaryUploadManager.js.map +1 -1
- package/lib/odspUrlHelper.js +2 -1
- package/lib/odspUrlHelper.js.map +1 -1
- package/lib/odspUtils.d.ts.map +1 -1
- package/lib/odspUtils.js +11 -3
- package/lib/odspUtils.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/retryUtils.d.ts.map +1 -1
- package/lib/retryUtils.js +9 -5
- package/lib/retryUtils.js.map +1 -1
- package/package.json +16 -16
- package/src/compactSnapshotParser.ts +3 -1
- package/src/compactSnapshotWriter.ts +6 -3
- package/src/epochTracker.ts +47 -7
- package/src/fetchSnapshot.ts +29 -15
- package/src/getFileLink.ts +33 -46
- package/src/odspDeltaStorageService.ts +4 -2
- package/src/odspDocumentService.ts +16 -12
- package/src/odspDocumentStorageManager.ts +49 -41
- package/src/odspDriverUrlResolverForShareLink.ts +2 -5
- package/src/odspLocationRedirection.ts +23 -0
- package/src/odspSummaryUploadManager.ts +3 -3
- package/src/odspUrlHelper.ts +1 -1
- package/src/odspUtils.ts +15 -4
- package/src/packageVersion.ts +1 -1
- package/src/retryUtils.ts +8 -5
|
@@ -16,6 +16,7 @@ import * as api from "@fluidframework/protocol-definitions";
|
|
|
16
16
|
import {
|
|
17
17
|
ISummaryContext,
|
|
18
18
|
DriverErrorType,
|
|
19
|
+
FetchSource,
|
|
19
20
|
} from "@fluidframework/driver-definitions";
|
|
20
21
|
import { RateLimiter, NonRetryableError } from "@fluidframework/driver-utils";
|
|
21
22
|
import {
|
|
@@ -92,6 +93,7 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
|
|
|
92
93
|
private readonly hostPolicy: HostStoragePolicyInternal,
|
|
93
94
|
private readonly epochTracker: EpochTracker,
|
|
94
95
|
private readonly flushCallback: () => Promise<FlushResult>,
|
|
96
|
+
private readonly relayServiceTenantAndSessionId: () => string,
|
|
95
97
|
private readonly snapshotFormatFetchType?: SnapshotFormatSupportType,
|
|
96
98
|
) {
|
|
97
99
|
super();
|
|
@@ -107,6 +109,7 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
|
|
|
107
109
|
logger,
|
|
108
110
|
epochTracker,
|
|
109
111
|
!!this.hostPolicy.sessionOptions?.forceAccessTokenViaAuthorizationHeader,
|
|
112
|
+
this.relayServiceTenantAndSessionId,
|
|
110
113
|
);
|
|
111
114
|
}
|
|
112
115
|
|
|
@@ -204,7 +207,7 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
|
|
|
204
207
|
return super.getSnapshotTree(version, scenarioName);
|
|
205
208
|
}
|
|
206
209
|
|
|
207
|
-
public async getVersions(blobid: string | null, count: number, scenarioName?: string): Promise<api.IVersion[]> {
|
|
210
|
+
public async getVersions(blobid: string | null, count: number, scenarioName?: string, fetchSource?: FetchSource): Promise<api.IVersion[]> {
|
|
208
211
|
// Regular load workflow uses blobId === documentID to indicate "latest".
|
|
209
212
|
if (blobid !== this.documentId && blobid) {
|
|
210
213
|
// FluidFetch & FluidDebugger tools use empty sting to query for versions
|
|
@@ -230,14 +233,19 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
|
|
|
230
233
|
const hostSnapshotOptions = this.hostPolicy.snapshotOptions;
|
|
231
234
|
const odspSnapshotCacheValue: ISnapshotContents = await PerformanceEvent.timedExecAsync(
|
|
232
235
|
this.logger,
|
|
233
|
-
{ eventName: "ObtainSnapshot" },
|
|
236
|
+
{ eventName: "ObtainSnapshot", fetchSource },
|
|
234
237
|
async (event: PerformanceEvent) => {
|
|
235
238
|
const props: GetVersionsTelemetryProps = {};
|
|
236
239
|
let retrievedSnapshot: ISnapshotContents | undefined;
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
this.
|
|
240
|
+
|
|
241
|
+
let method: string;
|
|
242
|
+
if (fetchSource === FetchSource.noCache) {
|
|
243
|
+
retrievedSnapshot = await this.fetchSnapshot(hostSnapshotOptions, scenarioName);
|
|
244
|
+
method = "networkOnly";
|
|
245
|
+
} else {
|
|
246
|
+
// Here's the logic to grab the persistent cache snapshot implemented by the host
|
|
247
|
+
// Epoch tracker is responsible for communicating with the persistent cache, handling epochs and cache versions
|
|
248
|
+
const cachedSnapshotP: Promise<ISnapshotContents | undefined> = this.epochTracker.get(createCacheSnapshotKey(this.odspResolvedUrl))
|
|
241
249
|
.then(async (snapshotCachedEntry: ISnapshotCachedEntry) => {
|
|
242
250
|
if (snapshotCachedEntry !== undefined) {
|
|
243
251
|
// If the cached entry does not contain the entry time, then assign it a default of 30 days old.
|
|
@@ -264,46 +272,46 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
|
|
|
264
272
|
return snapshotCachedEntry;
|
|
265
273
|
});
|
|
266
274
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
if (retrievedSnapshot === undefined) {
|
|
286
|
-
// if network failed -> wait for cache ( then return network failure)
|
|
287
|
-
// If cache returned empty or failed -> wait for network (success of failure)
|
|
288
|
-
if (promiseRaceWinner.index === 1) {
|
|
289
|
-
retrievedSnapshot = await cachedSnapshotP;
|
|
290
|
-
method = "cache";
|
|
291
|
-
}
|
|
275
|
+
// Based on the concurrentSnapshotFetch policy:
|
|
276
|
+
// Either retrieve both the network and cache snapshots concurrently and pick the first to return,
|
|
277
|
+
// or grab the cache value and then the network value if the cache value returns undefined.
|
|
278
|
+
if (this.hostPolicy.concurrentSnapshotFetch && !this.hostPolicy.summarizerClient) {
|
|
279
|
+
const networkSnapshotP = this.fetchSnapshot(hostSnapshotOptions, scenarioName);
|
|
280
|
+
|
|
281
|
+
// Ensure that failures on both paths are ignored initially.
|
|
282
|
+
// I.e. if cache fails for some reason, we will proceed with network result.
|
|
283
|
+
// And vice versa - if (for example) client is offline and network request fails first, we
|
|
284
|
+
// do want to attempt to succeed with cached data!
|
|
285
|
+
const promiseRaceWinner = await promiseRaceWithWinner([
|
|
286
|
+
cachedSnapshotP.catch(() => undefined),
|
|
287
|
+
networkSnapshotP.catch(() => undefined),
|
|
288
|
+
]);
|
|
289
|
+
retrievedSnapshot = promiseRaceWinner.value;
|
|
290
|
+
method = promiseRaceWinner.index === 0 ? "cache" : "network";
|
|
291
|
+
|
|
292
292
|
if (retrievedSnapshot === undefined) {
|
|
293
|
-
|
|
294
|
-
|
|
293
|
+
// if network failed -> wait for cache ( then return network failure)
|
|
294
|
+
// If cache returned empty or failed -> wait for network (success of failure)
|
|
295
|
+
if (promiseRaceWinner.index === 1) {
|
|
296
|
+
retrievedSnapshot = await cachedSnapshotP;
|
|
297
|
+
method = "cache";
|
|
298
|
+
}
|
|
299
|
+
if (retrievedSnapshot === undefined) {
|
|
300
|
+
retrievedSnapshot = await networkSnapshotP;
|
|
301
|
+
method = "network";
|
|
302
|
+
}
|
|
295
303
|
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
// while the first caller is awaiting later async code in this block.
|
|
304
|
+
} else {
|
|
305
|
+
// Note: There's a race condition here - another caller may come past the undefined check
|
|
306
|
+
// while the first caller is awaiting later async code in this block.
|
|
300
307
|
|
|
301
|
-
|
|
308
|
+
retrievedSnapshot = await cachedSnapshotP;
|
|
302
309
|
|
|
303
|
-
|
|
310
|
+
method = retrievedSnapshot !== undefined ? "cache" : "network";
|
|
304
311
|
|
|
305
|
-
|
|
306
|
-
|
|
312
|
+
if (retrievedSnapshot === undefined) {
|
|
313
|
+
retrievedSnapshot = await this.fetchSnapshot(hostSnapshotOptions, scenarioName);
|
|
314
|
+
}
|
|
307
315
|
}
|
|
308
316
|
}
|
|
309
317
|
if (method === "network") {
|
|
@@ -136,7 +136,7 @@ export class OdspDriverUrlResolverForShareLink implements IUrlResolver {
|
|
|
136
136
|
// We need to remove the nav param if set by host when setting the sharelink as otherwise the shareLinkId
|
|
137
137
|
// when redeeming the share link during the redeem fallback for trees latest call becomes greater than
|
|
138
138
|
// the eligible length.
|
|
139
|
-
odspResolvedUrl.shareLinkInfo = Object.assign(odspResolvedUrl.shareLinkInfo
|
|
139
|
+
odspResolvedUrl.shareLinkInfo = Object.assign(odspResolvedUrl.shareLinkInfo ?? {},
|
|
140
140
|
{ sharingLinkToRedeem: this.removeNavParam(request.url) });
|
|
141
141
|
}
|
|
142
142
|
if (odspResolvedUrl.itemId) {
|
|
@@ -171,10 +171,7 @@ export class OdspDriverUrlResolverForShareLink implements IUrlResolver {
|
|
|
171
171
|
return cachedLinkPromise;
|
|
172
172
|
}
|
|
173
173
|
const newLinkPromise = getFileLink(
|
|
174
|
-
this.shareLinkFetcherProps.tokenFetcher,
|
|
175
|
-
resolvedUrl,
|
|
176
|
-
this.shareLinkFetcherProps.identityType,
|
|
177
|
-
this.logger,
|
|
174
|
+
this.shareLinkFetcherProps.tokenFetcher, resolvedUrl, this.logger,
|
|
178
175
|
).catch((error) => {
|
|
179
176
|
// This should imply that error is a non-retriable error.
|
|
180
177
|
this.logger.sendErrorEvent({ eventName: "FluidFileUrlError" }, error);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { IFluidResolvedUrl } from "@fluidframework/driver-definitions";
|
|
7
|
+
import { IOdspResolvedUrl } from "@fluidframework/odsp-driver-definitions";
|
|
8
|
+
import { getOdspResolvedUrl } from "./odspUtils";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* It takes a resolved url with old siteUrl and creates a new resolved url with updated site url domain.
|
|
12
|
+
* @param resolvedUrl - Previous odsp resolved url with older site url.
|
|
13
|
+
* @param redirectLocation - Url at which the network call has to be made. It contains new site info.
|
|
14
|
+
* @returns - Resolved url after patching the correct siteUrl.
|
|
15
|
+
*/
|
|
16
|
+
export function patchOdspResolvedUrl(resolvedUrl: IFluidResolvedUrl, redirectLocation: string): IOdspResolvedUrl {
|
|
17
|
+
const odspResolvedUrl = { ...getOdspResolvedUrl(resolvedUrl) };
|
|
18
|
+
// Generate the new SiteUrl from the redirection location.
|
|
19
|
+
const newSiteDomain = new URL(redirectLocation).origin;
|
|
20
|
+
const newSiteUrl = `${newSiteDomain}${new URL(odspResolvedUrl.siteUrl).pathname}`;
|
|
21
|
+
odspResolvedUrl.siteUrl = newSiteUrl;
|
|
22
|
+
return odspResolvedUrl;
|
|
23
|
+
}
|
|
@@ -39,6 +39,7 @@ export class OdspSummaryUploadManager {
|
|
|
39
39
|
logger: ITelemetryLogger,
|
|
40
40
|
private readonly epochTracker: EpochTracker,
|
|
41
41
|
private readonly forceAccessTokenViaAuthorizationHeader: boolean,
|
|
42
|
+
private readonly relayServiceTenantAndSessionId: () => string,
|
|
42
43
|
) {
|
|
43
44
|
this.mc = loggerToMonitoringContext(logger);
|
|
44
45
|
}
|
|
@@ -93,9 +94,8 @@ export class OdspSummaryUploadManager {
|
|
|
93
94
|
this.forceAccessTokenViaAuthorizationHeader,
|
|
94
95
|
);
|
|
95
96
|
headers["Content-Type"] = "application/json";
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
97
|
+
headers["If-Match"] = `fluid:sessionid=${
|
|
98
|
+
this.relayServiceTenantAndSessionId()}${parentHandle ? `;containerid=${parentHandle}` : ""}`;
|
|
99
99
|
|
|
100
100
|
const postBody = JSON.stringify(snapshot);
|
|
101
101
|
|
package/src/odspUrlHelper.ts
CHANGED
|
@@ -69,7 +69,7 @@ export function isOdcUrl(url: string | URL): boolean {
|
|
|
69
69
|
// Format: /v2.1/drives('ABC123')/items('ABC123!123')
|
|
70
70
|
const odcODataRegex = /\/v2.1\/drives\('[^/]+'\)\/items\('[\da-z]+!\d+'\)/;
|
|
71
71
|
|
|
72
|
-
return !!(odcRegex.exec(path)
|
|
72
|
+
return !!(odcRegex.exec(path) ?? odcODataRegex.exec(path));
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
/**
|
package/src/odspUtils.ts
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
NetworkErrorBasic,
|
|
14
14
|
} from "@fluidframework/driver-utils";
|
|
15
15
|
import { assert, performance } from "@fluidframework/common-utils";
|
|
16
|
-
import { ChildLogger, PerformanceEvent, wrapError } from "@fluidframework/telemetry-utils";
|
|
16
|
+
import { ChildLogger, PerformanceEvent, TelemetryDataTag, wrapError } from "@fluidframework/telemetry-utils";
|
|
17
17
|
import {
|
|
18
18
|
fetchIncorrectResponse,
|
|
19
19
|
throwOdspNetworkError,
|
|
@@ -123,7 +123,8 @@ export async function fetchHelper(
|
|
|
123
123
|
}, (error) => {
|
|
124
124
|
const online = isOnline();
|
|
125
125
|
const errorText = `${error}`;
|
|
126
|
-
|
|
126
|
+
const urlRegex = /((http|https):\/\/(\S*))/i;
|
|
127
|
+
const redactedErrorText = errorText.replace(urlRegex, "REDACTED_URL");
|
|
127
128
|
// This error is thrown by fetch() when AbortSignal is provided and it gets cancelled
|
|
128
129
|
if (error.name === "AbortError") {
|
|
129
130
|
throw new RetryableError(
|
|
@@ -143,13 +144,23 @@ export async function fetchHelper(
|
|
|
143
144
|
if (online === OnlineStatus.Offline) {
|
|
144
145
|
throw new RetryableError(
|
|
145
146
|
// pre-0.58 error message prefix: Offline
|
|
146
|
-
`ODSP fetch failure (Offline): ${
|
|
147
|
+
`ODSP fetch failure (Offline): ${redactedErrorText}`,
|
|
148
|
+
DriverErrorType.offlineError,
|
|
149
|
+
{
|
|
150
|
+
driverVersion,
|
|
151
|
+
rawErrorMessage: { value: errorText, tag: TelemetryDataTag.UserData },
|
|
152
|
+
});
|
|
147
153
|
} else {
|
|
148
154
|
// It is perhaps still possible that this is due to being offline, the error does not reveal enough
|
|
149
155
|
// information to conclude. Could also be DNS errors, malformed fetch request, CSP violation, etc.
|
|
150
156
|
throw new RetryableError(
|
|
151
157
|
// pre-0.58 error message prefix: Fetch error
|
|
152
|
-
`ODSP fetch failure: ${
|
|
158
|
+
`ODSP fetch failure: ${redactedErrorText}`,
|
|
159
|
+
DriverErrorType.fetchFailure,
|
|
160
|
+
{
|
|
161
|
+
driverVersion,
|
|
162
|
+
rawErrorMessage: { value: errorText, tag: TelemetryDataTag.UserData },
|
|
163
|
+
});
|
|
153
164
|
}
|
|
154
165
|
});
|
|
155
166
|
}
|
package/src/packageVersion.ts
CHANGED
package/src/retryUtils.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
7
7
|
import { delay, performance } from "@fluidframework/common-utils";
|
|
8
|
-
import { canRetryOnError } from "@fluidframework/driver-utils";
|
|
8
|
+
import { canRetryOnError, getRetryDelayFromError } from "@fluidframework/driver-utils";
|
|
9
9
|
import { OdspErrorType } from "@fluidframework/odsp-driver-definitions";
|
|
10
10
|
import { Odsp409Error } from "./epochTracker";
|
|
11
11
|
|
|
@@ -43,8 +43,10 @@ export async function runWithRetry<T>(
|
|
|
43
43
|
|
|
44
44
|
const coherencyError = error?.[Odsp409Error] === true;
|
|
45
45
|
const serviceReadonlyError = error?.errorType === OdspErrorType.serviceReadOnly;
|
|
46
|
-
// Retry for retriable 409 coherency errors or serviceReadOnly errors.
|
|
47
|
-
|
|
46
|
+
// Retry for retriable 409 coherency errors or serviceReadOnly errors. These errors are always retriable
|
|
47
|
+
// unless someone specifically set canRetry = false on the error like in fetchSnapshot() flow. So in
|
|
48
|
+
// that case don't retry.
|
|
49
|
+
if (!((coherencyError || serviceReadonlyError) && canRetry)) {
|
|
48
50
|
throw error;
|
|
49
51
|
}
|
|
50
52
|
|
|
@@ -54,8 +56,8 @@ export async function runWithRetry<T>(
|
|
|
54
56
|
if (attempts === 5) {
|
|
55
57
|
logger.sendErrorEvent(
|
|
56
58
|
{
|
|
57
|
-
eventName: coherencyError ?
|
|
58
|
-
"
|
|
59
|
+
eventName: coherencyError ? "CoherencyErrorTooManyRetries" :
|
|
60
|
+
"ServiceReadonlyErrorTooManyRetries",
|
|
59
61
|
callName,
|
|
60
62
|
attempts,
|
|
61
63
|
duration: performance.now() - start, // record total wait time.
|
|
@@ -66,6 +68,7 @@ export async function runWithRetry<T>(
|
|
|
66
68
|
throw error;
|
|
67
69
|
}
|
|
68
70
|
|
|
71
|
+
retryAfter = getRetryDelayFromError(error) ?? retryAfter;
|
|
69
72
|
await delay(Math.floor(retryAfter));
|
|
70
73
|
retryAfter += retryAfter / 4 * (1 + Math.random());
|
|
71
74
|
lastError = error;
|