@fluidframework/container-runtime 2.32.0 → 2.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/api-report/container-runtime.legacy.alpha.api.md +71 -67
  3. package/container-runtime.test-files.tar +0 -0
  4. package/dist/blobManager/blobManager.d.ts +7 -4
  5. package/dist/blobManager/blobManager.d.ts.map +1 -1
  6. package/dist/blobManager/blobManager.js +38 -12
  7. package/dist/blobManager/blobManager.js.map +1 -1
  8. package/dist/channelCollection.d.ts +4 -0
  9. package/dist/channelCollection.d.ts.map +1 -1
  10. package/dist/channelCollection.js +24 -0
  11. package/dist/channelCollection.js.map +1 -1
  12. package/dist/compatUtils.d.ts +74 -0
  13. package/dist/compatUtils.d.ts.map +1 -0
  14. package/dist/compatUtils.js +151 -0
  15. package/dist/compatUtils.js.map +1 -0
  16. package/dist/compressionDefinitions.d.ts +39 -0
  17. package/dist/compressionDefinitions.d.ts.map +1 -0
  18. package/dist/compressionDefinitions.js +30 -0
  19. package/dist/compressionDefinitions.js.map +1 -0
  20. package/dist/containerRuntime.d.ts +78 -52
  21. package/dist/containerRuntime.d.ts.map +1 -1
  22. package/dist/containerRuntime.js +141 -54
  23. package/dist/containerRuntime.js.map +1 -1
  24. package/dist/dataStoreContext.d.ts +3 -0
  25. package/dist/dataStoreContext.d.ts.map +1 -1
  26. package/dist/dataStoreContext.js +122 -66
  27. package/dist/dataStoreContext.js.map +1 -1
  28. package/dist/deltaManagerProxies.d.ts +55 -12
  29. package/dist/deltaManagerProxies.d.ts.map +1 -1
  30. package/dist/deltaManagerProxies.js +63 -55
  31. package/dist/deltaManagerProxies.js.map +1 -1
  32. package/dist/gc/gcDefinitions.d.ts +2 -0
  33. package/dist/gc/gcDefinitions.d.ts.map +1 -1
  34. package/dist/gc/gcDefinitions.js.map +1 -1
  35. package/dist/index.d.ts +4 -2
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +3 -2
  38. package/dist/index.js.map +1 -1
  39. package/dist/legacy.d.ts +1 -0
  40. package/dist/opLifecycle/batchManager.d.ts +3 -15
  41. package/dist/opLifecycle/batchManager.d.ts.map +1 -1
  42. package/dist/opLifecycle/batchManager.js +5 -39
  43. package/dist/opLifecycle/batchManager.js.map +1 -1
  44. package/dist/opLifecycle/definitions.d.ts +44 -11
  45. package/dist/opLifecycle/definitions.d.ts.map +1 -1
  46. package/dist/opLifecycle/definitions.js.map +1 -1
  47. package/dist/opLifecycle/index.d.ts +3 -3
  48. package/dist/opLifecycle/index.d.ts.map +1 -1
  49. package/dist/opLifecycle/index.js +3 -2
  50. package/dist/opLifecycle/index.js.map +1 -1
  51. package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
  52. package/dist/opLifecycle/opCompressor.js +4 -4
  53. package/dist/opLifecycle/opCompressor.js.map +1 -1
  54. package/dist/opLifecycle/opDecompressor.js +3 -3
  55. package/dist/opLifecycle/opDecompressor.js.map +1 -1
  56. package/dist/opLifecycle/opGroupingManager.d.ts +2 -2
  57. package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
  58. package/dist/opLifecycle/opGroupingManager.js +1 -2
  59. package/dist/opLifecycle/opGroupingManager.js.map +1 -1
  60. package/dist/opLifecycle/opSerialization.d.ts +3 -1
  61. package/dist/opLifecycle/opSerialization.d.ts.map +1 -1
  62. package/dist/opLifecycle/opSerialization.js +4 -2
  63. package/dist/opLifecycle/opSerialization.js.map +1 -1
  64. package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
  65. package/dist/opLifecycle/opSplitter.js +2 -2
  66. package/dist/opLifecycle/opSplitter.js.map +1 -1
  67. package/dist/opLifecycle/outbox.d.ts +25 -3
  68. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  69. package/dist/opLifecycle/outbox.js +112 -61
  70. package/dist/opLifecycle/outbox.js.map +1 -1
  71. package/dist/packageVersion.d.ts +1 -1
  72. package/dist/packageVersion.js +1 -1
  73. package/dist/packageVersion.js.map +1 -1
  74. package/dist/pendingStateManager.d.ts +36 -7
  75. package/dist/pendingStateManager.d.ts.map +1 -1
  76. package/dist/pendingStateManager.js +83 -16
  77. package/dist/pendingStateManager.js.map +1 -1
  78. package/dist/runtimeLayerCompatState.d.ts.map +1 -1
  79. package/dist/runtimeLayerCompatState.js +1 -1
  80. package/dist/runtimeLayerCompatState.js.map +1 -1
  81. package/dist/summary/documentSchema.d.ts +1 -0
  82. package/dist/summary/documentSchema.d.ts.map +1 -1
  83. package/dist/summary/documentSchema.js +2 -0
  84. package/dist/summary/documentSchema.js.map +1 -1
  85. package/lib/blobManager/blobManager.d.ts +7 -4
  86. package/lib/blobManager/blobManager.d.ts.map +1 -1
  87. package/lib/blobManager/blobManager.js +38 -12
  88. package/lib/blobManager/blobManager.js.map +1 -1
  89. package/lib/channelCollection.d.ts +4 -0
  90. package/lib/channelCollection.d.ts.map +1 -1
  91. package/lib/channelCollection.js +24 -0
  92. package/lib/channelCollection.js.map +1 -1
  93. package/lib/compatUtils.d.ts +74 -0
  94. package/lib/compatUtils.d.ts.map +1 -0
  95. package/lib/compatUtils.js +142 -0
  96. package/lib/compatUtils.js.map +1 -0
  97. package/lib/compressionDefinitions.d.ts +39 -0
  98. package/lib/compressionDefinitions.d.ts.map +1 -0
  99. package/lib/compressionDefinitions.js +27 -0
  100. package/lib/compressionDefinitions.js.map +1 -0
  101. package/lib/containerRuntime.d.ts +78 -52
  102. package/lib/containerRuntime.d.ts.map +1 -1
  103. package/lib/containerRuntime.js +143 -56
  104. package/lib/containerRuntime.js.map +1 -1
  105. package/lib/dataStoreContext.d.ts +3 -0
  106. package/lib/dataStoreContext.d.ts.map +1 -1
  107. package/lib/dataStoreContext.js +57 -1
  108. package/lib/dataStoreContext.js.map +1 -1
  109. package/lib/deltaManagerProxies.d.ts +55 -12
  110. package/lib/deltaManagerProxies.d.ts.map +1 -1
  111. package/lib/deltaManagerProxies.js +63 -55
  112. package/lib/deltaManagerProxies.js.map +1 -1
  113. package/lib/gc/gcDefinitions.d.ts +2 -0
  114. package/lib/gc/gcDefinitions.d.ts.map +1 -1
  115. package/lib/gc/gcDefinitions.js.map +1 -1
  116. package/lib/index.d.ts +4 -2
  117. package/lib/index.d.ts.map +1 -1
  118. package/lib/index.js +2 -1
  119. package/lib/index.js.map +1 -1
  120. package/lib/legacy.d.ts +1 -0
  121. package/lib/opLifecycle/batchManager.d.ts +3 -15
  122. package/lib/opLifecycle/batchManager.d.ts.map +1 -1
  123. package/lib/opLifecycle/batchManager.js +4 -37
  124. package/lib/opLifecycle/batchManager.js.map +1 -1
  125. package/lib/opLifecycle/definitions.d.ts +44 -11
  126. package/lib/opLifecycle/definitions.d.ts.map +1 -1
  127. package/lib/opLifecycle/definitions.js.map +1 -1
  128. package/lib/opLifecycle/index.d.ts +3 -3
  129. package/lib/opLifecycle/index.d.ts.map +1 -1
  130. package/lib/opLifecycle/index.js +2 -2
  131. package/lib/opLifecycle/index.js.map +1 -1
  132. package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
  133. package/lib/opLifecycle/opCompressor.js +2 -2
  134. package/lib/opLifecycle/opCompressor.js.map +1 -1
  135. package/lib/opLifecycle/opDecompressor.js +1 -1
  136. package/lib/opLifecycle/opDecompressor.js.map +1 -1
  137. package/lib/opLifecycle/opGroupingManager.d.ts +2 -2
  138. package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
  139. package/lib/opLifecycle/opGroupingManager.js +1 -2
  140. package/lib/opLifecycle/opGroupingManager.js.map +1 -1
  141. package/lib/opLifecycle/opSerialization.d.ts +3 -1
  142. package/lib/opLifecycle/opSerialization.d.ts.map +1 -1
  143. package/lib/opLifecycle/opSerialization.js +4 -2
  144. package/lib/opLifecycle/opSerialization.js.map +1 -1
  145. package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
  146. package/lib/opLifecycle/opSplitter.js +1 -1
  147. package/lib/opLifecycle/opSplitter.js.map +1 -1
  148. package/lib/opLifecycle/outbox.d.ts +25 -3
  149. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  150. package/lib/opLifecycle/outbox.js +110 -61
  151. package/lib/opLifecycle/outbox.js.map +1 -1
  152. package/lib/packageVersion.d.ts +1 -1
  153. package/lib/packageVersion.js +1 -1
  154. package/lib/packageVersion.js.map +1 -1
  155. package/lib/pendingStateManager.d.ts +36 -7
  156. package/lib/pendingStateManager.d.ts.map +1 -1
  157. package/lib/pendingStateManager.js +84 -17
  158. package/lib/pendingStateManager.js.map +1 -1
  159. package/lib/runtimeLayerCompatState.d.ts.map +1 -1
  160. package/lib/runtimeLayerCompatState.js +2 -2
  161. package/lib/runtimeLayerCompatState.js.map +1 -1
  162. package/lib/summary/documentSchema.d.ts +1 -0
  163. package/lib/summary/documentSchema.d.ts.map +1 -1
  164. package/lib/summary/documentSchema.js +2 -0
  165. package/lib/summary/documentSchema.js.map +1 -1
  166. package/lib/tsdoc-metadata.json +1 -1
  167. package/package.json +21 -20
  168. package/src/blobManager/blobManager.ts +48 -15
  169. package/src/channelCollection.ts +27 -0
  170. package/src/compatUtils.ts +211 -0
  171. package/src/compressionDefinitions.ts +47 -0
  172. package/src/containerRuntime.ts +259 -108
  173. package/src/dataStoreContext.ts +82 -2
  174. package/src/deltaManagerProxies.ts +132 -70
  175. package/src/gc/gcDefinitions.ts +2 -0
  176. package/src/index.ts +5 -3
  177. package/src/opLifecycle/batchManager.ts +7 -52
  178. package/src/opLifecycle/definitions.ts +45 -11
  179. package/src/opLifecycle/index.ts +7 -2
  180. package/src/opLifecycle/opCompressor.ts +2 -2
  181. package/src/opLifecycle/opDecompressor.ts +1 -1
  182. package/src/opLifecycle/opGroupingManager.ts +7 -5
  183. package/src/opLifecycle/opSerialization.ts +6 -2
  184. package/src/opLifecycle/opSplitter.ts +1 -1
  185. package/src/opLifecycle/outbox.ts +154 -85
  186. package/src/packageVersion.ts +1 -1
  187. package/src/pendingStateManager.ts +135 -21
  188. package/src/runtimeLayerCompatState.ts +5 -2
  189. package/src/summary/documentSchema.ts +3 -0
