@fluidframework/container-runtime 2.40.0-336023 → 2.40.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 (92) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/api-report/container-runtime.legacy.alpha.api.md +4 -0
  3. package/container-runtime.test-files.tar +0 -0
  4. package/dist/blobManager/blobManager.d.ts +31 -8
  5. package/dist/blobManager/blobManager.d.ts.map +1 -1
  6. package/dist/blobManager/blobManager.js +90 -17
  7. package/dist/blobManager/blobManager.js.map +1 -1
  8. package/dist/channelCollection.d.ts +8 -2
  9. package/dist/channelCollection.d.ts.map +1 -1
  10. package/dist/channelCollection.js +29 -6
  11. package/dist/channelCollection.js.map +1 -1
  12. package/dist/compatUtils.d.ts +19 -10
  13. package/dist/compatUtils.d.ts.map +1 -1
  14. package/dist/compatUtils.js +39 -32
  15. package/dist/compatUtils.js.map +1 -1
  16. package/dist/containerRuntime.d.ts +29 -13
  17. package/dist/containerRuntime.d.ts.map +1 -1
  18. package/dist/containerRuntime.js +139 -149
  19. package/dist/containerRuntime.js.map +1 -1
  20. package/dist/dataStoreContext.d.ts +12 -4
  21. package/dist/dataStoreContext.d.ts.map +1 -1
  22. package/dist/dataStoreContext.js +37 -18
  23. package/dist/dataStoreContext.js.map +1 -1
  24. package/dist/index.d.ts +1 -0
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js.map +1 -1
  27. package/dist/legacy.d.ts +1 -0
  28. package/dist/opLifecycle/index.d.ts +1 -1
  29. package/dist/opLifecycle/index.d.ts.map +1 -1
  30. package/dist/opLifecycle/index.js.map +1 -1
  31. package/dist/opLifecycle/outbox.d.ts +20 -7
  32. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  33. package/dist/opLifecycle/outbox.js +16 -20
  34. package/dist/opLifecycle/outbox.js.map +1 -1
  35. package/dist/packageVersion.d.ts +1 -1
  36. package/dist/packageVersion.d.ts.map +1 -1
  37. package/dist/packageVersion.js +1 -1
  38. package/dist/packageVersion.js.map +1 -1
  39. package/dist/pendingStateManager.d.ts +22 -8
  40. package/dist/pendingStateManager.d.ts.map +1 -1
  41. package/dist/pendingStateManager.js +11 -16
  42. package/dist/pendingStateManager.js.map +1 -1
  43. package/lib/blobManager/blobManager.d.ts +31 -8
  44. package/lib/blobManager/blobManager.d.ts.map +1 -1
  45. package/lib/blobManager/blobManager.js +91 -18
  46. package/lib/blobManager/blobManager.js.map +1 -1
  47. package/lib/channelCollection.d.ts +8 -2
  48. package/lib/channelCollection.d.ts.map +1 -1
  49. package/lib/channelCollection.js +29 -6
  50. package/lib/channelCollection.js.map +1 -1
  51. package/lib/compatUtils.d.ts +19 -10
  52. package/lib/compatUtils.d.ts.map +1 -1
  53. package/lib/compatUtils.js +36 -29
  54. package/lib/compatUtils.js.map +1 -1
  55. package/lib/containerRuntime.d.ts +29 -13
  56. package/lib/containerRuntime.d.ts.map +1 -1
  57. package/lib/containerRuntime.js +60 -70
  58. package/lib/containerRuntime.js.map +1 -1
  59. package/lib/dataStoreContext.d.ts +12 -4
  60. package/lib/dataStoreContext.d.ts.map +1 -1
  61. package/lib/dataStoreContext.js +38 -19
  62. package/lib/dataStoreContext.js.map +1 -1
  63. package/lib/index.d.ts +1 -0
  64. package/lib/index.d.ts.map +1 -1
  65. package/lib/index.js.map +1 -1
  66. package/lib/legacy.d.ts +1 -0
  67. package/lib/opLifecycle/index.d.ts +1 -1
  68. package/lib/opLifecycle/index.d.ts.map +1 -1
  69. package/lib/opLifecycle/index.js.map +1 -1
  70. package/lib/opLifecycle/outbox.d.ts +20 -7
  71. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  72. package/lib/opLifecycle/outbox.js +16 -20
  73. package/lib/opLifecycle/outbox.js.map +1 -1
  74. package/lib/packageVersion.d.ts +1 -1
  75. package/lib/packageVersion.d.ts.map +1 -1
  76. package/lib/packageVersion.js +1 -1
  77. package/lib/packageVersion.js.map +1 -1
  78. package/lib/pendingStateManager.d.ts +22 -8
  79. package/lib/pendingStateManager.d.ts.map +1 -1
  80. package/lib/pendingStateManager.js +11 -16
  81. package/lib/pendingStateManager.js.map +1 -1
  82. package/package.json +18 -18
  83. package/src/blobManager/blobManager.ts +141 -33
  84. package/src/channelCollection.ts +42 -6
  85. package/src/compatUtils.ts +53 -30
  86. package/src/containerRuntime.ts +102 -81
  87. package/src/dataStoreContext.ts +44 -25
  88. package/src/index.ts +1 -0
  89. package/src/opLifecycle/index.ts +1 -0
  90. package/src/opLifecycle/outbox.ts +42 -33
  91. package/src/packageVersion.ts +1 -1
  92. package/src/pendingStateManager.ts +37 -20
