@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.
Files changed (40) hide show
  1. package/.mocharc.cjs +1 -2
  2. package/container-runtime.test-files.tar +0 -0
  3. package/dist/blobManager/blobManager.d.ts +21 -15
  4. package/dist/blobManager/blobManager.d.ts.map +1 -1
  5. package/dist/blobManager/blobManager.js +105 -102
  6. package/dist/blobManager/blobManager.js.map +1 -1
  7. package/dist/blobManager/blobManagerSnapSum.d.ts +4 -4
  8. package/dist/blobManager/blobManagerSnapSum.d.ts.map +1 -1
  9. package/dist/blobManager/blobManagerSnapSum.js +30 -33
  10. package/dist/blobManager/blobManagerSnapSum.js.map +1 -1
  11. package/dist/containerRuntime.js +1 -1
  12. package/dist/containerRuntime.js.map +1 -1
  13. package/dist/legacy.d.ts +2 -1
  14. package/dist/packageVersion.d.ts +1 -1
  15. package/dist/packageVersion.d.ts.map +1 -1
  16. package/dist/packageVersion.js +1 -1
  17. package/dist/packageVersion.js.map +1 -1
  18. package/internal.d.ts +1 -1
  19. package/legacy.d.ts +1 -1
  20. package/lib/blobManager/blobManager.d.ts +21 -15
  21. package/lib/blobManager/blobManager.d.ts.map +1 -1
  22. package/lib/blobManager/blobManager.js +105 -102
  23. package/lib/blobManager/blobManager.js.map +1 -1
  24. package/lib/blobManager/blobManagerSnapSum.d.ts +4 -4
  25. package/lib/blobManager/blobManagerSnapSum.d.ts.map +1 -1
  26. package/lib/blobManager/blobManagerSnapSum.js +26 -29
  27. package/lib/blobManager/blobManagerSnapSum.js.map +1 -1
  28. package/lib/containerRuntime.js +1 -1
  29. package/lib/containerRuntime.js.map +1 -1
  30. package/lib/legacy.d.ts +2 -1
  31. package/lib/packageVersion.d.ts +1 -1
  32. package/lib/packageVersion.d.ts.map +1 -1
  33. package/lib/packageVersion.js +1 -1
  34. package/lib/packageVersion.js.map +1 -1
  35. package/lib/tsdoc-metadata.json +1 -1
  36. package/package.json +26 -26
  37. package/src/blobManager/blobManager.ts +118 -121
  38. package/src/blobManager/blobManagerSnapSum.ts +31 -53
  39. package/src/containerRuntime.ts +1 -1
  40. package/src/packageVersion.ts +1 -1
package/.mocharc.cjs CHANGED
@@ -7,8 +7,7 @@
7
7
 
8
8
  const getFluidTestMochaConfig = require("@fluid-internal/mocha-test-setup/mocharc-common");
9
9
 
10
- const packageDir = __dirname;
11
- const config = getFluidTestMochaConfig(packageDir);
10
+ const config = getFluidTestMochaConfig(__dirname);
12
11
  // TODO: figure out why this package needs the --exit flag, tests might not be cleaning up correctly after themselves
13
12
  // AB#7856
14
13
  config.exit = true;
Binary file
@@ -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. Contains identity entries (storageId storageId) for storage IDs. All requested IDs should
63
- * be a key in this map. Blobs created while the container is detached are stored in IDetachedBlobStorage which
64
- * gives local IDs; the storage IDs are filled in at attach time.
65
- * Note: It contains mappings from all clients, i.e., from remote clients as well. local ID comes from the client
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 localBlobIdGenerator;
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 localBlobIdGenerator?: (() => string) | undefined;
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(blobId: string): boolean;
111
+ hasBlob(localId: string): boolean;
113
112
  /**
114
113
  * Retrieve the blob with the given local blob id.
115
- * @param blobId - The local blob id. Likely coming from a handle.
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(blobId: string, payloadPending: boolean): Promise<ArrayBufferLike>;
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>/<blobId>`. The blob ids are all local ids.
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 in-use anymore, the redirect table entries
172
- * for the storage ids are deleted as well.
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 remove them the next summary. The service would them delete them some time in the future.
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
- setRedirectTable(table: Map<string, string>): void;
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":"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"}
1
+ {"version":3,"file":"blobManager.d.ts","sourceRoot":"","sources":["../../src/blobManager/blobManager.ts"],"names":[],"mappings":"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"}
@@ -78,20 +78,20 @@ class BlobManager {
78
78
  * because we know that the server will not delete the blob corresponding to that storage ID.
79
79
  */
