@fluidframework/container-runtime 2.0.0-internal.1.2.0.93071 → 2.0.0-internal.1.2.2

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 (128) hide show
  1. package/dist/batchManager.d.ts +7 -2
  2. package/dist/batchManager.d.ts.map +1 -1
  3. package/dist/batchManager.js +19 -17
  4. package/dist/batchManager.js.map +1 -1
  5. package/dist/batchTracker.d.ts +1 -2
  6. package/dist/batchTracker.d.ts.map +1 -1
  7. package/dist/batchTracker.js +1 -2
  8. package/dist/batchTracker.js.map +1 -1
  9. package/dist/containerRuntime.d.ts +10 -5
  10. package/dist/containerRuntime.d.ts.map +1 -1
  11. package/dist/containerRuntime.js +87 -63
  12. package/dist/containerRuntime.js.map +1 -1
  13. package/dist/dataStoreContext.d.ts +14 -5
  14. package/dist/dataStoreContext.d.ts.map +1 -1
  15. package/dist/dataStoreContext.js +19 -11
  16. package/dist/dataStoreContext.js.map +1 -1
  17. package/dist/dataStores.d.ts +6 -2
  18. package/dist/dataStores.d.ts.map +1 -1
  19. package/dist/dataStores.js +7 -9
  20. package/dist/dataStores.js.map +1 -1
  21. package/dist/deltaScheduler.d.ts +6 -4
  22. package/dist/deltaScheduler.d.ts.map +1 -1
  23. package/dist/deltaScheduler.js +6 -4
  24. package/dist/deltaScheduler.js.map +1 -1
  25. package/dist/garbageCollection.d.ts +41 -12
  26. package/dist/garbageCollection.d.ts.map +1 -1
  27. package/dist/garbageCollection.js +176 -98
  28. package/dist/garbageCollection.js.map +1 -1
  29. package/dist/gcSweepReadyUsageDetection.d.ts +53 -0
  30. package/dist/gcSweepReadyUsageDetection.d.ts.map +1 -0
  31. package/dist/gcSweepReadyUsageDetection.js +135 -0
  32. package/dist/gcSweepReadyUsageDetection.js.map +1 -0
  33. package/dist/orderedClientElection.d.ts +28 -10
  34. package/dist/orderedClientElection.d.ts.map +1 -1
  35. package/dist/orderedClientElection.js +14 -4
  36. package/dist/orderedClientElection.js.map +1 -1
  37. package/dist/packageVersion.d.ts +1 -1
  38. package/dist/packageVersion.d.ts.map +1 -1
  39. package/dist/packageVersion.js +1 -1
  40. package/dist/packageVersion.js.map +1 -1
  41. package/dist/pendingStateManager.d.ts.map +1 -1
  42. package/dist/pendingStateManager.js +4 -2
  43. package/dist/pendingStateManager.js.map +1 -1
  44. package/dist/scheduleManager.d.ts +6 -3
  45. package/dist/scheduleManager.d.ts.map +1 -1
  46. package/dist/scheduleManager.js +21 -13
  47. package/dist/scheduleManager.js.map +1 -1
  48. package/dist/summarizerTypes.d.ts +13 -6
  49. package/dist/summarizerTypes.d.ts.map +1 -1
  50. package/dist/summarizerTypes.js.map +1 -1
  51. package/dist/summaryCollection.d.ts.map +1 -1
  52. package/dist/summaryCollection.js +3 -6
  53. package/dist/summaryCollection.js.map +1 -1
  54. package/dist/summaryManager.d.ts +2 -2
  55. package/dist/summaryManager.js +2 -2
  56. package/dist/summaryManager.js.map +1 -1
  57. package/lib/batchManager.d.ts +7 -2
  58. package/lib/batchManager.d.ts.map +1 -1
  59. package/lib/batchManager.js +19 -17
  60. package/lib/batchManager.js.map +1 -1
  61. package/lib/batchTracker.d.ts +1 -2
  62. package/lib/batchTracker.d.ts.map +1 -1
  63. package/lib/batchTracker.js +1 -2
  64. package/lib/batchTracker.js.map +1 -1
  65. package/lib/containerRuntime.d.ts +10 -5
  66. package/lib/containerRuntime.d.ts.map +1 -1
  67. package/lib/containerRuntime.js +87 -63
  68. package/lib/containerRuntime.js.map +1 -1
  69. package/lib/dataStoreContext.d.ts +14 -5
  70. package/lib/dataStoreContext.d.ts.map +1 -1
  71. package/lib/dataStoreContext.js +20 -12
  72. package/lib/dataStoreContext.js.map +1 -1
  73. package/lib/dataStores.d.ts +6 -2
  74. package/lib/dataStores.d.ts.map +1 -1
  75. package/lib/dataStores.js +7 -9
  76. package/lib/dataStores.js.map +1 -1
  77. package/lib/deltaScheduler.d.ts +6 -4
  78. package/lib/deltaScheduler.d.ts.map +1 -1
  79. package/lib/deltaScheduler.js +6 -4
  80. package/lib/deltaScheduler.js.map +1 -1
  81. package/lib/garbageCollection.d.ts +41 -12
  82. package/lib/garbageCollection.d.ts.map +1 -1
  83. package/lib/garbageCollection.js +175 -97
  84. package/lib/garbageCollection.js.map +1 -1
  85. package/lib/gcSweepReadyUsageDetection.d.ts +53 -0
  86. package/lib/gcSweepReadyUsageDetection.d.ts.map +1 -0
  87. package/lib/gcSweepReadyUsageDetection.js +130 -0
  88. package/lib/gcSweepReadyUsageDetection.js.map +1 -0
  89. package/lib/orderedClientElection.d.ts +28 -10
  90. package/lib/orderedClientElection.d.ts.map +1 -1
  91. package/lib/orderedClientElection.js +14 -4
  92. package/lib/orderedClientElection.js.map +1 -1
  93. package/lib/packageVersion.d.ts +1 -1
  94. package/lib/packageVersion.d.ts.map +1 -1
  95. package/lib/packageVersion.js +1 -1
  96. package/lib/packageVersion.js.map +1 -1
  97. package/lib/pendingStateManager.d.ts.map +1 -1
  98. package/lib/pendingStateManager.js +4 -2
  99. package/lib/pendingStateManager.js.map +1 -1
  100. package/lib/scheduleManager.d.ts +6 -3
  101. package/lib/scheduleManager.d.ts.map +1 -1
  102. package/lib/scheduleManager.js +22 -14
  103. package/lib/scheduleManager.js.map +1 -1
  104. package/lib/summarizerTypes.d.ts +13 -6
  105. package/lib/summarizerTypes.d.ts.map +1 -1
  106. package/lib/summarizerTypes.js.map +1 -1
  107. package/lib/summaryCollection.d.ts.map +1 -1
  108. package/lib/summaryCollection.js +3 -6
  109. package/lib/summaryCollection.js.map +1 -1
  110. package/lib/summaryManager.d.ts +2 -2
  111. package/lib/summaryManager.js +2 -2
  112. package/lib/summaryManager.js.map +1 -1
  113. package/package.json +19 -16
  114. package/src/batchManager.ts +22 -19
  115. package/src/batchTracker.ts +1 -2
  116. package/src/containerRuntime.ts +118 -80
  117. package/src/dataStoreContext.ts +20 -10
  118. package/src/dataStores.ts +7 -8
  119. package/src/deltaScheduler.ts +6 -4
  120. package/src/garbageCollection.ts +224 -134
  121. package/src/gcSweepReadyUsageDetection.ts +147 -0
  122. package/src/orderedClientElection.ts +31 -10
  123. package/src/packageVersion.ts +1 -1
  124. package/src/pendingStateManager.ts +4 -2
  125. package/src/scheduleManager.ts +30 -10
  126. package/src/summarizerTypes.ts +14 -6
  127. package/src/summaryCollection.ts +3 -5
  128. package/src/summaryManager.ts +2 -2
