@fluidframework/odsp-driver 2.92.0 → 2.100.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +1 -1
- package/dist/compactSnapshotParser.d.ts +2 -2
- package/dist/compactSnapshotParser.d.ts.map +1 -1
- package/dist/compactSnapshotParser.js.map +1 -1
- package/dist/epochTracker.d.ts +8 -4
- package/dist/epochTracker.d.ts.map +1 -1
- package/dist/epochTracker.js +12 -9
- package/dist/epochTracker.js.map +1 -1
- package/dist/fetchSnapshot.d.ts +10 -7
- package/dist/fetchSnapshot.d.ts.map +1 -1
- package/dist/fetchSnapshot.js +58 -25
- package/dist/fetchSnapshot.js.map +1 -1
- package/dist/odspDocumentStorageManager.d.ts.map +1 -1
- package/dist/odspDocumentStorageManager.js +1 -1
- package/dist/odspDocumentStorageManager.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/prefetchLatestSnapshot.d.ts.map +1 -1
- package/dist/prefetchLatestSnapshot.js +2 -2
- package/dist/prefetchLatestSnapshot.js.map +1 -1
- package/eslint.config.mts +1 -1
- package/lib/compactSnapshotParser.d.ts +2 -2
- package/lib/compactSnapshotParser.d.ts.map +1 -1
- package/lib/compactSnapshotParser.js.map +1 -1
- package/lib/epochTracker.d.ts +8 -4
- package/lib/epochTracker.d.ts.map +1 -1
- package/lib/epochTracker.js +13 -10
- package/lib/epochTracker.js.map +1 -1
- package/lib/fetchSnapshot.d.ts +10 -7
- package/lib/fetchSnapshot.d.ts.map +1 -1
- package/lib/fetchSnapshot.js +37 -4
- package/lib/fetchSnapshot.js.map +1 -1
- package/lib/odspDocumentStorageManager.d.ts.map +1 -1
- package/lib/odspDocumentStorageManager.js +1 -1
- package/lib/odspDocumentStorageManager.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/prefetchLatestSnapshot.d.ts.map +1 -1
- package/lib/prefetchLatestSnapshot.js +2 -2
- package/lib/prefetchLatestSnapshot.js.map +1 -1
- package/lib/tsdoc-metadata.json +1 -1
- package/package.json +17 -18
- package/src/compactSnapshotParser.ts +2 -2
- package/src/epochTracker.ts +30 -11
- package/src/fetchSnapshot.ts +53 -8
- package/src/odspDocumentStorageManager.ts +1 -0
- package/src/packageVersion.ts +1 -1
- package/src/prefetchLatestSnapshot.ts +2 -1
|
@@ -46,7 +46,7 @@ export async function prefetchLatestSnapshot(resolvedUrl, getStorageToken, persi
|
|
|
46
46
|
};
|
|
47
47
|
const getAuthHeader = toInstrumentedOdspStorageTokenFetcher(odspLogger, resolvedUrlData, getStorageToken);
|
|
48
48
|
const snapshotDownloader = async (finalOdspResolvedUrl, storageTokenFetcher, tokenFetchOptions, loadingGroupId, snapshotOptions, controller) => {
|
|
49
|
-
return downloadSnapshot(finalOdspResolvedUrl, storageTokenFetcher, tokenFetchOptions, loadingGroupId, snapshotOptions, undefined, controller);
|
|
49
|
+
return downloadSnapshot(finalOdspResolvedUrl, storageTokenFetcher, tokenFetchOptions, loadingGroupId, snapshotOptions, odspLogger, undefined, controller);
|
|
50
50
|
};
|
|
51
51
|
const snapshotKey = createCacheSnapshotKey(odspResolvedUrl, useGroupIdsForSnapshotFetch);
|
|
52
52
|
let cacheP;
|
|
@@ -88,7 +88,7 @@ export async function prefetchLatestSnapshot(resolvedUrl, getStorageToken, persi
|
|
|
88
88
|
}, 5000);
|
|
89
89
|
})
|
|
90
90
|
.catch((error) => {
|
|
91
|
-
// Remove it from the non persistent cache if an error
|
|
91
|
+
// Remove it from the non persistent cache if an error occurred.
|
|
92
92
|
snapshotNonPersistentCache?.remove(nonPersistentCacheKey);
|
|
93
93
|
snapshotContentsWithEpochP.reject(error);
|
|
94
94
|
throw error;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefetchLatestSnapshot.js","sourceRoot":"","sources":["../src/prefetchLatestSnapshot.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 { performanceNow } from \"@fluid-internal/client-utils\";\nimport type { ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert, Deferred } from \"@fluidframework/core-utils/internal\";\nimport type {\n\tIPersistedCache,\n\tIResolvedUrl,\n} from \"@fluidframework/driver-definitions/internal\";\nimport { getKeyForCacheEntry } from \"@fluidframework/driver-utils/internal\";\nimport type {\n\tIOdspResolvedUrl,\n\tIOdspUrlParts,\n\tISnapshotOptions,\n\tOdspResourceTokenFetchOptions,\n\tTokenFetcher,\n\tInstrumentedStorageTokenFetcher,\n} from \"@fluidframework/odsp-driver-definitions/internal\";\nimport {\n\tPerformanceEvent,\n\tcreateChildMonitoringContext,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport type { IVersionedValueWithEpoch } from \"./contracts.js\";\nimport {\n\ttype ISnapshotRequestAndResponseOptions,\n\ttype SnapshotFormatSupportType,\n\tdownloadSnapshot,\n\tfetchSnapshotWithRedeem,\n} from \"./fetchSnapshot.js\";\nimport type { IPrefetchSnapshotContents } from \"./odspCache.js\";\nimport type { OdspDocumentServiceFactory } from \"./odspDocumentServiceFactory.js\";\nimport {\n\tcreateCacheSnapshotKey,\n\tcreateOdspLogger,\n\tgetOdspResolvedUrl,\n\tsnapshotWithLoadingGroupIdSupported,\n\ttoInstrumentedOdspStorageTokenFetcher,\n\ttype TokenFetchOptionsEx,\n} from \"./odspUtils.js\";\n\n/**\n * Function to prefetch the snapshot and cached it in the persistant cache, so that when the container is loaded\n * the cached latest snapshot could be used and removes the network call from the critical path.\n *\n * @param resolvedUrl - Resolved url to fetch the snapshot.\n * @param getStorageToken - function that can provide the storage token for a given site. This is\n * is also referred to as the \"VROOM\" token in SPO.\n * @param persistedCache - Cache to store the fetched snapshot.\n * @param forceAccessTokenViaAuthorizationHeader - @deprecated Not used, true value always used instead. Whether to force passing given token via authorization header.\n * @param logger - Logger to have telemetry events.\n * @param hostSnapshotFetchOptions - Options to fetch the snapshot if any. Otherwise default will be used.\n * @param enableRedeemFallback - True to have the sharing link redeem fallback in case the Trees Latest/Redeem\n * 1RT call fails with redeem error. During fallback it will first redeem the sharing link and then make\n * the Trees latest call.\n * Note: this can be considered deprecated - it will be replaced with `snapshotFormatFetchType`.\n * @param fetchBinarySnapshotFormat - Control if we want to fetch binary format snapshot.\n * @param snapshotFormatFetchType - Snapshot format to fetch.\n * @param odspDocumentServiceFactory - factory to access the non persistent cache and store the prefetch promise.\n *\n * @returns `true` if the snapshot is cached, `false` otherwise.\n * @legacy\n * @beta\n */\nexport async function prefetchLatestSnapshot(\n\tresolvedUrl: IResolvedUrl,\n\tgetStorageToken: TokenFetcher<OdspResourceTokenFetchOptions>,\n\tpersistedCache: IPersistedCache,\n\tforceAccessTokenViaAuthorizationHeader: boolean,\n\tlogger: ITelemetryBaseLogger,\n\thostSnapshotFetchOptions: ISnapshotOptions | undefined,\n\tenableRedeemFallback: boolean = true,\n\tfetchBinarySnapshotFormat?: boolean,\n\tsnapshotFormatFetchType?: SnapshotFormatSupportType,\n\todspDocumentServiceFactory?: OdspDocumentServiceFactory,\n): Promise<boolean> {\n\tconst mc = createChildMonitoringContext({ logger, namespace: \"PrefetchSnapshot\" });\n\tconst odspLogger = createOdspLogger(mc.logger);\n\tconst useGroupIdsForSnapshotFetch = snapshotWithLoadingGroupIdSupported(mc.config);\n\t// For prefetch, we just want to fetch the ungrouped data and want to use the new API if the\n\t// feature gate is set, so provide an empty array.\n\tconst loadingGroupIds = useGroupIdsForSnapshotFetch ? [] : undefined;\n\tconst odspResolvedUrl = getOdspResolvedUrl(resolvedUrl);\n\n\tconst resolvedUrlData: IOdspUrlParts = {\n\t\tsiteUrl: odspResolvedUrl.siteUrl,\n\t\tdriveId: odspResolvedUrl.driveId,\n\t\titemId: odspResolvedUrl.itemId,\n\t};\n\tconst getAuthHeader = toInstrumentedOdspStorageTokenFetcher(\n\t\todspLogger,\n\t\tresolvedUrlData,\n\t\tgetStorageToken,\n\t);\n\n\tconst snapshotDownloader = async (\n\t\tfinalOdspResolvedUrl: IOdspResolvedUrl,\n\t\tstorageTokenFetcher: InstrumentedStorageTokenFetcher,\n\t\ttokenFetchOptions: TokenFetchOptionsEx,\n\t\tloadingGroupId: string[] | undefined,\n\t\tsnapshotOptions: ISnapshotOptions | undefined,\n\t\tcontroller?: AbortController,\n\t): Promise<ISnapshotRequestAndResponseOptions> => {\n\t\treturn downloadSnapshot(\n\t\t\tfinalOdspResolvedUrl,\n\t\t\tstorageTokenFetcher,\n\t\t\ttokenFetchOptions,\n\t\t\tloadingGroupId,\n\t\t\tsnapshotOptions,\n\t\t\tundefined,\n\t\t\tcontroller,\n\t\t);\n\t};\n\tconst snapshotKey = createCacheSnapshotKey(odspResolvedUrl, useGroupIdsForSnapshotFetch);\n\tlet cacheP: Promise<void> | undefined;\n\tlet snapshotEpoch: string | undefined;\n\tconst putInCache = async (valueWithEpoch: IVersionedValueWithEpoch): Promise<void> => {\n\t\tsnapshotEpoch = valueWithEpoch.fluidEpoch;\n\t\tcacheP = persistedCache.put(snapshotKey, valueWithEpoch);\n\t\treturn cacheP;\n\t};\n\n\tconst removeEntries = async (): Promise<void> =>\n\t\tpersistedCache.removeEntries(snapshotKey.file);\n\treturn PerformanceEvent.timedExecAsync(\n\t\todspLogger,\n\t\t{ eventName: \"PrefetchLatestSnapshot\" },\n\t\tasync () => {\n\t\t\tconst prefetchStartTime = performanceNow();\n\t\t\t// Add the deferred promise to the cache, so that it can be leveraged while loading the container.\n\t\t\tconst snapshotContentsWithEpochP = new Deferred<IPrefetchSnapshotContents>();\n\t\t\tconst nonPersistentCacheKey = getKeyForCacheEntry(snapshotKey);\n\t\t\tconst snapshotNonPersistentCache =\n\t\t\t\todspDocumentServiceFactory?.snapshotPrefetchResultCache;\n\t\t\tsnapshotNonPersistentCache?.add(\n\t\t\t\tnonPersistentCacheKey,\n\t\t\t\tasync () => snapshotContentsWithEpochP.promise,\n\t\t\t);\n\t\t\tawait fetchSnapshotWithRedeem(\n\t\t\t\todspResolvedUrl,\n\t\t\t\tgetAuthHeader,\n\t\t\t\thostSnapshotFetchOptions,\n\t\t\t\tforceAccessTokenViaAuthorizationHeader,\n\t\t\t\todspLogger,\n\t\t\t\tsnapshotDownloader,\n\t\t\t\tputInCache,\n\t\t\t\tremoveEntries,\n\t\t\t\tloadingGroupIds,\n\t\t\t\tenableRedeemFallback,\n\t\t\t)\n\t\t\t\t.then(async (value) => {\n\t\t\t\t\tassert(!!snapshotEpoch, 0x585 /* prefetched snapshot should have a valid epoch */);\n\t\t\t\t\tsnapshotContentsWithEpochP.resolve({\n\t\t\t\t\t\t...value,\n\t\t\t\t\t\tfluidEpoch: snapshotEpoch,\n\t\t\t\t\t\tprefetchStartTime,\n\t\t\t\t\t});\n\t\t\t\t\tassert(cacheP !== undefined, 0x1e7 /* \"caching was not performed!\" */);\n\t\t\t\t\tawait cacheP;\n\t\t\t\t\t// Schedule it to remove from cache after 5s.\n\t\t\t\t\t// 1. While it's in snapshotNonPersistentCache: Load flow will use this value and will not attempt\n\t\t\t\t\t// to fetch snapshot from network again. That's the best from perf POV, but cache will not be\n\t\t\t\t\t// updated if we keep it in this cache, thus we want to eventually remove snapshot from this cache.\n\t\t\t\t\t// 2. After it's removed from snapshotNonPersistentCache: snapshot is present in persistent cache,\n\t\t\t\t\t// so we sill still use it (in accordance with cache policy controlled by host). But load flow will\n\t\t\t\t\t// also fetch snapshot (in parallel) from storage and update cache. This is fine long term,\n\t\t\t\t\t// but is an extra cost (unneeded network call). However since it is 5s older, new network call\n\t\t\t\t\t// will update the snapshot in cache.\n\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\tsnapshotNonPersistentCache?.remove(nonPersistentCacheKey);\n\t\t\t\t\t}, 5000);\n\t\t\t\t})\n\t\t\t\t.catch((error) => {\n\t\t\t\t\t// Remove it from the non persistent cache if an error occured.\n\t\t\t\t\tsnapshotNonPersistentCache?.remove(nonPersistentCacheKey);\n\t\t\t\t\tsnapshotContentsWithEpochP.reject(error);\n\t\t\t\t\tthrow error;\n\t\t\t\t});\n\t\t\treturn true;\n\t\t},\n\t).catch(async (error) => {\n\t\todspLogger.sendErrorEvent({ eventName: \"PrefetchLatestSnapshotError\" }, error);\n\t\treturn false;\n\t});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"prefetchLatestSnapshot.js","sourceRoot":"","sources":["../src/prefetchLatestSnapshot.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 { performanceNow } from \"@fluid-internal/client-utils\";\nimport type { ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert, Deferred } from \"@fluidframework/core-utils/internal\";\nimport type {\n\tIPersistedCache,\n\tIResolvedUrl,\n} from \"@fluidframework/driver-definitions/internal\";\nimport { getKeyForCacheEntry } from \"@fluidframework/driver-utils/internal\";\nimport type {\n\tIOdspResolvedUrl,\n\tIOdspUrlParts,\n\tISnapshotOptions,\n\tOdspResourceTokenFetchOptions,\n\tTokenFetcher,\n\tInstrumentedStorageTokenFetcher,\n} from \"@fluidframework/odsp-driver-definitions/internal\";\nimport {\n\tPerformanceEvent,\n\tcreateChildMonitoringContext,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport type { IVersionedValueWithEpoch } from \"./contracts.js\";\nimport {\n\ttype ISnapshotRequestAndResponseOptions,\n\ttype SnapshotFormatSupportType,\n\tdownloadSnapshot,\n\tfetchSnapshotWithRedeem,\n} from \"./fetchSnapshot.js\";\nimport type { IPrefetchSnapshotContents } from \"./odspCache.js\";\nimport type { OdspDocumentServiceFactory } from \"./odspDocumentServiceFactory.js\";\nimport {\n\tcreateCacheSnapshotKey,\n\tcreateOdspLogger,\n\tgetOdspResolvedUrl,\n\tsnapshotWithLoadingGroupIdSupported,\n\ttoInstrumentedOdspStorageTokenFetcher,\n\ttype TokenFetchOptionsEx,\n} from \"./odspUtils.js\";\n\n/**\n * Function to prefetch the snapshot and cached it in the persistant cache, so that when the container is loaded\n * the cached latest snapshot could be used and removes the network call from the critical path.\n *\n * @param resolvedUrl - Resolved url to fetch the snapshot.\n * @param getStorageToken - function that can provide the storage token for a given site. This is\n * is also referred to as the \"VROOM\" token in SPO.\n * @param persistedCache - Cache to store the fetched snapshot.\n * @param forceAccessTokenViaAuthorizationHeader - @deprecated Not used, true value always used instead. Whether to force passing given token via authorization header.\n * @param logger - Logger to have telemetry events.\n * @param hostSnapshotFetchOptions - Options to fetch the snapshot if any. Otherwise default will be used.\n * @param enableRedeemFallback - True to have the sharing link redeem fallback in case the Trees Latest/Redeem\n * 1RT call fails with redeem error. During fallback it will first redeem the sharing link and then make\n * the Trees latest call.\n * Note: this can be considered deprecated - it will be replaced with `snapshotFormatFetchType`.\n * @param fetchBinarySnapshotFormat - Control if we want to fetch binary format snapshot.\n * @param snapshotFormatFetchType - Snapshot format to fetch.\n * @param odspDocumentServiceFactory - factory to access the non persistent cache and store the prefetch promise.\n *\n * @returns `true` if the snapshot is cached, `false` otherwise.\n * @legacy\n * @beta\n */\nexport async function prefetchLatestSnapshot(\n\tresolvedUrl: IResolvedUrl,\n\tgetStorageToken: TokenFetcher<OdspResourceTokenFetchOptions>,\n\tpersistedCache: IPersistedCache,\n\tforceAccessTokenViaAuthorizationHeader: boolean,\n\tlogger: ITelemetryBaseLogger,\n\thostSnapshotFetchOptions: ISnapshotOptions | undefined,\n\tenableRedeemFallback: boolean = true,\n\tfetchBinarySnapshotFormat?: boolean,\n\tsnapshotFormatFetchType?: SnapshotFormatSupportType,\n\todspDocumentServiceFactory?: OdspDocumentServiceFactory,\n): Promise<boolean> {\n\tconst mc = createChildMonitoringContext({ logger, namespace: \"PrefetchSnapshot\" });\n\tconst odspLogger = createOdspLogger(mc.logger);\n\tconst useGroupIdsForSnapshotFetch = snapshotWithLoadingGroupIdSupported(mc.config);\n\t// For prefetch, we just want to fetch the ungrouped data and want to use the new API if the\n\t// feature gate is set, so provide an empty array.\n\tconst loadingGroupIds = useGroupIdsForSnapshotFetch ? [] : undefined;\n\tconst odspResolvedUrl = getOdspResolvedUrl(resolvedUrl);\n\n\tconst resolvedUrlData: IOdspUrlParts = {\n\t\tsiteUrl: odspResolvedUrl.siteUrl,\n\t\tdriveId: odspResolvedUrl.driveId,\n\t\titemId: odspResolvedUrl.itemId,\n\t};\n\tconst getAuthHeader = toInstrumentedOdspStorageTokenFetcher(\n\t\todspLogger,\n\t\tresolvedUrlData,\n\t\tgetStorageToken,\n\t);\n\n\tconst snapshotDownloader = async (\n\t\tfinalOdspResolvedUrl: IOdspResolvedUrl,\n\t\tstorageTokenFetcher: InstrumentedStorageTokenFetcher,\n\t\ttokenFetchOptions: TokenFetchOptionsEx,\n\t\tloadingGroupId: string[] | undefined,\n\t\tsnapshotOptions: ISnapshotOptions | undefined,\n\t\tcontroller?: AbortController,\n\t): Promise<ISnapshotRequestAndResponseOptions> => {\n\t\treturn downloadSnapshot(\n\t\t\tfinalOdspResolvedUrl,\n\t\t\tstorageTokenFetcher,\n\t\t\ttokenFetchOptions,\n\t\t\tloadingGroupId,\n\t\t\tsnapshotOptions,\n\t\t\todspLogger,\n\t\t\tundefined,\n\t\t\tcontroller,\n\t\t);\n\t};\n\tconst snapshotKey = createCacheSnapshotKey(odspResolvedUrl, useGroupIdsForSnapshotFetch);\n\tlet cacheP: Promise<void> | undefined;\n\tlet snapshotEpoch: string | undefined;\n\tconst putInCache = async (valueWithEpoch: IVersionedValueWithEpoch): Promise<void> => {\n\t\tsnapshotEpoch = valueWithEpoch.fluidEpoch;\n\t\tcacheP = persistedCache.put(snapshotKey, valueWithEpoch);\n\t\treturn cacheP;\n\t};\n\n\tconst removeEntries = async (): Promise<void> =>\n\t\tpersistedCache.removeEntries(snapshotKey.file);\n\treturn PerformanceEvent.timedExecAsync(\n\t\todspLogger,\n\t\t{ eventName: \"PrefetchLatestSnapshot\" },\n\t\tasync () => {\n\t\t\tconst prefetchStartTime = performanceNow();\n\t\t\t// Add the deferred promise to the cache, so that it can be leveraged while loading the container.\n\t\t\tconst snapshotContentsWithEpochP = new Deferred<IPrefetchSnapshotContents>();\n\t\t\tconst nonPersistentCacheKey = getKeyForCacheEntry(snapshotKey);\n\t\t\tconst snapshotNonPersistentCache =\n\t\t\t\todspDocumentServiceFactory?.snapshotPrefetchResultCache;\n\t\t\tsnapshotNonPersistentCache?.add(\n\t\t\t\tnonPersistentCacheKey,\n\t\t\t\tasync () => snapshotContentsWithEpochP.promise,\n\t\t\t);\n\t\t\tawait fetchSnapshotWithRedeem(\n\t\t\t\todspResolvedUrl,\n\t\t\t\tgetAuthHeader,\n\t\t\t\thostSnapshotFetchOptions,\n\t\t\t\tforceAccessTokenViaAuthorizationHeader,\n\t\t\t\todspLogger,\n\t\t\t\tsnapshotDownloader,\n\t\t\t\tputInCache,\n\t\t\t\tremoveEntries,\n\t\t\t\tloadingGroupIds,\n\t\t\t\tenableRedeemFallback,\n\t\t\t)\n\t\t\t\t.then(async (value) => {\n\t\t\t\t\tassert(!!snapshotEpoch, 0x585 /* prefetched snapshot should have a valid epoch */);\n\t\t\t\t\tsnapshotContentsWithEpochP.resolve({\n\t\t\t\t\t\t...value,\n\t\t\t\t\t\tfluidEpoch: snapshotEpoch,\n\t\t\t\t\t\tprefetchStartTime,\n\t\t\t\t\t});\n\t\t\t\t\tassert(cacheP !== undefined, 0x1e7 /* \"caching was not performed!\" */);\n\t\t\t\t\tawait cacheP;\n\t\t\t\t\t// Schedule it to remove from cache after 5s.\n\t\t\t\t\t// 1. While it's in snapshotNonPersistentCache: Load flow will use this value and will not attempt\n\t\t\t\t\t// to fetch snapshot from network again. That's the best from perf POV, but cache will not be\n\t\t\t\t\t// updated if we keep it in this cache, thus we want to eventually remove snapshot from this cache.\n\t\t\t\t\t// 2. After it's removed from snapshotNonPersistentCache: snapshot is present in persistent cache,\n\t\t\t\t\t// so we sill still use it (in accordance with cache policy controlled by host). But load flow will\n\t\t\t\t\t// also fetch snapshot (in parallel) from storage and update cache. This is fine long term,\n\t\t\t\t\t// but is an extra cost (unneeded network call). However since it is 5s older, new network call\n\t\t\t\t\t// will update the snapshot in cache.\n\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\tsnapshotNonPersistentCache?.remove(nonPersistentCacheKey);\n\t\t\t\t\t}, 5000);\n\t\t\t\t})\n\t\t\t\t.catch((error) => {\n\t\t\t\t\t// Remove it from the non persistent cache if an error occurred.\n\t\t\t\t\tsnapshotNonPersistentCache?.remove(nonPersistentCacheKey);\n\t\t\t\t\tsnapshotContentsWithEpochP.reject(error);\n\t\t\t\t\tthrow error;\n\t\t\t\t});\n\t\t\treturn true;\n\t\t},\n\t).catch(async (error) => {\n\t\todspLogger.sendErrorEvent({ eventName: \"PrefetchLatestSnapshotError\" }, error);\n\t\treturn false;\n\t});\n}\n"]}
|
package/lib/tsdoc-metadata.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/odsp-driver",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.100.0",
|
|
4
4
|
"description": "Socket storage implementation for SPO and ODC",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -69,30 +69,30 @@
|
|
|
69
69
|
"temp-directory": "nyc/.nyc_output"
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
|
-
"@fluid-internal/client-utils": "~2.
|
|
73
|
-
"@fluidframework/core-interfaces": "~2.
|
|
74
|
-
"@fluidframework/core-utils": "~2.
|
|
75
|
-
"@fluidframework/driver-base": "~2.
|
|
76
|
-
"@fluidframework/driver-definitions": "~2.
|
|
77
|
-
"@fluidframework/driver-utils": "~2.
|
|
78
|
-
"@fluidframework/odsp-doclib-utils": "~2.
|
|
79
|
-
"@fluidframework/odsp-driver-definitions": "~2.
|
|
80
|
-
"@fluidframework/telemetry-utils": "~2.
|
|
72
|
+
"@fluid-internal/client-utils": "~2.100.0",
|
|
73
|
+
"@fluidframework/core-interfaces": "~2.100.0",
|
|
74
|
+
"@fluidframework/core-utils": "~2.100.0",
|
|
75
|
+
"@fluidframework/driver-base": "~2.100.0",
|
|
76
|
+
"@fluidframework/driver-definitions": "~2.100.0",
|
|
77
|
+
"@fluidframework/driver-utils": "~2.100.0",
|
|
78
|
+
"@fluidframework/odsp-doclib-utils": "~2.100.0",
|
|
79
|
+
"@fluidframework/odsp-driver-definitions": "~2.100.0",
|
|
80
|
+
"@fluidframework/telemetry-utils": "~2.100.0",
|
|
81
81
|
"socket.io-client": "^4.8.3",
|
|
82
82
|
"uuid": "^11.1.0"
|
|
83
83
|
},
|
|
84
84
|
"devDependencies": {
|
|
85
85
|
"@arethetypeswrong/cli": "^0.18.2",
|
|
86
86
|
"@biomejs/biome": "~2.4.5",
|
|
87
|
-
"@fluid-internal/mocha-test-setup": "~2.
|
|
88
|
-
"@fluid-tools/build-cli": "^0.
|
|
87
|
+
"@fluid-internal/mocha-test-setup": "~2.100.0",
|
|
88
|
+
"@fluid-tools/build-cli": "^0.65.0",
|
|
89
89
|
"@fluidframework/build-common": "^2.0.3",
|
|
90
|
-
"@fluidframework/build-tools": "^0.
|
|
90
|
+
"@fluidframework/build-tools": "^0.65.0",
|
|
91
91
|
"@fluidframework/eslint-config-fluid": "^9.0.0",
|
|
92
|
-
"@fluidframework/odsp-driver-previous": "npm:@fluidframework/odsp-driver@2.
|
|
93
|
-
"@microsoft/api-extractor": "7.
|
|
92
|
+
"@fluidframework/odsp-driver-previous": "npm:@fluidframework/odsp-driver@2.92.0",
|
|
93
|
+
"@microsoft/api-extractor": "7.58.1",
|
|
94
94
|
"@types/mocha": "^10.0.10",
|
|
95
|
-
"@types/node": "~
|
|
95
|
+
"@types/node": "~22.19.17",
|
|
96
96
|
"@types/sinon": "^17.0.3",
|
|
97
97
|
"c8": "^10.1.3",
|
|
98
98
|
"concurrently": "^9.2.1",
|
|
@@ -153,7 +153,6 @@
|
|
|
153
153
|
"test:mocha:esm": "mocha",
|
|
154
154
|
"test:mocha:verbose": "cross-env FLUID_TEST_VERBOSE=1 npm run test:mocha",
|
|
155
155
|
"tsc": "fluid-tsc commonjs --project ./tsconfig.cjs.json && copyfiles -f ../../../common/build/build-common/src/cjs/package.json ./dist",
|
|
156
|
-
"typetests:gen": "flub generate typetests --dir . -v"
|
|
157
|
-
"typetests:prepare": "flub typetests --dir . --reset --previous --normalize"
|
|
156
|
+
"typetests:gen": "flub generate typetests --dir . -v"
|
|
158
157
|
}
|
|
159
158
|
}
|
|
@@ -9,7 +9,7 @@ import type {
|
|
|
9
9
|
ISnapshotTree,
|
|
10
10
|
ISequencedDocumentMessage,
|
|
11
11
|
} from "@fluidframework/driver-definitions/internal";
|
|
12
|
-
import type {
|
|
12
|
+
import type { TelemetryLoggerExt } from "@fluidframework/telemetry-utils/internal";
|
|
13
13
|
|
|
14
14
|
import { ReadBuffer } from "./ReadBufferUtils.js";
|
|
15
15
|
import { measure } from "./odspUtils.js";
|
|
@@ -261,7 +261,7 @@ function readSnapshotSection(node: NodeTypes): {
|
|
|
261
261
|
*/
|
|
262
262
|
export function parseCompactSnapshotResponse(
|
|
263
263
|
buffer: Uint8Array,
|
|
264
|
-
logger:
|
|
264
|
+
logger: TelemetryLoggerExt,
|
|
265
265
|
): ISnapshotContentsWithProps {
|
|
266
266
|
const { builder, telemetryProps } = TreeBuilder.load(new ReadBuffer(buffer), logger);
|
|
267
267
|
assert(builder.length === 1, 0x219 /* "1 root should be there" */);
|
package/src/epochTracker.ts
CHANGED
|
@@ -25,14 +25,18 @@ import {
|
|
|
25
25
|
snapshotKey,
|
|
26
26
|
snapshotWithLoadingGroupIdKey,
|
|
27
27
|
} from "@fluidframework/odsp-driver-definitions/internal";
|
|
28
|
+
import type { TelemetryLoggerExt } from "@fluidframework/telemetry-utils/internal";
|
|
28
29
|
import {
|
|
29
|
-
type ITelemetryLoggerExt,
|
|
30
30
|
PerformanceEvent,
|
|
31
|
+
extractTelemetryLoggerExt,
|
|
31
32
|
isFluidError,
|
|
32
33
|
loggerToMonitoringContext,
|
|
33
34
|
normalizeError,
|
|
35
|
+
toITelemetryLoggerExt,
|
|
34
36
|
wrapError,
|
|
35
37
|
} from "@fluidframework/telemetry-utils/internal";
|
|
38
|
+
// eslint-disable-next-line import-x/no-internal-modules -- Needed to avoid specialized /internal ITelemetryLoggerExt
|
|
39
|
+
import type { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils/legacy";
|
|
36
40
|
import { v4 as uuid } from "uuid";
|
|
37
41
|
|
|
38
42
|
import { type IVersionedValueWithEpoch, persistedCacheValueVersion } from "./contracts.js";
|
|
@@ -90,6 +94,9 @@ export const Odsp409Error = "Odsp409Error";
|
|
|
90
94
|
* then it also clears all the cached entries for the given container.
|
|
91
95
|
* @legacy
|
|
92
96
|
* @beta
|
|
97
|
+
*
|
|
98
|
+
* @privateRemarks
|
|
99
|
+
* This class should be hidden and an interface exposed to better manage internal types like telemetry logger.
|
|
93
100
|
*/
|
|
94
101
|
export class EpochTracker implements IPersistedFileCache {
|
|
95
102
|
private _fluidEpoch: string | undefined;
|
|
@@ -99,12 +106,15 @@ export class EpochTracker implements IPersistedFileCache {
|
|
|
99
106
|
private readonly driverId = uuid();
|
|
100
107
|
// This tracks the request number made by the driver instance.
|
|
101
108
|
private networkCallNumber = 1;
|
|
109
|
+
private readonly loggerInternal: TelemetryLoggerExt;
|
|
102
110
|
constructor(
|
|
103
111
|
protected readonly cache: IPersistedCache,
|
|
104
112
|
protected readonly fileEntry: IFileEntry,
|
|
105
113
|
protected readonly logger: ITelemetryLoggerExt,
|
|
106
114
|
protected readonly clientIsSummarizer?: boolean,
|
|
107
115
|
) {
|
|
116
|
+
this.loggerInternal = extractTelemetryLoggerExt(logger);
|
|
117
|
+
|
|
108
118
|
// Limits the max number of concurrent requests to 24.
|
|
109
119
|
this.rateLimiter = new RateLimiter(24);
|
|
110
120
|
|
|
@@ -121,7 +131,7 @@ export class EpochTracker implements IPersistedFileCache {
|
|
|
121
131
|
assert(this._fluidEpoch === undefined, 0x1db /* "epoch exists" */);
|
|
122
132
|
this._fluidEpoch = epoch;
|
|
123
133
|
|
|
124
|
-
this.
|
|
134
|
+
this.loggerInternal.sendTelemetryEvent({
|
|
125
135
|
eventName: "EpochLearnedFirstTime",
|
|
126
136
|
epoch,
|
|
127
137
|
fetchType,
|
|
@@ -137,7 +147,7 @@ export class EpochTracker implements IPersistedFileCache {
|
|
|
137
147
|
const value = (await this.cache.get(
|
|
138
148
|
this.fileEntryFromEntry(entry),
|
|
139
149
|
)) as IVersionedValueWithEpoch;
|
|
140
|
-
// Version mismatch between what the runtime expects and what it
|
|
150
|
+
// Version mismatch between what the runtime expects and what it received.
|
|
141
151
|
// The cached value should not be used
|
|
142
152
|
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain -- using ?. could change behavior
|
|
143
153
|
if (value === undefined || value.version !== persistedCacheValueVersion) {
|
|
@@ -161,7 +171,7 @@ export class EpochTracker implements IPersistedFileCache {
|
|
|
161
171
|
cacheTime === undefined ||
|
|
162
172
|
currentTime - cacheTime >= this.snapshotCacheExpiryTimeoutMs
|
|
163
173
|
) {
|
|
164
|
-
this.
|
|
174
|
+
this.loggerInternal.sendTelemetryEvent({
|
|
165
175
|
eventName: "odspVersionsCacheExpired",
|
|
166
176
|
duration: currentTime - cacheTime,
|
|
167
177
|
maxCacheAgeMs: this.snapshotCacheExpiryTimeoutMs,
|
|
@@ -172,7 +182,10 @@ export class EpochTracker implements IPersistedFileCache {
|
|
|
172
182
|
}
|
|
173
183
|
return value.value;
|
|
174
184
|
} catch (error) {
|
|
175
|
-
this.
|
|
185
|
+
this.loggerInternal.sendErrorEvent(
|
|
186
|
+
{ eventName: "cacheFetchError", type: entry.type },
|
|
187
|
+
error,
|
|
188
|
+
);
|
|
176
189
|
return undefined;
|
|
177
190
|
}
|
|
178
191
|
}
|
|
@@ -194,7 +207,10 @@ export class EpochTracker implements IPersistedFileCache {
|
|
|
194
207
|
fluidEpoch: this._fluidEpoch,
|
|
195
208
|
};
|
|
196
209
|
return this.cache.put(this.fileEntryFromEntry(entry), data).catch((error) => {
|
|
197
|
-
this.
|
|
210
|
+
this.loggerInternal.sendErrorEvent(
|
|
211
|
+
{ eventName: "cachePutError", type: entry.type },
|
|
212
|
+
error,
|
|
213
|
+
);
|
|
198
214
|
throw error;
|
|
199
215
|
});
|
|
200
216
|
}
|
|
@@ -203,7 +219,7 @@ export class EpochTracker implements IPersistedFileCache {
|
|
|
203
219
|
try {
|
|
204
220
|
return await this.cache.removeEntries(this.fileEntry);
|
|
205
221
|
} catch (error) {
|
|
206
|
-
this.
|
|
222
|
+
this.loggerInternal.sendErrorEvent({ eventName: "removeCacheEntries" }, error);
|
|
207
223
|
}
|
|
208
224
|
}
|
|
209
225
|
|
|
@@ -456,7 +472,10 @@ export class EpochTracker implements IPersistedFileCache {
|
|
|
456
472
|
fromCache,
|
|
457
473
|
fetchType,
|
|
458
474
|
});
|
|
459
|
-
this.
|
|
475
|
+
this.loggerInternal.sendErrorEvent(
|
|
476
|
+
{ eventName: "fileOverwrittenInStorage" },
|
|
477
|
+
epochError,
|
|
478
|
+
);
|
|
460
479
|
// If the epoch mismatches, then clear all entries for such file entry from cache.
|
|
461
480
|
await this.removeEntries();
|
|
462
481
|
throw epochError;
|
|
@@ -502,10 +521,10 @@ export class EpochTrackerWithRedemption extends EpochTracker {
|
|
|
502
521
|
constructor(
|
|
503
522
|
protected readonly cache: IPersistedCache,
|
|
504
523
|
protected readonly fileEntry: IFileEntry,
|
|
505
|
-
|
|
524
|
+
logger: TelemetryLoggerExt,
|
|
506
525
|
protected readonly clientIsSummarizer?: boolean,
|
|
507
526
|
) {
|
|
508
|
-
super(cache, fileEntry, logger, clientIsSummarizer);
|
|
527
|
+
super(cache, fileEntry, toITelemetryLoggerExt(logger), clientIsSummarizer);
|
|
509
528
|
// Handles the rejected promise within treesLatestDeferral.
|
|
510
529
|
this.treesLatestDeferral.promise.catch(() => {});
|
|
511
530
|
}
|
|
@@ -635,7 +654,7 @@ export function createOdspCacheAndTracker(
|
|
|
635
654
|
persistedCacheArg: IPersistedCache,
|
|
636
655
|
nonpersistentCache: INonPersistentCache,
|
|
637
656
|
fileEntry: IFileEntry,
|
|
638
|
-
logger:
|
|
657
|
+
logger: TelemetryLoggerExt,
|
|
639
658
|
clientIsSummarizer?: boolean,
|
|
640
659
|
): ICacheAndTracker {
|
|
641
660
|
const epochTracker = new EpochTrackerWithRedemption(
|
package/src/fetchSnapshot.ts
CHANGED
|
@@ -6,7 +6,12 @@
|
|
|
6
6
|
import { fromUtf8ToBase64 } from "@fluid-internal/client-utils";
|
|
7
7
|
import { assert } from "@fluidframework/core-utils/internal";
|
|
8
8
|
import { getW3CData } from "@fluidframework/driver-base/internal";
|
|
9
|
-
import
|
|
9
|
+
import {
|
|
10
|
+
DriverErrorTypes,
|
|
11
|
+
type ILocationRedirectionError,
|
|
12
|
+
type ISnapshot,
|
|
13
|
+
type ISnapshotTree,
|
|
14
|
+
} from "@fluidframework/driver-definitions/internal";
|
|
10
15
|
import {
|
|
11
16
|
type DriverErrorTelemetryProps,
|
|
12
17
|
NonRetryableError,
|
|
@@ -23,8 +28,8 @@ import {
|
|
|
23
28
|
type InstrumentedStorageTokenFetcher,
|
|
24
29
|
OdspErrorTypes,
|
|
25
30
|
} from "@fluidframework/odsp-driver-definitions/internal";
|
|
31
|
+
import type { TelemetryLoggerExt } from "@fluidframework/telemetry-utils/internal";
|
|
26
32
|
import {
|
|
27
|
-
type ITelemetryLoggerExt,
|
|
28
33
|
PerformanceEvent,
|
|
29
34
|
isFluidError,
|
|
30
35
|
wrapError,
|
|
@@ -54,6 +59,7 @@ import {
|
|
|
54
59
|
fetchAndParseAsJSONHelper,
|
|
55
60
|
fetchHelper,
|
|
56
61
|
getWithRetryForTokenRefresh,
|
|
62
|
+
getOdspResolvedUrl,
|
|
57
63
|
getWithRetryForTokenRefreshRepeat,
|
|
58
64
|
isSnapshotFetchForLoadingGroup,
|
|
59
65
|
measure,
|
|
@@ -88,7 +94,7 @@ export async function fetchSnapshot(
|
|
|
88
94
|
versionId: string,
|
|
89
95
|
fetchFullSnapshot: boolean,
|
|
90
96
|
forceAccessTokenViaAuthorizationHeader: boolean,
|
|
91
|
-
logger:
|
|
97
|
+
logger: TelemetryLoggerExt,
|
|
92
98
|
snapshotDownloader: (url: string) => Promise<IOdspResponse<unknown>>,
|
|
93
99
|
): Promise<ISnapshot> {
|
|
94
100
|
const path = `/trees/${versionId}`;
|
|
@@ -115,7 +121,7 @@ export async function fetchSnapshotWithRedeem(
|
|
|
115
121
|
storageTokenFetcher: InstrumentedStorageTokenFetcher,
|
|
116
122
|
snapshotOptions: ISnapshotOptions | undefined,
|
|
117
123
|
forceAccessTokenViaAuthorizationHeader: boolean,
|
|
118
|
-
logger:
|
|
124
|
+
logger: TelemetryLoggerExt,
|
|
119
125
|
snapshotDownloader: (
|
|
120
126
|
finalOdspResolvedUrl: IOdspResolvedUrl,
|
|
121
127
|
getAuthHeader: InstrumentedStorageTokenFetcher,
|
|
@@ -183,6 +189,31 @@ export async function fetchSnapshotWithRedeem(
|
|
|
183
189
|
putInCache,
|
|
184
190
|
loadingGroupIds,
|
|
185
191
|
);
|
|
192
|
+
} else if (
|
|
193
|
+
isLocationRedirectionError(error) &&
|
|
194
|
+
odspResolvedUrl.shareLinkInfo?.sharingLinkToRedeem !== undefined
|
|
195
|
+
) {
|
|
196
|
+
try {
|
|
197
|
+
// The redirect itself is handled earlier, but we need to redeem the sharing link
|
|
198
|
+
// now against the redirected URL rather than waiting until the error reaches the
|
|
199
|
+
// resolveWithLocationRedirectionHandling handler as it will call getAbsoluteURL
|
|
200
|
+
// and would fail due to permission issues since it will not attempt to redeem.
|
|
201
|
+
logger.sendTelemetryEvent(
|
|
202
|
+
{
|
|
203
|
+
eventName: "RedirectRedeemFallback",
|
|
204
|
+
errorType: error.errorType,
|
|
205
|
+
},
|
|
206
|
+
error,
|
|
207
|
+
);
|
|
208
|
+
const redirectedResolvedUrl: IOdspResolvedUrl = {
|
|
209
|
+
...getOdspResolvedUrl(error.redirectUrl),
|
|
210
|
+
shareLinkInfo: odspResolvedUrl.shareLinkInfo,
|
|
211
|
+
};
|
|
212
|
+
await redeemSharingLink(redirectedResolvedUrl, storageTokenFetcher, logger);
|
|
213
|
+
} catch (redeemError) {
|
|
214
|
+
logger.sendErrorEvent({ eventName: "RedirectRedeemFallbackError" }, redeemError);
|
|
215
|
+
}
|
|
216
|
+
throw error;
|
|
186
217
|
} else {
|
|
187
218
|
throw error;
|
|
188
219
|
}
|
|
@@ -208,7 +239,7 @@ export async function fetchSnapshotWithRedeem(
|
|
|
208
239
|
async function redeemSharingLink(
|
|
209
240
|
odspResolvedUrl: IOdspResolvedUrl,
|
|
210
241
|
getAuthHeader: InstrumentedStorageTokenFetcher,
|
|
211
|
-
logger:
|
|
242
|
+
logger: TelemetryLoggerExt,
|
|
212
243
|
): Promise<void> {
|
|
213
244
|
await PerformanceEvent.timedExecAsync(
|
|
214
245
|
logger,
|
|
@@ -275,7 +306,7 @@ async function fetchLatestSnapshotCore(
|
|
|
275
306
|
odspResolvedUrl: IOdspResolvedUrl,
|
|
276
307
|
getAuthHeader: InstrumentedStorageTokenFetcher,
|
|
277
308
|
snapshotOptions: ISnapshotOptions | undefined,
|
|
278
|
-
logger:
|
|
309
|
+
logger: TelemetryLoggerExt,
|
|
279
310
|
snapshotDownloader: (
|
|
280
311
|
finalOdspResolvedUrl: IOdspResolvedUrl,
|
|
281
312
|
getAuthHeader: InstrumentedStorageTokenFetcher,
|
|
@@ -682,15 +713,18 @@ function getTreeStatsCore(snapshotTree: ISnapshotTree, stats: ITreeStats): void
|
|
|
682
713
|
/**
|
|
683
714
|
* This function fetches the snapshot and parse it according to what is mentioned in response headers.
|
|
684
715
|
* @param odspResolvedUrl - resolved odsp url.
|
|
685
|
-
* @param
|
|
686
|
-
* @param
|
|
716
|
+
* @param getAuthHeader - function to fetch the auth header for the network request.
|
|
717
|
+
* @param tokenFetchOptions - options for fetching the token.
|
|
687
718
|
* @param loadingGroupIds - loadingGroupIds for which snapshot needs to be downloaded. Note:
|
|
688
719
|
* 1.) If undefined, then legacy trees latest call will be used where no groupId query param would be specified.
|
|
689
720
|
* 2.) If [] is passed, then snapshot with all ungrouped data will be fetched.
|
|
690
721
|
* 3.) If any groupId is specified like ["g1"], then snapshot for g1 group will be fetched.
|
|
722
|
+
* @param snapshotOptions - Options used to specify how and what to fetch in the snapshot.
|
|
723
|
+
* @param logger - logger for sending telemetry events.
|
|
691
724
|
* @param snapshotFormatFetchType - Snapshot format to fetch.
|
|
692
725
|
* @param controller - abort controller if caller needs to abort the network call.
|
|
693
726
|
* @param epochTracker - epoch tracker used to add/validate epoch in the network call.
|
|
727
|
+
* @param scenarioName - scenario name for telemetry.
|
|
694
728
|
* @returns fetched snapshot.
|
|
695
729
|
*/
|
|
696
730
|
export const downloadSnapshot = mockify(
|
|
@@ -700,6 +734,7 @@ export const downloadSnapshot = mockify(
|
|
|
700
734
|
tokenFetchOptions: TokenFetchOptionsEx,
|
|
701
735
|
loadingGroupIds: string[] | undefined,
|
|
702
736
|
snapshotOptions: ISnapshotOptions | undefined,
|
|
737
|
+
logger?: TelemetryLoggerExt,
|
|
703
738
|
snapshotFormatFetchType?: SnapshotFormatSupportType,
|
|
704
739
|
controller?: AbortController,
|
|
705
740
|
epochTracker?: EpochTracker,
|
|
@@ -756,6 +791,7 @@ export const downloadSnapshot = mockify(
|
|
|
756
791
|
"downloadSnapshot",
|
|
757
792
|
);
|
|
758
793
|
assert(authHeader !== null, 0x1e5 /* "Storage token should not be null" */);
|
|
794
|
+
logger?.sendTelemetryEvent({ eventName: "SnapshotAuthHeaderObtained" });
|
|
759
795
|
const { body, headers } = getFormBodyAndHeaders(odspResolvedUrl, authHeader, header);
|
|
760
796
|
const fetchOptions = {
|
|
761
797
|
body,
|
|
@@ -782,6 +818,7 @@ export const downloadSnapshot = mockify(
|
|
|
782
818
|
true,
|
|
783
819
|
scenarioName,
|
|
784
820
|
) ?? fetchHelper(url, fetchOptions));
|
|
821
|
+
logger?.sendTelemetryEvent({ eventName: "SnapshotFetchResponseReceived" });
|
|
785
822
|
|
|
786
823
|
return {
|
|
787
824
|
odspResponse,
|
|
@@ -791,6 +828,14 @@ export const downloadSnapshot = mockify(
|
|
|
791
828
|
},
|
|
792
829
|
);
|
|
793
830
|
|
|
831
|
+
function isLocationRedirectionError(error: unknown): error is ILocationRedirectionError {
|
|
832
|
+
return (
|
|
833
|
+
typeof error === "object" &&
|
|
834
|
+
error !== null &&
|
|
835
|
+
(error as Partial<IOdspError>).errorType === DriverErrorTypes.locationRedirection
|
|
836
|
+
);
|
|
837
|
+
}
|
|
838
|
+
|
|
794
839
|
function isRedeemSharingLinkError(
|
|
795
840
|
odspResolvedUrl: IOdspResolvedUrl,
|
|
796
841
|
error: Partial<IOdspError>,
|
package/src/packageVersion.ts
CHANGED
|
@@ -110,6 +110,7 @@ export async function prefetchLatestSnapshot(
|
|
|
110
110
|
tokenFetchOptions,
|
|
111
111
|
loadingGroupId,
|
|
112
112
|
snapshotOptions,
|
|
113
|
+
odspLogger,
|
|
113
114
|
undefined,
|
|
114
115
|
controller,
|
|
115
116
|
);
|
|
@@ -174,7 +175,7 @@ export async function prefetchLatestSnapshot(
|
|
|
174
175
|
}, 5000);
|
|
175
176
|
})
|
|
176
177
|
.catch((error) => {
|
|
177
|
-
// Remove it from the non persistent cache if an error
|
|
178
|
+
// Remove it from the non persistent cache if an error occurred.
|
|
178
179
|
snapshotNonPersistentCache?.remove(nonPersistentCacheKey);
|
|
179
180
|
snapshotContentsWithEpochP.reject(error);
|
|
180
181
|
throw error;
|