@fluidframework/container-runtime 2.0.0-internal.3.2.2 → 2.0.0-internal.3.3.1

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/dist/containerRuntime.d.ts +32 -53
  2. package/dist/containerRuntime.d.ts.map +1 -1
  3. package/dist/containerRuntime.js +40 -18
  4. package/dist/containerRuntime.js.map +1 -1
  5. package/dist/dataStores.d.ts.map +1 -1
  6. package/dist/dataStores.js +8 -3
  7. package/dist/dataStores.js.map +1 -1
  8. package/dist/deltaManagerSummarizerProxy.d.ts +19 -0
  9. package/dist/deltaManagerSummarizerProxy.d.ts.map +1 -0
  10. package/dist/deltaManagerSummarizerProxy.js +40 -0
  11. package/dist/deltaManagerSummarizerProxy.js.map +1 -0
  12. package/dist/gc/garbageCollection.d.ts +2 -33
  13. package/dist/gc/garbageCollection.d.ts.map +1 -1
  14. package/dist/gc/garbageCollection.js +36 -181
  15. package/dist/gc/garbageCollection.js.map +1 -1
  16. package/dist/gc/gcConfigs.d.ts +22 -0
  17. package/dist/gc/gcConfigs.d.ts.map +1 -0
  18. package/dist/gc/gcConfigs.js +138 -0
  19. package/dist/gc/gcConfigs.js.map +1 -0
  20. package/dist/gc/gcDefinitions.d.ts +101 -3
  21. package/dist/gc/gcDefinitions.d.ts.map +1 -1
  22. package/dist/gc/gcDefinitions.js +8 -3
  23. package/dist/gc/gcDefinitions.js.map +1 -1
  24. package/dist/gc/gcHelpers.d.ts +12 -1
  25. package/dist/gc/gcHelpers.d.ts.map +1 -1
  26. package/dist/gc/gcHelpers.js +55 -1
  27. package/dist/gc/gcHelpers.js.map +1 -1
  28. package/dist/gc/gcSummaryStateTracker.d.ts +1 -2
  29. package/dist/gc/gcSummaryStateTracker.d.ts.map +1 -1
  30. package/dist/gc/gcSummaryStateTracker.js +28 -37
  31. package/dist/gc/gcSummaryStateTracker.js.map +1 -1
  32. package/dist/gc/index.d.ts +3 -2
  33. package/dist/gc/index.d.ts.map +1 -1
  34. package/dist/gc/index.js +2 -1
  35. package/dist/gc/index.js.map +1 -1
  36. package/dist/index.d.ts +2 -2
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js.map +1 -1
  39. package/dist/opLifecycle/batchManager.d.ts +9 -0
  40. package/dist/opLifecycle/batchManager.d.ts.map +1 -1
  41. package/dist/opLifecycle/batchManager.js +19 -2
  42. package/dist/opLifecycle/batchManager.js.map +1 -1
  43. package/dist/opLifecycle/index.d.ts +1 -1
  44. package/dist/opLifecycle/index.d.ts.map +1 -1
  45. package/dist/opLifecycle/index.js +2 -1
  46. package/dist/opLifecycle/index.js.map +1 -1
  47. package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
  48. package/dist/opLifecycle/opCompressor.js +24 -10
  49. package/dist/opLifecycle/opCompressor.js.map +1 -1
  50. package/dist/opLifecycle/opSplitter.d.ts +14 -2
  51. package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
  52. package/dist/opLifecycle/opSplitter.js +35 -18
  53. package/dist/opLifecycle/opSplitter.js.map +1 -1
  54. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  55. package/dist/opLifecycle/outbox.js +25 -19
  56. package/dist/opLifecycle/outbox.js.map +1 -1
  57. package/dist/packageVersion.d.ts +1 -1
  58. package/dist/packageVersion.js +1 -1
  59. package/dist/packageVersion.js.map +1 -1
  60. package/dist/storageServiceWithAttachBlobs.d.ts +17 -0
  61. package/dist/storageServiceWithAttachBlobs.d.ts.map +1 -0
  62. package/dist/storageServiceWithAttachBlobs.js +32 -0
  63. package/dist/storageServiceWithAttachBlobs.js.map +1 -0
  64. package/dist/summary/runWhileConnectedCoordinator.d.ts +3 -2
  65. package/dist/summary/runWhileConnectedCoordinator.d.ts.map +1 -1
  66. package/dist/summary/runWhileConnectedCoordinator.js +5 -4
  67. package/dist/summary/runWhileConnectedCoordinator.js.map +1 -1
  68. package/dist/summary/summarizerTypes.d.ts +2 -0
  69. package/dist/summary/summarizerTypes.d.ts.map +1 -1
  70. package/dist/summary/summarizerTypes.js.map +1 -1
  71. package/lib/containerRuntime.d.ts +32 -53
  72. package/lib/containerRuntime.d.ts.map +1 -1
  73. package/lib/containerRuntime.js +41 -19
  74. package/lib/containerRuntime.js.map +1 -1
  75. package/lib/dataStores.d.ts.map +1 -1
  76. package/lib/dataStores.js +9 -4
  77. package/lib/dataStores.js.map +1 -1
  78. package/lib/deltaManagerSummarizerProxy.d.ts +19 -0
  79. package/lib/deltaManagerSummarizerProxy.d.ts.map +1 -0
  80. package/lib/deltaManagerSummarizerProxy.js +36 -0
  81. package/lib/deltaManagerSummarizerProxy.js.map +1 -0
  82. package/lib/gc/garbageCollection.d.ts +2 -33
  83. package/lib/gc/garbageCollection.d.ts.map +1 -1
  84. package/lib/gc/garbageCollection.js +39 -184
  85. package/lib/gc/garbageCollection.js.map +1 -1
  86. package/lib/gc/gcConfigs.d.ts +22 -0
  87. package/lib/gc/gcConfigs.d.ts.map +1 -0
  88. package/lib/gc/gcConfigs.js +134 -0
  89. package/lib/gc/gcConfigs.js.map +1 -0
  90. package/lib/gc/gcDefinitions.d.ts +101 -3
  91. package/lib/gc/gcDefinitions.d.ts.map +1 -1
  92. package/lib/gc/gcDefinitions.js +7 -2
  93. package/lib/gc/gcDefinitions.js.map +1 -1
  94. package/lib/gc/gcHelpers.d.ts +12 -1
  95. package/lib/gc/gcHelpers.d.ts.map +1 -1
  96. package/lib/gc/gcHelpers.js +53 -0
  97. package/lib/gc/gcHelpers.js.map +1 -1
  98. package/lib/gc/gcSummaryStateTracker.d.ts +1 -2
  99. package/lib/gc/gcSummaryStateTracker.d.ts.map +1 -1
  100. package/lib/gc/gcSummaryStateTracker.js +28 -37
  101. package/lib/gc/gcSummaryStateTracker.js.map +1 -1
  102. package/lib/gc/index.d.ts +3 -2
  103. package/lib/gc/index.d.ts.map +1 -1
  104. package/lib/gc/index.js +1 -1
  105. package/lib/gc/index.js.map +1 -1
  106. package/lib/index.d.ts +2 -2
  107. package/lib/index.d.ts.map +1 -1
  108. package/lib/index.js.map +1 -1
  109. package/lib/opLifecycle/batchManager.d.ts +9 -0
  110. package/lib/opLifecycle/batchManager.d.ts.map +1 -1
  111. package/lib/opLifecycle/batchManager.js +17 -1
  112. package/lib/opLifecycle/batchManager.js.map +1 -1
  113. package/lib/opLifecycle/index.d.ts +1 -1
  114. package/lib/opLifecycle/index.d.ts.map +1 -1
  115. package/lib/opLifecycle/index.js +1 -1
  116. package/lib/opLifecycle/index.js.map +1 -1
  117. package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
  118. package/lib/opLifecycle/opCompressor.js +25 -11
  119. package/lib/opLifecycle/opCompressor.js.map +1 -1
  120. package/lib/opLifecycle/opSplitter.d.ts +14 -2
  121. package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
  122. package/lib/opLifecycle/opSplitter.js +35 -18
  123. package/lib/opLifecycle/opSplitter.js.map +1 -1
  124. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  125. package/lib/opLifecycle/outbox.js +26 -20
  126. package/lib/opLifecycle/outbox.js.map +1 -1
  127. package/lib/packageVersion.d.ts +1 -1
  128. package/lib/packageVersion.js +1 -1
  129. package/lib/packageVersion.js.map +1 -1
  130. package/lib/storageServiceWithAttachBlobs.d.ts +17 -0
  131. package/lib/storageServiceWithAttachBlobs.d.ts.map +1 -0
  132. package/lib/storageServiceWithAttachBlobs.js +28 -0
  133. package/lib/storageServiceWithAttachBlobs.js.map +1 -0
  134. package/lib/summary/runWhileConnectedCoordinator.d.ts +3 -2
  135. package/lib/summary/runWhileConnectedCoordinator.d.ts.map +1 -1
  136. package/lib/summary/runWhileConnectedCoordinator.js +5 -4
  137. package/lib/summary/runWhileConnectedCoordinator.js.map +1 -1
  138. package/lib/summary/summarizerTypes.d.ts +2 -0
  139. package/lib/summary/summarizerTypes.d.ts.map +1 -1
  140. package/lib/summary/summarizerTypes.js.map +1 -1
  141. package/package.json +20 -31
  142. package/src/containerRuntime.ts +71 -74
  143. package/src/dataStores.ts +9 -4
  144. package/src/deltaManagerSummarizerProxy.ts +46 -0
  145. package/src/gc/garbageCollection.ts +50 -290
  146. package/src/gc/gcConfigs.ts +177 -0
  147. package/src/gc/gcDefinitions.ts +110 -4
  148. package/src/gc/gcHelpers.ts +78 -1
  149. package/src/gc/gcSummaryStateTracker.ts +35 -42
  150. package/src/gc/index.ts +8 -2
  151. package/src/index.ts +1 -2
  152. package/src/opLifecycle/README.md +2 -2
  153. package/src/opLifecycle/batchManager.ts +19 -1
  154. package/src/opLifecycle/index.ts +1 -1
  155. package/src/opLifecycle/opCompressor.ts +31 -12
  156. package/src/opLifecycle/opSplitter.ts +44 -20
  157. package/src/opLifecycle/outbox.ts +32 -20
  158. package/src/packageVersion.ts +1 -1
  159. package/src/storageServiceWithAttachBlobs.ts +38 -0
  160. package/src/summary/runWhileConnectedCoordinator.ts +7 -7
  161. package/src/summary/summarizerTypes.ts +2 -0
