@fluidframework/container-loader 2.51.0 → 2.53.0-350190

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 (56) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/connectionManager.d.ts +7 -1
  3. package/dist/connectionManager.d.ts.map +1 -1
  4. package/dist/connectionManager.js +28 -1
  5. package/dist/connectionManager.js.map +1 -1
  6. package/dist/container.d.ts +11 -8
  7. package/dist/container.d.ts.map +1 -1
  8. package/dist/container.js +37 -22
  9. package/dist/container.js.map +1 -1
  10. package/dist/containerContext.d.ts +49 -25
  11. package/dist/containerContext.d.ts.map +1 -1
  12. package/dist/containerContext.js +29 -38
  13. package/dist/containerContext.js.map +1 -1
  14. package/dist/containerStorageAdapter.d.ts +10 -3
  15. package/dist/containerStorageAdapter.d.ts.map +1 -1
  16. package/dist/containerStorageAdapter.js +12 -3
  17. package/dist/containerStorageAdapter.js.map +1 -1
  18. package/dist/packageVersion.d.ts +1 -1
  19. package/dist/packageVersion.d.ts.map +1 -1
  20. package/dist/packageVersion.js +1 -1
  21. package/dist/packageVersion.js.map +1 -1
  22. package/dist/serializedStateManager.d.ts +2 -2
  23. package/dist/serializedStateManager.d.ts.map +1 -1
  24. package/dist/serializedStateManager.js +5 -5
  25. package/dist/serializedStateManager.js.map +1 -1
  26. package/lib/connectionManager.d.ts +7 -1
  27. package/lib/connectionManager.d.ts.map +1 -1
  28. package/lib/connectionManager.js +28 -1
  29. package/lib/connectionManager.js.map +1 -1
  30. package/lib/container.d.ts +11 -8
  31. package/lib/container.d.ts.map +1 -1
  32. package/lib/container.js +37 -22
  33. package/lib/container.js.map +1 -1
  34. package/lib/containerContext.d.ts +49 -25
  35. package/lib/containerContext.d.ts.map +1 -1
  36. package/lib/containerContext.js +29 -38
  37. package/lib/containerContext.js.map +1 -1
  38. package/lib/containerStorageAdapter.d.ts +10 -3
  39. package/lib/containerStorageAdapter.d.ts.map +1 -1
  40. package/lib/containerStorageAdapter.js +12 -3
  41. package/lib/containerStorageAdapter.js.map +1 -1
  42. package/lib/packageVersion.d.ts +1 -1
  43. package/lib/packageVersion.d.ts.map +1 -1
  44. package/lib/packageVersion.js +1 -1
  45. package/lib/packageVersion.js.map +1 -1
  46. package/lib/serializedStateManager.d.ts +2 -2
  47. package/lib/serializedStateManager.d.ts.map +1 -1
  48. package/lib/serializedStateManager.js +5 -5
  49. package/lib/serializedStateManager.js.map +1 -1
  50. package/package.json +14 -14
  51. package/src/connectionManager.ts +38 -0
  52. package/src/container.ts +50 -59
  53. package/src/containerContext.ts +128 -52
  54. package/src/containerStorageAdapter.ts +19 -6
  55. package/src/packageVersion.ts +1 -1
  56. package/src/serializedStateManager.ts +5 -9
