Skip to content

Commit 67afa66

Browse files
scottn12JosmithrAbe27342
authored
container-runtime: Add API for default configuration (#24422)
## Description This PR adds an API that allows customers to set leverage the default configuration logic added in #24379. The API is an **optional** property added to the `LoadContainerRuntimeParams` interface, which is used when instantiating a container runtime. This PR also renames the API from `compatibilityVersion` -> `minVersionForCollab` (it was changed from `compatibilityMode` -> `compatibilityVersion` in #24407). ### Considerations The API was added to `LoadContainerRuntimeParams` for two reasons: 1. Allow us to give clear guidance to customers. For example, "Pass in `minVersionForCollab` X to ensure collaboration works with clients running runtime version X. 2. It provides a simple path to requiring customers to pass in a `minVersionForCollab` in the future (by making the param required). One alternative is to expose a helper function that will generate the `runtimeOptions` object that is used when instantiating a container runtime. However, this may complicate the guidance given to customers and does not provide a clear path to making this a required step in the future. ## Example A customer may use the API like so: ```ts const containerRuntime = await ContainerRuntime.loadRuntime({ context, registryEntries, existing, provideEntryPoint, runtimeOptions: { enableGroupedBatching: false, }, minVersionForCollab: "2.20.0", }); ``` There are two noteworthy observations: 1. `minVersionForCollab` is set to `"2.20.0"`. This will ensure the default configurations for `IContainerRuntimeOptionsInternal` are set such that we will ensure that clients running version 2.20.0 or later of the runtime can collaborate. 5. We also manually set `enableGroupedBatching` in the `runtimeOptions` param. Batching is normally enabled by default when `minVersionForCollab` is set to `"2.0.0"` or later, but any configurations manually set in `runtimeOptions` will override the default configurations. ## Next Steps - Add "fail fast" mechanism that prevents explicity setting `runtimeOptions` in such a way that contradicts `minVersionForCollab`. For example, setting `enableGroupedBatching` to be `true` and `minVersionForCollab` to `"1.4.0"`. This should not be allowed since clients running v1.4.0 of the runtime will not be able to collaborate if batching is enabled. See [AB#37810](https://dev.azure.com/fluidframework/235294da-091d-4c29-84fc-cdfc3d90890b/_workitems/edit/37810) ## Misc [AB#36088](https://dev.azure.com/fluidframework/235294da-091d-4c29-84fc-cdfc3d90890b/_workitems/edit/36088) --------- Co-authored-by: Joshua Smithrud <[email protected]> Co-authored-by: Abram Sanderson <[email protected]>
1 parent 471813c commit 67afa66

File tree

11 files changed

+242
-118
lines changed

11 files changed

+242
-118
lines changed

packages/framework/aqueduct/api-report/aqueduct.legacy.alpha.api.md

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export class BaseContainerRuntimeFactory extends RuntimeFactoryHelper implements
1919
export interface BaseContainerRuntimeFactoryProps {
2020
// @deprecated (undocumented)
2121
dependencyContainer?: IFluidDependencySynthesizer;
22+
minVersionForCollab?: MinimumVersionForCollab | undefined;
2223
provideEntryPoint: (runtime: IContainerRuntime) => Promise<FluidObject>;
2324
registryEntries: NamedFluidDataStoreRegistryEntries;
2425
// @deprecated

packages/framework/aqueduct/src/container-runtime-factories/baseContainerRuntimeFactory.ts

+9
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
FluidDataStoreRegistry,
1212
loadContainerRuntime,
1313
type IContainerRuntimeOptions,
14+
type MinimumVersionForCollab,
1415
} from "@fluidframework/container-runtime/internal";
1516
import type { IContainerRuntime } from "@fluidframework/container-runtime-definitions/internal";
1617
import type { FluidObject } from "@fluidframework/core-interfaces";
@@ -61,6 +62,11 @@ export interface BaseContainerRuntimeFactoryProps {
6162
* created with this factory
6263
*/
6364
provideEntryPoint: (runtime: IContainerRuntime) => Promise<FluidObject>;
65+
/**
66+
* The minVersionForCollab passed to the ContainerRuntime when instantiating it.
67+
* See {@link @fluidframework/container-runtime#LoadContainerRuntimeParams} for more details on this property.
68+
*/
69+
minVersionForCollab?: MinimumVersionForCollab | undefined;
6470
}
6571

6672
/**
@@ -88,6 +94,7 @@ export class BaseContainerRuntimeFactory
8894
// eslint-disable-next-line import/no-deprecated
8995
private readonly requestHandlers: RuntimeRequestHandler[];
9096
private readonly provideEntryPoint: (runtime: IContainerRuntime) => Promise<FluidObject>;
97+
private readonly minVersionForCollab: MinimumVersionForCollab | undefined;
9198

9299
public constructor(props: BaseContainerRuntimeFactoryProps) {
93100
super();
@@ -98,6 +105,7 @@ export class BaseContainerRuntimeFactory
98105
this.provideEntryPoint = props.provideEntryPoint;
99106
this.requestHandlers = props.requestHandlers ?? [];
100107
this.registry = new FluidDataStoreRegistry(this.registryEntries);
108+
this.minVersionForCollab = props.minVersionForCollab;
101109
}
102110

103111
/**
@@ -146,6 +154,7 @@ export class BaseContainerRuntimeFactory
146154
// eslint-disable-next-line import/no-deprecated
147155
requestHandler: buildRuntimeRequestHandler(...this.requestHandlers),
148156
provideEntryPoint: this.provideEntryPoint,
157+
minVersionForCollab: this.minVersionForCollab,
149158
});
150159
}
151160

packages/framework/fluid-static/src/compatibilityConfiguration.ts

+10-31
Original file line numberDiff line numberDiff line change
@@ -3,48 +3,27 @@
33
* Licensed under the MIT License.
44
*/
55

6-
import {
7-
CompressionAlgorithms,
8-
type IContainerRuntimeOptionsInternal,
9-
} from "@fluidframework/container-runtime/internal";
10-
import { FlushMode } from "@fluidframework/runtime-definitions/internal";
6+
import type { IContainerRuntimeOptionsInternal } from "@fluidframework/container-runtime/internal";
117

128
import type { CompatibilityMode } from "./types.js";
139

1410
/**
15-
* Specifies the configured runtime options for each {@link CompatibilityMode}..
11+
* The CompatibilityMode selected determines the set of runtime options to use. In "1" mode we support
12+
* full interop with true 1.x clients, while in "2" mode we only support interop with 2.x clients.
13+
*
14+
* @privateRemarks In general, we can use the `compatibilityMode` property of `LoadContainerRuntimeParams` to apply
15+
* the proper configurations. However, there are some options that we need to explicity set that differ
16+
* from the default values (i.e. `enableRuntimeIdCompressor` below).
1617
*/
1718
export const compatibilityModeRuntimeOptions: Record<
1819
CompatibilityMode,
1920
IContainerRuntimeOptionsInternal
2021
> = {
21-
"1": {
22-
// 1.x clients are compatible with TurnBased flushing, but here we elect to remain on Immediate flush mode
23-
// as a work-around for inability to send batches larger than 1Mb. Immediate flushing keeps batches smaller as
24-
// fewer messages will be included per flush.
25-
flushMode: FlushMode.Immediate,
26-
// Op compression is on by default but introduces a new type of op which is not compatible with 1.x clients.
27-
compressionOptions: {
28-
minimumBatchSizeInBytes: Number.POSITIVE_INFINITY, // disabled
29-
compressionAlgorithm: CompressionAlgorithms.lz4,
30-
},
31-
// Grouped batching is on by default but introduces a new type of op which is not compatible with 1.x clients.
32-
enableGroupedBatching: false,
33-
// TODO: Include explicit disables for things that are currently off-by-default?
34-
35-
// Explicitly disable running Sweep in compat mode "1". Sweep is supported only in 2.x. So, when 1.x and 2.x
36-
// clients are running in parallel, running sweep will fail 1.x clients.
37-
gcOptions: { enableGCSweep: undefined },
38-
},
22+
"1": {},
3923
"2": {
40-
// Explicit schema control explicitly makes the container incompatible with 1.x clients, to force their
41-
// ejection from collaboration and prevent container corruption. It is off by default and must be explicitly enabled.
42-
explicitSchemaControl: true,
4324
// The runtime ID compressor is a prerequisite to use SharedTree but is off by default and must be explicitly enabled.
44-
// It introduces a new type of op which is not compatible with 1.x clients.
25+
// In general, we don't want to enable this by default since it increases the bundle size. However, since SharedTree
26+
// is bundled with the fluid-framework package, we need to enable it here to support SharedTree.
4527
enableRuntimeIdCompressor: "on",
46-
// Explicitly disable running Sweep in compat mode "2". Although sweep is supported in 2.x, it is disabled by default.
47-
// This setting explicitly disables it to be extra safe.
48-
gcOptions: { enableGCSweep: undefined },
4928
},
5029
};

packages/framework/fluid-static/src/rootDataObject.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ import {
1010
DataObjectFactory,
1111
} from "@fluidframework/aqueduct/internal";
1212
import type { IRuntimeFactory } from "@fluidframework/container-definitions/internal";
13-
import { FluidDataStoreRegistry } from "@fluidframework/container-runtime/internal";
13+
import {
14+
FluidDataStoreRegistry,
15+
type MinimumVersionForCollab,
16+
} from "@fluidframework/container-runtime/internal";
1417
import type { IContainerRuntime } from "@fluidframework/container-runtime-definitions/internal";
1518
import type { FluidObject, IFluidLoadable } from "@fluidframework/core-interfaces";
1619
import type { IChannelFactory } from "@fluidframework/datastore-definitions/internal";
@@ -36,6 +39,14 @@ import {
3639
parseDataObjectsFromSharedObjects,
3740
} from "./utils.js";
3841

42+
/**
43+
* Maps CompatibilityMode to a semver valid string that can be passed to the container runtime.
44+
*/
45+
const compatibilityModeToMinVersionForCollab = {
46+
"1": "1.0.0",
47+
"2": "2.0.0",
48+
} as const satisfies Record<CompatibilityMode, MinimumVersionForCollab>;
49+
3950
/**
4051
* Input props for {@link RootDataObject.initializingFirstTime}.
4152
*/
@@ -242,6 +253,7 @@ class DOProviderContainerRuntimeFactory extends BaseContainerRuntimeFactory {
242253
registryEntries: [rootDataObjectFactory.registryEntry],
243254
runtimeOptions: compatibilityModeRuntimeOptions[compatibilityMode],
244255
provideEntryPoint,
256+
minVersionForCollab: compatibilityModeToMinVersionForCollab[compatibilityMode],
245257
});
246258
this.rootDataObjectFactory = rootDataObjectFactory;
247259
this.initialObjects = schema.initialObjects;

