@fluidframework/container-runtime 2.0.0-internal.2.1.1 → 2.0.0-internal.2.2.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.
Files changed (161) hide show
  1. package/.eslintrc.js +1 -1
  2. package/dist/blobManager.d.ts +20 -5
  3. package/dist/blobManager.d.ts.map +1 -1
  4. package/dist/blobManager.js +57 -15
  5. package/dist/blobManager.js.map +1 -1
  6. package/dist/containerRuntime.d.ts +39 -40
  7. package/dist/containerRuntime.d.ts.map +1 -1
  8. package/dist/containerRuntime.js +115 -278
  9. package/dist/containerRuntime.js.map +1 -1
  10. package/dist/dataStoreContext.d.ts +3 -1
  11. package/dist/dataStoreContext.d.ts.map +1 -1
  12. package/dist/dataStoreContext.js +21 -3
  13. package/dist/dataStoreContext.js.map +1 -1
  14. package/dist/dataStores.d.ts +8 -5
  15. package/dist/dataStores.d.ts.map +1 -1
  16. package/dist/dataStores.js +26 -13
  17. package/dist/dataStores.js.map +1 -1
  18. package/dist/garbageCollection.d.ts +15 -17
  19. package/dist/garbageCollection.d.ts.map +1 -1
  20. package/dist/garbageCollection.js +92 -106
  21. package/dist/garbageCollection.js.map +1 -1
  22. package/dist/garbageCollectionConstants.d.ts +19 -0
  23. package/dist/garbageCollectionConstants.d.ts.map +1 -0
  24. package/dist/garbageCollectionConstants.js +34 -0
  25. package/dist/garbageCollectionConstants.js.map +1 -0
  26. package/dist/gcSweepReadyUsageDetection.js +2 -2
  27. package/dist/gcSweepReadyUsageDetection.js.map +1 -1
  28. package/dist/index.d.ts +4 -2
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +8 -6
  31. package/dist/index.js.map +1 -1
  32. package/dist/opLifecycle/batchManager.d.ts +30 -0
  33. package/dist/opLifecycle/batchManager.d.ts.map +1 -0
  34. package/dist/{batchManager.js → opLifecycle/batchManager.js} +25 -10
  35. package/dist/opLifecycle/batchManager.js.map +1 -0
  36. package/dist/opLifecycle/definitions.d.ts +40 -0
  37. package/dist/opLifecycle/definitions.d.ts.map +1 -0
  38. package/dist/opLifecycle/definitions.js +7 -0
  39. package/dist/opLifecycle/definitions.js.map +1 -0
  40. package/dist/opLifecycle/index.d.ts +12 -0
  41. package/dist/opLifecycle/index.d.ts.map +1 -0
  42. package/dist/opLifecycle/index.js +21 -0
  43. package/dist/opLifecycle/index.js.map +1 -0
  44. package/dist/opLifecycle/opCompressor.d.ts +18 -0
  45. package/dist/opLifecycle/opCompressor.d.ts.map +1 -0
  46. package/dist/opLifecycle/opCompressor.js +53 -0
  47. package/dist/opLifecycle/opCompressor.js.map +1 -0
  48. package/dist/opLifecycle/opDecompressor.d.ts +20 -0
  49. package/dist/opLifecycle/opDecompressor.d.ts.map +1 -0
  50. package/dist/opLifecycle/opDecompressor.js +72 -0
  51. package/dist/opLifecycle/opDecompressor.js.map +1 -0
  52. package/dist/opLifecycle/opSplitter.d.ts +17 -0
  53. package/dist/opLifecycle/opSplitter.d.ts.map +1 -0
  54. package/dist/opLifecycle/opSplitter.js +61 -0
  55. package/dist/opLifecycle/opSplitter.js.map +1 -0
  56. package/dist/opLifecycle/outbox.d.ts +47 -0
  57. package/dist/opLifecycle/outbox.d.ts.map +1 -0
  58. package/dist/opLifecycle/outbox.js +153 -0
  59. package/dist/opLifecycle/outbox.js.map +1 -0
  60. package/dist/opLifecycle/remoteMessageProcessor.d.ts +26 -0
  61. package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -0
  62. package/dist/opLifecycle/remoteMessageProcessor.js +81 -0
  63. package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -0
  64. package/dist/packageVersion.d.ts +1 -1
  65. package/dist/packageVersion.js +1 -1
  66. package/dist/packageVersion.js.map +1 -1
  67. package/dist/summaryFormat.js +2 -2
  68. package/dist/summaryFormat.js.map +1 -1
  69. package/lib/blobManager.d.ts +20 -5
  70. package/lib/blobManager.d.ts.map +1 -1
  71. package/lib/blobManager.js +59 -17
  72. package/lib/blobManager.js.map +1 -1
  73. package/lib/containerRuntime.d.ts +39 -40
  74. package/lib/containerRuntime.d.ts.map +1 -1
  75. package/lib/containerRuntime.js +113 -275
  76. package/lib/containerRuntime.js.map +1 -1
  77. package/lib/dataStoreContext.d.ts +3 -1
  78. package/lib/dataStoreContext.d.ts.map +1 -1
  79. package/lib/dataStoreContext.js +23 -5
  80. package/lib/dataStoreContext.js.map +1 -1
  81. package/lib/dataStores.d.ts +8 -5
  82. package/lib/dataStores.d.ts.map +1 -1
  83. package/lib/dataStores.js +28 -15
  84. package/lib/dataStores.js.map +1 -1
  85. package/lib/garbageCollection.d.ts +15 -17
  86. package/lib/garbageCollection.d.ts.map +1 -1
  87. package/lib/garbageCollection.js +72 -86
  88. package/lib/garbageCollection.js.map +1 -1
  89. package/lib/garbageCollectionConstants.d.ts +19 -0
  90. package/lib/garbageCollectionConstants.d.ts.map +1 -0
  91. package/lib/garbageCollectionConstants.js +31 -0
  92. package/lib/garbageCollectionConstants.js.map +1 -0
  93. package/lib/gcSweepReadyUsageDetection.js +1 -1
  94. package/lib/gcSweepReadyUsageDetection.js.map +1 -1
  95. package/lib/index.d.ts +4 -2
  96. package/lib/index.d.ts.map +1 -1
  97. package/lib/index.js +3 -2
  98. package/lib/index.js.map +1 -1
  99. package/lib/opLifecycle/batchManager.d.ts +30 -0
  100. package/lib/opLifecycle/batchManager.d.ts.map +1 -0
  101. package/lib/{batchManager.js → opLifecycle/batchManager.js} +25 -10
  102. package/lib/opLifecycle/batchManager.js.map +1 -0
  103. package/lib/opLifecycle/definitions.d.ts +40 -0
  104. package/lib/opLifecycle/definitions.d.ts.map +1 -0
  105. package/lib/opLifecycle/definitions.js +6 -0
  106. package/lib/opLifecycle/definitions.js.map +1 -0
  107. package/lib/opLifecycle/index.d.ts +12 -0
  108. package/lib/opLifecycle/index.d.ts.map +1 -0
  109. package/lib/opLifecycle/index.js +11 -0
  110. package/lib/opLifecycle/index.js.map +1 -0
  111. package/lib/opLifecycle/opCompressor.d.ts +18 -0
  112. package/lib/opLifecycle/opCompressor.d.ts.map +1 -0
  113. package/lib/opLifecycle/opCompressor.js +49 -0
  114. package/lib/opLifecycle/opCompressor.js.map +1 -0
  115. package/lib/opLifecycle/opDecompressor.d.ts +20 -0
  116. package/lib/opLifecycle/opDecompressor.d.ts.map +1 -0
  117. package/lib/opLifecycle/opDecompressor.js +68 -0
  118. package/lib/opLifecycle/opDecompressor.js.map +1 -0
  119. package/lib/opLifecycle/opSplitter.d.ts +17 -0
  120. package/lib/opLifecycle/opSplitter.d.ts.map +1 -0
  121. package/lib/opLifecycle/opSplitter.js +57 -0
  122. package/lib/opLifecycle/opSplitter.js.map +1 -0
  123. package/lib/opLifecycle/outbox.d.ts +47 -0
  124. package/lib/opLifecycle/outbox.d.ts.map +1 -0
  125. package/lib/opLifecycle/outbox.js +149 -0
  126. package/lib/opLifecycle/outbox.js.map +1 -0
  127. package/lib/opLifecycle/remoteMessageProcessor.d.ts +26 -0
  128. package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -0
  129. package/lib/opLifecycle/remoteMessageProcessor.js +76 -0
  130. package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -0
  131. package/lib/packageVersion.d.ts +1 -1
  132. package/lib/packageVersion.js +1 -1
  133. package/lib/packageVersion.js.map +1 -1
  134. package/lib/summaryFormat.js +1 -1
  135. package/lib/summaryFormat.js.map +1 -1
  136. package/package.json +35 -21
  137. package/prettier.config.cjs +8 -0
  138. package/src/blobManager.ts +74 -19
  139. package/src/containerRuntime.ts +144 -341
  140. package/src/dataStoreContext.ts +33 -5
  141. package/src/dataStores.ts +32 -16
  142. package/src/garbageCollection.ts +106 -82
  143. package/src/garbageCollectionConstants.ts +35 -0
  144. package/src/gcSweepReadyUsageDetection.ts +1 -1
  145. package/src/index.ts +6 -4
  146. package/src/{batchManager.ts → opLifecycle/batchManager.ts} +41 -23
  147. package/src/opLifecycle/definitions.ts +44 -0
  148. package/src/opLifecycle/index.ts +17 -0
  149. package/src/opLifecycle/opCompressor.ts +64 -0
  150. package/src/opLifecycle/opDecompressor.ts +84 -0
  151. package/src/opLifecycle/opSplitter.ts +78 -0
  152. package/src/opLifecycle/outbox.ts +204 -0
  153. package/src/opLifecycle/remoteMessageProcessor.ts +90 -0
  154. package/src/packageVersion.ts +1 -1
  155. package/src/summaryFormat.ts +1 -1
  156. package/dist/batchManager.d.ts +0 -36
  157. package/dist/batchManager.d.ts.map +0 -1
  158. package/dist/batchManager.js.map +0 -1
  159. package/lib/batchManager.d.ts +0 -36
  160. package/lib/batchManager.d.ts.map +0 -1
  161. package/lib/batchManager.js.map +0 -1
