@fluidframework/container-runtime 2.1.0-274160 → 2.1.0-276985

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 (201) hide show
  1. package/README.md +14 -5
  2. package/container-runtime.test-files.tar +0 -0
  3. package/{lib → dist/blobManager}/blobManager.d.ts +9 -28
  4. package/dist/blobManager/blobManager.d.ts.map +1 -0
  5. package/dist/{blobManager.js → blobManager/blobManager.js} +23 -83
  6. package/dist/blobManager/blobManager.js.map +1 -0
  7. package/dist/blobManager/blobManagerSnapSum.d.ts +30 -0
  8. package/dist/blobManager/blobManagerSnapSum.d.ts.map +1 -0
  9. package/dist/blobManager/blobManagerSnapSum.js +82 -0
  10. package/dist/blobManager/blobManagerSnapSum.js.map +1 -0
  11. package/dist/blobManager/index.d.ts +7 -0
  12. package/dist/blobManager/index.d.ts.map +1 -0
  13. package/dist/blobManager/index.js +16 -0
  14. package/dist/blobManager/index.js.map +1 -0
  15. package/dist/channelCollection.d.ts +1 -0
  16. package/dist/channelCollection.d.ts.map +1 -1
  17. package/dist/channelCollection.js +23 -13
  18. package/dist/channelCollection.js.map +1 -1
  19. package/dist/containerRuntime.d.ts +20 -7
  20. package/dist/containerRuntime.d.ts.map +1 -1
  21. package/dist/containerRuntime.js +82 -77
  22. package/dist/containerRuntime.js.map +1 -1
  23. package/dist/dataStoreContext.d.ts +1 -0
  24. package/dist/dataStoreContext.d.ts.map +1 -1
  25. package/dist/dataStoreContext.js +7 -2
  26. package/dist/dataStoreContext.js.map +1 -1
  27. package/dist/gc/garbageCollection.js +2 -2
  28. package/dist/gc/garbageCollection.js.map +1 -1
  29. package/dist/gc/gcDefinitions.d.ts +9 -0
  30. package/dist/gc/gcDefinitions.d.ts.map +1 -1
  31. package/dist/gc/gcDefinitions.js +1 -0
  32. package/dist/gc/gcDefinitions.js.map +1 -1
  33. package/dist/index.d.ts +1 -1
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js.map +1 -1
  36. package/dist/legacy.d.ts +1 -1
  37. package/dist/messageTypes.d.ts +1 -0
  38. package/dist/messageTypes.d.ts.map +1 -1
  39. package/dist/messageTypes.js +1 -0
  40. package/dist/messageTypes.js.map +1 -1
  41. package/dist/opLifecycle/batchManager.d.ts +4 -0
  42. package/dist/opLifecycle/batchManager.d.ts.map +1 -1
  43. package/dist/opLifecycle/batchManager.js.map +1 -1
  44. package/dist/opLifecycle/outbox.d.ts +8 -4
  45. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  46. package/dist/opLifecycle/outbox.js +18 -16
  47. package/dist/opLifecycle/outbox.js.map +1 -1
  48. package/dist/opLifecycle/remoteMessageProcessor.d.ts +17 -1
  49. package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  50. package/dist/opLifecycle/remoteMessageProcessor.js +43 -5
  51. package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
  52. package/dist/packageVersion.d.ts +1 -1
  53. package/dist/packageVersion.js +1 -1
  54. package/dist/packageVersion.js.map +1 -1
  55. package/dist/pendingStateManager.d.ts +11 -7
  56. package/dist/pendingStateManager.d.ts.map +1 -1
  57. package/dist/pendingStateManager.js +34 -15
  58. package/dist/pendingStateManager.js.map +1 -1
  59. package/dist/summary/documentSchema.d.ts +8 -0
  60. package/dist/summary/documentSchema.d.ts.map +1 -1
  61. package/dist/summary/documentSchema.js +2 -0
  62. package/dist/summary/documentSchema.js.map +1 -1
  63. package/dist/summary/index.d.ts +1 -1
  64. package/dist/summary/index.d.ts.map +1 -1
  65. package/dist/summary/index.js +1 -2
  66. package/dist/summary/index.js.map +1 -1
  67. package/dist/summary/orderedClientElection.d.ts +1 -0
  68. package/dist/summary/orderedClientElection.d.ts.map +1 -1
  69. package/dist/summary/orderedClientElection.js.map +1 -1
  70. package/dist/summary/runWhileConnectedCoordinator.d.ts +1 -0
  71. package/dist/summary/runWhileConnectedCoordinator.d.ts.map +1 -1
  72. package/dist/summary/runWhileConnectedCoordinator.js.map +1 -1
  73. package/dist/summary/summarizer.d.ts +1 -0
  74. package/dist/summary/summarizer.d.ts.map +1 -1
  75. package/dist/summary/summarizer.js +1 -0
  76. package/dist/summary/summarizer.js.map +1 -1
  77. package/dist/summary/summarizerTypes.d.ts +29 -0
  78. package/dist/summary/summarizerTypes.d.ts.map +1 -1
  79. package/dist/summary/summarizerTypes.js.map +1 -1
  80. package/dist/summary/summaryCollection.d.ts +10 -0
  81. package/dist/summary/summaryCollection.d.ts.map +1 -1
  82. package/dist/summary/summaryCollection.js +1 -0
  83. package/dist/summary/summaryCollection.js.map +1 -1
  84. package/dist/summary/summaryFormat.d.ts +8 -1
  85. package/dist/summary/summaryFormat.d.ts.map +1 -1
  86. package/dist/summary/summaryFormat.js +3 -3
  87. package/dist/summary/summaryFormat.js.map +1 -1
  88. package/{dist → lib/blobManager}/blobManager.d.ts +9 -28
  89. package/lib/blobManager/blobManager.d.ts.map +1 -0
  90. package/lib/{blobManager.js → blobManager/blobManager.js} +21 -83
  91. package/lib/blobManager/blobManager.js.map +1 -0
  92. package/lib/blobManager/blobManagerSnapSum.d.ts +30 -0
  93. package/lib/blobManager/blobManagerSnapSum.d.ts.map +1 -0
  94. package/lib/blobManager/blobManagerSnapSum.js +75 -0
  95. package/lib/blobManager/blobManagerSnapSum.js.map +1 -0
  96. package/lib/blobManager/index.d.ts +7 -0
  97. package/lib/blobManager/index.d.ts.map +1 -0
  98. package/lib/blobManager/index.js +7 -0
  99. package/lib/blobManager/index.js.map +1 -0
  100. package/lib/channelCollection.d.ts +1 -0
  101. package/lib/channelCollection.d.ts.map +1 -1
  102. package/lib/channelCollection.js +23 -13
  103. package/lib/channelCollection.js.map +1 -1
  104. package/lib/containerRuntime.d.ts +20 -7
  105. package/lib/containerRuntime.d.ts.map +1 -1
  106. package/lib/containerRuntime.js +31 -26
  107. package/lib/containerRuntime.js.map +1 -1
  108. package/lib/dataStoreContext.d.ts +1 -0
  109. package/lib/dataStoreContext.d.ts.map +1 -1
  110. package/lib/dataStoreContext.js +7 -2
  111. package/lib/dataStoreContext.js.map +1 -1
  112. package/lib/gc/garbageCollection.js +2 -2
  113. package/lib/gc/garbageCollection.js.map +1 -1
  114. package/lib/gc/gcDefinitions.d.ts +9 -0
  115. package/lib/gc/gcDefinitions.d.ts.map +1 -1
  116. package/lib/gc/gcDefinitions.js +1 -0
  117. package/lib/gc/gcDefinitions.js.map +1 -1
  118. package/lib/index.d.ts +1 -1
  119. package/lib/index.d.ts.map +1 -1
  120. package/lib/index.js.map +1 -1
  121. package/lib/legacy.d.ts +1 -1
  122. package/lib/messageTypes.d.ts +1 -0
  123. package/lib/messageTypes.d.ts.map +1 -1
  124. package/lib/messageTypes.js +1 -0
  125. package/lib/messageTypes.js.map +1 -1
  126. package/lib/opLifecycle/batchManager.d.ts +4 -0
  127. package/lib/opLifecycle/batchManager.d.ts.map +1 -1
  128. package/lib/opLifecycle/batchManager.js.map +1 -1
  129. package/lib/opLifecycle/outbox.d.ts +8 -4
  130. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  131. package/lib/opLifecycle/outbox.js +18 -16
  132. package/lib/opLifecycle/outbox.js.map +1 -1
  133. package/lib/opLifecycle/remoteMessageProcessor.d.ts +17 -1
  134. package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  135. package/lib/opLifecycle/remoteMessageProcessor.js +41 -3
  136. package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
  137. package/lib/packageVersion.d.ts +1 -1
  138. package/lib/packageVersion.js +1 -1
  139. package/lib/packageVersion.js.map +1 -1
  140. package/lib/pendingStateManager.d.ts +11 -7
  141. package/lib/pendingStateManager.d.ts.map +1 -1
  142. package/lib/pendingStateManager.js +35 -16
  143. package/lib/pendingStateManager.js.map +1 -1
  144. package/lib/summary/documentSchema.d.ts +8 -0
  145. package/lib/summary/documentSchema.d.ts.map +1 -1
  146. package/lib/summary/documentSchema.js +2 -0
  147. package/lib/summary/documentSchema.js.map +1 -1
  148. package/lib/summary/index.d.ts +1 -1
  149. package/lib/summary/index.d.ts.map +1 -1
  150. package/lib/summary/index.js +1 -1
  151. package/lib/summary/index.js.map +1 -1
  152. package/lib/summary/orderedClientElection.d.ts +1 -0
  153. package/lib/summary/orderedClientElection.d.ts.map +1 -1
  154. package/lib/summary/orderedClientElection.js.map +1 -1
  155. package/lib/summary/runWhileConnectedCoordinator.d.ts +1 -0
  156. package/lib/summary/runWhileConnectedCoordinator.d.ts.map +1 -1
  157. package/lib/summary/runWhileConnectedCoordinator.js.map +1 -1
  158. package/lib/summary/summarizer.d.ts +1 -0
  159. package/lib/summary/summarizer.d.ts.map +1 -1
  160. package/lib/summary/summarizer.js +1 -0
  161. package/lib/summary/summarizer.js.map +1 -1
  162. package/lib/summary/summarizerTypes.d.ts +29 -0
  163. package/lib/summary/summarizerTypes.d.ts.map +1 -1
  164. package/lib/summary/summarizerTypes.js.map +1 -1
  165. package/lib/summary/summaryCollection.d.ts +10 -0
  166. package/lib/summary/summaryCollection.d.ts.map +1 -1
  167. package/lib/summary/summaryCollection.js +1 -0
  168. package/lib/summary/summaryCollection.js.map +1 -1
  169. package/lib/summary/summaryFormat.d.ts +8 -1
  170. package/lib/summary/summaryFormat.d.ts.map +1 -1
  171. package/lib/summary/summaryFormat.js +1 -1
  172. package/lib/summary/summaryFormat.js.map +1 -1
  173. package/package.json +23 -23
  174. package/src/{blobManager.ts → blobManager/blobManager.ts} +38 -122
  175. package/src/blobManager/blobManagerSnapSum.ts +133 -0
  176. package/src/blobManager/index.ts +19 -0
  177. package/src/channelCollection.ts +23 -13
  178. package/src/containerRuntime.ts +57 -39
  179. package/src/dataStoreContext.ts +8 -2
  180. package/src/gc/garbageCollection.ts +2 -2
  181. package/src/gc/gcDefinitions.ts +9 -0
  182. package/src/index.ts +1 -1
  183. package/src/messageTypes.ts +1 -0
  184. package/src/opLifecycle/batchManager.ts +4 -0
  185. package/src/opLifecycle/outbox.ts +19 -21
  186. package/src/opLifecycle/remoteMessageProcessor.ts +63 -6
  187. package/src/packageVersion.ts +1 -1
  188. package/src/pendingStateManager.ts +43 -20
  189. package/src/summary/documentSchema.ts +8 -0
  190. package/src/summary/index.ts +0 -1
  191. package/src/summary/orderedClientElection.ts +1 -0
  192. package/src/summary/runWhileConnectedCoordinator.ts +1 -0
  193. package/src/summary/summarizer.ts +1 -0
  194. package/src/summary/summarizerTypes.ts +29 -0
  195. package/src/summary/summaryCollection.ts +10 -0
  196. package/src/summary/summaryFormat.ts +9 -1
  197. package/dist/blobManager.d.ts.map +0 -1
  198. package/dist/blobManager.js.map +0 -1
  199. package/lib/blobManager.d.ts.map +0 -1
  200. package/lib/blobManager.js.map +0 -1
  201. /package/api-report/{container-runtime.alpha.api.md → container-runtime.legacy.alpha.api.md} +0 -0
