Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .changeset/zesty-otters-collab.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
"@fluidframework/fluid-static": minor
"fluid-framework": minor
"__section": legacy
---
`createTreeContainerRuntimeFactory` no longer accepts `minVersionForCollabOverride`

The `minVersionForCollabOverride` property on the `props` argument of `createTreeContainerRuntimeFactory` has been removed.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a beta breaking change. It will need to be kept until 2.110 - file deprecation issue under #26499.


Pass a [`MinimumVersionForCollab`](https://fluidframework.com/docs/api/runtime-definitions/minimumversionforcollab-typealias) semver string (for example `"2.10.0"`) directly via the `compatibilityMode` property instead. `compatibilityMode` now accepts either a `MinimumVersionForCollab` semver string or the existing (deprecated) `CompatibilityMode` values `"1"` / `"2"`.

Check failure on line 10 in .changeset/zesty-otters-collab.md

View workflow job for this annotation

GitHub Actions / vale

[vale] reported by reviewdog 🐶 [Vale.Spelling] Did you really mean 'semver'? Raw Output: {"message": "[Vale.Spelling] Did you really mean 'semver'?", "location": {"path": ".changeset/zesty-otters-collab.md", "range": {"start": {"line": 10, "column": 288}}}, "severity": "ERROR"}

Check failure on line 10 in .changeset/zesty-otters-collab.md

View workflow job for this annotation

GitHub Actions / vale

[vale] reported by reviewdog 🐶 [Vale.Spelling] Did you really mean 'semver'? Raw Output: {"message": "[Vale.Spelling] Did you really mean 'semver'?", "location": {"path": ".changeset/zesty-otters-collab.md", "range": {"start": {"line": 10, "column": 127}}}, "severity": "ERROR"}

Before:

```ts
createTreeContainerRuntimeFactory({
schema,
compatibilityMode: "2",
minVersionForCollabOverride: "2.10.0",
});
```

After:

```ts
createTreeContainerRuntimeFactory({
schema,
compatibilityMode: "2.10.0",
});
```
26 changes: 11 additions & 15 deletions CrossClientCompatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,25 +108,21 @@ compat internally (see

#### Configuring Cross-Client Compatibility (Declarative Model)

If you are using a service client (i.e. `AzureClient` or `OdspClient`), cross-client compatibility is configured via the `CompatibilityMode` parameter. This is a required argument when creating or loading a container:
If you are using a service client (i.e. `AzureClient` or `OdspClient`), cross-client compatibility is
configured by passing a `minVersionForCollab` semver string when creating or loading a container:

```typescript
// Creating a new container
const { container } = await azureClient.createContainer(schema, compatibilityMode);
const { container } = await azureClient.createContainer(schema, "2.0.0");

// Loading an existing container
const { container } = await azureClient.getContainer(id, schema, compatibilityMode);
const { container } = await azureClient.getContainer(id, schema, "2.0.0");
```

The client will map `CompatibilityMode` to a `minVersionForCollab` value (see [utils.ts](./packages/framework/fluid-static/src/utils.ts) for details) and automatically configure runtime options via [compatibilityConfiguration.ts](./packages/framework/fluid-static/src/compatibilityConfiguration.ts). This means you do not need to manage individual runtime options or version strings directly.

Below is the mapping of `CompatibilityMode` values to `minVersionForCollab` at the time of writing. For the most up-to-date mapping, please refer to `compatibilityModeToMinVersionForCollab` in [utils.ts](./packages/framework/fluid-static/src/utils.ts).

<!-- prettier-ignore -->
| Mode | Meaning | Mapped `minVersionForCollab` |
| --- | --- | --- |
| `"1"` | Supports collaboration with 1.x clients. Uses a conservative set of runtime options. | `"1.0.0"` |
| `"2"` | Supports collaboration with 2.x clients only. Enables newer features (e.g., runtime ID compressor for SharedTree support). | `"2.0.0"` |
This sets the minimum Fluid version allowed to collaborate on the document and automatically configures
runtime options to be compatible with that version (see
[compatibilityConfiguration.ts](./packages/framework/fluid-static/src/compatibilityConfiguration.ts)).
You do not need to manage individual runtime options directly.

#### Configuring Cross-Client Compatibility (Encapsulated Model)

Expand Down Expand Up @@ -165,9 +161,9 @@ version your users are [saturated](#terminology) on. This will ensure:
We recommend following the below pattern to ensure cross-client compatibility. Keeping your compatibility configuration up-to-date on an ongoing basis ensures you are always within a safe compatibility window.

1. Observe the distribution of Fluid versions across your application's clients. See [Observing Client Version Distribution](./FluidCompatibilityConsiderations.md#observing-client-version-distribution) for how to do this using telemetry.
2. Update your compatibility configuration to match the oldest deployed version that your clients are [saturated](#terminology) on:
- **Declarative model**: Set `CompatibilityMode` to the value corresponding to that saturated version.
- **Encapsulated model**: Set `minVersionForCollab` to the specific saturated version (e.g., `"2.10.0"`).
2. Update your compatibility configuration to match the oldest deployed version that your clients are
[saturated](#terminology) on. In both the declarative and encapsulated models, set `minVersionForCollab`
to that saturated version (e.g., `"2.10.0"`).
3. Verify that the configured compatibility checkpoint is within the supported compatibility window of the Fluid Framework version you want to upgrade to. If it is, bump your Fluid Framework dependencies and update your lock file (so a newer version isn't picked up implicitly); no further action is required. If not, wait for further saturation and return to step 1.
4. Monitor telemetry for warnings/errors to ensure safe rollout (see [Errors and Warnings to Monitor](#errors-and-warnings-to-monitor) below). At this point any clients running a version older than the configured `minVersionForCollab` may be blocked from accessing the document.

Expand Down
4 changes: 2 additions & 2 deletions examples/apps/presence-tracker/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ async function start(): Promise<void> {
if (createNew) {
// The client will create a new detached container using the schema
// A detached container will enable the app to modify the container before attaching it to the client
({ container } = await client.createContainer(containerSchema, "2"));
({ container } = await client.createContainer(containerSchema, "2.0.0"));

// If the app is in a `createNew` state, and the container is detached, we attach the container.
// This uploads the container to the service and connects to the collaboration session.
Expand All @@ -56,7 +56,7 @@ async function start(): Promise<void> {
id = location.hash.slice(1);
// Use the unique container ID to fetch the container created earlier. It will already be connected to the
// collaboration session.
({ container } = await client.getContainer(id, containerSchema, "2"));
({ container } = await client.getContainer(id, containerSchema, "2.0.0"));
}

const presence = getPresence(container);
Expand Down
2 changes: 1 addition & 1 deletion examples/benchmarks/tablebench/src/azure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export async function initFluid(): Promise<{ view: TreeView<typeof Table> }> {
const { tree } = container.initialObjects;
view = tree.viewWith(config);
} else {
({ container } = await client.createContainer(containerSchema, "2"));
({ container } = await client.createContainer(containerSchema, "2.0.0"));
const { tree } = container.initialObjects;
view = tree.viewWith(config);
view.initialize(generateTable(10000));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export async function createFluidContainer(
console.log("Creating new container...");
let createContainerResult: ContainerLoadResult;
try {
createContainerResult = await client.createContainer(containerSchema, "2");
createContainerResult = await client.createContainer(containerSchema, "2.0.0");
} catch (error) {
console.error(`Encountered error creating Fluid container: "${error}".`);
throw error;
Expand Down Expand Up @@ -126,7 +126,7 @@ export async function loadExistingFluidContainer(
console.log("Loading existing container...");
let loadContainerResult: ContainerLoadResult;
try {
loadContainerResult = await client.getContainer(containerId, containerSchema, "2");
loadContainerResult = await client.getContainer(containerId, containerSchema, "2.0.0");
} catch (error) {
console.error(`Encountered error loading Fluid container: "${error}".`);
throw error;
Expand Down
4 changes: 2 additions & 2 deletions examples/data-objects/text-editor/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ async function createAndAttachNewContainer(client: AzureClient): Promise<{
containerId: string;
treeView: TreeViewAlpha<typeof TextEditorRoot>;
}> {
const { container } = await client.createContainer(containerSchema, "2");
const { container } = await client.createContainer(containerSchema, "2.0.0");

const treeView = asAlpha<typeof TextEditorRoot>(
container.initialObjects.tree.viewWith(treeConfig),
Expand Down Expand Up @@ -127,7 +127,7 @@ async function loadExistingContainer(
container: IFluidContainer<typeof containerSchema>;
treeView: TreeViewAlpha<typeof TextEditorRoot>;
}> {
const { container } = await client.getContainer(containerId, containerSchema, "2");
const { container } = await client.getContainer(containerId, containerSchema, "2.0.0");
const treeView = asAlpha(container.initialObjects.tree.viewWith(treeConfig));
return {
container,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ async function start(): Promise<void> {
if (createNew) {
// The client will create a new detached container using the schema
// A detached container will enable the app to modify the container before attaching it to the client
({ container, services } = await client.createContainer(diceRollerContainerSchema, "2"));
({ container, services } = await client.createContainer(
diceRollerContainerSchema,
"2.0.0",
));
// const map1 = container.initialObjects.map1 as ISharedMap;
// map1.set("diceValue", 1);
// const map2 = container.initialObjects.map1 as ISharedMap;
Expand All @@ -63,7 +66,11 @@ async function start(): Promise<void> {
id = location.hash.slice(1);
// Use the unique container ID to fetch the container created earlier. It will already be connected to the
// collaboration session.
({ container, services } = await client.getContainer(id, diceRollerContainerSchema, "2"));
({ container, services } = await client.getContainer(
id,
diceRollerContainerSchema,
"2.0.0",
));
appModel = loadAppFromExistingContainer(container);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ async function createContainerAndRenderInElement(
containerId,
createDOProviderContainerRuntimeFactory({
schema: diceRollerContainerSchema,
compatibilityMode: "2",
compatibilityMode: "2.0.0",
}),
createNewFlag,
);
Expand Down
4 changes: 2 additions & 2 deletions examples/service-clients/azure-client/todo-list/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ async function start(): Promise<void> {
if (createNew) {
// The client will create a new detached container using the schema
// A detached container will enable the app to modify the container before attaching it to the client
({ container } = await client.createContainer(todoListContainerSchema, "2"));
({ container } = await client.createContainer(todoListContainerSchema, "2.0.0"));
// Initialize our models so they are ready for use with our controllers
appModel = await initializeAppForNewContainer(container);

Expand All @@ -71,7 +71,7 @@ async function start(): Promise<void> {
containerId = location.hash.slice(1);
// Use the unique container ID to fetch the container created earlier. It will already be connected to the
// collaboration session.
({ container } = await client.getContainer(containerId, todoListContainerSchema, "2"));
({ container } = await client.getContainer(containerId, todoListContainerSchema, "2.0.0"));
appModel = loadAppFromExistingContainer(container);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ async function createContainerAndRenderInElement(
containerId,
createTreeContainerRuntimeFactory({
schema: todoListContainerSchema,
compatibilityMode: "2",
compatibilityMode: "2.0.0",
}),
createNewFlag,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export async function loadFluidData<T extends ContainerSchema>(
services: OdspContainerServices;
container: IFluidContainer<T>;
}> {
const { container, services } = await client.getContainer(itemId, schema);
const { container, services } = await client.getContainer(itemId, schema, "2.0.0");

return { services, container };
}
Expand All @@ -36,7 +36,7 @@ export async function createFluidData<T extends ContainerSchema>(
}> {
// The client will create a new detached container using the schema
// A detached container will enable the app to modify the container before attaching it to the client
const { container, services } = await client.createContainer(schema);
const { container, services } = await client.createContainer(schema, "2.0.0");

return { services, container };
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
```ts

// @public
// @public @deprecated
export type CompatibilityMode = "1" | "2";

// @public
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
```ts

// @public
// @public @deprecated
export type CompatibilityMode = "1" | "2";

// @public
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

```ts

// @public
// @public @deprecated
export type CompatibilityMode = "1" | "2";

// @public
Expand All @@ -19,10 +19,9 @@ export interface ContainerSchema {
// @beta @legacy
export function createTreeContainerRuntimeFactory(props: {
readonly schema: TreeContainerSchema;
readonly compatibilityMode: CompatibilityMode;
readonly compatibilityMode: MinimumVersionForCollab | CompatibilityMode;
readonly rootDataStoreRegistry?: IFluidDataStoreRegistry;
readonly runtimeOptionOverrides?: Partial<IContainerRuntimeOptions>;
readonly minVersionForCollabOverride?: MinimumVersionForCollab;
}): IRuntimeFactory;

// @public
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
```ts

// @public
// @public @deprecated
export type CompatibilityMode = "1" | "2";

// @public
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
```ts

// @public
// @public @deprecated
export type CompatibilityMode = "1" | "2";

// @public
Expand Down
3 changes: 2 additions & 1 deletion packages/framework/fluid-static/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,8 @@
"@fluidframework/runtime-utils": "workspace:~",
"@fluidframework/shared-object-base": "workspace:~",
"@fluidframework/telemetry-utils": "workspace:~",
"@fluidframework/tree": "workspace:~"
"@fluidframework/tree": "workspace:~",
"semver-ts": "^1.0.3"
},
"devDependencies": {
"@arethetypeswrong/cli": "^0.18.2",
Expand Down
35 changes: 26 additions & 9 deletions packages/framework/fluid-static/src/compatibilityConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@
*/

import type { IContainerRuntimeOptionsInternal } from "@fluidframework/container-runtime/internal";

import type { CompatibilityMode } from "./types.js";
import type { MinimumVersionForCollab } from "@fluidframework/runtime-definitions/internal";
import { gte } from "semver-ts";

/**
* The CompatibilityMode selected determines the set of runtime options to use. In "1" mode we support
* full interop with true 1.x clients, while in "2" mode we only support interop with 2.x clients.
* The `minVersionForCollab` determines the set of runtime options to use.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I don't think this description is very accurate. Probably worth reworking this.

* For a 1.x `minVersionForCollab` we support full interop with true 1.x clients.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: these specifics probably belong in a @remarks block. The summary block should be reserved for a brief semantic description.

* For a 2.x `minVersionForCollab` we only support interop with 2.x clients.
*
* @privateRemarks In general, we can use the `compatibilityMode` property of `LoadContainerRuntimeParams` to apply
* the proper configurations. However, there are some options that we need to explicity set that differ
* from the default values (i.e. `enableRuntimeIdCompressor` below).
* @privateRemarks The purpose of this map is to use a different set of defaults
* than what the runtime normally uses based on a given `minVersionForCollab` (e.g. `enableRuntimeIdCompressor` below).)
*/
export const compatibilityModeRuntimeOptions: Record<
CompatibilityMode,
const minVersionForCollabToDefaultRuntimeOptions: Record<
"1" | "2",
IContainerRuntimeOptionsInternal
> = {
"1": {},
Expand All @@ -27,3 +27,20 @@ export const compatibilityModeRuntimeOptions: Record<
enableRuntimeIdCompressor: "on",
},
};

/**
* Returns the fluid-static-specific runtime option overrides for the given `minVersionForCollab`.
*
* @remarks
* The bulk of runtime defaults for a given `minVersionForCollab` are selected by container-runtime
* (via `getMinVersionForCollabDefaults`). This function only contributes the additional overrides
* that fluid-static needs to layer on top of those defaults.
* @internal
*/
export function defaultRuntimeOptionsForMinVersion(
minVersionForCollab: MinimumVersionForCollab,
): IContainerRuntimeOptionsInternal {
return minVersionForCollabToDefaultRuntimeOptions[
gte(minVersionForCollab, "2.0.0") ? "2" : "1"
Comment thread
jason-ha marked this conversation as resolved.
];
}
5 changes: 4 additions & 1 deletion packages/framework/fluid-static/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,7 @@ export type {
Myself,
TreeContainerSchema,
} from "./types.js";
export { isTreeContainerSchema } from "./utils.js";
export {
isTreeContainerSchema,
resolveCompatibilityModeToMinVersionForCollab,
} from "./utils.js";
Loading
Loading