@@ -1 +1 @@
1
- {"version":3,"file":"serializedStateManager.js","sourceRoot":"","sources":["../src/serializedStateManager.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 { stringToBuffer } from \"@fluid-internal/client-utils\";\nimport {\n\tIGetPendingLocalStateProps,\n\tIRuntime,\n} from \"@fluidframework/container-definitions/internal\";\nimport type {\n\tIEventProvider,\n\tIEvent,\n\tITelemetryBaseLogger,\n\tTagged,\n} from \"@fluidframework/core-interfaces\";\nimport { Timer, assert } from \"@fluidframework/core-utils/internal\";\nimport {\n\tFetchSource,\n\tIDocumentStorageService,\n\tIResolvedUrl,\n\tISnapshot,\n\ttype IDocumentAttributes,\n\tISnapshotTree,\n\tIVersion,\n\tISequencedDocumentMessage,\n} from \"@fluidframework/driver-definitions/internal\";\nimport { getSnapshotTree } from \"@fluidframework/driver-utils/internal\";\nimport {\n\tMonitoringContext,\n\tPerformanceEvent,\n\tUsageError,\n\tcreateChildMonitoringContext,\n\ttype TelemetryEventPropertyTypeExt,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport {\n\tISerializableBlobContents,\n\tgetBlobContentsFromTree,\n} from \"./containerStorageAdapter.js\";\nimport { convertSnapshotToSnapshotInfo, getDocumentAttributes } from \"./utils.js\";\n\n/**\n * This is very similar to {@link @fluidframework/protocol-definitions/internal#ISnapshot}, but the difference is\n * that the blobs of ISnapshot are of type ArrayBufferLike, while the blobs of this interface are serializable because\n * they are already converted to string.\n */\nexport interface SnapshotWithBlobs {\n\t/**\n\t * Snapshot from which container initially loaded.\n\t */\n\tbaseSnapshot: ISnapshotTree;\n\t/**\n\t * Serializable blobs from the base snapshot. Used to load offline since\n\t * storage is not available.\n\t */\n\tsnapshotBlobs: ISerializableBlobContents;\n}\n\n/**\n * State saved by a container at close time, to be used to load a new instance\n * of the container to the same state\n *\n * This is very similar to {@link @fluidframework/protocol-definitions/internal#ISnapshot}, but the difference is\n * that the blobs of ISnapshot are of type ArrayBufferLike, while the blobs of this interface are serializable because\n * they are already converted to string.\n *\n * @internal\n */\nexport interface IPendingContainerState extends SnapshotWithBlobs {\n\t/**\n\t * This container was attached (as opposed to IPendingDetachedContainerState.attached which is false)\n\t */\n\tattached: true;\n\t/**\n\t * Runtime-specific state that will be needed to properly rehydrate\n\t * (it's included in ContainerContext passed to instantiateRuntime)\n\t */\n\tpendingRuntimeState: unknown;\n\t/**\n\t * Any group snapshots (aka delay-loaded) we've downloaded from the service for this container\n\t */\n\tloadedGroupIdSnapshots?: Record<string, ISnapshotInfo>;\n\t/**\n\t * All ops since base snapshot sequence number up to the latest op\n\t * seen when the container was closed. Used to apply stashed (saved pending)\n\t * ops at the same sequence number at which they were made.\n\t */\n\tsavedOps: ISequencedDocumentMessage[];\n\t/**\n\t * The Container's URL in the service, needed to hook up the driver during rehydration\n\t */\n\turl: string;\n\t/**\n\t * If the Container was connected when serialized, its clientId. Used as the initial clientId upon rehydration, until reconnected.\n\t */\n\tclientId?: string;\n}\n\n/**\n * State saved by a container in detached state, to be used to load a new instance\n * of the container to the same state (rehydrate)\n * @internal\n */\nexport interface IPendingDetachedContainerState extends SnapshotWithBlobs {\n\t/**\n\t * This container was not attached (as opposed to IPendingContainerState.attached which is true)\n\t */\n\tattached: false;\n\t/**\n\t * Indicates whether we expect the rehydrated container to have non-empty Detached Blob Storage\n\t */\n\thasAttachmentBlobs: boolean;\n\t/**\n\t * Used by the memory blob storage to persisted attachment blobs\n\t */\n\tattachmentBlobs?: string;\n\t/**\n\t * Runtime-specific state that will be needed to properly rehydrate\n\t * (it's included in ContainerContext passed to instantiateRuntime)\n\t */\n\tpendingRuntimeState?: unknown;\n}\n\nexport interface ISnapshotInfo extends SnapshotWithBlobs {\n\tsnapshotSequenceNumber: number;\n\tsnapshotFetchedTime?: number | undefined;\n}\n\nexport type ISerializedStateManagerDocumentStorageService = Pick<\n\tIDocumentStorageService,\n\t\"getSnapshot\" | \"getSnapshotTree\" | \"getVersions\" | \"readBlob\"\n> & {\n\tloadedGroupIdSnapshots: Record<string, ISnapshot>;\n};\n\ninterface ISerializerEvent extends IEvent {\n\t(event: \"saved\", listener: (dirty: boolean) => void): void;\n}\n\n/**\n * Helper class to manage the state of the container needed for proper serialization.\n *\n * It holds the pendingLocalState the container was rehydrated from (if any),\n * as well as the snapshot to be used for serialization.\n * It also keeps track of container dirty state and which local ops have been processed\n */\nexport class SerializedStateManager {\n\tprivate readonly processedOps: ISequencedDocumentMessage[] = [];\n\tprivate readonly mc: MonitoringContext;\n\tprivate snapshot: ISnapshotInfo | undefined;\n\tprivate latestSnapshot: ISnapshotInfo | undefined;\n\tprivate _refreshSnapshotP: Promise<number> | undefined;\n\tprivate readonly lastSavedOpSequenceNumber: number = 0;\n\tprivate readonly refreshTimer: Timer;\n\tprivate readonly snapshotRefreshTimeoutMs: number = 60 * 60 * 24 * 1000;\n\n\t/**\n\t * @param pendingLocalState - The pendingLocalState being rehydrated, if any (undefined when loading directly from storage)\n\t * @param subLogger - Container's logger to use as parent for our logger\n\t * @param storageAdapter - Storage adapter for fetching snapshots\n\t * @param _offlineLoadEnabled - Is serializing/rehydrating containers allowed?\n\t * @param containerEvent - Source of the \"saved\" event when the container has all its pending state uploaded\n\t * @param containerDirty - Is the container \"dirty\"? That's the opposite of \"saved\" - there is pending state that may not have been received yet by the service.\n\t */\n\tconstructor(\n\t\tprivate readonly pendingLocalState: IPendingContainerState | undefined,\n\t\tsubLogger: ITelemetryBaseLogger,\n\t\tprivate readonly storageAdapter: ISerializedStateManagerDocumentStorageService,\n\t\tprivate readonly _offlineLoadEnabled: boolean,\n\t\tcontainerEvent: IEventProvider<ISerializerEvent>,\n\t\tprivate readonly containerDirty: () => boolean,\n\t\tprivate readonly supportGetSnapshotApi: () => boolean,\n\t\tsnapshotRefreshTimeoutMs?: number,\n\t) {\n\t\tthis.mc = createChildMonitoringContext({\n\t\t\tlogger: subLogger,\n\t\t\tnamespace: \"serializedStateManager\",\n\t\t});\n\n\t\tthis.snapshotRefreshTimeoutMs = snapshotRefreshTimeoutMs ?? this.snapshotRefreshTimeoutMs;\n\t\tthis.refreshTimer = new Timer(this.snapshotRefreshTimeoutMs, () =>\n\t\t\tthis.tryRefreshSnapshot(),\n\t\t);\n\t\t// special case handle. Obtaining the last saved op seq num to avoid\n\t\t// refreshing the snapshot before we have processed it. It could cause\n\t\t// a subsequent stashing to have a newer snapshot than allowed.\n\t\tif (pendingLocalState && pendingLocalState.savedOps.length > 0) {\n\t\t\tconst savedOpsSize = pendingLocalState.savedOps.length;\n\t\t\tthis.lastSavedOpSequenceNumber =\n\t\t\t\tpendingLocalState.savedOps[savedOpsSize - 1].sequenceNumber;\n\t\t}\n\t\tcontainerEvent.on(\"saved\", () => this.updateSnapshotAndProcessedOpsMaybe());\n\t}\n\n\tpublic get offlineLoadEnabled(): boolean {\n\t\treturn this._offlineLoadEnabled;\n\t}\n\n\t/**\n\t * Promise that will resolve (or reject) once we've tried to download the latest snapshot(s) from storage\n\t * only intended to be used for testing purposes.\n\t * @returns The snapshot sequence number associated with the latest fetched snapshot\n\t */\n\tpublic get refreshSnapshotP(): Promise<number> | undefined {\n\t\treturn this._refreshSnapshotP;\n\t}\n\n\t/**\n\t * Called whenever an incoming op is processed by the Container\n\t */\n\tpublic addProcessedOp(message: ISequencedDocumentMessage): void {\n\t\tif (this.offlineLoadEnabled) {\n\t\t\tthis.processedOps.push(message);\n\t\t\tthis.updateSnapshotAndProcessedOpsMaybe();\n\t\t}\n\t}\n\n\t/**\n\t * This wraps the basic functionality of fetching the snapshot for this container during Container load.\n\t *\n\t * If we have pendingLocalState, we get the snapshot from there.\n\t * Otherwise, fetch it from storage (according to specifiedVersion if provided).\n\t *\n\t * @param specifiedVersion - If a version is specified and we don't have pendingLocalState, fetch this version from storage.\n\t * @param supportGetSnapshotApi - a boolean indicating whether to use the fetchISnapshot or fetchISnapshotTree.\n\t * @returns The snapshot to boot the container from\n\t */\n\tpublic async fetchSnapshot(specifiedVersion: string | undefined): Promise<{\n\t\tbaseSnapshot: ISnapshot | ISnapshotTree;\n\t\tversion: IVersion | undefined;\n\t}> {\n\t\tif (this.pendingLocalState === undefined) {\n\t\t\tconst { baseSnapshot, version } = await getSnapshot(\n\t\t\t\tthis.mc,\n\t\t\t\tthis.storageAdapter,\n\t\t\t\tthis.supportGetSnapshotApi(),\n\t\t\t\tspecifiedVersion,\n\t\t\t);\n\t\t\tconst baseSnapshotTree: ISnapshotTree | undefined = getSnapshotTree(baseSnapshot);\n\t\t\t// non-interactive clients will not have any pending state we want to save\n\t\t\tif (this.offlineLoadEnabled) {\n\t\t\t\tconst snapshotBlobs = await getBlobContentsFromTree(baseSnapshot, this.storageAdapter);\n\t\t\t\tconst attributes = await getDocumentAttributes(this.storageAdapter, baseSnapshotTree);\n\t\t\t\tthis.snapshot = {\n\t\t\t\t\tbaseSnapshot: baseSnapshotTree,\n\t\t\t\t\tsnapshotBlobs,\n\t\t\t\t\tsnapshotSequenceNumber: attributes.sequenceNumber,\n\t\t\t\t};\n\t\t\t\tthis.refreshTimer.start();\n\t\t\t}\n\t\t\treturn { baseSnapshot, version };\n\t\t} else {\n\t\t\tconst { baseSnapshot, snapshotBlobs } = this.pendingLocalState;\n\t\t\tconst attributes = await getDocumentAttributes(this.storageAdapter, baseSnapshot);\n\t\t\tthis.snapshot = {\n\t\t\t\tbaseSnapshot,\n\t\t\t\tsnapshotBlobs,\n\t\t\t\tsnapshotSequenceNumber: attributes.sequenceNumber,\n\t\t\t};\n\t\t\tthis.tryRefreshSnapshot();\n\t\t\tconst blobContents = new Map<string, ArrayBuffer>();\n\t\t\tfor (const [id, value] of Object.entries(snapshotBlobs)) {\n\t\t\t\tblobContents.set(id, stringToBuffer(value, \"utf8\"));\n\t\t\t}\n\t\t\tconst iSnapshot: ISnapshot = {\n\t\t\t\tsequenceNumber: this.snapshot.snapshotSequenceNumber,\n\t\t\t\tsnapshotTree: baseSnapshot,\n\t\t\t\tblobContents,\n\t\t\t\tlatestSequenceNumber: undefined,\n\t\t\t\tops: [],\n\t\t\t\tsnapshotFormatV: 1,\n\t\t\t};\n\t\t\treturn { baseSnapshot: iSnapshot, version: undefined };\n\t\t}\n\t}\n\n\tprivate tryRefreshSnapshot(): void {\n\t\tif (\n\t\t\tthis.mc.config.getBoolean(\"Fluid.Container.enableOfflineSnapshotRefresh\") === true &&\n\t\t\tthis._refreshSnapshotP === undefined &&\n\t\t\tthis.latestSnapshot === undefined\n\t\t) {\n\t\t\t// Don't block on the refresh snapshot call - it is for the next time we serialize, not booting this incarnation\n\t\t\tthis._refreshSnapshotP = this.refreshLatestSnapshot(this.supportGetSnapshotApi());\n\t\t\tthis._refreshSnapshotP\n\t\t\t\t.catch(\n\t\t\t\t\t(error: TelemetryEventPropertyTypeExt | Tagged<TelemetryEventPropertyTypeExt>) => {\n\t\t\t\t\t\tthis.mc.logger.sendTelemetryEvent({\n\t\t\t\t\t\t\teventName: \"RefreshLatestSnapshotFailed\",\n\t\t\t\t\t\t\terror,\n\t\t\t\t\t\t});\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\t.finally(() => {\n\t\t\t\t\tthis._refreshSnapshotP = undefined;\n\t\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Fetch the latest snapshot for the container, including delay-loaded groupIds if pendingLocalState was provided and contained any groupIds.\n\t * Note that this will update the StorageAdapter's cached snapshots for the groupIds (if present)\n\t *\n\t * @param supportGetSnapshotApi - a boolean indicating whether to use the fetchISnapshot or fetchISnapshotTree (must be true to fetch by groupIds)\n\t */\n\tprivate async refreshLatestSnapshot(supportGetSnapshotApi: boolean): Promise<number> {\n\t\tthis.latestSnapshot = await getLatestSnapshotInfo(\n\t\t\tthis.mc,\n\t\t\tthis.storageAdapter,\n\t\t\tsupportGetSnapshotApi,\n\t\t);\n\n\t\t// These are loading groupIds that the containerRuntime has requested over its lifetime.\n\t\t// We will fetch the latest snapshot for the groupIds, which will update storageAdapter.loadedGroupIdSnapshots's cache\n\t\tconst downloadedGroupIds = Object.keys(this.storageAdapter.loadedGroupIdSnapshots);\n\t\tif (supportGetSnapshotApi && downloadedGroupIds.length > 0) {\n\t\t\tassert(\n\t\t\t\tthis.storageAdapter.getSnapshot !== undefined,\n\t\t\t\t0x972 /* getSnapshot should exist */,\n\t\t\t);\n\t\t\t// (This is a separate network call from above because it requires work for storage to add a special base groupId)\n\t\t\tconst snapshot = await this.storageAdapter.getSnapshot({\n\t\t\t\tversionId: undefined,\n\t\t\t\tscenarioName: \"getLatestSnapshotInfo\",\n\t\t\t\tcacheSnapshot: false,\n\t\t\t\tloadingGroupIds: downloadedGroupIds,\n\t\t\t\tfetchSource: FetchSource.noCache,\n\t\t\t});\n\t\t\tassert(snapshot !== undefined, 0x973 /* Snapshot should exist */);\n\t\t}\n\n\t\treturn this.updateSnapshotAndProcessedOpsMaybe();\n\t}\n\n\t/**\n\t * Updates class snapshot and processedOps if we have a new snapshot and it's among processedOps range.\n\t */\n\tprivate updateSnapshotAndProcessedOpsMaybe(): number {\n\t\tconst snapshotSequenceNumber = this.latestSnapshot?.snapshotSequenceNumber;\n\t\tif (\n\t\t\tsnapshotSequenceNumber === undefined ||\n\t\t\tthis.processedOps.length === 0 ||\n\t\t\tthis.processedOps[this.processedOps.length - 1].sequenceNumber <\n\t\t\t\tthis.lastSavedOpSequenceNumber ||\n\t\t\tthis.containerDirty()\n\t\t) {\n\t\t\t// can't refresh latest snapshot until we have processed the ops up to it.\n\t\t\t// Pending state would be behind the latest snapshot.\n\t\t\treturn -1;\n\t\t}\n\t\tconst firstProcessedOpSequenceNumber = this.processedOps[0].sequenceNumber;\n\t\tconst lastProcessedOpSequenceNumber =\n\t\t\tthis.processedOps[this.processedOps.length - 1].sequenceNumber;\n\n\t\tif (snapshotSequenceNumber < firstProcessedOpSequenceNumber) {\n\t\t\t// Snapshot seq number is older than our first processed op, which could mean we're fetching\n\t\t\t// the same snapshot that we already have or snapshot is too old, implicating an unexpected behavior.\n\t\t\tthis.mc.logger.sendTelemetryEvent({\n\t\t\t\teventName: \"OldSnapshotFetchWhileRefreshing\",\n\t\t\t\tsnapshotSequenceNumber,\n\t\t\t\tfirstProcessedOpSequenceNumber,\n\t\t\t\tlastProcessedOpSequenceNumber,\n\t\t\t\tstashedSnapshotSequenceNumber: this.snapshot?.snapshotSequenceNumber,\n\t\t\t});\n\t\t\tthis.latestSnapshot = undefined;\n\t\t\tthis.refreshTimer.restart();\n\t\t} else if (snapshotSequenceNumber <= lastProcessedOpSequenceNumber) {\n\t\t\t// Snapshot seq num is between the first and last processed op.\n\t\t\t// Remove the ops that are already part of the snapshot\n\t\t\tthis.processedOps.splice(0, snapshotSequenceNumber - firstProcessedOpSequenceNumber + 1);\n\t\t\tthis.snapshot = this.latestSnapshot;\n\t\t\tthis.latestSnapshot = undefined;\n\t\t\tthis.refreshTimer.restart();\n\t\t\tthis.mc.logger.sendTelemetryEvent({\n\t\t\t\teventName: \"SnapshotRefreshed\",\n\t\t\t\tsnapshotSequenceNumber,\n\t\t\t\tfirstProcessedOpSequenceNumber,\n\t\t\t\tnewFirstProcessedOpSequenceNumber:\n\t\t\t\t\tthis.processedOps.length === 0 ? undefined : this.processedOps[0].sequenceNumber,\n\t\t\t});\n\t\t}\n\t\treturn snapshotSequenceNumber;\n\t}\n\n\t/**\n\t * When the Container attaches, we need to stash the initial snapshot (a form of the attach summary).\n\t * This method is only meant to be used by Container.attach() to set the initial\n\t * base snapshot when attaching.\n\t * @param snapshot - snapshot and blobs collected while attaching (a form of the attach summary)\n\t */\n\tpublic setInitialSnapshot(snapshot: SnapshotWithBlobs | undefined): void {\n\t\tif (this.offlineLoadEnabled) {\n\t\t\tassert(\n\t\t\t\tthis.snapshot === undefined,\n\t\t\t\t0x937 /* inital snapshot should only be defined once */,\n\t\t\t);\n\t\t\tassert(snapshot !== undefined, 0x938 /* attachment snapshot should be defined */);\n\t\t\tconst { baseSnapshot, snapshotBlobs } = snapshot;\n\t\t\tconst attributesHash =\n\t\t\t\t\".protocol\" in baseSnapshot.trees\n\t\t\t\t\t? baseSnapshot.trees[\".protocol\"].blobs.attributes\n\t\t\t\t\t: baseSnapshot.blobs[\".attributes\"];\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n\t\t\tconst attributes = JSON.parse(snapshotBlobs[attributesHash]);\n\t\t\tassert(\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n\t\t\t\tattributes.sequenceNumber === 0,\n\t\t\t\t0x939 /* trying to set a non attachment snapshot */,\n\t\t\t);\n\t\t\tthis.snapshot = {\n\t\t\t\t...snapshot,\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n\t\t\t\tsnapshotSequenceNumber: attributes.sequenceNumber as number,\n\t\t\t};\n\t\t\tthis.refreshTimer.start();\n\t\t}\n\t}\n\n\t/**\n\t * Assembles and serializes the {@link IPendingContainerState} for the container,\n\t * to be stored and used to rehydrate the container at a later time.\n\t */\n\tpublic async getPendingLocalState(\n\t\tprops: IGetPendingLocalStateProps,\n\t\tclientId: string | undefined,\n\t\truntime: Pick<IRuntime, \"getPendingLocalState\">,\n\t\tresolvedUrl: IResolvedUrl,\n\t): Promise<string> {\n\t\treturn PerformanceEvent.timedExecAsync(\n\t\t\tthis.mc.logger,\n\t\t\t{\n\t\t\t\teventName: \"getPendingLocalState\",\n\t\t\t\tdetails: {\n\t\t\t\t\tnotifyImminentClosure: props.notifyImminentClosure,\n\t\t\t\t\tsessionExpiryTimerStarted: props.sessionExpiryTimerStarted,\n\t\t\t\t\tsnapshotSequenceNumber: props.snapshotSequenceNumber,\n\t\t\t\t\tprocessedOpsSize: this.processedOps.length,\n\t\t\t\t},\n\t\t\t\tclientId,\n\t\t\t},\n\t\t\tasync () => {\n\t\t\t\tif (!this.offlineLoadEnabled) {\n\t\t\t\t\tthrow new UsageError(\"Can't get pending local state unless offline load is enabled\");\n\t\t\t\t}\n\t\t\t\tassert(this.snapshot !== undefined, 0x8e5 /* no base data */);\n\t\t\t\tconst pendingRuntimeState = await runtime.getPendingLocalState({\n\t\t\t\t\t...props,\n\t\t\t\t\tsnapshotSequenceNumber: this.snapshot.snapshotSequenceNumber,\n\t\t\t\t\tsessionExpiryTimerStarted: this.snapshot.snapshotFetchedTime,\n\t\t\t\t});\n\t\t\t\t// This conversion is required because ArrayBufferLike doesn't survive JSON.stringify\n\t\t\t\tconst loadedGroupIdSnapshots = {};\n\t\t\t\tlet hasGroupIdSnapshots = false;\n\t\t\t\tconst groupIdSnapshots = Object.entries(this.storageAdapter.loadedGroupIdSnapshots);\n\t\t\t\tif (groupIdSnapshots.length > 0) {\n\t\t\t\t\tfor (const [groupId, snapshot] of groupIdSnapshots) {\n\t\t\t\t\t\thasGroupIdSnapshots = true;\n\t\t\t\t\t\tloadedGroupIdSnapshots[groupId] = convertSnapshotToSnapshotInfo(snapshot);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst pendingState: IPendingContainerState = {\n\t\t\t\t\tattached: true,\n\t\t\t\t\tpendingRuntimeState,\n\t\t\t\t\tbaseSnapshot: this.snapshot.baseSnapshot,\n\t\t\t\t\tsnapshotBlobs: this.snapshot.snapshotBlobs,\n\t\t\t\t\tloadedGroupIdSnapshots: hasGroupIdSnapshots ? loadedGroupIdSnapshots : undefined,\n\t\t\t\t\tsavedOps: this.processedOps,\n\t\t\t\t\turl: resolvedUrl.url,\n\t\t\t\t\tclientId,\n\t\t\t\t};\n\n\t\t\t\treturn JSON.stringify(pendingState);\n\t\t\t},\n\t\t);\n\t}\n}\n\n/**\n * Retrieves the most recent snapshot and returns its info.\n *\n * @param mc - The monitoring context.\n * @param storageAdapter - The storage adapter providing methods to retrieve the snapshot.\n * @param supportGetSnapshotApi - a boolean indicating whether to use the fetchISnapshot or fetchISnapshotTree.\n * @returns a SnapshotInfo object containing the snapshot tree, snapshot blobs and its sequence number.\n */\nexport async function getLatestSnapshotInfo(\n\tmc: MonitoringContext,\n\tstorageAdapter: ISerializedStateManagerDocumentStorageService,\n\tsupportGetSnapshotApi: boolean,\n): Promise<ISnapshotInfo | undefined> {\n\treturn PerformanceEvent.timedExecAsync(\n\t\tmc.logger,\n\t\t{ eventName: \"GetLatestSnapshotInfo\" },\n\t\tasync () => {\n\t\t\t// get the latest non cached snapshot version\n\t\t\tconst specifiedVersion: IVersion[] = await storageAdapter.getVersions(\n\t\t\t\t// eslint-disable-next-line unicorn/no-null\n\t\t\t\tnull,\n\t\t\t\t1,\n\t\t\t\t\"getLatestSnapshotInfo\",\n\t\t\t\tFetchSource.noCache,\n\t\t\t);\n\t\t\tconst { baseSnapshot } = await getSnapshot(\n\t\t\t\tmc,\n\t\t\t\tstorageAdapter,\n\t\t\t\tsupportGetSnapshotApi,\n\t\t\t\tspecifiedVersion[0]?.id,\n\t\t\t);\n\n\t\t\tconst baseSnapshotTree: ISnapshotTree | undefined = getSnapshotTree(baseSnapshot);\n\t\t\tconst snapshotFetchedTime = Date.now();\n\t\t\tconst snapshotBlobs = await getBlobContentsFromTree(baseSnapshot, storageAdapter);\n\t\t\tconst attributes: IDocumentAttributes = await getDocumentAttributes(\n\t\t\t\tstorageAdapter,\n\t\t\t\tbaseSnapshotTree,\n\t\t\t);\n\t\t\tconst snapshotSequenceNumber = attributes.sequenceNumber;\n\t\t\treturn {\n\t\t\t\tbaseSnapshot: baseSnapshotTree,\n\t\t\t\tsnapshotBlobs,\n\t\t\t\tsnapshotSequenceNumber,\n\t\t\t\tsnapshotFetchedTime,\n\t\t\t};\n\t\t},\n\t).catch(() => undefined);\n}\n\n/**\n * Retrieves a snapshot from the storage adapter and transforms it into an ISnapshotTree object.\n *\n * @param mc - The monitoring context.\n * @param storageAdapter - The storage adapter providing methods to retrieve the snapshot.\n * @param supportGetSnapshotApi - a boolean indicating whether to use the fetchISnapshot or fetchISnapshotTree.\n * @param specifiedVersion - An optional version string specifying the version of the snapshot tree to fetch.\n * @returns - An ISnapshotTree and its version.\n */\nasync function getSnapshot(\n\tmc: MonitoringContext,\n\tstorageAdapter: Pick<\n\t\tIDocumentStorageService,\n\t\t\"getSnapshot\" | \"getSnapshotTree\" | \"getVersions\"\n\t>,\n\tsupportGetSnapshotApi: boolean,\n\tspecifiedVersion: string | undefined,\n): Promise<{ baseSnapshot: ISnapshot | ISnapshotTree; version?: IVersion }> {\n\tconst { snapshot, version } = supportGetSnapshotApi\n\t\t? await fetchISnapshot(mc, storageAdapter, specifiedVersion)\n\t\t: await fetchISnapshotTree(mc, storageAdapter, specifiedVersion);\n\tassert(snapshot !== undefined, 0x8e4 /* Snapshot should exist */);\n\treturn { baseSnapshot: snapshot, version };\n}\n\n/**\n * Fetches an ISnapshot from a storage adapter based on the specified version.\n *\n * @param mc - The monitoring context.\n * @param storageAdapter - The storage adapter providing a getSnapshot method to retrieve the ISnapshot and version.\n * @param specifiedVersion - An optional version string specifying the version of the snapshot tree to fetch.\n * @returns - The fetched snapshot tree and its version.\n */\nexport async function fetchISnapshot(\n\tmc: MonitoringContext,\n\tstorageAdapter: Pick<IDocumentStorageService, \"getSnapshot\">,\n\tspecifiedVersion: string | undefined,\n): Promise<{ snapshot?: ISnapshot; version?: IVersion }> {\n\tconst snapshot = await storageAdapter.getSnapshot?.({ versionId: specifiedVersion });\n\tconst version: IVersion | undefined =\n\t\tsnapshot?.snapshotTree.id === undefined\n\t\t\t? undefined\n\t\t\t: {\n\t\t\t\t\tid: snapshot.snapshotTree.id,\n\t\t\t\t\ttreeId: snapshot.snapshotTree.id,\n\t\t\t\t};\n\n\tif (snapshot === undefined && specifiedVersion !== undefined) {\n\t\tmc.logger.sendErrorEvent({\n\t\t\teventName: \"getSnapshotTreeFailed\",\n\t\t\tid: specifiedVersion,\n\t\t});\n\t} else if (snapshot !== undefined && version?.id === undefined) {\n\t\tmc.logger.sendErrorEvent({\n\t\t\teventName: \"getSnapshotFetchedTreeWithoutVersionId\",\n\t\t\thasVersion: version !== undefined, // if hasVersion is true, this means that the contract with the service was broken.\n\t\t});\n\t}\n\treturn { snapshot, version };\n}\n\n/**\n * Fetches an ISnapshotTree from a storage adapter based on the specified version.\n *\n * @param mc - The monitoring context.\n * @param storageAdapter - The storage adapter providing methods to retrieve the ISnapshotTree and version.\n * @param specifiedVersion - An optional version string specifying the version of the snapshot tree to fetch.\n * @returns - The fetched snapshot tree and its version.\n */\nexport async function fetchISnapshotTree(\n\tmc: MonitoringContext,\n\tstorageAdapter: Pick<IDocumentStorageService, \"getSnapshotTree\" | \"getVersions\">,\n\tspecifiedVersion: string | undefined,\n): Promise<{ snapshot?: ISnapshotTree; version?: IVersion | undefined }> {\n\t// API uses null\n\t// eslint-disable-next-line unicorn/no-null\n\tconst versions = await storageAdapter.getVersions(specifiedVersion ?? null, 1);\n\tconst version = versions[0];\n\n\tif (version === undefined && specifiedVersion !== undefined) {\n\t\t// We should have a defined version to load from if specified version requested\n\t\tmc.logger.sendErrorEvent({\n\t\t\teventName: \"NoVersionFoundWhenSpecified\",\n\t\t\tid: specifiedVersion,\n\t\t});\n\t}\n\tconst snapshot = (await storageAdapter.getSnapshotTree(version)) ?? undefined;\n\n\tif (snapshot === undefined && version !== undefined) {\n\t\tmc.logger.sendErrorEvent({ eventName: \"getSnapshotTreeFailed\", id: version.id });\n\t} else if (snapshot !== undefined && version?.id === undefined) {\n\t\tmc.logger.sendErrorEvent({\n\t\t\teventName: \"getSnapshotFetchedTreeWithoutVersionId\",\n\t\t\thasVersion: version !== undefined, // if hasVersion is true, this means that the contract with the service was broken.\n\t\t});\n\t}\n\treturn { snapshot, version };\n}\n"]}
1
+ {"version":3,"file":"serializedStateManager.js","sourceRoot":"","sources":["../src/serializedStateManager.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 { stringToBuffer } from \"@fluid-internal/client-utils\";\nimport { IRuntime } from \"@fluidframework/container-definitions/internal\";\nimport type {\n\tIEventProvider,\n\tIEvent,\n\tITelemetryBaseLogger,\n\tTagged,\n} from \"@fluidframework/core-interfaces\";\nimport { Timer, assert } from \"@fluidframework/core-utils/internal\";\nimport {\n\tFetchSource,\n\tIDocumentStorageService,\n\tIResolvedUrl,\n\tISnapshot,\n\ttype IDocumentAttributes,\n\tISnapshotTree,\n\tIVersion,\n\tISequencedDocumentMessage,\n} from \"@fluidframework/driver-definitions/internal\";\nimport { getSnapshotTree } from \"@fluidframework/driver-utils/internal\";\nimport {\n\tMonitoringContext,\n\tPerformanceEvent,\n\tUsageError,\n\tcreateChildMonitoringContext,\n\ttype TelemetryEventPropertyTypeExt,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport {\n\tISerializableBlobContents,\n\tgetBlobContentsFromTree,\n} from \"./containerStorageAdapter.js\";\nimport { convertSnapshotToSnapshotInfo, getDocumentAttributes } from \"./utils.js\";\n\n/**\n * This is very similar to {@link @fluidframework/protocol-definitions/internal#ISnapshot}, but the difference is\n * that the blobs of ISnapshot are of type ArrayBufferLike, while the blobs of this interface are serializable because\n * they are already converted to string.\n */\nexport interface SnapshotWithBlobs {\n\t/**\n\t * Snapshot from which container initially loaded.\n\t */\n\tbaseSnapshot: ISnapshotTree;\n\t/**\n\t * Serializable blobs from the base snapshot. Used to load offline since\n\t * storage is not available.\n\t */\n\tsnapshotBlobs: ISerializableBlobContents;\n}\n\n/**\n * State saved by a container at close time, to be used to load a new instance\n * of the container to the same state\n *\n * This is very similar to {@link @fluidframework/protocol-definitions/internal#ISnapshot}, but the difference is\n * that the blobs of ISnapshot are of type ArrayBufferLike, while the blobs of this interface are serializable because\n * they are already converted to string.\n *\n * @internal\n */\nexport interface IPendingContainerState extends SnapshotWithBlobs {\n\t/**\n\t * This container was attached (as opposed to IPendingDetachedContainerState.attached which is false)\n\t */\n\tattached: true;\n\t/**\n\t * Runtime-specific state that will be needed to properly rehydrate\n\t * (it's included in ContainerContext passed to instantiateRuntime)\n\t */\n\tpendingRuntimeState: unknown;\n\t/**\n\t * Any group snapshots (aka delay-loaded) we've downloaded from the service for this container\n\t */\n\tloadedGroupIdSnapshots?: Record<string, ISnapshotInfo>;\n\t/**\n\t * All ops since base snapshot sequence number up to the latest op\n\t * seen when the container was closed. Used to apply stashed (saved pending)\n\t * ops at the same sequence number at which they were made.\n\t */\n\tsavedOps: ISequencedDocumentMessage[];\n\t/**\n\t * The Container's URL in the service, needed to hook up the driver during rehydration\n\t */\n\turl: string;\n\t/**\n\t * If the Container was connected when serialized, its clientId. Used as the initial clientId upon rehydration, until reconnected.\n\t */\n\tclientId?: string;\n}\n\n/**\n * State saved by a container in detached state, to be used to load a new instance\n * of the container to the same state (rehydrate)\n * @internal\n */\nexport interface IPendingDetachedContainerState extends SnapshotWithBlobs {\n\t/**\n\t * This container was not attached (as opposed to IPendingContainerState.attached which is true)\n\t */\n\tattached: false;\n\t/**\n\t * Indicates whether we expect the rehydrated container to have non-empty Detached Blob Storage\n\t */\n\thasAttachmentBlobs: boolean;\n\t/**\n\t * Used by the memory blob storage to persisted attachment blobs\n\t */\n\tattachmentBlobs?: string;\n\t/**\n\t * Runtime-specific state that will be needed to properly rehydrate\n\t * (it's included in ContainerContext passed to instantiateRuntime)\n\t */\n\tpendingRuntimeState?: unknown;\n}\n\nexport interface ISnapshotInfo extends SnapshotWithBlobs {\n\tsnapshotSequenceNumber: number;\n\tsnapshotFetchedTime?: number | undefined;\n}\n\nexport type ISerializedStateManagerDocumentStorageService = Pick<\n\tIDocumentStorageService,\n\t\"getSnapshot\" | \"getSnapshotTree\" | \"getVersions\" | \"readBlob\"\n> & {\n\tloadedGroupIdSnapshots: Record<string, ISnapshot>;\n};\n\ninterface ISerializerEvent extends IEvent {\n\t(event: \"saved\", listener: (dirty: boolean) => void): void;\n}\n\n/**\n * Helper class to manage the state of the container needed for proper serialization.\n *\n * It holds the pendingLocalState the container was rehydrated from (if any),\n * as well as the snapshot to be used for serialization.\n * It also keeps track of container dirty state and which local ops have been processed\n */\nexport class SerializedStateManager {\n\tprivate readonly processedOps: ISequencedDocumentMessage[] = [];\n\tprivate readonly mc: MonitoringContext;\n\tprivate snapshot: ISnapshotInfo | undefined;\n\tprivate latestSnapshot: ISnapshotInfo | undefined;\n\tprivate _refreshSnapshotP: Promise<number> | undefined;\n\tprivate readonly lastSavedOpSequenceNumber: number = 0;\n\tprivate readonly refreshTimer: Timer;\n\tprivate readonly snapshotRefreshTimeoutMs: number = 60 * 60 * 24 * 1000;\n\n\t/**\n\t * @param pendingLocalState - The pendingLocalState being rehydrated, if any (undefined when loading directly from storage)\n\t * @param subLogger - Container's logger to use as parent for our logger\n\t * @param storageAdapter - Storage adapter for fetching snapshots\n\t * @param _offlineLoadEnabled - Is serializing/rehydrating containers allowed?\n\t * @param containerEvent - Source of the \"saved\" event when the container has all its pending state uploaded\n\t * @param containerDirty - Is the container \"dirty\"? That's the opposite of \"saved\" - there is pending state that may not have been received yet by the service.\n\t */\n\tconstructor(\n\t\tprivate readonly pendingLocalState: IPendingContainerState | undefined,\n\t\tsubLogger: ITelemetryBaseLogger,\n\t\tprivate readonly storageAdapter: ISerializedStateManagerDocumentStorageService,\n\t\tprivate readonly _offlineLoadEnabled: boolean,\n\t\tcontainerEvent: IEventProvider<ISerializerEvent>,\n\t\tprivate readonly containerDirty: () => boolean,\n\t\tprivate readonly supportGetSnapshotApi: () => boolean,\n\t\tsnapshotRefreshTimeoutMs?: number,\n\t) {\n\t\tthis.mc = createChildMonitoringContext({\n\t\t\tlogger: subLogger,\n\t\t\tnamespace: \"serializedStateManager\",\n\t\t});\n\n\t\tthis.snapshotRefreshTimeoutMs = snapshotRefreshTimeoutMs ?? this.snapshotRefreshTimeoutMs;\n\t\tthis.refreshTimer = new Timer(this.snapshotRefreshTimeoutMs, () =>\n\t\t\tthis.tryRefreshSnapshot(),\n\t\t);\n\t\t// special case handle. Obtaining the last saved op seq num to avoid\n\t\t// refreshing the snapshot before we have processed it. It could cause\n\t\t// a subsequent stashing to have a newer snapshot than allowed.\n\t\tif (pendingLocalState && pendingLocalState.savedOps.length > 0) {\n\t\t\tconst savedOpsSize = pendingLocalState.savedOps.length;\n\t\t\tthis.lastSavedOpSequenceNumber =\n\t\t\t\tpendingLocalState.savedOps[savedOpsSize - 1].sequenceNumber;\n\t\t}\n\t\tcontainerEvent.on(\"saved\", () => this.updateSnapshotAndProcessedOpsMaybe());\n\t}\n\n\tpublic get offlineLoadEnabled(): boolean {\n\t\treturn this._offlineLoadEnabled;\n\t}\n\n\t/**\n\t * Promise that will resolve (or reject) once we've tried to download the latest snapshot(s) from storage\n\t * only intended to be used for testing purposes.\n\t * @returns The snapshot sequence number associated with the latest fetched snapshot\n\t */\n\tpublic get refreshSnapshotP(): Promise<number> | undefined {\n\t\treturn this._refreshSnapshotP;\n\t}\n\n\t/**\n\t * Called whenever an incoming op is processed by the Container\n\t */\n\tpublic addProcessedOp(message: ISequencedDocumentMessage): void {\n\t\tif (this.offlineLoadEnabled) {\n\t\t\tthis.processedOps.push(message);\n\t\t\tthis.updateSnapshotAndProcessedOpsMaybe();\n\t\t}\n\t}\n\n\t/**\n\t * This wraps the basic functionality of fetching the snapshot for this container during Container load.\n\t *\n\t * If we have pendingLocalState, we get the snapshot from there.\n\t * Otherwise, fetch it from storage (according to specifiedVersion if provided).\n\t *\n\t * @param specifiedVersion - If a version is specified and we don't have pendingLocalState, fetch this version from storage.\n\t * @param supportGetSnapshotApi - a boolean indicating whether to use the fetchISnapshot or fetchISnapshotTree.\n\t * @returns The snapshot to boot the container from\n\t */\n\tpublic async fetchSnapshot(specifiedVersion: string | undefined): Promise<{\n\t\tbaseSnapshot: ISnapshot | ISnapshotTree;\n\t\tversion: IVersion | undefined;\n\t}> {\n\t\tif (this.pendingLocalState === undefined) {\n\t\t\tconst { baseSnapshot, version } = await getSnapshot(\n\t\t\t\tthis.mc,\n\t\t\t\tthis.storageAdapter,\n\t\t\t\tthis.supportGetSnapshotApi(),\n\t\t\t\tspecifiedVersion,\n\t\t\t);\n\t\t\tconst baseSnapshotTree: ISnapshotTree | undefined = getSnapshotTree(baseSnapshot);\n\t\t\t// non-interactive clients will not have any pending state we want to save\n\t\t\tif (this.offlineLoadEnabled) {\n\t\t\t\tconst snapshotBlobs = await getBlobContentsFromTree(baseSnapshot, this.storageAdapter);\n\t\t\t\tconst attributes = await getDocumentAttributes(this.storageAdapter, baseSnapshotTree);\n\t\t\t\tthis.snapshot = {\n\t\t\t\t\tbaseSnapshot: baseSnapshotTree,\n\t\t\t\t\tsnapshotBlobs,\n\t\t\t\t\tsnapshotSequenceNumber: attributes.sequenceNumber,\n\t\t\t\t};\n\t\t\t\tthis.refreshTimer.start();\n\t\t\t}\n\t\t\treturn { baseSnapshot, version };\n\t\t} else {\n\t\t\tconst { baseSnapshot, snapshotBlobs } = this.pendingLocalState;\n\t\t\tconst attributes = await getDocumentAttributes(this.storageAdapter, baseSnapshot);\n\t\t\tthis.snapshot = {\n\t\t\t\tbaseSnapshot,\n\t\t\t\tsnapshotBlobs,\n\t\t\t\tsnapshotSequenceNumber: attributes.sequenceNumber,\n\t\t\t};\n\t\t\tthis.tryRefreshSnapshot();\n\t\t\tconst blobContents = new Map<string, ArrayBuffer>();\n\t\t\tfor (const [id, value] of Object.entries(snapshotBlobs)) {\n\t\t\t\tblobContents.set(id, stringToBuffer(value, \"utf8\"));\n\t\t\t}\n\t\t\tconst iSnapshot: ISnapshot = {\n\t\t\t\tsequenceNumber: this.snapshot.snapshotSequenceNumber,\n\t\t\t\tsnapshotTree: baseSnapshot,\n\t\t\t\tblobContents,\n\t\t\t\tlatestSequenceNumber: undefined,\n\t\t\t\tops: [],\n\t\t\t\tsnapshotFormatV: 1,\n\t\t\t};\n\t\t\treturn { baseSnapshot: iSnapshot, version: undefined };\n\t\t}\n\t}\n\n\tprivate tryRefreshSnapshot(): void {\n\t\tif (\n\t\t\tthis.mc.config.getBoolean(\"Fluid.Container.enableOfflineSnapshotRefresh\") === true &&\n\t\t\tthis._refreshSnapshotP === undefined &&\n\t\t\tthis.latestSnapshot === undefined\n\t\t) {\n\t\t\t// Don't block on the refresh snapshot call - it is for the next time we serialize, not booting this incarnation\n\t\t\tthis._refreshSnapshotP = this.refreshLatestSnapshot(this.supportGetSnapshotApi());\n\t\t\tthis._refreshSnapshotP\n\t\t\t\t.catch(\n\t\t\t\t\t(error: TelemetryEventPropertyTypeExt | Tagged<TelemetryEventPropertyTypeExt>) => {\n\t\t\t\t\t\tthis.mc.logger.sendTelemetryEvent({\n\t\t\t\t\t\t\teventName: \"RefreshLatestSnapshotFailed\",\n\t\t\t\t\t\t\terror,\n\t\t\t\t\t\t});\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\t.finally(() => {\n\t\t\t\t\tthis._refreshSnapshotP = undefined;\n\t\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Fetch the latest snapshot for the container, including delay-loaded groupIds if pendingLocalState was provided and contained any groupIds.\n\t * Note that this will update the StorageAdapter's cached snapshots for the groupIds (if present)\n\t *\n\t * @param supportGetSnapshotApi - a boolean indicating whether to use the fetchISnapshot or fetchISnapshotTree (must be true to fetch by groupIds)\n\t */\n\tprivate async refreshLatestSnapshot(supportGetSnapshotApi: boolean): Promise<number> {\n\t\tthis.latestSnapshot = await getLatestSnapshotInfo(\n\t\t\tthis.mc,\n\t\t\tthis.storageAdapter,\n\t\t\tsupportGetSnapshotApi,\n\t\t);\n\n\t\t// These are loading groupIds that the containerRuntime has requested over its lifetime.\n\t\t// We will fetch the latest snapshot for the groupIds, which will update storageAdapter.loadedGroupIdSnapshots's cache\n\t\tconst downloadedGroupIds = Object.keys(this.storageAdapter.loadedGroupIdSnapshots);\n\t\tif (supportGetSnapshotApi && downloadedGroupIds.length > 0) {\n\t\t\tassert(\n\t\t\t\tthis.storageAdapter.getSnapshot !== undefined,\n\t\t\t\t0x972 /* getSnapshot should exist */,\n\t\t\t);\n\t\t\t// (This is a separate network call from above because it requires work for storage to add a special base groupId)\n\t\t\tconst snapshot = await this.storageAdapter.getSnapshot({\n\t\t\t\tversionId: undefined,\n\t\t\t\tscenarioName: \"getLatestSnapshotInfo\",\n\t\t\t\tcacheSnapshot: false,\n\t\t\t\tloadingGroupIds: downloadedGroupIds,\n\t\t\t\tfetchSource: FetchSource.noCache,\n\t\t\t});\n\t\t\tassert(snapshot !== undefined, 0x973 /* Snapshot should exist */);\n\t\t}\n\n\t\treturn this.updateSnapshotAndProcessedOpsMaybe();\n\t}\n\n\t/**\n\t * Updates class snapshot and processedOps if we have a new snapshot and it's among processedOps range.\n\t */\n\tprivate updateSnapshotAndProcessedOpsMaybe(): number {\n\t\tconst snapshotSequenceNumber = this.latestSnapshot?.snapshotSequenceNumber;\n\t\tif (\n\t\t\tsnapshotSequenceNumber === undefined ||\n\t\t\tthis.processedOps.length === 0 ||\n\t\t\tthis.processedOps[this.processedOps.length - 1].sequenceNumber <\n\t\t\t\tthis.lastSavedOpSequenceNumber ||\n\t\t\tthis.containerDirty()\n\t\t) {\n\t\t\t// can't refresh latest snapshot until we have processed the ops up to it.\n\t\t\t// Pending state would be behind the latest snapshot.\n\t\t\treturn -1;\n\t\t}\n\t\tconst firstProcessedOpSequenceNumber = this.processedOps[0].sequenceNumber;\n\t\tconst lastProcessedOpSequenceNumber =\n\t\t\tthis.processedOps[this.processedOps.length - 1].sequenceNumber;\n\n\t\tif (snapshotSequenceNumber < firstProcessedOpSequenceNumber) {\n\t\t\t// Snapshot seq number is older than our first processed op, which could mean we're fetching\n\t\t\t// the same snapshot that we already have or snapshot is too old, implicating an unexpected behavior.\n\t\t\tthis.mc.logger.sendTelemetryEvent({\n\t\t\t\teventName: \"OldSnapshotFetchWhileRefreshing\",\n\t\t\t\tsnapshotSequenceNumber,\n\t\t\t\tfirstProcessedOpSequenceNumber,\n\t\t\t\tlastProcessedOpSequenceNumber,\n\t\t\t\tstashedSnapshotSequenceNumber: this.snapshot?.snapshotSequenceNumber,\n\t\t\t});\n\t\t\tthis.latestSnapshot = undefined;\n\t\t\tthis.refreshTimer.restart();\n\t\t} else if (snapshotSequenceNumber <= lastProcessedOpSequenceNumber) {\n\t\t\t// Snapshot seq num is between the first and last processed op.\n\t\t\t// Remove the ops that are already part of the snapshot\n\t\t\tthis.processedOps.splice(0, snapshotSequenceNumber - firstProcessedOpSequenceNumber + 1);\n\t\t\tthis.snapshot = this.latestSnapshot;\n\t\t\tthis.latestSnapshot = undefined;\n\t\t\tthis.refreshTimer.restart();\n\t\t\tthis.mc.logger.sendTelemetryEvent({\n\t\t\t\teventName: \"SnapshotRefreshed\",\n\t\t\t\tsnapshotSequenceNumber,\n\t\t\t\tfirstProcessedOpSequenceNumber,\n\t\t\t\tnewFirstProcessedOpSequenceNumber:\n\t\t\t\t\tthis.processedOps.length === 0 ? undefined : this.processedOps[0].sequenceNumber,\n\t\t\t});\n\t\t}\n\t\treturn snapshotSequenceNumber;\n\t}\n\n\t/**\n\t * When the Container attaches, we need to stash the initial snapshot (a form of the attach summary).\n\t * This method is only meant to be used by Container.attach() to set the initial\n\t * base snapshot when attaching.\n\t * @param snapshot - snapshot and blobs collected while attaching (a form of the attach summary)\n\t */\n\tpublic setInitialSnapshot(snapshot: SnapshotWithBlobs | undefined): void {\n\t\tif (this.offlineLoadEnabled) {\n\t\t\tassert(\n\t\t\t\tthis.snapshot === undefined,\n\t\t\t\t0x937 /* inital snapshot should only be defined once */,\n\t\t\t);\n\t\t\tassert(snapshot !== undefined, 0x938 /* attachment snapshot should be defined */);\n\t\t\tconst { baseSnapshot, snapshotBlobs } = snapshot;\n\t\t\tconst attributesHash =\n\t\t\t\t\".protocol\" in baseSnapshot.trees\n\t\t\t\t\t? baseSnapshot.trees[\".protocol\"].blobs.attributes\n\t\t\t\t\t: baseSnapshot.blobs[\".attributes\"];\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n\t\t\tconst attributes = JSON.parse(snapshotBlobs[attributesHash]);\n\t\t\tassert(\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n\t\t\t\tattributes.sequenceNumber === 0,\n\t\t\t\t0x939 /* trying to set a non attachment snapshot */,\n\t\t\t);\n\t\t\tthis.snapshot = {\n\t\t\t\t...snapshot,\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n\t\t\t\tsnapshotSequenceNumber: attributes.sequenceNumber as number,\n\t\t\t};\n\t\t\tthis.refreshTimer.start();\n\t\t}\n\t}\n\n\t/**\n\t * Assembles and serializes the {@link IPendingContainerState} for the container,\n\t * to be stored and used to rehydrate the container at a later time.\n\t */\n\tpublic async getPendingLocalState(\n\t\tclientId: string | undefined,\n\t\truntime: Pick<IRuntime, \"getPendingLocalState\">,\n\t\tresolvedUrl: IResolvedUrl,\n\t): Promise<string> {\n\t\treturn PerformanceEvent.timedExecAsync(\n\t\t\tthis.mc.logger,\n\t\t\t{\n\t\t\t\teventName: \"getPendingLocalState\",\n\t\t\t\tdetails: {\n\t\t\t\t\tnotifyImminentClosure: false,\n\t\t\t\t\tsessionExpiryTimerStarted: undefined,\n\t\t\t\t\tsnapshotSequenceNumber: undefined,\n\t\t\t\t\tprocessedOpsSize: this.processedOps.length,\n\t\t\t\t},\n\t\t\t\tclientId,\n\t\t\t},\n\t\t\tasync () => {\n\t\t\t\tif (!this.offlineLoadEnabled) {\n\t\t\t\t\tthrow new UsageError(\"Can't get pending local state unless offline load is enabled\");\n\t\t\t\t}\n\t\t\t\tassert(this.snapshot !== undefined, 0x8e5 /* no base data */);\n\t\t\t\tconst pendingRuntimeState = await runtime.getPendingLocalState({\n\t\t\t\t\tnotifyImminentClosure: false,\n\t\t\t\t\tsnapshotSequenceNumber: this.snapshot.snapshotSequenceNumber,\n\t\t\t\t\tsessionExpiryTimerStarted: this.snapshot.snapshotFetchedTime,\n\t\t\t\t});\n\t\t\t\t// This conversion is required because ArrayBufferLike doesn't survive JSON.stringify\n\t\t\t\tconst loadedGroupIdSnapshots = {};\n\t\t\t\tlet hasGroupIdSnapshots = false;\n\t\t\t\tconst groupIdSnapshots = Object.entries(this.storageAdapter.loadedGroupIdSnapshots);\n\t\t\t\tif (groupIdSnapshots.length > 0) {\n\t\t\t\t\tfor (const [groupId, snapshot] of groupIdSnapshots) {\n\t\t\t\t\t\thasGroupIdSnapshots = true;\n\t\t\t\t\t\tloadedGroupIdSnapshots[groupId] = convertSnapshotToSnapshotInfo(snapshot);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst pendingState: IPendingContainerState = {\n\t\t\t\t\tattached: true,\n\t\t\t\t\tpendingRuntimeState,\n\t\t\t\t\tbaseSnapshot: this.snapshot.baseSnapshot,\n\t\t\t\t\tsnapshotBlobs: this.snapshot.snapshotBlobs,\n\t\t\t\t\tloadedGroupIdSnapshots: hasGroupIdSnapshots ? loadedGroupIdSnapshots : undefined,\n\t\t\t\t\tsavedOps: this.processedOps,\n\t\t\t\t\turl: resolvedUrl.url,\n\t\t\t\t\tclientId,\n\t\t\t\t};\n\n\t\t\t\treturn JSON.stringify(pendingState);\n\t\t\t},\n\t\t);\n\t}\n}\n\n/**\n * Retrieves the most recent snapshot and returns its info.\n *\n * @param mc - The monitoring context.\n * @param storageAdapter - The storage adapter providing methods to retrieve the snapshot.\n * @param supportGetSnapshotApi - a boolean indicating whether to use the fetchISnapshot or fetchISnapshotTree.\n * @returns a SnapshotInfo object containing the snapshot tree, snapshot blobs and its sequence number.\n */\nexport async function getLatestSnapshotInfo(\n\tmc: MonitoringContext,\n\tstorageAdapter: ISerializedStateManagerDocumentStorageService,\n\tsupportGetSnapshotApi: boolean,\n): Promise<ISnapshotInfo | undefined> {\n\treturn PerformanceEvent.timedExecAsync(\n\t\tmc.logger,\n\t\t{ eventName: \"GetLatestSnapshotInfo\" },\n\t\tasync () => {\n\t\t\t// get the latest non cached snapshot version\n\t\t\tconst specifiedVersion: IVersion[] = await storageAdapter.getVersions(\n\t\t\t\t// eslint-disable-next-line unicorn/no-null\n\t\t\t\tnull,\n\t\t\t\t1,\n\t\t\t\t\"getLatestSnapshotInfo\",\n\t\t\t\tFetchSource.noCache,\n\t\t\t);\n\t\t\tconst { baseSnapshot } = await getSnapshot(\n\t\t\t\tmc,\n\t\t\t\tstorageAdapter,\n\t\t\t\tsupportGetSnapshotApi,\n\t\t\t\tspecifiedVersion[0]?.id,\n\t\t\t);\n\n\t\t\tconst baseSnapshotTree: ISnapshotTree | undefined = getSnapshotTree(baseSnapshot);\n\t\t\tconst snapshotFetchedTime = Date.now();\n\t\t\tconst snapshotBlobs = await getBlobContentsFromTree(baseSnapshot, storageAdapter);\n\t\t\tconst attributes: IDocumentAttributes = await getDocumentAttributes(\n\t\t\t\tstorageAdapter,\n\t\t\t\tbaseSnapshotTree,\n\t\t\t);\n\t\t\tconst snapshotSequenceNumber = attributes.sequenceNumber;\n\t\t\treturn {\n\t\t\t\tbaseSnapshot: baseSnapshotTree,\n\t\t\t\tsnapshotBlobs,\n\t\t\t\tsnapshotSequenceNumber,\n\t\t\t\tsnapshotFetchedTime,\n\t\t\t};\n\t\t},\n\t).catch(() => undefined);\n}\n\n/**\n * Retrieves a snapshot from the storage adapter and transforms it into an ISnapshotTree object.\n *\n * @param mc - The monitoring context.\n * @param storageAdapter - The storage adapter providing methods to retrieve the snapshot.\n * @param supportGetSnapshotApi - a boolean indicating whether to use the fetchISnapshot or fetchISnapshotTree.\n * @param specifiedVersion - An optional version string specifying the version of the snapshot tree to fetch.\n * @returns - An ISnapshotTree and its version.\n */\nasync function getSnapshot(\n\tmc: MonitoringContext,\n\tstorageAdapter: Pick<\n\t\tIDocumentStorageService,\n\t\t\"getSnapshot\" | \"getSnapshotTree\" | \"getVersions\"\n\t>,\n\tsupportGetSnapshotApi: boolean,\n\tspecifiedVersion: string | undefined,\n): Promise<{ baseSnapshot: ISnapshot | ISnapshotTree; version?: IVersion }> {\n\tconst { snapshot, version } = supportGetSnapshotApi\n\t\t? await fetchISnapshot(mc, storageAdapter, specifiedVersion)\n\t\t: await fetchISnapshotTree(mc, storageAdapter, specifiedVersion);\n\tassert(snapshot !== undefined, 0x8e4 /* Snapshot should exist */);\n\treturn { baseSnapshot: snapshot, version };\n}\n\n/**\n * Fetches an ISnapshot from a storage adapter based on the specified version.\n *\n * @param mc - The monitoring context.\n * @param storageAdapter - The storage adapter providing a getSnapshot method to retrieve the ISnapshot and version.\n * @param specifiedVersion - An optional version string specifying the version of the snapshot tree to fetch.\n * @returns - The fetched snapshot tree and its version.\n */\nexport async function fetchISnapshot(\n\tmc: MonitoringContext,\n\tstorageAdapter: Pick<IDocumentStorageService, \"getSnapshot\">,\n\tspecifiedVersion: string | undefined,\n): Promise<{ snapshot?: ISnapshot; version?: IVersion }> {\n\tconst snapshot = await storageAdapter.getSnapshot?.({ versionId: specifiedVersion });\n\tconst version: IVersion | undefined =\n\t\tsnapshot?.snapshotTree.id === undefined\n\t\t\t? undefined\n\t\t\t: {\n\t\t\t\t\tid: snapshot.snapshotTree.id,\n\t\t\t\t\ttreeId: snapshot.snapshotTree.id,\n\t\t\t\t};\n\n\tif (snapshot === undefined && specifiedVersion !== undefined) {\n\t\tmc.logger.sendErrorEvent({\n\t\t\teventName: \"getSnapshotTreeFailed\",\n\t\t\tid: specifiedVersion,\n\t\t});\n\t} else if (snapshot !== undefined && version?.id === undefined) {\n\t\tmc.logger.sendErrorEvent({\n\t\t\teventName: \"getSnapshotFetchedTreeWithoutVersionId\",\n\t\t\thasVersion: version !== undefined, // if hasVersion is true, this means that the contract with the service was broken.\n\t\t});\n\t}\n\treturn { snapshot, version };\n}\n\n/**\n * Fetches an ISnapshotTree from a storage adapter based on the specified version.\n *\n * @param mc - The monitoring context.\n * @param storageAdapter - The storage adapter providing methods to retrieve the ISnapshotTree and version.\n * @param specifiedVersion - An optional version string specifying the version of the snapshot tree to fetch.\n * @returns - The fetched snapshot tree and its version.\n */\nexport async function fetchISnapshotTree(\n\tmc: MonitoringContext,\n\tstorageAdapter: Pick<IDocumentStorageService, \"getSnapshotTree\" | \"getVersions\">,\n\tspecifiedVersion: string | undefined,\n): Promise<{ snapshot?: ISnapshotTree; version?: IVersion | undefined }> {\n\t// API uses null\n\t// eslint-disable-next-line unicorn/no-null\n\tconst versions = await storageAdapter.getVersions(specifiedVersion ?? null, 1);\n\tconst version = versions[0];\n\n\tif (version === undefined && specifiedVersion !== undefined) {\n\t\t// We should have a defined version to load from if specified version requested\n\t\tmc.logger.sendErrorEvent({\n\t\t\teventName: \"NoVersionFoundWhenSpecified\",\n\t\t\tid: specifiedVersion,\n\t\t});\n\t}\n\tconst snapshot = (await storageAdapter.getSnapshotTree(version)) ?? undefined;\n\n\tif (snapshot === undefined && version !== undefined) {\n\t\tmc.logger.sendErrorEvent({ eventName: \"getSnapshotTreeFailed\", id: version.id });\n\t} else if (snapshot !== undefined && version?.id === undefined) {\n\t\tmc.logger.sendErrorEvent({\n\t\t\teventName: \"getSnapshotFetchedTreeWithoutVersionId\",\n\t\t\thasVersion: version !== undefined, // if hasVersion is true, this means that the contract with the service was broken.\n\t\t});\n\t}\n\treturn { snapshot, version };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/container-loader",
3
- "version": "2.51.0",
3
+ "version": "2.53.0-350190",
4
4
  "description": "Fluid container loader",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -119,13 +119,13 @@
119
119
  "temp-directory": "nyc/.nyc_output"
120
120
  },
121
121
  "dependencies": {
122
- "@fluid-internal/client-utils": "~2.51.0",
123
- "@fluidframework/container-definitions": "~2.51.0",
124
- "@fluidframework/core-interfaces": "~2.51.0",
125
- "@fluidframework/core-utils": "~2.51.0",
126
- "@fluidframework/driver-definitions": "~2.51.0",
127
- "@fluidframework/driver-utils": "~2.51.0",
128
- "@fluidframework/telemetry-utils": "~2.51.0",
122
+ "@fluid-internal/client-utils": "2.53.0-350190",
123
+ "@fluidframework/container-definitions": "2.53.0-350190",
124
+ "@fluidframework/core-interfaces": "2.53.0-350190",
125
+ "@fluidframework/core-utils": "2.53.0-350190",
126
+ "@fluidframework/driver-definitions": "2.53.0-350190",
127
+ "@fluidframework/driver-utils": "2.53.0-350190",
128
+ "@fluidframework/telemetry-utils": "2.53.0-350190",
129
129
  "@types/events_pkg": "npm:@types/events@^3.0.0",
130
130
  "@ungap/structured-clone": "^1.2.0",
131
131
  "debug": "^4.3.4",
@@ -136,13 +136,13 @@
136
136
  "devDependencies": {
137
137
  "@arethetypeswrong/cli": "^0.17.1",
138
138
  "@biomejs/biome": "~1.9.3",
139
- "@fluid-internal/client-utils": "~2.51.0",
140
- "@fluid-internal/mocha-test-setup": "~2.51.0",
141
- "@fluid-private/test-loader-utils": "~2.51.0",
142
- "@fluid-tools/build-cli": "^0.56.0",
139
+ "@fluid-internal/client-utils": "2.53.0-350190",
140
+ "@fluid-internal/mocha-test-setup": "2.53.0-350190",
141
+ "@fluid-private/test-loader-utils": "2.53.0-350190",
142
+ "@fluid-tools/build-cli": "^0.57.0",
143
143
  "@fluidframework/build-common": "^2.0.3",
144
- "@fluidframework/build-tools": "^0.56.0",
145
- "@fluidframework/container-loader-previous": "npm:@fluidframework/container-loader@2.50.0",
144
+ "@fluidframework/build-tools": "^0.57.0",
145
+ "@fluidframework/container-loader-previous": "npm:@fluidframework/container-loader@2.52.0",
146
146
  "@fluidframework/eslint-config-fluid": "^5.7.4",
147
147
  "@microsoft/api-extractor": "7.52.8",
148
148
  "@types/debug": "^4.1.5",
@@ -248,6 +248,12 @@ export class ConnectionManager implements IConnectionManager {
248
248
 
249
249
  private connectFirstConnection = true;
250
250
 
251
+ /**
252
+ * Tracks the initial connection start time for the entire lifetime of the class.
253
+ * This is used only for retryConnectionTimeoutMs checks and is never reset.
254
+ */
255
+ private initialConnectionStartTime: number | undefined;
256
+
251
257
  private _connectionVerboseProps: Record<string, string | number> = {};
252
258
 
253
259
  private _connectionProps: ITelemetryBaseProperties = {};
@@ -406,6 +412,7 @@ export class ConnectionManager implements IConnectionManager {
406
412
  reconnectAllowed: boolean,
407
413
  private readonly logger: ITelemetryLoggerExt,
408
414
  private readonly props: IConnectionManagerFactoryArgs,
415
+ private readonly retryConnectionTimeoutMs?: number,
409
416
  ) {
410
417
  this.clientDetails = this.client.details;
411
418
  this.defaultReconnectionMode = this.client.mode;
@@ -584,6 +591,12 @@ export class ConnectionManager implements IConnectionManager {
584
591
  let delayMs = InitialReconnectDelayInMs;
585
592
  let connectRepeatCount = 0;
586
593
  const connectStartTime = performanceNow();
594
+
595
+ // Set the initial connection start time only once for the entire lifetime of the class
596
+ if (this.initialConnectionStartTime === undefined) {
597
+ this.initialConnectionStartTime = connectStartTime;
598
+ }
599
+
587
600
  let lastError: unknown;
588
601
 
589
602
  const abortController = new AbortController();
@@ -704,6 +717,31 @@ export class ConnectionManager implements IConnectionManager {
704
717
  this.props.reconnectionDelayHandler(delayMs, origError);
705
718
  }
706
719
 
720
+ // Check if the calculated delay would exceed the remaining timeout with a conservative buffer
721
+ if (this.retryConnectionTimeoutMs !== undefined) {
722
+ const elapsedTime = performanceNow() - this.initialConnectionStartTime;
723
+ const remainingTime = this.retryConnectionTimeoutMs - elapsedTime;
724
+ const connectionAttemptDuration = performanceNow() - connectStartTime;
725
+
726
+ // Use a 75% buffer to be conservative about timeout usage
727
+ const timeoutBufferThreshold = remainingTime * 0.75;
728
+ // Estimate the next attempt time as the delay plus the time it took to connect
729
+ const estimatedNextAttemptTime = delayMs + connectionAttemptDuration;
730
+
731
+ if (estimatedNextAttemptTime >= timeoutBufferThreshold) {
732
+ this.logger.sendTelemetryEvent({
733
+ eventName: "RetryDelayExceedsConnectionTimeout",
734
+ attempts: connectRepeatCount,
735
+ duration: formatTick(elapsedTime),
736
+ calculatedDelayMs: delayMs,
737
+ remainingTimeMs: remainingTime,
738
+ timeoutMs: this.retryConnectionTimeoutMs,
739
+ });
740
+ // Throw the error immediately since the estimated time for delay + next connection attempt would exceed our conservative timeout buffer
741
+ throw normalizeError(origError, { props: fatalConnectErrorProp });
742
+ }
743
+ }
744
+
707
745
  await new Promise<void>((resolve) => {
708
746
  setTimeout(resolve, delayMs);
709
747
  });
package/src/container.ts CHANGED
@@ -26,7 +26,6 @@ import type {
26
26
  IFluidCodeDetails,
27
27
  IFluidCodeDetailsComparer,
28
28
  IFluidModuleWithDetails,
29
- IGetPendingLocalStateProps,
30
29
  IProvideFluidCodeDetailsComparer,
31
30
  IProvideRuntimeFactory,
32
31
  IRuntime,
@@ -246,6 +245,16 @@ export interface IContainerCreateProps {
246
245
  * protocol implementation for handling the quorum and/or the audience.
247
246
  */
248
247
  readonly protocolHandlerBuilder?: ProtocolHandlerBuilder;
248
+
249
+ /**
250
+ * Optional property for specifying a timeout for retry connection loop.
251
+ *
252
+ * If provided, container will use this value as the maximum time to wait
253
+ * for a successful connection before giving up throwing the most recent error.
254
+ *
255
+ * If not provided, default behavior will be to retry until non-retryable error occurs.
256
+ */
257
+ readonly retryConnectionTimeoutMs?: number;
249
258
  }
250
259
 
251
260
  /**
@@ -608,6 +617,7 @@ export class Container
608
617
  private attachmentData: AttachmentData = { state: AttachState.Detached };
609
618
  private readonly serializedStateManager: SerializedStateManager;
610
619
  private readonly _containerId: string;
620
+ private readonly _retryConnectionTimeoutMs: number | undefined;
611
621
 
612
622
  private lastVisible: number | undefined;
613
623
  private readonly visibilityEventHandler: (() => void) | undefined;
@@ -790,12 +800,22 @@ export class Container
790
800
  scope,
791
801
  subLogger,
792
802
  protocolHandlerBuilder,
803
+ retryConnectionTimeoutMs,
793
804
  } = createProps;
794
805
 
806
+ // Validate that the Driver is compatible with this Loader.
807
+ const maybeDriverCompatDetails =
808
+ documentServiceFactory as FluidObject<ILayerCompatDetails>;
809
+ validateDriverCompatibility(
810
+ maybeDriverCompatDetails.ILayerCompatDetails,
811
+ (error) => {} /* disposeFn */, // There is nothing to dispose here, so just ignore the error.
812
+ );
813
+
795
814
  this.connectionTransitionTimes[ConnectionState.Disconnected] = performanceNow();
796
815
  const pendingLocalState = loadProps?.pendingLocalState;
797
816
 
798
817
  this._canReconnect = canReconnect ?? true;
818
+ this._retryConnectionTimeoutMs = retryConnectionTimeoutMs;
799
819
  this.clientDetailsOverride = clientDetailsOverride;
800
820
  this.urlResolver = urlResolver;
801
821
  this.serviceFactory = documentServiceFactory;
@@ -1178,30 +1198,12 @@ export class Container
1178
1198
  }
1179
1199
  }
1180
1200
 
1181
- public async closeAndGetPendingLocalState(
1182
- stopBlobAttachingSignal?: AbortSignal,
1183
- ): Promise<string> {
1184
- // runtime matches pending ops to successful ones by clientId and client seq num, so we need to close the
1185
- // container at the same time we get pending state, otherwise this container could reconnect and resubmit with
1186
- // a new clientId and a future container using stale pending state without the new clientId would resubmit them
1187
- const pendingState = await this.getPendingLocalStateCore({
1188
- notifyImminentClosure: true,
1189
- stopBlobAttachingSignal,
1190
- });
1191
- this.close();
1192
- return pendingState;
1193
- }
1194
-
1195
1201
  /**
1196
1202
  * Serialize current container state required to rehydrate to the same position without dataloss.
1197
1203
  * Note: The container must already be attached. For detached containers use {@link serialize}
1198
1204
  * @returns stringified {@link IPendingContainerState} for the container
1199
1205
  */
1200
1206
  public async getPendingLocalState(): Promise<string> {
1201
- return this.getPendingLocalStateCore({ notifyImminentClosure: false });
1202
- }
1203
-
1204
- private async getPendingLocalStateCore(props: IGetPendingLocalStateProps): Promise<string> {
1205
1207
  if (this.closed || this._disposed) {
1206
1208
  throw new UsageError(
1207
1209
  "Pending state cannot be retried if the container is closed or disposed",
@@ -1216,7 +1218,6 @@ export class Container
1216
1218
  0x0d2 /* "resolved url should be valid Fluid url" */,
1217
1219
  );
1218
1220
  const pendingState = await this.serializedStateManager.getPendingLocalState(
1219
- props,
1220
1221
  this.clientId,
1221
1222
  this.runtime,
1222
1223
  this.resolvedUrl,
@@ -1231,7 +1232,7 @@ export class Container
1231
1232
  /**
1232
1233
  * Serialize current container state required to rehydrate to the same position without dataloss.
1233
1234
  * Note: The container must be detached and not closed. For attached containers use
1234
- * {@link getPendingLocalState} or {@link closeAndGetPendingLocalState}
1235
+ * {@link getPendingLocalState}
1235
1236
  * @returns stringified {@link IPendingDetachedContainerState} for the container
1236
1237
  */
1237
1238
  public serialize(): string {
@@ -1619,13 +1620,6 @@ export class Container
1619
1620
  }, // progress
1620
1621
  );
1621
1622
  }
1622
-
1623
- // Validate that the Driver is compatible with this Loader.
1624
- const maybeDriverCompatDetails = service as FluidObject<ILayerCompatDetails>;
1625
- validateDriverCompatibility(maybeDriverCompatDetails.ILayerCompatDetails, (error) =>
1626
- this.dispose(error),
1627
- );
1628
-
1629
1623
  return service;
1630
1624
  }
1631
1625
 
@@ -2061,6 +2055,7 @@ export class Container
2061
2055
  this._canReconnect,
2062
2056
  createChildLogger({ logger: this.subLogger, namespace: "ConnectionManager" }),
2063
2057
  props,
2058
+ this._retryConnectionTimeoutMs,
2064
2059
  ),
2065
2060
  );
2066
2061
 
@@ -2444,37 +2439,39 @@ export class Container
2444
2439
 
2445
2440
  const existing = snapshotTree !== undefined;
2446
2441
 
2447
- const context = new ContainerContext(
2448
- this.options,
2449
- this.scope,
2450
- snapshotTree,
2451
- this._loadedFromVersion,
2452
- this._deltaManager,
2453
- this.storageAdapter,
2454
- this.protocolHandler.quorum,
2455
- this.protocolHandler.audience,
2442
+ const context = new ContainerContext({
2443
+ options: this.options,
2444
+ scope: this.scope,
2445
+ baseSnapshot: snapshotTree,
2446
+ version: this._loadedFromVersion,
2447
+ deltaManager: this._deltaManager,
2448
+ storage: this.storageAdapter,
2449
+ quorum: this.protocolHandler.quorum,
2450
+ audience: this.protocolHandler.audience,
2456
2451
  loader,
2457
- (type, contents, batch, metadata) =>
2452
+ submitFn: (type, contents, batch, metadata) =>
2458
2453
  this.submitContainerMessage(type, contents, batch, metadata),
2459
- (summaryOp: ISummaryContent, referenceSequenceNumber?: number) =>
2454
+ submitSummaryFn: (summaryOp: ISummaryContent, referenceSequenceNumber?: number) =>
2460
2455
  this.submitSummaryMessage(summaryOp, referenceSequenceNumber),
2461
- (batch: IBatchMessage[], referenceSequenceNumber?: number) =>
2456
+ submitBatchFn: (batch: IBatchMessage[], referenceSequenceNumber?: number) =>
2462
2457
  this.submitBatch(batch, referenceSequenceNumber),
2463
- (content, targetClientId) => this.submitSignal(content, targetClientId),
2464
- (error?: ICriticalContainerError) => this.dispose(error),
2465
- (error?: ICriticalContainerError) => this.close(error),
2466
- this.updateDirtyContainerState,
2467
- this.getAbsoluteUrl,
2468
- () => this.resolvedUrl?.id,
2469
- () => this.clientId,
2470
- () => this.attachState,
2471
- () => this.connected,
2472
- this._deltaManager.clientDetails,
2458
+ submitSignalFn: (content, targetClientId) =>
2459
+ this.submitSignal(content, targetClientId),
2460
+ disposeFn: (error?: ICriticalContainerError) => this.dispose(error),
2461
+ closeFn: (error?: ICriticalContainerError) => this.close(error),
2462
+ updateDirtyContainerState: this.updateDirtyContainerState,
2463
+ getAbsoluteUrl: this.getAbsoluteUrl,
2464
+ getContainerDiagnosticId: () => this.resolvedUrl?.id,
2465
+ getClientId: () => this.clientId,
2466
+ getAttachState: () => this.attachState,
2467
+ getConnected: () => this.connected,
2468
+ getConnectionState: () => this.connectionState,
2469
+ clientDetails: this._deltaManager.clientDetails,
2473
2470
  existing,
2474
- this.subLogger,
2471
+ taggedLogger: this.subLogger,
2475
2472
  pendingLocalState,
2476
- snapshot,
2477
- );
2473
+ snapshotWithContents: snapshot,
2474
+ });
2478
2475
 
2479
2476
  const runtime = await PerformanceEvent.timedExecAsync(
2480
2477
  this.subLogger,
@@ -2572,10 +2569,4 @@ export interface IContainerExperimental extends IContainer {
2572
2569
  * @returns serialized blob that can be passed to Loader.resolve()
2573
2570
  */
2574
2571
  getPendingLocalState?(): Promise<string>;
2575
-
2576
- /**
2577
- * Closes the container and returns serialized local state intended to be
2578
- * given to a newly loaded container.
2579
- */
2580
- closeAndGetPendingLocalState?(stopBlobAttachingSignal?: AbortSignal): Promise<string>;
2581
2572
  }