package/README.md CHANGED
@@ -52,10 +52,12 @@ groupId.
52
52
 
53
53
  ## Using Fluid Framework libraries
54
54
 
55
- When taking a dependency on a Fluid Framework library, we recommend using a `^` (caret) version range, such as `^1.3.4`.
55
+ When taking a dependency on a Fluid Framework library's public APIs, we recommend using a `^` (caret) version range, such as `^1.3.4`.
56
56
  While Fluid Framework libraries may use different ranges with interdependencies between other Fluid Framework libraries,
57
57
  library consumers should always prefer `^`.
58
58
 
59
+ If using any of Fluid Framework's unstable APIs (for example, its `beta` APIs), we recommend using a more constrained version range, such as `~`.
60
+
59
61
  ## Installation
60
62
 
61
63
  To get started, install the package by running the following command:
@@ -64,6 +66,15 @@ To get started, install the package by running the following command:
64
66
  npm i @fluidframework/container-runtime
65
67
  ```
66
68
 
69
+ ## Importing from this package
70
+
71
+ This package leverages [package.json exports](https://nodejs.org/api/packages.html#exports) to separate its APIs by support level.
72
+ For more information on the related support guarantees, see [API Support Levels](https://fluidframework.com/docs/build/releases-and-apitags/#api-support-levels).
73
+
74
+ To access the `public` ([SemVer](https://semver.org/)) APIs, import via `@fluidframework/container-runtime` like normal.
75
+
76
+ To access the `legacy` APIs, import via `@fluidframework/container-runtime/legacy`.
77
+
67
78
  ## API Documentation
68
79
 
69
80
  API documentation for **@fluidframework/container-runtime** is available at <https://fluidframework.com/docs/apis/container-runtime>.
@@ -88,11 +99,9 @@ Use of Microsoft trademarks or logos in modified versions of this project must n
88
99
 
89
100
  ## Help
90
101
 
91
- Not finding what you're looking for in this README? Check out our [GitHub
92
- Wiki](https://github.com/microsoft/FluidFramework/wiki) or [fluidframework.com](https://fluidframework.com/docs/).
102
+ Not finding what you're looking for in this README? Check out [fluidframework.com](https://fluidframework.com/docs/).
93
103
 
94
- Still not finding what you're looking for? Please [file an
95
- issue](https://github.com/microsoft/FluidFramework/wiki/Submitting-Bugs-and-Feature-Requests).
104
+ Still not finding what you're looking for? Please [file an issue](https://github.com/microsoft/FluidFramework/wiki/Submitting-Bugs-and-Feature-Requests).
96
105
 
97
106
  Thank you!
98
107
 
Binary file
@@ -6,9 +6,10 @@ import { TypedEventEmitter } from "@fluid-internal/client-utils";
6
6
  import { ICriticalContainerError } from "@fluidframework/container-definitions";
7
7
  import { IContainerRuntime, IContainerRuntimeEvents } from "@fluidframework/container-runtime-definitions/internal";
8
8
  import { IFluidHandleContext, type IFluidHandleInternal } from "@fluidframework/core-interfaces/internal";
9
- import { IDocumentStorageService, ISnapshotTree, ISequencedDocumentMessage } from "@fluidframework/driver-definitions/internal";
9
+ import { IDocumentStorageService, ISequencedDocumentMessage } from "@fluidframework/driver-definitions/internal";
10
10
  import { IGarbageCollectionData, ISummaryTreeWithStats, ITelemetryContext } from "@fluidframework/runtime-definitions/internal";
11
11
  import { FluidHandleBase } from "@fluidframework/runtime-utils/internal";
12
+ import { type IBlobManagerLoadInfo } from "./blobManagerSnapSum.js";
12
13
  /**
13
14
  * This class represents blob (long string)
14
15
  * This object is used only when creating (writing) new blob and serialization purposes.
@@ -28,14 +29,6 @@ export declare class BlobHandle extends FluidHandleBase<ArrayBufferLike> {
28
29
  attachGraph(): void;
29
30
  bind(handle: IFluidHandleInternal): void;
30
31
  }
31
- /**
32
- * Information from a snapshot needed to load BlobManager
33
- * @alpha
34
- */
35
- export interface IBlobManagerLoadInfo {
36
- ids?: string[];
37
- redirectTable?: [string, string][];
38
- }
39
32
  export type IBlobManagerRuntime = Pick<IContainerRuntime, "attachState" | "connected" | "baseLogger" | "clientDetails"> & TypedEventEmitter<IContainerRuntimeEvents>;
40
33
  export interface IPendingBlobs {
41
34
  [id: string]: {
@@ -49,9 +42,8 @@ export interface IPendingBlobs {
49
42
  export interface IBlobManagerEvents {
50
43
  (event: "noPendingBlobs", listener: () => void): any;
51
44
  }
45
+ export declare const blobManagerBasePath: "_blobs";
52
46
  export declare class BlobManager extends TypedEventEmitter<IBlobManagerEvents> {
53
- static readonly basePath = "_blobs";
54
- private static readonly redirectTableBlobName;
55
47
  private readonly mc;
56
48
  /**
57
49
  * Map of local IDs to storage IDs. Contains identity entries (id → id) for storage IDs. All requested IDs should
@@ -108,11 +100,6 @@ export declare class BlobManager extends TypedEventEmitter<IBlobManagerEvents> {
108
100
  * Upload blobs added while offline. This must be completed before connecting and resubmitting ops.
109
101
  */
110
102
  trackPendingStashedUploads(): Promise<void>;
111
- /**
112
- * Set of actual storage IDs (i.e., IDs that can be requested from storage). This will be empty if the container is
113
- * detached or there are no (non-pending) attachment blobs in the document
114
- */
115
- private get storageIds();
116
103
  getBlob(blobId: string): Promise<ArrayBufferLike>;
117
104
  private getBlobHandle;
118
105
  private createBlobDetached;
@@ -133,17 +120,6 @@ export declare class BlobManager extends TypedEventEmitter<IBlobManagerEvents> {
133
120
  */
134
121
  reSubmit(metadata: Record<string, unknown> | undefined): void;
135
122
  processBlobAttachOp(message: ISequencedDocumentMessage, local: boolean): void;
136
- /**
137
- * Reads blobs needed to load BlobManager from storage.
138
- * @param blobsTree - Tree containing IDs of previously attached blobs. We
139
- * look for the IDs in the blob entries of the tree since the both the r11s
140
- * and SPO drivers replace the attachment types returned in snapshot() with blobs.
141
- */
142
- static load(blobsTree: ISnapshotTree | undefined, tryFetchBlob: (id: string) => Promise<[string, string][]>): Promise<IBlobManagerLoadInfo>;
143
- /**
144
- * Load a set of previously attached blob IDs and redirect table from a previous snapshot.
145
- */
146
- private load;
147
123
  summarize(telemetryContext?: ITelemetryContext): ISummaryTreeWithStats;
148
124
  /**
149
125
  * Generates data used for garbage collection. Each blob uploaded represents a node in the GC graph as it can be
@@ -161,7 +137,7 @@ export declare class BlobManager extends TypedEventEmitter<IBlobManagerEvents> {
161
137
  deleteSweepReadyNodes(sweepReadyBlobRoutes: readonly string[]): readonly string[];
162
138
  /**
163
139
  * Delete blobs with the given routes from the redirect table.
164
- * The routes are GC nodes paths of format -`/<BlobManager.basePath>/<blobId>`. The blob ids are all local ids.
140
+ * The routes are GC nodes paths of format -`/<blobManagerBasePath>/<blobId>`. The blob ids are all local ids.
165
141
  * Deleting the blobs involves 2 steps:
166
142
  * 1. The redirect table entry for the local ids are deleted.
167
143
  * 2. If the storage ids corresponding to the deleted local ids are not in-use anymore, the redirect table entries
@@ -179,4 +155,9 @@ export declare class BlobManager extends TypedEventEmitter<IBlobManagerEvents> {
179
155
  setRedirectTable(table: Map<string, string>): void;
180
156
  attachAndGetPendingBlobs(stopBlobAttachingSignal?: AbortSignal): Promise<IPendingBlobs | undefined>;
181
157
  }
158
+ /**
159
+ * Returns whether a given path is for attachment blobs that are in the format - "/blobManagerBasePath/...".
160
+ */
161
+ export declare const isBlobPath: (path: string) => path is `/_blobs/${string}`;
162
+ export declare const areBlobPathParts: (pathParts: string[]) => pathParts is ["", "_blobs", string];
182
163
  //# sourceMappingURL=blobManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blobManager.d.ts","sourceRoot":"","sources":["../../src/blobManager/blobManager.ts"],"names":[],"mappings":"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"}
@@ -4,7 +4,7 @@
4
4
  * Licensed under the MIT License.
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.BlobManager = exports.BlobHandle = void 0;
7
+ exports.areBlobPathParts = exports.isBlobPath = exports.BlobManager = exports.blobManagerBasePath = exports.BlobHandle = void 0;
8
8
  const client_utils_1 = require("@fluid-internal/client-utils");
9
9
  const container_definitions_1 = require("@fluidframework/container-definitions");
10
10
  const internal_1 = require("@fluidframework/core-utils/internal");
@@ -12,6 +12,7 @@ const internal_2 = require("@fluidframework/driver-utils/internal");
12
12
  const internal_3 = require("@fluidframework/runtime-utils/internal");
13
13
  const internal_4 = require("@fluidframework/telemetry-utils/internal");
14
14
  const uuid_1 = require("uuid");
15
+ const blobManagerSnapSum_js_1 = require("./blobManagerSnapSum.js");
15
16
  /**
16
17
  * This class represents blob (long string)
17
18
  * This object is used only when creating (writing) new blob and serialization purposes.
@@ -49,6 +50,7 @@ const stashedPendingBlobOverrides = {
49
50
  minTTLInSeconds: undefined,
50
51
  uploadTime: undefined,
51
52
  };
53
+ exports.blobManagerBasePath = "_blobs";
52
54
  class BlobManager extends client_utils_1.TypedEventEmitter {
53
55
  constructor(props) {
54
56
  super();
@@ -74,7 +76,7 @@ class BlobManager extends client_utils_1.TypedEventEmitter {
74
76
  logger: this.runtime.baseLogger,
75
77
  namespace: "BlobManager",
76
78
  });
77
- this.redirectTable = this.load(snapshot);
79
+ this.redirectTable = (0, blobManagerSnapSum_js_1.toRedirectTable)(snapshot, this.mc.logger, this.runtime.attachState);
78
80
  // Begin uploading stashed blobs from previous container instance
79
81
  Object.entries(stashedBlobs ?? {}).forEach(([localId, entry]) => {
80
82
  const { acked, storageId, minTTLInSeconds, uploadTime } = entry;
@@ -165,20 +167,6 @@ class BlobManager extends client_utils_1.TypedEventEmitter {
165
167
  count: pendingUploads.length,
166
168
  }, async () => Promise.all(pendingUploads), { start: true, end: true });
167
169
  }
168
- /**
169
- * Set of actual storage IDs (i.e., IDs that can be requested from storage). This will be empty if the container is
170
- * detached or there are no (non-pending) attachment blobs in the document
171
- */
172
- get storageIds() {
173
- const ids = new Set(this.redirectTable.values());
174
- // If we are detached, we will not have storage IDs, only undefined
175
- const undefinedValueInTable = ids.delete(undefined);
176
- // For a detached container, entries are inserted into the redirect table with an undefined storage ID.
177
- // For an attached container, entries are inserted w/storage ID after the BlobAttach op round-trips.
178
- (0, internal_1.assert)(!undefinedValueInTable ||
179
- (this.runtime.attachState === container_definitions_1.AttachState.Detached && ids.size === 0), 0x382 /* 'redirectTable' must contain only undefined while detached / defined values while attached */);
180
- return ids;
181
- }
182
170
  async getBlob(blobId) {
183
171
  // Verify that the blob is not deleted, i.e., it has not been garbage collected. If it is, this will throw
184
172
  // an error, failing the call.
@@ -217,7 +205,7 @@ class BlobManager extends client_utils_1.TypedEventEmitter {
217
205
  this.deletePendingBlobMaybe(id);
218
206
  }
219
207
  : undefined;
220
- return new BlobHandle(`${BlobManager.basePath}/${id}`, this.routeContext, async () => this.getBlob(id), callback);
208
+ return new BlobHandle(getGCNodePathFromBlobId(id), this.routeContext, async () => this.getBlob(id), callback);
221
209
  }
222
210
  async createBlobDetached(blob) {
223
211
  // Blobs created while the container is detached are stored in IDetachedBlobStorage.
@@ -325,7 +313,8 @@ class BlobManager extends client_utils_1.TypedEventEmitter {
325
313
  if (!entry.opsent) {
326
314
  this.sendBlobAttachOp(localId, response.id);
327
315
  }
328
- if (this.storageIds.has(response.id)) {
316
+ const storageIds = (0, blobManagerSnapSum_js_1.getStorageIds)(this.redirectTable, this.runtime.attachState);
317
+ if (storageIds.has(response.id)) {
329
318
  // The blob is de-duped. Set up a local ID to storage ID mapping and return the blob. Since this is
330
319
  // an existing blob, we don't have to wait for the op to be ack'd since this step has already
331
320
  // happened before and so, the server won't delete it.
@@ -403,60 +392,8 @@ class BlobManager extends client_utils_1.TypedEventEmitter {
403
392
  }
404
393
  }
405
394
  }
406
- /**
407
- * Reads blobs needed to load BlobManager from storage.
408
- * @param blobsTree - Tree containing IDs of previously attached blobs. We
409
- * look for the IDs in the blob entries of the tree since the both the r11s
410
- * and SPO drivers replace the attachment types returned in snapshot() with blobs.
411
- */
412
- static async load(blobsTree, tryFetchBlob) {
413
- if (!blobsTree) {
414
- return {};
415
- }
416
- let redirectTable;
417
- const tableId = blobsTree.blobs[this.redirectTableBlobName];
418
- if (tableId) {
419
- redirectTable = await tryFetchBlob(tableId);
420
- }
421
- const ids = Object.entries(blobsTree.blobs)
422
- .filter(([k, _]) => k !== this.redirectTableBlobName)
423
- .map(([_, v]) => v);
424
- return { ids, redirectTable };
425
- }
426
- /**
427
- * Load a set of previously attached blob IDs and redirect table from a previous snapshot.
428
- */
429
- load(snapshot) {
430
- this.mc.logger.sendTelemetryEvent({
431
- eventName: "AttachmentBlobsLoaded",
432
- count: snapshot.ids?.length ?? 0,
433
- redirectTable: snapshot.redirectTable?.length,
434
- });
435
- const table = new Map(snapshot.redirectTable);
436
- if (snapshot.ids) {
437
- const detached = this.runtime.attachState === container_definitions_1.AttachState.Detached;
438
- // If we are detached, we don't have storage IDs yet, so set to undefined
439
- // Otherwise, set identity (id -> id) entries
440
- snapshot.ids.forEach((entry) => table.set(entry, detached ? undefined : entry));
441
- }
442
- return table;
443
- }
444
395
  summarize(telemetryContext) {
445
- // if storageIds is empty, it means we are detached and have only local IDs, or that there are no blobs attached
446
- const blobIds = this.storageIds.size > 0
447
- ? Array.from(this.storageIds)
448
- : Array.from(this.redirectTable.keys());
449
- const builder = new internal_3.SummaryTreeBuilder();
450
- blobIds.forEach((blobId) => {
451
- builder.addAttachment(blobId);
452
- });
453
- // Any non-identity entries in the table need to be saved in the summary
454
- if (this.redirectTable.size > blobIds.length) {
455
- builder.addBlob(BlobManager.redirectTableBlobName,
456
- // filter out identity entries
457
- JSON.stringify(Array.from(this.redirectTable.entries()).filter(([localId, storageId]) => localId !== storageId)));
458
- }
459
- return builder.getSummaryTree();
396
+ return (0, blobManagerSnapSum_js_1.summarizeBlobManagerState)(this.redirectTable, this.runtime.attachState);
460
397
  }
461
398
  /**
462
399
  * Generates data used for garbage collection. Each blob uploaded represents a node in the GC graph as it can be
@@ -490,7 +427,7 @@ class BlobManager extends client_utils_1.TypedEventEmitter {
490
427
  }
491
428
  /**
492
429
  * Delete blobs with the given routes from the redirect table.
493
- * The routes are GC nodes paths of format -`/<BlobManager.basePath>/<blobId>`. The blob ids are all local ids.
430
+ * The routes are GC nodes paths of format -`/<blobManagerBasePath>/<blobId>`. The blob ids are all local ids.
494
431
  * Deleting the blobs involves 2 steps:
495
432
  * 1. The redirect table entry for the local ids are deleted.
496
433
  * 2. If the storage ids corresponding to the deleted local ids are not in-use anymore, the redirect table entries
@@ -554,7 +491,7 @@ class BlobManager extends client_utils_1.TypedEventEmitter {
554
491
  // Only log deleted events. Tombstone events are logged by garbage collector.
555
492
  this.mc.logger.sendErrorEvent({
556
493
  eventName: "GC_Deleted_Blob_Requested",
557
- pkg: BlobManager.basePath,
494
+ pkg: exports.blobManagerBasePath,
558
495
  }, error);
559
496
  throw error;
560
497
  }
@@ -628,22 +565,25 @@ class BlobManager extends client_utils_1.TypedEventEmitter {
628
565
  }
629
566
  }
630
567
  exports.BlobManager = BlobManager;
631
- BlobManager.basePath = "_blobs";
632
- BlobManager.redirectTableBlobName = ".redirectTable";
633
568
  /**
634
- * For a blobId, returns its path in GC's graph. The node path is of the format `/<BlobManager.basePath>/<blobId>`.
569
+ * For a blobId, returns its path in GC's graph. The node path is of the format `/<blobManagerBasePath>/<blobId>`.
635
570
  * This path must match the path of the blob handle returned by the createBlob API because blobs are marked
636
571
  * referenced by storing these handles in a referenced DDS.
637
572
  */
638
- function getGCNodePathFromBlobId(blobId) {
639
- return `/${BlobManager.basePath}/${blobId}`;
640
- }
573
+ const getGCNodePathFromBlobId = (blobId) => `/${exports.blobManagerBasePath}/${blobId}`;
641
574
  /**
642
- * For a given GC node path, return the blobId. The node path is of the format `/<BlobManager.basePath>/<blobId>`.
575
+ * For a given GC node path, return the blobId. The node path is of the format `/<basePath>/<blobId>`.
643
576
  */
644
- function getBlobIdFromGCNodePath(nodePath) {
577
+ const getBlobIdFromGCNodePath = (nodePath) => {
645
578
  const pathParts = nodePath.split("/");
646
- (0, internal_1.assert)(pathParts.length === 3 && pathParts[1] === BlobManager.basePath, 0x5bd /* Invalid blob node path */);
579
+ (0, internal_1.assert)((0, exports.areBlobPathParts)(pathParts), 0x5bd /* Invalid blob node path */);
647
580
  return pathParts[2];
648
- }
581
+ };
582
+ /**
583
+ * Returns whether a given path is for attachment blobs that are in the format - "/blobManagerBasePath/...".
584
+ */
585
+ const isBlobPath = (path) => (0, exports.areBlobPathParts)(path.split("/"));
586
+ exports.isBlobPath = isBlobPath;
587
+ const areBlobPathParts = (pathParts) => pathParts.length === 3 && pathParts[1] === exports.blobManagerBasePath;
588
+ exports.areBlobPathParts = areBlobPathParts;
649
589
  //# sourceMappingURL=blobManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blobManager.js","sourceRoot":"","sources":["../../src/blobManager/blobManager.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport {\n\tTypedEventEmitter,\n\tbufferToString,\n\tstringToBuffer,\n} from \"@fluid-internal/client-utils\";\nimport { AttachState, ICriticalContainerError } from \"@fluidframework/container-definitions\";\nimport {\n\tIContainerRuntime,\n\tIContainerRuntimeEvents,\n} from \"@fluidframework/container-runtime-definitions/internal\";\nimport {\n\tIFluidHandleContext,\n\ttype IFluidHandleInternal,\n} from \"@fluidframework/core-interfaces/internal\";\nimport { assert, Deferred } from \"@fluidframework/core-utils/internal\";\nimport {\n\tIDocumentStorageService,\n\tICreateBlobResponse,\n\tISequencedDocumentMessage,\n} from \"@fluidframework/driver-definitions/internal\";\nimport { canRetryOnError, runWithRetry } from \"@fluidframework/driver-utils/internal\";\nimport {\n\tIGarbageCollectionData,\n\tISummaryTreeWithStats,\n\tITelemetryContext,\n} from \"@fluidframework/runtime-definitions/internal\";\nimport {\n\tFluidHandleBase,\n\tcreateResponseError,\n\tgenerateHandleContextPath,\n\tresponseToException,\n} from \"@fluidframework/runtime-utils/internal\";\nimport {\n\tGenericError,\n\tLoggingError,\n\tMonitoringContext,\n\tPerformanceEvent,\n\tcreateChildMonitoringContext,\n\twrapError,\n} from \"@fluidframework/telemetry-utils/internal\";\nimport { v4 as uuid } from \"uuid\";\n\nimport { IBlobMetadata } from \"../metadata.js\";\n\nimport {\n\tgetStorageIds,\n\tsummarizeBlobManagerState,\n\ttoRedirectTable,\n\ttype IBlobManagerLoadInfo,\n} from \"./blobManagerSnapSum.js\";\n\n/**\n * This class represents blob (long string)\n * This object is used only when creating (writing) new blob and serialization purposes.\n * De-serialization process goes through FluidObjectHandle and request flow:\n * DataObject.request() recognizes requests in the form of `/blobs/<id>`\n * and loads blob.\n */\nexport class BlobHandle extends FluidHandleBase<ArrayBufferLike> {\n\tprivate attached: boolean = false;\n\n\tpublic get isAttached(): boolean {\n\t\treturn this.routeContext.isAttached && this.attached;\n\t}\n\n\tpublic readonly absolutePath: string;\n\n\tconstructor(\n\t\tpublic readonly path: string,\n\t\tpublic readonly routeContext: IFluidHandleContext,\n\t\tpublic get: () => Promise<ArrayBufferLike>,\n\t\tprivate readonly onAttachGraph?: () => void,\n\t) {\n\t\tsuper();\n\t\tthis.absolutePath = generateHandleContextPath(path, this.routeContext);\n\t}\n\n\tpublic attachGraph() {\n\t\tif (!this.attached) {\n\t\t\tthis.attached = true;\n\t\t\tthis.onAttachGraph?.();\n\t\t}\n\t}\n\n\tpublic bind(handle: IFluidHandleInternal) {\n\t\tthrow new Error(\"Cannot bind to blob handle\");\n\t}\n}\n\n// Restrict the IContainerRuntime interface to the subset required by BlobManager. This helps to make\n// the contract explicit and reduces the amount of mocking required for tests.\nexport type IBlobManagerRuntime = Pick<\n\tIContainerRuntime,\n\t\"attachState\" | \"connected\" | \"baseLogger\" | \"clientDetails\"\n> &\n\tTypedEventEmitter<IContainerRuntimeEvents>;\n\ntype ICreateBlobResponseWithTTL = ICreateBlobResponse &\n\tPartial<Record<\"minTTLInSeconds\", number>>;\n\ninterface PendingBlob {\n\tblob: ArrayBufferLike;\n\topsent?: boolean;\n\tstorageId?: string;\n\thandleP: Deferred<BlobHandle>;\n\tuploadP?: Promise<ICreateBlobResponse | void>;\n\tuploadTime?: number;\n\tminTTLInSeconds?: number;\n\tattached?: boolean;\n\tacked?: boolean;\n\tabortSignal?: AbortSignal;\n\tstashedUpload?: boolean;\n}\n\nexport interface IPendingBlobs {\n\t[id: string]: {\n\t\tblob: string;\n\t\tstorageId?: string;\n\t\tuploadTime?: number;\n\t\tminTTLInSeconds?: number;\n\t\tacked?: boolean;\n\t};\n}\n\nexport interface IBlobManagerEvents {\n\t(event: \"noPendingBlobs\", listener: () => void);\n}\n\nconst stashedPendingBlobOverrides: Pick<\n\tPendingBlob,\n\t\"stashedUpload\" | \"storageId\" | \"minTTLInSeconds\" | \"uploadTime\"\n> = {\n\tstashedUpload: true,\n\tstorageId: undefined,\n\tminTTLInSeconds: undefined,\n\tuploadTime: undefined,\n} as const;\n\nexport const blobManagerBasePath = \"_blobs\" as const;\n\nexport class BlobManager extends TypedEventEmitter<IBlobManagerEvents> {\n\tprivate readonly mc: MonitoringContext;\n\n\t/**\n\t * Map of local IDs to storage IDs. Contains identity entries (id → id) for storage IDs. All requested IDs should\n\t * be a key in this map. Blobs created while the container is detached are stored in IDetachedBlobStorage which\n\t * gives local IDs; the storage IDs are filled in at attach time.\n\t * Note: It contains mappings from all clients, i.e., from remote clients as well. local ID comes from the client\n\t * that uploaded the blob but its mapping to storage ID is needed in all clients in order to retrieve the blob.\n\t */\n\tprivate readonly redirectTable: Map<string, string | undefined>;\n\n\t/**\n\t * Blobs which we have not yet seen a BlobAttach op round-trip and not yet attached to a DDS.\n\t */\n\tprivate readonly pendingBlobs: Map<string, PendingBlob> = new Map();\n\n\t/**\n\t * Track ops in flight for online flow. This is used for optimizations where if we receive an ack for a storage ID,\n\t * we can resolve all pending blobs with the same storage ID even though they may have different local IDs. That's\n\t * because we know that the server will not delete the blob corresponding to that storage ID.\n\t */\n\tprivate readonly opsInFlight: Map<string, string[]> = new Map();\n\n\tprivate readonly sendBlobAttachOp: (localId: string, storageId?: string) => void;\n\tprivate stopAttaching: boolean = false;\n\n\tprivate readonly routeContext: IFluidHandleContext;\n\tprivate readonly getStorage: () => IDocumentStorageService;\n\t// Called when a blob node is requested. blobPath is the path of the blob's node in GC's graph.\n\t// blobPath's format - `/<basePath>/<blobId>`.\n\tprivate readonly blobRequested: (blobPath: string) => void;\n\t// Called to check if a blob has been deleted by GC.\n\t// blobPath's format - `/<basePath>/<blobId>`.\n\tprivate readonly isBlobDeleted: (blobPath: string) => boolean;\n\tprivate readonly runtime: IBlobManagerRuntime;\n\tprivate readonly closeContainer: (error?: ICriticalContainerError) => void;\n\n\tconstructor(props: {\n\t\treadonly routeContext: IFluidHandleContext;\n\t\tsnapshot: IBlobManagerLoadInfo;\n\t\treadonly getStorage: () => IDocumentStorageService;\n\t\t/**\n\t\t * Submit a BlobAttach op. When a blob is uploaded, there is a short grace period before which the blob is\n\t\t * deleted. The BlobAttach op notifies the server that blob is in use. The server will then not delete the\n\t\t * the blob as long as it is listed as referenced in future summaries. The summarizing client will know to\n\t\t * include the storage ID in the summary when it sees the op.\n\t\t *\n\t\t * The op will also include a local ID to inform all clients of the relation to the storage ID, without\n\t\t * knowledge of which they cannot request the blob from storage. It's important that this op is sequenced\n\t\t * before any ops that reference the local ID, otherwise, an invalid handle could be added to the document.\n\t\t */\n\t\tsendBlobAttachOp: (localId: string, storageId?: string) => void;\n\t\t// Called when a blob node is requested. blobPath is the path of the blob's node in GC's graph.\n\t\t// blobPath's format - `/<basePath>/<blobId>`.\n\t\treadonly blobRequested: (blobPath: string) => void;\n\t\t// Called to check if a blob has been deleted by GC.\n\t\t// blobPath's format - `/<basePath>/<blobId>`.\n\t\treadonly isBlobDeleted: (blobPath: string) => boolean;\n\t\treadonly runtime: IBlobManagerRuntime;\n\t\tstashedBlobs: IPendingBlobs | undefined;\n\t\treadonly closeContainer: (error?: ICriticalContainerError) => void;\n\t}) {\n\t\tsuper();\n\t\tconst {\n\t\t\trouteContext,\n\t\t\tsnapshot,\n\t\t\tgetStorage,\n\t\t\tsendBlobAttachOp,\n\t\t\tblobRequested,\n\t\t\tisBlobDeleted,\n\t\t\truntime,\n\t\t\tstashedBlobs,\n\t\t\tcloseContainer,\n\t\t} = props;\n\t\tthis.routeContext = routeContext;\n\t\tthis.getStorage = getStorage;\n\t\tthis.blobRequested = blobRequested;\n\t\tthis.isBlobDeleted = isBlobDeleted;\n\t\tthis.runtime = runtime;\n\t\tthis.closeContainer = closeContainer;\n\n\t\tthis.mc = createChildMonitoringContext({\n\t\t\tlogger: this.runtime.baseLogger,\n\t\t\tnamespace: \"BlobManager\",\n\t\t});\n\n\t\tthis.redirectTable = toRedirectTable(snapshot, this.mc.logger, this.runtime.attachState);\n\n\t\t// Begin uploading stashed blobs from previous container instance\n\t\tObject.entries(stashedBlobs ?? {}).forEach(([localId, entry]) => {\n\t\t\tconst { acked, storageId, minTTLInSeconds, uploadTime } = entry;\n\t\t\tconst blob = stringToBuffer(entry.blob, \"base64\");\n\t\t\tconst pendingEntry: PendingBlob = {\n\t\t\t\tblob,\n\t\t\t\topsent: true,\n\t\t\t\thandleP: new Deferred(),\n\t\t\t\tstorageId,\n\t\t\t\tuploadP: undefined,\n\t\t\t\tuploadTime,\n\t\t\t\tminTTLInSeconds,\n\t\t\t\tattached: true,\n\t\t\t\tacked,\n\t\t\t};\n\t\t\tthis.pendingBlobs.set(localId, pendingEntry);\n\n\t\t\tif (storageId !== undefined && minTTLInSeconds && uploadTime) {\n\t\t\t\tconst timeLapseSinceLocalUpload = (Date.now() - uploadTime) / 1000;\n\t\t\t\t// stashed entries with more than half-life in storage will not be reuploaded\n\t\t\t\tif (minTTLInSeconds - timeLapseSinceLocalUpload > minTTLInSeconds / 2) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.pendingBlobs.set(localId, {\n\t\t\t\t...pendingEntry,\n\t\t\t\t...stashedPendingBlobOverrides,\n\t\t\t\tuploadP: this.uploadBlob(localId, blob),\n\t\t\t});\n\t\t});\n\n\t\tthis.sendBlobAttachOp = (localId: string, blobId?: string) => {\n\t\t\tconst pendingEntry = this.pendingBlobs.get(localId);\n\t\t\tassert(\n\t\t\t\tpendingEntry !== undefined,\n\t\t\t\t0x725 /* Must have pending blob entry for upcoming op */,\n\t\t\t);\n\t\t\tif (pendingEntry?.uploadTime && pendingEntry?.minTTLInSeconds) {\n\t\t\t\tconst secondsSinceUpload = (Date.now() - pendingEntry.uploadTime) / 1000;\n\t\t\t\tconst expired = pendingEntry.minTTLInSeconds - secondsSinceUpload < 0;\n\t\t\t\tthis.mc.logger.sendTelemetryEvent({\n\t\t\t\t\teventName: \"sendBlobAttach\",\n\t\t\t\t\tsecondsSinceUpload,\n\t\t\t\t\tminTTLInSeconds: pendingEntry.minTTLInSeconds,\n\t\t\t\t\texpired,\n\t\t\t\t});\n\t\t\t\tif (expired) {\n\t\t\t\t\t// we want to avoid submitting ops with broken handles\n\t\t\t\t\tthis.closeContainer(\n\t\t\t\t\t\tnew GenericError(\"Trying to submit a BlobAttach for expired blob\", undefined, {\n\t\t\t\t\t\t\tlocalId,\n\t\t\t\t\t\t\tblobId,\n\t\t\t\t\t\t\tsecondsSinceUpload,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tpendingEntry.opsent = true;\n\t\t\treturn sendBlobAttachOp(localId, blobId);\n\t\t};\n\t}\n\n\tpublic get allBlobsAttached(): boolean {\n\t\tfor (const [, entry] of this.pendingBlobs) {\n\t\t\tif (entry.attached === false) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic get hasPendingBlobs(): boolean {\n\t\treturn (\n\t\t\t(this.runtime.attachState !== AttachState.Attached && this.redirectTable.size > 0) ||\n\t\t\tthis.pendingBlobs.size > 0\n\t\t);\n\t}\n\n\tprivate createAbortError(pending?: PendingBlob) {\n\t\treturn new LoggingError(\"uploadBlob aborted\", {\n\t\t\tacked: pending?.acked,\n\t\t\tuploadTime: pending?.uploadTime,\n\t\t});\n\t}\n\n\tpublic hasPendingStashedUploads(): boolean {\n\t\treturn Array.from(this.pendingBlobs.values()).some((e) => e.stashedUpload === true);\n\t}\n\t/**\n\t * Upload blobs added while offline. This must be completed before connecting and resubmitting ops.\n\t */\n\tpublic async trackPendingStashedUploads(): Promise<void> {\n\t\tconst pendingUploads = Array.from(this.pendingBlobs.values())\n\t\t\t.filter((e) => e.stashedUpload === true)\n\t\t\t.map(async (e) => e.uploadP);\n\t\tif (pendingUploads.length === 0) {\n\t\t\treturn;\n\t\t}\n\t\tawait PerformanceEvent.timedExecAsync(\n\t\t\tthis.mc.logger,\n\t\t\t{\n\t\t\t\teventName: \"BlobUploadProcessStashedChanges\",\n\t\t\t\tcount: pendingUploads.length,\n\t\t\t},\n\t\t\tasync () => Promise.all(pendingUploads),\n\t\t\t{ start: true, end: true },\n\t\t);\n\t}\n\n\tpublic async getBlob(blobId: string): Promise<ArrayBufferLike> {\n\t\t// Verify that the blob is not deleted, i.e., it has not been garbage collected. If it is, this will throw\n\t\t// an error, failing the call.\n\t\tthis.verifyBlobNotDeleted(blobId);\n\t\t// Let runtime know that the corresponding GC node was requested.\n\t\t// Note that this will throw if the blob is inactive or tombstoned and throwing on incorrect usage\n\t\t// is configured.\n\t\tthis.blobRequested(getGCNodePathFromBlobId(blobId));\n\n\t\tconst pending = this.pendingBlobs.get(blobId);\n\t\tif (pending) {\n\t\t\treturn pending.blob;\n\t\t}\n\n\t\tlet storageId: string;\n\t\tif (this.runtime.attachState === AttachState.Detached) {\n\t\t\tassert(this.redirectTable.has(blobId), 0x383 /* requesting unknown blobs */);\n\n\t\t\t// Blobs created while the container is detached are stored in IDetachedBlobStorage.\n\t\t\t// The 'IDocumentStorageService.readBlob()' call below will retrieve these via localId.\n\t\t\tstorageId = blobId;\n\t\t} else {\n\t\t\tconst attachedStorageId = this.redirectTable.get(blobId);\n\t\t\tassert(!!attachedStorageId, 0x11f /* \"requesting unknown blobs\" */);\n\t\t\tstorageId = attachedStorageId;\n\t\t}\n\n\t\treturn PerformanceEvent.timedExecAsync(\n\t\t\tthis.mc.logger,\n\t\t\t{ eventName: \"AttachmentReadBlob\", id: storageId },\n\t\t\tasync () => {\n\t\t\t\treturn this.getStorage().readBlob(storageId);\n\t\t\t},\n\t\t\t{ end: true, cancel: \"error\" },\n\t\t);\n\t}\n\n\tprivate getBlobHandle(id: string): BlobHandle {\n\t\tassert(\n\t\t\tthis.redirectTable.has(id) || this.pendingBlobs.has(id),\n\t\t\t0x384 /* requesting handle for unknown blob */,\n\t\t);\n\t\tconst pending = this.pendingBlobs.get(id);\n\t\tconst callback = pending\n\t\t\t? () => {\n\t\t\t\t\tpending.attached = true;\n\t\t\t\t\tthis.emit(\"blobAttached\", pending);\n\t\t\t\t\tthis.deletePendingBlobMaybe(id);\n\t\t\t\t}\n\t\t\t: undefined;\n\t\treturn new BlobHandle(\n\t\t\tgetGCNodePathFromBlobId(id),\n\t\t\tthis.routeContext,\n\t\t\tasync () => this.getBlob(id),\n\t\t\tcallback,\n\t\t);\n\t}\n\n\tprivate async createBlobDetached(\n\t\tblob: ArrayBufferLike,\n\t): Promise<IFluidHandleInternal<ArrayBufferLike>> {\n\t\t// Blobs created while the container is detached are stored in IDetachedBlobStorage.\n\t\t// The 'IDocumentStorageService.createBlob()' call below will respond with a localId.\n\t\tconst response = await this.getStorage().createBlob(blob);\n\t\tthis.setRedirection(response.id, undefined);\n\t\treturn this.getBlobHandle(response.id);\n\t}\n\n\tpublic async createBlob(\n\t\tblob: ArrayBufferLike,\n\t\tsignal?: AbortSignal,\n\t): Promise<IFluidHandleInternal<ArrayBufferLike>> {\n\t\tif (this.runtime.attachState === AttachState.Detached) {\n\t\t\treturn this.createBlobDetached(blob);\n\t\t}\n\t\tif (this.runtime.attachState === AttachState.Attaching) {\n\t\t\t// blob upload is not supported in \"Attaching\" state\n\t\t\tthis.mc.logger.sendTelemetryEvent({ eventName: \"CreateBlobWhileAttaching\" });\n\t\t\tawait new Promise<void>((resolve) => this.runtime.once(\"attached\", resolve));\n\t\t}\n\t\tassert(\n\t\t\tthis.runtime.attachState === AttachState.Attached,\n\t\t\t0x385 /* For clarity and paranoid defense against adding future attachment states */,\n\t\t);\n\n\t\tif (signal?.aborted) {\n\t\t\tthrow this.createAbortError();\n\t\t}\n\n\t\t// Create a local ID for the blob. After uploading it to storage and before returning it, a local ID to\n\t\t// storage ID mapping is created.\n\t\tconst localId = uuid();\n\t\tconst pendingEntry: PendingBlob = {\n\t\t\tblob,\n\t\t\thandleP: new Deferred(),\n\t\t\tuploadP: this.uploadBlob(localId, blob),\n\t\t\tattached: false,\n\t\t\tacked: false,\n\t\t\tabortSignal: signal,\n\t\t\topsent: false,\n\t\t};\n\t\tthis.pendingBlobs.set(localId, pendingEntry);\n\n\t\tconst abortListener = () => {\n\t\t\tif (!pendingEntry.acked) {\n\t\t\t\tpendingEntry.handleP.reject(this.createAbortError(pendingEntry));\n\t\t\t}\n\t\t};\n\t\tsignal?.addEventListener(\"abort\", abortListener, { once: true });\n\n\t\treturn pendingEntry.handleP.promise.finally(() => {\n\t\t\tsignal?.removeEventListener(\"abort\", abortListener);\n\t\t});\n\t}\n\n\tprivate async uploadBlob(\n\t\tlocalId: string,\n\t\tblob: ArrayBufferLike,\n\t): Promise<ICreateBlobResponse | void> {\n\t\treturn runWithRetry(\n\t\t\tasync () => {\n\t\t\t\ttry {\n\t\t\t\t\treturn await this.getStorage().createBlob(blob);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst entry = this.pendingBlobs.get(localId);\n\t\t\t\t\tassert(\n\t\t\t\t\t\t!!entry,\n\t\t\t\t\t\t0x387 /* Must have pending blob entry for blob which failed to upload */,\n\t\t\t\t\t);\n\t\t\t\t\tif (entry.opsent && !canRetryOnError(error)) {\n\t\t\t\t\t\tthrow wrapError(\n\t\t\t\t\t\t\terror,\n\t\t\t\t\t\t\t() => new LoggingError(`uploadBlob error`, { canRetry: true }),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"createBlob\",\n\t\t\tthis.mc.logger,\n\t\t\t{\n\t\t\t\tcancel: this.pendingBlobs.get(localId)?.abortSignal,\n\t\t\t},\n\t\t).then(\n\t\t\t(response) => this.onUploadResolve(localId, response),\n\t\t\t(error) => {\n\t\t\t\t// it will only reject if we haven't sent an op\n\t\t\t\t// and is a non-retriable error. It will only reject\n\t\t\t\t// the promise but not throw any error outside.\n\t\t\t\tthis.pendingBlobs.get(localId)?.handleP.reject(error);\n\t\t\t\tthis.deletePendingBlob(localId);\n\t\t\t},\n\t\t);\n\t}\n\n\t/**\n\t * Set up a mapping in the redirect table from fromId to toId. Also, notify the runtime that a reference is added\n\t * which is required for GC.\n\t */\n\tprivate setRedirection(fromId: string, toId: string | undefined) {\n\t\tthis.redirectTable.set(fromId, toId);\n\t}\n\n\tprivate deletePendingBlobMaybe(id: string) {\n\t\tif (this.pendingBlobs.has(id)) {\n\t\t\tconst entry = this.pendingBlobs.get(id);\n\t\t\tif (entry?.attached && entry?.acked) {\n\t\t\t\tthis.deletePendingBlob(id);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate deletePendingBlob(id: string) {\n\t\tif (this.pendingBlobs.delete(id) && !this.hasPendingBlobs) {\n\t\t\tthis.emit(\"noPendingBlobs\");\n\t\t}\n\t}\n\n\tprivate onUploadResolve(localId: string, response: ICreateBlobResponseWithTTL) {\n\t\tconst entry = this.pendingBlobs.get(localId);\n\t\tassert(entry !== undefined, 0x6c8 /* pending blob entry not found for uploaded blob */);\n\t\tif ((entry.abortSignal?.aborted === true && !entry.opsent) || this.stopAttaching) {\n\t\t\tthis.deletePendingBlob(localId);\n\t\t\treturn;\n\t\t}\n\t\tassert(\n\t\t\tentry.storageId === undefined,\n\t\t\t0x386 /* Must have pending blob entry for uploaded blob */,\n\t\t);\n\t\tentry.stashedUpload = undefined;\n\t\tentry.storageId = response.id;\n\t\tentry.uploadTime = Date.now();\n\t\tentry.minTTLInSeconds = response.minTTLInSeconds;\n\t\t// Send a blob attach op. This serves two purposes:\n\t\t// 1. If its a new blob, i.e., it isn't de-duped, the server will keep the blob alive if it sees this op\n\t\t// until its storage ID is added to the next summary.\n\t\t// 2. It will create a local ID to storage ID mapping in all clients which is needed to retrieve the\n\t\t// blob from the server via the storage ID.\n\t\tif (!entry.opsent) {\n\t\t\tthis.sendBlobAttachOp(localId, response.id);\n\t\t}\n\t\tconst storageIds = getStorageIds(this.redirectTable, this.runtime.attachState);\n\t\tif (storageIds.has(response.id)) {\n\t\t\t// The blob is de-duped. Set up a local ID to storage ID mapping and return the blob. Since this is\n\t\t\t// an existing blob, we don't have to wait for the op to be ack'd since this step has already\n\t\t\t// happened before and so, the server won't delete it.\n\t\t\tthis.setRedirection(localId, response.id);\n\t\t\tentry.handleP.resolve(this.getBlobHandle(localId));\n\t\t\tthis.deletePendingBlobMaybe(localId);\n\t\t} else {\n\t\t\t// If there is already an op for this storage ID, append the local ID to the list. Once any op for\n\t\t\t// this storage ID is ack'd, all pending blobs for it can be resolved since the op will keep the\n\t\t\t// blob alive in storage.\n\t\t\tthis.opsInFlight.set(\n\t\t\t\tresponse.id,\n\t\t\t\t(this.opsInFlight.get(response.id) ?? []).concat(localId),\n\t\t\t);\n\t\t}\n\t\treturn response;\n\t}\n\n\t/**\n\t * Resubmit a BlobAttach op. Used to add storage IDs to ops that were\n\t * submitted to runtime while disconnected.\n\t * @param metadata - op metadata containing storage and/or local IDs\n\t */\n\tpublic reSubmit(metadata: Record<string, unknown> | undefined) {\n\t\tassert(!!metadata, 0x38b /* Resubmitted ops must have metadata */);\n\t\tconst { localId, blobId }: { localId?: string; blobId?: string } = metadata;\n\t\tassert(localId !== undefined, 0x50d /* local ID not available on reSubmit */);\n\t\tconst pendingEntry = this.pendingBlobs.get(localId);\n\n\t\tif (!blobId) {\n\t\t\t// We submitted this op while offline. The blob should have been uploaded by now.\n\t\t\tassert(\n\t\t\t\tpendingEntry?.opsent === true && !!pendingEntry?.storageId,\n\t\t\t\t0x38d /* blob must be uploaded before resubmitting BlobAttach op */,\n\t\t\t);\n\t\t\treturn this.sendBlobAttachOp(localId, pendingEntry?.storageId);\n\t\t}\n\t\treturn this.sendBlobAttachOp(localId, blobId);\n\t}\n\n\tpublic processBlobAttachOp(message: ISequencedDocumentMessage, local: boolean) {\n\t\tconst localId = (message.metadata as IBlobMetadata | undefined)?.localId;\n\t\tconst blobId = (message.metadata as IBlobMetadata | undefined)?.blobId;\n\n\t\tif (localId) {\n\t\t\tconst pendingEntry = this.pendingBlobs.get(localId);\n\t\t\tif (pendingEntry?.abortSignal?.aborted) {\n\t\t\t\tthis.deletePendingBlob(localId);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tassert(blobId !== undefined, 0x12a /* \"Missing blob id on metadata\" */);\n\n\t\t// Set up a mapping from local ID to storage ID. This is crucial since without this the blob cannot be\n\t\t// requested from the server.\n\t\t// Note: The check for undefined is needed for back-compat when localId was not part of the BlobAttach op that\n\t\t// was sent when online.\n\t\tif (localId !== undefined) {\n\t\t\tthis.setRedirection(localId, blobId);\n\t\t}\n\t\t// set identity (id -> id) entry\n\t\tthis.setRedirection(blobId, blobId);\n\n\t\tif (local) {\n\t\t\tassert(localId !== undefined, 0x50e /* local ID not present in blob attach message */);\n\t\t\tconst waitingBlobs = this.opsInFlight.get(blobId);\n\t\t\tif (waitingBlobs !== undefined) {\n\t\t\t\t// For each op corresponding to this storage ID that we are waiting for, resolve the pending blob.\n\t\t\t\t// This is safe because the server will keep the blob alive and the op containing the local ID to\n\t\t\t\t// storage ID is already in flight and any op containing this local ID will be sequenced after that.\n\t\t\t\twaitingBlobs.forEach((pendingLocalId) => {\n\t\t\t\t\tconst entry = this.pendingBlobs.get(pendingLocalId);\n\t\t\t\t\tassert(\n\t\t\t\t\t\tentry !== undefined,\n\t\t\t\t\t\t0x38f /* local online BlobAttach op with no pending blob entry */,\n\t\t\t\t\t);\n\t\t\t\t\tthis.setRedirection(pendingLocalId, blobId);\n\t\t\t\t\tentry.acked = true;\n\t\t\t\t\tentry.handleP.resolve(this.getBlobHandle(pendingLocalId));\n\t\t\t\t\tthis.deletePendingBlobMaybe(pendingLocalId);\n\t\t\t\t});\n\t\t\t\tthis.opsInFlight.delete(blobId);\n\t\t\t}\n\t\t\tconst localEntry = this.pendingBlobs.get(localId);\n\t\t\tif (localEntry) {\n\t\t\t\tlocalEntry.acked = true;\n\t\t\t\tlocalEntry.handleP.resolve(this.getBlobHandle(localId));\n\t\t\t\tthis.deletePendingBlobMaybe(localId);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic summarize(telemetryContext?: ITelemetryContext): ISummaryTreeWithStats {\n\t\treturn summarizeBlobManagerState(this.redirectTable, this.runtime.attachState);\n\t}\n\n\t/**\n\t * Generates data used for garbage collection. Each blob uploaded represents a node in the GC graph as it can be\n\t * individually referenced by storing its handle in a referenced DDS. Returns the list of blob ids as GC nodes.\n\t * @param fullGC - true to bypass optimizations and force full generation of GC data. BlobManager doesn't care\n\t * about this for now because the data is a simple list of blob ids.\n\t */\n\tpublic getGCData(fullGC: boolean = false): IGarbageCollectionData {\n\t\tconst gcData: IGarbageCollectionData = { gcNodes: {} };\n\t\tfor (const [localId, storageId] of this.redirectTable) {\n\t\t\tassert(!!storageId, 0x390 /* Must be attached to get GC data */);\n\t\t\t// Only return local ids as GC nodes because a blob can only be referenced via its local id. The storage\n\t\t\t// id entries have the same key and value, ignore them.\n\t\t\t// The outbound routes are empty because a blob node cannot reference other nodes. It can only be referenced\n\t\t\t// by adding its handle to a referenced DDS.\n\t\t\tif (localId !== storageId) {\n\t\t\t\tgcData.gcNodes[getGCNodePathFromBlobId(localId)] = [];\n\t\t\t}\n\t\t}\n\t\treturn gcData;\n\t}\n\n\t/**\n\t * Delete attachment blobs that are sweep ready.\n\t * @param sweepReadyBlobRoutes - The routes of blobs that are sweep ready and should be deleted. These routes will\n\t * be based off of local ids.\n\t * @returns The routes of blobs that were deleted.\n\t */\n\tpublic deleteSweepReadyNodes(sweepReadyBlobRoutes: readonly string[]): readonly string[] {\n\t\tthis.deleteBlobsFromRedirectTable(sweepReadyBlobRoutes);\n\t\treturn Array.from(sweepReadyBlobRoutes);\n\t}\n\n\t/**\n\t * Delete blobs with the given routes from the redirect table.\n\t * The routes are GC nodes paths of format -`/<blobManagerBasePath>/<blobId>`. The blob ids are all local ids.\n\t * Deleting the blobs involves 2 steps:\n\t * 1. The redirect table entry for the local ids are deleted.\n\t * 2. If the storage ids corresponding to the deleted local ids are not in-use anymore, the redirect table entries\n\t * for the storage ids are deleted as well.\n\t *\n\t * Note that this does not delete the blobs from storage service immediately. Deleting the blobs from redirect table\n\t * will remove them the next summary. The service would them delete them some time in the future.\n\t */\n\tprivate deleteBlobsFromRedirectTable(blobRoutes: readonly string[]) {\n\t\tif (blobRoutes.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\t// This tracks the storage ids of local ids that are deleted. After the local ids have been deleted, if any of\n\t\t// these storage ids are unused, they will be deleted as well.\n\t\tconst maybeUnusedStorageIds: Set<string> = new Set();\n\t\tfor (const route of blobRoutes) {\n\t\t\tconst blobId = getBlobIdFromGCNodePath(route);\n\t\t\t// If the blob hasn't already been deleted, log an error because this should never happen.\n\t\t\t// If the blob has already been deleted, log a telemetry event. This can happen because multiple GC\n\t\t\t// sweep ops can contain the same data store. It would be interesting to track how often this happens.\n\t\t\tconst alreadyDeleted = this.isBlobDeleted(route);\n\t\t\tif (!this.redirectTable.has(blobId)) {\n\t\t\t\tthis.mc.logger.sendTelemetryEvent({\n\t\t\t\t\teventName: \"DeletedAttachmentBlobNotFound\",\n\t\t\t\t\tcategory: alreadyDeleted ? \"generic\" : \"error\",\n\t\t\t\t\tblobId,\n\t\t\t\t\tdetails: { alreadyDeleted },\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst storageId = this.redirectTable.get(blobId);\n\t\t\tassert(!!storageId, 0x5bb /* Must be attached to run GC */);\n\t\t\tmaybeUnusedStorageIds.add(storageId);\n\t\t\tthis.redirectTable.delete(blobId);\n\t\t}\n\n\t\t// Find out storage ids that are in-use and remove them from maybeUnusedStorageIds. A storage id is in-use if\n\t\t// the redirect table has a local id -> storage id entry for it.\n\t\tfor (const [localId, storageId] of this.redirectTable.entries()) {\n\t\t\tassert(!!storageId, 0x5bc /* Must be attached to run GC */);\n\t\t\t// For every storage id, the redirect table has a id -> id entry. These do not make the storage id in-use.\n\t\t\tif (maybeUnusedStorageIds.has(storageId) && localId !== storageId) {\n\t\t\t\tmaybeUnusedStorageIds.delete(storageId);\n\t\t\t}\n\t\t}\n\n\t\t// For unused storage ids, delete their id -> id entries from the redirect table.\n\t\t// This way they'll be absent from the next summary, and the service is free to delete them from storage.\n\t\tfor (const storageId of maybeUnusedStorageIds) {\n\t\t\tthis.redirectTable.delete(storageId);\n\t\t}\n\t}\n\n\t/**\n\t * Verifies that the blob with given id is not deleted, i.e., it has not been garbage collected. If the blob is GC'd,\n\t * log an error and throw if necessary.\n\t */\n\tprivate verifyBlobNotDeleted(blobId: string) {\n\t\tif (!this.isBlobDeleted(getGCNodePathFromBlobId(blobId))) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst request = { url: blobId };\n\t\tconst error = responseToException(\n\t\t\tcreateResponseError(404, `Blob was deleted`, request),\n\t\t\trequest,\n\t\t);\n\t\t// Only log deleted events. Tombstone events are logged by garbage collector.\n\t\tthis.mc.logger.sendErrorEvent(\n\t\t\t{\n\t\t\t\teventName: \"GC_Deleted_Blob_Requested\",\n\t\t\t\tpkg: blobManagerBasePath,\n\t\t\t},\n\t\t\terror,\n\t\t);\n\t\tthrow error;\n\t}\n\n\tpublic setRedirectTable(table: Map<string, string>) {\n\t\tassert(\n\t\t\tthis.runtime.attachState === AttachState.Detached,\n\t\t\t0x252 /* \"redirect table can only be set in detached container\" */,\n\t\t);\n\t\tassert(\n\t\t\tthis.redirectTable.size === table.size,\n\t\t\t0x391 /* Redirect table size must match BlobManager's local ID count */,\n\t\t);\n\t\tfor (const [localId, storageId] of table) {\n\t\t\tassert(this.redirectTable.has(localId), 0x254 /* \"unrecognized id in redirect table\" */);\n\t\t\tthis.setRedirection(localId, storageId);\n\t\t\t// set identity (id -> id) entry\n\t\t\tthis.setRedirection(storageId, storageId);\n\t\t}\n\t}\n\n\tpublic async attachAndGetPendingBlobs(\n\t\tstopBlobAttachingSignal?: AbortSignal,\n\t): Promise<IPendingBlobs | undefined> {\n\t\treturn PerformanceEvent.timedExecAsync(\n\t\t\tthis.mc.logger,\n\t\t\t{ eventName: \"GetPendingBlobs\" },\n\t\t\tasync () => {\n\t\t\t\tif (this.pendingBlobs.size === 0) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst blobs = {};\n\t\t\t\tconst localBlobs = new Set<PendingBlob>();\n\t\t\t\twhile (localBlobs.size < this.pendingBlobs.size) {\n\t\t\t\t\tconst attachBlobsP: Promise<void>[] = [];\n\t\t\t\t\tfor (const [id, entry] of this.pendingBlobs) {\n\t\t\t\t\t\tif (!localBlobs.has(entry)) {\n\t\t\t\t\t\t\tlocalBlobs.add(entry);\n\t\t\t\t\t\t\tentry.handleP.resolve(this.getBlobHandle(id));\n\t\t\t\t\t\t\tattachBlobsP.push(\n\t\t\t\t\t\t\t\tnew Promise<void>((resolve, reject) => {\n\t\t\t\t\t\t\t\t\tstopBlobAttachingSignal?.addEventListener(\n\t\t\t\t\t\t\t\t\t\t\"abort\",\n\t\t\t\t\t\t\t\t\t\t() => {\n\t\t\t\t\t\t\t\t\t\t\tthis.stopAttaching = true;\n\t\t\t\t\t\t\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t{ once: true },\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\tconst onBlobAttached = (attachedEntry) => {\n\t\t\t\t\t\t\t\t\t\tif (attachedEntry === entry) {\n\t\t\t\t\t\t\t\t\t\t\tthis.off(\"blobAttached\", onBlobAttached);\n\t\t\t\t\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\tif (!entry.attached) {\n\t\t\t\t\t\t\t\t\t\tthis.on(\"blobAttached\", onBlobAttached);\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tawait Promise.allSettled(attachBlobsP).catch(() => {});\n\t\t\t\t}\n\n\t\t\t\tfor (const [id, entry] of this.pendingBlobs) {\n\t\t\t\t\tif (stopBlobAttachingSignal?.aborted && !entry.attached) {\n\t\t\t\t\t\tthis.mc.logger.sendTelemetryEvent({\n\t\t\t\t\t\t\teventName: \"UnableToStashBlob\",\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tassert(entry.attached === true, 0x790 /* stashed blob should be attached */);\n\t\t\t\t\tif (!entry.opsent) {\n\t\t\t\t\t\tthis.sendBlobAttachOp(id, entry.storageId);\n\t\t\t\t\t}\n\t\t\t\t\tblobs[id] = {\n\t\t\t\t\t\tblob: bufferToString(entry.blob, \"base64\"),\n\t\t\t\t\t\tstorageId: entry.storageId,\n\t\t\t\t\t\tacked: entry.acked,\n\t\t\t\t\t\tminTTLInSeconds: entry.minTTLInSeconds,\n\t\t\t\t\t\tuploadTime: entry.uploadTime,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn Object.keys(blobs).length > 0 ? blobs : undefined;\n\t\t\t},\n\t\t);\n\t}\n}\n\n/**\n * For a blobId, returns its path in GC's graph. The node path is of the format `/<blobManagerBasePath>/<blobId>`.\n * This path must match the path of the blob handle returned by the createBlob API because blobs are marked\n * referenced by storing these handles in a referenced DDS.\n */\nconst getGCNodePathFromBlobId = (blobId: string) => `/${blobManagerBasePath}/${blobId}`;\n\n/**\n * For a given GC node path, return the blobId. The node path is of the format `/<basePath>/<blobId>`.\n */\nconst getBlobIdFromGCNodePath = (nodePath: string) => {\n\tconst pathParts = nodePath.split(\"/\");\n\tassert(areBlobPathParts(pathParts), 0x5bd /* Invalid blob node path */);\n\treturn pathParts[2];\n};\n\n/**\n * Returns whether a given path is for attachment blobs that are in the format - \"/blobManagerBasePath/...\".\n */\nexport const isBlobPath = (path: string): path is `/${typeof blobManagerBasePath}/${string}` =>\n\tareBlobPathParts(path.split(\"/\"));\n\nexport const areBlobPathParts = (\n\tpathParts: string[],\n): pathParts is [\"\", typeof blobManagerBasePath, string] =>\n\tpathParts.length === 3 && pathParts[1] === blobManagerBasePath;\n"]}
@@ -0,0 +1,30 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import { AttachState, type IContainerContext } from "@fluidframework/container-definitions/internal";
6
+ import type { ISummaryTreeWithStats } from "@fluidframework/runtime-definitions/internal";
7
+ import type { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils/internal";
8
+ /**
9
+ * Information from a snapshot needed to load BlobManager
10
+ * @legacy
11
+ * @alpha
12
+ */
13
+ export interface IBlobManagerLoadInfo {
14
+ ids?: string[];
15
+ redirectTable?: [string, string][];
16
+ }
17
+ export declare const redirectTableBlobName = ".redirectTable";
18
+ /**
19
+ * @internal
20
+ */
21
+ export declare const blobsTreeName = ".blobs";
22
+ /**
23
+ * Reads blobs needed to load BlobManager from storage.
24
+ *
25
+ */
26
+ export declare const loadBlobManagerLoadInfo: (context: Pick<IContainerContext, "baseSnapshot" | "storage" | "attachState">) => Promise<IBlobManagerLoadInfo>;
27
+ export declare const toRedirectTable: (snapshot: IBlobManagerLoadInfo, logger: ITelemetryLoggerExt, attachState: AttachState) => Map<string, string | undefined>;
28
+ export declare const summarizeBlobManagerState: (redirectTable: Map<string, string | undefined>, attachState: AttachState) => ISummaryTreeWithStats;
29
+ export declare const getStorageIds: (redirectTable: Map<string, string | undefined>, attachState: AttachState) => Set<string>;
30
+ //# sourceMappingURL=blobManagerSnapSum.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blobManagerSnapSum.d.ts","sourceRoot":"","sources":["../../src/blobManager/blobManagerSnapSum.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACN,WAAW,EACX,KAAK,iBAAiB,EACtB,MAAM,gDAAgD,CAAC;AAGxD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8CAA8C,CAAC;AAE1F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0CAA0C,CAAC;AAEpF;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACpC,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;CACnC;AAED,eAAO,MAAM,qBAAqB,mBAAmB,CAAC;AAEtD;;GAEG;AACH,eAAO,MAAM,aAAa,WAAW,CAAC;AAEtC;;;GAGG;AACH,eAAO,MAAM,uBAAuB,YAC1B,KAAK,iBAAiB,EAAE,cAAc,GAAG,SAAS,GAAG,aAAa,CAAC,KAC1E,QAAQ,oBAAoB,CAAoB,CAAC;AAsBpD,eAAO,MAAM,eAAe,aACjB,oBAAoB,UACtB,mBAAmB,eACd,WAAW,KACtB,IAAI,MAAM,EAAE,MAAM,GAAG,SAAS,CAchC,CAAC;AAEF,eAAO,MAAM,yBAAyB,kBACtB,IAAI,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,eACjC,WAAW,KACtB,qBAAgE,CAAC;AAgCpE,eAAO,MAAM,aAAa,kBACV,IAAI,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,eACjC,WAAW,gBAexB,CAAC"}
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ /*!
3
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
4
+ * Licensed under the MIT License.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.getStorageIds = exports.summarizeBlobManagerState = exports.toRedirectTable = exports.loadBlobManagerLoadInfo = exports.blobsTreeName = exports.redirectTableBlobName = void 0;
8
+ const internal_1 = require("@fluidframework/container-definitions/internal");
9
+ const internal_2 = require("@fluidframework/core-utils/internal");
10
+ const internal_3 = require("@fluidframework/driver-utils/internal");
11
+ const internal_4 = require("@fluidframework/runtime-utils/internal");
12
+ exports.redirectTableBlobName = ".redirectTable";
13
+ /**
14
+ * @internal
15
+ */
16
+ exports.blobsTreeName = ".blobs";
17
+ /**
18
+ * Reads blobs needed to load BlobManager from storage.
19
+ *
20
+ */
21
+ const loadBlobManagerLoadInfo = async (context) => loadV1(context);
22
+ exports.loadBlobManagerLoadInfo = loadBlobManagerLoadInfo;
23
+ const loadV1 = async (context) => {
24
+ const blobsTree = context.baseSnapshot?.trees[exports.blobsTreeName];
25
+ if (!blobsTree) {
26
+ return {};
27
+ }
28
+ let redirectTableEntries = [];
29
+ const tableId = blobsTree.blobs[exports.redirectTableBlobName];
30
+ if (tableId) {
31
+ redirectTableEntries = await (0, internal_3.readAndParse)(context.storage, tableId);
32
+ }
33
+ const ids = Object.entries(blobsTree.blobs)
34
+ .filter(([k, _]) => k !== exports.redirectTableBlobName)
35
+ .map(([_, v]) => v);
36
+ return { ids, redirectTable: redirectTableEntries };
37
+ };
38
+ const toRedirectTable = (snapshot, logger, attachState) => {
39
+ logger.sendTelemetryEvent({
40
+ eventName: "AttachmentBlobsLoaded",
41
+ count: snapshot.ids?.length ?? 0,
42
+ redirectTable: snapshot.redirectTable?.length,
43
+ });
44
+ const redirectTable = new Map(snapshot.redirectTable);
45
+ const detached = attachState !== internal_1.AttachState.Attached;
46
+ if (snapshot.ids) {
47
+ // If we are detached, we don't have storage IDs yet, so set to undefined
48
+ // Otherwise, set identity (id -> id) entries.
49
+ snapshot.ids.forEach((entry) => redirectTable.set(entry, detached ? undefined : entry));
50
+ }
51
+ return redirectTable;
52
+ };
53
+ exports.toRedirectTable = toRedirectTable;
54
+ const summarizeBlobManagerState = (redirectTable, attachState) => summarizeV1(redirectTable, attachState);
55
+ exports.summarizeBlobManagerState = summarizeBlobManagerState;
56
+ const summarizeV1 = (redirectTable, attachState) => {
57
+ const storageIds = (0, exports.getStorageIds)(redirectTable, attachState);
58
+ // if storageIds is empty, it means we are detached and have only local IDs, or that there are no blobs attached
59
+ const blobIds = storageIds.size > 0 ? Array.from(storageIds) : Array.from(redirectTable.keys());
60
+ const builder = new internal_4.SummaryTreeBuilder();
61
+ blobIds.forEach((blobId) => {
62
+ builder.addAttachment(blobId);
63
+ });
64
+ // Any non-identity entries in the table need to be saved in the summary
65
+ if (redirectTable.size > blobIds.length) {
66
+ builder.addBlob(exports.redirectTableBlobName,
67
+ // filter out identity entries
68
+ JSON.stringify(Array.from(redirectTable.entries()).filter(([localId, storageId]) => localId !== storageId)));
69
+ }
70
+ return builder.getSummaryTree();
71
+ };
72
+ const getStorageIds = (redirectTable, attachState) => {
73
+ const ids = new Set(redirectTable.values());
74
+ // If we are detached, we will not have storage IDs, only undefined
75
+ const undefinedValueInTable = ids.delete(undefined);
76
+ // For a detached container, entries are inserted into the redirect table with an undefined storage ID.
77
+ // For an attached container, entries are inserted w/storage ID after the BlobAttach op round-trips.
78
+ (0, internal_2.assert)(!undefinedValueInTable || (attachState === internal_1.AttachState.Detached && ids.size === 0), 0x382 /* 'redirectTable' must contain only undefined while detached / defined values while attached */);
79
+ return ids;
80
+ };
81
+ exports.getStorageIds = getStorageIds;
82
+ //# sourceMappingURL=blobManagerSnapSum.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blobManagerSnapSum.js","sourceRoot":"","sources":["../../src/blobManager/blobManagerSnapSum.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport {\n\tAttachState,\n\ttype IContainerContext,\n} from \"@fluidframework/container-definitions/internal\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport { readAndParse } from \"@fluidframework/driver-utils/internal\";\nimport type { ISummaryTreeWithStats } from \"@fluidframework/runtime-definitions/internal\";\nimport { SummaryTreeBuilder } from \"@fluidframework/runtime-utils/internal\";\nimport type { ITelemetryLoggerExt } from \"@fluidframework/telemetry-utils/internal\";\n\n/**\n * Information from a snapshot needed to load BlobManager\n * @legacy\n * @alpha\n */\nexport interface IBlobManagerLoadInfo {\n\tids?: string[];\n\tredirectTable?: [string, string][];\n}\n\nexport const redirectTableBlobName = \".redirectTable\";\n\n/**\n * @internal\n */\nexport const blobsTreeName = \".blobs\";\n\n/**\n * Reads blobs needed to load BlobManager from storage.\n *\n */\nexport const loadBlobManagerLoadInfo = async (\n\tcontext: Pick<IContainerContext, \"baseSnapshot\" | \"storage\" | \"attachState\">,\n): Promise<IBlobManagerLoadInfo> => loadV1(context);\n\nconst loadV1 = async (\n\tcontext: Pick<IContainerContext, \"baseSnapshot\" | \"storage\" | \"attachState\">,\n): Promise<IBlobManagerLoadInfo> => {\n\tconst blobsTree = context.baseSnapshot?.trees[blobsTreeName];\n\n\tif (!blobsTree) {\n\t\treturn {};\n\t}\n\tlet redirectTableEntries: [string, string][] = [];\n\tconst tableId = blobsTree.blobs[redirectTableBlobName];\n\tif (tableId) {\n\t\tredirectTableEntries = await readAndParse(context.storage, tableId);\n\t}\n\tconst ids = Object.entries(blobsTree.blobs)\n\t\t.filter(([k, _]) => k !== redirectTableBlobName)\n\t\t.map(([_, v]) => v);\n\n\treturn { ids, redirectTable: redirectTableEntries };\n};\n\nexport const toRedirectTable = (\n\tsnapshot: IBlobManagerLoadInfo,\n\tlogger: ITelemetryLoggerExt,\n\tattachState: AttachState,\n): Map<string, string | undefined> => {\n\tlogger.sendTelemetryEvent({\n\t\teventName: \"AttachmentBlobsLoaded\",\n\t\tcount: snapshot.ids?.length ?? 0,\n\t\tredirectTable: snapshot.redirectTable?.length,\n\t});\n\tconst redirectTable = new Map<string, string | undefined>(snapshot.redirectTable);\n\tconst detached = attachState !== AttachState.Attached;\n\tif (snapshot.ids) {\n\t\t// If we are detached, we don't have storage IDs yet, so set to undefined\n\t\t// Otherwise, set identity (id -> id) entries.\n\t\tsnapshot.ids.forEach((entry) => redirectTable.set(entry, detached ? undefined : entry));\n\t}\n\treturn redirectTable;\n};\n\nexport const summarizeBlobManagerState = (\n\tredirectTable: Map<string, string | undefined>,\n\tattachState: AttachState,\n): ISummaryTreeWithStats => summarizeV1(redirectTable, attachState);\n\nconst summarizeV1 = (\n\tredirectTable: Map<string, string | undefined>,\n\tattachState: AttachState,\n): ISummaryTreeWithStats => {\n\tconst storageIds = getStorageIds(redirectTable, attachState);\n\n\t// if storageIds is empty, it means we are detached and have only local IDs, or that there are no blobs attached\n\tconst blobIds =\n\t\tstorageIds.size > 0 ? Array.from(storageIds) : Array.from(redirectTable.keys());\n\tconst builder = new SummaryTreeBuilder();\n\tblobIds.forEach((blobId) => {\n\t\tbuilder.addAttachment(blobId);\n\t});\n\n\t// Any non-identity entries in the table need to be saved in the summary\n\tif (redirectTable.size > blobIds.length) {\n\t\tbuilder.addBlob(\n\t\t\tredirectTableBlobName,\n\t\t\t// filter out identity entries\n\t\t\tJSON.stringify(\n\t\t\t\tArray.from(redirectTable.entries()).filter(\n\t\t\t\t\t([localId, storageId]) => localId !== storageId,\n\t\t\t\t),\n\t\t\t),\n\t\t);\n\t}\n\n\treturn builder.getSummaryTree();\n};\n\nexport const getStorageIds = (\n\tredirectTable: Map<string, string | undefined>,\n\tattachState: AttachState,\n) => {\n\tconst ids = new Set<string | undefined>(redirectTable.values());\n\n\t// If we are detached, we will not have storage IDs, only undefined\n\tconst undefinedValueInTable = ids.delete(undefined);\n\n\t// For a detached container, entries are inserted into the redirect table with an undefined storage ID.\n\t// For an attached container, entries are inserted w/storage ID after the BlobAttach op round-trips.\n\tassert(\n\t\t!undefinedValueInTable || (attachState === AttachState.Detached && ids.size === 0),\n\t\t0x382 /* 'redirectTable' must contain only undefined while detached / defined values while attached */,\n\t);\n\n\treturn ids as Set<string>;\n};\n"]}
@@ -0,0 +1,7 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ export { BlobManager, IPendingBlobs, IBlobManagerRuntime, IBlobManagerEvents, blobManagerBasePath, isBlobPath, } from "./blobManager.js";
6
+ export { loadBlobManagerLoadInfo, IBlobManagerLoadInfo, blobsTreeName, redirectTableBlobName, } from "./blobManagerSnapSum.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/blobManager/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACN,WAAW,EACX,aAAa,EACb,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,UAAU,GACV,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACN,uBAAuB,EACvB,oBAAoB,EACpB,aAAa,EACb,qBAAqB,GACrB,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ /*!
3
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
4
+ * Licensed under the MIT License.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.redirectTableBlobName = exports.blobsTreeName = exports.loadBlobManagerLoadInfo = exports.isBlobPath = exports.blobManagerBasePath = exports.BlobManager = void 0;
8
+ var blobManager_js_1 = require("./blobManager.js");
9
+ Object.defineProperty(exports, "BlobManager", { enumerable: true, get: function () { return blobManager_js_1.BlobManager; } });
10
+ Object.defineProperty(exports, "blobManagerBasePath", { enumerable: true, get: function () { return blobManager_js_1.blobManagerBasePath; } });
11
+ Object.defineProperty(exports, "isBlobPath", { enumerable: true, get: function () { return blobManager_js_1.isBlobPath; } });
12
+ var blobManagerSnapSum_js_1 = require("./blobManagerSnapSum.js");
13
+ Object.defineProperty(exports, "loadBlobManagerLoadInfo", { enumerable: true, get: function () { return blobManagerSnapSum_js_1.loadBlobManagerLoadInfo; } });
14
+ Object.defineProperty(exports, "blobsTreeName", { enumerable: true, get: function () { return blobManagerSnapSum_js_1.blobsTreeName; } });
15
+ Object.defineProperty(exports, "redirectTableBlobName", { enumerable: true, get: function () { return blobManagerSnapSum_js_1.redirectTableBlobName; } });
16
+ //# sourceMappingURL=index.js.map