@fluidframework/odsp-driver 1.2.2 → 2.0.0-internal.1.0.0.82159
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/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 +19 -12
- package/dist/fetchSnapshot.js.map +1 -1
- package/dist/getFileLink.d.ts.map +1 -1
- package/dist/getFileLink.js +17 -25
- 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.js +3 -3
- 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/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 +19 -12
- package/lib/fetchSnapshot.js.map +1 -1
- package/lib/getFileLink.d.ts.map +1 -1
- package/lib/getFileLink.js +19 -27
- 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.js +3 -3
- 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/package.json +15 -15
- package/src/epochTracker.ts +47 -7
- package/src/fetchSnapshot.ts +29 -15
- package/src/getFileLink.ts +22 -25
- package/src/odspDeltaStorageService.ts +4 -2
- package/src/odspDocumentService.ts +16 -12
- package/src/odspDocumentStorageManager.ts +49 -41
- package/src/odspDriverUrlResolverForShareLink.ts +1 -1
- 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/lib/odspUtils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"odspUtils.js","sourceRoot":"","sources":["../src/odspUtils.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryProperties, ITelemetryBaseLogger, ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { IResolvedUrl, DriverErrorType } from \"@fluidframework/driver-definitions\";\nimport {\n isOnline,\n OnlineStatus,\n RetryableError,\n NonRetryableError,\n NetworkErrorBasic,\n} from \"@fluidframework/driver-utils\";\nimport { assert, performance } from \"@fluidframework/common-utils\";\nimport { ChildLogger, PerformanceEvent, wrapError } from \"@fluidframework/telemetry-utils\";\nimport {\n fetchIncorrectResponse,\n throwOdspNetworkError,\n getSPOAndGraphRequestIdsFromResponse,\n} from \"@fluidframework/odsp-doclib-utils\";\nimport {\n IOdspResolvedUrl,\n TokenFetchOptions,\n OdspErrorType,\n tokenFromResponse,\n isTokenFromCache,\n OdspResourceTokenFetchOptions,\n ShareLinkTypes,\n TokenFetcher,\n ICacheEntry,\n snapshotKey,\n InstrumentedStorageTokenFetcher,\n IOdspUrlParts,\n} from \"@fluidframework/odsp-driver-definitions\";\nimport { fetch } from \"./fetch\";\nimport { pkgVersion as driverVersion } from \"./packageVersion\";\nimport { IOdspSnapshot } from \"./contracts\";\n\nexport const getWithRetryForTokenRefreshRepeat = \"getWithRetryForTokenRefreshRepeat\";\n\n/** Parse the given url and return the origin (host name) */\nexport const getOrigin = (url: string) => new URL(url).origin;\n\nexport interface IOdspResponse<T> {\n content: T;\n headers: Map<string, string>;\n propsToLog: ITelemetryProperties;\n duration: number;\n}\n\nexport interface TokenFetchOptionsEx extends TokenFetchOptions {\n /** previous error we hit in getWithRetryForTokenRefresh */\n previousError?: any;\n}\n\nfunction headersToMap(headers: Headers) {\n const newHeaders = new Map<string, string>();\n for (const [key, value] of headers.entries()) {\n newHeaders.set(key, value);\n }\n return newHeaders;\n}\n\n/**\n * This API should be used with pretty much all network calls (fetch, webSocket connection) in order\n * to correctly handle expired tokens. It relies on callback fetching token, and be able to refetch\n * token on failure. Only specific cases get retry call with refresh = true, all other / unknown errors\n * simply propagate to caller\n */\nexport async function getWithRetryForTokenRefresh<T>(get: (options: TokenFetchOptionsEx) => Promise<T>) {\n return get({ refresh: false }).catch(async (e) => {\n const options: TokenFetchOptionsEx = { refresh: true, previousError: e };\n switch (e.errorType) {\n // If the error is 401 or 403 refresh the token and try once more.\n case DriverErrorType.authorizationError:\n return get({ ...options, claims: e.claims, tenantId: e.tenantId });\n\n case DriverErrorType.incorrectServerResponse: // some error on the wire, retry once\n case OdspErrorType.fetchTokenError: // If the token was null, then retry once.\n return get(options);\n\n default:\n // Caller may determine that it wants one retry\n if (e[getWithRetryForTokenRefreshRepeat] === true) {\n return get(options);\n }\n throw e;\n }\n });\n}\n\nexport async function fetchHelper(\n requestInfo: RequestInfo,\n requestInit: RequestInit | undefined,\n): Promise<IOdspResponse<Response>> {\n const start = performance.now();\n\n // Node-fetch and dom have conflicting typing, force them to work by casting for now\n return fetch(requestInfo, requestInit).then(async (fetchResponse) => {\n const response = fetchResponse as any as Response;\n // Let's assume we can retry.\n if (!response) {\n throw new NonRetryableError(\n // pre-0.58 error message: No response from fetch call\n \"No response from ODSP fetch call\",\n DriverErrorType.incorrectServerResponse,\n { driverVersion });\n }\n if (!response.ok || response.status < 200 || response.status >= 300) {\n throwOdspNetworkError(\n // pre-0.58 error message prefix: odspFetchError\n `ODSP fetch error [${response.status}]`, response.status, response, await response.text());\n }\n\n const headers = headersToMap(response.headers);\n return {\n content: response,\n headers,\n propsToLog: getSPOAndGraphRequestIdsFromResponse(headers),\n duration: performance.now() - start,\n };\n }, (error) => {\n const online = isOnline();\n const errorText = `${error}`;\n\n // This error is thrown by fetch() when AbortSignal is provided and it gets cancelled\n if (error.name === \"AbortError\") {\n throw new RetryableError(\n \"Fetch Timeout (AbortError)\", OdspErrorType.fetchTimeout, { driverVersion });\n }\n // TCP/IP timeout\n if (errorText.includes(\"ETIMEDOUT\")) {\n throw new RetryableError(\n \"Fetch Timeout (ETIMEDOUT)\", OdspErrorType.fetchTimeout, { driverVersion });\n }\n\n //\n // WARNING: Do not log error object itself or any of its properties!\n // It could contain PII, like URI in message itself, or token in properties.\n // It is also non-serializable object due to circular references.\n //\n if (online === OnlineStatus.Offline) {\n throw new RetryableError(\n // pre-0.58 error message prefix: Offline\n `ODSP fetch failure (Offline): ${errorText}`, DriverErrorType.offlineError, { driverVersion });\n } else {\n // It is perhaps still possible that this is due to being offline, the error does not reveal enough\n // information to conclude. Could also be DNS errors, malformed fetch request, CSP violation, etc.\n throw new RetryableError(\n // pre-0.58 error message prefix: Fetch error\n `ODSP fetch failure: ${errorText}`, DriverErrorType.fetchFailure, { driverVersion });\n }\n });\n}\n\n/**\n * A utility function to fetch and parse as JSON with support for retries\n * @param requestInfo - fetch requestInfo, can be a string\n * @param requestInit - fetch requestInit\n */\nexport async function fetchArray(\n requestInfo: RequestInfo,\n requestInit: RequestInit | undefined,\n): Promise<IOdspResponse<ArrayBuffer>> {\n const { content, headers, propsToLog, duration } = await fetchHelper(requestInfo, requestInit);\n\n const arrayBuffer = await content.arrayBuffer();\n propsToLog.bodySize = arrayBuffer.byteLength;\n return {\n headers,\n content: arrayBuffer,\n propsToLog,\n duration,\n };\n}\n\n/**\n * A utility function to fetch and parse as JSON with support for retries\n * @param requestInfo - fetch requestInfo, can be a string\n * @param requestInit - fetch requestInit\n */\nexport async function fetchAndParseAsJSONHelper<T>(\n requestInfo: RequestInfo,\n requestInit: RequestInit | undefined,\n): Promise<IOdspResponse<T>> {\n const { content, headers, propsToLog, duration } = await fetchHelper(requestInfo, requestInit);\n let text: string | undefined;\n try {\n text = await content.text();\n } catch (e) {\n // JSON.parse() can fail and message would container full request URI, including\n // tokens... It fails for me with \"Unexpected end of JSON input\" quite often - an attempt to download big file\n // (many ops) almost always ends up with this error - I'd guess 1% of op request end up here... It always\n // succeeds on retry.\n // So do not log error object itself.\n throwOdspNetworkError(\n // pre-0.58 error message: errorWhileParsingFetchResponse\n \"Error while parsing fetch response\",\n fetchIncorrectResponse,\n content, // response\n text,\n );\n }\n\n propsToLog.bodySize = text.length;\n const res = {\n headers,\n content: JSON.parse(text),\n propsToLog,\n duration,\n };\n return res;\n}\n\nexport interface INewFileInfo {\n siteUrl: string;\n driveId: string;\n filename: string;\n filePath: string;\n /**\n * application can request creation of a share link along with the creation of a new file\n * by passing in an optional param to specify the kind of sharing link\n * (at the time of adding this comment Sept/2021), odsp only supports csl\n */\n createLinkType?: ShareLinkTypes;\n}\n\nexport function getOdspResolvedUrl(resolvedUrl: IResolvedUrl): IOdspResolvedUrl {\n assert((resolvedUrl as IOdspResolvedUrl).odspResolvedUrl === true, 0x1de /* \"Not an ODSP resolved url\" */);\n return resolvedUrl as IOdspResolvedUrl;\n}\n\nexport const createOdspLogger = (logger?: ITelemetryBaseLogger) =>\n ChildLogger.create(\n logger,\n \"OdspDriver\",\n {\n all:\n {\n driverVersion,\n },\n });\n\nexport function evalBlobsAndTrees(snapshot: IOdspSnapshot) {\n let numTrees = 0;\n let numBlobs = 0;\n let encodedBlobsSize = 0;\n let decodedBlobsSize = 0;\n for (const tree of snapshot.trees) {\n for (const treeEntry of tree.entries) {\n if (treeEntry.type === \"blob\") {\n numBlobs++;\n } else if (treeEntry.type === \"tree\") {\n numTrees++;\n }\n }\n }\n if (snapshot.blobs !== undefined) {\n for (const blob of snapshot.blobs) {\n decodedBlobsSize += blob.size;\n encodedBlobsSize += blob.content.length;\n }\n }\n return { numTrees, numBlobs, encodedBlobsSize, decodedBlobsSize };\n}\n\nexport function toInstrumentedOdspTokenFetcher(\n logger: ITelemetryLogger,\n resolvedUrlParts: IOdspUrlParts,\n tokenFetcher: TokenFetcher<OdspResourceTokenFetchOptions>,\n throwOnNullToken: boolean,\n): InstrumentedStorageTokenFetcher {\n return async (options: TokenFetchOptions, name: string, alwaysRecordTokenFetchTelemetry: boolean = false) => {\n // Telemetry note: if options.refresh is true, there is a potential perf issue:\n // Host should optimize and provide non-expired tokens on all critical paths.\n // Exceptions: race conditions around expiration, revoked tokens, host that does not care\n // (fluid-fetcher)\n return PerformanceEvent.timedExecAsync(\n logger,\n {\n eventName: `${name}_GetToken`,\n attempts: options.refresh ? 2 : 1,\n hasClaims: !!options.claims,\n hasTenantId: !!options.tenantId,\n },\n async (event) => tokenFetcher({\n ...options,\n ...resolvedUrlParts,\n }).then((tokenResponse) => {\n const token = tokenFromResponse(tokenResponse);\n // This event alone generates so many events that is materially impacts cost of telemetry\n // Thus do not report end event when it comes back quickly.\n // Note that most of the hosts do not report if result is comming from cache or not,\n // so we can't rely on that here. But always record if specified explicitly for cases such as\n // calling trees/latest during load.\n if (alwaysRecordTokenFetchTelemetry || event.duration >= 32) {\n event.end({ fromCache: isTokenFromCache(tokenResponse), isNull: token === null });\n }\n if (token === null && throwOnNullToken) {\n throw new NonRetryableError(\n // pre-0.58 error message: Token is null for ${name} call\n `The Host-provided token fetcher returned null`,\n OdspErrorType.fetchTokenError,\n { method: name, driverVersion });\n }\n return token;\n }, (error) => {\n // There is an important but unofficial contract here where token providers can set canRetry: true\n // to hook into the driver's retry logic (e.g. the retry loop when initiating a connection)\n const rawCanRetry = error?.canRetry;\n const tokenError = wrapError(\n error,\n (errorMessage) => new NetworkErrorBasic(\n `The Host-provided token fetcher threw an error: ${errorMessage}`,\n OdspErrorType.fetchTokenError,\n typeof rawCanRetry === \"boolean\" ? rawCanRetry : false /* canRetry */,\n { method: name, driverVersion }));\n throw tokenError;\n }),\n { cancel: \"generic\" });\n };\n}\n\nexport function createCacheSnapshotKey(odspResolvedUrl: IOdspResolvedUrl): ICacheEntry {\n const cacheEntry: ICacheEntry = {\n type: snapshotKey,\n key: \"\",\n file: {\n resolvedUrl: odspResolvedUrl,\n docId: odspResolvedUrl.hashedDocumentId,\n },\n };\n return cacheEntry;\n}\n\n// 80KB is the max body size that we can put in ump post body for server to be able to accept it.\n// Keeping it 78KB to be a little cautious. As per the telemetry 99p is less than 78KB.\nexport const maxUmpPostBodySize = 79872;\n"]}
|
|
1
|
+
{"version":3,"file":"odspUtils.js","sourceRoot":"","sources":["../src/odspUtils.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryProperties, ITelemetryBaseLogger, ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { IResolvedUrl, DriverErrorType } from \"@fluidframework/driver-definitions\";\nimport {\n isOnline,\n OnlineStatus,\n RetryableError,\n NonRetryableError,\n NetworkErrorBasic,\n} from \"@fluidframework/driver-utils\";\nimport { assert, performance } from \"@fluidframework/common-utils\";\nimport { ChildLogger, PerformanceEvent, TelemetryDataTag, wrapError } from \"@fluidframework/telemetry-utils\";\nimport {\n fetchIncorrectResponse,\n throwOdspNetworkError,\n getSPOAndGraphRequestIdsFromResponse,\n} from \"@fluidframework/odsp-doclib-utils\";\nimport {\n IOdspResolvedUrl,\n TokenFetchOptions,\n OdspErrorType,\n tokenFromResponse,\n isTokenFromCache,\n OdspResourceTokenFetchOptions,\n ShareLinkTypes,\n TokenFetcher,\n ICacheEntry,\n snapshotKey,\n InstrumentedStorageTokenFetcher,\n IOdspUrlParts,\n} from \"@fluidframework/odsp-driver-definitions\";\nimport { fetch } from \"./fetch\";\nimport { pkgVersion as driverVersion } from \"./packageVersion\";\nimport { IOdspSnapshot } from \"./contracts\";\n\nexport const getWithRetryForTokenRefreshRepeat = \"getWithRetryForTokenRefreshRepeat\";\n\n/** Parse the given url and return the origin (host name) */\nexport const getOrigin = (url: string) => new URL(url).origin;\n\nexport interface IOdspResponse<T> {\n content: T;\n headers: Map<string, string>;\n propsToLog: ITelemetryProperties;\n duration: number;\n}\n\nexport interface TokenFetchOptionsEx extends TokenFetchOptions {\n /** previous error we hit in getWithRetryForTokenRefresh */\n previousError?: any;\n}\n\nfunction headersToMap(headers: Headers) {\n const newHeaders = new Map<string, string>();\n for (const [key, value] of headers.entries()) {\n newHeaders.set(key, value);\n }\n return newHeaders;\n}\n\n/**\n * This API should be used with pretty much all network calls (fetch, webSocket connection) in order\n * to correctly handle expired tokens. It relies on callback fetching token, and be able to refetch\n * token on failure. Only specific cases get retry call with refresh = true, all other / unknown errors\n * simply propagate to caller\n */\nexport async function getWithRetryForTokenRefresh<T>(get: (options: TokenFetchOptionsEx) => Promise<T>) {\n return get({ refresh: false }).catch(async (e) => {\n const options: TokenFetchOptionsEx = { refresh: true, previousError: e };\n switch (e.errorType) {\n // If the error is 401 or 403 refresh the token and try once more.\n case DriverErrorType.authorizationError:\n return get({ ...options, claims: e.claims, tenantId: e.tenantId });\n\n case DriverErrorType.incorrectServerResponse: // some error on the wire, retry once\n case OdspErrorType.fetchTokenError: // If the token was null, then retry once.\n return get(options);\n\n default:\n // Caller may determine that it wants one retry\n if (e[getWithRetryForTokenRefreshRepeat] === true) {\n return get(options);\n }\n throw e;\n }\n });\n}\n\nexport async function fetchHelper(\n requestInfo: RequestInfo,\n requestInit: RequestInit | undefined,\n): Promise<IOdspResponse<Response>> {\n const start = performance.now();\n\n // Node-fetch and dom have conflicting typing, force them to work by casting for now\n return fetch(requestInfo, requestInit).then(async (fetchResponse) => {\n const response = fetchResponse as any as Response;\n // Let's assume we can retry.\n if (!response) {\n throw new NonRetryableError(\n // pre-0.58 error message: No response from fetch call\n \"No response from ODSP fetch call\",\n DriverErrorType.incorrectServerResponse,\n { driverVersion });\n }\n if (!response.ok || response.status < 200 || response.status >= 300) {\n throwOdspNetworkError(\n // pre-0.58 error message prefix: odspFetchError\n `ODSP fetch error [${response.status}]`, response.status, response, await response.text());\n }\n\n const headers = headersToMap(response.headers);\n return {\n content: response,\n headers,\n propsToLog: getSPOAndGraphRequestIdsFromResponse(headers),\n duration: performance.now() - start,\n };\n }, (error) => {\n const online = isOnline();\n const errorText = `${error}`;\n const urlRegex = /((http|https):\\/\\/(\\S*))/i;\n const redactedErrorText = errorText.replace(urlRegex, \"REDACTED_URL\");\n // This error is thrown by fetch() when AbortSignal is provided and it gets cancelled\n if (error.name === \"AbortError\") {\n throw new RetryableError(\n \"Fetch Timeout (AbortError)\", OdspErrorType.fetchTimeout, { driverVersion });\n }\n // TCP/IP timeout\n if (errorText.includes(\"ETIMEDOUT\")) {\n throw new RetryableError(\n \"Fetch Timeout (ETIMEDOUT)\", OdspErrorType.fetchTimeout, { driverVersion });\n }\n\n //\n // WARNING: Do not log error object itself or any of its properties!\n // It could contain PII, like URI in message itself, or token in properties.\n // It is also non-serializable object due to circular references.\n //\n if (online === OnlineStatus.Offline) {\n throw new RetryableError(\n // pre-0.58 error message prefix: Offline\n `ODSP fetch failure (Offline): ${redactedErrorText}`,\n DriverErrorType.offlineError,\n {\n driverVersion,\n rawErrorMessage: { value: errorText, tag: TelemetryDataTag.UserData },\n });\n } else {\n // It is perhaps still possible that this is due to being offline, the error does not reveal enough\n // information to conclude. Could also be DNS errors, malformed fetch request, CSP violation, etc.\n throw new RetryableError(\n // pre-0.58 error message prefix: Fetch error\n `ODSP fetch failure: ${redactedErrorText}`,\n DriverErrorType.fetchFailure,\n {\n driverVersion,\n rawErrorMessage: { value: errorText, tag: TelemetryDataTag.UserData },\n });\n }\n });\n}\n\n/**\n * A utility function to fetch and parse as JSON with support for retries\n * @param requestInfo - fetch requestInfo, can be a string\n * @param requestInit - fetch requestInit\n */\nexport async function fetchArray(\n requestInfo: RequestInfo,\n requestInit: RequestInit | undefined,\n): Promise<IOdspResponse<ArrayBuffer>> {\n const { content, headers, propsToLog, duration } = await fetchHelper(requestInfo, requestInit);\n\n const arrayBuffer = await content.arrayBuffer();\n propsToLog.bodySize = arrayBuffer.byteLength;\n return {\n headers,\n content: arrayBuffer,\n propsToLog,\n duration,\n };\n}\n\n/**\n * A utility function to fetch and parse as JSON with support for retries\n * @param requestInfo - fetch requestInfo, can be a string\n * @param requestInit - fetch requestInit\n */\nexport async function fetchAndParseAsJSONHelper<T>(\n requestInfo: RequestInfo,\n requestInit: RequestInit | undefined,\n): Promise<IOdspResponse<T>> {\n const { content, headers, propsToLog, duration } = await fetchHelper(requestInfo, requestInit);\n let text: string | undefined;\n try {\n text = await content.text();\n } catch (e) {\n // JSON.parse() can fail and message would container full request URI, including\n // tokens... It fails for me with \"Unexpected end of JSON input\" quite often - an attempt to download big file\n // (many ops) almost always ends up with this error - I'd guess 1% of op request end up here... It always\n // succeeds on retry.\n // So do not log error object itself.\n throwOdspNetworkError(\n // pre-0.58 error message: errorWhileParsingFetchResponse\n \"Error while parsing fetch response\",\n fetchIncorrectResponse,\n content, // response\n text,\n );\n }\n\n propsToLog.bodySize = text.length;\n const res = {\n headers,\n content: JSON.parse(text),\n propsToLog,\n duration,\n };\n return res;\n}\n\nexport interface INewFileInfo {\n siteUrl: string;\n driveId: string;\n filename: string;\n filePath: string;\n /**\n * application can request creation of a share link along with the creation of a new file\n * by passing in an optional param to specify the kind of sharing link\n * (at the time of adding this comment Sept/2021), odsp only supports csl\n */\n createLinkType?: ShareLinkTypes;\n}\n\nexport function getOdspResolvedUrl(resolvedUrl: IResolvedUrl): IOdspResolvedUrl {\n assert((resolvedUrl as IOdspResolvedUrl).odspResolvedUrl === true, 0x1de /* \"Not an ODSP resolved url\" */);\n return resolvedUrl as IOdspResolvedUrl;\n}\n\nexport const createOdspLogger = (logger?: ITelemetryBaseLogger) =>\n ChildLogger.create(\n logger,\n \"OdspDriver\",\n {\n all:\n {\n driverVersion,\n },\n });\n\nexport function evalBlobsAndTrees(snapshot: IOdspSnapshot) {\n let numTrees = 0;\n let numBlobs = 0;\n let encodedBlobsSize = 0;\n let decodedBlobsSize = 0;\n for (const tree of snapshot.trees) {\n for (const treeEntry of tree.entries) {\n if (treeEntry.type === \"blob\") {\n numBlobs++;\n } else if (treeEntry.type === \"tree\") {\n numTrees++;\n }\n }\n }\n if (snapshot.blobs !== undefined) {\n for (const blob of snapshot.blobs) {\n decodedBlobsSize += blob.size;\n encodedBlobsSize += blob.content.length;\n }\n }\n return { numTrees, numBlobs, encodedBlobsSize, decodedBlobsSize };\n}\n\nexport function toInstrumentedOdspTokenFetcher(\n logger: ITelemetryLogger,\n resolvedUrlParts: IOdspUrlParts,\n tokenFetcher: TokenFetcher<OdspResourceTokenFetchOptions>,\n throwOnNullToken: boolean,\n): InstrumentedStorageTokenFetcher {\n return async (options: TokenFetchOptions, name: string, alwaysRecordTokenFetchTelemetry: boolean = false) => {\n // Telemetry note: if options.refresh is true, there is a potential perf issue:\n // Host should optimize and provide non-expired tokens on all critical paths.\n // Exceptions: race conditions around expiration, revoked tokens, host that does not care\n // (fluid-fetcher)\n return PerformanceEvent.timedExecAsync(\n logger,\n {\n eventName: `${name}_GetToken`,\n attempts: options.refresh ? 2 : 1,\n hasClaims: !!options.claims,\n hasTenantId: !!options.tenantId,\n },\n async (event) => tokenFetcher({\n ...options,\n ...resolvedUrlParts,\n }).then((tokenResponse) => {\n const token = tokenFromResponse(tokenResponse);\n // This event alone generates so many events that is materially impacts cost of telemetry\n // Thus do not report end event when it comes back quickly.\n // Note that most of the hosts do not report if result is comming from cache or not,\n // so we can't rely on that here. But always record if specified explicitly for cases such as\n // calling trees/latest during load.\n if (alwaysRecordTokenFetchTelemetry || event.duration >= 32) {\n event.end({ fromCache: isTokenFromCache(tokenResponse), isNull: token === null });\n }\n if (token === null && throwOnNullToken) {\n throw new NonRetryableError(\n // pre-0.58 error message: Token is null for ${name} call\n `The Host-provided token fetcher returned null`,\n OdspErrorType.fetchTokenError,\n { method: name, driverVersion });\n }\n return token;\n }, (error) => {\n // There is an important but unofficial contract here where token providers can set canRetry: true\n // to hook into the driver's retry logic (e.g. the retry loop when initiating a connection)\n const rawCanRetry = error?.canRetry;\n const tokenError = wrapError(\n error,\n (errorMessage) => new NetworkErrorBasic(\n `The Host-provided token fetcher threw an error: ${errorMessage}`,\n OdspErrorType.fetchTokenError,\n typeof rawCanRetry === \"boolean\" ? rawCanRetry : false /* canRetry */,\n { method: name, driverVersion }));\n throw tokenError;\n }),\n { cancel: \"generic\" });\n };\n}\n\nexport function createCacheSnapshotKey(odspResolvedUrl: IOdspResolvedUrl): ICacheEntry {\n const cacheEntry: ICacheEntry = {\n type: snapshotKey,\n key: \"\",\n file: {\n resolvedUrl: odspResolvedUrl,\n docId: odspResolvedUrl.hashedDocumentId,\n },\n };\n return cacheEntry;\n}\n\n// 80KB is the max body size that we can put in ump post body for server to be able to accept it.\n// Keeping it 78KB to be a little cautious. As per the telemetry 99p is less than 78KB.\nexport const maxUmpPostBodySize = 79872;\n"]}
|
package/lib/packageVersion.d.ts
CHANGED
|
@@ -5,5 +5,5 @@
|
|
|
5
5
|
* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
|
|
6
6
|
*/
|
|
7
7
|
export declare const pkgName = "@fluidframework/odsp-driver";
|
|
8
|
-
export declare const pkgVersion = "1.
|
|
8
|
+
export declare const pkgVersion = "2.0.0-internal.1.0.0.82159";
|
|
9
9
|
//# sourceMappingURL=packageVersion.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packageVersion.d.ts","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,OAAO,gCAAgC,CAAC;AACrD,eAAO,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"packageVersion.d.ts","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,OAAO,gCAAgC,CAAC;AACrD,eAAO,MAAM,UAAU,+BAA+B,CAAC"}
|
package/lib/packageVersion.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,6BAA6B,CAAC;AACrD,MAAM,CAAC,MAAM,UAAU,GAAG,
|
|
1
|
+
{"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,6BAA6B,CAAC;AACrD,MAAM,CAAC,MAAM,UAAU,GAAG,4BAA4B,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/odsp-driver\";\nexport const pkgVersion = \"2.0.0-internal.1.0.0.82159\";\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/odsp-driver",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "2.0.0-internal.1.0.0.82159",
|
|
4
4
|
"description": "Socket storage implementation for SPO and ODC",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -62,16 +62,16 @@
|
|
|
62
62
|
"dependencies": {
|
|
63
63
|
"@fluidframework/common-definitions": "^0.20.1",
|
|
64
64
|
"@fluidframework/common-utils": "^0.32.1",
|
|
65
|
-
"@fluidframework/core-interfaces": "
|
|
66
|
-
"@fluidframework/driver-base": "
|
|
67
|
-
"@fluidframework/driver-definitions": "
|
|
68
|
-
"@fluidframework/driver-utils": "
|
|
69
|
-
"@fluidframework/gitresources": "^0.
|
|
70
|
-
"@fluidframework/odsp-doclib-utils": "
|
|
71
|
-
"@fluidframework/odsp-driver-definitions": "
|
|
72
|
-
"@fluidframework/protocol-base": "^0.
|
|
73
|
-
"@fluidframework/protocol-definitions": "^0.
|
|
74
|
-
"@fluidframework/telemetry-utils": "
|
|
65
|
+
"@fluidframework/core-interfaces": "2.0.0-internal.1.0.0.82159",
|
|
66
|
+
"@fluidframework/driver-base": "2.0.0-internal.1.0.0.82159",
|
|
67
|
+
"@fluidframework/driver-definitions": "2.0.0-internal.1.0.0.82159",
|
|
68
|
+
"@fluidframework/driver-utils": "2.0.0-internal.1.0.0.82159",
|
|
69
|
+
"@fluidframework/gitresources": "^0.1037.1000-0",
|
|
70
|
+
"@fluidframework/odsp-doclib-utils": "2.0.0-internal.1.0.0.82159",
|
|
71
|
+
"@fluidframework/odsp-driver-definitions": "2.0.0-internal.1.0.0.82159",
|
|
72
|
+
"@fluidframework/protocol-base": "^0.1037.1000-0",
|
|
73
|
+
"@fluidframework/protocol-definitions": "^0.1029.1000-0",
|
|
74
|
+
"@fluidframework/telemetry-utils": "2.0.0-internal.1.0.0.82159",
|
|
75
75
|
"abort-controller": "^3.0.0",
|
|
76
76
|
"node-fetch": "^2.6.1",
|
|
77
77
|
"socket.io-client": "^4.4.1",
|
|
@@ -79,10 +79,10 @@
|
|
|
79
79
|
},
|
|
80
80
|
"devDependencies": {
|
|
81
81
|
"@fluidframework/build-common": "^0.24.0",
|
|
82
|
-
"@fluidframework/build-tools": "^0.
|
|
82
|
+
"@fluidframework/build-tools": "^0.3.0-0",
|
|
83
83
|
"@fluidframework/eslint-config-fluid": "^0.28.2000",
|
|
84
|
-
"@fluidframework/mocha-test-setup": "
|
|
85
|
-
"@fluidframework/odsp-driver-previous": "npm:@fluidframework/odsp-driver
|
|
84
|
+
"@fluidframework/mocha-test-setup": "2.0.0-internal.1.0.0.82159",
|
|
85
|
+
"@fluidframework/odsp-driver-previous": "npm:@fluidframework/odsp-driver@^1.0.0",
|
|
86
86
|
"@microsoft/api-extractor": "^7.22.2",
|
|
87
87
|
"@rushstack/eslint-config": "^2.5.1",
|
|
88
88
|
"@types/mocha": "^9.1.1",
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
"typescript-formatter": "7.1.0"
|
|
101
101
|
},
|
|
102
102
|
"typeValidation": {
|
|
103
|
-
"version": "
|
|
103
|
+
"version": "2.0.0",
|
|
104
104
|
"broken": {}
|
|
105
105
|
}
|
|
106
106
|
}
|
package/src/epochTracker.ts
CHANGED
|
@@ -6,7 +6,12 @@
|
|
|
6
6
|
import { v4 as uuid } from "uuid";
|
|
7
7
|
import { assert, Deferred } from "@fluidframework/common-utils";
|
|
8
8
|
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
ThrottlingError,
|
|
11
|
+
RateLimiter,
|
|
12
|
+
NonRetryableError,
|
|
13
|
+
LocationRedirectionError,
|
|
14
|
+
} from "@fluidframework/driver-utils";
|
|
10
15
|
import { IConnected } from "@fluidframework/protocol-definitions";
|
|
11
16
|
import {
|
|
12
17
|
snapshotKey,
|
|
@@ -15,9 +20,16 @@ import {
|
|
|
15
20
|
IFileEntry,
|
|
16
21
|
IPersistedCache,
|
|
17
22
|
IOdspError,
|
|
23
|
+
IOdspErrorAugmentations,
|
|
24
|
+
IOdspResolvedUrl,
|
|
18
25
|
} from "@fluidframework/odsp-driver-definitions";
|
|
19
26
|
import { DriverErrorType } from "@fluidframework/driver-definitions";
|
|
20
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
PerformanceEvent,
|
|
29
|
+
isFluidError,
|
|
30
|
+
normalizeError,
|
|
31
|
+
loggerToMonitoringContext,
|
|
32
|
+
} from "@fluidframework/telemetry-utils";
|
|
21
33
|
import { fetchAndParseAsJSONHelper, fetchArray, fetchHelper, getOdspResolvedUrl, IOdspResponse } from "./odspUtils";
|
|
22
34
|
import {
|
|
23
35
|
IOdspCache,
|
|
@@ -27,6 +39,7 @@ import {
|
|
|
27
39
|
import { IVersionedValueWithEpoch, persistedCacheValueVersion } from "./contracts";
|
|
28
40
|
import { ClpCompliantAppHeader } from "./contractsPublic";
|
|
29
41
|
import { pkgVersion as driverVersion } from "./packageVersion";
|
|
42
|
+
import { patchOdspResolvedUrl } from "./odspLocationRedirection";
|
|
30
43
|
|
|
31
44
|
export type FetchType = "blob" | "createBlob" | "createFile" | "joinSession" | "ops" | "test" | "snapshotTree" |
|
|
32
45
|
"treesLatest" | "uploadSummary" | "push" | "versions";
|
|
@@ -47,6 +60,7 @@ export const defaultCacheExpiryTimeoutMs: number = 2 * 24 * 60 * 60 * 1000;
|
|
|
47
60
|
export class EpochTracker implements IPersistedFileCache {
|
|
48
61
|
private _fluidEpoch: string | undefined;
|
|
49
62
|
|
|
63
|
+
private readonly snapshotCacheExpiryTimeoutMs: number;
|
|
50
64
|
public readonly rateLimiter: RateLimiter;
|
|
51
65
|
private readonly driverId = uuid();
|
|
52
66
|
// This tracks the request number made by the driver instance.
|
|
@@ -59,6 +73,12 @@ export class EpochTracker implements IPersistedFileCache {
|
|
|
59
73
|
) {
|
|
60
74
|
// Limits the max number of concurrent requests to 24.
|
|
61
75
|
this.rateLimiter = new RateLimiter(24);
|
|
76
|
+
|
|
77
|
+
// We need this for GC testing until we properly plumb through the snapshot expiration policy (see PR #11168)
|
|
78
|
+
this.snapshotCacheExpiryTimeoutMs =
|
|
79
|
+
loggerToMonitoringContext(logger).config.getBoolean("Fluid.Driver.Odsp.TestOverride.DisableSnapshotCache")
|
|
80
|
+
? 0
|
|
81
|
+
: defaultCacheExpiryTimeoutMs;
|
|
62
82
|
}
|
|
63
83
|
|
|
64
84
|
// public for UT purposes only!
|
|
@@ -95,17 +115,17 @@ export class EpochTracker implements IPersistedFileCache {
|
|
|
95
115
|
} else if (this._fluidEpoch !== value.fluidEpoch) {
|
|
96
116
|
return undefined;
|
|
97
117
|
}
|
|
98
|
-
// Expire the cached snapshot if it's older than
|
|
118
|
+
// Expire the cached snapshot if it's older than snapshotCacheExpiryTimeoutMs and immediately
|
|
99
119
|
// expire all old caches that do not have cacheEntryTime
|
|
100
120
|
if (entry.type === snapshotKey) {
|
|
101
121
|
const cacheTime = value.value?.cacheEntryTime;
|
|
102
122
|
const currentTime = Date.now();
|
|
103
|
-
if (cacheTime === undefined || currentTime - cacheTime >=
|
|
123
|
+
if (cacheTime === undefined || currentTime - cacheTime >= this.snapshotCacheExpiryTimeoutMs) {
|
|
104
124
|
this.logger.sendTelemetryEvent(
|
|
105
125
|
{
|
|
106
126
|
eventName: "odspVersionsCacheExpired",
|
|
107
127
|
duration: currentTime - cacheTime,
|
|
108
|
-
maxCacheAgeMs:
|
|
128
|
+
maxCacheAgeMs: this.snapshotCacheExpiryTimeoutMs,
|
|
109
129
|
});
|
|
110
130
|
await this.removeEntries();
|
|
111
131
|
return undefined;
|
|
@@ -121,8 +141,8 @@ export class EpochTracker implements IPersistedFileCache {
|
|
|
121
141
|
|
|
122
142
|
public async put(entry: IEntry, value: any) {
|
|
123
143
|
assert(this._fluidEpoch !== undefined, 0x1dd /* "no epoch" */);
|
|
124
|
-
// For snapshots, the value should have the cacheEntryTime.
|
|
125
|
-
// than
|
|
144
|
+
// For snapshots, the value should have the cacheEntryTime.
|
|
145
|
+
// This will be used to expire snapshots older than snapshotCacheExpiryTimeoutMs.
|
|
126
146
|
if (entry.type === snapshotKey) {
|
|
127
147
|
value.cacheEntryTime = value.cacheEntryTime ?? Date.now();
|
|
128
148
|
}
|
|
@@ -224,6 +244,26 @@ export class EpochTracker implements IPersistedFileCache {
|
|
|
224
244
|
}
|
|
225
245
|
await this.checkForEpochError(error, epochFromResponse, fetchType);
|
|
226
246
|
throw error;
|
|
247
|
+
}).catch((error) => {
|
|
248
|
+
// If the error is about location redirection, then we need to generate new resolved url with correct
|
|
249
|
+
// location info.
|
|
250
|
+
if (isFluidError(error) && error.errorType === DriverErrorType.fileNotFoundOrAccessDeniedError) {
|
|
251
|
+
const redirectLocation = (error as IOdspErrorAugmentations).redirectLocation;
|
|
252
|
+
if (redirectLocation !== undefined) {
|
|
253
|
+
const redirectUrl: IOdspResolvedUrl = patchOdspResolvedUrl(
|
|
254
|
+
this.fileEntry.resolvedUrl,
|
|
255
|
+
redirectLocation,
|
|
256
|
+
);
|
|
257
|
+
const locationRedirectionError = new LocationRedirectionError(
|
|
258
|
+
error.message,
|
|
259
|
+
redirectUrl,
|
|
260
|
+
{ driverVersion, redirectLocation },
|
|
261
|
+
);
|
|
262
|
+
locationRedirectionError.addTelemetryProperties(error.getTelemetryProperties());
|
|
263
|
+
throw locationRedirectionError;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
throw error;
|
|
227
267
|
}).catch((error) => {
|
|
228
268
|
const fluidError = normalizeError(error, { props: { XRequestStatsHeader: clientCorrelationId } });
|
|
229
269
|
throw fluidError;
|
package/src/fetchSnapshot.ts
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
InstrumentedStorageTokenFetcher,
|
|
17
17
|
} from "@fluidframework/odsp-driver-definitions";
|
|
18
18
|
import { ISnapshotTree } from "@fluidframework/protocol-definitions";
|
|
19
|
-
import { isRuntimeMessage, NonRetryableError } from "@fluidframework/driver-utils";
|
|
19
|
+
import { DriverErrorTelemetryProps, isRuntimeMessage, NonRetryableError } from "@fluidframework/driver-utils";
|
|
20
20
|
import { IOdspSnapshot, ISnapshotCachedEntry, IVersionedValueWithEpoch, persistedCacheValueVersion } from "./contracts";
|
|
21
21
|
import { getQueryString } from "./getQueryString";
|
|
22
22
|
import { getUrlAndHeadersWithAuth } from "./getUrlAndHeadersWithAuth";
|
|
@@ -234,19 +234,30 @@ async function fetchLatestSnapshotCore(
|
|
|
234
234
|
snapshotOptions,
|
|
235
235
|
controller,
|
|
236
236
|
);
|
|
237
|
+
|
|
237
238
|
const odspResponse = response.odspResponse;
|
|
238
239
|
const contentType = odspResponse.headers.get("content-type");
|
|
239
|
-
|
|
240
|
+
|
|
241
|
+
// Measure how much time we spend processing payload
|
|
242
|
+
const snapshotParseEvent = PerformanceEvent.start(logger, {
|
|
243
|
+
eventName: "SnapshotParse",
|
|
244
|
+
driverVersion: pkgVersion,
|
|
245
|
+
contentType,
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
const propsToLog: DriverErrorTelemetryProps = {
|
|
240
249
|
...odspResponse.propsToLog,
|
|
241
250
|
contentType,
|
|
242
251
|
accept: response.requestHeaders.accept,
|
|
252
|
+
driverVersion: pkgVersion,
|
|
243
253
|
};
|
|
244
254
|
let parsedSnapshotContents: IOdspResponse<ISnapshotContents> | undefined;
|
|
255
|
+
|
|
245
256
|
try {
|
|
246
257
|
switch (contentType) {
|
|
247
258
|
case "application/json": {
|
|
248
259
|
const text = await odspResponse.content.text();
|
|
249
|
-
|
|
260
|
+
propsToLog.bodySize = text.length;
|
|
250
261
|
const content: IOdspSnapshot = JSON.parse(text);
|
|
251
262
|
validateBlobsAndTrees(content);
|
|
252
263
|
const snapshotContents: ISnapshotContents =
|
|
@@ -256,7 +267,7 @@ async function fetchLatestSnapshotCore(
|
|
|
256
267
|
}
|
|
257
268
|
case "application/ms-fluid": {
|
|
258
269
|
const content = await odspResponse.content.arrayBuffer();
|
|
259
|
-
|
|
270
|
+
propsToLog.bodySize = content.byteLength;
|
|
260
271
|
const snapshotContents: ISnapshotContents = parseCompactSnapshotResponse(
|
|
261
272
|
new ReadBuffer(new Uint8Array(content)));
|
|
262
273
|
if (snapshotContents.snapshotTree.trees === undefined ||
|
|
@@ -264,7 +275,7 @@ async function fetchLatestSnapshotCore(
|
|
|
264
275
|
throw new NonRetryableError(
|
|
265
276
|
"Returned odsp snapshot is malformed. No trees or blobs!",
|
|
266
277
|
DriverErrorType.incorrectServerResponse,
|
|
267
|
-
|
|
278
|
+
propsToLog,
|
|
268
279
|
);
|
|
269
280
|
}
|
|
270
281
|
parsedSnapshotContents = { ...odspResponse, content: snapshotContents };
|
|
@@ -274,12 +285,12 @@ async function fetchLatestSnapshotCore(
|
|
|
274
285
|
throw new NonRetryableError(
|
|
275
286
|
"Unknown snapshot content type",
|
|
276
287
|
DriverErrorType.incorrectServerResponse,
|
|
277
|
-
|
|
288
|
+
propsToLog,
|
|
278
289
|
);
|
|
279
290
|
}
|
|
280
291
|
} catch (error) {
|
|
281
292
|
if (isFluidError(error)) {
|
|
282
|
-
error.addTelemetryProperties(
|
|
293
|
+
error.addTelemetryProperties(propsToLog);
|
|
283
294
|
throw error;
|
|
284
295
|
}
|
|
285
296
|
const enhancedError = wrapError(
|
|
@@ -287,11 +298,14 @@ async function fetchLatestSnapshotCore(
|
|
|
287
298
|
(errorMessage) => new NonRetryableError(
|
|
288
299
|
`Error parsing snapshot response: ${errorMessage}`,
|
|
289
300
|
DriverErrorType.genericError,
|
|
290
|
-
|
|
301
|
+
propsToLog));
|
|
291
302
|
throw enhancedError;
|
|
292
303
|
}
|
|
304
|
+
|
|
293
305
|
assert(parsedSnapshotContents !== undefined, 0x312 /* snapshot should be parsed */);
|
|
294
306
|
const snapshot = parsedSnapshotContents.content;
|
|
307
|
+
const { trees, numBlobs, encodedBlobsSize } = evalBlobsAndTrees(snapshot);
|
|
308
|
+
|
|
295
309
|
// From: https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming
|
|
296
310
|
// fetchStart: immediately before the browser starts to fetch the resource.
|
|
297
311
|
// requestStart: immediately before the browser starts requesting the resource from the server
|
|
@@ -344,9 +358,6 @@ async function fetchLatestSnapshotCore(
|
|
|
344
358
|
}
|
|
345
359
|
}
|
|
346
360
|
|
|
347
|
-
const { numTrees, numBlobs, encodedBlobsSize } =
|
|
348
|
-
evalBlobsAndTrees(parsedSnapshotContents.content);
|
|
349
|
-
|
|
350
361
|
// There are some scenarios in ODSP where we cannot cache, trees/latest will explicitly tell us when we
|
|
351
362
|
// cannot cache using an HTTP response header.
|
|
352
363
|
const canCache =
|
|
@@ -375,8 +386,11 @@ async function fetchLatestSnapshotCore(
|
|
|
375
386
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
376
387
|
putInCache(valueWithEpoch);
|
|
377
388
|
}
|
|
389
|
+
|
|
390
|
+
snapshotParseEvent.end();
|
|
391
|
+
|
|
378
392
|
event.end({
|
|
379
|
-
trees
|
|
393
|
+
trees,
|
|
380
394
|
blobs: snapshot.blobs?.size ?? 0,
|
|
381
395
|
leafNodes: numBlobs,
|
|
382
396
|
encodedBlobsSize,
|
|
@@ -406,7 +420,7 @@ async function fetchLatestSnapshotCore(
|
|
|
406
420
|
// Azure Fluid Relay service is the redeem status (S means success), and FRP is a flag to indicate
|
|
407
421
|
// if the permission has changed.
|
|
408
422
|
sltelemetry: odspResponse.headers.get("x-fluid-sltelemetry"),
|
|
409
|
-
...
|
|
423
|
+
...propsToLog,
|
|
410
424
|
});
|
|
411
425
|
return snapshot;
|
|
412
426
|
},
|
|
@@ -466,13 +480,13 @@ function getFormBodyAndHeaders(
|
|
|
466
480
|
}
|
|
467
481
|
|
|
468
482
|
function evalBlobsAndTrees(snapshot: ISnapshotContents) {
|
|
469
|
-
const
|
|
483
|
+
const trees = countTreesInSnapshotTree(snapshot.snapshotTree);
|
|
470
484
|
const numBlobs = snapshot.blobs.size;
|
|
471
485
|
let encodedBlobsSize = 0;
|
|
472
486
|
for (const [_, blobContent] of snapshot.blobs) {
|
|
473
487
|
encodedBlobsSize += blobContent.byteLength;
|
|
474
488
|
}
|
|
475
|
-
return {
|
|
489
|
+
return { trees, numBlobs, encodedBlobsSize };
|
|
476
490
|
}
|
|
477
491
|
|
|
478
492
|
export function validateBlobsAndTrees(snapshot: IOdspSnapshot) {
|
package/src/getFileLink.ts
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
7
|
-
import { assert
|
|
8
|
-
import { canRetryOnError,
|
|
7
|
+
import { assert } from "@fluidframework/common-utils";
|
|
8
|
+
import { canRetryOnError, NonRetryableError } from "@fluidframework/driver-utils";
|
|
9
9
|
import { PerformanceEvent } from "@fluidframework/telemetry-utils";
|
|
10
10
|
import { DriverErrorType } from "@fluidframework/driver-definitions";
|
|
11
11
|
import {
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
import { getUrlAndHeadersWithAuth } from "./getUrlAndHeadersWithAuth";
|
|
18
18
|
import { fetchHelper, getWithRetryForTokenRefresh, toInstrumentedOdspTokenFetcher } from "./odspUtils";
|
|
19
19
|
import { pkgVersion as driverVersion } from "./packageVersion";
|
|
20
|
+
import { runWithRetry } from "./retryUtils";
|
|
20
21
|
|
|
21
22
|
// Store cached responses for the lifetime of web session as file link remains the same for given file item
|
|
22
23
|
const fileLinkCache = new Map<string, Promise<string>>();
|
|
@@ -47,33 +48,29 @@ export async function getFileLink(
|
|
|
47
48
|
return maybeFileLinkCacheEntry;
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
const
|
|
51
|
-
let
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
// If the error is throttling error, then wait for the specified time before retrying.
|
|
66
|
-
// If the waitTime is not specified, then we start with retrying immediately to max of 8s.
|
|
67
|
-
retryAfterMs = getRetryDelayFromError(err) ?? Math.min(retryAfterMs * 2, 8000);
|
|
68
|
-
await delay(retryAfterMs);
|
|
51
|
+
const fileLinkGenerator = async function() {
|
|
52
|
+
let fileLinkCore: string;
|
|
53
|
+
try {
|
|
54
|
+
fileLinkCore = await runWithRetry(
|
|
55
|
+
async () => getFileLinkCore(getToken, odspUrlParts, identityType, logger),
|
|
56
|
+
"getFileLinkCore",
|
|
57
|
+
logger,
|
|
58
|
+
);
|
|
59
|
+
} catch (err) {
|
|
60
|
+
// runWithRetry throws a non retriable error after it hits the max # of attempts
|
|
61
|
+
// or encounters an unexpected error type
|
|
62
|
+
if (!canRetryOnError(err)) {
|
|
63
|
+
// Delete from the cache to permit retrying later.
|
|
64
|
+
fileLinkCache.delete(cacheKey);
|
|
69
65
|
}
|
|
70
|
-
|
|
66
|
+
throw err;
|
|
67
|
+
}
|
|
71
68
|
|
|
72
69
|
// We are guaranteed to run the getFileLinkCore at least once with successful result (which must be a string)
|
|
73
|
-
assert(
|
|
74
|
-
return
|
|
70
|
+
assert(fileLinkCore !== undefined, 0x292 /* "Unexpected undefined result from getFileLinkCore" */);
|
|
71
|
+
return fileLinkCore;
|
|
75
72
|
};
|
|
76
|
-
const fileLink =
|
|
73
|
+
const fileLink = fileLinkGenerator();
|
|
77
74
|
fileLinkCache.set(cacheKey, fileLink);
|
|
78
75
|
return fileLink;
|
|
79
76
|
}
|
|
@@ -35,13 +35,14 @@ export class OdspDeltaStorageService {
|
|
|
35
35
|
* @param from - inclusive
|
|
36
36
|
* @param to - exclusive
|
|
37
37
|
* @param telemetryProps - properties to add when issuing telemetry events
|
|
38
|
+
* @param scenarioName - reason for fetching ops
|
|
38
39
|
* @returns ops retrieved & info if result was partial (i.e. more is available)
|
|
39
40
|
*/
|
|
40
41
|
public async get(
|
|
41
42
|
from: number,
|
|
42
43
|
to: number,
|
|
43
44
|
telemetryProps: ITelemetryProperties,
|
|
44
|
-
|
|
45
|
+
scenarioName?: string,
|
|
45
46
|
): Promise<IDeltasFetchResult> {
|
|
46
47
|
return getWithRetryForTokenRefresh(async (options) => {
|
|
47
48
|
// Note - this call ends up in getSocketStorageDiscovery() and can refresh token
|
|
@@ -78,7 +79,7 @@ export class OdspDeltaStorageService {
|
|
|
78
79
|
},
|
|
79
80
|
"ops",
|
|
80
81
|
true,
|
|
81
|
-
|
|
82
|
+
scenarioName,
|
|
82
83
|
);
|
|
83
84
|
clearTimeout(timer);
|
|
84
85
|
const deltaStorageResponse = response.content;
|
|
@@ -99,6 +100,7 @@ export class OdspDeltaStorageService {
|
|
|
99
100
|
from,
|
|
100
101
|
to,
|
|
101
102
|
...telemetryProps,
|
|
103
|
+
reason: scenarioName,
|
|
102
104
|
});
|
|
103
105
|
|
|
104
106
|
// It is assumed that server always returns all the ops that it has in the range that was requested.
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
7
|
-
import { performance } from "@fluidframework/common-utils";
|
|
7
|
+
import { assert, performance } from "@fluidframework/common-utils";
|
|
8
8
|
import {
|
|
9
9
|
ChildLogger,
|
|
10
10
|
IFluidErrorBase,
|
|
@@ -21,12 +21,7 @@ import {
|
|
|
21
21
|
IDocumentServicePolicies,
|
|
22
22
|
DriverErrorType,
|
|
23
23
|
} from "@fluidframework/driver-definitions";
|
|
24
|
-
import {
|
|
25
|
-
canRetryOnError,
|
|
26
|
-
DeltaStreamConnectionForbiddenError,
|
|
27
|
-
NonRetryableError,
|
|
28
|
-
} from "@fluidframework/driver-utils";
|
|
29
|
-
import { IFacetCodes } from "@fluidframework/odsp-doclib-utils";
|
|
24
|
+
import { canRetryOnError, DeltaStreamConnectionForbiddenError, NonRetryableError } from "@fluidframework/driver-utils";
|
|
30
25
|
import {
|
|
31
26
|
IClient,
|
|
32
27
|
ISequencedDocumentMessage,
|
|
@@ -39,6 +34,7 @@ import {
|
|
|
39
34
|
InstrumentedStorageTokenFetcher,
|
|
40
35
|
OdspErrorType,
|
|
41
36
|
} from "@fluidframework/odsp-driver-definitions";
|
|
37
|
+
import { hasFacetCodes } from "@fluidframework/odsp-doclib-utils";
|
|
42
38
|
import type { io as SocketIOClientStatic } from "socket.io-client";
|
|
43
39
|
import { HostStoragePolicyInternal, ISocketStorageDiscovery } from "./contracts";
|
|
44
40
|
import { IOdspCache } from "./odspCache";
|
|
@@ -113,6 +109,8 @@ export class OdspDocumentService implements IDocumentService {
|
|
|
113
109
|
|
|
114
110
|
private currentConnection?: OdspDocumentDeltaConnection;
|
|
115
111
|
|
|
112
|
+
private relayServiceTenantAndSessionId: string | undefined;
|
|
113
|
+
|
|
116
114
|
/**
|
|
117
115
|
* @param odspResolvedUrl - resolved url identifying document that will be managed by this service instance.
|
|
118
116
|
* @param getStorageToken - function that can provide the storage token. This is is also referred to as
|
|
@@ -189,6 +187,11 @@ export class OdspDocumentService implements IDocumentService {
|
|
|
189
187
|
}
|
|
190
188
|
throw new Error("Disconnected while uploading summary (attempt to perform flush())");
|
|
191
189
|
},
|
|
190
|
+
() => {
|
|
191
|
+
assert(this.relayServiceTenantAndSessionId !== undefined,
|
|
192
|
+
"relayServiceTenantAndSessionId should be present");
|
|
193
|
+
return this.relayServiceTenantAndSessionId;
|
|
194
|
+
},
|
|
192
195
|
this.mc.config.getNumber("Fluid.Driver.Odsp.snapshotFormatFetchType"),
|
|
193
196
|
);
|
|
194
197
|
}
|
|
@@ -271,7 +274,7 @@ export class OdspDocumentService implements IDocumentService {
|
|
|
271
274
|
this.socketIoClientFactory().catch(annotateAndRethrowConnectionError("socketIoClientFactory")),
|
|
272
275
|
]);
|
|
273
276
|
|
|
274
|
-
const finalWebsocketToken = websocketToken ?? (websocketEndpoint.socketToken
|
|
277
|
+
const finalWebsocketToken = websocketToken ?? (websocketEndpoint.socketToken ?? null);
|
|
275
278
|
if (finalWebsocketToken === null) {
|
|
276
279
|
throw this.annotateConnectionError(
|
|
277
280
|
new NonRetryableError(
|
|
@@ -344,10 +347,9 @@ export class OdspDocumentService implements IDocumentService {
|
|
|
344
347
|
requestSocketToken: boolean,
|
|
345
348
|
options: TokenFetchOptionsEx,
|
|
346
349
|
) {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
for (const code of likelyFacetCodes.facetCodes) {
|
|
350
|
+
const response = await this.joinSessionCore(requestSocketToken, options).catch((e) => {
|
|
351
|
+
if (hasFacetCodes(e) && e.facetCodes !== undefined) {
|
|
352
|
+
for (const code of e.facetCodes) {
|
|
351
353
|
switch (code) {
|
|
352
354
|
case "sessionForbiddenOnPreservedFiles":
|
|
353
355
|
case "sessionForbiddenOnModerationEnabledLibrary":
|
|
@@ -364,6 +366,8 @@ export class OdspDocumentService implements IDocumentService {
|
|
|
364
366
|
}
|
|
365
367
|
throw e;
|
|
366
368
|
});
|
|
369
|
+
this.relayServiceTenantAndSessionId = `${response.tenantId}/${response.id}`;
|
|
370
|
+
return response;
|
|
367
371
|
}
|
|
368
372
|
|
|
369
373
|
private async joinSessionCore(
|