80
80
  this.opsInFlight = new Map();
81
- const { routeContext, blobManagerLoadInfo, storage, sendBlobAttachOp, blobRequested, isBlobDeleted, runtime, localBlobIdGenerator, createBlobPayloadPending, } = props;
81
+ const { routeContext, blobManagerLoadInfo, storage, sendBlobAttachOp, blobRequested, isBlobDeleted, runtime, localIdGenerator, createBlobPayloadPending, } = props;
82
82
  this.routeContext = routeContext;
83
83
  this.storage = storage;
84
84
  this.blobRequested = blobRequested;
85
85
  this.isBlobDeleted = isBlobDeleted;
86
86
  this.runtime = runtime;
87
- this.localBlobIdGenerator = localBlobIdGenerator ?? uuid_1.v4;
87
+ this.localIdGenerator = localIdGenerator ?? uuid_1.v4;
88
88
  this.createBlobPayloadPending = createBlobPayloadPending;
89
89
  this.mc = (0, internal_4.createChildMonitoringContext)({
90
90
  logger: this.runtime.baseLogger,
91
91
  namespace: "BlobManager",
92
92
  });
93
- this.redirectTable = (0, blobManagerSnapSum_js_1.toRedirectTable)(blobManagerLoadInfo, this.mc.logger, this.runtime.attachState);
94
- this.sendBlobAttachOp = (localId, blobId) => {
93
+ this.redirectTable = (0, blobManagerSnapSum_js_1.toRedirectTable)(blobManagerLoadInfo, this.mc.logger);
94
+ this.sendBlobAttachOp = (localId, storageId) => {
95
95
  const pendingEntry = this.pendingBlobs.get(localId);
96
96
  (0, internal_2.assert)(pendingEntry !== undefined, 0x725 /* Must have pending blob entry for upcoming op */);
97
97
  if (pendingEntry?.uploadTime && pendingEntry?.minTTLInSeconds) {
@@ -117,7 +117,7 @@ class BlobManager {
117
117
  }
118
118
  }
119
119
  pendingEntry.opsent = true;
120
- sendBlobAttachOp(localId, blobId);
120
+ sendBlobAttachOp(localId, storageId);
121
121
  };
122
122
  }
123
123
  get allBlobsAttached() {
@@ -138,56 +138,46 @@ class BlobManager {
138
138
  uploadTime: pending?.uploadTime,
139
139
  });
140
140
  }
