@fluidframework/container-runtime 2.1.0 → 2.1.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 (36) hide show
  1. package/container-runtime.test-files.tar +0 -0
  2. package/dist/containerRuntime.d.ts +1 -0
  3. package/dist/containerRuntime.d.ts.map +1 -1
  4. package/dist/containerRuntime.js +40 -34
  5. package/dist/containerRuntime.js.map +1 -1
  6. package/dist/opLifecycle/remoteMessageProcessor.d.ts +7 -7
  7. package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  8. package/dist/opLifecycle/remoteMessageProcessor.js +7 -18
  9. package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
  10. package/dist/packageVersion.d.ts +1 -1
  11. package/dist/packageVersion.js +1 -1
  12. package/dist/packageVersion.js.map +1 -1
  13. package/dist/pendingStateManager.d.ts +2 -12
  14. package/dist/pendingStateManager.d.ts.map +1 -1
  15. package/dist/pendingStateManager.js +0 -12
  16. package/dist/pendingStateManager.js.map +1 -1
  17. package/lib/containerRuntime.d.ts +1 -0
  18. package/lib/containerRuntime.d.ts.map +1 -1
  19. package/lib/containerRuntime.js +40 -34
  20. package/lib/containerRuntime.js.map +1 -1
  21. package/lib/opLifecycle/remoteMessageProcessor.d.ts +7 -7
  22. package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  23. package/lib/opLifecycle/remoteMessageProcessor.js +7 -18
  24. package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
  25. package/lib/packageVersion.d.ts +1 -1
  26. package/lib/packageVersion.js +1 -1
  27. package/lib/packageVersion.js.map +1 -1
  28. package/lib/pendingStateManager.d.ts +2 -12
  29. package/lib/pendingStateManager.d.ts.map +1 -1
  30. package/lib/pendingStateManager.js +0 -12
  31. package/lib/pendingStateManager.js.map +1 -1
  32. package/package.json +19 -32
  33. package/src/containerRuntime.ts +48 -39
  34. package/src/opLifecycle/remoteMessageProcessor.ts +9 -27
  35. package/src/packageVersion.ts +1 -1
  36. package/src/pendingStateManager.ts +2 -21