@@ -7,18 +7,24 @@ import { v4 as uuid } from "uuid";
7
7
  import { IFluidHandle, IFluidHandleContext } from "@fluidframework/core-interfaces";
8
8
  import { IDocumentStorageService } from "@fluidframework/driver-definitions";
9
9
  import { ICreateBlobResponse, ISequencedDocumentMessage, ISnapshotTree } from "@fluidframework/protocol-definitions";
10
- import { generateHandleContextPath, SummaryTreeBuilder } from "@fluidframework/runtime-utils";
11
- import { ITelemetryLogger } from "@fluidframework/common-definitions";
10
+ import {
11
+ createResponseError,
12
+ generateHandleContextPath,
13
+ responseToException,
14
+ SummaryTreeBuilder,
15
+ } from "@fluidframework/runtime-utils";
12
16
  import { assert, bufferToString, Deferred, stringToBuffer, TypedEventEmitter } from "@fluidframework/common-utils";
13
17
  import { IContainerRuntime, IContainerRuntimeEvents } from "@fluidframework/container-runtime-definitions";
14
18
  import { AttachState } from "@fluidframework/container-definitions";
15
- import { ChildLogger, PerformanceEvent } from "@fluidframework/telemetry-utils";
19
+ import { ChildLogger, loggerToMonitoringContext, MonitoringContext, PerformanceEvent } from "@fluidframework/telemetry-utils";
16
20
  import {
17
21
  IGarbageCollectionData,
18
22
  ISummaryTreeWithStats,
19
23
  ITelemetryContext,
20
24
  } from "@fluidframework/runtime-definitions";