@@ -204,7 +204,7 @@ export interface ContainerRuntimeMessage {
204
204
 
205
205
  export interface ISummaryBaseConfiguration {
206
206
  /**
207
- * Delay before first attempt to spawn summarizing container.
207
+ * Delay before first attempt to spawn summarizing container.
208
208
  */
209
209
  initialSummarizerDelayMs: number;
210
210
 
@@ -540,9 +540,12 @@ export function isRuntimeMessage(message: ISequencedDocumentMessage): boolean {
540
540
 
541
541
  /**
542
542
  * Unpacks runtime messages
543
- * @internal - no promises RE back-compat - this is internal API.
543
+ *
544
+ * @remarks This API makes no promises regarding backward-compatability. This is internal API.
544
545
  * @param message - message (as it observed in storage / service)
545
546
  * @returns unpacked runtime message
547
+ *
548
+ * @internal
546
549
  */
547
550
  export function unpackRuntimeMessage(message: ISequencedDocumentMessage) {
548
551
  if (message.type === MessageType.Operation) {
@@ -850,7 +853,14 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
850
853
  private readonly scheduleManager: ScheduleManager;
851
854
  private readonly blobManager: BlobManager;
852
855
  private readonly pendingStateManager: PendingStateManager;
853
- private readonly batchManager = new BatchManager();
856
+
857
+ // Provide lower soft limit - we want to have some number of ops to get efficiency in compression & bandwidth usage,
858
+ // but at the same time we want to send these ops sooner, to reduce overall latency of processing a batch.
859
+ // So there is some ballance here, that depends on compression algorithm and its efficiency working with smaller
860
+ // payloads. That number represents final (compressed) bits (once compression is implemented).
861
+ private readonly pendingAttachBatch = new BatchManager(64 * 1024);
862
+ private readonly pendingBatch = new BatchManager();
863
+
854
864
  private readonly garbageCollector: IGarbageCollector;
855
865
 
856
866
  // Local copy of incomplete received chunks.
@@ -861,6 +871,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
861
871
  /** The last message processed at the time of the last summary. */
862
872
  private messageAtLastSummary: ISummaryMetadataMessage | undefined;
863
873
 
874
+ private get emptyBatch() {
875
+ return this.pendingBatch.empty && this.pendingAttachBatch.empty;
876
+ }
877
+
864
878
  private get summarizer(): Summarizer {
865
879
  assert(this._summarizer !== undefined, 0x257 /* "This is not summarizing container" */);
866
880
  return this._summarizer;
@@ -896,11 +910,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
896
910
  if (this.runtimeOptions.summaryOptions.summarizerClientElection === true) {
897
911
  return true;
898
912
  }
899
- if (this.summaryConfiguration.state !== "disabled") {
900
- return this.summaryConfiguration.summarizerClientElection === true;
901
- } else {
902
- return false;
903
- }
913
+ return this.summaryConfiguration.state !== "disabled"
914
+ ? this.summaryConfiguration.summarizerClientElection === true
915
+ : false;
904
916
  }
905
917
  private readonly maxOpsSinceLastSummary: number;
906
918
  private getMaxOpsSinceLastSummary(): number {
@@ -909,11 +921,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
909
921
  if (this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary !== undefined) {
910
922
  return this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary;
911
923
  }
912
- if (this.summaryConfiguration.state !== "disabled") {
913
- return this.summaryConfiguration.maxOpsSinceLastSummary;
914
- } else {
915
- return 0;
916
- }
924
+ return this.summaryConfiguration.state !== "disabled"
925
+ ? this.summaryConfiguration.maxOpsSinceLastSummary
926
+ : 0;
917
927
  }
918
928
 
919
929
  private readonly initialSummarizerDelayMs: number;
@@ -923,11 +933,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
923
933
  if (this.runtimeOptions.summaryOptions.initialSummarizerDelayMs !== undefined) {
924
934
  return this.runtimeOptions.summaryOptions.initialSummarizerDelayMs;
925
935
  }
926
- if (this.summaryConfiguration.state !== "disabled") {
927
- return this.summaryConfiguration.initialSummarizerDelayMs;
928
- } else {
929
- return 0;
930
- }
936
+ return this.summaryConfiguration.state !== "disabled"
937
+ ? this.summaryConfiguration.initialSummarizerDelayMs
938
+ : 0;
931
939
  }
932
940
 
933
941
  private readonly createContainerMetadata: ICreateContainerMetadata;
@@ -998,6 +1006,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
998
1006
  getNodePackagePath: async (nodePath: string) => this.getGCNodePackagePath(nodePath),
999
1007
  getLastSummaryTimestampMs: () => this.messageAtLastSummary?.timestamp,
1000
1008
  readAndParseBlob: async <T>(id: string) => readAndParse<T>(this.storage, id),
1009
+ getContainerDiagnosticId: () => this.context.id,
1010
+ activeConnection: () => this.deltaManager.active,
1001
1011
  });
1002
1012
 
1003
1013
  const loadedFromSequenceNumber = this.deltaManager.initialSequenceNumber;
@@ -1072,6 +1082,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1072
1082
  this.scheduleManager = new ScheduleManager(
1073
1083
  context.deltaManager,
1074
1084
  this,
1085
+ () => this.clientId,
1075
1086
  ChildLogger.create(this.logger, "ScheduleManager"),
1076
1087
  );
1077
1088
 
@@ -1320,15 +1331,12 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1320
1331
 
1321
1332
  if (id === BlobManager.basePath && requestParser.isLeaf(2)) {
1322
1333
  const blob = await this.blobManager.getBlob(requestParser.pathParts[1]);
1323
- if (blob) {
1324
- return {
1334
+ return blob
1335
+ ? {
1325
1336
  status: 200,
1326
1337
  mimeType: "fluid/object",
1327
1338
  value: blob,
1328
- };
1329
- } else {
1330
- return create404Response(request);
1331
- }
1339
+ } : create404Response(request);
1332
1340
  } else if (requestParser.pathParts.length > 0) {
1333
1341
  const dataStore = await this.getDataStoreFromRequest(id, request);
1334
1342
  const subRequest = requestParser.createSubRequest(1);
@@ -1575,7 +1583,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1575
1583
  this._perfSignalData.trackingSignalSequenceNumber = undefined;
1576
1584
  } else {
1577
1585
  assert(this.attachState === AttachState.Attached,
1578
- "Connection is possible only if container exists in storage");
1586
+ 0x3cd /* Connection is possible only if container exists in storage */);
1579
1587
  }
1580
1588
 
1581
1589
  // Fail while disconnected
@@ -1603,6 +1611,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1603
1611
  }