@@ -695,7 +695,7 @@ export const makeLegacySendBatchFn =
695
695
  type MessageWithContext = {
696
696
  local: boolean;
697
697
  savedOp?: boolean;
698
- localOpMetadata?: unknown;
698
+ batchStartCsn: number;
699
699
  } & (
700
700
  | {
701
701
  message: InboundSequencedContainerRuntimeMessage;
@@ -1267,6 +1267,7 @@ export class ContainerRuntime
1267
1267
  private dirtyContainer: boolean;
1268
1268
  private emitDirtyDocumentEvent = true;
1269
1269
  private readonly disableAttachReorder: boolean | undefined;
1270
+ private readonly useDeltaManagerOpsProxy: boolean;
1270
1271
  private readonly closeSummarizerDelayMs: number;
1271
1272
  private readonly defaultTelemetrySignalSampleCount = 100;
1272
1273
  private readonly _perfSignalData: IPerfSignalReport = {
@@ -1558,8 +1559,8 @@ export class ContainerRuntime
1558
1559
  );
1559
1560
 
1560
1561
  let outerDeltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>;
1561
- const useDeltaManagerOpsProxy =
1562
- this.mc.config.getBoolean("Fluid.ContainerRuntime.DeltaManagerOpsProxy") !== false;
1562
+ this.useDeltaManagerOpsProxy =
1563
+ this.mc.config.getBoolean("Fluid.ContainerRuntime.DeltaManagerOpsProxy") === true;
1563
1564
  // The summarizerDeltaManager Proxy is used to lie to the summarizer to convince it is in the right state as a summarizer client.
1564
1565
  const summarizerDeltaManagerProxy = new DeltaManagerSummarizerProxy(
1565
1566
  this.innerDeltaManager,
@@ -1568,7 +1569,7 @@ export class ContainerRuntime
1568
1569
 
1569
1570
  // The DeltaManagerPendingOpsProxy is used to control the minimum sequence number
1570
1571
  // It allows us to lie to the layers below so that they can maintain enough local state for rebasing ops.
1571
- if (useDeltaManagerOpsProxy) {
1572
+ if (this.useDeltaManagerOpsProxy) {
1572
1573
  const pendingOpsDeltaManagerProxy = new DeltaManagerPendingOpsProxy(
1573
1574
  summarizerDeltaManagerProxy,
1574
1575
  this.pendingStateManager,
@@ -2634,38 +2635,34 @@ export class ContainerRuntime
2634
2635
  const messageCopy = { ...messageArg };
2635
2636
  // We expect runtime messages to have JSON contents - deserialize it in place.
2636
2637
  ensureContentsDeserialized(messageCopy, modernRuntimeMessage, logLegacyCase);
2637
- if (modernRuntimeMessage) {
2638
- const processResult = this.remoteMessageProcessor.process(messageCopy, logLegacyCase);
2639
- if (processResult === undefined) {
2640
- // This means the incoming message is an incomplete part of a message or batch
2641
- // and we need to process more messages before the rest of the system can understand it.
2642
- return;
2643
- }
2644
- const batchStartCsn = processResult.batchStartCsn;
2645
- const batch = processResult.messages;
2646
- const messages: {
2647
- message: InboundSequencedContainerRuntimeMessage;
2648
- localOpMetadata: unknown;
2649
- }[] = local
2650
- ? this.pendingStateManager.processPendingLocalBatch(batch, batchStartCsn)
2651
- : batch.map((message) => ({ message, localOpMetadata: undefined }));
2652
- messages.forEach(({ message, localOpMetadata }) => {
2653
- const msg: MessageWithContext = {
2654
- message,
2655
- local,
2656
- modernRuntimeMessage,
2657
- savedOp,
2658
- localOpMetadata,
2659
- };
2660
- this.ensureNoDataModelChanges(() => this.processCore(msg));
2661
- });
2662
- } else {
2663
- const msg: MessageWithContext = {
2664
- message: messageCopy as InboundSequencedContainerRuntimeMessageOrSystemMessage,
2665
- local,
2666
- modernRuntimeMessage,
2667
- savedOp,
2668
- };
2638
+ const processResult = this.remoteMessageProcessor.process(messageCopy);
2639
+ if (processResult === undefined) {
2640
+ // This means the incoming message is an incomplete part of a message or batch
2641
+ // and we need to process more messages before the rest of the system can understand it.
2642
+ return;
2643
+ }
2644
+ for (const message of processResult.messages) {
2645
+ const msg: MessageWithContext = modernRuntimeMessage
2646
+ ? {
2647
+ // Cast it since we expect it to be this based on modernRuntimeMessage computation above.
2648
+ // There is nothing really ensuring that anytime original message.type is Operation that
2649
+ // the result messages will be so. In the end modern bool being true only directs to
2650
+ // throw error if ultimately unrecognized without compat details saying otherwise.
2651
+ message: message as InboundSequencedContainerRuntimeMessage,
2652
+ local,
2653
+ modernRuntimeMessage,
2654
+ batchStartCsn: processResult.batchStartCsn,
2655
+ }
2656
+ : // Unrecognized message will be ignored.
2657
+ {
2658
+ message,
2659
+ local,
2660
+ modernRuntimeMessage,
2661
+ batchStartCsn: processResult.batchStartCsn,
2662
+ };
2663
+ msg.savedOp = savedOp;
2664
+
2665
+ // ensure that we observe any re-entrancy, and if needed, rebase ops
2669
2666
  this.ensureNoDataModelChanges(() => this.processCore(msg));
2670
2667
  }
2671
2668
  }
@@ -2676,13 +2673,14 @@ export class ContainerRuntime
2676
2673
  * Direct the message to the correct subsystem for processing, and implement other side effects
2677
2674
  */
2678
2675
  private processCore(messageWithContext: MessageWithContext) {
2679
- const { message, local, localOpMetadata } = messageWithContext;
2676
+ const { message, local } = messageWithContext;
2680
2677
 
2681
2678
  // Intercept to reduce minimum sequence number to the delta manager's minimum sequence number.
2682
2679
  // Sequence numbers are not guaranteed to follow any sort of order. Re-entrancy is one of those situations
2683
2680
  if (
2681
+ this.useDeltaManagerOpsProxy &&
2684
2682
  this.deltaManager.minimumSequenceNumber <
2685
- messageWithContext.message.minimumSequenceNumber
2683
+ messageWithContext.message.minimumSequenceNumber
2686
2684
  ) {
2687
2685
  messageWithContext.message.minimumSequenceNumber =
2688
2686
  this.deltaManager.minimumSequenceNumber;
@@ -2696,12 +2694,23 @@ export class ContainerRuntime
2696
2694
  this._processedClientSequenceNumber = message.clientSequenceNumber;
2697
2695
 
2698
2696
  try {
2699
- // RemoteMessageProcessor would have already reconstituted Chunked Ops into the original op type
2697
+ // See commit that added this assert for more details.
2698
+ // These calls should be made for all but chunked ops:
2699
+ // 1) this.pendingStateManager.processPendingLocalMessage() below
2700
+ // 2) this.resetReconnectCount() below
2700
2701
  assert(
2701
2702
  message.type !== ContainerMessageType.ChunkedOp,
2702
2703
  0x93b /* we should never get here with chunked ops */,
2703
2704
  );
2704
2705
 
2706
+ let localOpMetadata: unknown;
2707
+ if (local && messageWithContext.modernRuntimeMessage) {
2708
+ localOpMetadata = this.pendingStateManager.processPendingLocalMessage(
2709
+ messageWithContext.message,
2710
+ messageWithContext.batchStartCsn,
2711
+ );
2712
+ }
2713
+
2705
2714
  // If there are no more pending messages after processing a local message,
2706
2715
  // the document is no longer dirty.
2707
2716
  if (!this.hasPendingMessages()) {
@@ -13,6 +13,7 @@ import {
13
13
  ContainerMessageType,
14
14
  type InboundContainerRuntimeMessage,
15
15
  type InboundSequencedContainerRuntimeMessage,
16
+ type InboundSequencedContainerRuntimeMessageOrSystemMessage,
16
17
  type InboundSequencedRecentlyAddedContainerRuntimeMessage,
17
18
  } from "../messageTypes.js";
18
19
  import { asBatchMetadata } from "../metadata.js";
@@ -35,7 +36,6 @@ export class RemoteMessageProcessor {
35
36
  * @remarks For chunked batches, this is the CSN of the "representative" chunk (the final chunk)
36
37
  */
37
38
  private batchStartCsn: number | undefined;
38
- private readonly processorBatch: InboundSequencedContainerRuntimeMessage[] = [];
39
39
 
40
40
  constructor(
41
41
  private readonly opSplitter: OpSplitter,
@@ -52,7 +52,7 @@ export class RemoteMessageProcessor {
52
52
  }
53
53
 
54
54
  /**
55
- * Ungroups and Unchunks the runtime ops of a batch received over the wire
55
+ * Ungroups and Unchunks the runtime ops encapsulated by the single remoteMessage received over the wire
56
56
  * @param remoteMessageCopy - A shallow copy of a message from another client, possibly virtualized
57
57
  * (grouped, compressed, and/or chunked).
58
58
  * Being a shallow copy, it's considered mutable, meaning no other Container or other parallel procedure
@@ -67,15 +67,13 @@ export class RemoteMessageProcessor {
67
67
  * 3. If grouped, ungroup the message
68
68
  * For more details, see https://github.com/microsoft/FluidFramework/blob/main/packages/runtime/container-runtime/src/opLifecycle/README.md#inbound
69
69
  *
70
- * @returns all the unchunked, decompressed, ungrouped, unpacked InboundSequencedContainerRuntimeMessage from a single batch
71
- * or undefined if the batch is not yet complete.
70
+ * @returns the unchunked, decompressed, ungrouped, unpacked SequencedContainerRuntimeMessages encapsulated in the remote message.
71
+ * For ops that weren't virtualized (e.g. System ops that the ContainerRuntime will ultimately ignore),
72
+ * a singleton array [remoteMessageCopy] is returned
72
73
  */
73
- public process(
74
- remoteMessageCopy: ISequencedDocumentMessage,
75
- logLegacyCase: (codePath: string) => void,
76
- ):
74
+ public process(remoteMessageCopy: ISequencedDocumentMessage):
77
75
  | {
78
- messages: InboundSequencedContainerRuntimeMessage[];
76
+ messages: InboundSequencedContainerRuntimeMessageOrSystemMessage[];
79
77
  batchStartCsn: number;
80
78
  }
81
79
  | undefined {
@@ -109,10 +107,6 @@ export class RemoteMessageProcessor {
109
107
  this.batchStartCsn === undefined,
110
108
  0x9d3 /* Grouped batch interrupting another batch */,
111
109
  );
112
- assert(
113
- this.processorBatch.length === 0,
114
- 0x9d4 /* Processor batch should be empty on grouped batch */,
115
- );
116
110
  return {
117
111
  messages: this.opGroupingManager.ungroupOp(message).map(unpack),
118
112
  batchStartCsn: message.clientSequenceNumber,
@@ -122,21 +116,9 @@ export class RemoteMessageProcessor {
122
116
  const batchStartCsn = this.getAndUpdateBatchStartCsn(message);
123
117
 
124
118
  // Do a final unpack of runtime messages in case the message was not grouped, compressed, or chunked
125
- unpackRuntimeMessage(message, logLegacyCase);
126
- this.processorBatch.push(message as InboundSequencedContainerRuntimeMessage);
127
-
128
- // this.batchStartCsn is undefined only if we have processed all messages in the batch.
129
- // If it's still defined, we're still in the middle of a batch, so we return nothing, letting
130
- // containerRuntime know that we're waiting for more messages to complete the batch.
131
- if (this.batchStartCsn !== undefined) {
132
- // batch not yet complete
133
- return undefined;
134
- }
135
-
136
- const messages = [...this.processorBatch];
137
- this.processorBatch.length = 0;
119
+ unpackRuntimeMessage(message);
138
120
  return {
139
- messages,
121
+ messages: [message as InboundSequencedContainerRuntimeMessageOrSystemMessage],
140
122
  batchStartCsn,
141
123
  };
142
124
  }
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/container-runtime";
9
- export const pkgVersion = "2.1.0";
9
+ export const pkgVersion = "2.1.1";
@@ -16,7 +16,7 @@ import {
16
16
  import Deque from "double-ended-queue";
17
17
  import { v4 as uuid } from "uuid";
18
18
 
19
- import { type InboundSequencedContainerRuntimeMessage } from "./messageTypes.js";
19
+ import { InboundSequencedContainerRuntimeMessage } from "./messageTypes.js";
20
20
  import { asBatchMetadata, IBatchMetadata } from "./metadata.js";
21
21
  import { BatchId, BatchMessage, generateBatchId } from "./opLifecycle/index.js";
22
22
  import { pkgVersion } from "./packageVersion.js";
@@ -293,25 +293,6 @@ export class PendingStateManager implements IDisposable {
293
293
  }
294
294
  }
295
295
 
296
- /**
297
- * Processes the incoming batch from the server. It verifies that messages are received in the right order and
298
- * that the batch information is correct.
299
- * @param batch - The batch that is being processed.
300
- * @param batchStartCsn - The clientSequenceNumber of the start of this message's batch
301
- */
302
- public processPendingLocalBatch(
303
- batch: InboundSequencedContainerRuntimeMessage[],
304
- batchStartCsn: number,
305
- ): {
306
- message: InboundSequencedContainerRuntimeMessage;
307
- localOpMetadata: unknown;
308
- }[] {
309
- return batch.map((message) => ({
310
- message,
311
- localOpMetadata: this.processPendingLocalMessage(message, batchStartCsn),
312
- }));
313
- }
314
-
315
296
  /**
316
297
  * Processes a local message once its ack'd by the server. It verifies that there was no data corruption and that
317
298
  * the batch information was preserved for batch messages.
@@ -319,7 +300,7 @@ export class PendingStateManager implements IDisposable {
319
300
  * @param batchStartCsn - The clientSequenceNumber of the start of this message's batch (assigned during submit)
320
301
  * (not to be confused with message.clientSequenceNumber - the overwritten value in case of grouped batching)
321
302
  */
322
- private processPendingLocalMessage(
303
+ public processPendingLocalMessage(
323
304
  message: InboundSequencedContainerRuntimeMessage,
324
305
  batchStartCsn: number,
325
306
  ): unknown {