packages/runtime/container-runtime/api-report/container-runtime.legacy.alpha.api.md

+4
Original file line numberDiff line numberDiff line change
@@ -338,13 +338,17 @@ export interface LoadContainerRuntimeParams {
338338
containerScope?: FluidObject;
339339
context: IContainerContext;
340340
existing: boolean;
341+
minVersionForCollab?: MinimumVersionForCollab;
341342
provideEntryPoint: (containerRuntime: IContainerRuntime) => Promise<FluidObject>;
342343
registryEntries: NamedFluidDataStoreRegistryEntries;
343344
// @deprecated
344345
requestHandler?: (request: IRequest, runtime: IContainerRuntime) => Promise<IResponse>;
345346
runtimeOptions?: IContainerRuntimeOptions;
346347
}
347348

349+
// @alpha @legacy
350+
export type MinimumVersionForCollab = `${1 | 2}.${bigint}.${bigint}` | `${1 | 2}.${bigint}.${bigint}-${string}`;
351+
348352
// @alpha @deprecated @legacy (undocumented)
349353
export type OmitAttributesVersions<T> = Omit<T, "snapshotFormatVersion" | "summaryFormatVersion">;
350354

packages/runtime/container-runtime/src/compatUtils.ts

+53-30
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { pkgVersion } from "./packageVersion.js";
1616
/**
1717
* Our policy is to support N/N-1 compatibility by default, where N is the most
1818
* recent public major release of the runtime.
19-
* Therefore, if the customer does not provide a compatibility mode, we will
19+
* Therefore, if the customer does not provide a minVersionForCollab, we will
2020
* default to use N-1.
2121
*
2222
* However, this is not consistent with today's behavior. Some options (i.e.
@@ -26,12 +26,21 @@ import { pkgVersion } from "./packageVersion.js";
2626
* Importantly though, N/N-2 compatibility is still guaranteed with the proper
2727
* configurations set.
2828
*
29-
* Further to distinguish unspecified `compatibilityVersion` from a specified
29+
* Further to distinguish unspecified `minVersionForCollab` from a specified
3030
* version and allow `enableExplicitSchemaControl` to default to `true` for
3131
* any 2.0.0+ version, we will use a special value of `2.0.0-defaults`, which
3232
* is semantically less than 2.0.0.
3333
*/
34-
export const defaultCompatibilityVersion = "2.0.0-defaults" as const;
34+
export const defaultMinVersionForCollab =
35+
"2.0.0-defaults" as const satisfies MinimumVersionForCollab;
36+
37+
/**
38+
* We don't want allow a version before the major public release of the LTS version.
39+
* Today we use "1.0.0", because our policy supports N/N-1 & N/N-2, which includes
40+
* all minor versions of N. Though LTS starts at 1.4.0, we should stay consistent
41+
* with our policy and allow all 1.x versions to be compatible with 2.x.
42+
*/
43+
const lowestMinVersionForCollab = "1.0.0" as const satisfies MinimumVersionForCollab;
3544

