Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(container-runtime): Add IProtocolOptions and CompatibilityMode to container-runtime #23951

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@

```ts

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

export { compatibilityModeRuntimeOptions }

// @public
export type ContainerAttachProps<T = unknown> = T;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@

```ts

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

export { compatibilityModeRuntimeOptions }

// @public
export type ContainerAttachProps<T = unknown> = T;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@

```ts

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

export { compatibilityModeRuntimeOptions }

// @public
export type ContainerAttachProps<T = unknown> = T;
Expand Down
6 changes: 5 additions & 1 deletion packages/framework/fluid-static/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ export {
export { createDOProviderContainerRuntimeFactory } from "./rootDataObject.js";
export { createServiceAudience } from "./serviceAudience.js";
export type {
CompatibilityMode,
ContainerSchema,
ContainerAttachProps,
IConnection,
Expand All @@ -33,3 +32,8 @@ export type {
MemberChangedListener,
Myself,
} from "./types.js";

// Re-export so other packages don't need to pull in container-runtime
// TODO: Should we re-export?
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't see a great reason to re-export the API in fluid-static, but I believe this would be a breaking change to a public API. Do we need to stage removing the API completely from fluid-static?

export type { CompatibilityMode } from "@fluidframework/container-runtime";
export { compatibilityModeRuntimeOptions } from "@fluidframework/container-runtime/internal";
4 changes: 2 additions & 2 deletions packages/framework/fluid-static/src/rootDataObject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ import {
DataObjectFactory,
} from "@fluidframework/aqueduct/internal";
import type { IRuntimeFactory } from "@fluidframework/container-definitions/internal";
import type { CompatibilityMode } from "@fluidframework/container-runtime";
import { compatibilityModeRuntimeOptions } from "@fluidframework/container-runtime/internal";
import type { IContainerRuntime } from "@fluidframework/container-runtime-definitions/internal";
import type { FluidObject, IFluidLoadable } from "@fluidframework/core-interfaces";
import type { IDirectory } from "@fluidframework/map/internal";
import type { SharedObjectKind } from "@fluidframework/shared-object-base";
import type { ISharedObjectKind } from "@fluidframework/shared-object-base/internal";

import { compatibilityModeRuntimeOptions } from "./compatibilityConfiguration.js";
import type {
CompatibilityMode,
ContainerSchema,
IRootDataObject,
LoadableObjectKind,
Expand Down
6 changes: 0 additions & 6 deletions packages/framework/fluid-static/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,6 @@ import type { IEvent, IEventProvider, IFluidLoadable } from "@fluidframework/cor
import type { SharedObjectKind } from "@fluidframework/shared-object-base";
import type { ISharedObjectKind } from "@fluidframework/shared-object-base/internal";

/**
* Valid compatibility modes that may be specified when creating a DOProviderContainerRuntimeFactory.
* @public
*/
export type CompatibilityMode = "1" | "2";

/**
* A mapping of string identifiers to instantiated `DataObject`s or `SharedObject`s.
* @internal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

```ts

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

// (No @packageDocumentation comment for this package)

```
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
// @alpha
export const AllowTombstoneRequestHeaderKey = "allowTombstone";

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

// @alpha
export enum CompressionAlgorithms {
// (undocumented)
Expand Down Expand Up @@ -192,6 +195,7 @@ export interface IContainerRuntimeMetadata extends ICreateContainerMetadata, IGC
// @alpha
export interface IContainerRuntimeOptions {
readonly chunkSizeInBytes?: number;
readonly compatibilityMode?: CompatibilityMode;
readonly compressionOptions?: ICompressionRuntimeOptions;
// @deprecated
readonly enableGroupedBatching?: boolean;
Expand All @@ -201,6 +205,7 @@ export interface IContainerRuntimeOptions {
readonly gcOptions?: IGCRuntimeOptions;
readonly loadSequenceNumberVerification?: "close" | "log" | "bypass";
readonly maxBatchSizeInBytes?: number;
readonly protocolOptions?: IProtocolOptions;
// (undocumented)
readonly summaryOptions?: ISummaryRuntimeOptions;
}
Expand Down Expand Up @@ -354,6 +359,16 @@ export interface IOnDemandSummarizeOptions extends ISummarizeOptions {
readonly retryOnFailure?: boolean;
}

// @alpha
export interface IProtocolOptions {
readonly compressionOptions?: ICompressionRuntimeOptions;
readonly enableGCSweep?: true | undefined;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

gcOptions is a tricky case, since it contains a compatability/protocol related option. Ultimately, I decided to just include that specific option (enableGCSweep) in IProtocolOptions and leave the rest of in IContainerRuntimeOptions.

// @deprecated
readonly enableGroupedBatching?: boolean;
readonly enableRuntimeIdCompressor?: IdCompressorMode;
readonly explicitSchemaControl?: boolean;
}

// @alpha @deprecated
export interface IRefreshSummaryAckOptions {
readonly ackHandle: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

```ts

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

