@fluidframework/container-runtime 2.70.0-361248 → 2.70.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 (69) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/container-runtime.test-files.tar +0 -0
  3. package/dist/blobManager/blobManager.d.ts.map +1 -1
  4. package/dist/blobManager/blobManager.js +0 -1
  5. package/dist/blobManager/blobManager.js.map +1 -1
  6. package/dist/containerRuntime.d.ts +25 -6
  7. package/dist/containerRuntime.d.ts.map +1 -1
  8. package/dist/containerRuntime.js +92 -22
  9. package/dist/containerRuntime.js.map +1 -1
  10. package/dist/dataStore.d.ts +1 -1
  11. package/dist/dataStore.d.ts.map +1 -1
  12. package/dist/dataStore.js +7 -8
  13. package/dist/dataStore.js.map +1 -1
  14. package/dist/dataStoreContext.d.ts.map +1 -1
  15. package/dist/dataStoreContext.js +1 -1
  16. package/dist/dataStoreContext.js.map +1 -1
  17. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  18. package/dist/opLifecycle/outbox.js +7 -0
  19. package/dist/opLifecycle/outbox.js.map +1 -1
  20. package/dist/packageVersion.d.ts +1 -1
  21. package/dist/packageVersion.d.ts.map +1 -1
  22. package/dist/packageVersion.js +1 -1
  23. package/dist/packageVersion.js.map +1 -1
  24. package/dist/runtimeLayerCompatState.d.ts +4 -3
  25. package/dist/runtimeLayerCompatState.d.ts.map +1 -1
  26. package/dist/runtimeLayerCompatState.js +4 -35
  27. package/dist/runtimeLayerCompatState.js.map +1 -1
  28. package/dist/storageServiceWithAttachBlobs.d.ts +0 -36
  29. package/dist/storageServiceWithAttachBlobs.d.ts.map +1 -1
  30. package/dist/storageServiceWithAttachBlobs.js +0 -55
  31. package/dist/storageServiceWithAttachBlobs.js.map +1 -1
  32. package/lib/blobManager/blobManager.d.ts.map +1 -1
  33. package/lib/blobManager/blobManager.js +0 -1
  34. package/lib/blobManager/blobManager.js.map +1 -1
  35. package/lib/containerRuntime.d.ts +25 -6
  36. package/lib/containerRuntime.d.ts.map +1 -1
  37. package/lib/containerRuntime.js +92 -22
  38. package/lib/containerRuntime.js.map +1 -1
  39. package/lib/dataStore.d.ts +1 -1
  40. package/lib/dataStore.d.ts.map +1 -1
  41. package/lib/dataStore.js +2 -3
  42. package/lib/dataStore.js.map +1 -1
  43. package/lib/dataStoreContext.d.ts.map +1 -1
  44. package/lib/dataStoreContext.js +1 -1
  45. package/lib/dataStoreContext.js.map +1 -1
  46. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  47. package/lib/opLifecycle/outbox.js +7 -0
  48. package/lib/opLifecycle/outbox.js.map +1 -1
  49. package/lib/packageVersion.d.ts +1 -1
  50. package/lib/packageVersion.d.ts.map +1 -1
  51. package/lib/packageVersion.js +1 -1
  52. package/lib/packageVersion.js.map +1 -1
  53. package/lib/runtimeLayerCompatState.d.ts +4 -3
  54. package/lib/runtimeLayerCompatState.d.ts.map +1 -1
  55. package/lib/runtimeLayerCompatState.js +5 -36
  56. package/lib/runtimeLayerCompatState.js.map +1 -1
  57. package/lib/storageServiceWithAttachBlobs.d.ts +0 -36
  58. package/lib/storageServiceWithAttachBlobs.d.ts.map +1 -1
  59. package/lib/storageServiceWithAttachBlobs.js +0 -55
  60. package/lib/storageServiceWithAttachBlobs.js.map +1 -1
  61. package/package.json +20 -17
  62. package/src/blobManager/blobManager.ts +0 -1
  63. package/src/containerRuntime.ts +144 -37
  64. package/src/dataStore.ts +6 -9
  65. package/src/dataStoreContext.ts +1 -0
  66. package/src/opLifecycle/outbox.ts +10 -0
  67. package/src/packageVersion.ts +1 -1
  68. package/src/runtimeLayerCompatState.ts +23 -39
  69. package/src/storageServiceWithAttachBlobs.ts +0 -92