@@ -5,11 +5,17 @@
5
5
 
6
6
  import { TypedEventEmitter, type ILayerCompatDetails } from "@fluid-internal/client-utils";
7
7
  import { AttachState, IAudience } from "@fluidframework/container-definitions";
8
- import { IDeltaManager } from "@fluidframework/container-definitions/internal";
8
+ import {
9
+ IDeltaManager,
10
+ isIDeltaManagerFull,
11
+ type IDeltaManagerFull,
12
+ type ReadOnlyInfo,
13
+ } from "@fluidframework/container-definitions/internal";
9
14
  import {
10
15
  FluidObject,
11
16
  IDisposable,
12
17
  ITelemetryBaseProperties,
18
+ type IErrorBase,
13
19
  type IEvent,
14
20
  } from "@fluidframework/core-interfaces";
15
21
  import {
@@ -75,6 +81,7 @@ import {
75
81
  tagCodeArtifacts,
76
82
  } from "@fluidframework/telemetry-utils/internal";
77
83
 
84
+ import { BaseDeltaManagerProxy } from "./deltaManagerProxies.js";
78
85
  import {
79
86
  runtimeCompatDetailsForDataStore,
80
87
  validateDatastoreCompatibility,
@@ -188,6 +195,58 @@ export interface IFluidDataStoreContextEvents extends IEvent {
188
195
  (event: "attaching" | "attached", listener: () => void);
189
196
  }
190
197
 
198
+ /**
199
+ * Eventually we should remove the delta manger from being exposed to Datastore runtimes via the context. However to remove that exposure we need to add new
200
+ * features, and those features themselves need forward and back compat. This proxy is here to enable that back compat. Each feature this proxy is used to
201
+ * support should be listed below, and as layer compat support goes away for those feature, we should also remove them from this proxy, with the eventual goal
202
+ * of completely removing this proxy.
203
+ *
204
+ * - Everything regarding readonly is to support older datastore runtimes which do not have the setReadonly function, so they must get their readonly state via the delta manager.
205
+ *
206
+ */
207
+ class ContextDeltaManagerProxy extends BaseDeltaManagerProxy {
208
+ constructor(
209
+ base: IDeltaManagerFull,
210
+ private readonly isReadOnly: () => boolean,
211
+ ) {
212
+ super(base, {
213
+ onReadonly: (): void => {
214
+ /* readonly is controlled from the context which calls setReadonly */
215
+ },
216
+ });
217
+ }
218
+
219
+ public get readOnlyInfo(): ReadOnlyInfo {
220
+ const readonly = this.isReadOnly();
221
+ if (readonly === this.deltaManager.readOnlyInfo.readonly) {
222
+ return this.deltaManager.readOnlyInfo;
223
+ } else {
224
+ return readonly === true
225
+ ? {
226
+ readonly,
227
+ forced: false,
228
+ permissions: undefined,
229
+ storageOnly: false,
230
+ }
231
+ : { readonly };
232
+ }
233
+ }
234
+
235
+ /**
236
+ * Called by the owning datastore context to configure the readonly
237
+ * state of the delta manger that is project down to the datastore
238
+ * runtime. This state may not align with that of the true delta
239
+ * manager if the context wishes to control the read only state
240
+ * differently than the delta manager itself.
241
+ */
242
+ public setReadonly(
243
+ readonly: boolean,
244
+ readonlyConnectionReason?: { reason: string; error?: IErrorBase },
245
+ ): void {
246
+ this.emit("readonly", readonly, readonlyConnectionReason);
247
+ }
248
+ }
249
+
191
250
  /**
192
251
  * Represents the context for the store. This context is passed to the store runtime.
193
252
  * @internal
@@ -217,10 +276,13 @@ export abstract class FluidDataStoreContext
217
276
  return this.parentContext.baseLogger;
218
277
  }
219
278
 
279
+ private readonly _contextDeltaManagerProxy: ContextDeltaManagerProxy;
220
280
  public get deltaManager(): IDeltaManager<ISequencedDocumentMessage, IDocumentMessage> {
221
- return this.parentContext.deltaManager;
281
+ return this._contextDeltaManagerProxy;
222
282
  }
223
283
 
284
+ public isReadOnly = (): boolean => this.parentContext.isReadOnly?.() ?? false;
285
+
224
286
  public get connected(): boolean {
225
287
  return this.parentContext.connected;
226
288
  }
@@ -420,6 +482,16 @@ export abstract class FluidDataStoreContext
420
482
  // By default, a data store can log maximum 10 local changes telemetry in summarizer.
421
483
  this.localChangesTelemetryCount =
422
484
  this.mc.config.getNumber("Fluid.Telemetry.LocalChangesTelemetryCount") ?? 10;
485
+
486
+ assert(
487
+ isIDeltaManagerFull(this.parentContext.deltaManager),
488
+ 0xb83 /* Invalid delta manager */,
489
+ );
490
+
491
+ this._contextDeltaManagerProxy = new ContextDeltaManagerProxy(
492
+ this.parentContext.deltaManager,
493
+ () => this.isReadOnly(),
494
+ );
423
495
  }
