@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.
- package/CHANGELOG.md +10 -0
- package/container-runtime.test-files.tar +0 -0
- package/dist/blobManager/blobManager.d.ts.map +1 -1
- package/dist/blobManager/blobManager.js +0 -1
- package/dist/blobManager/blobManager.js.map +1 -1
- package/dist/containerRuntime.d.ts +25 -6
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +92 -22
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.d.ts +1 -1
- package/dist/dataStore.d.ts.map +1 -1
- package/dist/dataStore.js +7 -8
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +1 -1
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +7 -0
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/runtimeLayerCompatState.d.ts +4 -3
- package/dist/runtimeLayerCompatState.d.ts.map +1 -1
- package/dist/runtimeLayerCompatState.js +4 -35
- package/dist/runtimeLayerCompatState.js.map +1 -1
- package/dist/storageServiceWithAttachBlobs.d.ts +0 -36
- package/dist/storageServiceWithAttachBlobs.d.ts.map +1 -1
- package/dist/storageServiceWithAttachBlobs.js +0 -55
- package/dist/storageServiceWithAttachBlobs.js.map +1 -1
- package/lib/blobManager/blobManager.d.ts.map +1 -1
- package/lib/blobManager/blobManager.js +0 -1
- package/lib/blobManager/blobManager.js.map +1 -1
- package/lib/containerRuntime.d.ts +25 -6
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +92 -22
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.d.ts +1 -1
- package/lib/dataStore.d.ts.map +1 -1
- package/lib/dataStore.js +2 -3
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +1 -1
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +7 -0
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/runtimeLayerCompatState.d.ts +4 -3
- package/lib/runtimeLayerCompatState.d.ts.map +1 -1
- package/lib/runtimeLayerCompatState.js +5 -36
- package/lib/runtimeLayerCompatState.js.map +1 -1
- package/lib/storageServiceWithAttachBlobs.d.ts +0 -36
- package/lib/storageServiceWithAttachBlobs.d.ts.map +1 -1
- package/lib/storageServiceWithAttachBlobs.js +0 -55
- package/lib/storageServiceWithAttachBlobs.js.map +1 -1
- package/package.json +20 -17
- package/src/blobManager/blobManager.ts +0 -1
- package/src/containerRuntime.ts +144 -37
- package/src/dataStore.ts +6 -9
- package/src/dataStoreContext.ts +1 -0
- package/src/opLifecycle/outbox.ts +10 -0
- package/src/packageVersion.ts +1 -1
- package/src/runtimeLayerCompatState.ts +23 -39
- package/src/storageServiceWithAttachBlobs.ts +0 -92
package/lib/containerRuntime.js
CHANGED
|
@@ -454,6 +454,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
454
454
|
};
|
|
455
455
|
const runtime = new containerRuntimeCtor(context, registry, metadata, electedSummarizerData, chunks ?? [], aliases ?? [], internalRuntimeOptions, containerScope, logger, existing, blobManagerLoadInfo, context.storage, createIdCompressorFn, documentSchemaController, featureGatesForTelemetry, provideEntryPoint, semanticVersionToMinimumVersionForCollab(updatedMinVersionForCollab), requestHandler, undefined, // summaryConfiguration
|
|
456
456
|
recentBatchInfo);
|
|
457
|
+
runtime.sharePendingBlobs();
|
|
457
458
|
// Initialize the base state of the runtime before it's returned.
|
|
458
459
|
await runtime.initializeBaseState(context.loader);
|
|
459
460
|
// Apply stashed ops with a reference sequence number equal to the sequence number of the snapshot,
|
|
@@ -621,9 +622,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
621
622
|
* Enter Staging Mode, such that ops submitted to the ContainerRuntime will not be sent to the ordering service.
|
|
622
623
|
* To exit Staging Mode, call either discardChanges or commitChanges on the Stage Controls returned from this method.
|
|
623
624
|
*
|
|
624
|
-
* @returns
|
|
625
|
+
* @returns Controls for exiting Staging Mode.
|
|
625
626
|
*/
|
|
626
|
-
// eslint-disable-next-line import/no-deprecated
|
|
627
627
|
this.enterStagingMode = () => {
|
|
628
628
|
if (this.stageControls !== undefined) {
|
|
629
629
|
throw new UsageError("Already in staging mode");
|
|
@@ -652,7 +652,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
652
652
|
throw normalizedError;
|
|
653
653
|
}
|
|
654
654
|
};
|
|
655
|
-
// eslint-disable-next-line import/no-deprecated
|
|
656
655
|
const stageControls = {
|
|
657
656
|
discardChanges: () => exitStagingMode(() => {
|
|
658
657
|
// Pop all staged batches from the PSM and roll them back in LIFO order
|
|
@@ -678,6 +677,39 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
678
677
|
return this.stageControls;
|
|
679
678
|
};
|
|
680
679
|
this.readAndParseBlob = async (id) => readAndParse(this.storage, id);
|
|
680
|
+
/**
|
|
681
|
+
* ContainerRuntime knows about additional restrictions on when blob sharing can be resumed as compared
|
|
682
|
+
* to BlobManager. In particular, it wants to avoid sharing blobs while in readonly state, and it also
|
|
683
|
+
* wants to avoid sharing blobs before connection completes (otherwise it may cause the sharing to happen
|
|
684
|
+
* before processing shared ops).
|
|
685
|
+
*
|
|
686
|
+
* This method can be called safely before those conditions are met. In the background, it will wait until
|
|
687
|
+
* it is safe before initiating sharing. It will close the container on any error.
|
|
688
|
+
*/
|
|
689
|
+
this.sharePendingBlobs = () => {
|
|
690
|
+
new Promise((resolve) => {
|
|
691
|
+
// eslint-disable-next-line unicorn/consistent-function-scoping
|
|
692
|
+
const canStartSharing = () => this.connected && this.deltaManager.readOnlyInfo.readonly !== true;
|
|
693
|
+
if (canStartSharing()) {
|
|
694
|
+
resolve();
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
const checkCanShare = (readonly) => {
|
|
698
|
+
if (canStartSharing()) {
|
|
699
|
+
this.deltaManager.off("readonly", checkCanShare);
|
|
700
|
+
this.off("connected", checkCanShare);
|
|
701
|
+
resolve();
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
this.deltaManager.on("readonly", checkCanShare);
|
|
705
|
+
this.on("connected", checkCanShare);
|
|
706
|
+
})
|
|
707
|
+
.then(this.blobManager.sharePendingBlobs)
|
|
708
|
+
// It may not be necessary to close the container on failures - this should just mean there's
|
|
709
|
+
// a handle in the container that is stuck pending, which is a scenario that customers need to
|
|
710
|
+
// handle anyway. Starting with this more aggressive/restrictive behavior to be cautious.
|
|
711
|
+
.catch(this.closeFn);
|
|
712
|
+
};
|
|
681
713
|
// While internal, ContainerRuntime has not been converted to use the new events support.
|
|
682
714
|
// Recreate the required events (new pattern) with injected, wrapper new emitter.
|
|
683
715
|
// It is lazily create to avoid listeners (old events) that ultimately go nowhere.
|
|
@@ -688,7 +720,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
688
720
|
eventEmitter.emit("joined", { clientId, canWrite });
|
|
689
721
|
});
|
|
690
722
|
this.on("disconnectedFromService", () => eventEmitter.emit("disconnected"));
|
|
691
|
-
this.on("
|
|
723
|
+
this.on("operabilityChanged", (canWrite) => eventEmitter.emit("operabilityChanged", canWrite));
|
|
692
724
|
}
|
|
693
725
|
else {
|
|
694
726
|
this.on("connected", (clientId) => {
|
|
@@ -698,14 +730,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
698
730
|
}
|
|
699
731
|
return eventEmitter;
|
|
700
732
|
});
|
|
701
|
-
const { options, clientDetails, connected, baseSnapshot, submitFn, submitBatchFn, submitSummaryFn, submitSignalFn, disposeFn, closeFn, deltaManager, quorum, audience, pendingLocalState, supportedFeatures, snapshotWithContents, getConnectionState, } = context;
|
|
733
|
+
const { options, clientDetails, connected, baseSnapshot, submitFn, submitBatchFn, submitSummaryFn, submitSignalFn, disposeFn, closeFn, deltaManager, quorum, audience, signalAudience, pendingLocalState, supportedFeatures, snapshotWithContents, getConnectionState, } = context;
|
|
702
734
|
this.getConnectionState = getConnectionState;
|
|
703
735
|
// In old loaders without dispose functionality, closeFn is equivalent but will also switch container to readonly mode
|
|
704
736
|
this.disposeFn = disposeFn ?? closeFn;
|
|
705
737
|
this.isSnapshotInstanceOfISnapshot = snapshotWithContents !== undefined;
|
|
706
|
-
// Validate that the Loader is compatible with this Runtime.
|
|
707
|
-
const maybeLoaderCompatDetailsForRuntime = context;
|
|
708
|
-
validateLoaderCompatibility(maybeLoaderCompatDetailsForRuntime.ILayerCompatDetails, this.disposeFn);
|
|
709
738
|
this.mc = createChildMonitoringContext({
|
|
710
739
|
logger: this.baseLogger,
|
|
711
740
|
namespace: "ContainerRuntime",
|
|
@@ -715,6 +744,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
715
744
|
},
|
|
716
745
|
},
|
|
717
746
|
});
|
|
747
|
+
// Validate that the Loader is compatible with this Runtime.
|
|
748
|
+
const maybeLoaderCompatDetailsForRuntime = context;
|
|
749
|
+
validateLoaderCompatibility(maybeLoaderCompatDetailsForRuntime.ILayerCompatDetails, this.disposeFn, this.mc.logger);
|
|
718
750
|
// If we support multiple algorithms in the future, then we would need to manage it here carefully.
|
|
719
751
|
// We can use runtimeOptions.compressionOptions.compressionAlgorithm, but only if it's in the schema list!
|
|
720
752
|
// If it's not in the list, then we will need to either use no compression, or fallback to some other (supported by format)
|
|
@@ -814,10 +846,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
814
846
|
this.nextSummaryNumber = loadSummaryNumber + 1;
|
|
815
847
|
this.messageAtLastSummary = lastMessageFromMetadata(metadata);
|
|
816
848
|
// Note that we only need to pull the *initial* connected state from the context.
|
|
817
|
-
// Later updates come through calls to setConnectionState.
|
|
849
|
+
// Later updates come through calls to setConnectionState/Status.
|
|
818
850
|
this.canSendOps = connected;
|
|
819
851
|
this.canSendSignals = this.getConnectionState
|
|
820
|
-
? this.getConnectionState() === ConnectionState.Connected
|
|
852
|
+
? this.getConnectionState() === ConnectionState.Connected ||
|
|
853
|
+
this.getConnectionState() === ConnectionState.CatchingUp
|
|
821
854
|
: undefined;
|
|
822
855
|
this.mc.logger.sendTelemetryEvent({
|
|
823
856
|
eventName: "GCFeatureMatrix",
|
|
@@ -1041,6 +1074,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1041
1074
|
oldClientId = clientId;
|
|
1042
1075
|
});
|
|
1043
1076
|
}
|
|
1077
|
+
this.signalAudience = signalAudience;
|
|
1044
1078
|
const closeSummarizerDelayOverride = this.mc.config.getNumber("Fluid.ContainerRuntime.Test.CloseSummarizerDelayOverrideMs");
|
|
1045
1079
|
this.closeSummarizerDelayMs =
|
|
1046
1080
|
closeSummarizerDelayOverride ?? defaultCloseSummarizerDelayMs;
|
|
@@ -1622,6 +1656,38 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1622
1656
|
}
|
|
1623
1657
|
}
|
|
1624
1658
|
setConnectionState(canSendOps, clientId) {
|
|
1659
|
+
this.setConnectionStateToConnectedOrDisconnected(canSendOps, clientId);
|
|
1660
|
+
}
|
|
1661
|
+
setConnectionStatus(status) {
|
|
1662
|
+
switch (status.connectionState) {
|
|
1663
|
+
case ConnectionState.Connected: {
|
|
1664
|
+
this.setConnectionStateToConnectedOrDisconnected(status.canSendOps, status.clientConnectionId);
|
|
1665
|
+
break;
|
|
1666
|
+
}
|
|
1667
|
+
case ConnectionState.Disconnected: {
|
|
1668
|
+
this.setConnectionStateToConnectedOrDisconnected(status.canSendOps, status.priorConnectedClientConnectionId);
|
|
1669
|
+
break;
|
|
1670
|
+
}
|
|
1671
|
+
case ConnectionState.CatchingUp: {
|
|
1672
|
+
assert(this.getConnectionState !== undefined &&
|
|
1673
|
+
this.getConnectionState() === ConnectionState.CatchingUp, 0xc8d /* connection state mismatch between getConnectionState and setConnectionStatus notification */);
|
|
1674
|
+
// Note: Historically when only `setConnectionState` of `IRuntime`
|
|
1675
|
+
// was supported, it was possible to be in `CatchingUp` state and
|
|
1676
|
+
// call through to `setConnectionStateCore` when there is a readonly
|
|
1677
|
+
// change - see `Container`'s `"deltaManager.on("readonly"`. There
|
|
1678
|
+
// would not be a transition of `canSendOps` in that case, but
|
|
1679
|
+
// `channelCollection` and `garbageCollector` would receive early
|
|
1680
|
+
// `setConnectionState` call AND `this` would `emit` "disconnected"
|
|
1681
|
+
// event.
|
|
1682
|
+
this.emitServiceConnectionEvents(
|
|
1683
|
+
/* canSendOpsChanged */ this.canSendOps,
|
|
1684
|
+
/* canSendOps */ false, status.pendingClientConnectionId);
|
|
1685
|
+
break;
|
|
1686
|
+
}
|
|
1687
|
+
// No default
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
setConnectionStateToConnectedOrDisconnected(canSendOps, clientId) {
|
|
1625
1691
|
// Validate we have consistent state
|
|
1626
1692
|
const currentClientId = this._audience.getSelf()?.clientId;
|
|
1627
1693
|
assert(clientId === currentClientId, 0x977 /* input clientId does not match Audience */);
|
|
@@ -1680,30 +1746,33 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1680
1746
|
* Emits service connection events based on connection state changes.
|
|
1681
1747
|
*
|
|
1682
1748
|
* @remarks
|
|
1683
|
-
* "connectedToService" is emitted when container connection state transitions to 'Connected' regardless of connection mode.
|
|
1749
|
+
* "connectedToService" is emitted when container connection state transitions to 'CatchingUp' or 'Connected' regardless of connection mode.
|
|
1684
1750
|
* "disconnectedFromService" excludes false "disconnected" events that happen when readonly client transitions to 'Connected'.
|
|
1685
1751
|
*/
|
|
1686
1752
|
emitServiceConnectionEvents(canSendOpsChanged, canSendOps, clientId) {
|
|
1687
1753
|
if (!this.getConnectionState) {
|
|
1688
1754
|
return;
|
|
1689
1755
|
}
|
|
1690
|
-
const
|
|
1756
|
+
const connectionState = this.getConnectionState();
|
|
1757
|
+
const canSendSignals = connectionState === ConnectionState.Connected ||
|
|
1758
|
+
connectionState === ConnectionState.CatchingUp;
|
|
1691
1759
|
const canSendSignalsChanged = this.canSendSignals !== canSendSignals;
|
|
1692
1760
|
this.canSendSignals = canSendSignals;
|
|
1693
1761
|
if (canSendSignalsChanged) {
|
|
1694
|
-
// If canSendSignals changed, we either transitioned from
|
|
1762
|
+
// If canSendSignals changed, we either transitioned from CatchingUp or
|
|
1763
|
+
// Connected to Disconnected or EstablishingConnection to CatchingUp.
|
|
1695
1764
|
if (canSendSignals) {
|
|
1696
|
-
// Emit for
|
|
1765
|
+
// Emit for EstablishingConnection to CatchingUp or Connected transition
|
|
1697
1766
|
this.emit("connectedToService", clientId, canSendOps);
|
|
1698
1767
|
}
|
|
1699
1768
|
else {
|
|
1700
|
-
// Emit for Connected to Disconnected transition
|
|
1769
|
+
// Emit for CatchingUp or Connected to Disconnected transition
|
|
1701
1770
|
this.emit("disconnectedFromService");
|
|
1702
1771
|
}
|
|
1703
1772
|
}
|
|
1704
1773
|
else if (canSendOpsChanged) {
|
|
1705
|
-
// If canSendSignals did not change but canSendOps did, then
|
|
1706
|
-
this.emit("
|
|
1774
|
+
// If canSendSignals did not change but canSendOps did, then operations possible has changed.
|
|
1775
|
+
this.emit("operabilityChanged", canSendOps);
|
|
1707
1776
|
}
|
|
1708
1777
|
}
|
|
1709
1778
|
async notifyOpReplay(message) {
|
|
@@ -2094,7 +2163,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2094
2163
|
*/
|
|
2095
2164
|
orderSequentially(callback) {
|
|
2096
2165
|
let checkpoint;
|
|
2097
|
-
// eslint-disable-next-line import/no-deprecated
|
|
2098
2166
|
let stageControls;
|
|
2099
2167
|
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback") === true) {
|
|
2100
2168
|
if (!this.batchRunner.running && !this.inStagingMode) {
|
|
@@ -2835,7 +2903,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2835
2903
|
// TODO: better typing
|
|
2836
2904
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
|
|
2837
2905
|
contents, localOpMetadata = undefined) {
|
|
2838
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
2839
2906
|
this.submit({ type, contents }, localOpMetadata);
|
|
2840
2907
|
}
|
|
2841
2908
|
async uploadBlob(blob, signal) {
|
|
@@ -3304,7 +3371,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
3304
3371
|
const getConnectionState = this.getConnectionState;
|
|
3305
3372
|
if (getConnectionState) {
|
|
3306
3373
|
const connectionState = getConnectionState();
|
|
3307
|
-
if (connectionState === ConnectionState.Connected
|
|
3374
|
+
if (connectionState === ConnectionState.Connected ||
|
|
3375
|
+
connectionState === ConnectionState.CatchingUp) {
|
|
3376
|
+
// Note: when CatchingUp, canSendOps will always be false.
|
|
3308
3377
|
return this.canSendOps ? "joinedForWriting" : "joinedForReading";
|
|
3309
3378
|
}
|
|
3310
3379
|
}
|
|
@@ -3316,16 +3385,17 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
3316
3385
|
acquireExtension(id, factory, ...useContext) {
|
|
3317
3386
|
let entry = this.extensions.get(id);
|
|
3318
3387
|
if (entry === undefined) {
|
|
3388
|
+
const audience = this.signalAudience;
|
|
3319
3389
|
const runtime = {
|
|
3320
3390
|
getJoinedStatus: this.getJoinedStatus.bind(this),
|
|
3321
|
-
getClientId: () => this.clientId,
|
|
3391
|
+
getClientId: audience ? () => audience.getSelf()?.clientId : () => this.clientId,
|
|
3322
3392
|
events: this.lazyEventsForExtensions.value,
|
|
3323
3393
|
logger: this.baseLogger,
|
|
3324
3394
|
submitAddressedSignal: (addressChain, message) => {
|
|
3325
3395
|
this.submitExtensionSignal(id, addressChain, message);
|
|
3326
3396
|
},
|
|
3327
3397
|
getQuorum: this.getQuorum.bind(this),
|
|
3328
|
-
getAudience: this.getAudience.bind(this),
|
|
3398
|
+
getAudience: audience ? () => audience : this.getAudience.bind(this),
|
|
3329
3399
|
supportedFeatures: this.ILayerCompatDetails.supportedFeatures,
|
|
3330
3400
|
};
|
|
3331
3401
|
entry = new factory(runtime, ...useContext);
|