@@ -460,6 +460,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
460
460
  };
461
461
  const runtime = new containerRuntimeCtor(context, registry, metadata, electedSummarizerData, chunks ?? [], aliases ?? [], internalRuntimeOptions, containerScope, logger, existing, blobManagerLoadInfo, context.storage, createIdCompressorFn, documentSchemaController, featureGatesForTelemetry, provideEntryPoint, (0, internal_7.semanticVersionToMinimumVersionForCollab)(updatedMinVersionForCollab), requestHandler, undefined, // summaryConfiguration
462
462
  recentBatchInfo);
463
+ runtime.sharePendingBlobs();
463
464
  // Initialize the base state of the runtime before it's returned.
464
465
  await runtime.initializeBaseState(context.loader);
465
466
  // Apply stashed ops with a reference sequence number equal to the sequence number of the snapshot,
@@ -627,9 +628,8 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
627
628
  * Enter Staging Mode, such that ops submitted to the ContainerRuntime will not be sent to the ordering service.
628
629
  * To exit Staging Mode, call either discardChanges or commitChanges on the Stage Controls returned from this method.
629
630
  *
630
- * @returns StageControlsExperimental - Controls for exiting Staging Mode.
631
+ * @returns Controls for exiting Staging Mode.
631
632
  */
632
- // eslint-disable-next-line import/no-deprecated
633
633
  this.enterStagingMode = () => {
634
634
  if (this.stageControls !== undefined) {
635
635
  throw new internal_8.UsageError("Already in staging mode");
@@ -658,7 +658,6 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
658
658
  throw normalizedError;
659
659
  }
660
660
  };
661
- // eslint-disable-next-line import/no-deprecated
662
661
  const stageControls = {
663
662
  discardChanges: () => exitStagingMode(() => {
664
663
  // Pop all staged batches from the PSM and roll them back in LIFO order
@@ -684,6 +683,39 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
684
683
  return this.stageControls;
685
684
  };
686
685
  this.readAndParseBlob = async (id) => (0, internal_4.readAndParse)(this.storage, id);
686
+ /**
687
+ * ContainerRuntime knows about additional restrictions on when blob sharing can be resumed as compared
688
+ * to BlobManager. In particular, it wants to avoid sharing blobs while in readonly state, and it also
689
+ * wants to avoid sharing blobs before connection completes (otherwise it may cause the sharing to happen
690
+ * before processing shared ops).
691
+ *
692
+ * This method can be called safely before those conditions are met. In the background, it will wait until
693
+ * it is safe before initiating sharing. It will close the container on any error.
694
+ */
695
+ this.sharePendingBlobs = () => {
696
+ new Promise((resolve) => {
697
+ // eslint-disable-next-line unicorn/consistent-function-scoping
698
+ const canStartSharing = () => this.connected && this.deltaManager.readOnlyInfo.readonly !== true;
699
+ if (canStartSharing()) {
700
+ resolve();
701
+ return;
702
+ }
703
+ const checkCanShare = (readonly) => {
704
+ if (canStartSharing()) {
705
+ this.deltaManager.off("readonly", checkCanShare);
706
+ this.off("connected", checkCanShare);
707
+ resolve();
708
+ }
709
+ };
710
+ this.deltaManager.on("readonly", checkCanShare);
711
+ this.on("connected", checkCanShare);
712
+ })
713
+ .then(this.blobManager.sharePendingBlobs)
714
+ // It may not be necessary to close the container on failures - this should just mean there's
715
+ // a handle in the container that is stuck pending, which is a scenario that customers need to
716
+ // handle anyway. Starting with this more aggressive/restrictive behavior to be cautious.
717
+ .catch(this.closeFn);
718
+ };
687
719
  // While internal, ContainerRuntime has not been converted to use the new events support.
688
720
  // Recreate the required events (new pattern) with injected, wrapper new emitter.
689
721
  // It is lazily create to avoid listeners (old events) that ultimately go nowhere.
@@ -694,7 +726,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
694
726
  eventEmitter.emit("joined", { clientId, canWrite });
695
727
  });
