@fluidframework/odsp-driver 3.2.0 → 3.4.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.
Files changed (32) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/odspVersionManager/odspVersionManager.d.ts +8 -0
  3. package/dist/odspVersionManager/odspVersionManager.d.ts.map +1 -1
  4. package/dist/odspVersionManager/odspVersionManager.js +19 -5
  5. package/dist/odspVersionManager/odspVersionManager.js.map +1 -1
  6. package/dist/packageVersion.d.ts +1 -1
  7. package/dist/packageVersion.js +1 -1
  8. package/dist/packageVersion.js.map +1 -1
  9. package/dist/pointInTimeDriver/createPointInTimeDocumentService.d.ts.map +1 -1
  10. package/dist/pointInTimeDriver/createPointInTimeDocumentService.js +47 -5
  11. package/dist/pointInTimeDriver/createPointInTimeDocumentService.js.map +1 -1
  12. package/dist/pointInTimeDriver/odspPointInTimeDocumentService.d.ts.map +1 -1
  13. package/dist/pointInTimeDriver/odspPointInTimeDocumentService.js +40 -1
  14. package/dist/pointInTimeDriver/odspPointInTimeDocumentService.js.map +1 -1
  15. package/lib/odspVersionManager/odspVersionManager.d.ts +8 -0
  16. package/lib/odspVersionManager/odspVersionManager.d.ts.map +1 -1
  17. package/lib/odspVersionManager/odspVersionManager.js +19 -5
  18. package/lib/odspVersionManager/odspVersionManager.js.map +1 -1
  19. package/lib/packageVersion.d.ts +1 -1
  20. package/lib/packageVersion.js +1 -1
  21. package/lib/packageVersion.js.map +1 -1
  22. package/lib/pointInTimeDriver/createPointInTimeDocumentService.d.ts.map +1 -1
  23. package/lib/pointInTimeDriver/createPointInTimeDocumentService.js +48 -6
  24. package/lib/pointInTimeDriver/createPointInTimeDocumentService.js.map +1 -1
  25. package/lib/pointInTimeDriver/odspPointInTimeDocumentService.d.ts.map +1 -1
  26. package/lib/pointInTimeDriver/odspPointInTimeDocumentService.js +40 -1
  27. package/lib/pointInTimeDriver/odspPointInTimeDocumentService.js.map +1 -1
  28. package/package.json +12 -12
  29. package/src/odspVersionManager/odspVersionManager.ts +27 -7
  30. package/src/packageVersion.ts +1 -1
  31. package/src/pointInTimeDriver/createPointInTimeDocumentService.ts +59 -8
  32. package/src/pointInTimeDriver/odspPointInTimeDocumentService.ts +47 -8
@@ -5,5 +5,5 @@
5
5
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
6
  */
7
7
  export declare const pkgName = "@fluidframework/odsp-driver";
8
- export declare const pkgVersion = "3.2.0";
8
+ export declare const pkgVersion = "3.4.0";
9
9
  //# sourceMappingURL=packageVersion.d.ts.map
@@ -5,5 +5,5 @@
5
5
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
6
  */
7
7
  export const pkgName = "@fluidframework/odsp-driver";
8
- export const pkgVersion = "3.2.0";
8
+ export const pkgVersion = "3.4.0";
9
9
  //# sourceMappingURL=packageVersion.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,6BAA6B,CAAC;AACrD,MAAM,CAAC,MAAM,UAAU,GAAG,OAAO,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/odsp-driver\";\nexport const pkgVersion = \"3.2.0\";\n"]}
