Skip to content

Commit 200548e

Browse files
fix(tree2): pass and leverage revision metadata in invert (#18299)
1 parent 5ca91dc commit 200548e

File tree

12 files changed

+110
-35
lines changed

12 files changed

+110
-35
lines changed

experimental/dds/tree2/src/feature-libraries/modular-schema/fieldChangeHandler.ts

+2
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ export interface FieldChangeRebaser<TChangeset> {
8686
invertChild: NodeChangeInverter,
8787
genId: IdAllocator,
8888
crossFieldManager: CrossFieldManager,
89+
revisionMetadata: RevisionMetadataSource,
8990
): TChangeset;
9091

9192
/**
@@ -96,6 +97,7 @@ export interface FieldChangeRebaser<TChangeset> {
9697
originalRevision: RevisionTag | undefined,
9798
genId: IdAllocator,
9899
crossFieldManager: CrossFieldManager,
100+
revisionMetadata: RevisionMetadataSource,
99101
): TChangeset;
100102

101103
/**

experimental/dds/tree2/src/feature-libraries/modular-schema/modularChangeFamily.ts

+10
Original file line numberDiff line numberDiff line change
@@ -299,10 +299,14 @@ export class ModularChangeFamily
299299
const genId: IdAllocator = idAllocatorFromState(idState);
300300
const crossFieldTable = newCrossFieldTable<InvertData>();
301301

302+
const { revInfos } = getRevInfoFromTaggedChanges([change]);
303+
const revisionMetadata = revisionMetadataSourceFromInfo(revInfos);
304+
302305
const invertedFields = this.invertFieldMap(
303306
tagChange(change.change.fieldChanges, change.revision),
304307
genId,
305308
crossFieldTable,
309+
revisionMetadata,
306310
);
307311

308312
if (crossFieldTable.invalidatedFields.size > 0) {
@@ -317,6 +321,7 @@ export class ModularChangeFamily
317321
originalRevision,
318322
genId,
319323
newCrossFieldManager(crossFieldTable),
324+
revisionMetadata,
320325
);
321326
fieldChange.change = brand(amendedChange);
322327
}
@@ -345,6 +350,7 @@ export class ModularChangeFamily
345350
changes: TaggedChange<FieldChangeMap>,
346351
genId: IdAllocator,
347352
crossFieldTable: CrossFieldTable<InvertData>,
353+
revisionMetadata: RevisionMetadataSource,
348354
): FieldChangeMap {
349355
const invertedFields: FieldChangeMap = new Map();
350356

@@ -362,9 +368,11 @@ export class ModularChangeFamily
362368
{ revision, change: childChanges },
363369
genId,
364370
crossFieldTable,
371+
revisionMetadata,
365372
),
366373
genId,
367374
manager,
375+
revisionMetadata,
368376
);
369377

370378
const invertedFieldChange: FieldChange = {
@@ -389,6 +397,7 @@ export class ModularChangeFamily
389397
change: TaggedChange<NodeChangeset>,
390398
genId: IdAllocator,
391399
crossFieldTable: CrossFieldTable<InvertData>,
400+
revisionMetadata: RevisionMetadataSource,
392401
): NodeChangeset {
393402
const inverse: NodeChangeset = {};
394403

@@ -397,6 +406,7 @@ export class ModularChangeFamily
397406
{ ...change, change: change.change.fieldChanges },
398407
genId,
399408
crossFieldTable,
409+
revisionMetadata,
400410
);
401411
}
402412

experimental/dds/tree2/src/feature-libraries/sequence-field/invert.ts

+31-11
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import { assert, unreachableCase } from "@fluidframework/core-utils";
77
import { ChangeAtomId, RevisionTag, TaggedChange } from "../../core";
88
import { IdAllocator, fail } from "../../util";
9-
import { CrossFieldManager, CrossFieldTarget } from "../modular-schema";
9+
import { CrossFieldManager, CrossFieldTarget, RevisionMetadataSource } from "../modular-schema";
1010
import {
1111
Changeset,
1212
Mark,
@@ -22,7 +22,7 @@ import { MarkListFactory } from "./markListFactory";
2222
import {
2323
areInputCellsEmpty,
2424
extractMarkEffect,
25-
getDetachCellId,
25+
getDetachOutputId,
2626
getEndpoint,
2727
getOutputCellId,
2828
isAttach,
@@ -48,12 +48,14 @@ export function invert<TNodeChange>(
4848
invertChild: NodeChangeInverter<TNodeChange>,
4949
genId: IdAllocator,
5050
crossFieldManager: CrossFieldManager,
51+
revisionMetadata: RevisionMetadataSource,
5152
): Changeset<TNodeChange> {
5253
return invertMarkList(
5354
change.change,
5455
change.revision,
5556
invertChild,
5657
crossFieldManager as CrossFieldManager<TNodeChange>,
58+
revisionMetadata,
5759
);
5860
}
5961

@@ -75,11 +77,18 @@ function invertMarkList<TNodeChange>(
7577
revision: RevisionTag | undefined,
7678
invertChild: NodeChangeInverter<TNodeChange>,
7779
crossFieldManager: CrossFieldManager<TNodeChange>,
80+
revisionMetadata: RevisionMetadataSource,
7881
): MarkList<TNodeChange> {
7982
const inverseMarkList = new MarkListFactory<TNodeChange>();
8083

8184
for (const mark of markList) {
82-
const inverseMarks = invertMark(mark, revision, invertChild, crossFieldManager);
85+
const inverseMarks = invertMark(
86+
mark,
87+
revision,
88+
invertChild,
89+
crossFieldManager,
90+
revisionMetadata,
91+
);
8392
inverseMarkList.push(...inverseMarks);
8493
}
8594

@@ -91,6 +100,7 @@ function invertMark<TNodeChange>(
91100
revision: RevisionTag | undefined,
92101
invertChild: NodeChangeInverter<TNodeChange>,
93102
crossFieldManager: CrossFieldManager<TNodeChange>,
103+
revisionMetadata: RevisionMetadataSource,
94104
): Mark<TNodeChange>[] {
95105
const type = mark.type;
96106
switch (type) {
@@ -105,13 +115,11 @@ function invertMark<TNodeChange>(
105115
assert(revision !== undefined, 0x5a1 /* Unable to revert to undefined revision */);
106116
const markRevision = mark.revision ?? revision;
107117
if (mark.cellId === undefined) {
118+
const outputId = getDetachOutputId(mark, markRevision, revisionMetadata);
108119
const inverse = withNodeChange(
109120
{
110121
type: "Insert",
111-
cellId: mark.detachIdOverride ?? {
112-
revision: markRevision,
113-
localId: mark.id,
114-
},
122+
cellId: outputId,
115123
count: mark.count,
116124
},
117125
invertNodeChange(mark.changes, invertChild),
@@ -164,10 +172,10 @@ function invertMark<TNodeChange>(
164172
);
165173
}
166174

167-
const cellId = getDetachCellId(
175+
const cellId = getDetachOutputId(
168176
mark,
169177
mark.revision ?? revision ?? fail("Revision must be defined"),
170-
undefined,
178+
revisionMetadata,
171179
) ?? {
172180
revision: mark.revision ?? revision ?? fail("Revision must be defined"),
173181
localId: mark.id,
@@ -223,8 +231,20 @@ function invertMark<TNodeChange>(
223231
changes: mark.changes,
224232
...mark.detach,
225233
};
226-
const attachInverses = invertMark(attach, revision, invertChild, crossFieldManager);
227-
const detachInverses = invertMark(detach, revision, invertChild, crossFieldManager);
234+
const attachInverses = invertMark(
235+
attach,
236+
revision,
237+
invertChild,
238+
crossFieldManager,
239+
revisionMetadata,
240+
);
241+
const detachInverses = invertMark(
242+
detach,
243+
revision,
244+
invertChild,
245+
crossFieldManager,
246+
revisionMetadata,
247+
);
228248

229249
if (detachInverses.length === 0) {
230250
return attachInverses;

experimental/dds/tree2/src/feature-libraries/sequence-field/rebase.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727
cloneCellId,
2828
areOutputCellsEmpty,
2929
isNewAttach,
30-
getDetachCellId,
30+
getDetachOutputId,
3131
getInputCellId,
3232
isTransientEffect,
3333
getOutputCellId,
@@ -380,7 +380,7 @@ function rebaseMarkIgnoreChild<TNodeChange>(
380380
let rebasedMark = currMark;
381381
if (markEmptiesCells(baseMark)) {
382382
assert(isDetach(baseMark), 0x70b /* Only detach marks should empty cells */);
383-
const baseCellId = getDetachCellId(baseMark, baseRevision, metadata);
383+
const baseCellId = getDetachOutputId(baseMark, baseRevision, metadata);
384384

385385
// TODO: Should also check if this is a transient move source
386386
if (isMoveSource(baseMark)) {

experimental/dds/tree2/src/feature-libraries/sequence-field/utils.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -142,23 +142,23 @@ export function getOutputCellId(
142142
): CellId | undefined {
143143
if (markEmptiesCells(mark)) {
144144
assert(isDetach(mark), 0x750 /* Only detaches can empty cells */);
145-
return getDetachCellId(mark, revision, metadata);
145+
return getDetachOutputId(mark, revision, metadata);
146146
} else if (markFillsCells(mark)) {
147147
return undefined;
148148
} else if (isTransientEffect(mark)) {
149-
return getDetachCellId(mark.detach, revision, metadata);
149+
return getDetachOutputId(mark.detach, revision, metadata);
150150
}
151151

152152
return getInputCellId(mark, revision, metadata);
153153
}
154154

155-
export function getDetachCellId(
155+
export function getDetachOutputId(
156156
mark: Detach,
157157
revision: RevisionTag | undefined,
158158
metadata: RevisionMetadataSource | undefined,
159-
): CellId {
159+
): ChangeAtomId {
160160
return (
161-
getOverrideCellId(mark) ?? {
161+
getOverrideDetachId(mark) ?? {
162162
revision: getIntentionIfMetadataProvided(mark.revision ?? revision, metadata),
163163
localId: mark.id,
164164
}
@@ -172,7 +172,7 @@ function getIntentionIfMetadataProvided(
172172
return metadata === undefined ? revision : getIntention(revision, metadata);
173173
}
174174

175-
function getOverrideCellId(mark: Detach): CellId | undefined {
175+
function getOverrideDetachId(mark: Detach): ChangeAtomId | undefined {
176176
return mark.type !== "MoveOut" && mark.detachIdOverride !== undefined
177177
? mark.detachIdOverride
178178
: undefined;
@@ -470,7 +470,7 @@ function tryMergeEffects(
470470
if (
471471
isDetach(lhs) &&
472472
isDetach(rhs) &&
473-
!areMergeableCellIds(getOverrideCellId(lhs), lhsCount, getOverrideCellId(rhs))
473+
!areMergeableCellIds(getOverrideDetachId(lhs), lhsCount, getOverrideDetachId(rhs))
474474
) {
475475
return undefined;
476476
}

experimental/dds/tree2/src/test/feature-libraries/default-field-kinds/defaultFieldKinds.spec.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -175,11 +175,13 @@ describe("defaultFieldKinds", () => {
175175
return nodeChange2;
176176
};
177177

178+
const taggedChange = { revision: mintRevisionTag(), change: change1WithChildChange };
178179
const inverted = fieldHandler.rebaser.invert(
179-
{ revision: mintRevisionTag(), change: change1WithChildChange },
180+
taggedChange,
180181
childInverter,
181182
fakeIdAllocator,
182183
failCrossFieldManager,
184+
defaultRevisionMetadataFromChanges([taggedChange]),
183185
);
184186

185187
assert.deepEqual(inverted.childChanges, [["self", nodeChange2]]);

experimental/dds/tree2/src/test/feature-libraries/default-field-kinds/optionalChangeRebaser.spec.ts

+1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ function invert(change: TaggedChange<OptionalChangeset>): OptionalChangeset {
114114
// Optional fields should not generate IDs during invert
115115
fakeIdAllocator,
116116
failCrossFieldManager,
117+
defaultRevisionMetadataFromChanges([change]),
117118
);
118119
}
119120

experimental/dds/tree2/src/test/feature-libraries/default-field-kinds/optionalField.spec.ts

+4
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ describe("optionalField", () => {
231231
childInverter,
232232
fakeIdAllocator,
233233
failCrossFieldManager,
234+
defaultRevisionMetadataFromChanges([change1]),
234235
),
235236
expected,
236237
);
@@ -296,6 +297,7 @@ describe("optionalField", () => {
296297
() => assert.fail("Should not need to invert children"),
297298
fakeIdAllocator,
298299
failCrossFieldManager,
300+
defaultRevisionMetadataFromChanges([deletion]),
299301
),
300302
tag2,
301303
tag1,
@@ -561,6 +563,7 @@ describe("optionalField", () => {
561563
() => assert.fail("Should not need to invert children"),
562564
fakeIdAllocator,
563565
failCrossFieldManager,
566+
defaultRevisionMetadataFromChanges([clear]),
564567
);
565568
const actual = Array.from(
566569
optionalChangeHandler.relevantRemovedTrees(restore, failingDelegate),
@@ -621,6 +624,7 @@ describe("optionalField", () => {
621624
() => assert.fail("Should not need to invert children"),
622625
fakeIdAllocator,
623626
failCrossFieldManager,
627+
defaultRevisionMetadataFromChanges([clear]),
624628
),
625629
mintRevisionTag(),
626630
);

experimental/dds/tree2/src/test/feature-libraries/modular-schema/genericFieldKind.spec.ts

+11-3
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@ import {
2121
deltaForSet,
2222
} from "../../../core";
2323
import { fakeIdAllocator, brand } from "../../../util";
24-
import { EncodingTestData, makeEncodingTestSuite } from "../../utils";
24+
import {
25+
EncodingTestData,
26+
defaultRevisionMetadataFromChanges,
27+
makeEncodingTestSuite,
28+
} from "../../utils";
2529
import { IJsonCodec } from "../../../codec";
2630
import { singleJsonCursor } from "../../../domains";
2731
import { ValueChangeset, valueField, valueHandler } from "./basicRebasers";
@@ -85,11 +89,13 @@ const childComposer = (nodeChanges: TaggedChange<NodeChangeset>[]): NodeChangese
8589

8690
const childInverter = (nodeChange: NodeChangeset): NodeChangeset => {
8791
const valueChange = valueChangeFromNodeChange(nodeChange);
92+
const taggedChange = makeAnonChange(valueChange);
8893
const inverse = valueHandler.rebaser.invert(
89-
makeAnonChange(valueChange),
94+
taggedChange,
9095
unexpectedDelegate,
9196
fakeIdAllocator,
9297
crossFieldManager,
98+
defaultRevisionMetadataFromChanges([taggedChange]),
9399
);
94100
return nodeChangeFromValueChange(inverse);
95101
};
@@ -348,11 +354,13 @@ describe("Generic FieldKind", () => {
348354
nodeChange: nodeChange2To1,
349355
},
350356
];
357+
const taggedChange = makeAnonChange(forward);
351358
const actual = genericFieldKind.changeHandler.rebaser.invert(
352-
makeAnonChange(forward),
359+
taggedChange,
353360
childInverter,
354361
fakeIdAllocator,
355362
crossFieldManager,
363+
defaultRevisionMetadataFromChanges([taggedChange]),
356364
);
357365
assert.deepEqual(actual, expected);
358366
});

0 commit comments

Comments
 (0)