696
728
  this.on("disconnectedFromService", () => eventEmitter.emit("disconnected"));
697
- this.on("connectionTypeChanged", (canWrite) => eventEmitter.emit("connectionTypeChanged", canWrite));
729
+ this.on("operabilityChanged", (canWrite) => eventEmitter.emit("operabilityChanged", canWrite));
698
730
  }
699
731
  else {
700
732
  this.on("connected", (clientId) => {
@@ -704,14 +736,11 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
704
736
  }
705
737
  return eventEmitter;
706
738
  });
707
- const { options, clientDetails, connected, baseSnapshot, submitFn, submitBatchFn, submitSummaryFn, submitSignalFn, disposeFn, closeFn, deltaManager, quorum, audience, pendingLocalState, supportedFeatures, snapshotWithContents, getConnectionState, } = context;
739
+ const { options, clientDetails, connected, baseSnapshot, submitFn, submitBatchFn, submitSummaryFn, submitSignalFn, disposeFn, closeFn, deltaManager, quorum, audience, signalAudience, pendingLocalState, supportedFeatures, snapshotWithContents, getConnectionState, } = context;
708
740
  this.getConnectionState = getConnectionState;
709
741
  // In old loaders without dispose functionality, closeFn is equivalent but will also switch container to readonly mode
710
742
  this.disposeFn = disposeFn ?? closeFn;
711
743
  this.isSnapshotInstanceOfISnapshot = snapshotWithContents !== undefined;
712
- // Validate that the Loader is compatible with this Runtime.
713
- const maybeLoaderCompatDetailsForRuntime = context;
714
- (0, runtimeLayerCompatState_js_1.validateLoaderCompatibility)(maybeLoaderCompatDetailsForRuntime.ILayerCompatDetails, this.disposeFn);
715
744
  this.mc = (0, internal_8.createChildMonitoringContext)({
716
745
  logger: this.baseLogger,
717
746
  namespace: "ContainerRuntime",
@@ -721,6 +750,9 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
721
750
  },
722
751
  },
723
752
  });
753
+ // Validate that the Loader is compatible with this Runtime.
754
+ const maybeLoaderCompatDetailsForRuntime = context;
755
+ (0, runtimeLayerCompatState_js_1.validateLoaderCompatibility)(maybeLoaderCompatDetailsForRuntime.ILayerCompatDetails, this.disposeFn, this.mc.logger);
724
756
  // If we support multiple algorithms in the future, then we would need to manage it here carefully.
725
757
  // We can use runtimeOptions.compressionOptions.compressionAlgorithm, but only if it's in the schema list!
726
758
  // If it's not in the list, then we will need to either use no compression, or fallback to some other (supported by format)
@@ -820,10 +852,11 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
820
852
  this.nextSummaryNumber = loadSummaryNumber + 1;
821
853
  this.messageAtLastSummary = lastMessageFromMetadata(metadata);
822
854
  // Note that we only need to pull the *initial* connected state from the context.
823
- // Later updates come through calls to setConnectionState.
855
+ // Later updates come through calls to setConnectionState/Status.
824
856
  this.canSendOps = connected;
825
857
  this.canSendSignals = this.getConnectionState
826
- ? this.getConnectionState() === internal_1.ConnectionState.Connected
858
+ ? this.getConnectionState() === internal_1.ConnectionState.Connected ||
859
+ this.getConnectionState() === internal_1.ConnectionState.CatchingUp
827
860
  : undefined;