@@ -66,10 +66,27 @@ export interface IOutboxParameters {
66
66
  readonly logger: ITelemetryBaseLogger;
67
67
  readonly groupingManager: OpGroupingManager;
68
68
  readonly getCurrentSequenceNumbers: () => BatchSequenceNumbers;
69
- readonly reSubmit: (message: PendingMessageResubmitData) => void;
69
+ readonly reSubmit: (message: PendingMessageResubmitData, squash: boolean) => void;
70
70
  readonly opReentrancy: () => boolean;
71
71
  }
72
72
 
73
+ /**
74
+ * Info needed to correctly resubmit a batch
75
+ */
76
+ export interface BatchResubmitInfo {
77
+ /**
78
+ * If defined, indicates the Batch ID of the batch being resubmitted.
79
+ * This must be preserved on the new batch about to be submitted so they can be correlated/deduped in case both are sent.
80
+ */
81
+ batchId?: string;
82
+ /**
83
+ * Indicates whether or not this batch is "staged", meaning it should not be sent to the ordering service yet
84
+ * This is important on resubmit because we may be in Staging Mode for new changes,
85
+ * but resubmitting a non-staged change from before entering Staging Mode
86
+ */
87
+ staged: boolean;
88
+ }
89
+
73
90
  /**
74
91
  * Temporarily increase the stack limit while executing the provided action.
75
92
  * If a negative value is provided for `length`, no stack frames will be collected.
@@ -335,37 +352,33 @@ export class Outbox {
335
352
  * This method is expected to be called at the end of a batch.
336
353
  *
337
354
  * @throws If called from a reentrant context, or if the batch being flushed is too large.
338
- * @param resubmittingBatchId - If defined, indicates this is a resubmission of a batch
339
- * with the given Batch ID, which must be preserved
340
- * @param resubmittingStagedBatch - If defined, indicates this is a resubmission of a batch that is staged,
341
- * meaning it should not be sent to the ordering service yet.
355
+ * @param resubmitInfo - Key information when flushing a resubmitted batch. Undefined means this is not resubmit.
342
356
  */