424
496
 
425
497
  public dispose(): void {
@@ -437,6 +509,7 @@ export abstract class FluidDataStoreContext
437
509
  })
438
510
  .catch((error) => {});
439
511
  }
512
+ this._contextDeltaManagerProxy.dispose();
440
513
  }
441
514
 
442
515
  /**
@@ -615,6 +688,13 @@ export abstract class FluidDataStoreContext
615
688
  this.channel!.setConnectionState(connected, clientId);
616
689
  }
617
690
 
691
+ public notifyReadOnlyState(readonly: boolean): void {
692
+ this.verifyNotClosed("notifyReadOnlyState", false /* checkTombstone */);
693
+
694
+ this.channel?.notifyReadOnlyState?.(readonly);
695
+ this._contextDeltaManagerProxy.setReadonly(readonly);
696
+ }
697
+
618
698
  /**
619
699
  * Process messages for this data store. The messages here are contiguous messages for this data store in a batch.
620
700
  * @param messageCollection - The collection of messages to process.
@@ -25,13 +25,68 @@ import {
25
25
  import type { PendingStateManager } from "./pendingStateManager.js";
26
26
  import { summarizerClientType } from "./summary/index.js";
27
27
 
28
+ /**
29
+ * Interface defining event handlers for the BaseDeltaManagerProxy.
30
+ * These handlers allow customization of behavior for various DeltaManager events.
31
+ */
32
+ export interface BaseDeltaManagerProxyEventHandlers {
33
+ /**
34
+ * Event handler triggered when the readonly state changes.
35
+ * @param readonly - Indicates if the connection is readonly.
36
+ * @param readonlyConnectionReason - Optional reason and error details for the readonly state.
37
+ */
38
+ onReadonly: (
39
+ readonly: boolean,
40
+ readonlyConnectionReason?: { reason: string; error?: IErrorBase },
41
+ ) => void;
42
+
43
+ /**
44
+ * Event handler triggered before sending a message.
45
+ * @param messageBuffer - The buffer of messages to be sent.
46
+ */
47
+ onPrepareSend: (messageBuffer: unknown[]) => void;
48
+
49
+ /**
50
+ * Event handler triggered when an operation is submitted.
51
+ * @param message - The document message being submitted.
52
+ */
53
+ onSubmitOp: (message: IDocumentMessage) => void;
54
+
55
+ /**
56
+ * Event handler triggered when an operation is received.
57
+ * @param message - The sequenced document message received.
58
+ * @param processingTime - The time taken to process the message.
59
+ */
60
+ onOp: (message: ISequencedDocumentMessage, processingTime: number) => void;
61
+
62
+ /**
63
+ * Event handler triggered when a pong response is received.
64
+ * @param latency - The latency of the pong response.
65
+ */
66
+ onPong: (latency: number) => void;
67
+
68
+ /**
69
+ * Event handler triggered when a connection is established.
70
+ * @param details - The connection details.
71
+ * @param opsBehind - Optional number of operations behind the latest state.
72
+ */
73
+ onConnect: (details: IConnectionDetails, opsBehind?: number) => void;
74
+
75
+ /**
76
+ * Event handler triggered when a disconnection occurs.
77
+ * @param reason - The reason for the disconnection.
78
+ * @param error - Optional error details for the disconnection.
79
+ */
80
+ onDisconnect: (reason: string, error?: IAnyDriverError) => void;
81
+ }
82
+
28
83
  /**
29
84
  * Base class for DeltaManager proxy that proxy's access to the real DeltaManager.
30
85
  *
31
86
  * This class allows us to build proxy functionality without actually having to implement all the methods
32
87
  * of the DeltaManager.
33
88
  */
