@fluidframework/odsp-driver 3.1.0 → 3.3.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 +8 -0
- package/dist/odspVersionManager/odspVersionManager.d.ts +8 -0
- package/dist/odspVersionManager/odspVersionManager.d.ts.map +1 -1
- package/dist/odspVersionManager/odspVersionManager.js +19 -5
- package/dist/odspVersionManager/odspVersionManager.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/pointInTimeDriver/createPointInTimeDocumentService.d.ts.map +1 -1
- package/dist/pointInTimeDriver/createPointInTimeDocumentService.js +47 -5
- package/dist/pointInTimeDriver/createPointInTimeDocumentService.js.map +1 -1
- package/dist/pointInTimeDriver/odspPointInTimeDocumentService.d.ts.map +1 -1
- package/dist/pointInTimeDriver/odspPointInTimeDocumentService.js +40 -1
- package/dist/pointInTimeDriver/odspPointInTimeDocumentService.js.map +1 -1
- package/lib/odspVersionManager/odspVersionManager.d.ts +8 -0
- package/lib/odspVersionManager/odspVersionManager.d.ts.map +1 -1
- package/lib/odspVersionManager/odspVersionManager.js +19 -5
- package/lib/odspVersionManager/odspVersionManager.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/pointInTimeDriver/createPointInTimeDocumentService.d.ts.map +1 -1
- package/lib/pointInTimeDriver/createPointInTimeDocumentService.js +48 -6
- package/lib/pointInTimeDriver/createPointInTimeDocumentService.js.map +1 -1
- package/lib/pointInTimeDriver/odspPointInTimeDocumentService.d.ts.map +1 -1
- package/lib/pointInTimeDriver/odspPointInTimeDocumentService.js +40 -1
- package/lib/pointInTimeDriver/odspPointInTimeDocumentService.js.map +1 -1
- package/package.json +13 -17
- package/src/odspVersionManager/odspVersionManager.ts +27 -7
- package/src/packageVersion.ts +1 -1
- package/src/pointInTimeDriver/createPointInTimeDocumentService.ts +59 -8
- package/src/pointInTimeDriver/odspPointInTimeDocumentService.ts +47 -8
package/lib/packageVersion.d.ts
CHANGED
package/lib/packageVersion.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,6BAA6B,CAAC;AACrD,MAAM,CAAC,MAAM,UAAU,GAAG,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 = \"3.
|
|
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 = \"3.3.0\";\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createPointInTimeDocumentService.d.ts","sourceRoot":"","sources":["../../src/pointInTimeDriver/createPointInTimeDocumentService.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"createPointInTimeDocumentService.d.ts","sourceRoot":"","sources":["../../src/pointInTimeDriver/createPointInTimeDocumentService.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EACX,gBAAgB,EAChB,YAAY,EACZ,MAAM,6CAA6C,CAAC;AAErD,OAAO,KAAK,EACX,gBAAgB,EAEhB,MAAM,kDAAkD,CAAC;AAC1D,OAAO,EAGN,KAAK,kBAAkB,EACvB,MAAM,0CAA0C,CAAC;AAElD,OAAO,EAA6B,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGlF,OAAO,KAAK,EAAE,kDAAkD,EAAE,MAAM,sCAAsC,CAAC;AAM/G,OAAO,EAEN,KAAK,mBAAmB,EAExB,MAAM,6CAA6C,CAAC;AAErD,OAAO,EAAE,8BAA8B,EAAE,MAAM,qCAAqC,CAAC;AAErF,UAAU,uCAAuC;IAChD,QAAQ,CAAC,oBAAoB,CAAC,EAAE,CAC/B,eAAe,EAAE,gBAAgB,EACjC,MAAM,EAAE,kBAAkB,EAC1B,YAAY,EAAE,YAAY,KACtB,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACxD,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAC7B,WAAW,EAAE,YAAY,EACzB,WAAW,EAAE,MAAM,KACf,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CAC1C;AAiDD;;;;;;;;;;GAUG;AACH,wBAAsB,gCAAgC,CACrD,KAAK,EAAE,kDAAkD,GACvD,OAAO,CAAC,gBAAgB,CAAC,CAE3B;AAED;;;GAGG;AACH,wBAAsB,oCAAoC,CACzD,EACC,WAAW,EACX,oBAAoB,EACpB,MAAM,EACN,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,cAAc,EACd,qBAAqB,GACrB,EAAE,kDAAkD,EACrD,YAAY,GAAE,uCAA4C,GACxD,OAAO,CAAC,8BAA8B,CAAC,CAqHzC"}
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
|
+
import { performanceNow } from "@fluid-internal/client-utils";
|
|
5
6
|
import { UsageError } from "@fluidframework/driver-utils/internal";
|
|
6
|
-
import { createChildLogger, } from "@fluidframework/telemetry-utils/internal";
|
|
7
|
+
import { createChildLogger, isILoggingError, } from "@fluidframework/telemetry-utils/internal";
|
|
7
8
|
import { createOdspCacheAndTracker } from "../epochTracker.js";
|
|
8
9
|
import { NonPersistentCache } from "../odspCache.js";
|
|
9
10
|
import { OdspDriverUrlResolver } from "../odspDriverUrlResolver.js";
|
|
@@ -71,15 +72,54 @@ export async function createPointInTimeDocumentServiceCore({ resolvedUrl, target
|
|
|
71
72
|
docId: odspResolvedUrl.hashedDocumentId,
|
|
72
73
|
fileVersion: odspResolvedUrl.fileVersion,
|
|
73
74
|
}, extLogger, clientIsSummarizer, requestHeaders);
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
const baseSelectionStartTime = performanceNow();
|
|
76
|
+
let baseResult;
|
|
77
|
+
try {
|
|
78
|
+
const versionManager = await (dependencies.createVersionManager ??
|
|
79
|
+
(async (url, versionLogger, epochTracker) => createVersionManager(url, versionLogger, epochTracker, getStorageToken, requestHeaders)))(odspResolvedUrl, extLogger, cacheAndTracker.epochTracker);
|
|
80
|
+
baseResult = await versionManager.findBaseForSeq(targetSequenceNumber);
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
extLogger.sendErrorEvent({
|
|
84
|
+
eventName: "VersionMarkBaseVersionSelection",
|
|
85
|
+
outcome: "failed",
|
|
86
|
+
duration: performanceNow() - baseSelectionStartTime,
|
|
87
|
+
targetSequenceNumber,
|
|
88
|
+
errorType: error?.errorType,
|
|
89
|
+
availabilityOutcome: isILoggingError(error)
|
|
90
|
+
? error.getTelemetryProperties().versionMarkAvailabilityOutcome
|
|
91
|
+
: undefined,
|
|
92
|
+
}, error);
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
77
95
|
if (baseResult.kind === "noBaseVersion") {
|
|
78
96
|
const oldestResolvedSequenceDetail = baseResult.oldestResolvedSeq === undefined
|
|
79
97
|
? ""
|
|
80
98
|
: ` The oldest resolved file version is at sequence number ${baseResult.oldestResolvedSeq}.`;
|
|
81
|
-
|
|
99
|
+
const error = new UsageError(`No ODSP file version is available at or before sequence number ${targetSequenceNumber}.${oldestResolvedSequenceDetail}`);
|
|
100
|
+
error.addTelemetryProperties({
|
|
101
|
+
versionMarkAvailabilityOutcome: "baseVersionMissing",
|
|
102
|
+
});
|
|
103
|
+
extLogger.sendErrorEvent({
|
|
104
|
+
eventName: "VersionMarkBaseVersionSelection",
|
|
105
|
+
outcome: "failed",
|
|
106
|
+
duration: performanceNow() - baseSelectionStartTime,
|
|
107
|
+
targetSequenceNumber,
|
|
108
|
+
availabilityOutcome: "baseVersionMissing",
|
|
109
|
+
oldestResolvedSequenceNumber: baseResult.oldestResolvedSeq,
|
|
110
|
+
versionsProbed: baseResult.versionsProbed,
|
|
111
|
+
sequenceNumberFetchCount: baseResult.sequenceNumberFetchCount,
|
|
112
|
+
errorType: error.errorType,
|
|
113
|
+
});
|
|
114
|
+
throw error;
|
|
82
115
|
}
|
|
116
|
+
extLogger.sendPerformanceEvent({
|
|
117
|
+
eventName: "VersionMarkBaseVersionSelection",
|
|
118
|
+
outcome: "succeeded",
|
|
119
|
+
duration: performanceNow() - baseSelectionStartTime,
|
|
120
|
+
versionsProbed: baseResult.versionsProbed,
|
|
121
|
+
sequenceNumberFetchCount: baseResult.sequenceNumberFetchCount,
|
|
122
|
+
});
|
|
83
123
|
const recoverableResolvedUrl = await (dependencies.resolveFileVersion ?? resolveFileVersion)(resolvedUrl, baseResult.base.versionId);
|
|
84
124
|
const recoverableDocumentService = await createDocumentService(recoverableResolvedUrl, odspLogger, cacheAndTracker, clientIsSummarizer);
|
|
85
125
|
let liveDocumentService;
|
|
@@ -91,7 +131,9 @@ export async function createPointInTimeDocumentServiceCore({ resolvedUrl, target
|
|
|
91
131
|
recoverableDocumentService.dispose();
|
|
92
132
|
}
|
|
93
133
|
catch (disposeError) {
|
|
94
|
-
extLogger.sendErrorEvent({
|
|
134
|
+
extLogger.sendErrorEvent({
|
|
135
|
+
eventName: "VersionMarkRecoverableDocumentServiceDisposeError",
|
|
136
|
+
}, disposeError);
|
|
95
137
|
}
|
|
96
138
|
throw error;
|
|
97
139
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createPointInTimeDocumentService.js","sourceRoot":"","sources":["../../src/pointInTimeDriver/createPointInTimeDocumentService.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 type {\n\tIDocumentService,\n\tIResolvedUrl,\n} from \"@fluidframework/driver-definitions/internal\";\nimport { UsageError } from \"@fluidframework/driver-utils/internal\";\nimport type {\n\tIOdspResolvedUrl,\n\tIOdspUrlParts,\n} from \"@fluidframework/odsp-driver-definitions/internal\";\nimport {\n\tcreateChildLogger,\n\ttype TelemetryLoggerExt,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport { createOdspCacheAndTracker, type EpochTracker } from \"../epochTracker.js\";\nimport { NonPersistentCache } from \"../odspCache.js\";\nimport { OdspDriverUrlResolver } from \"../odspDriverUrlResolver.js\";\nimport type { IOdspPointInTimeDocumentServiceImplementationProps } from \"../odspDocumentServiceFactoryCore.js\";\nimport {\n\tcreateOdspLogger,\n\tgetOdspResolvedUrl,\n\ttoInstrumentedOdspStorageTokenFetcher,\n} from \"../odspUtils.js\";\nimport {\n\tcreateOdspVersionManager,\n\ttype IOdspVersionManager,\n\t// eslint-disable-next-line import-x/no-internal-modules -- the feature implementation owns version selection\n} from \"../odspVersionManager/odspVersionManager.js\";\n\nimport { OdspPointInTimeDocumentService } from \"./odspPointInTimeDocumentService.js\";\n\ninterface IPointInTimeDocumentServiceDependencies {\n\treadonly createVersionManager?: (\n\t\todspResolvedUrl: IOdspResolvedUrl,\n\t\tlogger: TelemetryLoggerExt,\n\t\tepochTracker: EpochTracker,\n\t) => IOdspVersionManager | Promise<IOdspVersionManager>;\n\treadonly resolveFileVersion?: (\n\t\tresolvedUrl: IResolvedUrl,\n\t\tfileVersion: string,\n\t) => IResolvedUrl | Promise<IResolvedUrl>;\n}\n\nasync function createVersionManager(\n\todspResolvedUrl: IOdspResolvedUrl,\n\tlogger: TelemetryLoggerExt,\n\tepochTracker: EpochTracker,\n\tgetStorageToken: IOdspPointInTimeDocumentServiceImplementationProps[\"getStorageToken\"],\n\trequestHeaders?: Readonly<Record<string, string>>,\n): Promise<IOdspVersionManager> {\n\tconst urlParts: IOdspUrlParts = {\n\t\tsiteUrl: odspResolvedUrl.siteUrl,\n\t\tdriveId: odspResolvedUrl.driveId,\n\t\titemId: odspResolvedUrl.itemId,\n\t};\n\tconst getAuthHeader = toInstrumentedOdspStorageTokenFetcher(\n\t\tlogger,\n\t\turlParts,\n\t\tgetStorageToken,\n\t);\n\treturn createOdspVersionManager({\n\t\turlParts,\n\t\tgetAuthHeader,\n\t\tepochTracker,\n\t\tlogger,\n\t\trequestHeaders,\n\t});\n}\n\nasync function resolveFileVersion(\n\tresolvedUrl: IResolvedUrl,\n\tfileVersion: string,\n): Promise<IResolvedUrl> {\n\tconst odspResolvedUrl = getOdspResolvedUrl(resolvedUrl);\n\tconst query = new URLSearchParams({\n\t\tdriveId: odspResolvedUrl.driveId,\n\t\titemId: odspResolvedUrl.itemId,\n\t\tfileVersion,\n\t});\n\tif (odspResolvedUrl.dataStorePath !== undefined) {\n\t\tquery.set(\"path\", odspResolvedUrl.dataStorePath);\n\t}\n\tif (odspResolvedUrl.codeHint?.containerPackageName !== undefined) {\n\t\tquery.set(\"containerPackageName\", odspResolvedUrl.codeHint.containerPackageName);\n\t}\n\treturn new OdspDriverUrlResolver().resolve({\n\t\turl: `${odspResolvedUrl.siteUrl}?${query.toString()}`,\n\t});\n}\n\n/**\n * ODSP's point-in-time document service implementation.\n *\n * @remarks Import this function from the dedicated point-in-time entrypoint and inject it through\n * {@link IOdspDocumentServiceFactoryOptions.pointInTimeDocumentServiceImplementation}.\n *\n * @param props - ODSP service dependencies and point-in-time load parameters supplied by the factory.\n * @returns A read-only ODSP document service materialized at the requested sequence number.\n *\n * @legacy @beta\n */\nexport async function createPointInTimeDocumentService(\n\tprops: IOdspPointInTimeDocumentServiceImplementationProps,\n): Promise<IDocumentService> {\n\treturn createPointInTimeDocumentServiceCore(props);\n}\n\n/**\n * Creates the point-in-time service with replaceable dependencies for testing.\n * @internal\n */\nexport async function createPointInTimeDocumentServiceCore(\n\t{\n\t\tresolvedUrl,\n\t\ttargetSequenceNumber,\n\t\tlogger,\n\t\tclientIsSummarizer,\n\t\tpersistedCache,\n\t\tgetStorageToken,\n\t\trequestHeaders,\n\t\tcreateDocumentService,\n\t}: IOdspPointInTimeDocumentServiceImplementationProps,\n\tdependencies: IPointInTimeDocumentServiceDependencies = {},\n): Promise<OdspPointInTimeDocumentService> {\n\tconst odspLogger = createOdspLogger(logger);\n\tconst extLogger = createChildLogger({ logger: odspLogger });\n\tconst odspResolvedUrl = getOdspResolvedUrl(resolvedUrl);\n\tconst cacheAndTracker = createOdspCacheAndTracker(\n\t\tpersistedCache,\n\t\tnew NonPersistentCache(),\n\t\t{\n\t\t\tresolvedUrl: odspResolvedUrl,\n\t\t\tdocId: odspResolvedUrl.hashedDocumentId,\n\t\t\tfileVersion: odspResolvedUrl.fileVersion,\n\t\t},\n\t\textLogger,\n\t\tclientIsSummarizer,\n\t\trequestHeaders,\n\t);\n\n\tconst versionManager = await (\n\t\tdependencies.createVersionManager ??\n\t\t(async (url, versionLogger, epochTracker) =>\n\t\t\tcreateVersionManager(url, versionLogger, epochTracker, getStorageToken, requestHeaders))\n\t)(odspResolvedUrl, extLogger, cacheAndTracker.epochTracker);\n\tconst baseResult = await versionManager.findBaseForSeq(targetSequenceNumber);\n\tif (baseResult.kind === \"noBaseVersion\") {\n\t\tconst oldestResolvedSequenceDetail =\n\t\t\tbaseResult.oldestResolvedSeq === undefined\n\t\t\t\t? \"\"\n\t\t\t\t: ` The oldest resolved file version is at sequence number ${baseResult.oldestResolvedSeq}.`;\n\t\tthrow new UsageError(\n\t\t\t`No ODSP file version is available at or before sequence number ${targetSequenceNumber}.${oldestResolvedSequenceDetail}`,\n\t\t);\n\t}\n\n\tconst recoverableResolvedUrl = await (dependencies.resolveFileVersion ?? resolveFileVersion)(\n\t\tresolvedUrl,\n\t\tbaseResult.base.versionId,\n\t);\n\tconst recoverableDocumentService = await createDocumentService(\n\t\trecoverableResolvedUrl,\n\t\todspLogger,\n\t\tcacheAndTracker,\n\t\tclientIsSummarizer,\n\t);\n\tlet liveDocumentService: IDocumentService;\n\ttry {\n\t\tliveDocumentService = await createDocumentService(\n\t\t\tresolvedUrl,\n\t\t\todspLogger,\n\t\t\tcacheAndTracker,\n\t\t\tclientIsSummarizer,\n\t\t);\n\t} catch (error) {\n\t\ttry {\n\t\t\trecoverableDocumentService.dispose();\n\t\t} catch (disposeError) {\n\t\t\textLogger.sendErrorEvent(\n\t\t\t\t{ eventName: \"PointInTimeRecoverableDocumentServiceDisposeError\" },\n\t\t\t\tdisposeError,\n\t\t\t);\n\t\t}\n\t\tthrow error;\n\t}\n\treturn new OdspPointInTimeDocumentService(\n\t\trecoverableResolvedUrl,\n\t\trecoverableDocumentService,\n\t\tliveDocumentService,\n\t\ttargetSequenceNumber,\n\t);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"createPointInTimeDocumentService.js","sourceRoot":"","sources":["../../src/pointInTimeDriver/createPointInTimeDocumentService.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 {\n\tIDocumentService,\n\tIResolvedUrl,\n} from \"@fluidframework/driver-definitions/internal\";\nimport { UsageError } from \"@fluidframework/driver-utils/internal\";\nimport type {\n\tIOdspResolvedUrl,\n\tIOdspUrlParts,\n} from \"@fluidframework/odsp-driver-definitions/internal\";\nimport {\n\tcreateChildLogger,\n\tisILoggingError,\n\ttype TelemetryLoggerExt,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport { createOdspCacheAndTracker, type EpochTracker } from \"../epochTracker.js\";\nimport { NonPersistentCache } from \"../odspCache.js\";\nimport { OdspDriverUrlResolver } from \"../odspDriverUrlResolver.js\";\nimport type { IOdspPointInTimeDocumentServiceImplementationProps } from \"../odspDocumentServiceFactoryCore.js\";\nimport {\n\tcreateOdspLogger,\n\tgetOdspResolvedUrl,\n\ttoInstrumentedOdspStorageTokenFetcher,\n} from \"../odspUtils.js\";\nimport {\n\tcreateOdspVersionManager,\n\ttype IOdspVersionManager,\n\t// eslint-disable-next-line import-x/no-internal-modules -- the feature implementation owns version selection\n} from \"../odspVersionManager/odspVersionManager.js\";\n\nimport { OdspPointInTimeDocumentService } from \"./odspPointInTimeDocumentService.js\";\n\ninterface IPointInTimeDocumentServiceDependencies {\n\treadonly createVersionManager?: (\n\t\todspResolvedUrl: IOdspResolvedUrl,\n\t\tlogger: TelemetryLoggerExt,\n\t\tepochTracker: EpochTracker,\n\t) => IOdspVersionManager | Promise<IOdspVersionManager>;\n\treadonly resolveFileVersion?: (\n\t\tresolvedUrl: IResolvedUrl,\n\t\tfileVersion: string,\n\t) => IResolvedUrl | Promise<IResolvedUrl>;\n}\n\nasync function createVersionManager(\n\todspResolvedUrl: IOdspResolvedUrl,\n\tlogger: TelemetryLoggerExt,\n\tepochTracker: EpochTracker,\n\tgetStorageToken: IOdspPointInTimeDocumentServiceImplementationProps[\"getStorageToken\"],\n\trequestHeaders?: Readonly<Record<string, string>>,\n): Promise<IOdspVersionManager> {\n\tconst urlParts: IOdspUrlParts = {\n\t\tsiteUrl: odspResolvedUrl.siteUrl,\n\t\tdriveId: odspResolvedUrl.driveId,\n\t\titemId: odspResolvedUrl.itemId,\n\t};\n\tconst getAuthHeader = toInstrumentedOdspStorageTokenFetcher(\n\t\tlogger,\n\t\turlParts,\n\t\tgetStorageToken,\n\t);\n\treturn createOdspVersionManager({\n\t\turlParts,\n\t\tgetAuthHeader,\n\t\tepochTracker,\n\t\tlogger,\n\t\trequestHeaders,\n\t});\n}\n\nasync function resolveFileVersion(\n\tresolvedUrl: IResolvedUrl,\n\tfileVersion: string,\n): Promise<IResolvedUrl> {\n\tconst odspResolvedUrl = getOdspResolvedUrl(resolvedUrl);\n\tconst query = new URLSearchParams({\n\t\tdriveId: odspResolvedUrl.driveId,\n\t\titemId: odspResolvedUrl.itemId,\n\t\tfileVersion,\n\t});\n\tif (odspResolvedUrl.dataStorePath !== undefined) {\n\t\tquery.set(\"path\", odspResolvedUrl.dataStorePath);\n\t}\n\tif (odspResolvedUrl.codeHint?.containerPackageName !== undefined) {\n\t\tquery.set(\"containerPackageName\", odspResolvedUrl.codeHint.containerPackageName);\n\t}\n\treturn new OdspDriverUrlResolver().resolve({\n\t\turl: `${odspResolvedUrl.siteUrl}?${query.toString()}`,\n\t});\n}\n\n/**\n * ODSP's point-in-time document service implementation.\n *\n * @remarks Import this function from the dedicated point-in-time entrypoint and inject it through\n * {@link IOdspDocumentServiceFactoryOptions.pointInTimeDocumentServiceImplementation}.\n *\n * @param props - ODSP service dependencies and point-in-time load parameters supplied by the factory.\n * @returns A read-only ODSP document service materialized at the requested sequence number.\n *\n * @legacy @beta\n */\nexport async function createPointInTimeDocumentService(\n\tprops: IOdspPointInTimeDocumentServiceImplementationProps,\n): Promise<IDocumentService> {\n\treturn createPointInTimeDocumentServiceCore(props);\n}\n\n/**\n * Creates the point-in-time service with replaceable dependencies for testing.\n * @internal\n */\nexport async function createPointInTimeDocumentServiceCore(\n\t{\n\t\tresolvedUrl,\n\t\ttargetSequenceNumber,\n\t\tlogger,\n\t\tclientIsSummarizer,\n\t\tpersistedCache,\n\t\tgetStorageToken,\n\t\trequestHeaders,\n\t\tcreateDocumentService,\n\t}: IOdspPointInTimeDocumentServiceImplementationProps,\n\tdependencies: IPointInTimeDocumentServiceDependencies = {},\n): Promise<OdspPointInTimeDocumentService> {\n\tconst odspLogger = createOdspLogger(logger);\n\tconst extLogger = createChildLogger({ logger: odspLogger });\n\tconst odspResolvedUrl = getOdspResolvedUrl(resolvedUrl);\n\tconst cacheAndTracker = createOdspCacheAndTracker(\n\t\tpersistedCache,\n\t\tnew NonPersistentCache(),\n\t\t{\n\t\t\tresolvedUrl: odspResolvedUrl,\n\t\t\tdocId: odspResolvedUrl.hashedDocumentId,\n\t\t\tfileVersion: odspResolvedUrl.fileVersion,\n\t\t},\n\t\textLogger,\n\t\tclientIsSummarizer,\n\t\trequestHeaders,\n\t);\n\n\tconst baseSelectionStartTime = performanceNow();\n\tlet baseResult: Awaited<ReturnType<IOdspVersionManager[\"findBaseForSeq\"]>>;\n\ttry {\n\t\tconst versionManager = await (\n\t\t\tdependencies.createVersionManager ??\n\t\t\t(async (url, versionLogger, epochTracker) =>\n\t\t\t\tcreateVersionManager(\n\t\t\t\t\turl,\n\t\t\t\t\tversionLogger,\n\t\t\t\t\tepochTracker,\n\t\t\t\t\tgetStorageToken,\n\t\t\t\t\trequestHeaders,\n\t\t\t\t))\n\t\t)(odspResolvedUrl, extLogger, cacheAndTracker.epochTracker);\n\t\tbaseResult = await versionManager.findBaseForSeq(targetSequenceNumber);\n\t} catch (error) {\n\t\textLogger.sendErrorEvent(\n\t\t\t{\n\t\t\t\teventName: \"VersionMarkBaseVersionSelection\",\n\t\t\t\toutcome: \"failed\",\n\t\t\t\tduration: performanceNow() - baseSelectionStartTime,\n\t\t\t\ttargetSequenceNumber,\n\t\t\t\terrorType: (error as Partial<{ errorType: string }> | undefined)?.errorType,\n\t\t\t\tavailabilityOutcome: isILoggingError(error)\n\t\t\t\t\t? error.getTelemetryProperties().versionMarkAvailabilityOutcome\n\t\t\t\t\t: undefined,\n\t\t\t},\n\t\t\terror,\n\t\t);\n\t\tthrow error;\n\t}\n\tif (baseResult.kind === \"noBaseVersion\") {\n\t\tconst oldestResolvedSequenceDetail =\n\t\t\tbaseResult.oldestResolvedSeq === undefined\n\t\t\t\t? \"\"\n\t\t\t\t: ` The oldest resolved file version is at sequence number ${baseResult.oldestResolvedSeq}.`;\n\t\tconst error = new UsageError(\n\t\t\t`No ODSP file version is available at or before sequence number ${targetSequenceNumber}.${oldestResolvedSequenceDetail}`,\n\t\t);\n\t\terror.addTelemetryProperties({\n\t\t\tversionMarkAvailabilityOutcome: \"baseVersionMissing\",\n\t\t});\n\t\textLogger.sendErrorEvent({\n\t\t\teventName: \"VersionMarkBaseVersionSelection\",\n\t\t\toutcome: \"failed\",\n\t\t\tduration: performanceNow() - baseSelectionStartTime,\n\t\t\ttargetSequenceNumber,\n\t\t\tavailabilityOutcome: \"baseVersionMissing\",\n\t\t\toldestResolvedSequenceNumber: baseResult.oldestResolvedSeq,\n\t\t\tversionsProbed: baseResult.versionsProbed,\n\t\t\tsequenceNumberFetchCount: baseResult.sequenceNumberFetchCount,\n\t\t\terrorType: error.errorType,\n\t\t});\n\t\tthrow error;\n\t}\n\textLogger.sendPerformanceEvent({\n\t\teventName: \"VersionMarkBaseVersionSelection\",\n\t\toutcome: \"succeeded\",\n\t\tduration: performanceNow() - baseSelectionStartTime,\n\t\tversionsProbed: baseResult.versionsProbed,\n\t\tsequenceNumberFetchCount: baseResult.sequenceNumberFetchCount,\n\t});\n\n\tconst recoverableResolvedUrl = await (dependencies.resolveFileVersion ?? resolveFileVersion)(\n\t\tresolvedUrl,\n\t\tbaseResult.base.versionId,\n\t);\n\tconst recoverableDocumentService = await createDocumentService(\n\t\trecoverableResolvedUrl,\n\t\todspLogger,\n\t\tcacheAndTracker,\n\t\tclientIsSummarizer,\n\t);\n\tlet liveDocumentService: IDocumentService;\n\ttry {\n\t\tliveDocumentService = await createDocumentService(\n\t\t\tresolvedUrl,\n\t\t\todspLogger,\n\t\t\tcacheAndTracker,\n\t\t\tclientIsSummarizer,\n\t\t);\n\t} catch (error) {\n\t\ttry {\n\t\t\trecoverableDocumentService.dispose();\n\t\t} catch (disposeError) {\n\t\t\textLogger.sendErrorEvent(\n\t\t\t\t{\n\t\t\t\t\teventName: \"VersionMarkRecoverableDocumentServiceDisposeError\",\n\t\t\t\t},\n\t\t\t\tdisposeError,\n\t\t\t);\n\t\t}\n\t\tthrow error;\n\t}\n\treturn new OdspPointInTimeDocumentService(\n\t\trecoverableResolvedUrl,\n\t\trecoverableDocumentService,\n\t\tliveDocumentService,\n\t\ttargetSequenceNumber,\n\t);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"odspPointInTimeDocumentService.d.ts","sourceRoot":"","sources":["../../src/pointInTimeDriver/odspPointInTimeDocumentService.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAEN,KAAK,OAAO,EACZ,KAAK,wBAAwB,EAC7B,KAAK,4BAA4B,EACjC,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,KAAK,YAAY,EAIjB,MAAM,6CAA6C,CAAC;
|
|
1
|
+
{"version":3,"file":"odspPointInTimeDocumentService.d.ts","sourceRoot":"","sources":["../../src/pointInTimeDriver/odspPointInTimeDocumentService.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAEN,KAAK,OAAO,EACZ,KAAK,wBAAwB,EAC7B,KAAK,4BAA4B,EACjC,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,KAAK,YAAY,EAIjB,MAAM,6CAA6C,CAAC;AA+CrD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,8BACZ,SAAQ,iBAAiB,CAAC,sBAAsB,CAChD,YAAW,gBAAgB;aAGV,WAAW,EAAE,YAAY;IAGzC,OAAO,CAAC,QAAQ,CAAC,0BAA0B;IAC3C,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IACpC,OAAO,CAAC,QAAQ,CAAC,oBAAoB;gBALrB,WAAW,EAAE,YAAY,EAGxB,0BAA0B,EAAE,gBAAgB,EAC5C,mBAAmB,EAAE,gBAAgB,EACrC,oBAAoB,EAAE,MAAM;IAQ9C,SAAgB,QAAQ,EAAE,wBAAwB,CAAyB;IAEpE,OAAO,IAAI,IAAI;IAMT,gBAAgB,IAAI,OAAO,CAAC,uBAAuB,CAAC;IAKpD,qBAAqB,IAAI,OAAO,CAAC,4BAA4B,CAAC;IAmC9D,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAStF,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAEpC;CACF"}
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
import { TypedEventEmitter } from "@fluid-internal/client-utils";
|
|
6
6
|
import { FetchSource, } from "@fluidframework/driver-definitions/internal";
|
|
7
7
|
import { DocumentStorageServiceProxy } from "@fluidframework/driver-utils/internal";
|
|
8
|
+
import { OdspErrorTypes } from "@fluidframework/odsp-driver-definitions/internal";
|
|
9
|
+
import { isFluidError } from "@fluidframework/telemetry-utils/internal";
|
|
8
10
|
/**
|
|
9
11
|
* Forces point-in-time snapshot reads to bypass caches while forwarding all other storage operations
|
|
10
12
|
* to the selected historical document service.
|
|
@@ -22,6 +24,21 @@ class PointInTimeDocumentStorageService extends DocumentStorageServiceProxy {
|
|
|
22
24
|
return super.getVersions(versionId, count, scenarioName, FetchSource.noCache);
|
|
23
25
|
}
|
|
24
26
|
}
|
|
27
|
+
function annotateMissingOps(error) {
|
|
28
|
+
if (!isFluidError(error)) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
// Attach a stable Version Mark availability classification only when the driver error
|
|
32
|
+
// confirms that the required historical ops are unavailable.
|
|
33
|
+
const historyIsUnavailable = error.errorType === OdspErrorTypes.cannotCatchUp ||
|
|
34
|
+
(error.errorType === OdspErrorTypes.genericNetworkError &&
|
|
35
|
+
error.getTelemetryProperties().opsFetchFailure === "tooManyRetries");
|
|
36
|
+
if (historyIsUnavailable) {
|
|
37
|
+
error.addTelemetryProperties({
|
|
38
|
+
versionMarkAvailabilityOutcome: "missingOps",
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
25
42
|
/**
|
|
26
43
|
* A read-only document service that materializes a document at a target sequence number by combining
|
|
27
44
|
* a recoverable snapshot with a bounded replay of live ops.
|
|
@@ -77,7 +94,29 @@ export class OdspPointInTimeDocumentService extends TypedEventEmitter {
|
|
|
77
94
|
// The exclusive upper bound needed to include the target op itself.
|
|
78
95
|
const boundedTo = this.targetSequenceNumber + 1;
|
|
79
96
|
return {
|
|
80
|
-
fetchMessages: (from, to, abortSignal, cachedOnly, fetchReason) =>
|
|
97
|
+
fetchMessages: (from, to, abortSignal, cachedOnly, fetchReason) => {
|
|
98
|
+
try {
|
|
99
|
+
const stream = liveDeltaStorage.fetchMessages(from, to === undefined ? boundedTo : Math.min(to, boundedTo), abortSignal, cachedOnly, fetchReason);
|
|
100
|
+
return {
|
|
101
|
+
read: async () => {
|
|
102
|
+
try {
|
|
103
|
+
return await stream.read();
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
// The bounded replay owns the conclusion that these specific failures mean
|
|
107
|
+
// the bridge to the target cannot be materialized. Preserve the driver's raw
|
|
108
|
+
// errorType while attaching the stable Version Mark availability outcome.
|
|
109
|
+
annotateMissingOps(error);
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
annotateMissingOps(error);
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
119
|
+
},
|
|
81
120
|
};
|
|
82
121
|
}
|
|
83
122
|
async connectToDeltaStream(_client) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"odspPointInTimeDocumentService.js","sourceRoot":"","sources":["../../src/pointInTimeDriver/odspPointInTimeDocumentService.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EACN,WAAW,GAYX,MAAM,6CAA6C,CAAC;AACrD,OAAO,EAAE,2BAA2B,EAAE,MAAM,uCAAuC,CAAC;AAEpF;;;GAGG;AACH,MAAM,iCAAkC,SAAQ,2BAA2B;IAC1D,KAAK,CAAC,WAAW,CAChC,oBAA4C;QAE5C,OAAO,KAAK,CAAC,WAAW,CAAC;YACxB,GAAG,oBAAoB;YACvB,WAAW,EAAE,WAAW,CAAC,OAAO;SAChC,CAAC,CAAC;IACJ,CAAC;IAEe,KAAK,CAAC,WAAW;IAChC,iHAAiH;IACjH,SAAwB,EACxB,KAAa,EACb,YAAqB;QAErB,OAAO,KAAK,CAAC,WAAW,CAAC,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IAC/E,CAAC;CACD;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,OAAO,8BACZ,SAAQ,iBAAyC;IAIhC;IAGC;IACA;IACA;IANlB,YACiB,WAAyB;IACzC,4FAA4F;IAC5F,sFAAsF;IACrE,0BAA4C,EAC5C,mBAAqC,EACrC,oBAA4B;QAE7C,KAAK,EAAE,CAAC;QAPQ,gBAAW,GAAX,WAAW,CAAc;QAGxB,+BAA0B,GAA1B,0BAA0B,CAAkB;QAC5C,wBAAmB,GAAnB,mBAAmB,CAAkB;QACrC,yBAAoB,GAApB,oBAAoB,CAAQ;QAG7C,IAAI,CAAC,mBAAmB,CAAC,EAAE,CAAC,gBAAgB,EAAE,IAAI,CAAC,qBAAqB,CAAC,CAAC;IAC3E,CAAC;IAED,+FAA+F;IAC/F,qEAAqE;IACrD,QAAQ,GAA6B,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;IAEpE,OAAO;QACb,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,gBAAgB,EAAE,IAAI,CAAC,qBAAqB,CAAC,CAAC;QAC3E,IAAI,CAAC,0BAA0B,CAAC,OAAO,EAAE,CAAC;QAC1C,IAAI,CAAC,mBAAmB,CAAC,OAAO,EAAE,CAAC;IACpC,CAAC;IAEM,KAAK,CAAC,gBAAgB;QAC5B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,0BAA0B,CAAC,gBAAgB,EAAE,CAAC;QACzE,OAAO,IAAI,iCAAiC,CAAC,OAAO,CAAC,CAAC;IACvD,CAAC;IAEM,KAAK,CAAC,qBAAqB;QACjC,MAAM,gBAAgB,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,qBAAqB,EAAE,CAAC;QAChF,oEAAoE;QACpE,MAAM,SAAS,GAAG,IAAI,CAAC,oBAAoB,GAAG,CAAC,CAAC;QAChD,OAAO;YACN,aAAa,EAAE,CAAC,IAAI,EAAE,EAAE,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,EAAE,CACjE,gBAAgB,CAAC,aAAa,CAC7B,IAAI,EACJ,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,CAAC,EACtD,WAAW,EACX,UAAU,EACV,WAAW,CACX;SACF,CAAC;IACH,CAAC;IAEM,KAAK,CAAC,oBAAoB,CAAC,OAAgB;QACjD,0FAA0F;QAC1F,yFAAyF;QACzF,8DAA8D;QAC9D,MAAM,IAAI,KAAK,CACd,2FAA2F,CAC3F,CAAC;IACH,CAAC;IAEgB,qBAAqB,GAAG,CAAC,QAAgC,EAAQ,EAAE;QACnF,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAC,CAAC;IACvC,CAAC,CAAC;CACF","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { TypedEventEmitter } from \"@fluid-internal/client-utils\";\nimport {\n\tFetchSource,\n\ttype IClient,\n\ttype IDocumentDeltaConnection,\n\ttype IDocumentDeltaStorageService,\n\ttype IDocumentService,\n\ttype IDocumentServiceEvents,\n\ttype IDocumentServicePolicies,\n\ttype IDocumentStorageService,\n\ttype IResolvedUrl,\n\ttype ISnapshot,\n\ttype ISnapshotFetchOptions,\n\ttype IVersion,\n} from \"@fluidframework/driver-definitions/internal\";\nimport { DocumentStorageServiceProxy } from \"@fluidframework/driver-utils/internal\";\n\n/**\n * Forces point-in-time snapshot reads to bypass caches while forwarding all other storage operations\n * to the selected historical document service.\n */\nclass PointInTimeDocumentStorageService extends DocumentStorageServiceProxy {\n\tpublic override async getSnapshot(\n\t\tsnapshotFetchOptions?: ISnapshotFetchOptions,\n\t): Promise<ISnapshot> {\n\t\treturn super.getSnapshot({\n\t\t\t...snapshotFetchOptions,\n\t\t\tfetchSource: FetchSource.noCache,\n\t\t});\n\t}\n\n\tpublic override async getVersions(\n\t\t// eslint-disable-next-line @rushstack/no-new-null -- IDocumentStorageService is a legacy API that requires null.\n\t\tversionId: string | null,\n\t\tcount: number,\n\t\tscenarioName?: string,\n\t): Promise<IVersion[]> {\n\t\treturn super.getVersions(versionId, count, scenarioName, FetchSource.noCache);\n\t}\n}\n\n/**\n * A read-only document service that materializes a document at a target sequence number by combining\n * a recoverable snapshot with a bounded replay of live ops.\n *\n * @remarks\n * Storage (the snapshot) is served from the closest file version at or before the target sequence\n * number. The ops needed to advance from that snapshot to the target are read from the live\n * document's delta storage, bounded so that no op past the target is ever fetched.\n *\n * The service advertises the {@link IDocumentServicePolicies.storageOnly} policy. This reuses the\n * loader's \"frozen\" load mechanism: the connection manager synthesizes a read-only frozen delta\n * stream instead of opening a live socket, and forces the container read-only. The delta manager\n * still catches up from the snapshot's sequence number through delta storage, which is exactly the\n * bounded replay we want. As a result no live delta-stream connection is ever established.\n *\n * Op availability is enforced by the delta storage stack itself: it validates that fetched batches\n * are contiguous from the requested start, keeps requesting until the bounded range is fully\n * delivered, and fails the fetch if the ops never materialize. So a stream that completes has\n * necessarily served the whole bridge, and no additional checks are needed here.\n *\n * @internal\n */\nexport class OdspPointInTimeDocumentService\n\textends TypedEventEmitter<IDocumentServiceEvents>\n\timplements IDocumentService\n{\n\tpublic constructor(\n\t\tpublic readonly resolvedUrl: IResolvedUrl,\n\t\t// Serves the snapshot: a read-only document service bound to the closest file version at or\n\t\t// before the target sequence number. Its storage is the base we replay ops on top of.\n\t\tprivate readonly recoverableDocumentService: IDocumentService,\n\t\tprivate readonly liveDocumentService: IDocumentService,\n\t\tprivate readonly targetSequenceNumber: number,\n\t) {\n\t\tsuper();\n\t\tthis.liveDocumentService.on(\"metadataUpdate\", this.metadataUpdateHandler);\n\t}\n\n\t// storageOnly makes the connection manager synthesize a read-only frozen delta stream (no live\n\t// socket) and force the container read-only - see the class remarks.\n\tpublic readonly policies: IDocumentServicePolicies = { storageOnly: true };\n\n\tpublic dispose(): void {\n\t\tthis.liveDocumentService.off(\"metadataUpdate\", this.metadataUpdateHandler);\n\t\tthis.recoverableDocumentService.dispose();\n\t\tthis.liveDocumentService.dispose();\n\t}\n\n\tpublic async connectToStorage(): Promise<IDocumentStorageService> {\n\t\tconst storage = await this.recoverableDocumentService.connectToStorage();\n\t\treturn new PointInTimeDocumentStorageService(storage);\n\t}\n\n\tpublic async connectToDeltaStorage(): Promise<IDocumentDeltaStorageService> {\n\t\tconst liveDeltaStorage = await this.liveDocumentService.connectToDeltaStorage();\n\t\t// The exclusive upper bound needed to include the target op itself.\n\t\tconst boundedTo = this.targetSequenceNumber + 1;\n\t\treturn {\n\t\t\tfetchMessages: (from, to, abortSignal, cachedOnly, fetchReason) =>\n\t\t\t\tliveDeltaStorage.fetchMessages(\n\t\t\t\t\tfrom,\n\t\t\t\t\tto === undefined ? boundedTo : Math.min(to, boundedTo),\n\t\t\t\t\tabortSignal,\n\t\t\t\t\tcachedOnly,\n\t\t\t\t\tfetchReason,\n\t\t\t\t),\n\t\t};\n\t}\n\n\tpublic async connectToDeltaStream(_client: IClient): Promise<IDocumentDeltaConnection> {\n\t\t// Unreachable under normal flow: the connection manager short-circuits on the storageOnly\n\t\t// policy and synthesizes a frozen delta stream before ever calling connectToDeltaStream.\n\t\t// Reaching here indicates a regression of that short-circuit.\n\t\tthrow new Error(\n\t\t\t\"OdspPointInTimeDocumentService is storage-only; connectToDeltaStream should not be called\",\n\t\t);\n\t}\n\n\tprivate readonly metadataUpdateHandler = (metadata: Record<string, string>): void => {\n\t\tthis.emit(\"metadataUpdate\", metadata);\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"odspPointInTimeDocumentService.js","sourceRoot":"","sources":["../../src/pointInTimeDriver/odspPointInTimeDocumentService.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 { TypedEventEmitter } from \"@fluid-internal/client-utils\";\nimport {\n\tFetchSource,\n\ttype IClient,\n\ttype IDocumentDeltaConnection,\n\ttype IDocumentDeltaStorageService,\n\ttype IDocumentService,\n\ttype IDocumentServiceEvents,\n\ttype IDocumentServicePolicies,\n\ttype IDocumentStorageService,\n\ttype IResolvedUrl,\n\ttype ISnapshot,\n\ttype ISnapshotFetchOptions,\n\ttype IVersion,\n} from \"@fluidframework/driver-definitions/internal\";\nimport { DocumentStorageServiceProxy } from \"@fluidframework/driver-utils/internal\";\nimport { OdspErrorTypes } from \"@fluidframework/odsp-driver-definitions/internal\";\nimport { isFluidError } from \"@fluidframework/telemetry-utils/internal\";\n\n/**\n * Forces point-in-time snapshot reads to bypass caches while forwarding all other storage operations\n * to the selected historical document service.\n */\nclass PointInTimeDocumentStorageService extends DocumentStorageServiceProxy {\n\tpublic override async getSnapshot(\n\t\tsnapshotFetchOptions?: ISnapshotFetchOptions,\n\t): Promise<ISnapshot> {\n\t\treturn super.getSnapshot({\n\t\t\t...snapshotFetchOptions,\n\t\t\tfetchSource: FetchSource.noCache,\n\t\t});\n\t}\n\n\tpublic override async getVersions(\n\t\t// eslint-disable-next-line @rushstack/no-new-null -- IDocumentStorageService is a legacy API that requires null.\n\t\tversionId: string | null,\n\t\tcount: number,\n\t\tscenarioName?: string,\n\t): Promise<IVersion[]> {\n\t\treturn super.getVersions(versionId, count, scenarioName, FetchSource.noCache);\n\t}\n}\n\nfunction annotateMissingOps(error: unknown): void {\n\tif (!isFluidError(error)) {\n\t\treturn;\n\t}\n\n\t// Attach a stable Version Mark availability classification only when the driver error\n\t// confirms that the required historical ops are unavailable.\n\tconst historyIsUnavailable =\n\t\terror.errorType === OdspErrorTypes.cannotCatchUp ||\n\t\t(error.errorType === OdspErrorTypes.genericNetworkError &&\n\t\t\terror.getTelemetryProperties().opsFetchFailure === \"tooManyRetries\");\n\tif (historyIsUnavailable) {\n\t\terror.addTelemetryProperties({\n\t\t\tversionMarkAvailabilityOutcome: \"missingOps\",\n\t\t});\n\t}\n}\n\n/**\n * A read-only document service that materializes a document at a target sequence number by combining\n * a recoverable snapshot with a bounded replay of live ops.\n *\n * @remarks\n * Storage (the snapshot) is served from the closest file version at or before the target sequence\n * number. The ops needed to advance from that snapshot to the target are read from the live\n * document's delta storage, bounded so that no op past the target is ever fetched.\n *\n * The service advertises the {@link IDocumentServicePolicies.storageOnly} policy. This reuses the\n * loader's \"frozen\" load mechanism: the connection manager synthesizes a read-only frozen delta\n * stream instead of opening a live socket, and forces the container read-only. The delta manager\n * still catches up from the snapshot's sequence number through delta storage, which is exactly the\n * bounded replay we want. As a result no live delta-stream connection is ever established.\n *\n * Op availability is enforced by the delta storage stack itself: it validates that fetched batches\n * are contiguous from the requested start, keeps requesting until the bounded range is fully\n * delivered, and fails the fetch if the ops never materialize. So a stream that completes has\n * necessarily served the whole bridge, and no additional checks are needed here.\n *\n * @internal\n */\nexport class OdspPointInTimeDocumentService\n\textends TypedEventEmitter<IDocumentServiceEvents>\n\timplements IDocumentService\n{\n\tpublic constructor(\n\t\tpublic readonly resolvedUrl: IResolvedUrl,\n\t\t// Serves the snapshot: a read-only document service bound to the closest file version at or\n\t\t// before the target sequence number. Its storage is the base we replay ops on top of.\n\t\tprivate readonly recoverableDocumentService: IDocumentService,\n\t\tprivate readonly liveDocumentService: IDocumentService,\n\t\tprivate readonly targetSequenceNumber: number,\n\t) {\n\t\tsuper();\n\t\tthis.liveDocumentService.on(\"metadataUpdate\", this.metadataUpdateHandler);\n\t}\n\n\t// storageOnly makes the connection manager synthesize a read-only frozen delta stream (no live\n\t// socket) and force the container read-only - see the class remarks.\n\tpublic readonly policies: IDocumentServicePolicies = { storageOnly: true };\n\n\tpublic dispose(): void {\n\t\tthis.liveDocumentService.off(\"metadataUpdate\", this.metadataUpdateHandler);\n\t\tthis.recoverableDocumentService.dispose();\n\t\tthis.liveDocumentService.dispose();\n\t}\n\n\tpublic async connectToStorage(): Promise<IDocumentStorageService> {\n\t\tconst storage = await this.recoverableDocumentService.connectToStorage();\n\t\treturn new PointInTimeDocumentStorageService(storage);\n\t}\n\n\tpublic async connectToDeltaStorage(): Promise<IDocumentDeltaStorageService> {\n\t\tconst liveDeltaStorage = await this.liveDocumentService.connectToDeltaStorage();\n\t\t// The exclusive upper bound needed to include the target op itself.\n\t\tconst boundedTo = this.targetSequenceNumber + 1;\n\t\treturn {\n\t\t\tfetchMessages: (from, to, abortSignal, cachedOnly, fetchReason) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst stream = liveDeltaStorage.fetchMessages(\n\t\t\t\t\t\tfrom,\n\t\t\t\t\t\tto === undefined ? boundedTo : Math.min(to, boundedTo),\n\t\t\t\t\t\tabortSignal,\n\t\t\t\t\t\tcachedOnly,\n\t\t\t\t\t\tfetchReason,\n\t\t\t\t\t);\n\t\t\t\t\treturn {\n\t\t\t\t\t\tread: async () => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\treturn await stream.read();\n\t\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t\t// The bounded replay owns the conclusion that these specific failures mean\n\t\t\t\t\t\t\t\t// the bridge to the target cannot be materialized. Preserve the driver's raw\n\t\t\t\t\t\t\t\t// errorType while attaching the stable Version Mark availability outcome.\n\t\t\t\t\t\t\t\tannotateMissingOps(error);\n\t\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\tannotateMissingOps(error);\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t}\n\n\tpublic async connectToDeltaStream(_client: IClient): Promise<IDocumentDeltaConnection> {\n\t\t// Unreachable under normal flow: the connection manager short-circuits on the storageOnly\n\t\t// policy and synthesizes a frozen delta stream before ever calling connectToDeltaStream.\n\t\t// Reaching here indicates a regression of that short-circuit.\n\t\tthrow new Error(\n\t\t\t\"OdspPointInTimeDocumentService is storage-only; connectToDeltaStream should not be called\",\n\t\t);\n\t}\n\n\tprivate readonly metadataUpdateHandler = (metadata: Record<string, string>): void => {\n\t\tthis.emit(\"metadataUpdate\", metadata);\n\t};\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/odsp-driver",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "Socket storage implementation for SPO and ODC",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -66,27 +66,27 @@
|
|
|
66
66
|
"temp-directory": "nyc/.nyc_output"
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
|
-
"@fluid-internal/client-utils": "~3.
|
|
70
|
-
"@fluidframework/core-interfaces": "~3.
|
|
71
|
-
"@fluidframework/core-utils": "~3.
|
|
72
|
-
"@fluidframework/driver-base": "~3.
|
|
73
|
-
"@fluidframework/driver-definitions": "~3.
|
|
74
|
-
"@fluidframework/driver-utils": "~3.
|
|
75
|
-
"@fluidframework/odsp-doclib-utils": "~3.
|
|
76
|
-
"@fluidframework/odsp-driver-definitions": "~3.
|
|
77
|
-
"@fluidframework/telemetry-utils": "~3.
|
|
69
|
+
"@fluid-internal/client-utils": "~3.3.0",
|
|
70
|
+
"@fluidframework/core-interfaces": "~3.3.0",
|
|
71
|
+
"@fluidframework/core-utils": "~3.3.0",
|
|
72
|
+
"@fluidframework/driver-base": "~3.3.0",
|
|
73
|
+
"@fluidframework/driver-definitions": "~3.3.0",
|
|
74
|
+
"@fluidframework/driver-utils": "~3.3.0",
|
|
75
|
+
"@fluidframework/odsp-doclib-utils": "~3.3.0",
|
|
76
|
+
"@fluidframework/odsp-driver-definitions": "~3.3.0",
|
|
77
|
+
"@fluidframework/telemetry-utils": "~3.3.0",
|
|
78
78
|
"socket.io-client": "^4.8.3",
|
|
79
79
|
"uuid": "^11.1.0"
|
|
80
80
|
},
|
|
81
81
|
"devDependencies": {
|
|
82
82
|
"@arethetypeswrong/cli": "^0.18.5",
|
|
83
83
|
"@biomejs/biome": "~2.4.5",
|
|
84
|
-
"@fluid-internal/mocha-test-setup": "~3.
|
|
84
|
+
"@fluid-internal/mocha-test-setup": "~3.3.0",
|
|
85
85
|
"@fluid-tools/build-cli": "^0.67.0",
|
|
86
86
|
"@fluidframework/build-common": "^2.0.3",
|
|
87
87
|
"@fluidframework/build-tools": "^0.67.0",
|
|
88
88
|
"@fluidframework/eslint-config-fluid": "^14.0.0",
|
|
89
|
-
"@fluidframework/odsp-driver-previous": "npm:@fluidframework/odsp-driver@2.
|
|
89
|
+
"@fluidframework/odsp-driver-previous": "npm:@fluidframework/odsp-driver@3.2.0",
|
|
90
90
|
"@microsoft/api-extractor": "7.58.1",
|
|
91
91
|
"@types/mocha": "^10.0.10",
|
|
92
92
|
"@types/node": "~22.19.17",
|
|
@@ -104,11 +104,7 @@
|
|
|
104
104
|
"typescript": "~6.0.3"
|
|
105
105
|
},
|
|
106
106
|
"typeValidation": {
|
|
107
|
-
"broken": {
|
|
108
|
-
"Function_getOdspPointInTimeDocumentServiceFactory": {
|
|
109
|
-
"backCompat": false
|
|
110
|
-
}
|
|
111
|
-
},
|
|
107
|
+
"broken": {},
|
|
112
108
|
"entrypoint": "legacy"
|
|
113
109
|
},
|
|
114
110
|
"scripts": {
|
|
@@ -54,6 +54,10 @@ export type BaseForSeq =
|
|
|
54
54
|
/** A recoverable version with `sequenceNumber <= target` was found. */
|
|
55
55
|
readonly kind: "found";
|
|
56
56
|
readonly base: ResolvedVersion;
|
|
57
|
+
/** Number of sealed version candidates examined before selecting the base. */
|
|
58
|
+
readonly versionsProbed: number;
|
|
59
|
+
/** Number of candidate sequence numbers that required an uncached ODSP fetch. */
|
|
60
|
+
readonly sequenceNumberFetchCount: number;
|
|
57
61
|
}
|
|
58
62
|
| {
|
|
59
63
|
/**
|
|
@@ -63,6 +67,10 @@ export type BaseForSeq =
|
|
|
63
67
|
readonly kind: "noBaseVersion";
|
|
64
68
|
/** The oldest sequence number that was resolved while searching, if any. */
|
|
65
69
|
readonly oldestResolvedSeq?: number;
|
|
70
|
+
/** Number of sealed version candidates examined before exhausting retained history. */
|
|
71
|
+
readonly versionsProbed: number;
|
|
72
|
+
/** Number of candidate sequence numbers that required an uncached ODSP fetch. */
|
|
73
|
+
readonly sequenceNumberFetchCount: number;
|
|
66
74
|
};
|
|
67
75
|
|
|
68
76
|
/**
|
|
@@ -111,8 +119,13 @@ export class OdspVersionManager implements IOdspVersionManager {
|
|
|
111
119
|
const candidates = versions.slice(1);
|
|
112
120
|
|
|
113
121
|
let oldestResolvedSeq: number | undefined;
|
|
122
|
+
let versionsProbed = 0;
|
|
123
|
+
let sequenceNumberFetchCount = 0;
|
|
114
124
|
for (const version of candidates) {
|
|
115
|
-
|
|
125
|
+
versionsProbed++;
|
|
126
|
+
const sequenceNumber = await this.resolveSeq(version.versionId, () => {
|
|
127
|
+
sequenceNumberFetchCount++;
|
|
128
|
+
});
|
|
116
129
|
oldestResolvedSeq =
|
|
117
130
|
oldestResolvedSeq === undefined
|
|
118
131
|
? sequenceNumber
|
|
@@ -121,10 +134,15 @@ export class OdspVersionManager implements IOdspVersionManager {
|
|
|
121
134
|
const base = { ...version, sequenceNumber };
|
|
122
135
|
// Confirm the chosen base shares the live document's lineage before handing it back
|
|
123
136
|
await this.validateLineageEpoch(base);
|
|
124
|
-
return { kind: "found", base };
|
|
137
|
+
return { kind: "found", base, versionsProbed, sequenceNumberFetchCount };
|
|
125
138
|
}
|
|
126
139
|
}
|
|
127
|
-
return {
|
|
140
|
+
return {
|
|
141
|
+
kind: "noBaseVersion",
|
|
142
|
+
oldestResolvedSeq,
|
|
143
|
+
versionsProbed,
|
|
144
|
+
sequenceNumberFetchCount,
|
|
145
|
+
};
|
|
128
146
|
}
|
|
129
147
|
|
|
130
148
|
private async validateLineageEpoch(base: ResolvedVersion): Promise<void> {
|
|
@@ -159,6 +177,7 @@ export class OdspVersionManager implements IOdspVersionManager {
|
|
|
159
177
|
driverVersion,
|
|
160
178
|
serverEpoch: liveEpoch,
|
|
161
179
|
clientEpoch: baseEpoch,
|
|
180
|
+
versionMarkAvailabilityOutcome: "lineageMismatch",
|
|
162
181
|
},
|
|
163
182
|
);
|
|
164
183
|
}
|
|
@@ -181,12 +200,13 @@ export class OdspVersionManager implements IOdspVersionManager {
|
|
|
181
200
|
);
|
|
182
201
|
}
|
|
183
202
|
|
|
184
|
-
private async resolveSeq(versionId: string): Promise<number> {
|
|
203
|
+
private async resolveSeq(versionId: string, onCacheMiss?: () => void): Promise<number> {
|
|
185
204
|
// Cached indefinitely (a sealed version's number is fixed); concurrent calls coalesce and a failed
|
|
186
205
|
// resolution is evicted so a later call retries.
|
|
187
|
-
return this.seqCache.addOrGet(versionId, async () =>
|
|
188
|
-
|
|
189
|
-
|
|
206
|
+
return this.seqCache.addOrGet(versionId, async () => {
|
|
207
|
+
onCacheMiss?.();
|
|
208
|
+
return this.fetcher.resolveSequenceNumber(versionId);
|
|
209
|
+
});
|
|
190
210
|
}
|
|
191
211
|
|
|
192
212
|
private async resolveVersionEpoch(versionId: string): Promise<string | undefined> {
|
package/src/packageVersion.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { performanceNow } from "@fluid-internal/client-utils";
|
|
6
7
|
import type {
|
|
7
8
|
IDocumentService,
|
|
8
9
|
IResolvedUrl,
|
|
@@ -14,6 +15,7 @@ import type {
|
|
|
14
15
|
} from "@fluidframework/odsp-driver-definitions/internal";
|
|
15
16
|
import {
|
|
16
17
|
createChildLogger,
|
|
18
|
+
isILoggingError,
|
|
17
19
|
type TelemetryLoggerExt,
|
|
18
20
|
} from "@fluidframework/telemetry-utils/internal";
|
|
19
21
|
|
|
@@ -143,21 +145,68 @@ export async function createPointInTimeDocumentServiceCore(
|
|
|
143
145
|
requestHeaders,
|
|
144
146
|
);
|
|
145
147
|
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
148
|
+
const baseSelectionStartTime = performanceNow();
|
|
149
|
+
let baseResult: Awaited<ReturnType<IOdspVersionManager["findBaseForSeq"]>>;
|
|
150
|
+
try {
|
|
151
|
+
const versionManager = await (
|
|
152
|
+
dependencies.createVersionManager ??
|
|
153
|
+
(async (url, versionLogger, epochTracker) =>
|
|
154
|
+
createVersionManager(
|
|
155
|
+
url,
|
|
156
|
+
versionLogger,
|
|
157
|
+
epochTracker,
|
|
158
|
+
getStorageToken,
|
|
159
|
+
requestHeaders,
|
|
160
|
+
))
|
|
161
|
+
)(odspResolvedUrl, extLogger, cacheAndTracker.epochTracker);
|
|
162
|
+
baseResult = await versionManager.findBaseForSeq(targetSequenceNumber);
|
|
163
|
+
} catch (error) {
|
|
164
|
+
extLogger.sendErrorEvent(
|
|
165
|
+
{
|
|
166
|
+
eventName: "VersionMarkBaseVersionSelection",
|
|
167
|
+
outcome: "failed",
|
|
168
|
+
duration: performanceNow() - baseSelectionStartTime,
|
|
169
|
+
targetSequenceNumber,
|
|
170
|
+
errorType: (error as Partial<{ errorType: string }> | undefined)?.errorType,
|
|
171
|
+
availabilityOutcome: isILoggingError(error)
|
|
172
|
+
? error.getTelemetryProperties().versionMarkAvailabilityOutcome
|
|
173
|
+
: undefined,
|
|
174
|
+
},
|
|
175
|
+
error,
|
|
176
|
+
);
|
|
177
|
+
throw error;
|
|
178
|
+
}
|
|
152
179
|
if (baseResult.kind === "noBaseVersion") {
|
|
153
180
|
const oldestResolvedSequenceDetail =
|
|
154
181
|
baseResult.oldestResolvedSeq === undefined
|
|
155
182
|
? ""
|
|
156
183
|
: ` The oldest resolved file version is at sequence number ${baseResult.oldestResolvedSeq}.`;
|
|
157
|
-
|
|
184
|
+
const error = new UsageError(
|
|
158
185
|
`No ODSP file version is available at or before sequence number ${targetSequenceNumber}.${oldestResolvedSequenceDetail}`,
|
|
159
186
|
);
|
|
187
|
+
error.addTelemetryProperties({
|
|
188
|
+
versionMarkAvailabilityOutcome: "baseVersionMissing",
|
|
189
|
+
});
|
|
190
|
+
extLogger.sendErrorEvent({
|
|
191
|
+
eventName: "VersionMarkBaseVersionSelection",
|
|
192
|
+
outcome: "failed",
|
|
193
|
+
duration: performanceNow() - baseSelectionStartTime,
|
|
194
|
+
targetSequenceNumber,
|
|
195
|
+
availabilityOutcome: "baseVersionMissing",
|
|
196
|
+
oldestResolvedSequenceNumber: baseResult.oldestResolvedSeq,
|
|
197
|
+
versionsProbed: baseResult.versionsProbed,
|
|
198
|
+
sequenceNumberFetchCount: baseResult.sequenceNumberFetchCount,
|
|
199
|
+
errorType: error.errorType,
|
|
200
|
+
});
|
|
201
|
+
throw error;
|
|
160
202
|
}
|
|
203
|
+
extLogger.sendPerformanceEvent({
|
|
204
|
+
eventName: "VersionMarkBaseVersionSelection",
|
|
205
|
+
outcome: "succeeded",
|
|
206
|
+
duration: performanceNow() - baseSelectionStartTime,
|
|
207
|
+
versionsProbed: baseResult.versionsProbed,
|
|
208
|
+
sequenceNumberFetchCount: baseResult.sequenceNumberFetchCount,
|
|
209
|
+
});
|
|
161
210
|
|
|
162
211
|
const recoverableResolvedUrl = await (dependencies.resolveFileVersion ?? resolveFileVersion)(
|
|
163
212
|
resolvedUrl,
|
|
@@ -182,7 +231,9 @@ export async function createPointInTimeDocumentServiceCore(
|
|
|
182
231
|
recoverableDocumentService.dispose();
|
|
183
232
|
} catch (disposeError) {
|
|
184
233
|
extLogger.sendErrorEvent(
|
|
185
|
-
{
|
|
234
|
+
{
|
|
235
|
+
eventName: "VersionMarkRecoverableDocumentServiceDisposeError",
|
|
236
|
+
},
|
|
186
237
|
disposeError,
|
|
187
238
|
);
|
|
188
239
|
}
|
|
@@ -19,6 +19,8 @@ import {
|
|
|
19
19
|
type IVersion,
|
|
20
20
|
} from "@fluidframework/driver-definitions/internal";
|
|
21
21
|
import { DocumentStorageServiceProxy } from "@fluidframework/driver-utils/internal";
|
|
22
|
+
import { OdspErrorTypes } from "@fluidframework/odsp-driver-definitions/internal";
|
|
23
|
+
import { isFluidError } from "@fluidframework/telemetry-utils/internal";
|
|
22
24
|
|
|
23
25
|
/**
|
|
24
26
|
* Forces point-in-time snapshot reads to bypass caches while forwarding all other storage operations
|
|
@@ -44,6 +46,24 @@ class PointInTimeDocumentStorageService extends DocumentStorageServiceProxy {
|
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
48
|
|
|
49
|
+
function annotateMissingOps(error: unknown): void {
|
|
50
|
+
if (!isFluidError(error)) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Attach a stable Version Mark availability classification only when the driver error
|
|
55
|
+
// confirms that the required historical ops are unavailable.
|
|
56
|
+
const historyIsUnavailable =
|
|
57
|
+
error.errorType === OdspErrorTypes.cannotCatchUp ||
|
|
58
|
+
(error.errorType === OdspErrorTypes.genericNetworkError &&
|
|
59
|
+
error.getTelemetryProperties().opsFetchFailure === "tooManyRetries");
|
|
60
|
+
if (historyIsUnavailable) {
|
|
61
|
+
error.addTelemetryProperties({
|
|
62
|
+
versionMarkAvailabilityOutcome: "missingOps",
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
47
67
|
/**
|
|
48
68
|
* A read-only document service that materializes a document at a target sequence number by combining
|
|
49
69
|
* a recoverable snapshot with a bounded replay of live ops.
|
|
@@ -102,14 +122,33 @@ export class OdspPointInTimeDocumentService
|
|
|
102
122
|
// The exclusive upper bound needed to include the target op itself.
|
|
103
123
|
const boundedTo = this.targetSequenceNumber + 1;
|
|
104
124
|
return {
|
|
105
|
-
fetchMessages: (from, to, abortSignal, cachedOnly, fetchReason) =>
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
125
|
+
fetchMessages: (from, to, abortSignal, cachedOnly, fetchReason) => {
|
|
126
|
+
try {
|
|
127
|
+
const stream = liveDeltaStorage.fetchMessages(
|
|
128
|
+
from,
|
|
129
|
+
to === undefined ? boundedTo : Math.min(to, boundedTo),
|
|
130
|
+
abortSignal,
|
|
131
|
+
cachedOnly,
|
|
132
|
+
fetchReason,
|
|
133
|
+
);
|
|
134
|
+
return {
|
|
135
|
+
read: async () => {
|
|
136
|
+
try {
|
|
137
|
+
return await stream.read();
|
|
138
|
+
} catch (error) {
|
|
139
|
+
// The bounded replay owns the conclusion that these specific failures mean
|
|
140
|
+
// the bridge to the target cannot be materialized. Preserve the driver's raw
|
|
141
|
+
// errorType while attaching the stable Version Mark availability outcome.
|
|
142
|
+
annotateMissingOps(error);
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
} catch (error) {
|
|
148
|
+
annotateMissingOps(error);
|
|
149
|
+
throw error;
|
|
150
|
+
}
|
|
151
|
+
},
|
|
113
152
|
};
|
|
114
153
|
}
|
|
115
154
|
|