141
- hasBlob(blobId) {
142
- return this.redirectTable.get(blobId) !== undefined;
141
+ hasBlob(localId) {
142
+ return this.redirectTable.get(localId) !== undefined;
143
143
  }
144
144
  /**
145
145
  * Retrieve the blob with the given local blob id.
146
- * @param blobId - The local blob id. Likely coming from a handle.
146
+ * @param localId - The local blob id. Likely coming from a handle.
147
147
  * @param payloadPending - Whether we suspect the payload may be pending and not available yet.
148
148
  * @returns A promise which resolves to the blob contents
149
149
  */
150
- async getBlob(blobId, payloadPending) {
150
+ async getBlob(localId, payloadPending) {
151
151
  // Verify that the blob is not deleted, i.e., it has not been garbage collected. If it is, this will throw
152
152
  // an error, failing the call.
153
- this.verifyBlobNotDeleted(blobId);
153
+ this.verifyBlobNotDeleted(localId);
154
154
  // Let runtime know that the corresponding GC node was requested.
155
155
  // Note that this will throw if the blob is inactive or tombstoned and throwing on incorrect usage
156
156
  // is configured.
157
- this.blobRequested(getGCNodePathFromBlobId(blobId));
158
- const pending = this.pendingBlobs.get(blobId);
157
+ this.blobRequested(getGCNodePathFromLocalId(localId));
158
+ const pending = this.pendingBlobs.get(localId);
159
159
  if (pending) {
160
160
  return pending.blob;
161
161
  }
162
- let storageId;
163
- if (this.runtime.attachState === internal_1.AttachState.Detached) {
164
- (0, internal_2.assert)(this.redirectTable.has(blobId), 0x383 /* requesting unknown blobs */);
165
- // Blobs created while the container is detached are stored in IDetachedBlobStorage.
166
- // The 'IContainerStorageService.readBlob()' call below will retrieve these via localId.
167
- storageId = blobId;
168
- }
169
- else {
170
- const attachedStorageId = this.redirectTable.get(blobId);
171
- if (!payloadPending) {
172
- // Only blob handles explicitly marked with pending payload are permitted to exist without
173
- // yet knowing their storage id. Otherwise they must already be associated with a storage id.
174
- (0, internal_2.assert)(attachedStorageId !== undefined, 0x11f /* "requesting unknown blobs" */);
175
- }
176
- // If we didn't find it in the redirectTable, assume the attach op is coming eventually and wait.
177
- // We do this even if the local client doesn't have the blob payloadPending flag enabled, in case a
178
- // remote client does have it enabled. This wait may be infinite if the uploading client failed
179
- // the upload and doesn't exist anymore.
180
- storageId =
181
- attachedStorageId ??
182
- (await new Promise((resolve) => {
183
- const onProcessBlobAttach = (localId, _storageId) => {
184
- if (localId === blobId) {
185
- this.internalEvents.off("processedBlobAttach", onProcessBlobAttach);
186
- resolve(_storageId);
187
- }
188
- };
189
- this.internalEvents.on("processedBlobAttach", onProcessBlobAttach);
190
- }));
162
+ let storageId = this.redirectTable.get(localId);
163
+ if (storageId === undefined) {
164
+ // Only blob handles explicitly marked with pending payload are permitted to exist without
165
+ // yet knowing their storage id. Otherwise they must already be associated with a storage id.
166
+ // Handles for detached blobs are not payload pending.
167
+ (0, internal_2.assert)(payloadPending, 0x11f /* "requesting unknown blobs" */);
168
+ // If we didn't find it in the redirectTable and it's payloadPending, assume the attach op is coming
169
+ // eventually and wait. We do this even if the local client doesn't have the blob payloadPending flag
170
+ // enabled, in case a remote client does have it enabled. This wait may be infinite if the uploading
171
+ // client failed the upload and doesn't exist anymore.
172
+ storageId = await new Promise((resolve) => {
173
+ const onProcessBlobAttach = (_localId, _storageId) => {
174
+ if (_localId === localId) {
175
+ this.internalEvents.off("processedBlobAttach", onProcessBlobAttach);
176
+ resolve(_storageId);
177
+ }
178
+ };
179
+ this.internalEvents.on("processedBlobAttach", onProcessBlobAttach);
180
+ });
191
181
  }
192
182
  return internal_4.PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "AttachmentReadBlob", id: storageId }, async (event) => {
193
183
  return this.storage.readBlob(storageId).catch((error) => {
@@ -211,15 +201,17 @@ class BlobManager {
211
201
  this.deletePendingBlobMaybe(localId);
212
202
  }
213
203
  : undefined;
214
- return new BlobHandle(getGCNodePathFromBlobId(localId), this.routeContext, async () => this.getBlob(localId, false), false, // payloadPending
204
+ return new BlobHandle(getGCNodePathFromLocalId(localId), this.routeContext, async () => this.getBlob(localId, false), false, // payloadPending
215
205
  callback);
216
206
  }
217
207
  async createBlobDetached(blob) {
208
+ const localId = this.localIdGenerator();
218
209
  // Blobs created while the container is detached are stored in IDetachedBlobStorage.
219
- // The 'IContainerStorageService.createBlob()' call below will respond with a localId.
220
- const response = await this.storage.createBlob(blob);
221
- this.setRedirection(response.id, undefined);
222
- return this.getBlobHandle(response.id);
210
+ // The 'IContainerStorageService.createBlob()' call below will respond with a pseudo storage ID.
211
+ // That pseudo storage ID will be replaced with the real storage ID at attach time.
212
+ const { id: detachedStorageId } = await this.storage.createBlob(blob);
213
+ this.setRedirection(localId, detachedStorageId);
214
+ return this.getBlobHandle(localId);
223
215
  }
224
216
  async createBlob(blob, signal) {
225
217
  if (this.runtime.attachState === internal_1.AttachState.Detached) {
@@ -241,7 +233,7 @@ class BlobManager {
241
233
  }
242
234
  // Create a local ID for the blob. After uploading it to storage and before returning it, a local ID to
243
235
  // storage ID mapping is created.
244
- const localId = this.localBlobIdGenerator();
236
+ const localId = this.localIdGenerator();
245
237
  const pendingEntry = {
246
238
  blob,
247
239
  handleP: new internal_2.Deferred(),
@@ -263,8 +255,8 @@ class BlobManager {
263
255
  });
264
256
  }
265
257
  createBlobWithPayloadPending(blob) {
266
- const localId = this.localBlobIdGenerator();
267
- const blobHandle = new BlobHandle(getGCNodePathFromBlobId(localId), this.routeContext, async () => blob, true, // payloadPending
258
+ const localId = this.localIdGenerator();
259
+ const blobHandle = new BlobHandle(getGCNodePathFromLocalId(localId), this.routeContext, async () => blob, true, // payloadPending
268
260
  () => {
269
261
  const pendingEntry = {
270
262
  blob,
@@ -379,7 +371,7 @@ class BlobManager {
379
371
  if (!entry.opsent) {
380
372
  this.sendBlobAttachOp(localId, response.id);
381
373
  }
382
- const storageIds = (0, blobManagerSnapSum_js_1.getStorageIds)(this.redirectTable, this.runtime.attachState);
374
+ const storageIds = (0, blobManagerSnapSum_js_1.getStorageIds)(this.redirectTable);
383
375
  if (storageIds.has(response.id)) {
384
376
  // The blob is de-duped. Set up a local ID to storage ID mapping and return the blob. Since this is
385
377
  // an existing blob, we don't have to wait for the op to be ack'd since this step has already
@@ -412,7 +404,7 @@ class BlobManager {
412
404
  */
413
405
  reSubmit(metadata) {
414
406
  (0, internal_2.assert)((0, metadata_js_1.isBlobMetadata)(metadata), 0xc01 /* Expected blob metadata for a BlobAttach op */);
415
- const { localId, blobId } = metadata;
407
+ const { localId, blobId: storageId } = metadata;
416
408
  // Any blob that we're actively trying to advance to attached state must have a
417
409
  // pendingBlobs entry. Decline to resubmit for anything else.
418
410
  // For example, we might be asked to resubmit stashed ops for blobs that never had
@@ -420,22 +412,22 @@ class BlobManager {
420
412
  // try to attach them since they won't be accessible to the customer and would just
421
413
  // be considered garbage immediately.
422
414
  if (this.pendingBlobs.has(localId)) {
423
- this.sendBlobAttachOp(localId, blobId);
415
+ this.sendBlobAttachOp(localId, storageId);
424
416
  }
425
417
  }
426
418
  processBlobAttachMessage(message, local) {
427
419
  (0, internal_2.assert)((0, metadata_js_1.isBlobMetadata)(message.metadata), 0xc02 /* Expected blob metadata for a BlobAttach op */);
428
- const { localId, blobId } = message.metadata;
420
+ const { localId, blobId: storageId } = message.metadata;
429
421
  const pendingEntry = this.pendingBlobs.get(localId);
430
422
  if (pendingEntry?.abortSignal?.aborted) {
431
423
  this.deletePendingBlob(localId);
432
424
  return;
433
425
  }
434
- this.setRedirection(localId, blobId);
426
+ this.setRedirection(localId, storageId);
435
427
  // set identity (id -> id) entry
436
- this.setRedirection(blobId, blobId);
428
+ this.setRedirection(storageId, storageId);
437
429
  if (local) {
438
- const waitingBlobs = this.opsInFlight.get(blobId);
430
+ const waitingBlobs = this.opsInFlight.get(storageId);
439
431
  if (waitingBlobs !== undefined) {
440
432
  // For each op corresponding to this storage ID that we are waiting for, resolve the pending blob.
441
433
  // This is safe because the server will keep the blob alive and the op containing the local ID to
@@ -443,14 +435,14 @@ class BlobManager {
443
435
  for (const pendingLocalId of waitingBlobs) {
444
436
  const entry = this.pendingBlobs.get(pendingLocalId);
445
437
  (0, internal_2.assert)(entry !== undefined, 0x38f /* local online BlobAttach op with no pending blob entry */);
446
- this.setRedirection(pendingLocalId, blobId);
438
+ this.setRedirection(pendingLocalId, storageId);
447
439
  entry.acked = true;
448
440
  const blobHandle = this.getBlobHandle(pendingLocalId);
449
441
  blobHandle.notifyShared();
450
442
  entry.handleP.resolve(blobHandle);
451
443
  this.deletePendingBlobMaybe(pendingLocalId);
452
444
  }
453
- this.opsInFlight.delete(blobId);
445
+ this.opsInFlight.delete(storageId);
454
446
  }
455
447
  const localEntry = this.pendingBlobs.get(localId);
456
448
  if (localEntry) {
@@ -461,10 +453,10 @@ class BlobManager {
461
453
  this.deletePendingBlobMaybe(localId);
462
454
  }
463
455
  }
464
- this.internalEvents.emit("processedBlobAttach", localId, blobId);
456
+ this.internalEvents.emit("processedBlobAttach", localId, storageId);
465
457
  }
466
458
  summarize(telemetryContext) {
467
- return (0, blobManagerSnapSum_js_1.summarizeBlobManagerState)(this.redirectTable, this.runtime.attachState);
459
+ return (0, blobManagerSnapSum_js_1.summarizeBlobManagerState)(this.redirectTable);
468
460
  }
469
461
  /**
470
462
  * Generates data used for garbage collection. Each blob uploaded represents a node in the GC graph as it can be
@@ -475,13 +467,12 @@ class BlobManager {
475
467
  getGCData(fullGC = false) {
476
468
  const gcData = { gcNodes: {} };
477
469
  for (const [localId, storageId] of this.redirectTable) {
478
- (0, internal_2.assert)(!!storageId, 0x390 /* Must be attached to get GC data */);
479
- // Only return local ids as GC nodes because a blob can only be referenced via its local id. The storage
480
- // id entries have the same key and value, ignore them.
481
- // The outbound routes are empty because a blob node cannot reference other nodes. It can only be referenced
482
- // by adding its handle to a referenced DDS.
470
+ // Don't report the identity mappings to GC - these exist to service old handles that referenced the storage
471
+ // IDs directly. We'll implicitly clean them up if all of their localId references get GC'd first.
483
472
  if (localId !== storageId) {
484
- gcData.gcNodes[getGCNodePathFromBlobId(localId)] = [];
473
+ // The outbound routes are empty because a blob node cannot reference other nodes. It can only be referenced
474
+ // by adding its handle to a referenced DDS.
475
+ gcData.gcNodes[getGCNodePathFromLocalId(localId)] = [];
485
476
  }
486
477
  }
487
478
  return gcData;
@@ -500,55 +491,53 @@ class BlobManager {
500
491
  * Delete blobs with the given routes from the redirect table.
501
492
  *
502
493
  * @remarks
503
- * The routes are GC nodes paths of format -`/<blobManagerBasePath>/<blobId>`. The blob ids are all local ids.
494
+ * The routes are GC nodes paths of format -`/<blobManagerBasePath>/<localId>`.
504
495
  * Deleting the blobs involves 2 steps:
505
496
  *
506
497
  * 1. The redirect table entry for the local ids are deleted.
507
498
  *
508
- * 2. If the storage ids corresponding to the deleted local ids are not in-use anymore, the redirect table entries
509
- * for the storage ids are deleted as well.
499
+ * 2. If the storage ids corresponding to the deleted local ids are not referenced by any further local ids, the
500
+ * identity mappings in the redirect table are deleted as well.
510
501
  *
511
502
  * Note that this does not delete the blobs from storage service immediately. Deleting the blobs from redirect table
512
- * will remove them the next summary. The service would them delete them some time in the future.
503
+ * will ensure we don't create an attachment blob for them at the next summary. The service would then delete them
504
+ * some time in the future.
513
505
  */
514
506
  deleteBlobsFromRedirectTable(blobRoutes) {
515
- if (blobRoutes.length === 0) {
516
- return;
517
- }
518
- // This tracks the storage ids of local ids that are deleted. After the local ids have been deleted, if any of
519
- // these storage ids are unused, they will be deleted as well.
507
+ // maybeUnusedStorageIds is used to compute the set of storage IDs that *used to have a local ID*, but that
508
+ // local ID is being deleted.
520
509
  const maybeUnusedStorageIds = new Set();
521
510
  for (const route of blobRoutes) {
522
- const blobId = getBlobIdFromGCNodePath(route);
511
+ const localId = getLocalIdFromGCNodePath(route);
523
512
  // If the blob hasn't already been deleted, log an error because this should never happen.
524
513
  // If the blob has already been deleted, log a telemetry event. This can happen because multiple GC
525
514
  // sweep ops can contain the same data store. It would be interesting to track how often this happens.
526
515
  const alreadyDeleted = this.isBlobDeleted(route);
527
- if (!this.redirectTable.has(blobId)) {
516
+ const storageId = this.redirectTable.get(localId);
517
+ if (storageId === undefined) {
528
518
  this.mc.logger.sendTelemetryEvent({
529
519
  eventName: "DeletedAttachmentBlobNotFound",
530
520
  category: alreadyDeleted ? "generic" : "error",
531
- blobId,
521
+ blobId: localId,
532
522
  details: { alreadyDeleted },
533
523
  });
534
524
  continue;
535
525
  }
536
- const storageId = this.redirectTable.get(blobId);
537
- (0, internal_2.assert)(!!storageId, 0x5bb /* Must be attached to run GC */);
538
526
  maybeUnusedStorageIds.add(storageId);
539
- this.redirectTable.delete(blobId);
527
+ this.redirectTable.delete(localId);
540
528
  }
541
- // Find out storage ids that are in-use and remove them from maybeUnusedStorageIds. A storage id is in-use if
542
- // the redirect table has a local id -> storage id entry for it.
543
- for (const [localId, storageId] of this.redirectTable.entries()) {
544
- (0, internal_2.assert)(!!storageId, 0x5bc /* Must be attached to run GC */);
545
- // For every storage id, the redirect table has a id -> id entry. These do not make the storage id in-use.
546
- if (maybeUnusedStorageIds.has(storageId) && localId !== storageId) {
529
+ // Remove any storage IDs that still have local IDs referring to them (excluding the identity mapping).
530
+ for (const [localId, storageId] of this.redirectTable) {
531
+ if (localId !== storageId) {
547
532
  maybeUnusedStorageIds.delete(storageId);
548
533
  }
549
534
  }
550
- // For unused storage ids, delete their id -> id entries from the redirect table.
551
- // This way they'll be absent from the next summary, and the service is free to delete them from storage.
535
+ // Now delete any identity mappings (storage ID -> storage ID) from the redirect table that used to be
536
+ // referenced by a distinct local ID. This way they'll be absent from the next summary, and the service
537
+ // is free to delete them from storage.
538
+ // WARNING: This can potentially delete identity mappings that are still referenced, if storage deduping
539
+ // has let us add a local ID -> storage ID mapping that is later deleted. AB#47337 tracks this issue
540
+ // and possible solutions.
552
541
  for (const storageId of maybeUnusedStorageIds) {
553
542
  this.redirectTable.delete(storageId);
554
543
  }
@@ -557,11 +546,11 @@ class BlobManager {
557
546
  * Verifies that the blob with given id is not deleted, i.e., it has not been garbage collected. If the blob is GC'd,
558
547
  * log an error and throw if necessary.
559
548
  */
560
- verifyBlobNotDeleted(blobId) {
561
- if (!this.isBlobDeleted(getGCNodePathFromBlobId(blobId))) {
549
+ verifyBlobNotDeleted(localId) {
550
+ if (!this.isBlobDeleted(getGCNodePathFromLocalId(localId))) {
562
551
  return;
563
552
  }
564
- const request = { url: blobId };
553
+ const request = { url: localId };
565
554
  const error = (0, internal_3.responseToException)((0, internal_3.createResponseError)(404, `Blob was deleted`, request), request);
566
555
  // Only log deleted events. Tombstone events are logged by garbage collector.
567
556
  this.mc.logger.sendErrorEvent({
@@ -570,14 +559,28 @@ class BlobManager {
570
559
  }, error);
571
560
  throw error;
572
561
  }
573
- setRedirectTable(table) {
562
+ /**
563
+ * Called in detached state just prior to attaching, this will update the redirect table by
564
+ * converting the pseudo storage IDs into real storage IDs using the provided detachedStorageTable.
565
+ * The provided table must have exactly the same set of pseudo storage IDs as are found in the redirect table.
566
+ * @param detachedStorageTable - A map of pseudo storage IDs to real storage IDs.
567
+ */
568
+ patchRedirectTable(detachedStorageTable) {
574
569
  (0, internal_2.assert)(this.runtime.attachState === internal_1.AttachState.Detached, 0x252 /* "redirect table can only be set in detached container" */);
575
- (0, internal_2.assert)(this.redirectTable.size === table.size, 0x391 /* Redirect table size must match BlobManager's local ID count */);
576
- for (const [localId, storageId] of table) {
577
- (0, internal_2.assert)(this.redirectTable.has(localId), 0x254 /* "unrecognized id in redirect table" */);
578
- this.setRedirection(localId, storageId);
570
+ // The values of the redirect table are the pseudo storage IDs, which are the keys of the
571
+ // detachedStorageTable. We expect to have a many:1 mapping from local IDs to pseudo
572
+ // storage IDs (many in the case that the storage dedupes the blob).
573
+ (0, internal_2.assert)(new Set(this.redirectTable.values()).size === detachedStorageTable.size, 0x391 /* Redirect table size must match BlobManager's local ID count */);
574
+ // Taking a snapshot of the redirect table entries before iterating, because
575
+ // we will be adding identity mappings to the the redirect table as we iterate
576
+ // and we don't want to include those in the iteration.
577
+ const redirectTableEntries = [...this.redirectTable.entries()];
578
+ for (const [localId, detachedStorageId] of redirectTableEntries) {
579
+ const newStorageId = detachedStorageTable.get(detachedStorageId);
580
+ (0, internal_2.assert)(newStorageId !== undefined, "Couldn't find a matching storage ID");
581
+ this.setRedirection(localId, newStorageId);
579
582
  // set identity (id -> id) entry
580
- this.setRedirection(storageId, storageId);
583
+ this.setRedirection(newStorageId, newStorageId);
581
584
  }
582
585
  }
583
586
  /**
@@ -610,15 +613,15 @@ class BlobManager {
610
613
  }
611
614
  exports.BlobManager = BlobManager;
612
615
  /**
613
- * For a blobId, returns its path in GC's graph. The node path is of the format `/<blobManagerBasePath>/<blobId>`.
616
+ * For a localId, returns its path in GC's graph. The node path is of the format `/<blobManagerBasePath>/<localId>`.
614
617
  * This path must match the path of the blob handle returned by the createBlob API because blobs are marked
615
618
  * referenced by storing these handles in a referenced DDS.
616
619
  */
617
- const getGCNodePathFromBlobId = (blobId) => `/${exports.blobManagerBasePath}/${blobId}`;
620
+ const getGCNodePathFromLocalId = (localId) => `/${exports.blobManagerBasePath}/${localId}`;
618
621
  /**
619
- * For a given GC node path, return the blobId. The node path is of the format `/<basePath>/<blobId>`.
622
+ * For a given GC node path, return the localId. The node path is of the format `/<basePath>/<localId>`.
620
623
  */
621
- const getBlobIdFromGCNodePath = (nodePath) => {
624
+ const getLocalIdFromGCNodePath = (nodePath) => {
622
625
  const pathParts = nodePath.split("/");
623
626
  (0, internal_2.assert)(areBlobPathParts(pathParts), 0x5bd /* Invalid blob node path */);
624
627
  return pathParts[2];