@fluidframework/container-runtime 2.60.0 → 2.61.0-355054
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/.mocharc.cjs +1 -2
- package/container-runtime.test-files.tar +0 -0
- package/dist/blobManager/blobManager.d.ts +21 -15
- package/dist/blobManager/blobManager.d.ts.map +1 -1
- package/dist/blobManager/blobManager.js +105 -102
- package/dist/blobManager/blobManager.js.map +1 -1
- package/dist/blobManager/blobManagerSnapSum.d.ts +4 -4
- package/dist/blobManager/blobManagerSnapSum.d.ts.map +1 -1
- package/dist/blobManager/blobManagerSnapSum.js +30 -33
- package/dist/blobManager/blobManagerSnapSum.js.map +1 -1
- package/dist/containerRuntime.js +1 -1
- package/dist/containerRuntime.js.map +1 -1
- package/dist/legacy.d.ts +2 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/internal.d.ts +1 -1
- package/legacy.d.ts +1 -1
- package/lib/blobManager/blobManager.d.ts +21 -15
- package/lib/blobManager/blobManager.d.ts.map +1 -1
- package/lib/blobManager/blobManager.js +105 -102
- package/lib/blobManager/blobManager.js.map +1 -1
- package/lib/blobManager/blobManagerSnapSum.d.ts +4 -4
- package/lib/blobManager/blobManagerSnapSum.d.ts.map +1 -1
- package/lib/blobManager/blobManagerSnapSum.js +26 -29
- package/lib/blobManager/blobManagerSnapSum.js.map +1 -1
- package/lib/containerRuntime.js +1 -1
- package/lib/containerRuntime.js.map +1 -1
- package/lib/legacy.d.ts +2 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/tsdoc-metadata.json +1 -1
- package/package.json +26 -26
- package/src/blobManager/blobManager.ts +118 -121
- package/src/blobManager/blobManagerSnapSum.ts +31 -53
- package/src/containerRuntime.ts +1 -1
- package/src/packageVersion.ts +1 -1
package/dist/legacy.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
export {
|
|
12
|
-
// @
|
|
12
|
+
// #region @legacyBeta APIs
|
|
13
13
|
AllowTombstoneRequestHeaderKey,
|
|
14
14
|
CompressionAlgorithms,
|
|
15
15
|
ContainerMessageType,
|
|
@@ -65,4 +65,5 @@ export {
|
|
|
65
65
|
TombstoneResponseHeaderKey,
|
|
66
66
|
disabledCompressionConfig,
|
|
67
67
|
loadContainerRuntime
|
|
68
|
+
// #endregion
|
|
68
69
|
} from "./index.js";
|
package/dist/packageVersion.d.ts
CHANGED
|
@@ -5,5 +5,5 @@
|
|
|
5
5
|
* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
|
|
6
6
|
*/
|
|
7
7
|
export declare const pkgName = "@fluidframework/container-runtime";
|
|
8
|
-
export declare const pkgVersion = "2.
|
|
8
|
+
export declare const pkgVersion = "2.61.0-355054";
|
|
9
9
|
//# sourceMappingURL=packageVersion.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packageVersion.d.ts","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,OAAO,sCAAsC,CAAC;AAC3D,eAAO,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"packageVersion.d.ts","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,OAAO,sCAAsC,CAAC;AAC3D,eAAO,MAAM,UAAU,kBAAkB,CAAC"}
|
package/dist/packageVersion.js
CHANGED
|
@@ -8,5 +8,5 @@
|
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.pkgVersion = exports.pkgName = void 0;
|
|
10
10
|
exports.pkgName = "@fluidframework/container-runtime";
|
|
11
|
-
exports.pkgVersion = "2.
|
|
11
|
+
exports.pkgVersion = "2.61.0-355054";
|
|
12
12
|
//# sourceMappingURL=packageVersion.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEU,QAAA,OAAO,GAAG,mCAAmC,CAAC;AAC9C,QAAA,UAAU,GAAG,
|
|
1
|
+
{"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEU,QAAA,OAAO,GAAG,mCAAmC,CAAC;AAC9C,QAAA,UAAU,GAAG,eAAe,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/container-runtime\";\nexport const pkgVersion = \"2.61.0-355054\";\n"]}
|
package/internal.d.ts
CHANGED
package/legacy.d.ts
CHANGED
|
@@ -59,11 +59,10 @@ export declare class BlobManager {
|
|
|
59
59
|
get events(): Listenable<IBlobManagerEvents>;
|
|
60
60
|
private readonly internalEvents;
|
|
61
61
|
/**
|
|
62
|
-
* Map of local IDs to storage IDs.
|
|
63
|
-
* be a key in this map. Blobs created while the container is detached are
|
|
64
|
-
* gives
|
|
65
|
-
*
|
|
66
|
-
* that uploaded the blob but its mapping to storage ID is needed in all clients in order to retrieve the blob.
|
|
62
|
+
* Map of local IDs to storage IDs. Also includes identity mappings of storage ID to storage ID for all known
|
|
63
|
+
* storage IDs. All requested IDs must be a key in this map. Blobs created while the container is detached are
|
|
64
|
+
* stored in IDetachedBlobStorage which gives pseudo storage IDs; the real storage IDs are filled in at attach
|
|
65
|
+
* time via setRedirectTable().
|
|
67
66
|
*/
|
|
68
67
|
private readonly redirectTable;
|
|
69
68
|
/**
|
|
@@ -82,7 +81,7 @@ export declare class BlobManager {
|
|
|
82
81
|
private readonly blobRequested;
|
|
83
82
|
private readonly isBlobDeleted;
|
|
84
83
|
private readonly runtime;
|
|
85
|
-
private readonly
|
|
84
|
+
private readonly localIdGenerator;
|
|
86
85
|
private readonly createBlobPayloadPending;
|
|
87
86
|
constructor(props: {
|
|
88
87
|
readonly routeContext: IFluidHandleContext;
|
|
@@ -103,20 +102,20 @@ export declare class BlobManager {
|
|
|
103
102
|
readonly isBlobDeleted: (blobPath: string) => boolean;
|
|
104
103
|
readonly runtime: IBlobManagerRuntime;
|
|
105
104
|
stashedBlobs: IPendingBlobs | undefined;
|
|
106
|
-
readonly
|
|
105
|
+
readonly localIdGenerator?: (() => string) | undefined;
|
|
107
106
|
readonly createBlobPayloadPending: boolean;
|
|
108
107
|
});
|
|
109
108
|
get allBlobsAttached(): boolean;
|
|
110
109
|
get hasPendingBlobs(): boolean;
|
|
111
110
|
private createAbortError;
|
|
112
|
-
hasBlob(
|
|
111
|
+
hasBlob(localId: string): boolean;
|
|
113
112
|
/**
|
|
114
113
|
* Retrieve the blob with the given local blob id.
|
|
115
|
-
* @param
|
|
114
|
+
* @param localId - The local blob id. Likely coming from a handle.
|
|
116
115
|
* @param payloadPending - Whether we suspect the payload may be pending and not available yet.
|
|
117
116
|
* @returns A promise which resolves to the blob contents
|
|
118
117
|
*/
|
|
119
|
-
getBlob(
|
|
118
|
+
getBlob(localId: string, payloadPending: boolean): Promise<ArrayBufferLike>;
|
|
120
119
|
private getBlobHandle;
|
|
121
120
|
private createBlobDetached;
|
|
122
121
|
createBlob(blob: ArrayBufferLike, signal?: AbortSignal): Promise<IFluidHandleInternalPayloadPending<ArrayBufferLike>>;
|
|
@@ -163,16 +162,17 @@ export declare class BlobManager {
|
|
|
163
162
|
* Delete blobs with the given routes from the redirect table.
|
|
164
163
|
*
|
|
165
164
|
* @remarks
|
|
166
|
-
* The routes are GC nodes paths of format -`/<blobManagerBasePath>/<
|
|
165
|
+
* The routes are GC nodes paths of format -`/<blobManagerBasePath>/<localId>`.
|
|
167
166
|
* Deleting the blobs involves 2 steps:
|
|
168
167
|
*
|
|
169
168
|
* 1. The redirect table entry for the local ids are deleted.
|
|
170
169
|
*
|
|
171
|
-
* 2. If the storage ids corresponding to the deleted local ids are not
|
|
172
|
-
*
|
|
170
|
+
* 2. If the storage ids corresponding to the deleted local ids are not referenced by any further local ids, the
|
|
171
|
+
* identity mappings in the redirect table are deleted as well.
|
|
173
172
|
*
|
|
174
173
|
* Note that this does not delete the blobs from storage service immediately. Deleting the blobs from redirect table
|
|
175
|
-
* will
|
|
174
|
+
* will ensure we don't create an attachment blob for them at the next summary. The service would then delete them
|
|
175
|
+
* some time in the future.
|
|
176
176
|
*/
|
|
177
177
|
private deleteBlobsFromRedirectTable;
|
|
178
178
|
/**
|
|
@@ -180,7 +180,13 @@ export declare class BlobManager {
|
|
|
180
180
|
* log an error and throw if necessary.
|
|
181
181
|
*/
|
|
182
182
|
private verifyBlobNotDeleted;
|
|
183
|
-
|
|
183
|
+
/**
|
|
184
|
+
* Called in detached state just prior to attaching, this will update the redirect table by
|
|
185
|
+
* converting the pseudo storage IDs into real storage IDs using the provided detachedStorageTable.
|
|
186
|
+
* The provided table must have exactly the same set of pseudo storage IDs as are found in the redirect table.
|
|
187
|
+
* @param detachedStorageTable - A map of pseudo storage IDs to real storage IDs.
|
|
188
|
+
*/
|
|
189
|
+
patchRedirectTable(detachedStorageTable: Map<string, string>): void;
|
|
184
190
|
/**
|
|
185
191
|
* To be used in getPendingLocalState flow. Get a serializable record of the blobs that are
|
|
186
192
|
* pending upload and/or their BlobAttach op, which can be given to a new BlobManager to
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blobManager.d.ts","sourceRoot":"","sources":["../../src/blobManager/blobManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAEN,KAAK,wBAAwB,EAC7B,MAAM,gDAAgD,CAAC;AACxD,OAAO,KAAK,EACX,iBAAiB,EACjB,uBAAuB,EACvB,MAAM,wDAAwD,CAAC;AAChE,OAAO,KAAK,EAEX,cAAc,EACd,mBAAmB,EACnB,kCAAkC,EAClC,iBAAiB,EACjB,uBAAuB,EACvB,UAAU,EACV,YAAY,EACZ,MAAM,0CAA0C,CAAC;AAGlD,OAAO,KAAK,EACX,sBAAsB,EACtB,qBAAqB,EACrB,iBAAiB,EACjB,yBAAyB,EACzB,MAAM,8CAA8C,CAAC;AACtD,OAAO,EACN,eAAe,EAIf,MAAM,wCAAwC,CAAC;AAYhD,OAAO,EAIN,KAAK,oBAAoB,EACzB,MAAM,yBAAyB,CAAC;AAEjC;;;;;;GAMG;AACH,qBAAa,UACZ,SAAQ,eAAe,CAAC,eAAe,CACvC,YACC,iBAAiB,CAAC,eAAe,CAAC,EAClC,kCAAkC,CAAC,eAAe,CAAC;aAgCnC,IAAI,EAAE,MAAM;aACZ,YAAY,EAAE,mBAAmB;IAC1C,GAAG,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC;aAC1B,cAAc,EAAE,OAAO;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC;IAlChC,OAAO,CAAC,QAAQ,CAAkB;IAElC,IAAW,UAAU,IAAI,OAAO,CAE/B;IAED,OAAO,CAAC,OAAO,CAEF;IACb,IAAW,MAAM,IAAI,UAAU,CAAC,uBAAuB,CAAC,CAEvD;IAED,OAAO,CAAC,MAAM,CAA2B;IACzC,IAAW,YAAY,IAAI,YAAY,CAEtC;IAED;;;OAGG;IACH,OAAO,CAAC,kBAAkB,CAAU;IACpC,IAAW,iBAAiB,IAAI,OAAO,CAEtC;IAED,SAAgB,YAAY,EAAE,MAAM,CAAC;gBAGpB,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,mBAAmB,EAC1C,GAAG,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,EAC1B,cAAc,EAAE,OAAO,EACtB,aAAa,CAAC,SAAQ,IAAI,aAAA;IAM5C,SAAgB,YAAY,QAAO,IAAI,CAGrC;IAEF,SAAgB,YAAY,UAAW,OAAO,KAAG,IAAI,CAGnD;IAEK,WAAW,IAAI,IAAI;CAM1B;AAID,MAAM,MAAM,mBAAmB,GAAG,IAAI,CACrC,iBAAiB,EACjB,aAAa,GAAG,WAAW,GAAG,YAAY,GAAG,eAAe,GAAG,UAAU,CACzE,GACA,cAAc,CAAC,uBAAuB,CAAC,CAAC;AAkBzC,MAAM,WAAW,aAAa;IAC7B,CAAC,OAAO,EAAE,MAAM,GAAG;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,KAAK,CAAC,EAAE,OAAO,CAAC;KAChB,CAAC;CACF;AAED,MAAM,WAAW,kBAAkB;IAClC,cAAc,EAAE,MAAM,IAAI,CAAC;CAC3B;AAQD,eAAO,MAAM,mBAAmB,UAAoB,CAAC;AAErD,qBAAa,WAAW;IACvB,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAoB;IAEvC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAuC;IACpE,IAAW,MAAM,IAAI,UAAU,CAAC,kBAAkB,CAAC,CAElD;IACD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA+C;IAE9E
|
|
1
|
+
{"version":3,"file":"blobManager.d.ts","sourceRoot":"","sources":["../../src/blobManager/blobManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -74,20 +74,20 @@ export class BlobManager {
|
|
|
74
74
|
* because we know that the server will not delete the blob corresponding to that storage ID.
|
|
75
75
|
*/
|
|
76
76
|
this.opsInFlight = new Map();
|
|
77
|
-
const { routeContext, blobManagerLoadInfo, storage, sendBlobAttachOp, blobRequested, isBlobDeleted, runtime,
|
|
77
|
+
const { routeContext, blobManagerLoadInfo, storage, sendBlobAttachOp, blobRequested, isBlobDeleted, runtime, localIdGenerator, createBlobPayloadPending, } = props;
|
|
78
78
|
this.routeContext = routeContext;
|
|
79
79
|
this.storage = storage;
|
|
80
80
|
this.blobRequested = blobRequested;
|
|
81
81
|
this.isBlobDeleted = isBlobDeleted;
|
|
82
82
|
this.runtime = runtime;
|
|
83
|
-
this.
|
|
83
|
+
this.localIdGenerator = localIdGenerator ?? uuid;
|
|
84
84
|
this.createBlobPayloadPending = createBlobPayloadPending;
|
|
85
85
|
this.mc = createChildMonitoringContext({
|
|
86
86
|
logger: this.runtime.baseLogger,
|
|
87
87
|
namespace: "BlobManager",
|
|
88
88
|
});
|
|
89
|
-
this.redirectTable = toRedirectTable(blobManagerLoadInfo, this.mc.logger
|
|
90
|
-
this.sendBlobAttachOp = (localId,
|
|
89
|
+
this.redirectTable = toRedirectTable(blobManagerLoadInfo, this.mc.logger);
|
|
90
|
+
this.sendBlobAttachOp = (localId, storageId) => {
|
|
91
91
|
const pendingEntry = this.pendingBlobs.get(localId);
|
|
92
92
|
assert(pendingEntry !== undefined, 0x725 /* Must have pending blob entry for upcoming op */);
|
|
93
93
|
if (pendingEntry?.uploadTime && pendingEntry?.minTTLInSeconds) {
|
|
@@ -113,7 +113,7 @@ export class BlobManager {
|
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
pendingEntry.opsent = true;
|
|
116
|
-
sendBlobAttachOp(localId,
|
|
116
|
+
sendBlobAttachOp(localId, storageId);
|
|
117
117
|
};
|
|
118
118
|
}
|
|
119
119
|
get allBlobsAttached() {
|
|
@@ -134,56 +134,46 @@ export class BlobManager {
|
|
|
134
134
|
uploadTime: pending?.uploadTime,
|
|
135
135
|
});
|
|
136
136
|
}
|
|
137
|
-
hasBlob(
|
|
138
|
-
return this.redirectTable.get(
|
|
137
|
+
hasBlob(localId) {
|
|
138
|
+
return this.redirectTable.get(localId) !== undefined;
|
|
139
139
|
}
|
|
140
140
|
/**
|
|
141
141
|
* Retrieve the blob with the given local blob id.
|
|
142
|
-
* @param
|
|
142
|
+
* @param localId - The local blob id. Likely coming from a handle.
|
|
143
143
|
* @param payloadPending - Whether we suspect the payload may be pending and not available yet.
|
|
144
144
|
* @returns A promise which resolves to the blob contents
|
|
145
145
|
*/
|
|
146
|
-
async getBlob(
|
|
146
|
+
async getBlob(localId, payloadPending) {
|
|
147
147
|
// Verify that the blob is not deleted, i.e., it has not been garbage collected. If it is, this will throw
|
|
148
148
|
// an error, failing the call.
|
|
149
|
-
this.verifyBlobNotDeleted(
|
|
149
|
+
this.verifyBlobNotDeleted(localId);
|
|
150
150
|
// Let runtime know that the corresponding GC node was requested.
|
|
151
151
|
// Note that this will throw if the blob is inactive or tombstoned and throwing on incorrect usage
|
|
152
152
|
// is configured.
|
|
153
|
-
this.blobRequested(
|
|
154
|
-
const pending = this.pendingBlobs.get(
|
|
153
|
+
this.blobRequested(getGCNodePathFromLocalId(localId));
|
|
154
|
+
const pending = this.pendingBlobs.get(localId);
|
|
155
155
|
if (pending) {
|
|
156
156
|
return pending.blob;
|
|
157
157
|
}
|
|
158
|
-
let storageId;
|
|
159
|
-
if (
|
|
160
|
-
|
|
161
|
-
//
|
|
162
|
-
//
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
attachedStorageId ??
|
|
178
|
-
(await new Promise((resolve) => {
|
|
179
|
-
const onProcessBlobAttach = (localId, _storageId) => {
|
|
180
|
-
if (localId === blobId) {
|
|
181
|
-
this.internalEvents.off("processedBlobAttach", onProcessBlobAttach);
|
|
182
|
-
resolve(_storageId);
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
|
-
this.internalEvents.on("processedBlobAttach", onProcessBlobAttach);
|
|
186
|
-
}));
|
|
158
|
+
let storageId = this.redirectTable.get(localId);
|
|
159
|
+
if (storageId === undefined) {
|
|
160
|
+
// Only blob handles explicitly marked with pending payload are permitted to exist without
|
|
161
|
+
// yet knowing their storage id. Otherwise they must already be associated with a storage id.
|
|
162
|
+
// Handles for detached blobs are not payload pending.
|
|
163
|
+
assert(payloadPending, 0x11f /* "requesting unknown blobs" */);
|
|
164
|
+
// If we didn't find it in the redirectTable and it's payloadPending, assume the attach op is coming
|
|
165
|
+
// eventually and wait. We do this even if the local client doesn't have the blob payloadPending flag
|
|
166
|
+
// enabled, in case a remote client does have it enabled. This wait may be infinite if the uploading
|
|
167
|
+
// client failed the upload and doesn't exist anymore.
|
|
168
|
+
storageId = await new Promise((resolve) => {
|
|
169
|
+
const onProcessBlobAttach = (_localId, _storageId) => {
|
|
170
|
+
if (_localId === localId) {
|
|
171
|
+
this.internalEvents.off("processedBlobAttach", onProcessBlobAttach);
|
|
172
|
+
resolve(_storageId);
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
this.internalEvents.on("processedBlobAttach", onProcessBlobAttach);
|
|
176
|
+
});
|
|
187
177
|
}
|
|
188
178
|
return PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "AttachmentReadBlob", id: storageId }, async (event) => {
|
|
189
179
|
return this.storage.readBlob(storageId).catch((error) => {
|
|
@@ -207,15 +197,17 @@ export class BlobManager {
|
|
|
207
197
|
this.deletePendingBlobMaybe(localId);
|
|
208
198
|
}
|
|
209
199
|
: undefined;
|
|
210
|
-
return new BlobHandle(
|
|
200
|
+
return new BlobHandle(getGCNodePathFromLocalId(localId), this.routeContext, async () => this.getBlob(localId, false), false, // payloadPending
|
|
211
201
|
callback);
|
|
212
202
|
}
|
|
213
203
|
async createBlobDetached(blob) {
|
|
204
|
+
const localId = this.localIdGenerator();
|
|
214
205
|
// Blobs created while the container is detached are stored in IDetachedBlobStorage.
|
|
215
|
-
// The 'IContainerStorageService.createBlob()' call below will respond with a
|
|
216
|
-
|
|
217
|
-
this.
|
|
218
|
-
|
|
206
|
+
// The 'IContainerStorageService.createBlob()' call below will respond with a pseudo storage ID.
|
|
207
|
+
// That pseudo storage ID will be replaced with the real storage ID at attach time.
|
|
208
|
+
const { id: detachedStorageId } = await this.storage.createBlob(blob);
|
|
209
|
+
this.setRedirection(localId, detachedStorageId);
|
|
210
|
+
return this.getBlobHandle(localId);
|
|
219
211
|
}
|
|
220
212
|
async createBlob(blob, signal) {
|
|
221
213
|
if (this.runtime.attachState === AttachState.Detached) {
|
|
@@ -237,7 +229,7 @@ export class BlobManager {
|
|
|
237
229
|
}
|
|
238
230
|
// Create a local ID for the blob. After uploading it to storage and before returning it, a local ID to
|
|
239
231
|
// storage ID mapping is created.
|
|
240
|
-
const localId = this.
|
|
232
|
+
const localId = this.localIdGenerator();
|
|
241
233
|
const pendingEntry = {
|
|
242
234
|
blob,
|
|
243
235
|
handleP: new Deferred(),
|
|
@@ -259,8 +251,8 @@ export class BlobManager {
|
|
|
259
251
|
});
|
|
260
252
|
}
|
|
261
253
|
createBlobWithPayloadPending(blob) {
|
|
262
|
-
const localId = this.
|
|
263
|
-
const blobHandle = new BlobHandle(
|
|
254
|
+
const localId = this.localIdGenerator();
|
|
255
|
+
const blobHandle = new BlobHandle(getGCNodePathFromLocalId(localId), this.routeContext, async () => blob, true, // payloadPending
|
|
264
256
|
() => {
|
|
265
257
|
const pendingEntry = {
|
|
266
258
|
blob,
|
|
@@ -375,7 +367,7 @@ export class BlobManager {
|
|
|
375
367
|
if (!entry.opsent) {
|
|
376
368
|
this.sendBlobAttachOp(localId, response.id);
|
|
377
369
|
}
|
|
378
|
-
const storageIds = getStorageIds(this.redirectTable
|
|
370
|
+
const storageIds = getStorageIds(this.redirectTable);
|
|
379
371
|
if (storageIds.has(response.id)) {
|
|
380
372
|
// The blob is de-duped. Set up a local ID to storage ID mapping and return the blob. Since this is
|
|
381
373
|
// an existing blob, we don't have to wait for the op to be ack'd since this step has already
|
|
@@ -408,7 +400,7 @@ export class BlobManager {
|
|
|
408
400
|
*/
|
|
409
401
|
reSubmit(metadata) {
|
|
410
402
|
assert(isBlobMetadata(metadata), 0xc01 /* Expected blob metadata for a BlobAttach op */);
|
|
411
|
-
const { localId, blobId } = metadata;
|
|
403
|
+
const { localId, blobId: storageId } = metadata;
|
|
412
404
|
// Any blob that we're actively trying to advance to attached state must have a
|
|
413
405
|
// pendingBlobs entry. Decline to resubmit for anything else.
|
|
414
406
|
// For example, we might be asked to resubmit stashed ops for blobs that never had
|
|
@@ -416,22 +408,22 @@ export class BlobManager {
|
|
|
416
408
|
// try to attach them since they won't be accessible to the customer and would just
|
|
417
409
|
// be considered garbage immediately.
|
|
418
410
|
if (this.pendingBlobs.has(localId)) {
|
|
419
|
-
this.sendBlobAttachOp(localId,
|
|
411
|
+
this.sendBlobAttachOp(localId, storageId);
|
|
420
412
|
}
|
|
421
413
|
}
|
|
422
414
|
processBlobAttachMessage(message, local) {
|
|
423
415
|
assert(isBlobMetadata(message.metadata), 0xc02 /* Expected blob metadata for a BlobAttach op */);
|
|
424
|
-
const { localId, blobId } = message.metadata;
|
|
416
|
+
const { localId, blobId: storageId } = message.metadata;
|
|
425
417
|
const pendingEntry = this.pendingBlobs.get(localId);
|
|
426
418
|
if (pendingEntry?.abortSignal?.aborted) {
|
|
427
419
|
this.deletePendingBlob(localId);
|
|
428
420
|
return;
|
|
429
421
|
}
|
|
430
|
-
this.setRedirection(localId,
|
|
422
|
+
this.setRedirection(localId, storageId);
|
|
431
423
|
// set identity (id -> id) entry
|
|
432
|
-
this.setRedirection(
|
|
424
|
+
this.setRedirection(storageId, storageId);
|
|
433
425
|
if (local) {
|
|
434
|
-
const waitingBlobs = this.opsInFlight.get(
|
|
426
|
+
const waitingBlobs = this.opsInFlight.get(storageId);
|
|
435
427
|
if (waitingBlobs !== undefined) {
|
|
436
428
|
// For each op corresponding to this storage ID that we are waiting for, resolve the pending blob.
|
|
437
429
|
// This is safe because the server will keep the blob alive and the op containing the local ID to
|
|
@@ -439,14 +431,14 @@ export class BlobManager {
|
|
|
439
431
|
for (const pendingLocalId of waitingBlobs) {
|
|
440
432
|
const entry = this.pendingBlobs.get(pendingLocalId);
|
|
441
433
|
assert(entry !== undefined, 0x38f /* local online BlobAttach op with no pending blob entry */);
|
|
442
|
-
this.setRedirection(pendingLocalId,
|
|
434
|
+
this.setRedirection(pendingLocalId, storageId);
|
|
443
435
|
entry.acked = true;
|
|
444
436
|
const blobHandle = this.getBlobHandle(pendingLocalId);
|
|
445
437
|
blobHandle.notifyShared();
|
|
446
438
|
entry.handleP.resolve(blobHandle);
|
|
447
439
|
this.deletePendingBlobMaybe(pendingLocalId);
|
|
448
440
|
}
|
|
449
|
-
this.opsInFlight.delete(
|
|
441
|
+
this.opsInFlight.delete(storageId);
|
|
450
442
|
}
|
|
451
443
|
const localEntry = this.pendingBlobs.get(localId);
|
|
452
444
|
if (localEntry) {
|
|
@@ -457,10 +449,10 @@ export class BlobManager {
|
|
|
457
449
|
this.deletePendingBlobMaybe(localId);
|
|
458
450
|
}
|
|
459
451
|
}
|
|
460
|
-
this.internalEvents.emit("processedBlobAttach", localId,
|
|
452
|
+
this.internalEvents.emit("processedBlobAttach", localId, storageId);
|
|
461
453
|
}
|
|
462
454
|
summarize(telemetryContext) {
|
|
463
|
-
return summarizeBlobManagerState(this.redirectTable
|
|
455
|
+
return summarizeBlobManagerState(this.redirectTable);
|
|
464
456
|
}
|
|
465
457
|
/**
|
|
466
458
|
* Generates data used for garbage collection. Each blob uploaded represents a node in the GC graph as it can be
|
|
@@ -471,13 +463,12 @@ export class BlobManager {
|
|
|
471
463
|
getGCData(fullGC = false) {
|
|
472
464
|
const gcData = { gcNodes: {} };
|
|
473
465
|
for (const [localId, storageId] of this.redirectTable) {
|
|
474
|
-
|
|
475
|
-
//
|
|
476
|
-
// id entries have the same key and value, ignore them.
|
|
477
|
-
// The outbound routes are empty because a blob node cannot reference other nodes. It can only be referenced
|
|
478
|
-
// by adding its handle to a referenced DDS.
|
|
466
|
+
// Don't report the identity mappings to GC - these exist to service old handles that referenced the storage
|
|
467
|
+
// IDs directly. We'll implicitly clean them up if all of their localId references get GC'd first.
|
|
479
468
|
if (localId !== storageId) {
|
|
480
|
-
|
|
469
|
+
// The outbound routes are empty because a blob node cannot reference other nodes. It can only be referenced
|
|
470
|
+
// by adding its handle to a referenced DDS.
|
|
471
|
+
gcData.gcNodes[getGCNodePathFromLocalId(localId)] = [];
|
|
481
472
|
}
|
|
482
473
|
}
|
|
483
474
|
return gcData;
|
|
@@ -496,55 +487,53 @@ export class BlobManager {
|
|
|
496
487
|
* Delete blobs with the given routes from the redirect table.
|
|
497
488
|
*
|
|
498
489
|
* @remarks
|
|
499
|
-
* The routes are GC nodes paths of format -`/<blobManagerBasePath>/<
|
|
490
|
+
* The routes are GC nodes paths of format -`/<blobManagerBasePath>/<localId>`.
|
|
500
491
|
* Deleting the blobs involves 2 steps:
|
|
501
492
|
*
|
|
502
493
|
* 1. The redirect table entry for the local ids are deleted.
|
|
503
494
|
*
|
|
504
|
-
* 2. If the storage ids corresponding to the deleted local ids are not
|
|
505
|
-
*
|
|
495
|
+
* 2. If the storage ids corresponding to the deleted local ids are not referenced by any further local ids, the
|
|
496
|
+
* identity mappings in the redirect table are deleted as well.
|
|
506
497
|
*
|
|
507
498
|
* Note that this does not delete the blobs from storage service immediately. Deleting the blobs from redirect table
|
|
508
|
-
* will
|
|
499
|
+
* will ensure we don't create an attachment blob for them at the next summary. The service would then delete them
|
|
500
|
+
* some time in the future.
|
|
509
501
|
*/
|
|
510
502
|
deleteBlobsFromRedirectTable(blobRoutes) {
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
}
|
|
514
|
-
// This tracks the storage ids of local ids that are deleted. After the local ids have been deleted, if any of
|
|
515
|
-
// these storage ids are unused, they will be deleted as well.
|
|
503
|
+
// maybeUnusedStorageIds is used to compute the set of storage IDs that *used to have a local ID*, but that
|
|
504
|
+
// local ID is being deleted.
|
|
516
505
|
const maybeUnusedStorageIds = new Set();
|
|
517
506
|
for (const route of blobRoutes) {
|
|
518
|
-
const
|
|
507
|
+
const localId = getLocalIdFromGCNodePath(route);
|
|
519
508
|
// If the blob hasn't already been deleted, log an error because this should never happen.
|
|
520
509
|
// If the blob has already been deleted, log a telemetry event. This can happen because multiple GC
|
|
521
510
|
// sweep ops can contain the same data store. It would be interesting to track how often this happens.
|
|
522
511
|
const alreadyDeleted = this.isBlobDeleted(route);
|
|
523
|
-
|
|
512
|
+
const storageId = this.redirectTable.get(localId);
|
|
513
|
+
if (storageId === undefined) {
|
|
524
514
|
this.mc.logger.sendTelemetryEvent({
|
|
525
515
|
eventName: "DeletedAttachmentBlobNotFound",
|
|
526
516
|
category: alreadyDeleted ? "generic" : "error",
|
|
527
|
-
blobId,
|
|
517
|
+
blobId: localId,
|
|
528
518
|
details: { alreadyDeleted },
|
|
529
519
|
});
|
|
530
520
|
continue;
|
|
531
521
|
}
|
|
532
|
-
const storageId = this.redirectTable.get(blobId);
|
|
533
|
-
assert(!!storageId, 0x5bb /* Must be attached to run GC */);
|
|
534
522
|
maybeUnusedStorageIds.add(storageId);
|
|
535
|
-
this.redirectTable.delete(
|
|
523
|
+
this.redirectTable.delete(localId);
|
|
536
524
|
}
|
|
537
|
-
//
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
assert(!!storageId, 0x5bc /* Must be attached to run GC */);
|
|
541
|
-
// For every storage id, the redirect table has a id -> id entry. These do not make the storage id in-use.
|
|
542
|
-
if (maybeUnusedStorageIds.has(storageId) && localId !== storageId) {
|
|
525
|
+
// Remove any storage IDs that still have local IDs referring to them (excluding the identity mapping).
|
|
526
|
+
for (const [localId, storageId] of this.redirectTable) {
|
|
527
|
+
if (localId !== storageId) {
|
|
543
528
|
maybeUnusedStorageIds.delete(storageId);
|
|
544
529
|
}
|
|
545
530
|
}
|
|
546
|
-
//
|
|
547
|
-
// This way they'll be absent from the next summary, and the service
|
|
531
|
+
// Now delete any identity mappings (storage ID -> storage ID) from the redirect table that used to be
|
|
532
|
+
// referenced by a distinct local ID. This way they'll be absent from the next summary, and the service
|
|
533
|
+
// is free to delete them from storage.
|
|
534
|
+
// WARNING: This can potentially delete identity mappings that are still referenced, if storage deduping
|
|
535
|
+
// has let us add a local ID -> storage ID mapping that is later deleted. AB#47337 tracks this issue
|
|
536
|
+
// and possible solutions.
|
|
548
537
|
for (const storageId of maybeUnusedStorageIds) {
|
|
549
538
|
this.redirectTable.delete(storageId);
|
|
550
539
|
}
|
|
@@ -553,11 +542,11 @@ export class BlobManager {
|
|
|
553
542
|
* Verifies that the blob with given id is not deleted, i.e., it has not been garbage collected. If the blob is GC'd,
|
|
554
543
|
* log an error and throw if necessary.
|
|
555
544
|
*/
|
|
556
|
-
verifyBlobNotDeleted(
|
|
557
|
-
if (!this.isBlobDeleted(
|
|
545
|
+
verifyBlobNotDeleted(localId) {
|
|
546
|
+
if (!this.isBlobDeleted(getGCNodePathFromLocalId(localId))) {
|
|
558
547
|
return;
|
|
559
548
|
}
|
|
560
|
-
const request = { url:
|
|
549
|
+
const request = { url: localId };
|
|
561
550
|
const error = responseToException(createResponseError(404, `Blob was deleted`, request), request);
|
|
562
551
|
// Only log deleted events. Tombstone events are logged by garbage collector.
|
|
563
552
|
this.mc.logger.sendErrorEvent({
|
|
@@ -566,14 +555,28 @@ export class BlobManager {
|
|
|
566
555
|
}, error);
|
|
567
556
|
throw error;
|
|
568
557
|
}
|
|
569
|
-
|
|
558
|
+
/**
|
|
559
|
+
* Called in detached state just prior to attaching, this will update the redirect table by
|
|
560
|
+
* converting the pseudo storage IDs into real storage IDs using the provided detachedStorageTable.
|
|
561
|
+
* The provided table must have exactly the same set of pseudo storage IDs as are found in the redirect table.
|
|
562
|
+
* @param detachedStorageTable - A map of pseudo storage IDs to real storage IDs.
|
|
563
|
+
*/
|
|
564
|
+
patchRedirectTable(detachedStorageTable) {
|
|
570
565
|
assert(this.runtime.attachState === AttachState.Detached, 0x252 /* "redirect table can only be set in detached container" */);
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
566
|
+
// The values of the redirect table are the pseudo storage IDs, which are the keys of the
|
|
567
|
+
// detachedStorageTable. We expect to have a many:1 mapping from local IDs to pseudo
|
|
568
|
+
// storage IDs (many in the case that the storage dedupes the blob).
|
|
569
|
+
assert(new Set(this.redirectTable.values()).size === detachedStorageTable.size, 0x391 /* Redirect table size must match BlobManager's local ID count */);
|
|
570
|
+
// Taking a snapshot of the redirect table entries before iterating, because
|
|
571
|
+
// we will be adding identity mappings to the the redirect table as we iterate
|
|
572
|
+
// and we don't want to include those in the iteration.
|
|
573
|
+
const redirectTableEntries = [...this.redirectTable.entries()];
|
|
574
|
+
for (const [localId, detachedStorageId] of redirectTableEntries) {
|
|
575
|
+
const newStorageId = detachedStorageTable.get(detachedStorageId);
|
|
576
|
+
assert(newStorageId !== undefined, "Couldn't find a matching storage ID");
|
|
577
|
+
this.setRedirection(localId, newStorageId);
|
|
575
578
|
// set identity (id -> id) entry
|
|
576
|
-
this.setRedirection(
|
|
579
|
+
this.setRedirection(newStorageId, newStorageId);
|
|
577
580
|
}
|
|
578
581
|
}
|
|
579
582
|
/**
|
|
@@ -605,15 +608,15 @@ export class BlobManager {
|
|
|
605
608
|
}
|
|
606
609
|
}
|
|
607
610
|
/**
|
|
608
|
-
* For a
|
|
611
|
+
* For a localId, returns its path in GC's graph. The node path is of the format `/<blobManagerBasePath>/<localId>`.
|
|
609
612
|
* This path must match the path of the blob handle returned by the createBlob API because blobs are marked
|
|
610
613
|
* referenced by storing these handles in a referenced DDS.
|
|
611
614
|
*/
|
|
612
|
-
const
|
|
615
|
+
const getGCNodePathFromLocalId = (localId) => `/${blobManagerBasePath}/${localId}`;
|
|
613
616
|
/**
|
|
614
|
-
* For a given GC node path, return the
|
|
617
|
+
* For a given GC node path, return the localId. The node path is of the format `/<basePath>/<localId>`.
|
|
615
618
|
*/
|
|
616
|
-
const
|
|
619
|
+
const getLocalIdFromGCNodePath = (nodePath) => {
|
|
617
620
|
const pathParts = nodePath.split("/");
|
|
618
621
|
assert(areBlobPathParts(pathParts), 0x5bd /* Invalid blob node path */);
|
|
619
622
|
return pathParts[2];
|