Skip to content

Commit 0c306cf

Browse files
scottn12ChumpChief
andauthored
improvement(runtime): Add fail fast mechanism for runtime options that require explicitSchemaControl (microsoft#25430)
## Description This PR adds a fail fast mechanism that will throw a `UsageError` if a runtime option that requires `explicitSchemaControl` is enabled without enabling `explicitSchemaControl` as well. To avoid disruption for existing customers, we will only apply this to runtime options that were added after `explicitSchemaControl` was introduced. Currently, this only applies to `createBlobPayloadPending`, but going forward FF devs will be required to add new runtime options to `runtimeOptionKeysThatRequireExplicitSchemaControl` (the build will not compile otherwise). This will enforce this fail fast mechanism for any new runtime options going forward. ## Misc [AB#48150](https://dev.azure.com/fluidframework/235294da-091d-4c29-84fc-cdfc3d90890b/_workitems/edit/48150) --------- Co-authored-by: Matt Rakow <ChumpChief@users.noreply.github.com>
1 parent d9c5ef8 commit 0c306cf

File tree

6 files changed

+109
-1
lines changed

6 files changed

+109
-1
lines changed

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

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,49 @@ export type RuntimeOptionsAffectingDocSchema = Omit<
4545
| "summaryOptions"
4646
>;
4747

48+
/**
49+
* Subset of {@link RuntimeOptionsAffectingDocSchema} which existed prior to the introduction of explicitSchemaControl.
50+
*
51+
* @remarks
52+
* Runtime options that affect document schema should generally require explicitSchemaControl to be enabled.
53+
* However, to prevent disruption to existing customers, options that existed prior to explicitSchemaControl
54+
* do not explicity require explicitSchemaControl to be enabled. Do not add new options to this list.
55+
*/
56+
type RuntimeOptionKeysPredatingExplicitSchemaControl = keyof Pick<
57+
RuntimeOptionsAffectingDocSchema,
58+
| "explicitSchemaControl"
59+
| "compressionOptions"
60+
| "enableRuntimeIdCompressor"
61+
| "flushMode"
62+
| "gcOptions"
63+
| "enableGroupedBatching"
64+
>;
65+
66+
/**
67+
* List of keys of {@link RuntimeOptionsAffectingDocSchema} which existed prior to the introduction of explicitSchemaControl.
68+
*
69+
* @remarks
70+
* Runtime options that affect document schema should generally require explicitSchemaControl to be enabled.
71+
* However, to prevent disruption to existing customers, options that existed prior to explicitSchemaControl
72+
* do not explicity require explicitSchemaControl to be enabled. Do not add new keys to this list.
73+
*/
74+
const keysOfOptionsPredatingExplicitSchemaControl = new Set([
75+
"explicitSchemaControl",
76+
"compressionOptions",
77+
"enableRuntimeIdCompressor",
78+
"flushMode",
79+
"gcOptions",
80+
"enableGroupedBatching",
81+
]) satisfies Set<RuntimeOptionKeysPredatingExplicitSchemaControl>;
82+
83+
/**
84+
* Subset of {@link RuntimeOptionsAffectingDocSchema} which require explicitSchemaControl to be enabled.
85+
*/
86+
export type RuntimeOptionKeysThatRequireExplicitSchemaControl = keyof Omit<
87+
RuntimeOptionsAffectingDocSchema,
88+
RuntimeOptionKeysPredatingExplicitSchemaControl
89+
>;
90+
4891
/**
4992
* Mapping of RuntimeOptionsAffectingDocSchema to their compatibility related configs.
5093
*
@@ -113,6 +156,19 @@ const runtimeOptionsAffectingDocSchemaConfigMap = {
113156
},
114157
} as const satisfies ConfigMap<RuntimeOptionsAffectingDocSchema>;
115158

159+
/**
160+
* Keys of {@link ContainerRuntimeOptionsInternal} that require explicitSchemaControl to be enabled.
161+
*/
162+
export const runtimeOptionKeysThatRequireExplicitSchemaControl = (
163+
Object.keys(
164+
runtimeOptionsAffectingDocSchemaConfigMap,
165+
) as (keyof RuntimeOptionsAffectingDocSchema)[]
166+
).filter((key) => {
167+
return !keysOfOptionsPredatingExplicitSchemaControl.has(
168+
key as RuntimeOptionKeysPredatingExplicitSchemaControl,
169+
);
170+
}) as RuntimeOptionKeysThatRequireExplicitSchemaControl[];
171+
116172
const runtimeOptionsAffectingDocSchemaConfigValidationMap = {
117173
enableGroupedBatching: configValueToMinVersionForCollab([
118174
[false, "1.0.0"],

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,8 @@ import {
195195
getMinVersionForCollabDefaults,
196196
type RuntimeOptionsAffectingDocSchema,
197197
validateRuntimeOptions,
198+
runtimeOptionKeysThatRequireExplicitSchemaControl,
199+
type RuntimeOptionKeysThatRequireExplicitSchemaControl,
198200
} from "./containerCompatibility.js";
199201
import { ContainerFluidHandleContext } from "./containerHandleContext.js";
200202
import { channelToDataStore } from "./dataStore.js";
@@ -947,6 +949,19 @@ export class ContainerRuntime
947949
createBlobPayloadPending = defaultConfigs.createBlobPayloadPending,
948950
}: IContainerRuntimeOptionsInternal = runtimeOptions;
949951

952+
// If explicitSchemaControl is off, ensure that options which require explicitSchemaControl are not enabled.
953+
if (!explicitSchemaControl) {
954+
const disallowedKeys = Object.keys(runtimeOptions).filter(
955+
(key) =>
956+
runtimeOptionKeysThatRequireExplicitSchemaControl.includes(
957+
key as RuntimeOptionKeysThatRequireExplicitSchemaControl,
958+
) && runtimeOptions[key] !== undefined,
959+
);
960+
if (disallowedKeys.length > 0) {
961+
throw new UsageError(`explicitSchemaControl must be enabled to use ${disallowedKeys}`);
962+
}
963+
}
964+
950965
// The logic for enableRuntimeIdCompressor is a bit different. Since `undefined` represents a logical state (off)
951966
// we need to check it's explicitly set in runtimeOptions. If so, we should use that value even if it's undefined.
952967
const enableRuntimeIdCompressor =

packages/runtime/container-runtime/src/test/containerRuntime.spec.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3739,6 +3739,29 @@ describe("Runtime", () => {
37393739
"Container should throw when op compression is on and op grouping is off",
37403740
);
37413741
});
3742+
3743+
it("Throws when createBlobPayloadPending is on and explicitSchemaControl is not enabled", async () => {
3744+
await assert.rejects(
3745+
async () =>
3746+
ContainerRuntime.loadRuntime({
3747+
context: getMockContext() as IContainerContext,
3748+
registryEntries: [],
3749+
existing: false,
3750+
runtimeOptions: {
3751+
createBlobPayloadPending: true,
3752+
},
3753+
provideEntryPoint: mockProvideEntryPoint,
3754+
}),
3755+
(error: IErrorBase) => {
3756+
return (
3757+
error.errorType === ContainerErrorTypes.usageError &&
3758+
error.message ===
3759+
"explicitSchemaControl must be enabled to use createBlobPayloadPending"
3760+
);
3761+
},
3762+
"Container should throw when createBlobPayloadPending is on and explicitSchemaControl is not enabled",
3763+
);
3764+
});
37423765
});
37433766
});
37443767

