/*! * Copyright (c) Microsoft Corporation and contributors. All rights reserved. * Licensed under the MIT License. */ import { type IContainerStorageService } from "@fluidframework/container-definitions/internal"; import type { IContainerRuntime, IContainerRuntimeEvents } from "@fluidframework/container-runtime-definitions/internal"; import type { IEventProvider, IFluidHandleContext, IFluidHandleInternalPayloadPending, ILocalFluidHandle, ILocalFluidHandleEvents, Listenable, PayloadState } from "@fluidframework/core-interfaces/internal"; import type { ICreateBlobResponse } from "@fluidframework/driver-definitions/internal"; import type { IGarbageCollectionData, ISummaryTreeWithStats, ITelemetryContext, ISequencedMessageEnvelope } from "@fluidframework/runtime-definitions/internal"; import { FluidHandleBase } from "@fluidframework/runtime-utils/internal"; import { type IBlobManagerLoadInfo } from "./blobManagerSnapSum.js"; /** * This class represents blob (long string) * This object is used only when creating (writing) new blob and serialization purposes. * De-serialization process goes through FluidObjectHandle and request flow: * DataObject.request() recognizes requests in the form of `/blobs/` * and loads blob. */ export declare class BlobHandle extends FluidHandleBase implements ILocalFluidHandle, IFluidHandleInternalPayloadPending { readonly path: string; readonly routeContext: IFluidHandleContext; get: () => Promise; readonly payloadPending: boolean; private readonly onAttachGraph?; private attached; get isAttached(): boolean; private _events; get events(): Listenable; private _payloadState; get payloadState(): PayloadState; /** * The error property starts undefined, signalling that there has been no error yet. * If an error occurs, the property will contain the error. */ private _payloadShareError; get payloadShareError(): unknown; readonly absolutePath: string; constructor(path: string, routeContext: IFluidHandleContext, get: () => Promise, payloadPending: boolean, onAttachGraph?: (() => void) | undefined); readonly notifyShared: () => void; readonly notifyFailed: (error: unknown) => void; attachGraph(): void; } export type IBlobManagerRuntime = Pick & IEventProvider; export type ICreateBlobResponseWithTTL = ICreateBlobResponse & Partial>; /** * A blob tracked by BlobManager that is only available on the local client. It is not currently * attempting an upload. */ interface LocalOnlyBlob { state: "localOnly"; blob: ArrayBufferLike; } /** * A blob tracked by BlobManager that has been uploaded to storage. If the TTL has not expired, it * should still be available in storage. It is not currently attempting to send a BlobAttach op. */ interface UploadedBlob { state: "uploaded"; blob: ArrayBufferLike; storageId: string; uploadTime: number; minTTLInSeconds: number | undefined; } /** * Serializable form of the LocalBlobRecord that can be used to save and restore pending state. * Omits attached blobs since they are fully uploaded and don't need to be saved and restored. * Omits uploading and attaching states since upon restore we will need to restart those processes. */ export type SerializableLocalBlobRecord = (Omit & { blob: string; }) | (Omit & { blob: string; }); export interface IPendingBlobs { [localId: string]: SerializableLocalBlobRecord; } export declare const blobManagerBasePath = "_blobs"; export declare class BlobManager { private readonly mc; private readonly internalEvents; /** * Map of local IDs to storage IDs. Also includes identity mappings of storage ID to storage ID for all known * storage IDs. All requested IDs must be a key in this map. Blobs created while the container is detached are * stored in IDetachedBlobStorage which gives pseudo storage IDs; the real storage IDs are filled in at attach * time via setRedirectTable(). */ private readonly redirectTable; /** * The localBlobCache has a dual role of caching locally-created blobs, as well as tracking their state as they * are shared. Keys are localIds. */ private readonly localBlobCache; /** * Blobs with an attached handle that have not finished blob-attaching are the set we need to provide from * getPendingState(). This stores their local IDs, and then we can look them up against the localBlobCache. */ private readonly pendingBlobsWithAttachedHandles; /** * Local IDs for any pending blobs we loaded with and have not yet started the upload/attach flow for. */ private readonly pendingOnlyLocalIds; private readonly sendBlobAttachMessage; private readonly routeContext; private readonly storage; private readonly blobRequested; private readonly isBlobDeleted; private readonly runtime; private readonly createBlobPayloadPending; constructor(props: { readonly routeContext: IFluidHandleContext; blobManagerLoadInfo: IBlobManagerLoadInfo; readonly storage: Pick; /** * Submit a BlobAttach message. When a blob is uploaded, there is a short grace period before which the blob is * deleted. The BlobAttach message notifies the server that blob is in use. The server will then not delete the * the blob as long as it is listed as referenced in future summaries. The summarizing client will know to * include the storage ID in the summary when it sees the message. * * The message will also include a local ID to inform all clients of the relation to the storage ID, without * knowledge of which they cannot request the blob from storage. It's important that this message is sequenced * before any messages that reference the local ID, otherwise, an invalid handle could be added to the document. */ sendBlobAttachMessage: (localId: string, storageId: string) => void; readonly blobRequested: (blobPath: string) => void; readonly isBlobDeleted: (blobPath: string) => boolean; readonly runtime: IBlobManagerRuntime; pendingBlobs: IPendingBlobs | undefined; readonly createBlobPayloadPending: boolean; }); /** * Returns whether a blob with the given localId can be retrieved by the BlobManager via getBlob(). */ hasBlob(localId: string): boolean; /** * Lookup the blob storage ID for a given local blob id. * @param localId - The local blob id. Likely coming from a handle. * @returns The storage ID if found and the blob is not pending, undefined otherwise. * @remarks * For blobs with pending payloads (localId exists but upload hasn't finished), this is expected to return undefined. * Consumers should use the observability APIs on the handle (handle.payloadState, payloadShared event) * to understand/wait for storage ID availability. * Similarly, when the runtime is detached, this will return undefined as no blobs have been uploaded to storage. */ lookupTemporaryBlobStorageId(localId: string): string | undefined; /** * Retrieve the blob with the given local blob id. * @param localId - The local blob id. Likely coming from a handle. * @param payloadPending - Whether we suspect the payload may be pending and not available yet. * @returns A promise which resolves to the blob contents */ getBlob(localId: string, payloadPending: boolean): Promise; private getNonPayloadPendingBlobHandle; createBlob(blob: ArrayBufferLike, signal?: AbortSignal): Promise>; private createBlobDetached; private createBlobLegacy; private createBlobWithPayloadPending; /** * Upload and attach the localBlobCache entry for the given localId. * * Expects the localBlobCache entry for the given localId to be in either localOnly or uploaded state * when called. Returns a promise that resolves when the blob completes uploading and attaching, or else * rejects if an error is encountered or the signal is aborted. */ private readonly uploadAndAttach; /** * Resubmit a BlobAttach op. Used to add storage IDs to messages that were * submitted to runtime while disconnected. * @param metadata - message metadata containing storage and/or local IDs */ reSubmit(metadata: Record | undefined): void; processBlobAttachMessage(message: ISequencedMessageEnvelope, local: boolean): void; summarize(telemetryContext?: ITelemetryContext): ISummaryTreeWithStats; /** * Generates data used for garbage collection. Each blob uploaded represents a node in the GC graph as it can be * individually referenced by storing its handle in a referenced DDS. Returns the list of blob ids as GC nodes. * @param fullGC - true to bypass optimizations and force full generation of GC data. BlobManager doesn't care * about this for now because the data is a simple list of blob ids. */ getGCData(fullGC?: boolean): IGarbageCollectionData; /** * Delete blobs with the given routes from the redirect table. * @returns The routes of blobs that were deleted. * * @remarks * The routes are GC nodes paths of format -`//`. * Deleting the blobs involves 2 steps: * * 1. The redirect table entry for the local ids are deleted. * * 2. If the storage ids corresponding to the deleted local ids are not referenced by any further local ids, the * identity mappings in the redirect table are deleted as well. * * Note that this does not delete the blobs from storage service immediately. Deleting the blobs from redirect table * will ensure we don't create an attachment blob for them at the next summary. The service would then delete them * some time in the future. */ deleteSweepReadyNodes(sweepReadyBlobRoutes: readonly string[]): readonly string[]; /** * Verifies that the blob with given id is not deleted, i.e., it has not been garbage collected. If the blob is GC'd, * log an error and throw if necessary. */ private verifyBlobNotDeleted; /** * Called in detached state just prior to attaching, this will update the redirect table by * converting the pseudo storage IDs into real storage IDs using the provided detachedStorageTable. * The provided table must have exactly the same set of pseudo storage IDs as are found in the redirect table. * @param detachedStorageTable - A map of pseudo storage IDs to real storage IDs. */ readonly patchRedirectTable: (detachedStorageTable: Map) => void; /** * Upload and attach any pending blobs that the BlobManager was loaded with that have not already * been attached in the meantime. * @returns A promise that resolves when all the uploads and attaches have completed, or rejects * if any of them fail. */ readonly sharePendingBlobs: () => Promise; /** * To be used in getPendingLocalState flow. Get a serializable record of the blobs that are * pending upload and/or their BlobAttach op, which can be given to a new BlobManager to * resume work. */ getPendingBlobs(): IPendingBlobs | undefined; } /** * For a localId, returns its path in GC's graph. The node path is of the format `//`. * This path must match the path of the blob handle returned by the createBlob API because blobs are marked * referenced by storing these handles in a referenced DDS. */ export declare const getGCNodePathFromLocalId: (localId: string) => string; /** * Returns whether a given path is for attachment blobs that are in the format - "/blobManagerBasePath/...". */ export declare const isBlobPath: (path: string) => path is `/_blobs/${string}`; export {}; //# sourceMappingURL=blobManager.d.ts.map