@@ -4,11 +4,12 @@
4
4
  */
5
5
 
6
6
  import { ITelemetryLogger } from "@fluidframework/common-definitions";
7
- import { IsoBuffer } from "@fluidframework/common-utils";
7
+ import { assert, IsoBuffer } from "@fluidframework/common-utils";
8
8
  import { UsageError } from "@fluidframework/container-utils";
9
9
  import { ChildLogger } from "@fluidframework/telemetry-utils";
10
10
  import { compress } from "lz4js";
11
11
  import { CompressionAlgorithms } from "../containerRuntime";
12
+ import { estimateSocketSize } from "./batchManager";
12
13
  import { IBatch, BatchMessage } from "./definitions";
13
14
 
14
15
  /**
@@ -24,21 +25,17 @@ export class OpCompressor {
24
25
  }
25
26
 
26
27
  public compressBatch(batch: IBatch): IBatch {
28
+ assert(
29
+ batch.contentSizeInBytes > 0 && batch.content.length > 0,
30
+ 0x5a4 /* Batch should not be empty */,
31
+ );
32
+
27
33
  const compressionStart = Date.now();
28
34
  const contentsAsBuffer = new TextEncoder().encode(this.serializeBatch(batch));
29
35
  const compressedContents = compress(contentsAsBuffer);