343
- public flush(resubmittingBatchId?: BatchId, resubmittingStagedBatch?: boolean): void {
357
+ public flush(resubmitInfo?: BatchResubmitInfo): void {
344
358
  assert(
345
359
  !this.isContextReentrant(),
346
360
  0xb7b /* Flushing must not happen while incoming changes are being processed */,
347
361
  );
348
-
349
- this.flushAll(resubmittingBatchId, resubmittingStagedBatch);
362
+ this.flushAll(resubmitInfo);
350
363
  }
351
364
 
352
- private flushAll(resubmittingBatchId?: BatchId, resubmittingStagedBatch?: boolean): void {
365
+ private flushAll(resubmitInfo?: BatchResubmitInfo): void {
353
366
  const allBatchesEmpty =
354
367
  this.idAllocationBatch.empty && this.blobAttachBatch.empty && this.mainBatch.empty;
355
368
  if (allBatchesEmpty) {
356
- // If we're resubmitting and all batches are empty, we need to flush an empty batch.
357
- // Note that we currently resubmit one batch at a time, so on resubmit, 2 of the 3 batches will *always* be empty.
369
+ // If we're resubmitting with a batchId and all batches are empty, we need to flush an empty batch.
370
+ // Note that we currently resubmit one batch at a time, so on resubmit, 1 of the 2 batches will *always* be empty.
358
371
  // It's theoretically possible that we don't *need* to resubmit this empty batch, and in those cases, it'll safely be ignored
359
372
  // by the rest of the system, including remote clients.
360
373
  // In some cases we *must* resubmit the empty batch (to match up with a non-empty version tracked locally by a container fork), so we do it always.
361
- if (resubmittingBatchId) {
362
- this.flushEmptyBatch(resubmittingBatchId, resubmittingStagedBatch === true);
374
+ if (resubmitInfo?.batchId !== undefined) {
375
+ this.flushEmptyBatch(resubmitInfo.batchId, resubmitInfo.staged);
363
376
  }
364
377
  return;
365
378
  }
366
379
 
367
380
  // Don't use resubmittingBatchId for idAllocationBatch.
368
- // ID Allocation messages are not directly resubmitted so we don't want to reuse the batch ID.
381
+ // ID Allocation messages are not directly resubmitted so don't pass the resubmitInfo
369
382
  this.flushInternal({
370
383
  batchManager: this.idAllocationBatch,
371
384
  // Note: For now, we will never stage ID Allocation messages.
@@ -374,13 +387,11 @@ export class Outbox {
374
387
  this.flushInternal({
375
388
  batchManager: this.blobAttachBatch,
376
389
  disableGroupedBatching: true,
377
- resubmittingBatchId,
378
- resubmittingStagedBatch,
390
+ resubmitInfo,
379
391
  });
380
392
  this.flushInternal({
381
393
  batchManager: this.mainBatch,
382
- resubmittingBatchId,
383
- resubmittingStagedBatch,
394
+ resubmitInfo,
384
395
  });
385
396
  }
386
397
 
@@ -416,25 +427,19 @@ export class Outbox {
416
427
  private flushInternal(params: {
417
428
  batchManager: BatchManager;
418
429
  disableGroupedBatching?: boolean;
419
- resubmittingBatchId?: BatchId; // undefined if not resubmitting
420
- resubmittingStagedBatch?: boolean; // undefined if not resubmitting
430
+ resubmitInfo?: BatchResubmitInfo; // undefined if not resubmitting
421
431
  }): void {
422
- const {
423
- batchManager,
424
- disableGroupedBatching = false,
425
- resubmittingBatchId,
426
- resubmittingStagedBatch,
427
- } = params;
432
+ const { batchManager, disableGroupedBatching = false, resubmitInfo } = params;
428
433
  if (batchManager.empty) {
429
434
  return;
430
435
  }
431
436
 
432
- const rawBatch = batchManager.popBatch(resubmittingBatchId);
437
+ const rawBatch = batchManager.popBatch(resubmitInfo?.batchId);
433
438
 
434
439
  // When resubmitting, we respect the staged state of the original batch.
435
440
  // In this case rawBatch.staged will match the state of inStagingMode when
436
441
  // the resubmit occurred, which is not relevant.
437
- const staged = resubmittingStagedBatch ?? rawBatch.staged === true;
442
+ const staged = resubmitInfo?.staged ?? rawBatch.staged === true;
438
443
 
439
444
  const groupingEnabled =
440
445
  !disableGroupedBatching && this.params.groupingManager.groupedBatchingEnabled();
@@ -490,12 +495,16 @@ export class Outbox {
490
495
  assert(batchManager.options.canRebase, 0x9a7 /* BatchManager does not support rebase */);
491
496
 
492
497
  this.rebasing = true;
498
+ const squash = false;
493
499
  for (const message of rawBatch.messages) {
494
- this.params.reSubmit({
495
- runtimeOp: message.runtimeOp,
496
- localOpMetadata: message.localOpMetadata,
497
- opMetadata: message.metadata,
498
- });
500
+ this.params.reSubmit(
501
+ {
502
+ runtimeOp: message.runtimeOp,
503
+ localOpMetadata: message.localOpMetadata,
504
+ opMetadata: message.metadata,
505
+ },
506
+ squash,
507
+ );
499
508
  }
500
509
 
501
510
  if (this.batchRebasesToReport > 0) {
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/container-runtime";
9
- export const pkgVersion = "2.40.0-336023";
9
+ export const pkgVersion = "2.40.0";
@@ -22,13 +22,13 @@ import {
22
22
  } from "./messageTypes.js";
23
23
  import { asBatchMetadata, asEmptyBatchLocalOpMetadata } from "./metadata.js";
24
24
  import {
25
- BatchId,
26
25
  LocalBatchMessage,
27
26
  getEffectiveBatchId,
28
27
  BatchStartInfo,
29
28
  InboundMessageResult,
30
29
  serializeOp,
31
30
  type LocalEmptyBatchPlaceholder,
31
+ type BatchResubmitInfo,
32
32
  } from "./opLifecycle/index.js";
33
33
 
34
34
  /**
@@ -114,11 +114,21 @@ export type PendingMessageResubmitData = Pick<
114
114
  runtimeOp: LocalContainerRuntimeMessage;
115
115
  };
116
116
 
117
+ export interface PendingBatchResubmitMetadata extends BatchResubmitInfo {
118
+ /**
119
+ * Whether changes in this batch should be squashed when resubmitting.
120
+ */
121
+ squash: boolean;
122
+ }
123
+
117
124
  export interface IRuntimeStateHandler {
118
125
  connected(): boolean;
119
126
  clientId(): string | undefined;
120
127
  applyStashedOp(serializedOp: string): Promise<unknown>;
121
- reSubmitBatch(batch: PendingMessageResubmitData[], batchId: BatchId, staged: boolean): void;
128
+ reSubmitBatch(
129
+ batch: PendingMessageResubmitData[],
130
+ metadata: PendingBatchResubmitMetadata,
131
+ ): void;
122
132
  isActiveConnection: () => boolean;
123
133
  isAttached: () => boolean;
124
134
  }
@@ -211,6 +221,24 @@ function toSerializableForm(
211
221
  };
212
222
  }
213
223
 
224
+ interface ReplayPendingStateOptions {
225
+ /**
226
+ * If true, only replay staged batches. This is used when we are exiting staging mode and want to rebase and submit the staged batches.
227
+ * Default: false
228
+ */
229
+ onlyStagedBatches: boolean;
230
+ /**
231
+ * @param squash - If true, edits should be squashed when resubmitting.
232
+ * Default: false
233
+ */
234
+ squash: boolean;
235
+ }
236
+
237
+ const defaultReplayPendingStatesOptions: ReplayPendingStateOptions = {
238
+ onlyStagedBatches: false,
239
+ squash: false,
240
+ };
241
+
214
242
  /**
215
243
  * PendingStateManager is responsible for maintaining the messages that have not been sent or have not yet been
216
244
  * acknowledged by the server. It also maintains the batch information for both automatically and manually flushed
@@ -683,11 +711,12 @@ export class PendingStateManager implements IDisposable {
683
711
  * Called when the Container's connection state changes. If the Container gets connected, it replays all the pending
684
712
  * states in its queue. This includes triggering resubmission of unacked ops.
685
713
  * ! Note: successfully resubmitting an op that has been successfully sequenced is not possible due to checks in the ConnectionStateHandler (Loader layer)
686
- * @param onlyStagedBatches - If true, only replay staged batches. This is used when we are exiting staging mode and want to rebase and submit the staged batches.
687
714
  */
688
- public replayPendingStates(onlyStagedBatches?: boolean): void {
715
+ public replayPendingStates(optionsParam?: ReplayPendingStateOptions): void {
716
+ const options = { ...defaultReplayPendingStatesOptions, ...optionsParam };
717
+ const { onlyStagedBatches, squash } = options;
689
718
  assert(
690
- this.stateHandler.connected(),
719
+ this.stateHandler.connected() || onlyStagedBatches === true,
691
720
  0x172 /* "The connection state is not consistent with the runtime" */,
692
721
  );
693
722
 
@@ -741,7 +770,7 @@ export class PendingStateManager implements IDisposable {
741
770
 
742
771
  if (asEmptyBatchLocalOpMetadata(pendingMessage.localOpMetadata)?.emptyBatch === true) {
743
772
  // Resubmit no messages, with the batchId. Will result in another empty batch marker.
744
- this.stateHandler.reSubmitBatch([], batchId, staged);
773
+ this.stateHandler.reSubmitBatch([], { batchId, staged, squash });
745
774
  continue;
746
775
  }
747
776
 
@@ -766,8 +795,7 @@ export class PendingStateManager implements IDisposable {
766
795
  opMetadata: pendingMessage.opMetadata,
767
796
  },
768
797
  ],
769
- batchId,
770
- staged,
798
+ { batchId, staged, squash },
771
799
  );
772
800
  continue;
773
801
  }
@@ -807,7 +835,7 @@ export class PendingStateManager implements IDisposable {
807
835
  );
808
836
  }
809
837
 
810
- this.stateHandler.reSubmitBatch(batch, batchId, staged);
838
+ this.stateHandler.reSubmitBatch(batch, { batchId, staged, squash });
811
839
  }
812
840
 
813
841
  // pending ops should no longer depend on previous sequenced local ops after resubmit
@@ -825,17 +853,6 @@ export class PendingStateManager implements IDisposable {
825
853
  }
826
854
  }
827
855
 
828
- /**
829
- * Clears the 'staged' flag off all pending messages.
830
- */
831
- public clearStagingFlags(): void {
832
- for (const message of this.pendingMessages.toArray()) {
833
- if (message.batchInfo.staged) {
834
- message.batchInfo.staged = false;
835
- }
836
- }
837
- }
838
-
839
856
  /**
840
857
  * Pops all staged batches, invoking the callback on each one in order (LIFO)
841
858
  */