21
25
  import { Throttler, formExponentialFn, IThrottler } from "./throttler";
26
+ import { summarizerClientType } from "./summarizerClientElection";
27
+ import { throwOnTombstoneUsageKey } from "./garbageCollectionConstants";
22
28
 
23
29
  /**
24
30
  * This class represents blob (long string)
@@ -83,7 +89,7 @@ export interface IBlobManagerLoadInfo {
83
89
  // Restrict the IContainerRuntime interface to the subset required by BlobManager. This helps to make
84
90
  // the contract explicit and reduces the amount of mocking required for tests.
85
91
  export type IBlobManagerRuntime =
86
- Pick<IContainerRuntime, "attachState" | "connected" | "logger"> & TypedEventEmitter<IContainerRuntimeEvents>;
92
+ Pick<IContainerRuntime, "attachState" | "connected" | "logger" | "clientDetails"> & TypedEventEmitter<IContainerRuntimeEvents>;
87
93
 
88
94
  // Note that while offline we "submit" an op before uploading the blob, but we always
89
95
  // expect blobs to be uploaded before we actually see the op round-trip
@@ -107,7 +113,7 @@ export interface IPendingBlobs { [id: string]: { blob: string; }; }
107
113
  export class BlobManager {
108
114
  public static readonly basePath = "_blobs";
109
115
  private static readonly redirectTableBlobName = ".redirectTable";
110
- private readonly logger: ITelemetryLogger;
116
+ private readonly mc: MonitoringContext;
111
117
 
112
118
  /**
113
119
  * Map of local (offline/detached) IDs to storage IDs. Contains identity entries
@@ -137,6 +143,14 @@ export class BlobManager {
137
143
  formExponentialFn({ coefficient: 20, initialDelay: 0 }),
138
144
  ));
139
145
 
146
+ /** If true, throw an error when a tombstone attachment blob is retrieved. */
147
+ private readonly throwOnTombstoneUsage: boolean;
148
+ /**
149
+ * This stores ides of tombstoned blobs.
150
+ * Tombstone is a temporary feature that imitates a blob getting swept by garbage collection.
151
+ */
152
+ private readonly tombstonedBlobs: Set<string> = new Set();
153
+
140
154
  constructor(
141
155
  private readonly routeContext: IFluidHandleContext,
142
156
  snapshot: IBlobManagerLoadInfo,
@@ -158,7 +172,12 @@ export class BlobManager {
158
172
  private readonly runtime: IBlobManagerRuntime,
159
173
  stashedBlobs: IPendingBlobs = {},
160
174
  ) {
161
- this.logger = ChildLogger.create(this.runtime.logger, "BlobManager");
175
+ this.mc = loggerToMonitoringContext(ChildLogger.create(this.runtime.logger, "BlobManager"));
176
+ // Read the feature flag that tells whether to throw when a tombstone blob is requested.
177
+ this.throwOnTombstoneUsage =
178
+ this.mc.config.getBoolean(throwOnTombstoneUsageKey) === true &&
179
+ this.runtime.clientDetails.type !== summarizerClientType;
180
+
162
181
  this.runtime.on("disconnected", () => this.onDisconnected());
163
182
  this.redirectTable = this.load(snapshot);
164
183
 
@@ -189,7 +208,7 @@ export class BlobManager {
189
208
  public async onConnected() {
190
209
  this.retryThrottler.cancel();
191
210
  const pendingUploads = this.pendingOfflineUploads.map(async (e) => e.uploadP);
192
- await PerformanceEvent.timedExecAsync(this.logger, {
211
+ await PerformanceEvent.timedExecAsync(this.mc.logger, {
193
212
  eventName: "BlobUploadOnConnected",
194
213
  count: pendingUploads.length,
195
214
  }, async () => Promise.all(pendingUploads),
@@ -239,6 +258,18 @@ export class BlobManager {
239
258
  }
240
259
 
241
260
  public async getBlob(blobId: string): Promise<ArrayBufferLike> {
261
+ const request = { url: blobId };
262
+ if (this.tombstonedBlobs.has(blobId) ) {
263
+ const error = responseToException(createResponseError(404, "Blob removed by gc", request), request);
264
+ this.mc.logger.sendErrorEvent({
265
+ eventName: "GC_Tombstone_Blob_Requested",
266
+ url: request.url,
267
+ }, error);
268
+ if (this.throwOnTombstoneUsage) {
269
+ throw error;
270
+ }
271
+ }
272
+
242
273
  const pending = this.pendingBlobs.get(blobId);
243
274
  if (pending) {
244
275
  return pending.blob;
@@ -259,7 +290,7 @@ export class BlobManager {
259
290
  this.gcNodeUpdated(this.getBlobGCNodePath(blobId));
260
291
 
261
292
  return PerformanceEvent.timedExecAsync(
262
- this.logger,
293
+ this.mc.logger,
263
294
  { eventName: "AttachmentReadBlob", id: storageId },
264
295
  async () => {
265
296
  return this.getStorage().readBlob(storageId);
@@ -292,7 +323,7 @@ export class BlobManager {
292
323
  }
293
324
  if (this.runtime.attachState === AttachState.Attaching) {
294
325
  // blob upload is not supported in "Attaching" state
295
- this.logger.sendTelemetryEvent({ eventName: "CreateBlobWhileAttaching" });
326
+ this.mc.logger.sendTelemetryEvent({ eventName: "CreateBlobWhileAttaching" });
296
327
  await new Promise<void>((resolve) => this.runtime.once("attached", resolve));
297
328
  }
298
329
  assert(this.runtime.attachState === AttachState.Attached,
@@ -314,7 +345,7 @@ export class BlobManager {
314
345
 
315
346
  private async uploadBlob(localId: string, blob: ArrayBufferLike): Promise<ICreateBlobResponse> {
316
347
  return PerformanceEvent.timedExecAsync(
317
- this.logger,
348
+ this.mc.logger,
318
349
  { eventName: "createBlob" },
319
350
  async () => this.getStorage().createBlob(blob),
320
351
  { end: true, cancel: this.runtime.connected ? "error" : "generic" },
@@ -351,7 +382,7 @@ export class BlobManager {
351
382
  }
352
383
  } else {
353
384
  // connected to storage but not ordering service?
354
- this.logger.sendTelemetryEvent({ eventName: "BlobUploadSuccessWhileDisconnected" });
385
+ this.mc.logger.sendTelemetryEvent({ eventName: "BlobUploadSuccessWhileDisconnected" });
355
386
  if (entry.status === PendingBlobStatus.OnlinePendingUpload) {
356
387
  this.transitionToOffline(localId);
357
388
  }
@@ -477,7 +508,7 @@ export class BlobManager {
477
508
  * Load a set of previously attached blob IDs and redirect table from a previous snapshot.
478
509
  */
479
510
  private load(snapshot: IBlobManagerLoadInfo): Map<string, string | undefined> {
480
- this.logger.sendTelemetryEvent({
511
+ this.mc.logger.sendTelemetryEvent({
481
512
  eventName: "AttachmentBlobsLoaded",
482
513
  count: snapshot.ids?.length ?? 0,
483
514
  redirectTable: snapshot.redirectTable?.length,
@@ -524,10 +555,32 @@ export class BlobManager {
524
555
  }
525
556
 
526
557
  /**
527
- * When running GC in test mode, this is called to delete blobs that are unused.
528
- * @param unusedRoutes - These are the blob node ids that are unused and should be deleted.
558
+ * This is called to update blobs whose routes are used. The used blobs are removed from the tombstone list.
559
+ * @param usedRoutes - The routes of the blob nodes that are used.
529
560
  */
530
- public deleteUnusedRoutes(unusedRoutes: string[]): void {
561
+ public updateUsedRoutes(usedRoutes: string[]) {
562
+ // The routes or blob node paths are in the same format as returned in getGCData -
563
+ // `/<BlobManager.basePath>/<blobId>`.
564
+ for (const route of usedRoutes) {
565
+ const pathParts = route.split("/");
566
+ assert(
567
+ pathParts.length === 3 && pathParts[1] === BlobManager.basePath,
568
+ 0x4bc /* Invalid blob node id in used routes. */,
569
+ );
570
+ const blobId = pathParts[2];
571
+ // Un-tombstone the blob if it was marked tombstone.
572
+ this.tombstonedBlobs.delete(blobId);
573
+ }
574
+ }
575
+
576
+ /**
577
+ * This is called to update blobs whose routes are unused. The unused blobs are either deleted or marked as
578
+ * tombstones.
579
+ * @param unusedRoutes - The routes of the blob nodes that are unused.
580
+ * @param tombstone - if true, the objects corresponding to unused routes are marked tombstones. Otherwise, they
581
+ * are deleted.
582
+ */
583
+ public updateUnusedRoutes(unusedRoutes: string[], tombstone: boolean): void {
531
584
  // The routes or blob node paths are in the same format as returned in getGCData -
532
585
  // `/<BlobManager.basePath>/<blobId>`.
533
586
  for (const route of unusedRoutes) {
@@ -538,11 +591,13 @@ export class BlobManager {
538
591
  );
539
592
  const blobId = pathParts[2];
540
593
 
541
- // The unused blobId could be a localId. If so, remove it from the redirect table and continue. The
542
- // corresponding storageId may still be used either directly or via other localIds.
543
- if (this.redirectTable?.has(blobId)) {
594
+ if (tombstone) {
595
+ // If tombstone is set, add this blob to the tombstone list.
596
+ this.tombstonedBlobs.add(blobId);
597
+ } else {
598
+ // The unused blobId could be a localId. If so, remove it from the redirect table and continue. The
599
+ // corresponding storageId may still be used either directly or via other localIds.
544
600
  this.redirectTable.delete(blobId);
545
- continue;
546
601
  }
547
602
  }
548
603
  }