1604
1612
 
1605
1613
  this.dataStores.setConnectionState(connected, clientId);
1614
+ this.garbageCollector.setConnectionState(connected, clientId);
1606
1615
 
1607
1616
  raiseConnectedEvent(this.mc.logger, this, connected, clientId);
1608
1617
  }
@@ -1670,7 +1679,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1670
1679
  case ContainerMessageType.Rejoin:
1671
1680
  break;
1672
1681
  default:
1673
- assert(!runtimeMessage, "Runtime message of unknown type");
1682
+ assert(!runtimeMessage, 0x3ce /* Runtime message of unknown type */);
1674
1683
  }
1675
1684
 
1676
1685
  // For back-compat, notify only about runtime messages for now.
@@ -1791,10 +1800,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1791
1800
  assert(this._orderSequentiallyCalls === 0,
1792
1801
  0x24c /* "Cannot call `flush()` from `orderSequentially`'s callback" */);
1793
1802
 
1794
- const batch = this.batchManager.popBatch();
1795
- this.flushBatch(batch);
1803
+ this.flushBatch(this.pendingAttachBatch.popBatch());
1804
+ this.flushBatch(this.pendingBatch.popBatch());
1796
1805
 
1797
- assert(this.batchManager.empty, "reentrancy");
1806
+ assert(this.emptyBatch, 0x3cf /* reentrancy */);
1798
1807
  }