34
- export abstract class BaseDeltaManagerProxy
89
+ export class BaseDeltaManagerProxy
35
90
  extends TypedEventEmitter<IDeltaManagerEvents>
36
91
  implements IDeltaManagerFull
37
92
  {
@@ -99,29 +154,64 @@ export abstract class BaseDeltaManagerProxy
99
154
  return this.deltaManager.readOnlyInfo;
100
155
  }
101
156
 
102
- constructor(protected readonly deltaManager: IDeltaManagerFull) {
157
+ private readonly eventHandlers: BaseDeltaManagerProxyEventHandlers;
158
+
159
+ constructor(
160
+ protected readonly deltaManager: IDeltaManagerFull,
161
+ overrides?: Partial<BaseDeltaManagerProxyEventHandlers>,
162
+ ) {
103
163
  super();
104
164
 
165
+ this.eventHandlers = {
166
+ onPrepareSend: (messageBuffer: unknown[]): void => {
167
+ this.emit("prepareSend", messageBuffer);
168
+ },
169
+ onSubmitOp: (message: IDocumentMessage): void => {
170
+ this.emit("submitOp", message);
171
+ },
172
+ onOp: (message: ISequencedDocumentMessage, processingTime: number): void => {
173
+ this.emit("op", message, processingTime);
174
+ },
175
+ onPong: (latency: number): void => {
176
+ this.emit("pong", latency);
177
+ },
178
+ onConnect: (details: IConnectionDetails, opsBehind?: number): void => {
179
+ this.emit("connect", details, opsBehind);
180
+ },
181
+ onDisconnect: (reason: string, error?: IAnyDriverError): void => {
182
+ this.emit("disconnect", reason, error);
183
+ },
184
+ onReadonly: (
185
+ readonly: boolean,
186
+ readonlyConnectionReason?: { reason: string; error?: IErrorBase },
187
+ ): void => {
188
+ this.emit("readonly", readonly, readonlyConnectionReason);
189
+ },
190
+
191
+ ...overrides,
192
+ };
193
+
105
194
  // We are expecting this class to have many listeners, so we suppress noisy "MaxListenersExceededWarning" logging.
106
195
  super.setMaxListeners(0);
107
196
 
108
- this.deltaManager.on("prepareSend", this.onPrepareSend);
109
- this.deltaManager.on("submitOp", this.onSubmitOp);
110
- this.deltaManager.on("op", this.onOp);
111
- this.deltaManager.on("pong", this.onPong);
112
- this.deltaManager.on("connect", this.onConnect);
113
- this.deltaManager.on("disconnect", this.onDisconnect);
114
- this.deltaManager.on("readonly", this.onReadonly);
197
+ this.deltaManager.on("prepareSend", this.eventHandlers.onPrepareSend);
198
+ this.deltaManager.on("submitOp", this.eventHandlers.onSubmitOp);
199
+ this.deltaManager.on("op", this.eventHandlers.onOp);
200
+ this.deltaManager.on("pong", this.eventHandlers.onPong);
201
+ this.deltaManager.on("connect", this.eventHandlers.onConnect);
202
+ this.deltaManager.on("disconnect", this.eventHandlers.onDisconnect);
203
+ this.deltaManager.on("readonly", this.eventHandlers.onReadonly);
115
204
  }
116
205
 
117
206
  public dispose(): void {
118
- this.deltaManager.off("prepareSend", this.onPrepareSend);
119
- this.deltaManager.off("submitOp", this.onSubmitOp);
120
- this.deltaManager.off("op", this.onOp);
121
- this.deltaManager.off("pong", this.onPong);
122
- this.deltaManager.off("connect", this.onConnect);
123
- this.deltaManager.off("disconnect", this.onDisconnect);
124
- this.deltaManager.off("readonly", this.onReadonly);
207
+ this.deltaManager.off("prepareSend", this.eventHandlers.onPrepareSend);
208
+ this.deltaManager.off("submitOp", this.eventHandlers.onSubmitOp);
209
+ this.deltaManager.off("op", this.eventHandlers.onOp);
210
+ this.deltaManager.off("pong", this.eventHandlers.onPong);
211
+ this.deltaManager.off("connect", this.eventHandlers.onConnect);
212
+ this.deltaManager.off("disconnect", this.eventHandlers.onDisconnect);
213
+ this.deltaManager.off("readonly", this.eventHandlers.onReadonly);
214
+ this.removeAllListeners();
125
215
  }
126
216
 
127
217
  public submitSignal(content: string, targetClientId?: string): void {
@@ -131,34 +221,6 @@ export abstract class BaseDeltaManagerProxy
131
221
  public flush(): void {
132
222
  return this.deltaManager.flush();
133
223
  }
134
-
135
- private readonly onPrepareSend = (messageBuffer: unknown[]): void => {
136
- this.emit("prepareSend", messageBuffer);
137
- };
138
- private readonly onSubmitOp = (message: IDocumentMessage): void => {
139
- this.emit("submitOp", message);
140
- };
141
- protected readonly onOp = (
142
- message: ISequencedDocumentMessage,
143
- processingTime: number,
144
- ): void => {
145
- this.emit("op", message, processingTime);
146
- };
147
- private readonly onPong = (latency: number): void => {
148
- this.emit("pong", latency);
149
- };
150
- private readonly onConnect = (details: IConnectionDetails, opsBehind?: number): void => {
151
- this.emit("connect", details, opsBehind);
152
- };
153
- private readonly onDisconnect = (reason: string, error?: IAnyDriverError): void => {
154
- this.emit("disconnect", reason, error);
155
- };
156
- private readonly onReadonly = (
157
- readonly: boolean,
158
- readonlyConnectionReason?: { reason: string; error?: IErrorBase },
159
- ): void => {
160
- this.emit("readonly", readonly, readonlyConnectionReason);
161
- };
162
224
  }
163
225
 
164
226
  /**
@@ -167,31 +229,34 @@ export abstract class BaseDeltaManagerProxy
167
229
  * - Summarizer client should not be active to layers below the container runtime to restrict local changes.
168
230
  */
169
231
  export class DeltaManagerSummarizerProxy extends BaseDeltaManagerProxy {
232
+ public static wrapIfSummarizer(deltaManager: IDeltaManagerFull): IDeltaManagerFull {
233
+ if (deltaManager.clientDetails.type === summarizerClientType) {
234
+ return new DeltaManagerSummarizerProxy(deltaManager);
235
+ }
236
+ return deltaManager;
237
+ }
238
+
239
+ // eslint-disable-next-line @typescript-eslint/class-literal-property-style
170
240
  public get active(): boolean {
171
241
  // Summarize clients should not be active. There shouldn't be any local changes (writes) in the summarizer
172
242
  // except for the SummarizeOp which is generated by the runtime.
173
- return !this.isSummarizerClient && this.deltaManager.active;
243
+ return false;
174
244
  }
175
245
 
176
246
  public get readOnlyInfo(): ReadOnlyInfo {
177
247
  // Summarizer clients should be read-only as far as the runtime and layers below are concerned. There shouldn't
178
248
  // be any local changes (writes) in the summarizer except for the summarize op which is generated by the runtime.
179
- if (this.isSummarizerClient) {
180
- return {
181
- readonly: true,
182
- forced: false,
183
- permissions: undefined,
184
- storageOnly: false,
185
- };
186
- }
187
- return this.deltaManager.readOnlyInfo;
188
- }
189
249
 
190
- private readonly isSummarizerClient: boolean;
250
+ return {
251
+ readonly: true,
252
+ forced: false,
253
+ permissions: undefined,
254
+ storageOnly: false,
255
+ };
256
+ }
191
257
 
192
258
  constructor(protected readonly deltaManager: IDeltaManagerFull) {
193
- super(deltaManager);
194
- this.isSummarizerClient = this.deltaManager.clientDetails.type === summarizerClientType;
259
+ super(deltaManager, { onReadonly: () => {} });
195
260
  }
196
261
  }