// (No @packageDocumentation comment for this package)

```
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

```ts

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

// (No @packageDocumentation comment for this package)

```
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,32 @@
* Licensed under the MIT License.
*/

import { FlushMode } from "@fluidframework/runtime-definitions/internal";

import {
CompressionAlgorithms,
type IContainerRuntimeOptionsInternal,
} from "@fluidframework/container-runtime/internal";
import { FlushMode } from "@fluidframework/runtime-definitions/internal";
} from "./containerRuntime.js";

/**
* Valid compatibility modes that may be specified when creating a DOProviderContainerRuntimeFactory.
* @public
*/
export type CompatibilityMode = "1" | "2";

import type { CompatibilityMode } from "./types.js";
/**
* The default compatibility mode is "1".
* This is based on our current cross-client compat policy, which states we must support the most recent
* adjacent public major version (currently 1.x).
* This value will need to be updated if our compat policy changes, or we release a major public version.
* @public
*/
export const defaultCompatibilityMode: CompatibilityMode = "1";

/**
* 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.
* @internal
*/
export const compatibilityModeRuntimeOptions: Record<
CompatibilityMode,
Expand All @@ -36,6 +51,8 @@ export const compatibilityModeRuntimeOptions: Record<
// Explicitly disable running Sweep in compat mode "1". Sweep is supported only in 2.x. So, when 1.x and 2.x
// clients are running in parallel, running sweep will fail 1.x clients.
gcOptions: { enableGCSweep: undefined },

disallowedVersions: [],
},
"2": {
// Explicit schema control explicitly makes the container incompatible with 1.x clients, to force their
Expand All @@ -47,5 +64,7 @@ export const compatibilityModeRuntimeOptions: Record<
// Explicitly disable running Sweep in compat mode "2". Although sweep is supported in 2.x, it is disabled by default.
// This setting explicitly disables it to be extra safe.
gcOptions: { enableGCSweep: undefined },

disallowedVersions: ["<2.0"],
},
};
112 changes: 107 additions & 5 deletions packages/runtime/container-runtime/src/containerRuntime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,11 @@ import {
getSummaryForDatastores,
wrapContext,
} from "./channelCollection.js";
import {
compatibilityModeRuntimeOptions,
defaultCompatibilityMode,
type CompatibilityMode,
} from "./compatibilityConfiguration.js";
import { IPerfSignalReport, ReportOpPerfTelemetry } from "./connectionTelemetry.js";
import { ContainerFluidHandleContext } from "./containerHandleContext.js";
import { channelToDataStore } from "./dataStore.js";
Expand Down Expand Up @@ -486,12 +491,71 @@ export interface ICompressionRuntimeOptions {
readonly compressionAlgorithm: CompressionAlgorithms;
}

/**
* TODO: TSDoc update
* Options for container runtime related to protocol/data format.
* All clients connected to the same container must be able to understand the same protocol.
* @legacy
* @alpha
*/
export interface IProtocolOptions {
/**
* Enable the IdCompressor in the runtime.
* @experimental Not ready for use.
*/
readonly enableRuntimeIdCompressor?: IdCompressorMode;

/**
* If enabled, the runtime will group messages within a batch into a single
* message to be sent to the service.
* The grouping and ungrouping of such messages is handled by the "OpGroupingManager".
*
* By default, the feature is enabled. This feature can only be disabled when compression is also disabled.
* @deprecated The ability to disable Grouped Batching is deprecated and will be removed in a future release. This feature is required for the proper functioning of the Fluid Framework.
*/
readonly enableGroupedBatching?: boolean;

/**
* When this property is set to true, it requires runtime to control is document schema properly through ops
* The benefit of this mode is that clients who do not understand schema will fail in predictable way, with predictable message,
* and will not attempt to limp along, which could cause data corruptions and crashes in random places.
* When this property is not set (or set to false), runtime operates in legacy mode, where new features (modifying document schema)
* are engaged as they become available, without giving legacy clients any chance to fail predictably.
*/
readonly explicitSchemaControl?: boolean;

/**
* Flag that if true, will enable the full Sweep Phase of garbage collection for this session,
* where Tombstoned objects are permanently deleted from the container.
*
* IMPORTANT: This only applies if this document is allowed to run Sweep Phase.
*
* Current default behavior is for Sweep Phase not to delete Tombstoned objects,
* but merely to prevent them from being loaded.
*/
readonly enableGCSweep?: true | undefined;

/**
* Enables the runtime to compress ops. See {@link ICompressionRuntimeOptions}.
*/
readonly compressionOptions?: ICompressionRuntimeOptions;
}

/**
* Options for container runtime.
* @legacy
* @alpha
*/
export interface IContainerRuntimeOptions {
/**
* TODO: TSDoc
*/
readonly protocolOptions?: IProtocolOptions;
/**
* TODO: TSDoc
*/
readonly compatibilityMode?: CompatibilityMode;

readonly summaryOptions?: ISummaryRuntimeOptions;
readonly gcOptions?: IGCRuntimeOptions;
/**
Expand Down Expand Up @@ -583,6 +647,11 @@ export interface IContainerRuntimeOptionsInternal extends IContainerRuntimeOptio
* In that case, batched messages will be sent individually (but still all at the same time).
*/
readonly enableGroupedBatching?: boolean;

/**
* TODO: TSDoc
*/
disallowedVersions?: string[];
}

/**
Expand Down Expand Up @@ -990,21 +1059,51 @@ export class ContainerRuntime

const mc = loggerToMonitoringContext(logger);

// While we transition towards using IProtocolOptions/CompatibilityMode, we will have to handle the different sources of container runtime options.
// Please note that the following logic will be much simpler after the overlapping properties are removed from IContainerRuntimeOptions.
const protocolOptions: IProtocolOptions = runtimeOptions.protocolOptions ?? {};
const compatibilityMode = runtimeOptions.compatibilityMode ?? defaultCompatibilityMode;
const defaultCompatibilityModeOptions: IContainerRuntimeOptionsInternal =
compatibilityModeRuntimeOptions[compatibilityMode];

// This loop does two things:
// 1. We check if any of the properties in protocolOptions are also defined in runtimeOptions. If they are not the same, we throw an error.
// 2. We set any undefined properties in runtimeOptions to the value in protocolOptions.
for (const key of Object.keys(protocolOptions)) {
if (protocolOptions[key] !== undefined) {
if (runtimeOptions[key] === undefined) {
runtimeOptions[key] = protocolOptions[key] as IContainerRuntimeOptions;
} else if (runtimeOptions[key] !== protocolOptions[key]) {
throw new Error(
`The ${key} property is defined differently in protocolOptions and runtimeOptions. Please remove one of them.`,
);
}
}
}

const {
summaryOptions = {},
gcOptions = {},
loadSequenceNumberVerification = "close",
flushMode = defaultFlushMode,
flushMode = defaultCompatibilityModeOptions.flushMode ?? defaultFlushMode,
compressionOptions = runtimeOptions.enableGroupedBatching === false
? disabledCompressionConfig // Compression must be disabled if Grouping is disabled
: defaultCompressionConfig,
maxBatchSizeInBytes = defaultMaxBatchSizeInBytes,
enableRuntimeIdCompressor,
enableRuntimeIdCompressor = defaultCompatibilityModeOptions.enableRuntimeIdCompressor,
chunkSizeInBytes = defaultChunkSizeInBytes,
enableGroupedBatching = true,
explicitSchemaControl = false,
enableGroupedBatching = defaultCompatibilityModeOptions.enableGroupedBatching ?? true,
explicitSchemaControl = defaultCompatibilityModeOptions.explicitSchemaControl ?? false,
// We currently dont' accept input for disallowedVersions, it is dictated by the compatibilityMode.
disallowedVersions = defaultCompatibilityModeOptions.disallowedVersions ?? [],
}: IContainerRuntimeOptionsInternal = runtimeOptions;

if (protocolOptions.enableGCSweep !== undefined) {
// gcSweep is a special case, since it is can only be true or undefined.
// Therefore, if it's defined we can assign the value in runtimeOptions.gcOptions.
gcOptions.enableGCSweep = protocolOptions.enableGCSweep;
}

const registry = new FluidDataStoreRegistry(registryEntries);

const tryFetchBlob = async <T>(blobName: string): Promise<T | undefined> => {
Expand Down Expand Up @@ -1180,7 +1279,7 @@ export class ContainerRuntime
compressionLz4,
idCompressorMode,
opGroupingEnabled: enableGroupedBatching,
disallowedVersions: [],
disallowedVersions,
},
(schema) => {
runtime.onSchemaChange(schema);
Expand All @@ -1206,6 +1305,9 @@ export class ContainerRuntime
enableRuntimeIdCompressor: enableRuntimeIdCompressor as "on" | "delayed",
enableGroupedBatching,
explicitSchemaControl,
protocolOptions,
compatibilityMode,
disallowedVersions,
};

const runtime = new containerRuntimeCtor(
Expand Down
5 changes: 5 additions & 0 deletions packages/runtime/container-runtime/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export {
CompressionAlgorithms,
RuntimeHeaderData,
disabledCompressionConfig,
IProtocolOptions,
} from "./containerRuntime.js";
export {
ContainerMessageType,
Expand Down Expand Up @@ -121,3 +122,7 @@ export {
IFluidDataStoreContextEvents,
} from "./dataStoreContext.js";
export { DataStoreContexts } from "./dataStoreContexts.js";
export {
CompatibilityMode,
compatibilityModeRuntimeOptions,
} from "./compatibilityConfiguration.js";
Loading
Loading