828
861
  this.mc.logger.sendTelemetryEvent({
829
862
  eventName: "GCFeatureMatrix",
@@ -1047,6 +1080,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
1047
1080
  oldClientId = clientId;
1048
1081
  });
1049
1082
  }
1083
+ this.signalAudience = signalAudience;
1050
1084
  const closeSummarizerDelayOverride = this.mc.config.getNumber("Fluid.ContainerRuntime.Test.CloseSummarizerDelayOverrideMs");
1051
1085
  this.closeSummarizerDelayMs =
1052
1086
  closeSummarizerDelayOverride ?? defaultCloseSummarizerDelayMs;
@@ -1628,6 +1662,38 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
1628
1662
  }
1629
1663
  }
1630
1664
  setConnectionState(canSendOps, clientId) {
1665
+ this.setConnectionStateToConnectedOrDisconnected(canSendOps, clientId);
1666
+ }
1667
+ setConnectionStatus(status) {
1668
+ switch (status.connectionState) {
1669
+ case internal_1.ConnectionState.Connected: {
1670
+ this.setConnectionStateToConnectedOrDisconnected(status.canSendOps, status.clientConnectionId);
1671
+ break;
1672
+ }
1673
+ case internal_1.ConnectionState.Disconnected: {
1674
+ this.setConnectionStateToConnectedOrDisconnected(status.canSendOps, status.priorConnectedClientConnectionId);
1675
+ break;
1676
+ }
1677
+ case internal_1.ConnectionState.CatchingUp: {
1678
+ (0, internal_2.assert)(this.getConnectionState !== undefined &&
1679
+ this.getConnectionState() === internal_1.ConnectionState.CatchingUp, 0xc8d /* connection state mismatch between getConnectionState and setConnectionStatus notification */);
1680
+ // Note: Historically when only `setConnectionState` of `IRuntime`
1681
+ // was supported, it was possible to be in `CatchingUp` state and
1682
+ // call through to `setConnectionStateCore` when there is a readonly
1683
+ // change - see `Container`'s `"deltaManager.on("readonly"`. There
1684
+ // would not be a transition of `canSendOps` in that case, but
1685
+ // `channelCollection` and `garbageCollector` would receive early
1686
+ // `setConnectionState` call AND `this` would `emit` "disconnected"
1687
+ // event.
1688
+ this.emitServiceConnectionEvents(
1689
+ /* canSendOpsChanged */ this.canSendOps,
1690
+ /* canSendOps */ false, status.pendingClientConnectionId);
1691
+ break;
1692
+ }
1693
+ // No default
1694
+ }
1695
+ }
1696
+ setConnectionStateToConnectedOrDisconnected(canSendOps, clientId) {
1631
1697
  // Validate we have consistent state
1632
1698
  const currentClientId = this._audience.getSelf()?.clientId;
1633
1699
  (0, internal_2.assert)(clientId === currentClientId, 0x977 /* input clientId does not match Audience */);
@@ -1686,30 +1752,33 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
1686
1752
  * Emits service connection events based on connection state changes.
1687
1753
  *
1688
1754
  * @remarks
1689
- * "connectedToService" is emitted when container connection state transitions to 'Connected' regardless of connection mode.
1755
+ * "connectedToService" is emitted when container connection state transitions to 'CatchingUp' or 'Connected' regardless of connection mode.
1690
1756
  * "disconnectedFromService" excludes false "disconnected" events that happen when readonly client transitions to 'Connected'.
1691
1757
  */
1692
1758
  emitServiceConnectionEvents(canSendOpsChanged, canSendOps, clientId) {
1693
1759
  if (!this.getConnectionState) {
1694
1760
  return;
1695
1761
  }
1696
- const canSendSignals = this.getConnectionState() === internal_1.ConnectionState.Connected;
1762
+ const connectionState = this.getConnectionState();
1763
+ const canSendSignals = connectionState === internal_1.ConnectionState.Connected ||
1764
+ connectionState === internal_1.ConnectionState.CatchingUp;
1697
1765
  const canSendSignalsChanged = this.canSendSignals !== canSendSignals;
1698
1766
  this.canSendSignals = canSendSignals;
1699
1767
  if (canSendSignalsChanged) {
1700
- // If canSendSignals changed, we either transitioned from Connected to Disconnected or CatchingUp to Connected
1768
+ // If canSendSignals changed, we either transitioned from CatchingUp or
1769
+ // Connected to Disconnected or EstablishingConnection to CatchingUp.
1701
1770
  if (canSendSignals) {
1702
- // Emit for CatchingUp to Connected transition
1771
+ // Emit for EstablishingConnection to CatchingUp or Connected transition
1703
1772
  this.emit("connectedToService", clientId, canSendOps);
1704
1773
  }
1705
1774
  else {
1706
- // Emit for Connected to Disconnected transition
1775
+ // Emit for CatchingUp or Connected to Disconnected transition
1707
1776
  this.emit("disconnectedFromService");
1708
1777
  }
1709
1778
  }
1710
1779
  else if (canSendOpsChanged) {
1711
- // If canSendSignals did not change but canSendOps did, then connection type has changed.
1712
- this.emit("connectionTypeChanged", canSendOps);
1780
+ // If canSendSignals did not change but canSendOps did, then operations possible has changed.
1781
+ this.emit("operabilityChanged", canSendOps);
1713
1782
  }
1714
1783
  }
1715
1784
  async notifyOpReplay(message) {
@@ -2100,7 +2169,6 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
2100
2169
  */
2101
2170
  orderSequentially(callback) {
2102
2171
  let checkpoint;
2103
- // eslint-disable-next-line import/no-deprecated
2104
2172
  let stageControls;
2105
2173
  if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback") === true) {
2106
2174
  if (!this.batchRunner.running && !this.inStagingMode) {
@@ -2841,7 +2909,6 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
2841
2909
  // TODO: better typing
2842
2910
  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
2843
2911
  contents, localOpMetadata = undefined) {
2844
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
2845
2912
  this.submit({ type, contents }, localOpMetadata);
2846
2913
  }
2847
2914
  async uploadBlob(blob, signal) {
@@ -3310,7 +3377,9 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
3310
3377
  const getConnectionState = this.getConnectionState;
3311
3378
  if (getConnectionState) {
3312
3379
  const connectionState = getConnectionState();
3313
- if (connectionState === internal_1.ConnectionState.Connected) {
3380
+ if (connectionState === internal_1.ConnectionState.Connected ||
3381
+ connectionState === internal_1.ConnectionState.CatchingUp) {
3382
+ // Note: when CatchingUp, canSendOps will always be false.
3314
3383
  return this.canSendOps ? "joinedForWriting" : "joinedForReading";
3315
3384
  }
3316
3385
  }
@@ -3322,16 +3391,17 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
3322
3391
  acquireExtension(id, factory, ...useContext) {
3323
3392
  let entry = this.extensions.get(id);
3324
3393
  if (entry === undefined) {
3394
+ const audience = this.signalAudience;
3325
3395
  const runtime = {
3326
3396
  getJoinedStatus: this.getJoinedStatus.bind(this),
3327
- getClientId: () => this.clientId,
3397
+ getClientId: audience ? () => audience.getSelf()?.clientId : () => this.clientId,
3328
3398
  events: this.lazyEventsForExtensions.value,
3329
3399
  logger: this.baseLogger,
3330
3400
  submitAddressedSignal: (addressChain, message) => {
3331
3401
  this.submitExtensionSignal(id, addressChain, message);
3332
3402
  },
3333
3403
  getQuorum: this.getQuorum.bind(this),
3334
- getAudience: this.getAudience.bind(this),
3404
+ getAudience: audience ? () => audience : this.getAudience.bind(this),
3335
3405
  supportedFeatures: this.ILayerCompatDetails.supportedFeatures,
3336
3406
  };
3337
3407
  entry = new factory(runtime, ...useContext);