@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/src/containerRuntime.ts
CHANGED
|
@@ -23,6 +23,7 @@ import type {
|
|
|
23
23
|
IDeltaManagerFull,
|
|
24
24
|
ILoader,
|
|
25
25
|
IContainerStorageService,
|
|
26
|
+
ConnectionStatus,
|
|
26
27
|
} from "@fluidframework/container-definitions/internal";
|
|
27
28
|
import {
|
|
28
29
|
ConnectionState,
|
|
@@ -120,10 +121,8 @@ import type {
|
|
|
120
121
|
IInboundSignalMessage,
|
|
121
122
|
IRuntimeMessagesContent,
|
|
122
123
|
ISummarizerNodeWithGC,
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
// eslint-disable-next-line import/no-deprecated
|
|
126
|
-
IContainerRuntimeBaseExperimental,
|
|
124
|
+
StageControlsInternal,
|
|
125
|
+
IContainerRuntimeBaseInternal,
|
|
127
126
|
IFluidParentContext,
|
|
128
127
|
MinimumVersionForCollab,
|
|
129
128
|
} from "@fluidframework/runtime-definitions/internal";
|
|
@@ -817,8 +816,7 @@ export class ContainerRuntime
|
|
|
817
816
|
extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
818
817
|
implements
|
|
819
818
|
IContainerRuntimeInternal,
|
|
820
|
-
|
|
821
|
-
IContainerRuntimeBaseExperimental,
|
|
819
|
+
IContainerRuntimeBaseInternal,
|
|
822
820
|
// eslint-disable-next-line import/no-deprecated
|
|
823
821
|
IContainerRuntimeWithResolveHandle_Deprecated,
|
|
824
822
|
IRuntime,
|
|
@@ -1215,6 +1213,8 @@ export class ContainerRuntime
|
|
|
1215
1213
|
recentBatchInfo,
|
|
1216
1214
|
);
|
|
1217
1215
|
|
|
1216
|
+
runtime.sharePendingBlobs();
|
|
1217
|
+
|
|
1218
1218
|
// Initialize the base state of the runtime before it's returned.
|
|
1219
1219
|
await runtime.initializeBaseState(context.loader);
|
|
1220
1220
|
|
|
@@ -1568,6 +1568,7 @@ export class ContainerRuntime
|
|
|
1568
1568
|
deltaManager,
|
|
1569
1569
|
quorum,
|
|
1570
1570
|
audience,
|
|
1571
|
+
signalAudience,
|
|
1571
1572
|
pendingLocalState,
|
|
1572
1573
|
supportedFeatures,
|
|
1573
1574
|
snapshotWithContents,
|
|
@@ -1581,13 +1582,6 @@ export class ContainerRuntime
|
|
|
1581
1582
|
|
|
1582
1583
|
this.isSnapshotInstanceOfISnapshot = snapshotWithContents !== undefined;
|
|
1583
1584
|
|
|
1584
|
-
// Validate that the Loader is compatible with this Runtime.
|
|
1585
|
-
const maybeLoaderCompatDetailsForRuntime = context as FluidObject<ILayerCompatDetails>;
|
|
1586
|
-
validateLoaderCompatibility(
|
|
1587
|
-
maybeLoaderCompatDetailsForRuntime.ILayerCompatDetails,
|
|
1588
|
-
this.disposeFn,
|
|
1589
|
-
);
|
|
1590
|
-
|
|
1591
1585
|
this.mc = createChildMonitoringContext({
|
|
1592
1586
|
logger: this.baseLogger,
|
|
1593
1587
|
namespace: "ContainerRuntime",
|
|
@@ -1598,6 +1592,14 @@ export class ContainerRuntime
|
|
|
1598
1592
|
},
|
|
1599
1593
|
});
|
|
1600
1594
|
|
|
1595
|
+
// Validate that the Loader is compatible with this Runtime.
|
|
1596
|
+
const maybeLoaderCompatDetailsForRuntime = context as FluidObject<ILayerCompatDetails>;
|
|
1597
|
+
validateLoaderCompatibility(
|
|
1598
|
+
maybeLoaderCompatDetailsForRuntime.ILayerCompatDetails,
|
|
1599
|
+
this.disposeFn,
|
|
1600
|
+
this.mc.logger,
|
|
1601
|
+
);
|
|
1602
|
+
|
|
1601
1603
|
// If we support multiple algorithms in the future, then we would need to manage it here carefully.
|
|
1602
1604
|
// We can use runtimeOptions.compressionOptions.compressionAlgorithm, but only if it's in the schema list!
|
|
1603
1605
|
// If it's not in the list, then we will need to either use no compression, or fallback to some other (supported by format)
|
|
@@ -1718,10 +1720,11 @@ export class ContainerRuntime
|
|
|
1718
1720
|
this.messageAtLastSummary = lastMessageFromMetadata(metadata);
|
|
1719
1721
|
|
|
1720
1722
|
// Note that we only need to pull the *initial* connected state from the context.
|
|
1721
|
-
// Later updates come through calls to setConnectionState.
|
|
1723
|
+
// Later updates come through calls to setConnectionState/Status.
|
|
1722
1724
|
this.canSendOps = connected;
|
|
1723
1725
|
this.canSendSignals = this.getConnectionState
|
|
1724
|
-
? this.getConnectionState() === ConnectionState.Connected
|
|
1726
|
+
? this.getConnectionState() === ConnectionState.Connected ||
|
|
1727
|
+
this.getConnectionState() === ConnectionState.CatchingUp
|
|
1725
1728
|
: undefined;
|
|
1726
1729
|
|
|
1727
1730
|
this.mc.logger.sendTelemetryEvent({
|
|
@@ -2037,6 +2040,8 @@ export class ContainerRuntime
|
|
|
2037
2040
|
});
|
|
2038
2041
|
}
|
|
2039
2042
|
|
|
2043
|
+
this.signalAudience = signalAudience;
|
|
2044
|
+
|
|
2040
2045
|
const closeSummarizerDelayOverride = this.mc.config.getNumber(
|
|
2041
2046
|
"Fluid.ContainerRuntime.Test.CloseSummarizerDelayOverrideMs",
|
|
2042
2047
|
);
|
|
@@ -2809,6 +2814,59 @@ export class ContainerRuntime
|
|
|
2809
2814
|
this.channelCollection.notifyReadOnlyState(readonly);
|
|
2810
2815
|
|
|
2811
2816
|
public setConnectionState(canSendOps: boolean, clientId?: string): void {
|
|
2817
|
+
this.setConnectionStateToConnectedOrDisconnected(canSendOps, clientId);
|
|
2818
|
+
}
|
|
2819
|
+
|
|
2820
|
+
public setConnectionStatus(status: ConnectionStatus): void {
|
|
2821
|
+
switch (status.connectionState) {
|
|
2822
|
+
case ConnectionState.Connected: {
|
|
2823
|
+
this.setConnectionStateToConnectedOrDisconnected(
|
|
2824
|
+
status.canSendOps,
|
|
2825
|
+
status.clientConnectionId,
|
|
2826
|
+
);
|
|
2827
|
+
|
|
2828
|
+
break;
|
|
2829
|
+
}
|
|
2830
|
+
case ConnectionState.Disconnected: {
|
|
2831
|
+
this.setConnectionStateToConnectedOrDisconnected(
|
|
2832
|
+
status.canSendOps,
|
|
2833
|
+
status.priorConnectedClientConnectionId,
|
|
2834
|
+
);
|
|
2835
|
+
|
|
2836
|
+
break;
|
|
2837
|
+
}
|
|
2838
|
+
case ConnectionState.CatchingUp: {
|
|
2839
|
+
assert(
|
|
2840
|
+
this.getConnectionState !== undefined &&
|
|
2841
|
+
this.getConnectionState() === ConnectionState.CatchingUp,
|
|
2842
|
+
0xc8d /* connection state mismatch between getConnectionState and setConnectionStatus notification */,
|
|
2843
|
+
);
|
|
2844
|
+
|
|
2845
|
+
// Note: Historically when only `setConnectionState` of `IRuntime`
|
|
2846
|
+
// was supported, it was possible to be in `CatchingUp` state and
|
|
2847
|
+
// call through to `setConnectionStateCore` when there is a readonly
|
|
2848
|
+
// change - see `Container`'s `"deltaManager.on("readonly"`. There
|
|
2849
|
+
// would not be a transition of `canSendOps` in that case, but
|
|
2850
|
+
// `channelCollection` and `garbageCollector` would receive early
|
|
2851
|
+
// `setConnectionState` call AND `this` would `emit` "disconnected"
|
|
2852
|
+
// event.
|
|
2853
|
+
|
|
2854
|
+
this.emitServiceConnectionEvents(
|
|
2855
|
+
/* canSendOpsChanged */ this.canSendOps,
|
|
2856
|
+
/* canSendOps */ false,
|
|
2857
|
+
status.pendingClientConnectionId,
|
|
2858
|
+
);
|
|
2859
|
+
|
|
2860
|
+
break;
|
|
2861
|
+
}
|
|
2862
|
+
// No default
|
|
2863
|
+
}
|
|
2864
|
+
}
|
|
2865
|
+
|
|
2866
|
+
private setConnectionStateToConnectedOrDisconnected(
|
|
2867
|
+
canSendOps: boolean,
|
|
2868
|
+
clientId: string | undefined,
|
|
2869
|
+
): void {
|
|
2812
2870
|
// Validate we have consistent state
|
|
2813
2871
|
const currentClientId = this._audience.getSelf()?.clientId;
|
|
2814
2872
|
assert(clientId === currentClientId, 0x977 /* input clientId does not match Audience */);
|
|
@@ -2893,7 +2951,7 @@ export class ContainerRuntime
|
|
|
2893
2951
|
* Emits service connection events based on connection state changes.
|
|
2894
2952
|
*
|
|
2895
2953
|
* @remarks
|
|
2896
|
-
* "connectedToService" is emitted when container connection state transitions to 'Connected' regardless of connection mode.
|
|
2954
|
+
* "connectedToService" is emitted when container connection state transitions to 'CatchingUp' or 'Connected' regardless of connection mode.
|
|
2897
2955
|
* "disconnectedFromService" excludes false "disconnected" events that happen when readonly client transitions to 'Connected'.
|
|
2898
2956
|
*/
|
|
2899
2957
|
private emitServiceConnectionEvents(
|
|
@@ -2905,21 +2963,25 @@ export class ContainerRuntime
|
|
|
2905
2963
|
return;
|
|
2906
2964
|
}
|
|
2907
2965
|
|
|
2908
|
-
const
|
|
2966
|
+
const connectionState = this.getConnectionState();
|
|
2967
|
+
const canSendSignals =
|
|
2968
|
+
connectionState === ConnectionState.Connected ||
|
|
2969
|
+
connectionState === ConnectionState.CatchingUp;
|
|
2909
2970
|
const canSendSignalsChanged = this.canSendSignals !== canSendSignals;
|
|
2910
2971
|
this.canSendSignals = canSendSignals;
|
|
2911
2972
|
if (canSendSignalsChanged) {
|
|
2912
|
-
// If canSendSignals changed, we either transitioned from
|
|
2973
|
+
// If canSendSignals changed, we either transitioned from CatchingUp or
|
|
2974
|
+
// Connected to Disconnected or EstablishingConnection to CatchingUp.
|
|
2913
2975
|
if (canSendSignals) {
|
|
2914
|
-
// Emit for
|
|
2976
|
+
// Emit for EstablishingConnection to CatchingUp or Connected transition
|
|
2915
2977
|
this.emit("connectedToService", clientId, canSendOps);
|
|
2916
2978
|
} else {
|
|
2917
|
-
// Emit for Connected to Disconnected transition
|
|
2979
|
+
// Emit for CatchingUp or Connected to Disconnected transition
|
|
2918
2980
|
this.emit("disconnectedFromService");
|
|
2919
2981
|
}
|
|
2920
2982
|
} else if (canSendOpsChanged) {
|
|
2921
|
-
// If canSendSignals did not change but canSendOps did, then
|
|
2922
|
-
this.emit("
|
|
2983
|
+
// If canSendSignals did not change but canSendOps did, then operations possible has changed.
|
|
2984
|
+
this.emit("operabilityChanged", canSendOps);
|
|
2923
2985
|
}
|
|
2924
2986
|
}
|
|
2925
2987
|
|
|
@@ -3452,8 +3514,7 @@ export class ContainerRuntime
|
|
|
3452
3514
|
*/
|
|
3453
3515
|
public orderSequentially<T>(callback: () => T): T {
|
|
3454
3516
|
let checkpoint: IBatchCheckpoint | undefined;
|
|
3455
|
-
|
|
3456
|
-
let stageControls: StageControlsExperimental | undefined;
|
|
3517
|
+
let stageControls: StageControlsInternal | undefined;
|
|
3457
3518
|
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback") === true) {
|
|
3458
3519
|
if (!this.batchRunner.running && !this.inStagingMode) {
|
|
3459
3520
|
stageControls = this.enterStagingMode();
|
|
@@ -3517,8 +3578,7 @@ export class ContainerRuntime
|
|
|
3517
3578
|
return result;
|
|
3518
3579
|
}
|
|
3519
3580
|
|
|
3520
|
-
|
|
3521
|
-
private stageControls: StageControlsExperimental | undefined;
|
|
3581
|
+
private stageControls: StageControlsInternal | undefined;
|
|
3522
3582
|
|
|
3523
3583
|
/**
|
|
3524
3584
|
* If true, the ContainerRuntime is not submitting any new ops to the ordering service.
|
|
@@ -3533,10 +3593,9 @@ export class ContainerRuntime
|
|
|
3533
3593
|
* Enter Staging Mode, such that ops submitted to the ContainerRuntime will not be sent to the ordering service.
|
|
3534
3594
|
* To exit Staging Mode, call either discardChanges or commitChanges on the Stage Controls returned from this method.
|
|
3535
3595
|
*
|
|
3536
|
-
* @returns
|
|
3596
|
+
* @returns Controls for exiting Staging Mode.
|
|
3537
3597
|
*/
|
|
3538
|
-
|
|
3539
|
-
public enterStagingMode = (): StageControlsExperimental => {
|
|
3598
|
+
public enterStagingMode = (): StageControlsInternal => {
|
|
3540
3599
|
if (this.stageControls !== undefined) {
|
|
3541
3600
|
throw new UsageError("Already in staging mode");
|
|
3542
3601
|
}
|
|
@@ -3569,8 +3628,7 @@ export class ContainerRuntime
|
|
|
3569
3628
|
}
|
|
3570
3629
|
};
|
|
3571
3630
|
|
|
3572
|
-
|
|
3573
|
-
const stageControls: StageControlsExperimental = {
|
|
3631
|
+
const stageControls: StageControlsInternal = {
|
|
3574
3632
|
discardChanges: () =>
|
|
3575
3633
|
exitStagingMode(() => {
|
|
3576
3634
|
// Pop all staged batches from the PSM and roll them back in LIFO order
|
|
@@ -3677,6 +3735,14 @@ export class ContainerRuntime
|
|
|
3677
3735
|
return this._audience;
|
|
3678
3736
|
}
|
|
3679
3737
|
|
|
3738
|
+
/**
|
|
3739
|
+
* When defined, this {@link @fluidframework/container-definitions#IAudience}
|
|
3740
|
+
* maintains member list using signals only.
|
|
3741
|
+
* Thus "write" members may be known earlier than quorum and avoid noise from
|
|
3742
|
+
* un-summarized quorum history.
|
|
3743
|
+
*/
|
|
3744
|
+
private readonly signalAudience?: IAudience;
|
|
3745
|
+
|
|
3680
3746
|
/**
|
|
3681
3747
|
* Returns true of container is dirty, i.e. there are some pending local changes that
|
|
3682
3748
|
* either were not sent out to delta stream or were not yet acknowledged.
|
|
@@ -4548,7 +4614,6 @@ export class ContainerRuntime
|
|
|
4548
4614
|
contents: any,
|
|
4549
4615
|
localOpMetadata: unknown = undefined,
|
|
4550
4616
|
): void {
|
|
4551
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
4552
4617
|
this.submit({ type, contents }, localOpMetadata);
|
|
4553
4618
|
}
|
|
4554
4619
|
|
|
@@ -5133,6 +5198,43 @@ export class ContainerRuntime
|
|
|
5133
5198
|
);
|
|
5134
5199
|
}
|
|
5135
5200
|
|
|
5201
|
+
/**
|
|
5202
|
+
* ContainerRuntime knows about additional restrictions on when blob sharing can be resumed as compared
|
|
5203
|
+
* to BlobManager. In particular, it wants to avoid sharing blobs while in readonly state, and it also
|
|
5204
|
+
* wants to avoid sharing blobs before connection completes (otherwise it may cause the sharing to happen
|
|
5205
|
+
* before processing shared ops).
|
|
5206
|
+
*
|
|
5207
|
+
* This method can be called safely before those conditions are met. In the background, it will wait until
|
|
5208
|
+
* it is safe before initiating sharing. It will close the container on any error.
|
|
5209
|
+
*/
|
|
5210
|
+
public sharePendingBlobs = (): void => {
|
|
5211
|
+
new Promise<void>((resolve) => {
|
|
5212
|
+
// eslint-disable-next-line unicorn/consistent-function-scoping
|
|
5213
|
+
const canStartSharing = (): boolean =>
|
|
5214
|
+
this.connected && this.deltaManager.readOnlyInfo.readonly !== true;
|
|
5215
|
+
|
|
5216
|
+
if (canStartSharing()) {
|
|
5217
|
+
resolve();
|
|
5218
|
+
return;
|
|
5219
|
+
}
|
|
5220
|
+
|
|
5221
|
+
const checkCanShare = (readonly: boolean): void => {
|
|
5222
|
+
if (canStartSharing()) {
|
|
5223
|
+
this.deltaManager.off("readonly", checkCanShare);
|
|
5224
|
+
this.off("connected", checkCanShare);
|
|
5225
|
+
resolve();
|
|
5226
|
+
}
|
|
5227
|
+
};
|
|
5228
|
+
this.deltaManager.on("readonly", checkCanShare);
|
|
5229
|
+
this.on("connected", checkCanShare);
|
|
5230
|
+
})
|
|
5231
|
+
.then(this.blobManager.sharePendingBlobs)
|
|
5232
|
+
// It may not be necessary to close the container on failures - this should just mean there's
|
|
5233
|
+
// a handle in the container that is stuck pending, which is a scenario that customers need to
|
|
5234
|
+
// handle anyway. Starting with this more aggressive/restrictive behavior to be cautious.
|
|
5235
|
+
.catch(this.closeFn);
|
|
5236
|
+
};
|
|
5237
|
+
|
|
5136
5238
|
public summarizeOnDemand(options: IOnDemandSummarizeOptions): ISummarizeResults {
|
|
5137
5239
|
if (this._summarizer !== undefined) {
|
|
5138
5240
|
return this._summarizer.summarizeOnDemand(options);
|
|
@@ -5169,8 +5271,8 @@ export class ContainerRuntime
|
|
|
5169
5271
|
eventEmitter.emit("joined", { clientId, canWrite });
|
|
5170
5272
|
});
|
|
5171
5273
|
this.on("disconnectedFromService", () => eventEmitter.emit("disconnected"));
|
|
5172
|
-
this.on("
|
|
5173
|
-
eventEmitter.emit("
|
|
5274
|
+
this.on("operabilityChanged", (canWrite: boolean) =>
|
|
5275
|
+
eventEmitter.emit("operabilityChanged", canWrite),
|
|
5174
5276
|
);
|
|
5175
5277
|
} else {
|
|
5176
5278
|
this.on("connected", (clientId: string) => {
|
|
@@ -5185,7 +5287,11 @@ export class ContainerRuntime
|
|
|
5185
5287
|
const getConnectionState = this.getConnectionState;
|
|
5186
5288
|
if (getConnectionState) {
|
|
5187
5289
|
const connectionState = getConnectionState();
|
|
5188
|
-
if (
|
|
5290
|
+
if (
|
|
5291
|
+
connectionState === ConnectionState.Connected ||
|
|
5292
|
+
connectionState === ConnectionState.CatchingUp
|
|
5293
|
+
) {
|
|
5294
|
+
// Note: when CatchingUp, canSendOps will always be false.
|
|
5189
5295
|
return this.canSendOps ? "joinedForWriting" : "joinedForReading";
|
|
5190
5296
|
}
|
|
5191
5297
|
} else if (this.canSendOps) {
|
|
@@ -5211,9 +5317,10 @@ export class ContainerRuntime
|
|
|
5211
5317
|
): T {
|
|
5212
5318
|
let entry = this.extensions.get(id);
|
|
5213
5319
|
if (entry === undefined) {
|
|
5320
|
+
const audience = this.signalAudience;
|
|
5214
5321
|
const runtime = {
|
|
5215
5322
|
getJoinedStatus: this.getJoinedStatus.bind(this),
|
|
5216
|
-
getClientId: () => this.clientId,
|
|
5323
|
+
getClientId: audience ? () => audience.getSelf()?.clientId : () => this.clientId,
|
|
5217
5324
|
events: this.lazyEventsForExtensions.value,
|
|
5218
5325
|
logger: this.baseLogger,
|
|
5219
5326
|
submitAddressedSignal: (
|
|
@@ -5223,7 +5330,7 @@ export class ContainerRuntime
|
|
|
5223
5330
|
this.submitExtensionSignal(id, addressChain, message);
|
|
5224
5331
|
},
|
|
5225
5332
|
getQuorum: this.getQuorum.bind(this),
|
|
5226
|
-
getAudience: this.getAudience.bind(this),
|
|
5333
|
+
getAudience: audience ? () => audience : this.getAudience.bind(this),
|
|
5227
5334
|
supportedFeatures: this.ILayerCompatDetails.supportedFeatures,
|
|
5228
5335
|
} satisfies ExtensionHost<TRuntimeProperties>;
|
|
5229
5336
|
entry = new factory(runtime, ...useContext);
|
package/src/dataStore.ts
CHANGED
|
@@ -7,12 +7,11 @@ import { AttachState } from "@fluidframework/container-definitions";
|
|
|
7
7
|
import type { FluidObject } from "@fluidframework/core-interfaces";
|
|
8
8
|
import type { IFluidHandleInternal } from "@fluidframework/core-interfaces/internal";
|
|
9
9
|
import { assert, unreachableCase } from "@fluidframework/core-utils/internal";
|
|
10
|
-
import
|
|
11
|
-
AliasResult,
|
|
12
|
-
IDataStore,
|
|
13
|
-
IFluidDataStoreChannel,
|
|
14
|
-
|
|
15
|
-
IContainerRuntimeBaseExperimental,
|
|
10
|
+
import {
|
|
11
|
+
type AliasResult,
|
|
12
|
+
type IDataStore,
|
|
13
|
+
type IFluidDataStoreChannel,
|
|
14
|
+
asLegacyAlpha,
|
|
16
15
|
} from "@fluidframework/runtime-definitions/internal";
|
|
17
16
|
import {
|
|
18
17
|
type ITelemetryLoggerExt,
|
|
@@ -80,9 +79,7 @@ class DataStore implements IDataStore {
|
|
|
80
79
|
if (alias.includes("/")) {
|
|
81
80
|
throw new UsageError(`The alias cannot contain slashes: '${alias}'`);
|
|
82
81
|
}
|
|
83
|
-
|
|
84
|
-
const runtime = this.parentContext.containerRuntime as IContainerRuntimeBaseExperimental;
|
|
85
|
-
if (runtime.inStagingMode === true) {
|
|
82
|
+
if (asLegacyAlpha(this.parentContext.containerRuntime).inStagingMode === true) {
|
|
86
83
|
throw new UsageError("Cannot set aliases while in staging mode");
|
|
87
84
|
}
|
|
88
85
|
|
package/src/dataStoreContext.ts
CHANGED
|
@@ -365,6 +365,16 @@ export class Outbox {
|
|
|
365
365
|
* @param resubmitInfo - Key information when flushing a resubmitted batch. Undefined means this is not resubmit.
|
|
366
366
|
*/
|
|
367
367
|
public flush(resubmitInfo?: BatchResubmitInfo): void {
|
|
368
|
+
// We have nothing to flush if all batchManagers are empty, and we we're not needing to resubmit an empty batch placeholder
|
|
369
|
+
if (
|
|
370
|
+
this.idAllocationBatch.empty &&
|
|
371
|
+
this.blobAttachBatch.empty &&
|
|
372
|
+
this.mainBatch.empty &&
|
|
373
|
+
resubmitInfo?.batchId === undefined
|
|
374
|
+
) {
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
|
|
368
378
|
assert(
|
|
369
379
|
!this.isContextReentrant(),
|
|
370
380
|
0xb7b /* Flushing must not happen while incoming changes are being processed */,
|
package/src/packageVersion.ts
CHANGED
|
@@ -3,17 +3,19 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
type ILayerCompatSupportRequirements,
|
|
6
|
+
import type {
|
|
7
|
+
ILayerCompatDetails,
|
|
8
|
+
ILayerCompatSupportRequirements,
|
|
10
9
|
} from "@fluid-internal/client-utils";
|
|
11
10
|
import type { ICriticalContainerError } from "@fluidframework/container-definitions";
|
|
12
11
|
import {
|
|
13
12
|
encodeHandlesInContainerRuntime,
|
|
14
13
|
notifiesReadOnlyState,
|
|
15
14
|
} from "@fluidframework/runtime-definitions/internal";
|
|
16
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
validateLayerCompatibility,
|
|
17
|
+
type ITelemetryLoggerExt,
|
|
18
|
+
} from "@fluidframework/telemetry-utils/internal";
|
|
17
19
|
|
|
18
20
|
import { pkgVersion } from "./packageVersion.js";
|
|
19
21
|
|
|
@@ -95,26 +97,17 @@ export const dataStoreSupportRequirementsForRuntime: ILayerCompatSupportRequirem
|
|
|
95
97
|
export function validateLoaderCompatibility(
|
|
96
98
|
maybeLoaderCompatDetailsForRuntime: ILayerCompatDetails | undefined,
|
|
97
99
|
disposeFn: (error?: ICriticalContainerError) => void,
|
|
100
|
+
logger: ITelemetryLoggerExt,
|
|
98
101
|
): void {
|
|
99
|
-
|
|
102
|
+
validateLayerCompatibility(
|
|
103
|
+
"runtime",
|
|
104
|
+
"loader",
|
|
105
|
+
runtimeCompatDetailsForLoader,
|
|
100
106
|
loaderSupportRequirementsForRuntime,
|
|
101
107
|
maybeLoaderCompatDetailsForRuntime,
|
|
108
|
+
disposeFn,
|
|
109
|
+
logger,
|
|
102
110
|
);
|
|
103
|
-
if (!layerCheckResult.isCompatible) {
|
|
104
|
-
const error = new UsageError("Runtime is not compatible with Loader", {
|
|
105
|
-
errorDetails: JSON.stringify({
|
|
106
|
-
runtimeVersion: runtimeCoreCompatDetails.pkgVersion,
|
|
107
|
-
loaderVersion: maybeLoaderCompatDetailsForRuntime?.pkgVersion,
|
|
108
|
-
runtimeGeneration: runtimeCoreCompatDetails.generation,
|
|
109
|
-
loaderGeneration: maybeLoaderCompatDetailsForRuntime?.generation,
|
|
110
|
-
minSupportedGeneration: loaderSupportRequirementsForRuntime.minSupportedGeneration,
|
|
111
|
-
isGenerationCompatible: layerCheckResult.isGenerationCompatible,
|
|
112
|
-
unsupportedFeatures: layerCheckResult.unsupportedFeatures,
|
|
113
|
-
}),
|
|
114
|
-
});
|
|
115
|
-
disposeFn(error);
|
|
116
|
-
throw error;
|
|
117
|
-
}
|
|
118
111
|
}
|
|
119
112
|
|
|
120
113
|
/**
|
|
@@ -122,26 +115,17 @@ export function validateLoaderCompatibility(
|
|
|
122
115
|
* @internal
|
|
123
116
|
*/
|
|
124
117
|
export function validateDatastoreCompatibility(
|
|
125
|
-
|
|
118
|
+
maybeDataStoreCompatDetailsForRuntime: ILayerCompatDetails | undefined,
|
|
126
119
|
disposeFn: () => void,
|
|
120
|
+
logger: ITelemetryLoggerExt,
|
|
127
121
|
): void {
|
|
128
|
-
|
|
122
|
+
validateLayerCompatibility(
|
|
123
|
+
"runtime",
|
|
124
|
+
"dataStore",
|
|
125
|
+
runtimeCompatDetailsForDataStore,
|
|
129
126
|
dataStoreSupportRequirementsForRuntime,
|
|
130
|
-
|
|
127
|
+
maybeDataStoreCompatDetailsForRuntime,
|
|
128
|
+
disposeFn,
|
|
129
|
+
logger,
|
|
131
130
|
);
|
|
132
|
-
if (!layerCheckResult.isCompatible) {
|
|
133
|
-
const error = new UsageError("Runtime is not compatible with DataStore", {
|
|
134
|
-
errorDetails: JSON.stringify({
|
|
135
|
-
runtimeVersion: runtimeCoreCompatDetails.pkgVersion,
|
|
136
|
-
dataStoreVersion: maybeDataStoreCompatDetails?.pkgVersion,
|
|
137
|
-
runtimeGeneration: runtimeCoreCompatDetails.generation,
|
|
138
|
-
dataStoreGeneration: maybeDataStoreCompatDetails?.generation,
|
|
139
|
-
minSupportedGeneration: dataStoreSupportRequirementsForRuntime.minSupportedGeneration,
|
|
140
|
-
isGenerationCompatible: layerCheckResult.isGenerationCompatible,
|
|
141
|
-
unsupportedFeatures: layerCheckResult.unsupportedFeatures,
|
|
142
|
-
}),
|
|
143
|
-
});
|
|
144
|
-
disposeFn();
|
|
145
|
-
throw error;
|
|
146
|
-
}
|
|
147
131
|
}
|
|
@@ -3,20 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type {
|
|
7
|
-
FetchSource,
|
|
8
|
-
ICreateBlobResponse,
|
|
9
|
-
IDocumentStorageServicePolicies,
|
|
10
|
-
ISnapshot,
|
|
11
|
-
ISnapshotFetchOptions,
|
|
12
|
-
ISnapshotTree,
|
|
13
|
-
ISummaryContext,
|
|
14
|
-
ISummaryHandle,
|
|
15
|
-
ISummaryTree,
|
|
16
|
-
IVersion,
|
|
17
|
-
} from "@fluidframework/driver-definitions/internal";
|
|
18
6
|
import type { IRuntimeStorageService } from "@fluidframework/runtime-definitions/internal";
|
|
19
|
-
import { UsageError } from "@fluidframework/telemetry-utils/internal";
|
|
20
7
|
|
|
21
8
|
/**
|
|
22
9
|
* IRuntimeStorageService proxy which intercepts requests if they can be satisfied by the blobs received in the
|
|
@@ -28,14 +15,6 @@ export class StorageServiceWithAttachBlobs implements IRuntimeStorageService {
|
|
|
28
15
|
private readonly attachBlobs: Map<string, ArrayBufferLike>,
|
|
29
16
|
) {}
|
|
30
17
|
|
|
31
|
-
/**
|
|
32
|
-
* {@link IRuntimeStorageService.policies}.
|
|
33
|
-
* @deprecated This will be removed in a future release. The DataStore layer does not need this.
|
|
34
|
-
*/
|
|
35
|
-
public get policies(): IDocumentStorageServicePolicies | undefined {
|
|
36
|
-
return this.internalStorageService.policies;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
18
|
public async readBlob(id: string): Promise<ArrayBufferLike> {
|
|
40
19
|
const blob = this.attachBlobs.get(id);
|
|
41
20
|
if (blob !== undefined) {
|
|
@@ -46,75 +25,4 @@ export class StorageServiceWithAttachBlobs implements IRuntimeStorageService {
|
|
|
46
25
|
// IRuntimeStorageService to cache appropriately, no need to double-cache.
|
|
47
26
|
return this.internalStorageService.readBlob(id);
|
|
48
27
|
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* {@link IRuntimeStorageService.getSnapshotTree}.
|
|
52
|
-
* @deprecated This will be removed in a future release. The DataStore layer does not need this.
|
|
53
|
-
*/
|
|
54
|
-
public async getSnapshotTree(
|
|
55
|
-
version?: IVersion,
|
|
56
|
-
scenarioName?: string,
|
|
57
|
-
// eslint-disable-next-line @rushstack/no-new-null
|
|
58
|
-
): Promise<ISnapshotTree | null> {
|
|
59
|
-
return this.internalStorageService.getSnapshotTree(version, scenarioName);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* {@link IRuntimeStorageService.getSnapshot}.
|
|
64
|
-
* @deprecated This will be removed in a future release. The DataStore layer does not need this.
|
|
65
|
-
*/
|
|
66
|
-
public async getSnapshot(snapshotFetchOptions?: ISnapshotFetchOptions): Promise<ISnapshot> {
|
|
67
|
-
if (this.internalStorageService.getSnapshot !== undefined) {
|
|
68
|
-
return this.internalStorageService.getSnapshot(snapshotFetchOptions);
|
|
69
|
-
}
|
|
70
|
-
throw new UsageError(
|
|
71
|
-
"getSnapshot api should exist on internal storage in documentStorageServiceProxy class",
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* {@link IRuntimeStorageService.getVersions}.
|
|
77
|
-
* @deprecated This will be removed in a future release. The DataStore layer does not need this.
|
|
78
|
-
*/
|
|
79
|
-
public async getVersions(
|
|
80
|
-
// eslint-disable-next-line @rushstack/no-new-null
|
|
81
|
-
versionId: string | null,
|
|
82
|
-
count: number,
|
|
83
|
-
scenarioName?: string,
|
|
84
|
-
fetchSource?: FetchSource,
|
|
85
|
-
): Promise<IVersion[]> {
|
|
86
|
-
return this.internalStorageService.getVersions(
|
|
87
|
-
versionId,
|
|
88
|
-
count,
|
|
89
|
-
scenarioName,
|
|
90
|
-
fetchSource,
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* {@link IRuntimeStorageService.uploadSummaryWithContext}.
|
|
96
|
-
* @deprecated This will be removed in a future release. The DataStore layer does not need this.
|
|
97
|
-
*/
|
|
98
|
-
public async uploadSummaryWithContext(
|
|
99
|
-
summary: ISummaryTree,
|
|
100
|
-
context: ISummaryContext,
|
|
101
|
-
): Promise<string> {
|
|
102
|
-
return this.internalStorageService.uploadSummaryWithContext(summary, context);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* {@link IRuntimeStorageService.createBlob}.
|
|
107
|
-
* @deprecated This will be removed in a future release. The DataStore layer does not need this.
|
|
108
|
-
*/
|
|
109
|
-
public async createBlob(file: ArrayBufferLike): Promise<ICreateBlobResponse> {
|
|
110
|
-
return this.internalStorageService.createBlob(file);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* {@link IRuntimeStorageService.downloadSummary}.
|
|
115
|
-
* @deprecated This will be removed in a future release. The DataStore layer does not need this.
|
|
116
|
-
*/
|
|
117
|
-
public async downloadSummary(handle: ISummaryHandle): Promise<ISummaryTree> {
|
|
118
|
-
return this.internalStorageService.downloadSummary(handle);
|
|
119
|
-
}
|
|
120
28
|
}
|