197
262
 
@@ -228,17 +293,6 @@ export class DeltaManagerPendingOpsProxy extends BaseDeltaManagerProxy {
228
293
  };
229
294
  }
230
295
 
231
- protected readonly onOp = (
232
- message: ISequencedDocumentMessage,
233
- processingTime: number,
234
- ): void => {
235
- const messageIntercept = {
236
- ...message,
237
- minimumSequenceNumber: this.minimumSequenceNumber,
238
- };
239
- this.emit("op", messageIntercept, processingTime);
240
- };
241
-
242
296
  constructor(
243
297
  protected readonly deltaManager: IDeltaManagerFull,
244
298
  private readonly pendingStateManager: Pick<
@@ -246,6 +300,14 @@ export class DeltaManagerPendingOpsProxy extends BaseDeltaManagerProxy {
246
300
  "minimumPendingMessageSequenceNumber"
247
301
  >,
248
302
  ) {
249
- super(deltaManager);
303
+ super(deltaManager, {
304
+ onOp: (message: ISequencedDocumentMessage, processingTime: number): void => {
305
+ const messageIntercept = {
306
+ ...message,
307
+ minimumSequenceNumber: this.minimumSequenceNumber,
308
+ };
309
+ this.emit("op", messageIntercept, processingTime);
310
+ },
311
+ });
250
312
  }
251
313
  }
