@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.
Files changed (95) hide show
  1. package/dist/epochTracker.d.ts +1 -0
  2. package/dist/epochTracker.d.ts.map +1 -1
  3. package/dist/epochTracker.js +24 -5
  4. package/dist/epochTracker.js.map +1 -1
  5. package/dist/fetchSnapshot.d.ts.map +1 -1
  6. package/dist/fetchSnapshot.js +19 -12
  7. package/dist/fetchSnapshot.js.map +1 -1
  8. package/dist/getFileLink.d.ts.map +1 -1
  9. package/dist/getFileLink.js +17 -25
  10. package/dist/getFileLink.js.map +1 -1
  11. package/dist/odspDeltaStorageService.d.ts +2 -1
  12. package/dist/odspDeltaStorageService.d.ts.map +1 -1
  13. package/dist/odspDeltaStorageService.js +5 -4
  14. package/dist/odspDeltaStorageService.js.map +1 -1
  15. package/dist/odspDocumentService.d.ts +1 -0
  16. package/dist/odspDocumentService.d.ts.map +1 -1
  17. package/dist/odspDocumentService.js +11 -5
  18. package/dist/odspDocumentService.js.map +1 -1
  19. package/dist/odspDocumentStorageManager.d.ts +4 -3
  20. package/dist/odspDocumentStorageManager.d.ts.map +1 -1
  21. package/dist/odspDocumentStorageManager.js +67 -60
  22. package/dist/odspDocumentStorageManager.js.map +1 -1
  23. package/dist/odspDriverUrlResolverForShareLink.js +3 -3
  24. package/dist/odspDriverUrlResolverForShareLink.js.map +1 -1
  25. package/dist/odspLocationRedirection.d.ts +14 -0
  26. package/dist/odspLocationRedirection.d.ts.map +1 -0
  27. package/dist/odspLocationRedirection.js +24 -0
  28. package/dist/odspLocationRedirection.js.map +1 -0
  29. package/dist/odspSummaryUploadManager.d.ts +2 -1
  30. package/dist/odspSummaryUploadManager.d.ts.map +1 -1
  31. package/dist/odspSummaryUploadManager.js +3 -4
  32. package/dist/odspSummaryUploadManager.js.map +1 -1
  33. package/dist/odspUrlHelper.js +2 -1
  34. package/dist/odspUrlHelper.js.map +1 -1
  35. package/dist/odspUtils.d.ts.map +1 -1
  36. package/dist/odspUtils.js +10 -2
  37. package/dist/odspUtils.js.map +1 -1
  38. package/dist/packageVersion.d.ts +1 -1
  39. package/dist/packageVersion.d.ts.map +1 -1
  40. package/dist/packageVersion.js +1 -1
  41. package/dist/packageVersion.js.map +1 -1
  42. package/lib/epochTracker.d.ts +1 -0
  43. package/lib/epochTracker.d.ts.map +1 -1
  44. package/lib/epochTracker.js +26 -7
  45. package/lib/epochTracker.js.map +1 -1
  46. package/lib/fetchSnapshot.d.ts.map +1 -1
  47. package/lib/fetchSnapshot.js +19 -12
  48. package/lib/fetchSnapshot.js.map +1 -1
  49. package/lib/getFileLink.d.ts.map +1 -1
  50. package/lib/getFileLink.js +19 -27
  51. package/lib/getFileLink.js.map +1 -1
  52. package/lib/odspDeltaStorageService.d.ts +2 -1
  53. package/lib/odspDeltaStorageService.d.ts.map +1 -1
  54. package/lib/odspDeltaStorageService.js +5 -4
  55. package/lib/odspDeltaStorageService.js.map +1 -1
  56. package/lib/odspDocumentService.d.ts +1 -0
  57. package/lib/odspDocumentService.d.ts.map +1 -1
  58. package/lib/odspDocumentService.js +13 -7
  59. package/lib/odspDocumentService.js.map +1 -1
  60. package/lib/odspDocumentStorageManager.d.ts +4 -3
  61. package/lib/odspDocumentStorageManager.d.ts.map +1 -1
  62. package/lib/odspDocumentStorageManager.js +68 -61
  63. package/lib/odspDocumentStorageManager.js.map +1 -1
  64. package/lib/odspDriverUrlResolverForShareLink.js +3 -3
  65. package/lib/odspDriverUrlResolverForShareLink.js.map +1 -1
  66. package/lib/odspLocationRedirection.d.ts +14 -0
  67. package/lib/odspLocationRedirection.d.ts.map +1 -0
  68. package/lib/odspLocationRedirection.js +20 -0
  69. package/lib/odspLocationRedirection.js.map +1 -0
  70. package/lib/odspSummaryUploadManager.d.ts +2 -1
  71. package/lib/odspSummaryUploadManager.d.ts.map +1 -1
  72. package/lib/odspSummaryUploadManager.js +3 -4
  73. package/lib/odspSummaryUploadManager.js.map +1 -1
  74. package/lib/odspUrlHelper.js +2 -1
  75. package/lib/odspUrlHelper.js.map +1 -1
  76. package/lib/odspUtils.d.ts.map +1 -1
  77. package/lib/odspUtils.js +11 -3
  78. package/lib/odspUtils.js.map +1 -1
  79. package/lib/packageVersion.d.ts +1 -1
  80. package/lib/packageVersion.d.ts.map +1 -1
  81. package/lib/packageVersion.js +1 -1
  82. package/lib/packageVersion.js.map +1 -1
  83. package/package.json +15 -15
  84. package/src/epochTracker.ts +47 -7
  85. package/src/fetchSnapshot.ts +29 -15
  86. package/src/getFileLink.ts +22 -25
  87. package/src/odspDeltaStorageService.ts +4 -2
  88. package/src/odspDocumentService.ts +16 -12
  89. package/src/odspDocumentStorageManager.ts +49 -41
  90. package/src/odspDriverUrlResolverForShareLink.ts +1 -1
  91. package/src/odspLocationRedirection.ts +23 -0
  92. package/src/odspSummaryUploadManager.ts +3 -3
  93. package/src/odspUrlHelper.ts +1 -1
  94. package/src/odspUtils.ts +15 -4
  95. package/src/packageVersion.ts +1 -1