30
36
  const compressedContent = IsoBuffer.from(compressedContents).toString("base64");
31
37
  const duration = Date.now() - compressionStart;
32
38
 
33
- if (batch.contentSizeInBytes > 200000) {
34
- this.logger.sendPerformanceEvent({
35
- eventName: "CompressedBatch",
36
- duration,
37
- sizeBeforeCompression: batch.contentSizeInBytes,
38
- sizeAfterCompression: compressedContent.length,
39
- });
40
- }
41
-
42
39
  const messages: BatchMessage[] = [];
43
40
  messages.push({
44
41
  ...batch.content[0],
@@ -47,15 +44,37 @@ export class OpCompressor {
47
44
  compression: CompressionAlgorithms.lz4,
48
45
  });
49
46
 
47
+ // Add empty placeholder messages to reserve the sequence numbers
50
48
  for (const message of batch.content.slice(1)) {
51
- messages.push({ ...message, contents: undefined });
49
+ messages.push({
50
+ deserializedContent: {
51
+ contents: undefined,
52
+ type: message.deserializedContent.type,
53
+ },
54
+ localOpMetadata: message.localOpMetadata,
55
+ metadata: message.metadata,
56
+ referenceSequenceNumber: message.referenceSequenceNumber,
57
+ });
52
58
  }
53
59
 
54
- return {
60
+ const compressedBatch: IBatch = {
55
61
  contentSizeInBytes: compressedContent.length,
56
62
  content: messages,
57
63
  referenceSequenceNumber: batch.referenceSequenceNumber,
58
64
  };
65
+
66
+ if (batch.contentSizeInBytes > 200000) {
67
+ this.logger.sendPerformanceEvent({
68
+ eventName: "CompressedBatch",
69
+ duration,
70
+ sizeBeforeCompression: batch.contentSizeInBytes,
71
+ sizeAfterCompression: compressedBatch.contentSizeInBytes,
72
+ opCount: compressedBatch.content.length,
73
+ socketSize: estimateSocketSize(compressedBatch),
74
+ });
75
+ }
76
+
77
+ return compressedBatch;
59
78
  }
60
79
 
61
80
  private serializeBatch(batch: IBatch): string {
@@ -13,6 +13,7 @@ import {
13
13
  import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
14
14
  import { ChildLogger } from "@fluidframework/telemetry-utils";
15
15
  import { ContainerMessageType, ContainerRuntimeMessage } from "../containerRuntime";
16
+ import { estimateSocketSize } from "./batchManager";
16
17
  import { BatchMessage, IBatch, IChunkedOp, IMessageProcessingResult } from "./definitions";
17
18
 
18
19
  /**
@@ -28,7 +29,7 @@ export class OpSplitter {
28
29
  private readonly submitBatchFn:
29
30
  | ((batch: IBatchMessage[], referenceSequenceNumber?: number) => number)
30
31
  | undefined,
31
- private readonly chunkSizeInBytes: number,
32
+ public readonly chunkSizeInBytes: number,
32
33
  private readonly maxBatchSizeInBytes: number,
33
34
  logger: ITelemetryLogger,
34
35
  ) {
@@ -161,7 +162,15 @@ export class OpSplitter {
161
162
  );
162
163
 
163
164
  const restOfMessages = batch.content.slice(1); // we expect these to be empty ops, created to reserve sequence numbers
164
- const chunks = splitOp(firstMessage, this.chunkSizeInBytes);
165
+ const socketSize = estimateSocketSize(batch);
166
+ const chunks = splitOp(
167
+ firstMessage,
168
+ this.chunkSizeInBytes,
169
+ // If we estimate that the socket batch size will exceed the batch limit
170
+ // we will inject an empty op to minimize the risk of the payload failing due to
171
+ // the overhead from the trailing empty ops in the batch.
172
+ socketSize >= this.maxBatchSizeInBytes,
173
+ );
165
174
 
166
175
  assert(this.submitBatchFn !== undefined, 0x519 /* We don't support old loaders */);
167
176
  // Send the first N-1 chunks immediately
@@ -181,11 +190,13 @@ export class OpSplitter {
181
190
  );
182
191
 
183
192
  this.logger.sendPerformanceEvent({
184
- eventName: "Chunked compressed batch",
193
+ // Used to be "Chunked compressed batch"
194
+ eventName: "CompressedChunkedBatch",
185
195
  length: batch.content.length,
186
196
  sizeInBytes: batch.contentSizeInBytes,
187
197
  chunks: chunks.length,
188
198
  chunkSizeInBytes: this.chunkSizeInBytes,
199
+ socketSize,
189
200
  });
190
201
 
191
202
  return {
@@ -214,7 +225,23 @@ const chunkToBatchMessage = (
214
225
  };
215
226
  };
216
227
 
217
- export const splitOp = (op: BatchMessage, chunkSizeInBytes: number): IChunkedOp[] => {
228
+ /**
229
+ * Splits an op into smaller ops (chunks), based on the size of the op and the `chunkSizeInBytes` parameter.
230
+ *
231
+ * The last op of the result will be bundled with empty ops in the same batch. There is a risk of the batch payload
232
+ * exceeding the 1MB limit due to the overhead from the empty ops. If the last op is large, the risk is even higher.
233
+ * To minimize the odds, an extra empty op can be added to the result using the `extraOp` parameter.
234
+ *
235
+ * @param op - the op to be split
236
+ * @param chunkSizeInBytes - how large should the chunks be
237
+ * @param extraOp - should an extra empty op be added to the result
238
+ * @returns an array of chunked ops
239
+ */
240
+ export const splitOp = (
241
+ op: BatchMessage,
242
+ chunkSizeInBytes: number,
243
+ extraOp: boolean = false,
244
+ ): IChunkedOp[] => {
218
245
  const chunks: IChunkedOp[] = [];
219
246
  assert(
220
247
  op.contents !== undefined && op.contents !== null,
@@ -222,36 +249,33 @@ export const splitOp = (op: BatchMessage, chunkSizeInBytes: number): IChunkedOp[
222
249
  );
223
250
 
224
251
  const contentLength = op.contents.length;
225
- const chunkCount = Math.floor((contentLength - 1) / chunkSizeInBytes) + 2;
252
+ const chunkCount = Math.floor((contentLength - 1) / chunkSizeInBytes) + 1 + (extraOp ? 1 : 0);
226
253
  let offset = 0;
227
- for (let i = 1; i < chunkCount; i++) {
254
+ for (let chunkId = 1; chunkId <= chunkCount; chunkId++) {
228
255
  const chunk: IChunkedOp = {
229
- chunkId: i,
256
+ chunkId,
230
257
  contents: op.contents.substr(offset, chunkSizeInBytes),
231
258
  originalType: op.deserializedContent.type,
232
259
  totalChunks: chunkCount,
233
260
  };
234
261
 
262
+ if (chunkId === chunkCount) {
263
+ // We don't need to port these to all the chunks,
264
+ // as we rebuild the original op when we process the
265
+ // last chunk, therefore it is the only one that needs it.
266
+ chunk.originalMetadata = op.metadata;
267
+ chunk.originalCompression = op.compression;
268
+ }
269
+
235
270
  chunks.push(chunk);
236
271
  offset += chunkSizeInBytes;
237
272
  assert(
238
- i === chunkCount - 1 || offset <= contentLength,
273
+ chunkId >= chunkCount - 1 || offset <= contentLength,
239
274
  0x58b /* Content offset within bounds */,
240
275
  );
241
276
  }
242
277
 
243
278
  assert(offset >= contentLength, 0x58c /* Content offset equal or larger than content length */);
244
- // The last chunk has empty contents, to minimize the risk of the
245
- // resulting payload exceeding 1MB due to the overhead from the empty ops
246
- // which will be bundled with this op.
247
- chunks.push({
248
- chunkId: chunkCount,
249
- contents: "",
250
- originalType: op.deserializedContent.type,
251
- totalChunks: chunkCount,
252
- originalMetadata: op.metadata,
253
- originalCompression: op.compression,
254
- });
255
-
279
+ assert(chunks.length === chunkCount, 0x5a5 /* Expected number of chunks */);
256
280
  return chunks;
257
281
  };
@@ -15,7 +15,7 @@ import {
15
15
  } from "@fluidframework/telemetry-utils";
16
16
  import { ICompressionRuntimeOptions } from "../containerRuntime";
17
17
  import { PendingStateManager } from "../pendingStateManager";
18
- import { BatchManager } from "./batchManager";
18
+ import { BatchManager, estimateSocketSize } from "./batchManager";
19
19
  import { BatchMessage, IBatch } from "./definitions";
20
20
  import { OpCompressor } from "./opCompressor";
21
21
  import { OpSplitter } from "./opSplitter";
@@ -184,24 +184,26 @@ export class Outbox {
184
184
  }
185
185
 
186
186
  const compressedBatch = this.params.compressor.compressBatch(batch);
187
- if (compressedBatch.contentSizeInBytes <= this.params.config.maxBatchSizeInBytes) {
188
- // If we don't reach the maximum supported size of a batch, it can safely be sent as is
189
- return compressedBatch;
190
- }
191
187
 
192
188
  if (this.params.splitter.isBatchChunkingEnabled) {
193
- return this.params.splitter.splitCompressedBatch(compressedBatch);
189
+ return compressedBatch.contentSizeInBytes <= this.params.splitter.chunkSizeInBytes
190
+ ? compressedBatch
191
+ : this.params.splitter.splitCompressedBatch(compressedBatch);
192
+ }
193
+
194
+ if (compressedBatch.contentSizeInBytes >= this.params.config.maxBatchSizeInBytes) {
195
+ throw new GenericError("BatchTooLarge", /* error */ undefined, {
196
+ batchSize: batch.contentSizeInBytes,
197
+ compressedBatchSize: compressedBatch.contentSizeInBytes,
198
+ count: compressedBatch.content.length,
199
+ limit: this.params.config.maxBatchSizeInBytes,
200
+ chunkingEnabled: this.params.splitter.isBatchChunkingEnabled,
201
+ compressionOptions: JSON.stringify(this.params.config.compressionOptions),
202
+ socketSize: estimateSocketSize(batch),
203
+ });
194
204
  }
195
205
 
196
- // If we've reached this point, the runtime would attempt to send a batch larger than the allowed size
197
- throw new GenericError("BatchTooLarge", /* error */ undefined, {
198
- batchSize: batch.contentSizeInBytes,
199
- compressedBatchSize: compressedBatch.contentSizeInBytes,
200
- count: compressedBatch.content.length,
201
- limit: this.params.config.maxBatchSizeInBytes,
202
- chunkingEnabled: this.params.splitter.isBatchChunkingEnabled,
203
- compressionOptions: JSON.stringify(this.params.config.compressionOptions),
204
- });
206
+ return compressedBatch;
205
207
  }
206
208
 
207
209
  /**
@@ -218,15 +220,25 @@ export class Outbox {
218
220
  return;
219
221
  }
220
222
 
223
+ const socketSize = estimateSocketSize(batch);
224
+ if (socketSize >= this.params.config.maxBatchSizeInBytes) {
225
+ this.mc.logger.sendPerformanceEvent({
226
+ eventName: "LargeBatch",
227
+ length: batch.content.length,
228
+ sizeInBytes: batch.contentSizeInBytes,
229
+ socketSize,
230
+ });
231
+ }
232
+
221
233
  if (this.params.containerContext.submitBatchFn === undefined) {
222
234
  // Legacy path - supporting old loader versions. Can be removed only when LTS moves above
223
235
  // version that has support for batches (submitBatchFn)
224
- for (const message of batch.content) {
225
- // Legacy path doesn't support compressed payloads and will submit uncompressed payload anyways
226
- if (message.metadata?.compressed) {
227
- delete message.metadata.compressed;
228
- }
236
+ assert(
237
+ batch.content[0].compression === undefined,
238
+ 0x5a6 /* Compression should not have happened if the loader does not support it */,
239
+ );
229
240
 
241
+ for (const message of batch.content) {
230
242
  this.params.containerContext.submitFn(
231
243
  MessageType.Operation,
232
244
  message.deserializedContent,
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/container-runtime";
9
- export const pkgVersion = "2.0.0-internal.3.2.2";
9
+ export const pkgVersion = "2.0.0-internal.3.3.1";
@@ -0,0 +1,38 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import {
7
+ IDocumentStorageService,
8
+ IDocumentStorageServicePolicies,
9
+ } from "@fluidframework/driver-definitions";
10
+ import { DocumentStorageServiceProxy } from "@fluidframework/driver-utils";
11
+
12
+ /**
13
+ * IDocumentStorageService proxy which intercepts requests if they can be satisfied by the blobs received in the
14
+ * attach message. We use this to avoid an unnecessary request to the storage service.
15
+ */
16
+ export class StorageServiceWithAttachBlobs extends DocumentStorageServiceProxy {
17
+ constructor(
18
+ internalStorageService: IDocumentStorageService,
19
+ private readonly attachBlobs: Map<string, ArrayBufferLike>,
20
+ ) {
21
+ super(internalStorageService);
22
+ }
23
+
24
+ public get policies(): IDocumentStorageServicePolicies | undefined {
25
+ return this.internalStorageService.policies;
26
+ }
27
+
28
+ public async readBlob(id: string): Promise<ArrayBufferLike> {
29
+ const blob = this.attachBlobs.get(id);
30
+ if (blob !== undefined) {
31
+ return blob;
32
+ }
33
+
34
+ // Note that it is intentional not to cache the result of this readBlob - we'll trust the real
35
+ // IDocumentStorageService to cache appropriately, no need to double-cache.
36
+ return this.internalStorageService.readBlob(id);
37
+ }
38
+ }
@@ -35,10 +35,7 @@ export class RunWhileConnectedCoordinator implements ICancellableSummarizerContr
35
35
 
36
36
  public get cancelled() {
37
37
  if (!this._cancelled) {
38
- assert(
39
- this.runtime.deltaManager.active,
40
- 0x25d /* "We should never connect as 'read'" */,
41
- );
38
+ assert(this.active(), 0x25d /* "We should never connect as 'read'" */);
42
39
 
43
40
  // This check can't be enabled in current design due to lastSummary flow, where
44
41
  // summarizer for closed container stays around and can produce one more summary.
@@ -63,13 +60,16 @@ export class RunWhileConnectedCoordinator implements ICancellableSummarizerContr
63
60
  return this.stopDeferred.promise;
64
61
  }
65
62
 
66
- public static async create(runtime: IConnectableRuntime) {
67
- const obj = new RunWhileConnectedCoordinator(runtime);
63
+ public static async create(runtime: IConnectableRuntime, active: () => boolean) {
64
+ const obj = new RunWhileConnectedCoordinator(runtime, active);
68
65
  await obj.waitStart();
69
66
  return obj;
70
67
  }
71
68
 
72
- protected constructor(private readonly runtime: IConnectableRuntime) {}
69
+ protected constructor(
70
+ private readonly runtime: IConnectableRuntime,
71
+ private readonly active: () => boolean,
72
+ ) {}
73
73
 
74
74
  /**
75
75
  * Starts and waits for a promise which resolves when connected.
@@ -84,6 +84,7 @@ export interface IConnectableRuntime {
84
84
  readonly disposed: boolean;
85
85
  readonly connected: boolean;
86
86
  readonly clientId: string | undefined;
87
+ /** @deprecated - Moved to `ISummarizerRuntime` as it's no longer needed here */
87
88
  readonly deltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>;
88
89
  once(event: "connected" | "disconnected" | "dispose", listener: () => void): this;
89
90
  }
@@ -92,6 +93,7 @@ export interface ISummarizerRuntime extends IConnectableRuntime {
92
93
  readonly logger: ITelemetryLogger;
93
94
  /** clientId of parent (non-summarizing) container that owns summarizer container */
94
95
  readonly summarizerClientId: string | undefined;
96
+ readonly deltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>;
95
97
  disposeFn?(): void;
96
98
  closeFn(): void;
97
99
  }