1
+ {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,6BAA6B,CAAC;AACrD,MAAM,CAAC,MAAM,UAAU,GAAG,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.4.0\";\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"createPointInTimeDocumentService.d.ts","sourceRoot":"","sources":["../../src/pointInTimeDriver/createPointInTimeDocumentService.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACX,gBAAgB,EAChB,YAAY,EACZ,MAAM,6CAA6C,CAAC;AAErD,OAAO,KAAK,EACX,gBAAgB,EAEhB,MAAM,kDAAkD,CAAC;AAC1D,OAAO,EAEN,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,CAoEzC"}
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 versionManager = await (dependencies.createVersionManager ??
75
- (async (url, versionLogger, epochTracker) => createVersionManager(url, versionLogger, epochTracker, getStorageToken, requestHeaders)))(odspResolvedUrl, extLogger, cacheAndTracker.epochTracker);
76
- const baseResult = await versionManager.findBaseForSeq(targetSequenceNumber);
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
- throw new UsageError(`No ODSP file version is available at or before sequence number ${targetSequenceNumber}.${oldestResolvedSequenceDetail}`);
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({ eventName: "PointInTimeRecoverableDocumentServiceDisposeError" }, disposeError);
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;AA2BrD;;;;;;;;;;;;;;;;;;;;;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;IAgB9D,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAStF,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAEpC;CACF"}
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) => liveDeltaStorage.fetchMessages(from, to === undefined ? boundedTo : Math.min(to, boundedTo), 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.2.0",
3
+ "version": "3.4.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.2.0",
70
- "@fluidframework/core-interfaces": "~3.2.0",
71
- "@fluidframework/core-utils": "~3.2.0",
72
- "@fluidframework/driver-base": "~3.2.0",
73
- "@fluidframework/driver-definitions": "~3.2.0",
74
- "@fluidframework/driver-utils": "~3.2.0",
75
- "@fluidframework/odsp-doclib-utils": "~3.2.0",
76
- "@fluidframework/odsp-driver-definitions": "~3.2.0",
77
- "@fluidframework/telemetry-utils": "~3.2.0",
69
+ "@fluid-internal/client-utils": "~3.4.0",
70
+ "@fluidframework/core-interfaces": "~3.4.0",
71
+ "@fluidframework/core-utils": "~3.4.0",
72
+ "@fluidframework/driver-base": "~3.4.0",
73
+ "@fluidframework/driver-definitions": "~3.4.0",
74
+ "@fluidframework/driver-utils": "~3.4.0",
75
+ "@fluidframework/odsp-doclib-utils": "~3.4.0",
76
+ "@fluidframework/odsp-driver-definitions": "~3.4.0",
77
+ "@fluidframework/telemetry-utils": "~3.4.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.2.0",
84
+ "@fluid-internal/mocha-test-setup": "~3.4.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@3.1.0",
89
+ "@fluidframework/odsp-driver-previous": "npm:@fluidframework/odsp-driver@3.3.0",
90
90
  "@microsoft/api-extractor": "7.58.1",
91
91
  "@types/mocha": "^10.0.10",
92
92
  "@types/node": "~22.19.17",
@@ -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
- const sequenceNumber = await this.resolveSeq(version.versionId);
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 { kind: "noBaseVersion", oldestResolvedSeq };
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
- this.fetcher.resolveSequenceNumber(versionId),
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> {
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/odsp-driver";
9
- export const pkgVersion = "3.2.0";
9
+ export const pkgVersion = "3.4.0";
@@ -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 versionManager = await (
147
- dependencies.createVersionManager ??
148
- (async (url, versionLogger, epochTracker) =>
149
- createVersionManager(url, versionLogger, epochTracker, getStorageToken, requestHeaders))
150
- )(odspResolvedUrl, extLogger, cacheAndTracker.epochTracker);
151
- const baseResult = await versionManager.findBaseForSeq(targetSequenceNumber);
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
- throw new UsageError(
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
- { eventName: "PointInTimeRecoverableDocumentServiceDisposeError" },
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
- liveDeltaStorage.fetchMessages(
107
- from,
108
- to === undefined ? boundedTo : Math.min(to, boundedTo),
109
- abortSignal,
110
- cachedOnly,
111
- fetchReason,
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