Skip to content

Commit fa499b0

Browse files
docs(tree): staged allowed types usage guide (#25581)
- adds a simple guide on rolling out new allowed types using the `staged` alpha API - removes the bulk of the `staged` API docs about rolling out types and links to the new guide instead --------- Co-authored-by: Craig Macomber (Microsoft) <42876482+CraigMacomber@users.noreply.github.com>
1 parent 6fb6526 commit fa499b0

File tree

3 files changed

+115
-94
lines changed

3 files changed

+115
-94
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
---
2+
title: Rolling Out New Allowed Types
3+
sidebar_position: 3
4+
---
5+
6+
# Rolling Out New Allowed Types
7+
8+
This guide shows how to safely add a new [TreeNodeSchema](../../../api/fluid-framework/treenodeschema-typealias) to an existing [AllowedTypes](../../../api/tree/allowedtypes-typealias) in a [SharedTree](../index.mdx) schema without breaking [forwards compatibility](./index.mdx#forwards-compatibility) with existing applications.
9+
10+
Directly adding a new node type to an existing field would cause old clients to be unable to load documents whose schema contain the new type, breaking them immediately.
11+
The following process stages changes to introduce a new allowed type in a non-breaking way.
12+
13+
## Step-by-Step Guide
14+
15+
The example below walks through the process of adding string support to a field that previously only supported numbers.
16+
The full code can be found in our [tests](https://github.com/microsoft/FluidFramework/blob/main/packages/dds/tree/src/test/simple-tree/api/stagedSchemaUpgrade.spec.ts).
17+
There are some differences because this guide describes the example as a real scenario involving code deployments while the full code example uses test utilities to emulate this.
18+
19+
We start with a schema that only supports numbers and initialize the tree with it:
20+
```typescript
21+
// Schema A: only number allowed
22+
const schemaA = SchemaFactoryAlpha.optional([SchemaFactoryAlpha.number]);
23+
24+
// initialize with schema A
25+
const configA = new TreeViewConfiguration({
26+
schema: schemaA,
27+
});
28+
const viewA = treeA.viewWith(configA);
29+
viewA.initialize(5);
30+
synchronizeTrees();
31+
```
32+
33+
### Step 1: Define and Stage the New Allowed Type
34+
35+
To add support for reading strings, we import the alpha APIs and use [`staged`](../../../api/fluid-framework/schemafactoryalpha-class#staged-property) to mark the new allowed type:
36+
37+
```typescript
38+
// Schema B: number or string (string is staged)
39+
const schemaB = SchemaFactoryAlpha.optional([
40+
SchemaFactoryAlpha.number,
41+
SchemaFactoryAlpha.staged(SchemaFactoryAlpha.string),
42+
]);
43+
```
44+
45+
### Step 2: Upgrade the Schema to Deploy Reading Support
46+
47+
We can now deploy `schemaB` to clients as a complete replacement to `schemaA`.
48+
At this point, new clients will be able to read documents containing strings and old documents will not.
49+
This is safe because no clients have the ability to insert strings into the document.
50+
51+
```typescript
52+
// view second tree with schema B
53+
const configB = new TreeViewConfiguration({
54+
schema: schemaB,
55+
});
56+
const viewB = treeB.viewWith(configB);
57+
// check that we can read the tree
58+
assert.deepEqual(viewB.root, 5);
59+
// upgrade to schema B: this is a no-op
60+
viewB.upgradeSchema();
61+
synchronizeTrees();
62+
63+
// check view A can read the document
64+
assert.deepEqual(viewA.root, 5);
65+
// check view B cannot write strings to the root
66+
assert.throws(() => {
67+
viewB.root = "test";
68+
});
69+
```
70+
71+
For this schema change, the schema does not need to be [upgraded](./index.mdx#schema-upgrade-process) because [`staged`](../../../api/fluid-framework/schemafactoryalpha-class#staged-property) allowed types are removed from stored schemas.
72+
Upgrading the schema would result in a noop.
73+
74+
### Step 3: Wait for Client Saturation
75+
76+
Ensure all clients in the deployment have the staged support before proceeding.
77+
Monitor the deployment to confirm coverage.
78+
79+
See the [staged rollouts](./index.mdx#staged-rollouts) section for more details on this step.
80+
81+
### Step 4: Upgrade to Allow Inserting the New Allowed Type
82+
83+
Once client saturation has been reached, remove the [`staged()`](../../../api/fluid-framework/schemafactoryalpha-class#staged-property) wrapper from the new allowed type and call `upgradeSchema` to make a change to the stored schema:
84+
85+
```typescript
86+
// Schema C: number or string, both fully allowed
87+
const schemaC = SchemaFactoryAlpha.optional([
88+
SchemaFactoryAlpha.number,
89+
SchemaFactoryAlpha.string,
90+
]);
91+
92+
// view third tree with schema C
93+
const configC = new TreeViewConfiguration({
94+
schema: schemaC,
95+
});
96+
const viewC = treeC.viewWith(configC);
97+
// upgrade to schema C and change the root to a string
98+
viewC.upgradeSchema();
99+
viewC.root = "test";
100+
synchronizeTrees();
101+
```
102+
103+
Any client code changes to add functionality for inserting the new allowed type can also be deployed in this step.
104+
105+
## See Also
106+
107+
- [`SchemaStaticsAlpha.staged()` API](../../../api/fluid-framework/schemafactoryalpha-class#staged-property) - API documentation
108+
- [Schema Definition](../schema-definition.mdx) - How to define schemas
109+
- [Node Types](../node-types.mdx) - Understanding different node types

docs/docs/data-structures/tree/schema-evolution/types-of-changes.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ class Note extends factory.object("Note", {
7979
}) {}
8080
```
8181

82-
To achieve [forwards compatibility](./index.mdx#forwards-compatibility), staged allowed types can be used to [roll out](./index.mdx#staged-rollouts) support for reading before writing, allowing older clients to read data created with new types (TODO add a guide on this process).
82+
To achieve [forwards compatibility](./index.mdx#forwards-compatibility), staged allowed types can be used to [roll out](./index.mdx#staged-rollouts) support for reading before writing, allowing older clients to read data created with new types.
83+
See [Rolling Out New Allowed Types](./allowed-types-rollout.mdx) for details.
8384

8485
## Incompatible Changes
8586

packages/dds/tree/src/simple-tree/api/schemaFactoryAlpha.ts

Lines changed: 4 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,9 @@ export interface SchemaStaticsAlpha {
7373
* Declares a staged type in a set of {@link AllowedTypes}.
7474
*
7575
* @remarks
76-
* Staged allowed types add support for loading documents which may or may not permit an allowed type in a location in a schema.
76+
* Staged allowed types add support for loading documents which may contain that type at the declared location.
7777
* This allows for an incremental rollout of a schema change to add a {@link TreeNodeSchema} to an {@link AllowedTypes} without breaking cross version collaboration.
78+
* A guide on this process can be found here: https://fluidframework.com/docs/data-structures/tree/schema-evolution/allowed-types-rollout
7879
*
7980
* Once enough clients have the type staged (and thus can read documents which allow it), documents can start being created and upgraded to allow the staged type.
8081
* This is done by deploying a new version of the app which removes the `staged` wrapper around the allowed type in the the schema definition.
@@ -85,107 +86,17 @@ export interface SchemaStaticsAlpha {
8586
* 1. {@link TreeView.initialize} will omit the staged allowed type from the newly created stored schema.
8687
* 2. {@link TreeView.upgradeSchema} will omit the staged allowed type from the the upgraded stored schema.
8788
* 3. When evaluating {@link TreeView.compatibility}, it will be viewable even if the staged allowed type is not present in the stored schema's corresponding allowed types.
88-
* 4. Because of the above, it is possible to get errors when inserting content which uses the staged allowed type when inserting the content into a tree who's stored schema does not permit it.
89+
* 4. Because of the above, it is possible to get errors when inserting content which uses the staged allowed type into a tree whose stored schema does not permit it.
8990
*
9091
* Currently, `staged` is not supported in the recursive type APIs: this is a known limitation which future versions of the API will address.
9192
*
9293
* @example
93-
* Suppose you have a schema which has a field that allows some type `A`, but you want to add support for type `B`.
94+
* A full code example of the schema migration process can be found in our {@link https://github.com/microsoft/FluidFramework/blob/main/packages/dds/tree/src/test/simple-tree/api/stagedSchemaUpgrade.spec.ts | tests}.
9495
*
95-
* The first change is to used to mark the new type as staged, replacing `A` in the schema with `[A, SchemaStaticsAlpha.staged(B)]`.
96-
* Once this is done, and any code which reads contents from documents is updated to handle any `B` content that may be present, this version of the code can be deployed.
97-
*
98-
* Once all users have the above changes, the schema can be updated again to `[A, B]`, and the app can be updated to allow creating of `B` content.
99-
* This updated version of the app will need to call {@link TreeView.upgradeSchema} when opening documents created by earlier versions.
100-
*
101-
* Adding a `B` schema as an option in the root could look like this:
102-
* ```typescript
103-
* const factory = new SchemaFactoryAlpha("test");
104-
* class A extends factory.objectAlpha("A", {}) {}
105-
* class B extends factory.objectAlpha("B", {}) {}
106-
*
107-
* // Does not support B
108-
* const configBefore = new TreeViewConfigurationAlpha({
109-
* schema: A,
110-
* });
111-
*
112-
* // Supports documents with or without B
113-
* const configStaged = new TreeViewConfigurationAlpha({
114-
* // Adds staged support for B.
115-
* // Currently this requires wrapping the root field with `SchemaFactoryAlpha.required`:
116-
* // this is normally implicitly included, but is currently required while the "staged" APIs are `@alpha`.
117-
* schema: SchemaFactoryAlpha.required([A, SchemaFactoryAlpha.staged(B)]),
118-
* });
119-
*
120-
* // Only supports documents with A and B: can be used to upgrade schema to add B.
121-
* const configAfter = new TreeViewConfigurationAlpha({
122-
* schema: [A, B],
123-
* });
124-
* ```
125-
* @example
126-
* Below is a full example of how the schema migration process works.
127-
* This can also be found in our {@link https://github.com/microsoft/FluidFramework/blob/main/packages/dds/tree/src/test/simple-tree/api/stagedSchemaUpgrade.spec.ts | tests}.
128-
* ```typescript
129-
* // Schema A: only number allowed
130-
* const schemaA = SchemaFactoryAlpha.optional([SchemaFactoryAlpha.number]);
131-
*
132-
* // Schema B: number or string (string is staged)
133-
* const schemaB = SchemaFactoryAlpha.optional([
134-
* SchemaFactoryAlpha.number,
135-
* SchemaFactoryAlpha.staged(SchemaFactoryAlpha.string),
136-
* ]);
137-
*
138-
* // Schema C: number or string, both fully allowed
139-
* const schemaC = SchemaFactoryAlpha.optional([
140-
* SchemaFactoryAlpha.number,
141-
* SchemaFactoryAlpha.string,
142-
* ]);
143-
*
144-
* // Initialize with schema A.
145-
* const configA = new TreeViewConfiguration({
146-
* schema: schemaA,
147-
* });
148-
* const viewA = treeA.viewWith(configA);
149-
* viewA.initialize(5);
150-
*
151-
* // Since we are running all the different versions of the app in the same process making changes synchronously,
152-
* // an explicit flush is needed to make them available to each other.
153-
* synchronizeTrees();
154-
*
155-
* assert.deepEqual(viewA.root, 5);
156-
*
157-
* // View the same document with a second tree using schema B.
158-
* const configB = new TreeViewConfiguration({
159-
* schema: schemaB,
160-
* });
161-
* const viewB = treeB.viewWith(configB);
162-
* // B cannot write strings to the root.
163-
* assert.throws(() => (viewB.root = "test"));
164-
*
165-
* // View the same document with a third tree using schema C.
166-
* const configC = new TreeViewConfiguration({
167-
* schema: schemaC,
168-
* });
169-
* const viewC = treeC.viewWith(configC);
170-
* // Upgrade to schema C
171-
* viewC.upgradeSchema();
172-
* // Use the newly enabled schema.
173-
* viewC.root = "test";
174-
*
175-
* synchronizeTrees();
176-
*
177-
* // View A is now incompatible with the stored schema:
178-
* assert.equal(viewA.compatibility.canView, false);
179-
*
180-
* // View B can still read the document, and now sees the string root which relies on the staged schema.
181-
* assert.deepEqual(viewB.root, "test");
182-
* ```
18396
* @privateRemarks
18497
* TODO:#44317 staged allowed types rely on schema validation of stored schema to output errors, these errors are not very
18598
* user friendly and should be improved, particularly in the case of staged allowed types
18699
*
187-
* TODO: the example above does not work tell in intellisense: its formatted to work onm the website. We should find a solution that works well for both.
188-
*
189100
* TODO: AB#45711: Update the docs above when recursive type support is added.
190101
*/
191102
staged: <const T extends LazyItem<TreeNodeSchema>>(

0 commit comments

Comments
 (0)