1799
1808
 
1800
1809
  protected flushBatch(batch: BatchMessage[]): void {
@@ -1843,7 +1852,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1843
1852
 
1844
1853
  // Convert from clientSequenceNumber of last message in the batch to clientSequenceNumber of first message.
1845
1854
  clientSequenceNumber -= batch.length - 1;
1846
- assert(clientSequenceNumber >= 0, "clientSequenceNumber can't be negative");
1855
+ assert(clientSequenceNumber >= 0, 0x3d0 /* clientSequenceNumber can't be negative */);
1847
1856
  }
1848
1857
 
1849
1858
  // Let the PendingStateManager know that a message was submitted.
@@ -1888,7 +1897,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
1888
1897
  private trackOrderSequentiallyCalls(callback: () => void): void {
1889
1898
  let checkpoint: { rollback: (action: (message: BatchMessage) => void) => void; } | undefined;
1890
1899
  if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
1891
- checkpoint = this.batchManager.checkpoint();
1900
+ // Note: we are not touching this.pendingAttachBatch here, for two reasons:
1901
+ // 1. It would not help, as we flush attach ops as they become available.
1902
+ // 2. There is no way to undo process of data store creation.
1903
+ checkpoint = this.pendingBatch.checkpoint();
1892
1904
  }
1893
1905
 
