@fluidframework/container-runtime 2.51.0 → 2.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/api-report/container-runtime.legacy.alpha.api.md +1 -2
- package/container-runtime.test-files.tar +0 -0
- package/dist/blobManager/blobManager.d.ts +15 -7
- package/dist/blobManager/blobManager.d.ts.map +1 -1
- package/dist/blobManager/blobManager.js +72 -186
- package/dist/blobManager/blobManager.js.map +1 -1
- package/dist/containerCompatibility.d.ts +34 -0
- package/dist/containerCompatibility.d.ts.map +1 -0
- package/dist/containerCompatibility.js +125 -0
- package/dist/containerCompatibility.js.map +1 -0
- package/dist/containerRuntime.d.ts +27 -15
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +175 -136
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts +6 -6
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/metadata.d.ts +3 -2
- package/dist/metadata.d.ts.map +1 -1
- package/dist/metadata.js +7 -1
- package/dist/metadata.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/storageServiceWithAttachBlobs.d.ts +40 -5
- package/dist/storageServiceWithAttachBlobs.d.ts.map +1 -1
- package/dist/storageServiceWithAttachBlobs.js +56 -5
- package/dist/storageServiceWithAttachBlobs.js.map +1 -1
- package/dist/summary/documentSchema.d.ts +1 -1
- package/dist/summary/documentSchema.d.ts.map +1 -1
- package/dist/summary/documentSchema.js.map +1 -1
- package/dist/summary/summaryFormat.d.ts +3 -3
- package/dist/summary/summaryFormat.d.ts.map +1 -1
- package/dist/summary/summaryFormat.js.map +1 -1
- package/lib/blobManager/blobManager.d.ts +15 -7
- package/lib/blobManager/blobManager.d.ts.map +1 -1
- package/lib/blobManager/blobManager.js +39 -153
- package/lib/blobManager/blobManager.js.map +1 -1
- package/lib/containerCompatibility.d.ts +34 -0
- package/lib/containerCompatibility.d.ts.map +1 -0
- package/lib/containerCompatibility.js +120 -0
- package/lib/containerCompatibility.js.map +1 -0
- package/lib/containerRuntime.d.ts +27 -15
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +103 -64
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts +6 -6
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +1 -1
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/index.d.ts +5 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/metadata.d.ts +3 -2
- package/lib/metadata.d.ts.map +1 -1
- package/lib/metadata.js +5 -0
- package/lib/metadata.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/storageServiceWithAttachBlobs.d.ts +40 -5
- package/lib/storageServiceWithAttachBlobs.d.ts.map +1 -1
- package/lib/storageServiceWithAttachBlobs.js +56 -5
- package/lib/storageServiceWithAttachBlobs.js.map +1 -1
- package/lib/summary/documentSchema.d.ts +1 -1
- package/lib/summary/documentSchema.d.ts.map +1 -1
- package/lib/summary/documentSchema.js.map +1 -1
- package/lib/summary/summaryFormat.d.ts +3 -3
- package/lib/summary/summaryFormat.d.ts.map +1 -1
- package/lib/summary/summaryFormat.js.map +1 -1
- package/package.json +20 -20
- package/src/blobManager/blobManager.ts +53 -195
- package/src/containerCompatibility.ts +176 -0
- package/src/containerRuntime.ts +157 -122
- package/src/dataStoreContext.ts +13 -5
- package/src/index.ts +6 -1
- package/src/metadata.ts +10 -2
- package/src/packageVersion.ts +1 -1
- package/src/storageServiceWithAttachBlobs.ts +92 -10
- package/src/summary/documentSchema.ts +1 -1
- package/src/summary/summaryFormat.ts +2 -2
- package/dist/compatUtils.d.ts +0 -106
- package/dist/compatUtils.d.ts.map +0 -1
- package/dist/compatUtils.js +0 -251
- package/dist/compatUtils.js.map +0 -1
- package/lib/compatUtils.d.ts +0 -106
- package/lib/compatUtils.d.ts.map +0 -1
- package/lib/compatUtils.js +0 -242
- package/lib/compatUtils.js.map +0 -1
- package/src/compatUtils.ts +0 -365
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"summaryFormat.js","sourceRoot":"","sources":["../../src/summary/summaryFormat.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 { assert } from \"@fluidframework/core-utils/internal\";\nimport { SummaryType } from \"@fluidframework/driver-definitions\";\nimport {\n\tIDocumentStorageService,\n\tISnapshotTree,\n\tISequencedDocumentMessage,\n} from \"@fluidframework/driver-definitions/internal\";\nimport { readAndParse } from \"@fluidframework/driver-utils/internal\";\nimport {\n\tISummaryTreeWithStats,\n\tchannelsTreeName,\n\tgcTreeKey,\n} from \"@fluidframework/runtime-definitions/internal\";\n\nimport { blobsTreeName } from \"../blobManager/index.js\";\nimport { IGCMetadata } from \"../gc/index.js\";\n\nimport { IDocumentSchema } from \"./documentSchema.js\";\n\n/**\n * @deprecated - This interface will no longer be exported in the future(AB#8004).\n * @legacy\n * @alpha\n */\nexport type OmitAttributesVersions<T> = Omit<\n\tT,\n\t\"snapshotFormatVersion\" | \"summaryFormatVersion\"\n>;\n\n/**\n * @deprecated - This interface will no longer be exported in the future(AB#8004).\n * @legacy\n * @alpha\n */\nexport interface IFluidDataStoreAttributes0 {\n\treadonly snapshotFormatVersion?: undefined;\n\treadonly summaryFormatVersion?: undefined;\n\tpkg: string;\n\t/**\n\t * This tells whether a data store is root. Root data stores are never collected.\n\t * Non-root data stores may be collected if they are not used. If this is not present, default it to\n\t * true. This will ensure that older data stores are incorrectly collected.\n\t */\n\treadonly isRootDataStore?: boolean;\n}\n\n/**\n * @deprecated - This interface will no longer be exported in the future(AB#8004).\n * @legacy\n * @alpha\n */\nexport interface IFluidDataStoreAttributes1\n\textends OmitAttributesVersions<IFluidDataStoreAttributes0> {\n\treadonly snapshotFormatVersion: \"0.1\";\n\treadonly summaryFormatVersion?: undefined;\n}\n\n/**\n * @deprecated - This interface will no longer be exported in the future(AB#8004).\n * @legacy\n * @alpha\n */\nexport interface IFluidDataStoreAttributes2\n\textends OmitAttributesVersions<IFluidDataStoreAttributes1> {\n\t/**\n\t * Switch from snapshotFormatVersion to summaryFormatVersion\n\t */\n\treadonly snapshotFormatVersion?: undefined;\n\treadonly summaryFormatVersion: 2;\n\t/**\n\t * True if channels are not isolated in .channels subtrees, otherwise isolated.\n\t * This is required in both datastore attributes as well as the root container,\n\t * because reused summary handles may cause different format versions in each\n\t * datastore subtree within the summary.\n\t */\n\treadonly disableIsolatedChannels?: true;\n}\n/**\n * Added IFluidDataStoreAttributes similar to IChannelAttributes which will tell the attributes of a\n * store like the package, snapshotFormatVersion to take different decisions based on a particular\n * snapshotFormatVersion.\n *\n * @deprecated - This interface will no longer be exported in the future(AB#8004).\n *\n * @legacy\n * @alpha\n *\n */\nexport type ReadFluidDataStoreAttributes =\n\t| IFluidDataStoreAttributes0\n\t| IFluidDataStoreAttributes1\n\t| IFluidDataStoreAttributes2;\nexport type WriteFluidDataStoreAttributes =\n\t| IFluidDataStoreAttributes1\n\t| IFluidDataStoreAttributes2;\n\nexport function getAttributesFormatVersion(attributes: ReadFluidDataStoreAttributes): number {\n\tif (attributes.summaryFormatVersion) {\n\t\t/**\n\t\t * Version 2+: Introduces .channels trees for isolation of\n\t\t * channel trees from data store objects.\n\t\t */\n\t\treturn attributes.summaryFormatVersion;\n\t} else if (attributes.snapshotFormatVersion === \"0.1\") {\n\t\t/**\n\t\t * Version 1: from this version the pkg within the data store\n\t\t * attributes blob is a JSON array rather than a string.\n\t\t */\n\t\treturn 1;\n\t}\n\t/**\n\t * Version 0: format version is missing from summary.\n\t * This indicates it is an older version.\n\t */\n\treturn 0;\n}\n\nexport function hasIsolatedChannels(attributes: ReadFluidDataStoreAttributes): boolean {\n\treturn !!attributes.summaryFormatVersion && !attributes.disableIsolatedChannels;\n}\n\n/**\n * @internal\n */\n\nexport interface IContainerRuntimeMetadata extends ICreateContainerMetadata, IGCMetadata {\n\treadonly summaryFormatVersion: 1;\n\t/**\n\t * @deprecated - used by old (prior to 2.0 RC3) runtimes\n\t */\n\treadonly message?: ISummaryMetadataMessage;\n\t/**\n\t * The last message processed at the time of summary. Only primitive property types are added to the summary.\n\t */\n\treadonly lastMessage?: ISummaryMetadataMessage;\n\t/**\n\t * True if channels are not isolated in .channels subtrees, otherwise isolated.\n\t */\n\treadonly disableIsolatedChannels?: true;\n\t/**\n\t * The summary number for a container's summary. Incremented on summaries throughout its lifetime.\n\t */\n\treadonly summaryNumber?: number;\n\t/**\n\t * GUID to identify a document in telemetry\n\t */\n\treadonly telemetryDocumentId?: string;\n\n\treadonly documentSchema?: IDocumentSchema;\n}\n\n/**\n * @internal\n */\nexport interface ICreateContainerMetadata {\n\t/**\n\t * Runtime version of the container when it was first created\n\t */\n\tcreateContainerRuntimeVersion?: string;\n\t/**\n\t * Timestamp of the container when it was first created\n\t */\n\tcreateContainerTimestamp?: number;\n}\n\n/**\n * The properties of an ISequencedDocumentMessage to be stored in the metadata blob in summary.\n * @internal\n */\nexport type ISummaryMetadataMessage = Pick<\n\tISequencedDocumentMessage,\n\t| \"clientId\"\n\t| \"clientSequenceNumber\"\n\t| \"minimumSequenceNumber\"\n\t| \"referenceSequenceNumber\"\n\t| \"sequenceNumber\"\n\t| \"timestamp\"\n\t| \"type\"\n>;\n\n/**\n * Extracts the properties from an ISequencedDocumentMessage as defined by ISummaryMetadataMessage. This message is\n * added to the metadata blob in summary.\n */\nexport const extractSummaryMetadataMessage = (\n\tmessage?: ISequencedDocumentMessage,\n): ISummaryMetadataMessage | undefined =>\n\tmessage === undefined\n\t\t? undefined\n\t\t: {\n\t\t\t\tclientId: message.clientId,\n\t\t\t\tclientSequenceNumber: message.clientSequenceNumber,\n\t\t\t\tminimumSequenceNumber: message.minimumSequenceNumber,\n\t\t\t\treferenceSequenceNumber: message.referenceSequenceNumber,\n\t\t\t\tsequenceNumber: message.sequenceNumber,\n\t\t\t\ttimestamp: message.timestamp,\n\t\t\t\ttype: message.type,\n\t\t\t};\n\nexport function getMetadataFormatVersion(metadata?: IContainerRuntimeMetadata): number {\n\t/**\n\t * Version 2+: Introduces runtime sequence number for data verification.\n\t *\n\t * Version 1+: Introduces .metadata blob and .channels trees for isolation of\n\t * data store trees from container-level objects.\n\t * Also introduces enableGC option stored in the summary.\n\t *\n\t * Version 0: metadata blob missing; format version is missing from summary.\n\t * This indicates it is an older version.\n\t */\n\treturn metadata?.summaryFormatVersion ?? 0;\n}\n\nexport const aliasBlobName = \".aliases\";\nexport const metadataBlobName = \".metadata\";\nexport const chunksBlobName = \".chunks\";\nexport const recentBatchInfoBlobName = \".recentBatchInfo\";\nexport const electedSummarizerBlobName = \".electedSummarizer\";\nexport const idCompressorBlobName = \".idCompressor\";\n\nexport function rootHasIsolatedChannels(metadata?: IContainerRuntimeMetadata): boolean {\n\treturn !!metadata && !metadata.disableIsolatedChannels;\n}\n\nexport const protocolTreeName = \".protocol\";\n\n/**\n * List of tree IDs at the container level which are reserved.\n * This is for older versions of summaries that do not yet have an\n * isolated data stores namespace. Without the namespace, this must\n * be used to prevent name collisions with data store IDs.\n */\nexport const nonDataStorePaths = [\n\tprotocolTreeName,\n\t\".logTail\",\n\t\".serviceProtocol\",\n\tblobsTreeName,\n\tgcTreeKey,\n\tidCompressorBlobName,\n];\n\nexport const dataStoreAttributesBlobName = \".component\";\n\n/**\n * Modifies summary tree and stats to put tree under .channels tree.\n *\n * @param summarizeResult - Summary tree and stats to modify\n *\n * @example\n *\n * Converts from:\n *\n * ```typescript\n * {\n * type: SummaryType.Tree,\n * tree: { a: {...}, b: {...}, c: {...} },\n * }\n * ```\n *\n * to:\n *\n * ```typescript\n * {\n * type: SummaryType.Tree,\n * tree: {\n * \".channels\": {\n * type: SummaryType.Tree,\n * tree: { a: {...}, b: {...}, c: {...} }\n * },\n * },\n * }\n * ```\n *\n * And adds +1 to treeNodeCount in stats.\n */\nexport function wrapSummaryInChannelsTree(summarizeResult: ISummaryTreeWithStats): void {\n\tsummarizeResult.summary = {\n\t\ttype: SummaryType.Tree,\n\t\ttree: { [channelsTreeName]: summarizeResult.summary },\n\t};\n\tsummarizeResult.stats.treeNodeCount++;\n}\n\nexport async function getFluidDataStoreAttributes(\n\tstorage: IDocumentStorageService,\n\tsnapshot: ISnapshotTree,\n): Promise<ReadFluidDataStoreAttributes> {\n\tconst attributes = await readAndParse<ReadFluidDataStoreAttributes>(\n\t\tstorage,\n\t\tsnapshot.blobs[dataStoreAttributesBlobName],\n\t);\n\t// Use the snapshotFormatVersion to determine how the pkg is encoded in the snapshot.\n\t// For snapshotFormatVersion = \"0.1\" (1) or above, pkg is jsonified, otherwise it is just a string.\n\t// However the feature of loading a detached container from snapshot, is added when the\n\t// snapshotFormatVersion is at least \"0.1\" (1), so we don't expect it to be anything else.\n\tconst formatVersion = getAttributesFormatVersion(attributes);\n\tassert(formatVersion > 0, 0x1d5 /* Invalid snapshot format version */);\n\treturn attributes;\n}\n\nexport { blobHeadersBlobName } from \"@fluidframework/driver-utils/internal\";\n"]}
|
|
1
|
+
{"version":3,"file":"summaryFormat.js","sourceRoot":"","sources":["../../src/summary/summaryFormat.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 { assert } from \"@fluidframework/core-utils/internal\";\nimport { SummaryType } from \"@fluidframework/driver-definitions\";\nimport {\n\tISnapshotTree,\n\tISequencedDocumentMessage,\n} from \"@fluidframework/driver-definitions/internal\";\nimport { readAndParse } from \"@fluidframework/driver-utils/internal\";\nimport {\n\tISummaryTreeWithStats,\n\tchannelsTreeName,\n\tgcTreeKey,\n\ttype IRuntimeStorageService,\n} from \"@fluidframework/runtime-definitions/internal\";\n\nimport { blobsTreeName } from \"../blobManager/index.js\";\nimport { IGCMetadata } from \"../gc/index.js\";\n\nimport { IDocumentSchema } from \"./documentSchema.js\";\n\n/**\n * @deprecated - This interface will no longer be exported in the future(AB#8004).\n * @legacy\n * @alpha\n */\nexport type OmitAttributesVersions<T> = Omit<\n\tT,\n\t\"snapshotFormatVersion\" | \"summaryFormatVersion\"\n>;\n\n/**\n * @deprecated - This interface will no longer be exported in the future(AB#8004).\n * @legacy\n * @alpha\n */\nexport interface IFluidDataStoreAttributes0 {\n\treadonly snapshotFormatVersion?: undefined;\n\treadonly summaryFormatVersion?: undefined;\n\tpkg: string;\n\t/**\n\t * This tells whether a data store is root. Root data stores are never collected.\n\t * Non-root data stores may be collected if they are not used. If this is not present, default it to\n\t * true. This will ensure that older data stores are incorrectly collected.\n\t */\n\treadonly isRootDataStore?: boolean;\n}\n\n/**\n * @deprecated - This interface will no longer be exported in the future(AB#8004).\n * @legacy\n * @alpha\n */\nexport interface IFluidDataStoreAttributes1\n\textends OmitAttributesVersions<IFluidDataStoreAttributes0> {\n\treadonly snapshotFormatVersion: \"0.1\";\n\treadonly summaryFormatVersion?: undefined;\n}\n\n/**\n * @deprecated - This interface will no longer be exported in the future(AB#8004).\n * @legacy\n * @alpha\n */\nexport interface IFluidDataStoreAttributes2\n\textends OmitAttributesVersions<IFluidDataStoreAttributes1> {\n\t/**\n\t * Switch from snapshotFormatVersion to summaryFormatVersion\n\t */\n\treadonly snapshotFormatVersion?: undefined;\n\treadonly summaryFormatVersion: 2;\n\t/**\n\t * True if channels are not isolated in .channels subtrees, otherwise isolated.\n\t * This is required in both datastore attributes as well as the root container,\n\t * because reused summary handles may cause different format versions in each\n\t * datastore subtree within the summary.\n\t */\n\treadonly disableIsolatedChannels?: true;\n}\n/**\n * Added IFluidDataStoreAttributes similar to IChannelAttributes which will tell the attributes of a\n * store like the package, snapshotFormatVersion to take different decisions based on a particular\n * snapshotFormatVersion.\n *\n * @deprecated - This interface will no longer be exported in the future(AB#8004).\n *\n * @legacy\n * @alpha\n *\n */\nexport type ReadFluidDataStoreAttributes =\n\t| IFluidDataStoreAttributes0\n\t| IFluidDataStoreAttributes1\n\t| IFluidDataStoreAttributes2;\nexport type WriteFluidDataStoreAttributes =\n\t| IFluidDataStoreAttributes1\n\t| IFluidDataStoreAttributes2;\n\nexport function getAttributesFormatVersion(attributes: ReadFluidDataStoreAttributes): number {\n\tif (attributes.summaryFormatVersion) {\n\t\t/**\n\t\t * Version 2+: Introduces .channels trees for isolation of\n\t\t * channel trees from data store objects.\n\t\t */\n\t\treturn attributes.summaryFormatVersion;\n\t} else if (attributes.snapshotFormatVersion === \"0.1\") {\n\t\t/**\n\t\t * Version 1: from this version the pkg within the data store\n\t\t * attributes blob is a JSON array rather than a string.\n\t\t */\n\t\treturn 1;\n\t}\n\t/**\n\t * Version 0: format version is missing from summary.\n\t * This indicates it is an older version.\n\t */\n\treturn 0;\n}\n\nexport function hasIsolatedChannels(attributes: ReadFluidDataStoreAttributes): boolean {\n\treturn !!attributes.summaryFormatVersion && !attributes.disableIsolatedChannels;\n}\n\n/**\n * @internal\n */\n\nexport interface IContainerRuntimeMetadata extends ICreateContainerMetadata, IGCMetadata {\n\treadonly summaryFormatVersion: 1;\n\t/**\n\t * @deprecated - used by old (prior to 2.0 RC3) runtimes\n\t */\n\treadonly message?: ISummaryMetadataMessage;\n\t/**\n\t * The last message processed at the time of summary. Only primitive property types are added to the summary.\n\t */\n\treadonly lastMessage?: ISummaryMetadataMessage;\n\t/**\n\t * True if channels are not isolated in .channels subtrees, otherwise isolated.\n\t */\n\treadonly disableIsolatedChannels?: true;\n\t/**\n\t * The summary number for a container's summary. Incremented on summaries throughout its lifetime.\n\t */\n\treadonly summaryNumber?: number;\n\t/**\n\t * GUID to identify a document in telemetry\n\t */\n\treadonly telemetryDocumentId?: string;\n\n\treadonly documentSchema?: IDocumentSchema;\n}\n\n/**\n * @internal\n */\nexport interface ICreateContainerMetadata {\n\t/**\n\t * Runtime version of the container when it was first created\n\t */\n\tcreateContainerRuntimeVersion?: string;\n\t/**\n\t * Timestamp of the container when it was first created\n\t */\n\tcreateContainerTimestamp?: number;\n}\n\n/**\n * The properties of an ISequencedDocumentMessage to be stored in the metadata blob in summary.\n * @internal\n */\nexport type ISummaryMetadataMessage = Pick<\n\tISequencedDocumentMessage,\n\t| \"clientId\"\n\t| \"clientSequenceNumber\"\n\t| \"minimumSequenceNumber\"\n\t| \"referenceSequenceNumber\"\n\t| \"sequenceNumber\"\n\t| \"timestamp\"\n\t| \"type\"\n>;\n\n/**\n * Extracts the properties from an ISequencedDocumentMessage as defined by ISummaryMetadataMessage. This message is\n * added to the metadata blob in summary.\n */\nexport const extractSummaryMetadataMessage = (\n\tmessage?: ISequencedDocumentMessage,\n): ISummaryMetadataMessage | undefined =>\n\tmessage === undefined\n\t\t? undefined\n\t\t: {\n\t\t\t\tclientId: message.clientId,\n\t\t\t\tclientSequenceNumber: message.clientSequenceNumber,\n\t\t\t\tminimumSequenceNumber: message.minimumSequenceNumber,\n\t\t\t\treferenceSequenceNumber: message.referenceSequenceNumber,\n\t\t\t\tsequenceNumber: message.sequenceNumber,\n\t\t\t\ttimestamp: message.timestamp,\n\t\t\t\ttype: message.type,\n\t\t\t};\n\nexport function getMetadataFormatVersion(metadata?: IContainerRuntimeMetadata): number {\n\t/**\n\t * Version 2+: Introduces runtime sequence number for data verification.\n\t *\n\t * Version 1+: Introduces .metadata blob and .channels trees for isolation of\n\t * data store trees from container-level objects.\n\t * Also introduces enableGC option stored in the summary.\n\t *\n\t * Version 0: metadata blob missing; format version is missing from summary.\n\t * This indicates it is an older version.\n\t */\n\treturn metadata?.summaryFormatVersion ?? 0;\n}\n\nexport const aliasBlobName = \".aliases\";\nexport const metadataBlobName = \".metadata\";\nexport const chunksBlobName = \".chunks\";\nexport const recentBatchInfoBlobName = \".recentBatchInfo\";\nexport const electedSummarizerBlobName = \".electedSummarizer\";\nexport const idCompressorBlobName = \".idCompressor\";\n\nexport function rootHasIsolatedChannels(metadata?: IContainerRuntimeMetadata): boolean {\n\treturn !!metadata && !metadata.disableIsolatedChannels;\n}\n\nexport const protocolTreeName = \".protocol\";\n\n/**\n * List of tree IDs at the container level which are reserved.\n * This is for older versions of summaries that do not yet have an\n * isolated data stores namespace. Without the namespace, this must\n * be used to prevent name collisions with data store IDs.\n */\nexport const nonDataStorePaths = [\n\tprotocolTreeName,\n\t\".logTail\",\n\t\".serviceProtocol\",\n\tblobsTreeName,\n\tgcTreeKey,\n\tidCompressorBlobName,\n];\n\nexport const dataStoreAttributesBlobName = \".component\";\n\n/**\n * Modifies summary tree and stats to put tree under .channels tree.\n *\n * @param summarizeResult - Summary tree and stats to modify\n *\n * @example\n *\n * Converts from:\n *\n * ```typescript\n * {\n * type: SummaryType.Tree,\n * tree: { a: {...}, b: {...}, c: {...} },\n * }\n * ```\n *\n * to:\n *\n * ```typescript\n * {\n * type: SummaryType.Tree,\n * tree: {\n * \".channels\": {\n * type: SummaryType.Tree,\n * tree: { a: {...}, b: {...}, c: {...} }\n * },\n * },\n * }\n * ```\n *\n * And adds +1 to treeNodeCount in stats.\n */\nexport function wrapSummaryInChannelsTree(summarizeResult: ISummaryTreeWithStats): void {\n\tsummarizeResult.summary = {\n\t\ttype: SummaryType.Tree,\n\t\ttree: { [channelsTreeName]: summarizeResult.summary },\n\t};\n\tsummarizeResult.stats.treeNodeCount++;\n}\n\nexport async function getFluidDataStoreAttributes(\n\tstorage: IRuntimeStorageService,\n\tsnapshot: ISnapshotTree,\n): Promise<ReadFluidDataStoreAttributes> {\n\tconst attributes = await readAndParse<ReadFluidDataStoreAttributes>(\n\t\tstorage,\n\t\tsnapshot.blobs[dataStoreAttributesBlobName],\n\t);\n\t// Use the snapshotFormatVersion to determine how the pkg is encoded in the snapshot.\n\t// For snapshotFormatVersion = \"0.1\" (1) or above, pkg is jsonified, otherwise it is just a string.\n\t// However the feature of loading a detached container from snapshot, is added when the\n\t// snapshotFormatVersion is at least \"0.1\" (1), so we don't expect it to be anything else.\n\tconst formatVersion = getAttributesFormatVersion(attributes);\n\tassert(formatVersion > 0, 0x1d5 /* Invalid snapshot format version */);\n\treturn attributes;\n}\n\nexport { blobHeadersBlobName } from \"@fluidframework/driver-utils/internal\";\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/container-runtime",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.52.0",
|
|
4
4
|
"description": "Fluid container runtime",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -119,18 +119,18 @@
|
|
|
119
119
|
"temp-directory": "nyc/.nyc_output"
|
|
120
120
|
},
|
|
121
121
|
"dependencies": {
|
|
122
|
-
"@fluid-internal/client-utils": "~2.
|
|
123
|
-
"@fluidframework/container-definitions": "~2.
|
|
124
|
-
"@fluidframework/container-runtime-definitions": "~2.
|
|
125
|
-
"@fluidframework/core-interfaces": "~2.
|
|
126
|
-
"@fluidframework/core-utils": "~2.
|
|
127
|
-
"@fluidframework/datastore": "~2.
|
|
128
|
-
"@fluidframework/driver-definitions": "~2.
|
|
129
|
-
"@fluidframework/driver-utils": "~2.
|
|
130
|
-
"@fluidframework/id-compressor": "~2.
|
|
131
|
-
"@fluidframework/runtime-definitions": "~2.
|
|
132
|
-
"@fluidframework/runtime-utils": "~2.
|
|
133
|
-
"@fluidframework/telemetry-utils": "~2.
|
|
122
|
+
"@fluid-internal/client-utils": "~2.52.0",
|
|
123
|
+
"@fluidframework/container-definitions": "~2.52.0",
|
|
124
|
+
"@fluidframework/container-runtime-definitions": "~2.52.0",
|
|
125
|
+
"@fluidframework/core-interfaces": "~2.52.0",
|
|
126
|
+
"@fluidframework/core-utils": "~2.52.0",
|
|
127
|
+
"@fluidframework/datastore": "~2.52.0",
|
|
128
|
+
"@fluidframework/driver-definitions": "~2.52.0",
|
|
129
|
+
"@fluidframework/driver-utils": "~2.52.0",
|
|
130
|
+
"@fluidframework/id-compressor": "~2.52.0",
|
|
131
|
+
"@fluidframework/runtime-definitions": "~2.52.0",
|
|
132
|
+
"@fluidframework/runtime-utils": "~2.52.0",
|
|
133
|
+
"@fluidframework/telemetry-utils": "~2.52.0",
|
|
134
134
|
"@tylerbu/sorted-btree-es6": "^1.8.0",
|
|
135
135
|
"double-ended-queue": "^2.1.0-0",
|
|
136
136
|
"lz4js": "^0.2.0",
|
|
@@ -140,16 +140,16 @@
|
|
|
140
140
|
"devDependencies": {
|
|
141
141
|
"@arethetypeswrong/cli": "^0.17.1",
|
|
142
142
|
"@biomejs/biome": "~1.9.3",
|
|
143
|
-
"@fluid-internal/mocha-test-setup": "~2.
|
|
144
|
-
"@fluid-private/stochastic-test-utils": "~2.
|
|
145
|
-
"@fluid-private/test-pairwise-generator": "~2.
|
|
143
|
+
"@fluid-internal/mocha-test-setup": "~2.52.0",
|
|
144
|
+
"@fluid-private/stochastic-test-utils": "~2.52.0",
|
|
145
|
+
"@fluid-private/test-pairwise-generator": "~2.52.0",
|
|
146
146
|
"@fluid-tools/benchmark": "^0.51.0",
|
|
147
|
-
"@fluid-tools/build-cli": "^0.
|
|
147
|
+
"@fluid-tools/build-cli": "^0.57.0",
|
|
148
148
|
"@fluidframework/build-common": "^2.0.3",
|
|
149
|
-
"@fluidframework/build-tools": "^0.
|
|
150
|
-
"@fluidframework/container-runtime-previous": "npm:@fluidframework/container-runtime@2.
|
|
149
|
+
"@fluidframework/build-tools": "^0.57.0",
|
|
150
|
+
"@fluidframework/container-runtime-previous": "npm:@fluidframework/container-runtime@2.51.0",
|
|
151
151
|
"@fluidframework/eslint-config-fluid": "^5.7.4",
|
|
152
|
-
"@fluidframework/test-runtime-utils": "~2.
|
|
152
|
+
"@fluidframework/test-runtime-utils": "~2.52.0",
|
|
153
153
|
"@microsoft/api-extractor": "7.52.8",
|
|
154
154
|
"@types/double-ended-queue": "^2.1.0",
|
|
155
155
|
"@types/lz4js": "^0.2.0",
|
|
@@ -3,8 +3,11 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { createEmitter } from "@fluid-internal/client-utils";
|
|
7
|
+
import {
|
|
8
|
+
AttachState,
|
|
9
|
+
type IContainerStorageService,
|
|
10
|
+
} from "@fluidframework/container-definitions/internal";
|
|
8
11
|
import {
|
|
9
12
|
IContainerRuntime,
|
|
10
13
|
IContainerRuntimeEvents,
|
|
@@ -20,10 +23,7 @@ import type {
|
|
|
20
23
|
PayloadState,
|
|
21
24
|
} from "@fluidframework/core-interfaces/internal";
|
|
22
25
|
import { assert, Deferred } from "@fluidframework/core-utils/internal";
|
|
23
|
-
import {
|
|
24
|
-
IDocumentStorageService,
|
|
25
|
-
ICreateBlobResponse,
|
|
26
|
-
} from "@fluidframework/driver-definitions/internal";
|
|
26
|
+
import { ICreateBlobResponse } from "@fluidframework/driver-definitions/internal";
|
|
27
27
|
import { canRetryOnError, runWithRetry } from "@fluidframework/driver-utils/internal";
|
|
28
28
|
import {
|
|
29
29
|
IGarbageCollectionData,
|
|
@@ -41,12 +41,13 @@ import {
|
|
|
41
41
|
LoggingError,
|
|
42
42
|
MonitoringContext,
|
|
43
43
|
PerformanceEvent,
|
|
44
|
+
UsageError,
|
|
44
45
|
createChildMonitoringContext,
|
|
45
46
|
wrapError,
|
|
46
47
|
} from "@fluidframework/telemetry-utils/internal";
|
|
47
48
|
import { v4 as uuid } from "uuid";
|
|
48
49
|
|
|
49
|
-
import {
|
|
50
|
+
import { isBlobMetadata } from "../metadata.js";
|
|
50
51
|
|
|
51
52
|
import {
|
|
52
53
|
getStorageIds,
|
|
@@ -148,7 +149,6 @@ interface PendingBlob {
|
|
|
148
149
|
attached?: boolean;
|
|
149
150
|
acked?: boolean;
|
|
150
151
|
abortSignal?: AbortSignal;
|
|
151
|
-
stashedUpload?: boolean;
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
export interface IPendingBlobs {
|
|
@@ -171,16 +171,6 @@ interface IBlobManagerInternalEvents {
|
|
|
171
171
|
processedBlobAttach: (localId: string, storageId: string) => void;
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
const stashedPendingBlobOverrides: Pick<
|
|
175
|
-
PendingBlob,
|
|
176
|
-
"stashedUpload" | "storageId" | "minTTLInSeconds" | "uploadTime"
|
|
177
|
-
> = {
|
|
178
|
-
stashedUpload: true,
|
|
179
|
-
storageId: undefined,
|
|
180
|
-
minTTLInSeconds: undefined,
|
|
181
|
-
uploadTime: undefined,
|
|
182
|
-
} as const;
|
|
183
|
-
|
|
184
174
|
export const blobManagerBasePath = "_blobs" as const;
|
|
185
175
|
|
|
186
176
|
export class BlobManager {
|
|
@@ -213,11 +203,10 @@ export class BlobManager {
|
|
|
213
203
|
*/
|
|
214
204
|
private readonly opsInFlight: Map<string, Set<string>> = new Map();
|
|
215
205
|
|
|
216
|
-
private readonly sendBlobAttachOp: (localId: string, storageId
|
|
217
|
-
private stopAttaching: boolean = false;
|
|
206
|
+
private readonly sendBlobAttachOp: (localId: string, storageId: string) => void;
|
|
218
207
|
|
|
219
208
|
private readonly routeContext: IFluidHandleContext;
|
|
220
|
-
private readonly storage:
|
|
209
|
+
private readonly storage: Pick<IContainerStorageService, "createBlob" | "readBlob">;
|
|
221
210
|
// Called when a blob node is requested. blobPath is the path of the blob's node in GC's graph.
|
|
222
211
|
// blobPath's format - `/<basePath>/<blobId>`.
|
|
223
212
|
private readonly blobRequested: (blobPath: string) => void;
|
|
@@ -226,9 +215,6 @@ export class BlobManager {
|
|
|
226
215
|
private readonly isBlobDeleted: (blobPath: string) => boolean;
|
|
227
216
|
private readonly runtime: IBlobManagerRuntime;
|
|
228
217
|
private readonly localBlobIdGenerator: () => string;
|
|
229
|
-
private readonly pendingStashedBlobs: Map<string, Promise<ICreateBlobResponse | void>> =
|
|
230
|
-
new Map();
|
|
231
|
-
public readonly stashedBlobsUploadP: Promise<(void | ICreateBlobResponse)[]>;
|
|
232
218
|
|
|
233
219
|
private readonly createBlobPayloadPending: boolean;
|
|
234
220
|
|
|
@@ -236,7 +222,7 @@ export class BlobManager {
|
|
|
236
222
|
readonly routeContext: IFluidHandleContext;
|
|
237
223
|
|
|
238
224
|
blobManagerLoadInfo: IBlobManagerLoadInfo;
|
|
239
|
-
readonly storage:
|
|
225
|
+
readonly storage: Pick<IContainerStorageService, "createBlob" | "readBlob">;
|
|
240
226
|
/**
|
|
241
227
|
* Submit a BlobAttach op. When a blob is uploaded, there is a short grace period before which the blob is
|
|
242
228
|
* deleted. The BlobAttach op notifies the server that blob is in use. The server will then not delete the
|
|
@@ -247,7 +233,7 @@ export class BlobManager {
|
|
|
247
233
|
* knowledge of which they cannot request the blob from storage. It's important that this op is sequenced
|
|
248
234
|
* before any ops that reference the local ID, otherwise, an invalid handle could be added to the document.
|
|
249
235
|
*/
|
|
250
|
-
sendBlobAttachOp: (localId: string, storageId
|
|
236
|
+
sendBlobAttachOp: (localId: string, storageId: string) => void;
|
|
251
237
|
// Called when a blob node is requested. blobPath is the path of the blob's node in GC's graph.
|
|
252
238
|
// blobPath's format - `/<basePath>/<blobId>`.
|
|
253
239
|
readonly blobRequested: (blobPath: string) => void;
|
|
@@ -267,7 +253,6 @@ export class BlobManager {
|
|
|
267
253
|
blobRequested,
|
|
268
254
|
isBlobDeleted,
|
|
269
255
|
runtime,
|
|
270
|
-
stashedBlobs,
|
|
271
256
|
localBlobIdGenerator,
|
|
272
257
|
createBlobPayloadPending,
|
|
273
258
|
} = props;
|
|
@@ -290,48 +275,7 @@ export class BlobManager {
|
|
|
290
275
|
this.runtime.attachState,
|
|
291
276
|
);
|
|
292
277
|
|
|
293
|
-
|
|
294
|
-
for (const [localId, entry] of Object.entries(stashedBlobs ?? {})) {
|
|
295
|
-
const { acked, storageId, minTTLInSeconds, uploadTime } = entry;
|
|
296
|
-
const blob = stringToBuffer(entry.blob, "base64");
|
|
297
|
-
const pendingEntry: PendingBlob = {
|
|
298
|
-
blob,
|
|
299
|
-
opsent: true,
|
|
300
|
-
handleP: new Deferred(),
|
|
301
|
-
storageId,
|
|
302
|
-
uploadP: undefined,
|
|
303
|
-
uploadTime,
|
|
304
|
-
minTTLInSeconds,
|
|
305
|
-
attached: true,
|
|
306
|
-
acked,
|
|
307
|
-
};
|
|
308
|
-
this.pendingBlobs.set(localId, pendingEntry);
|
|
309
|
-
|
|
310
|
-
if (storageId !== undefined && minTTLInSeconds && uploadTime) {
|
|
311
|
-
const timeLapseSinceLocalUpload = (Date.now() - uploadTime) / 1000;
|
|
312
|
-
// stashed entries with more than half-life in storage will not be reuploaded
|
|
313
|
-
if (minTTLInSeconds - timeLapseSinceLocalUpload > minTTLInSeconds / 2) {
|
|
314
|
-
continue;
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
this.pendingStashedBlobs.set(localId, this.uploadBlob(localId, blob));
|
|
318
|
-
this.pendingBlobs.set(localId, {
|
|
319
|
-
...pendingEntry,
|
|
320
|
-
...stashedPendingBlobOverrides,
|
|
321
|
-
uploadP: this.pendingStashedBlobs.get(localId),
|
|
322
|
-
});
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
this.stashedBlobsUploadP = PerformanceEvent.timedExecAsync(
|
|
326
|
-
this.mc.logger,
|
|
327
|
-
{ eventName: "BlobUploadProcessStashedChanges", count: this.pendingStashedBlobs.size },
|
|
328
|
-
async () => Promise.all(this.pendingStashedBlobs.values()),
|
|
329
|
-
{ start: true, end: true },
|
|
330
|
-
).finally(() => {
|
|
331
|
-
this.pendingStashedBlobs.clear();
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
this.sendBlobAttachOp = (localId: string, blobId?: string) => {
|
|
278
|
+
this.sendBlobAttachOp = (localId: string, blobId: string) => {
|
|
335
279
|
const pendingEntry = this.pendingBlobs.get(localId);
|
|
336
280
|
assert(
|
|
337
281
|
pendingEntry !== undefined,
|
|
@@ -387,10 +331,6 @@ export class BlobManager {
|
|
|
387
331
|
});
|
|
388
332
|
}
|
|
389
333
|
|
|
390
|
-
public hasPendingStashedUploads(): boolean {
|
|
391
|
-
return [...this.pendingBlobs.values()].some((e) => e.stashedUpload === true);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
334
|
public hasBlob(blobId: string): boolean {
|
|
395
335
|
return this.redirectTable.get(blobId) !== undefined;
|
|
396
336
|
}
|
|
@@ -420,7 +360,7 @@ export class BlobManager {
|
|
|
420
360
|
assert(this.redirectTable.has(blobId), 0x383 /* requesting unknown blobs */);
|
|
421
361
|
|
|
422
362
|
// Blobs created while the container is detached are stored in IDetachedBlobStorage.
|
|
423
|
-
// The '
|
|
363
|
+
// The 'IContainerStorageService.readBlob()' call below will retrieve these via localId.
|
|
424
364
|
storageId = blobId;
|
|
425
365
|
} else {
|
|
426
366
|
const attachedStorageId = this.redirectTable.get(blobId);
|
|
@@ -491,7 +431,7 @@ export class BlobManager {
|
|
|
491
431
|
blob: ArrayBufferLike,
|
|
492
432
|
): Promise<IFluidHandleInternalPayloadPending<ArrayBufferLike>> {
|
|
493
433
|
// Blobs created while the container is detached are stored in IDetachedBlobStorage.
|
|
494
|
-
// The '
|
|
434
|
+
// The 'IContainerStorageService.createBlob()' call below will respond with a localId.
|
|
495
435
|
const response = await this.storage.createBlob(blob);
|
|
496
436
|
this.setRedirection(response.id, undefined);
|
|
497
437
|
return this.getBlobHandle(response.id);
|
|
@@ -671,18 +611,9 @@ export class BlobManager {
|
|
|
671
611
|
response: ICreateBlobResponseWithTTL,
|
|
672
612
|
): ICreateBlobResponseWithTTL | undefined {
|
|
673
613
|
const entry = this.pendingBlobs.get(localId);
|
|
674
|
-
if (entry === undefined && this.pendingStashedBlobs.has(localId)) {
|
|
675
|
-
// The blob was already processed and deleted. This can happen if the blob was reuploaded by
|
|
676
|
-
// the stashing process and the original upload was processed before the stashed upload.
|
|
677
|
-
this.mc.logger.sendTelemetryEvent({
|
|
678
|
-
eventName: "StashedBlobAlreadyProcessed",
|
|
679
|
-
localId,
|
|
680
|
-
});
|
|
681
|
-
return;
|
|
682
|
-
}
|
|
683
614
|
|
|
684
615
|
assert(entry !== undefined, 0x6c8 /* pending blob entry not found for uploaded blob */);
|
|
685
|
-
if (
|
|
616
|
+
if (entry.abortSignal?.aborted === true && !entry.opsent) {
|
|
686
617
|
this.mc.logger.sendTelemetryEvent({
|
|
687
618
|
eventName: "BlobAborted",
|
|
688
619
|
localId,
|
|
@@ -694,7 +625,6 @@ export class BlobManager {
|
|
|
694
625
|
entry.storageId === undefined,
|
|
695
626
|
0x386 /* Must have pending blob entry for uploaded blob */,
|
|
696
627
|
);
|
|
697
|
-
entry.stashedUpload = undefined;
|
|
698
628
|
entry.storageId = response.id;
|
|
699
629
|
entry.uploadTime = Date.now();
|
|
700
630
|
entry.minTTLInSeconds = response.minTTLInSeconds;
|
|
@@ -738,46 +668,35 @@ export class BlobManager {
|
|
|
738
668
|
* @param metadata - op metadata containing storage and/or local IDs
|
|
739
669
|
*/
|
|
740
670
|
public reSubmit(metadata: Record<string, unknown> | undefined): void {
|
|
741
|
-
assert(
|
|
742
|
-
const { localId, blobId }
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
);
|
|
752
|
-
return this.sendBlobAttachOp(localId, pendingEntry?.storageId);
|
|
671
|
+
assert(isBlobMetadata(metadata), 0xc01 /* Expected blob metadata for a BlobAttach op */);
|
|
672
|
+
const { localId, blobId } = metadata;
|
|
673
|
+
// Any blob that we're actively trying to advance to attached state must have a
|
|
674
|
+
// pendingBlobs entry. Decline to resubmit for anything else.
|
|
675
|
+
// For example, we might be asked to resubmit stashed ops for blobs that never had
|
|
676
|
+
// their handle attached - these won't have a pendingBlobs entry and we shouldn't
|
|
677
|
+
// try to attach them since they won't be accessible to the customer and would just
|
|
678
|
+
// be considered garbage immediately.
|
|
679
|
+
if (this.pendingBlobs.has(localId)) {
|
|
680
|
+
this.sendBlobAttachOp(localId, blobId);
|
|
753
681
|
}
|
|
754
|
-
return this.sendBlobAttachOp(localId, blobId);
|
|
755
682
|
}
|
|
756
683
|
|
|
757
684
|
public processBlobAttachMessage(message: ISequencedMessageEnvelope, local: boolean): void {
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
}
|
|
768
|
-
assert(blobId !== undefined, 0x12a /* "Missing blob id on metadata" */);
|
|
769
|
-
|
|
770
|
-
// Set up a mapping from local ID to storage ID. This is crucial since without this the blob cannot be
|
|
771
|
-
// requested from the server.
|
|
772
|
-
// Note: The check for undefined is needed for back-compat when localId was not part of the BlobAttach op that
|
|
773
|
-
// was sent when online.
|
|
774
|
-
if (localId !== undefined) {
|
|
775
|
-
this.setRedirection(localId, blobId);
|
|
685
|
+
assert(
|
|
686
|
+
isBlobMetadata(message.metadata),
|
|
687
|
+
0xc02 /* Expected blob metadata for a BlobAttach op */,
|
|
688
|
+
);
|
|
689
|
+
const { localId, blobId } = message.metadata;
|
|
690
|
+
const pendingEntry = this.pendingBlobs.get(localId);
|
|
691
|
+
if (pendingEntry?.abortSignal?.aborted) {
|
|
692
|
+
this.deletePendingBlob(localId);
|
|
693
|
+
return;
|
|
776
694
|
}
|
|
695
|
+
|
|
696
|
+
this.setRedirection(localId, blobId);
|
|
777
697
|
// set identity (id -> id) entry
|
|
778
698
|
this.setRedirection(blobId, blobId);
|
|
779
699
|
|
|
780
|
-
assert(localId !== undefined, 0x50e /* local ID not present in blob attach message */);
|
|
781
700
|
if (local) {
|
|
782
701
|
const waitingBlobs = this.opsInFlight.get(blobId);
|
|
783
702
|
if (waitingBlobs !== undefined) {
|
|
@@ -950,6 +869,21 @@ export class BlobManager {
|
|
|
950
869
|
}
|
|
951
870
|
}
|
|
952
871
|
|
|
872
|
+
/**
|
|
873
|
+
* To be used in getPendingLocalState flow. Get a serializable record of the blobs that are
|
|
874
|
+
* pending upload and/or their BlobAttach op, which can be given to a new BlobManager to
|
|
875
|
+
* resume work.
|
|
876
|
+
*
|
|
877
|
+
* @privateRemarks
|
|
878
|
+
* For now, we don't track any pending blobs since the getPendingBlobs flow doesn't enable
|
|
879
|
+
* restoring to a state where an accessible handle has been stored by the customer (and we'll
|
|
880
|
+
* just drop any BlobAttach ops on the ground during reSubmit). However, once we add support
|
|
881
|
+
* for payload-pending handles, this will return the blobs associated with those handles.
|
|
882
|
+
*/
|
|
883
|
+
public getPendingBlobs(): IPendingBlobs | undefined {
|
|
884
|
+
return undefined;
|
|
885
|
+
}
|
|
886
|
+
|
|
953
887
|
/**
|
|
954
888
|
* Part of container serialization when imminent closure is enabled (Currently when calling closeAndGetPendingLocalState).
|
|
955
889
|
* This asynchronous function resolves all pending createBlob calls and waits for each blob
|
|
@@ -963,83 +897,7 @@ export class BlobManager {
|
|
|
963
897
|
public async attachAndGetPendingBlobs(
|
|
964
898
|
stopBlobAttachingSignal?: AbortSignal,
|
|
965
899
|
): Promise<IPendingBlobs | undefined> {
|
|
966
|
-
|
|
967
|
-
this.mc.logger,
|
|
968
|
-
{ eventName: "GetPendingBlobs" },
|
|
969
|
-
async () => {
|
|
970
|
-
if (this.pendingBlobs.size === 0) {
|
|
971
|
-
return;
|
|
972
|
-
}
|
|
973
|
-
const blobs = {};
|
|
974
|
-
const localBlobs = new Set<PendingBlob>();
|
|
975
|
-
// This while is used to stash blobs created while attaching and getting blobs
|
|
976
|
-
while (localBlobs.size < this.pendingBlobs.size) {
|
|
977
|
-
const attachHandlesP: Promise<void>[] = [];
|
|
978
|
-
for (const [localId, entry] of this.pendingBlobs) {
|
|
979
|
-
if (!localBlobs.has(entry)) {
|
|
980
|
-
localBlobs.add(entry);
|
|
981
|
-
// In order to follow natural blob creation flow we need to:
|
|
982
|
-
// 1 send the blob attach op
|
|
983
|
-
// 2 resolve the blob handle
|
|
984
|
-
// 3 wait for op referencing the blob
|
|
985
|
-
if (!entry.opsent) {
|
|
986
|
-
this.sendBlobAttachOp(localId, entry.storageId);
|
|
987
|
-
}
|
|
988
|
-
// Resolving the blob handle to let hosts continue with their operations (it will resolve
|
|
989
|
-
// original createBlob call) and let them attach the blob. This is a lie we told since the upload
|
|
990
|
-
// hasn't finished yet, but it's fine since we will retry on rehydration.
|
|
991
|
-
entry.handleP.resolve(this.getBlobHandle(localId));
|
|
992
|
-
// Array of promises that will resolve when handles get attached.
|
|
993
|
-
attachHandlesP.push(
|
|
994
|
-
new Promise<void>((resolve, reject) => {
|
|
995
|
-
stopBlobAttachingSignal?.addEventListener(
|
|
996
|
-
"abort",
|
|
997
|
-
() => {
|
|
998
|
-
this.stopAttaching = true;
|
|
999
|
-
reject(new Error("Operation aborted"));
|
|
1000
|
-
},
|
|
1001
|
-
{ once: true },
|
|
1002
|
-
);
|
|
1003
|
-
const onHandleAttached = (attachedEntry: PendingBlob): void => {
|
|
1004
|
-
if (attachedEntry === entry) {
|
|
1005
|
-
this.internalEvents.off("handleAttached", onHandleAttached);
|
|
1006
|
-
resolve();
|
|
1007
|
-
}
|
|
1008
|
-
};
|
|
1009
|
-
if (entry.attached) {
|
|
1010
|
-
resolve();
|
|
1011
|
-
} else {
|
|
1012
|
-
this.internalEvents.on("handleAttached", onHandleAttached);
|
|
1013
|
-
}
|
|
1014
|
-
}),
|
|
1015
|
-
);
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
// Wait for all blobs to be attached. This is important, otherwise serialized container
|
|
1019
|
-
// could send the blobAttach op without any op that references the blob, making it useless.
|
|
1020
|
-
await Promise.allSettled(attachHandlesP);
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
|
-
for (const [localId, entry] of this.pendingBlobs) {
|
|
1024
|
-
if (stopBlobAttachingSignal?.aborted && !entry.attached) {
|
|
1025
|
-
this.mc.logger.sendTelemetryEvent({
|
|
1026
|
-
eventName: "UnableToStashBlob",
|
|
1027
|
-
id: localId,
|
|
1028
|
-
});
|
|
1029
|
-
continue;
|
|
1030
|
-
}
|
|
1031
|
-
assert(entry.attached === true, 0x790 /* stashed blob should be attached */);
|
|
1032
|
-
blobs[localId] = {
|
|
1033
|
-
blob: bufferToString(entry.blob, "base64"),
|
|
1034
|
-
storageId: entry.storageId,
|
|
1035
|
-
acked: entry.acked,
|
|
1036
|
-
minTTLInSeconds: entry.minTTLInSeconds,
|
|
1037
|
-
uploadTime: entry.uploadTime,
|
|
1038
|
-
};
|
|
1039
|
-
}
|
|
1040
|
-
return Object.keys(blobs).length > 0 ? blobs : undefined;
|
|
1041
|
-
},
|
|
1042
|
-
);
|
|
900
|
+
throw new UsageError("attachAndGetPendingBlobs is no longer supported");
|
|
1043
901
|
}
|
|
1044
902
|
}
|
|
1045
903
|
|