3645
/**
3746
* String in a valid semver format specifying bottom of a minor version
@@ -43,6 +52,18 @@ export type MinimumMinorSemanticVersion = `${bigint}.${bigint}.0` | `${bigint}.0
4352

4453
/**
4554
* String in a valid semver format of a specific version at least specifying minor.
55+
*
56+
* @legacy
57+
* @alpha
58+
*/
59+
export type MinimumVersionForCollab =
60+
| `${1 | 2}.${bigint}.${bigint}`
61+
| `${1 | 2}.${bigint}.${bigint}-${string}`;
62+
63+
/**
64+
* String in a valid semver format of a specific version at least specifying minor.
65+
* Unlike {@link MinimumVersionForCollab}, this type allows any bigint for the major version.
66+
* Used as a more generic type that allows major versions other than 1 or 2.
4667
*/
4768
export type SemanticVersion =
4869
| `${bigint}.${bigint}.${bigint}`
@@ -84,13 +105,13 @@ export type RuntimeOptionsAffectingDocSchema = Omit<
84105
/**
85106
* Mapping of RuntimeOptionsAffectingDocSchema to their compatibility related configs.
86107
*
87-
* Each key in this map corresponds to a property in RuntimeOptionsAffectingDocSchema. The value is an object that maps SemanticVersions
88-
* to the appropriate default value for that property to supporting that SemanticVersion. If clients running SemanticVersion X are able to understand
89-
* the format changes introduced by the property, then the default value for that SemanticVersion will enable the feature associated with the property.
108+
* Each key in this map corresponds to a property in RuntimeOptionsAffectingDocSchema. The value is an object that maps MinimumVersionForCollab
109+
* to the appropriate default value for that property to supporting that MinimumVersionForCollab. If clients running MinimumVersionForCollab X are able to understand
110+
* the format changes introduced by the property, then the default value for that MinimumVersionForCollab will enable the feature associated with the property.
90111
* Otherwise, the feature will be disabled.
91112
*
92-
* For example if the compatibilityVersion is a 1.x version (i.e. "1.5.0"), then the default value for `enableGroupedBatching` will be false since 1.x
93-
* clients do not understand the document format when batching is enabled. If the compatibilityVersion is a 2.x client (i.e. "2.0.0" or later), then the
113+
* For example if the minVersionForCollab is a 1.x version (i.e. "1.5.0"), then the default value for `enableGroupedBatching` will be false since 1.x
114+
* clients do not understand the document format when batching is enabled. If the minVersionForCollab is a 2.x client (i.e. "2.0.0" or later), then the
94115
* default value for `enableGroupedBatching` will be true because clients running 2.0 or later will be able to understand the format changes associated
95116
* with the batching feature.
96117
*/
@@ -118,14 +139,14 @@ const runtimeOptionsAffectingDocSchemaConfigMap = {
118139
explicitSchemaControl: {
119140
"1.0.0": false,
120141
// This option's intention is to prevent 1.x clients from joining sessions
121-
// when enabled. This is set to true when the compatibility version is set
142+
// when enabled. This is set to true when the minVersionForCollab is set
122143
// to >=2.0.0 (explicitly). This is different than other 2.0 defaults
123144
// because it was not enabled by default prior to the implementation of
124-
// `compatibilityVersion`.
125-
// `defaultCompatibilityVersion` is set to "2.0.0-defaults" which "2.0.0"
145+
// `minVersionForCollab`.
146+
// `defaultMinVersionForCollab` is set to "2.0.0-defaults" which "2.0.0"
126147
// does not satisfy to avoiding enabling this option by default as of
127-
// `compatibilityVersion` introduction, which could be unexpected.
128-
// Only enable as a default when `compatibilityVersion` is specified at
148+
// `minVersionForCollab` introduction, which could be unexpected.
149+
// Only enable as a default when `minVersionForCollab` is specified at
129150
// 2.0.0+.
130151
"2.0.0": true,
131152
} as const,
@@ -138,7 +159,7 @@ const runtimeOptionsAffectingDocSchemaConfigMap = {
138159
} as const,
139160
gcOptions: {
140161
"1.0.0": {},
141-
// Although sweep is supported in 2.x, it is disabled by default until compatibilityVersion>=3.0.0 to be extra safe.
162+
// Although sweep is supported in 2.x, it is disabled by default until minVersionForCollab>=3.0.0 to be extra safe.
142163
"3.0.0": { enableGCSweep: true },
143164
} as const,
144165
createBlobPayloadPending: {
@@ -150,24 +171,24 @@ const runtimeOptionsAffectingDocSchemaConfigMap = {
150171
} as const satisfies ConfigMap<RuntimeOptionsAffectingDocSchema>;
151172

152173
/**
153-
* Returns the default RuntimeOptionsAffectingDocSchema configuration for a given compatibility version.
174+
* Returns the default RuntimeOptionsAffectingDocSchema configuration for a given minVersionForCollab.
154175
*/
155-
export function getCompatibilityVersionDefaults(
156-
compatibilityVersion: SemanticVersion,
176+
export function getMinVersionForCollabDefaults(
177+
minVersionForCollab: MinimumVersionForCollab,
157178
): RuntimeOptionsAffectingDocSchema {
158179
return getConfigsForCompatMode(
159-
compatibilityVersion,
180+
minVersionForCollab,
160181
runtimeOptionsAffectingDocSchemaConfigMap,
161182
// This is a bad cast away from Partial that getConfigsForCompatMode provides.
162183
// ConfigMap should be restructured to provide RuntimeOptionsAffectingDocSchema guarantee.
163184
) as RuntimeOptionsAffectingDocSchema;
164185
}
165186

166187
/**
167-
* Returns a default configuration given compatibility version and configuration version map.
188+
* Returns a default configuration given minVersionForCollab and configuration version map.
168189
*/
169190
export function getConfigsForCompatMode<T extends Record<SemanticVersion, unknown>>(
170-
compatibilityVersion: SemanticVersion,
191+
minVersionForCollab: SemanticVersion,
171192
configMap: ConfigMap<T>,
172193
): Partial<T> {
173194
const defaultConfigs: Partial<T> = {};
@@ -176,14 +197,14 @@ export function getConfigsForCompatMode<T extends Record<SemanticVersion, unknow
176197
const config = configMap[key as keyof T];
177198
// Sort the versions in ascending order so we can short circuit the loop.
178199
const versions = Object.keys(config).sort(compare);
179-
// For each config, we iterate over the keys and check if compatibilityVersion is greater than or equal to the version.
200+
// For each config, we iterate over the keys and check if minVersionForCollab is greater than or equal to the version.
180201
// If so, we set it as the default value for the option. At the end of the loop we should have the most recent default
181-
// value that is compatible with the version specified as the compatibilityVersion.
202+
// value that is compatible with the version specified as the minVersionForCollab.
182203
for (const version of versions) {
183-
if (gte(compatibilityVersion, version)) {
204+
if (gte(minVersionForCollab, version)) {
184205
defaultConfigs[key] = config[version as MinimumMinorSemanticVersion];
185206
} else {
186-
// If the compatibility mode is less than the version, we break out of the loop since we don't need to check
207+
// If the minVersionForCollab is less than the version, we break out of the loop since we don't need to check
187208
// any later versions.
188209
break;
189210
}
@@ -193,13 +214,15 @@ export function getConfigsForCompatMode<T extends Record<SemanticVersion, unknow
193214
}
194215

195216
/**
196-
* Checks if the compatibility version is valid.
197-
* A valid compatibility version is a string that is a valid semver version and is less than or equal to the current package version.
217+
* Checks if the minVersionForCollab is valid.
218+
* A valid minVersionForCollab is a MinimumVersionForCollab that is at least `lowestMinVersionForCollab` and less than or equal to the current package version.
198219
*/
199-
export function isValidCompatVersion(compatibilityVersion: SemanticVersion): boolean {
220+
export function isValidMinVersionForCollab(
221+
minVersionForCollab: MinimumVersionForCollab,
222+
): boolean {
200223
return (
201-
compatibilityVersion !== undefined &&
202-
valid(compatibilityVersion) !== null &&
203-
lte(compatibilityVersion, pkgVersion)
224+
valid(minVersionForCollab) !== null &&
225+
gte(minVersionForCollab, lowestMinVersionForCollab) &&
226+
lte(minVersionForCollab, pkgVersion)
204227
);
205228
}

0 commit comments

Comments
 (0)