1894
1906
  try {
@@ -2270,7 +2282,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2270
2282
 
2271
2283
  /**
2272
2284
  * Runs garbage collection and updates the reference / used state of the nodes in the container.
2273
- * @returns the statistics of the garbage collection run.
2285
+ * @returns the statistics of the garbage collection run; undefined if GC did not run.
2274
2286
  */
2275
2287
  public async collectGarbage(
2276
2288
  options: {
@@ -2281,7 +2293,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2281
2293
  /** True to generate full GC data */
2282
2294
  fullGC?: boolean;
2283
2295
  },
2284
- ): Promise<IGCStats> {
2296
+ ): Promise<IGCStats | undefined> {
2285
2297
  return this.garbageCollector.collectGarbage(options);
2286
2298
  }
2287
2299
 
@@ -2316,7 +2328,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2316
2328
  },
2317
2329
  );
2318
2330
 
2319
- assert(this.batchManager.empty, "Can't trigger summary in the middle of a batch");
2331
+ assert(this.emptyBatch, 0x3d1 /* Can't trigger summary in the middle of a batch */);
2320
2332
 
2321
2333
  let latestSnapshotVersionId: string | undefined;
2322
2334
  if (refreshLatestAck) {
@@ -2407,7 +2419,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2407
2419
  const forcedFullTree = this.garbageCollector.summaryStateNeedsReset;
2408
2420
  try {
2409
2421
  summarizeResult = await this.summarize({
2410
- fullTree: fullTree || forcedFullTree,
2422
+ fullTree: fullTree ?? forcedFullTree,
2411
2423
  trackState: true,
2412
2424
  summaryLogger: summaryNumberLogger,
2413
2425
  runGC: this.garbageCollector.shouldRunGC,
@@ -2575,16 +2587,16 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2575
2587
  }
2576
2588
 
2577
2589
  private hasPendingMessages() {
2578
- return this.pendingStateManager.hasPendingMessages() || !this.batchManager.empty;
2590
+ return this.pendingStateManager.hasPendingMessages() || !this.emptyBatch;
2579
2591
  }
2580
2592
 
2581
2593
  private updateDocumentDirtyState(dirty: boolean) {
2582
2594
  if (this.attachState !== AttachState.Attached) {
2583
- assert(dirty, "Non-attached container is dirty");
2595
+ assert(dirty, 0x3d2 /* Non-attached container is dirty */);
2584
2596
  } else {
2585
2597
  // Other way is not true = see this.isContainerMessageDirtyable()
2586
2598
  assert(!dirty || this.hasPendingMessages(),
2587
- "if doc is dirty, there has to be pending ops");
2599
+ 0x3d3 /* if doc is dirty, there has to be pending ops */);
2588
2600
  }
2589
2601
 
2590
2602
  if (this.dirtyContainer === dirty) {
@@ -2661,29 +2673,57 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2661
2673
  // failure (batch is too large). Pushing them earlier and outside of main batch should alleviate
2662
2674
  // these issues.
2663
2675
  // Cons:
2664
- // With large batches, relay service may throttle clients. Clients may disconnect while throttled.
2676
+ // 1. With large batches, relay service may throttle clients. Clients may disconnect while throttled.
2665
2677
  // This change creates new possibility of a lot of newly created data stores never being referenced
2666
2678
  // because client died before it had a change to submit the rest of the ops. This will create more
2667
2679
  // garbage that needs to be collected leveraging GC (Garbage Collection) feature.
2680
+ // 2. Sending ops out of order means they are excluded from rollback functionality. This is not an issue
2681
+ // today as rollback can't undo creation of data store. To some extent not sending them is a bigger
2682
+ // issue than sending.
2668
2683
  // Please note that this does not change file format, so it can be disabled in the future if this
2669
2684
  // optimization no longer makes sense (for example, batch compression may make it less appealing).
2670
- if (this._flushMode === FlushMode.TurnBased && type === ContainerMessageType.Attach &&
2671
- this.mc.config.getBoolean("Fluid.ContainerRuntime.disableAttachOpReorder") !== true) {
2672
- this.flushBatch([message]);
2685
+ if (type === ContainerMessageType.Attach &&
2686
+ this.mc.config.getBoolean("Fluid.ContainerRuntime.disableAttachOpReorder") !== true) {
2687
+ if (!this.pendingAttachBatch.push(message)) {
2688
+ // BatchManager has two limits - soft limit & hard limit. Soft limit is only engaged
2689
+ // when queue is not empty.
2690
+ // Flush queue & retry. Failure on retry would mean - single message is bigger than hard limit
2691
+ this.flushBatch(this.pendingAttachBatch.popBatch());
2692
+ if (!this.pendingAttachBatch.push(message)) {
2693
+ throw new GenericError(
2694
+ "BatchTooLarge",
2695
+ /* error */ undefined,
2696
+ {
2697
+ opSize: message.contents.length,
2698
+ count: this.pendingAttachBatch.length,
2699
+ limit: this.pendingAttachBatch.limit,
2700
+ });
2701
+ }
2702
+ }
2673
2703
  } else {
2674
- this.batchManager.push(message);
2675
- if (this._flushMode !== FlushMode.TurnBased) {
2676
- this.flush();
2677
- } else if (!this.flushTrigger) {
2678
- this.flushTrigger = true;
2679
- // Queue a microtask to detect the end of the turn and force a flush.
2680
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
2681
- Promise.resolve().then(() => {
2682
- this.flushTrigger = false;
2683
- this.flush();
2684
- });
2704
+ if (!this.pendingBatch.push(message)) {
2705
+ throw new GenericError(
2706
+ "BatchTooLarge",
2707
+ /* error */ undefined,
2708
+ {
2709
+ opSize: message.contents.length,
2710
+ count: this.pendingBatch.length,
2711
+ limit: this.pendingBatch.limit,
2712
+ });
2685
2713
  }
2686
2714
  }
2715
+
2716
+ if (this._flushMode !== FlushMode.TurnBased) {
2717
+ this.flush();
2718
+ } else if (!this.flushTrigger) {
2719
+ this.flushTrigger = true;
2720
+ // Queue a microtask to detect the end of the turn and force a flush.
2721
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
2722
+ Promise.resolve().then(() => {
2723
+ this.flushTrigger = false;
2724
+ this.flush();
2725
+ });
2726
+ }
2687
2727
  } catch (error) {
2688
2728
  this.closeFn(error as GenericError);
2689
2729
  throw error;
@@ -2699,17 +2739,15 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2699
2739
  assert(this.connected, 0x133 /* "Container disconnected when trying to submit system message" */);
2700
2740
 
2701
2741
  // System message should not be sent in the middle of the batch.
2702
- assert(this.batchManager.empty, "System op in the middle of a batch");
2742
+ assert(this.emptyBatch, 0x3d4 /* System op in the middle of a batch */);
2703
2743
 
2704
2744
  // back-compat: ADO #1385: Make this call unconditional in the future
2705
- if (this.context.submitSummaryFn !== undefined) {
2706
- return this.context.submitSummaryFn(contents);
2707
- } else {
2708
- return this.context.submitFn(
2745
+ return this.context.submitSummaryFn !== undefined
2746
+ ? this.context.submitSummaryFn(contents)
2747
+ : this.context.submitFn(
2709
2748
  MessageType.Summarize,
2710
2749
  contents,
2711
- false); // batch
2712
- }
2750
+ false);
2713
2751
  }
2714
2752
 
2715
2753
  /**
@@ -2785,25 +2823,25 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2785
2823
  // It should only be done by the summarizerNode, if required.
2786
2824
  const snapshotTreeFetcher = async () => {
2787
2825
  const fetchResult = await this.fetchSnapshotFromStorage(
2788
- ackHandle,
2789
- summaryLogger,
2790
- {
2791
- eventName: "RefreshLatestSummaryGetSnapshot",
2792
- ackHandle,
2793
- summaryRefSeq,
2794
- fetchLatest: false,
2795
- });
2796
- return fetchResult.snapshotTree;
2797
- };
2798
- const result = await this.summarizerNode.refreshLatestSummary(
2799
- proposalHandle,
2800
- summaryRefSeq,
2801
- snapshotTreeFetcher,
2802
- readAndParseBlob,
2803
- summaryLogger,
2804
- );
2805
-
2806
- // Notify the garbage collector so it can update its latest summary state.
2826
+ ackHandle,
2827
+ summaryLogger,
2828
+ {
2829
+ eventName: "RefreshLatestSummaryGetSnapshot",
2830
+ ackHandle,
2831
+ summaryRefSeq,
2832
+ fetchLatest: false,
2833
+ });
2834
+ return fetchResult.snapshotTree;
2835
+ };
2836
+ const result = await this.summarizerNode.refreshLatestSummary(
2837
+ proposalHandle,
2838
+ summaryRefSeq,
2839
+ snapshotTreeFetcher,
2840
+ readAndParseBlob,
2841
+ summaryLogger,
2842
+ );
2843
+
2844
+ // Notify the garbage collector so it can update its latest summary state.
2807
2845
  await this.garbageCollector.latestSummaryStateRefreshed(result, readAndParseBlob);
2808
2846
  }
2809
2847
 
@@ -2987,7 +3025,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
2987
3025
 
2988
3026
  // If it's not the case, we should take it into account when calculating dirty state.
2989
3027
  assert(this.context.attachState === AttachState.Attached,
2990
- "this function is called for attached containers only");
3028
+ 0x3d5 /* this function is called for attached containers only */);
2991
3029
  if (!this.hasPendingMessages()) {
2992
3030
  this.updateDocumentDirtyState(false);
2993
3031
  }
@@ -37,6 +37,7 @@ import {
37
37
  IContainerRuntime,
38
38
  } from "@fluidframework/container-runtime-definitions";
39
39
  import {
40
+ BindState,
40
41
  channelsTreeName,
41
42
  CreateChildSummarizerNodeFn,
42
43
  CreateChildSummarizerNodeParam,
@@ -224,9 +225,8 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
224
225
  protected registry: IFluidDataStoreRegistry | undefined;
225
226
 
226
227
  protected detachedRuntimeCreation = false;
227
- // back-compat (for tests) - can be removed in 2.0.0-alpha.2.0.0, or earlier if compat tests drop n/n-2 coverage
228
- // @ts-expect-error - This shouldn't be referenced in the current version, but needs to be here for back-compat
229
- private readonly bindToContext: () => void;
228
+ /** @deprecated - To be replaced by calling makeLocallyVisible directly */
229
+ public readonly bindToContext: () => void;
230
230
  protected channel: IFluidDataStoreChannel | undefined;
231
231
  private loaded = false;
232
232
  protected pending: ISequencedDocumentMessage[] | undefined = [];
@@ -253,6 +253,7 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
253
253
  constructor(
254
254
  props: IFluidDataStoreContextProps,
255
255
  private readonly existing: boolean,
256
+ private bindState: BindState, // Used to assert for state tracking purposes
256
257
  public readonly isLocalDataStore: boolean,
257
258
  private readonly makeLocallyVisibleFn: () => void,
258
259
  ) {
@@ -273,8 +274,11 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
273
274
  this.containerRuntime.attachState : AttachState.Detached;
274
275
 
275
276
  this.bindToContext = () => {
277
+ assert(this.bindState === BindState.NotBound, 0x13b /* "datastore context is already in bound state" */);
278
+ this.bindState = BindState.Binding;
276
279
  assert(this.channel !== undefined, 0x13c /* "undefined channel on datastore context" */);
277
280
  this.makeLocallyVisible();
281
+ this.bindState = BindState.Bound;
278
282
  };
279
283
 
280
284
  const thisSummarizeInternal =
@@ -515,12 +519,18 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
515
519
 
516
520
  /**
517
521
  * After GC has run, called to notify the data store of routes used in it. These are used for the following:
522
+ *
518
523
  * 1. To identify if this data store is being referenced in the document or not.
524
+ *
519
525
  * 2. To determine if it needs to re-summarize in case used routes changed since last summary.
526
+ *
520
527
  * 3. These are added to the summary generated by the data store.
521
- * 4. To notify child contexts of their used routes. This is done immediately if the data store is loaded. Else,
522
- * it is done when realizing the data store.
528
+ *
529
+ * 4. To notify child contexts of their used routes. This is done immediately if the data store is loaded.
530
+ * Else, it is done when realizing the data store.
531
+ *
523
532
  * 5. To update the timestamp when this data store or any children are marked as unreferenced.
533
+ *
524
534
  * @param usedRoutes - The routes that are used in this data store.
525
535
  * @param gcTimestamp - The time when GC was run that generated these used routes. If any node becomes unreferenced
526
536
  * as part of this GC run, this should be used to update the time when it happens.
@@ -776,6 +786,7 @@ export class RemoteFluidDataStoreContext extends FluidDataStoreContext {
776
786
  super(
777
787
  props,
778
788
  true /* existing */,
789
+ BindState.Bound,
779
790
  false /* isLocalDataStore */,
780
791
  () => {
781
792
  throw new Error("Already attached");
@@ -814,11 +825,9 @@ export class RemoteFluidDataStoreContext extends FluidDataStoreContext {
814
825
  // For snapshotFormatVersion = "0.1" (1) or above, pkg is jsonified, otherwise it is just a string.
815
826
  const formatVersion = getAttributesFormatVersion(attributes);
816
827
  if (formatVersion < 1) {
817
- if (attributes.pkg.startsWith("[\"") && attributes.pkg.endsWith("\"]")) {
818
- pkgFromSnapshot = JSON.parse(attributes.pkg) as string[];
819
- } else {
820
- pkgFromSnapshot = [attributes.pkg];
821
- }
828
+ pkgFromSnapshot = attributes.pkg.startsWith("[\"") && attributes.pkg.endsWith("\"]")
829
+ ? JSON.parse(attributes.pkg) as string[]
830
+ : [attributes.pkg];
822
831
  } else {
823
832
  pkgFromSnapshot = JSON.parse(attributes.pkg) as string[];
824
833
  }
@@ -880,6 +889,7 @@ export class LocalFluidDataStoreContextBase extends FluidDataStoreContext {
880
889
  super(
881
890
  props,
882
891
  props.snapshotTree !== undefined ? true : false /* existing */,
892
+ props.snapshotTree ? BindState.Bound : BindState.NotBound,
883
893
  true /* isLocalDataStore */,
884
894
  props.makeLocallyVisibleFn,
885
895
  );
package/src/dataStores.ts CHANGED
@@ -469,12 +469,7 @@ export class DataStores implements IDisposable {
469
469
  }
470
470
 
471
471
  public setAttachState(attachState: AttachState.Attaching | AttachState.Attached): void {
472
- let eventName: "attaching" | "attached";
473
- if (attachState === AttachState.Attaching) {
474
- eventName = "attaching";
475
- } else {
476
- eventName = "attached";
477
- }
472
+ const eventName = attachState === AttachState.Attaching ? "attaching" : "attached";
478
473
  for (const [, context] of this.contexts) {
479
474
  // Fire only for bounded stores.
480
475
  if (!this.contexts.isNotBound(context.id)) {
@@ -566,11 +561,15 @@ export class DataStores implements IDisposable {
566
561
 
567
562
  /**
568
563
  * Generates data used for garbage collection. It does the following:
564
+ *
569
565
  * 1. Calls into each child data store context to get its GC data.
566
+ *
570
567
  * 2. Prefixes the child context's id to the GC nodes in the child's GC data. This makes sure that the node can be
571
- * identified as belonging to the child.
568
+ * identified as belonging to the child.
569
+ *
572
570
  * 3. Adds a GC node for this channel to the nodes received from the children. All these nodes together represent
573
- * the GC data of this channel.
571
+ * the GC data of this channel.
572
+ *
574
573
  * @param fullGC - true to bypass optimizations and force full generation of GC data.
575
574
  */
576
575
  public async getGCData(fullGC: boolean = false): Promise<IGarbageCollectionData> {
@@ -18,12 +18,14 @@ import {
18
18
  * DeltaScheduler is responsible for the scheduling of inbound delta queue in cases where there
19
19
  * is more than one op a particular run of the queue. It does not schedule if there is just one
20
20
  * op or just one batch in the run. It does the following two things:
21
+ *
21
22
  * 1. If the ops have been processed for more than a specific amount of time, it pauses the queue
22
- * and calls setTimeout to schedule a resume of the queue. This ensures that we don't block
23
- * the JS thread for a long time processing ops synchronously (for example, when catching up
24
- * ops right after boot or catching up ops / delayed realizing data stores by summarizer).
23
+ * and calls setTimeout to schedule a resume of the queue. This ensures that we don't block
24
+ * the JS thread for a long time processing ops synchronously (for example, when catching up
25
+ * ops right after boot or catching up ops / delayed realizing data stores by summarizer).
26
+ *
25
27
  * 2. If we scheduled a particular run of the queue, it logs telemetry for the number of ops
26
- * processed, the time and number of turns it took to process the ops.
28
+ * processed, the time and number of turns it took to process the ops.
27
29
  */
28
30
  export class DeltaScheduler {
29
31
  private readonly deltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>;