@@ -4134,7 +4157,7 @@ describe("Runtime", () => {
41344157
existing: false,
41354158
// We would normally throw (since `createBlobPayloadPending` requires 2.40), but since we did
41364159
// not explicity set minVersionForCollab, we allow it.
4137-
runtimeOptions: { createBlobPayloadPending: true },
4160+
runtimeOptions: { createBlobPayloadPending: true, explicitSchemaControl: true },
41384161
provideEntryPoint: mockProvideEntryPoint,
41394162
});
41404163
});

packages/test/local-server-stress-tests/src/stressDataObject.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,7 @@ export const createRuntimeFactory = (): IRuntimeFactory => {
355355
},
356356
enableRuntimeIdCompressor: "on",
357357
createBlobPayloadPending: true,
358+
explicitSchemaControl: true,
358359
};
359360

360361
return {

packages/test/test-end-to-end-tests/src/test/blobsisAttached.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ for (const createBlobPayloadPending of [undefined, true] as const) {
5252
fluidDataObjectType: DataObjectFactoryType.Test,
5353
registry,
5454
runtimeOptions: {
55+
explicitSchemaControl: true,
5556
createBlobPayloadPending,
5657
},
5758
};

packages/test/test-service-load/src/optionsMatrix.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,18 @@ export function generateRuntimeOptions(
141141
}
142142
});
143143

144+
// Override explicitSchemaControl to enabled if createBlobPayloadPending is enabled
145+
pairwiseOptions.map((options) => {
146+
if (options.createBlobPayloadPending) {
147+
(
148+
options as {
149+
// Remove readonly modifier to allow overriding
150+
-readonly [P in keyof ContainerRuntimeOptionsInternal]: ContainerRuntimeOptionsInternal[P];
151+
}
152+
).explicitSchemaControl = true;
153+
}
154+
});
155+
144156
return pairwiseOptions;
145157
}
146158

0 commit comments

Comments
 (0)