@@ -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"]}
@@ -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.2.2";
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,UAAU,CAAC"}
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"}
@@ -5,5 +5,5 @@
5
5
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
6
  */
7
7
  export const pkgName = "@fluidframework/odsp-driver";
8
- export const pkgVersion = "1.2.2";
8
+ export const pkgVersion = "2.0.0-internal.1.0.0.82159";
9
9
  //# sourceMappingURL=packageVersion.js.map
@@ -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,OAAO,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 = \"1.2.2\";\n"]}
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.2.2",
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": "^1.2.2",
66
- "@fluidframework/driver-base": "^1.2.2",
67
- "@fluidframework/driver-definitions": "^1.2.2",
68
- "@fluidframework/driver-utils": "^1.2.2",
69
- "@fluidframework/gitresources": "^0.1036.5000",
70
- "@fluidframework/odsp-doclib-utils": "^1.2.2",
71
- "@fluidframework/odsp-driver-definitions": "^1.2.2",
72
- "@fluidframework/protocol-base": "^0.1036.5000",
73
- "@fluidframework/protocol-definitions": "^0.1028.2000",
74
- "@fluidframework/telemetry-utils": "^1.2.2",
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.2.74327",
82
+ "@fluidframework/build-tools": "^0.3.0-0",
83
83
  "@fluidframework/eslint-config-fluid": "^0.28.2000",
84
- "@fluidframework/mocha-test-setup": "^1.2.2",
85
- "@fluidframework/odsp-driver-previous": "npm:@fluidframework/odsp-driver@1.2.1",
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": "1.2.2",
103
+ "version": "2.0.0",
104
104
  "broken": {}
105
105
  }
106
106
  }
@@ -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 { ThrottlingError, RateLimiter, NonRetryableError } from "@fluidframework/driver-utils";
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 { PerformanceEvent, isFluidError, normalizeError } from "@fluidframework/telemetry-utils";
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 the defaultCacheExpiryTimeoutMs and immediately
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 >= defaultCacheExpiryTimeoutMs) {
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: defaultCacheExpiryTimeoutMs,
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. This will be used to expire snapshots older
125
- // than the defaultCacheExpiryTimeoutMs.
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;
@@ -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
- odspResponse.propsToLog = {
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
- odspResponse.propsToLog.bodySize = text.length;
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
- odspResponse.propsToLog.bodySize = content.byteLength;
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
- { driverVersion: pkgVersion, ...odspResponse.propsToLog },
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
- { driverVersion: pkgVersion, ...odspResponse.propsToLog },
288
+ propsToLog,
278
289
  );
279
290
  }
280
291
  } catch (error) {
281
292
  if (isFluidError(error)) {
282
- error.addTelemetryProperties({ driverVersion: pkgVersion, ...odspResponse.propsToLog });
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
- { driverVersion: pkgVersion, ...odspResponse.propsToLog }));
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: numTrees,
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
- ...odspResponse.propsToLog,
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 numTrees = countTreesInSnapshotTree(snapshot.snapshotTree);
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 { numTrees, numBlobs, encodedBlobsSize };
489
+ return { trees, numBlobs, encodedBlobsSize };
476
490
  }
477
491
 
478
492
  export function validateBlobsAndTrees(snapshot: IOdspSnapshot) {
@@ -4,8 +4,8 @@
4
4
  */
5
5
 
6
6
  import { ITelemetryLogger } from "@fluidframework/common-definitions";
7
- import { assert, delay } from "@fluidframework/common-utils";
8
- import { canRetryOnError, getRetryDelayFromError, NonRetryableError } from "@fluidframework/driver-utils";
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 valueGenerator = async function() {
51
- let result: string | undefined;
52
- let success = false;
53
- let retryAfterMs = 1000;
54
- do {
55
- try {
56
- result = await getFileLinkCore(getToken, odspUrlParts, identityType, logger);
57
- success = true;
58
- } catch (err) {
59
- // If it is not retriable, then just throw
60
- if (!canRetryOnError(err)) {
61
- // Delete from the cache to permit retrying later.
62
- fileLinkCache.delete(cacheKey);
63
- throw err;
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
- } while (!success);
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(result !== undefined, 0x292 /* "Unexpected undefined result from getFileLinkCore" */);
74
- return result;
70
+ assert(fileLinkCore !== undefined, 0x292 /* "Unexpected undefined result from getFileLinkCore" */);
71
+ return fileLinkCore;
75
72
  };
76
- const fileLink = valueGenerator();
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
- fetchReason?: string,
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
- fetchReason,
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 || null);
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
- return this.joinSessionCore(requestSocketToken, options).catch((e) => {
348
- const likelyFacetCodes = e as IFacetCodes;
349
- if (Array.isArray(likelyFacetCodes.facetCodes)) {
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(