@@ -333,6 +333,8 @@ export type GarbageCollectionMessage = ISweepMessage | ITombstoneLoadedMessage;
333
333
 
334
334
  /**
335
335
  * Defines the APIs for the runtime object to be passed to the garbage collector.
336
+ *
337
+ * @internal
336
338
  */
337
339
  export interface IGarbageCollectionRuntime {
338
340
  /**
package/src/index.ts CHANGED
@@ -4,6 +4,8 @@
4
4
  */
5
5
 
6
6
  export {
7
+ ContainerRuntimeOptions,
8
+ ContainerRuntimeOptionsInternal,
7
9
  ISummaryRuntimeOptions,
8
10
  IContainerRuntimeOptions,
9
11
  IContainerRuntimeOptionsInternal,
@@ -14,11 +16,10 @@ export {
14
16
  DeletedResponseHeaderKey,
15
17
  TombstoneResponseHeaderKey,
16
18
  InactiveResponseHeaderKey,
17
- ICompressionRuntimeOptions,
18
- CompressionAlgorithms,
19
19
  RuntimeHeaderData,
20
- disabledCompressionConfig,
21
20
  } from "./containerRuntime.js";
21
+ export type { ICompressionRuntimeOptions } from "./compressionDefinitions.js";
22
+ export { CompressionAlgorithms, disabledCompressionConfig } from "./compressionDefinitions.js";
22
23
  export {
23
24
  ContainerMessageType,
24
25
  UnknownContainerRuntimeMessage,
@@ -35,6 +36,7 @@ export {
35
36
  IGCMetadata,
36
37
  GCFeatureMatrix,
37
38
  GCVersion,
39
+ IGarbageCollectionRuntime,
38
40
  IGCRuntimeOptions,
39
41
  IMarkPhaseStats,
40
42
  ISweepPhaseStats,
@@ -10,20 +10,15 @@ import {
10
10
  TelemetryDataTag,
11
11
  } from "@fluidframework/telemetry-utils/internal";
12
12
 
13
- import { ICompressionRuntimeOptions } from "../containerRuntime.js";
13
+ import { ICompressionRuntimeOptions } from "../compressionDefinitions.js";
14
14
  import { asBatchMetadata, type IBatchMetadata } from "../metadata.js";
15
15
  import type { IPendingMessage } from "../pendingStateManager.js";
16
16
 
17
- import {
18
- LocalBatchMessage,
19
- IBatchCheckpoint,
20
- type LocalBatch,
21
- type OutboundBatch,
22
- } from "./definitions.js";
17
+ import { LocalBatchMessage, IBatchCheckpoint, type LocalBatch } from "./definitions.js";
18
+ import { serializeOp } from "./opSerialization.js";
23
19
  import type { BatchStartInfo } from "./remoteMessageProcessor.js";
24
20
 
25
21
  export interface IBatchManagerOptions {
26
- readonly hardLimit: number;
27
22
  readonly compressionOptions?: ICompressionRuntimeOptions;
28
23
 
29
24
  /**
@@ -78,26 +73,16 @@ export function getEffectiveBatchId(
78
73
  return batchStart.batchId ?? generateBatchId(batchStart.clientId, batchStart.batchStartCsn);
79
74
  }
80
75
 
81
- /**
82
- * Estimated size of the stringification overhead for an op accumulated
83
- * from runtime to loader to the service.
84
- */
85
- const opOverhead = 200;
86
-
87
76
  /**
88
77
  * Helper class that manages partial batch & rollback.
89
78
  */
90
79
  export class BatchManager {
91
80
  private pendingBatch: LocalBatchMessage[] = [];
92
- private batchContentSize = 0;
93
81
  private hasReentrantOps = false;
94
82
 
95
83
  public get length(): number {
96
84
  return this.pendingBatch.length;
97
85
  }
98
- public get contentSizeInBytes(): number {
99
- return this.batchContentSize;
100
- }
101
86
 
102
87
  public get sequenceNumbers(): BatchSequenceNumbers {
103
88
  return {
@@ -125,29 +110,14 @@ export class BatchManager {
125
110
  message: LocalBatchMessage,
126
111
  reentrant: boolean,
127
112
  currentClientSequenceNumber?: number,
128
- ): boolean {
129
- const contentSize = this.batchContentSize + (message.serializedOp?.length ?? 0);
130
- const opCount = this.pendingBatch.length;
113
+ ): void {
131
114
  this.hasReentrantOps = this.hasReentrantOps || reentrant;
132
115
 
133
- // Attempt to estimate batch size, aka socket message size.
134
- // Each op has pretty large envelope, estimating to be 200 bytes.
135
- // Also content will be strigified, and that adds a lot of overhead due to a lot of escape characters.
136
- // Not taking it into account, as compression work should help there - compressed payload will be
137
- // initially stored as base64, and that requires only 2 extra escape characters.
138
- const socketMessageSize = contentSize + opOverhead * opCount;
139
-
140
- if (socketMessageSize >= this.options.hardLimit) {
141
- return false;
142
- }
143
-
144
116
  if (this.pendingBatch.length === 0) {
145
117
  this.clientSequenceNumber = currentClientSequenceNumber;
146
118
  }
147
119
 
148
- this.batchContentSize = contentSize;
149
120
  this.pendingBatch.push(message);
150
- return true;
151
121
  }
152
122
 
153
123
  public get empty(): boolean {
@@ -158,15 +128,15 @@ export class BatchManager {
158
128
  * Gets the pending batch and clears state for the next batch.
159
129
  */
160
130
  public popBatch(batchId?: BatchId): LocalBatch {
131
+ assert(this.pendingBatch[0] !== undefined, 0xb8a /* expected non-empty batch */);
161
132
  const batch: LocalBatch = {
162
133
  messages: this.pendingBatch,
163
- contentSizeInBytes: this.batchContentSize,
164
134
  referenceSequenceNumber: this.referenceSequenceNumber,
165
135
  hasReentrantOps: this.hasReentrantOps,
136
+ staged: this.pendingBatch[0].staged,
166
137
  };
167
138
 
168
139
  this.pendingBatch = [];
169
- this.batchContentSize = 0;
170
140
  this.clientSequenceNumber = undefined;
171
141
  this.hasReentrantOps = false;
172
142
 
@@ -184,7 +154,6 @@ export class BatchManager {
184
154
  this.clientSequenceNumber = startSequenceNumber;
185
155
  const rollbackOpsLifo = this.pendingBatch.splice(startPoint).reverse();
186
156
  for (const message of rollbackOpsLifo) {
187
- this.batchContentSize -= message.serializedOp?.length ?? 0;
188
157
  process(message);
189
158
  }
190
159
  const count = this.pendingBatch.length - startPoint;
@@ -192,9 +161,7 @@ export class BatchManager {
192
161
  throw new LoggingError("Ops generated during rollback", {
193
162
  count,
194
163
  ...tagData(TelemetryDataTag.UserData, {
195
- ops: JSON.stringify(
196
- this.pendingBatch.slice(startPoint).map((b) => b.serializedOp),
197
- ),
164
+ ops: serializeOp(this.pendingBatch.slice(startPoint).map((b) => b.runtimeOp)),
198
165
  }),
199
166
  });
200
167
  }
@@ -233,18 +200,6 @@ const addBatchMetadata = (batch: LocalBatch, batchId?: BatchId): LocalBatch => {
233
200
  return batch;
234
201
  };
235
202
 
236
- /**
237
- * Estimates the real size in bytes on the socket for a given batch. It assumes that
238
- * the envelope size (and the size of an empty op) is 200 bytes, taking into account
239
- * extra overhead from stringification.
240
- *
241
- * @param batch - the batch to inspect
242
- * @returns An estimate of the payload size in bytes which will be produced when the batch is sent over the wire
243
- */
244
- export const estimateSocketSize = (batch: OutboundBatch): number => {
245
- return batch.contentSizeInBytes + opOverhead * batch.messages.length;
246
- };
247
-
248
203
  export const sequenceNumbersMatch = (
249
204
  seqNums: BatchSequenceNumbers,
250
205
  otherSeqNums: BatchSequenceNumbers,
@@ -5,17 +5,33 @@
5
5
 
6
6
  import type { IBatchMessage } from "@fluidframework/container-definitions/internal";
7
7
 
8
- import { CompressionAlgorithms } from "../containerRuntime.js";
8
+ import { CompressionAlgorithms } from "../compressionDefinitions.js";
9
+ import type { LocalContainerRuntimeMessage } from "../messageTypes.js";
9
10
 
10
11
  /**
11
12
  * Local Batch message, before it is virtualized and sent to the ordering service
12
13
  */
13
14
  export interface LocalBatchMessage {
14
- serializedOp: string;
15
+ /**
16
+ * The original local op
17
+ */
18
+ runtimeOp: LocalContainerRuntimeMessage;
19
+ /**
20
+ * Optional metadata which is not to be serialized with the op, and is visible to the ordering service
21
+ */
15
22
  metadata?: Record<string, unknown>;
23
+ /**
24
+ * Metadata used by this local client in flows such as rebase
25
+ */
16
26
  localOpMetadata?: unknown;
27
+ /**
28
+ * Reference sequence number this op is based on
29
+ */
17
30
  referenceSequenceNumber: number;
18
- compression?: CompressionAlgorithms;
31
+ /**
32
+ * If true, this op is not to be submitted to the ordering service yet, since it was submitted during Staging Mode
33
+ */
34
+ staged?: boolean;
19
35
 
20
36
  /**
21
37
  * @deprecated Use serializedOp
@@ -23,6 +39,15 @@ export interface LocalBatchMessage {
23
39
  contents?: never; // To ensure we don't leave this one when converting from OutboundBatchMessage
24
40
  }
25
41
 
42
+ /**
43
+ * Placeholder for an empty batch, for tracking the pending local empty batch
44
+ */
45
+ export interface LocalEmptyBatchPlaceholder {
46
+ metadata?: Record<string, unknown>;
47
+ localOpMetadata: { emptyBatch: true };
48
+ referenceSequenceNumber: number;
49
+ }
50
+
26
51
  /**
27
52
  * Virtualized Batch message, on its way out the door to the ordering service
28
53
  */
@@ -40,30 +65,39 @@ export type OutboundBatchMessage = IBatchMessage & {
40
65
  /**
41
66
  * A batch of messages we have accumulated locally, but haven't sent to the ordering service yet.
42
67
  */
43
- export type LocalBatch = IBatch<LocalBatchMessage[]>;
68
+ export interface LocalBatch extends IBatch<LocalBatchMessage[]> {
69
+ /**
70
+ * If true, this batch is not to be submitted to the ordering service yet, since it was submitted during Staging Mode
71
+ */
72
+ staged?: boolean;
73
+ }
44
74
 
45
75
  /**
46
76
  * A batch of messages that has been virtualized as needed (grouped, compressed, chunked)
47
77
  * and is ready to be sent to the ordering service.
78
+ * At the very least, the op contents have been serialized to string.
48
79
  */
49
- export type OutboundBatch = IBatch<OutboundBatchMessage[]>;
80
+ export interface OutboundBatch<
81
+ TMessages extends OutboundBatchMessage[] = OutboundBatchMessage[],
82
+ > extends IBatch<TMessages> {
83
+ /**
84
+ * Sum of the in-memory content sizes of all messages in the batch.
85
+ * If the batch is compressed, this number reflects the post-compression size.
86
+ */
87
+ readonly contentSizeInBytes: number;
88
+ }
50
89
 
51
90
  /**
52
91
  * An {@link OutboundBatch} with exactly one message
53
92
  * This type is helpful as Grouping yields this kind of batch, and Compression only operates on this type of batch.
54
93
  */
55
- export type OutboundSingletonBatch = IBatch<[OutboundBatchMessage]>;
94
+ export type OutboundSingletonBatch = OutboundBatch<[OutboundBatchMessage]>;
56
95
 
57
96
  /**
58
97
  * Base batch interface used internally by the runtime.
59
98
  * See {@link LocalBatch} and {@link OutboundBatch} for the concrete types.
60
99
  */
61
100
  interface IBatch<TMessages extends LocalBatchMessage[] | OutboundBatchMessage[]> {
62
- /**
63
- * Sum of the in-memory content sizes of all messages in the batch.
64
- * If the batch is compressed, this number reflects the post-compression size.
65
- */
66
- readonly contentSizeInBytes: number;
67
101
  /**
68
102
  * All the messages in the batch
69
103
  */