Skip to content

Commit a39b87a

Browse files
committedMar 7, 2025
update all the stuffs
1 parent 133c963 commit a39b87a

File tree

11 files changed

+163
-36
lines changed

11 files changed

+163
-36
lines changed
 

‎package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"prepare": "export TURBO_FORCE=true; turbo run build:pkg; pnpm run prepare:types;",
1313
"prepare:types": "export TURBO_FORCE=true; tsc --build --force; turbo run build:glint;",
1414
"release": "./release/index.ts",
15-
"build": "turbo _build --log-order=stream --filter=./packages/* --concurrency=10;",
15+
"build": "turbo run build:pkg --log-order=stream --concurrency=10;",
1616
"sync": "pnpm --filter './packages/*' run --parallel --if-present sync",
1717
"build:docs": "mkdir -p packages/-ember-data/dist && cd ./docs-generator && node ./compile-docs.js",
1818
"lint:tests": "turbo --log-order=stream lint --filter=./tests/* --continue --concurrency=10",

‎packages/build-config/src/debugging.ts

+8
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,11 @@ export const LOG_INSTANCE_CACHE: boolean = false;
111111
* @public
112112
*/
113113
export const LOG_METRIC_COUNTS: boolean = false;
114+
/**
115+
* Helps when debugging causes of a change notification
116+
* when processing an update to a hasMany relationship.
117+
*
118+
* @property {boolean} DEBUG_RELATIONSHIP_NOTIFICATIONS
119+
* @public
120+
*/
121+
export const DEBUG_RELATIONSHIP_NOTIFICATIONS: boolean = false;

‎packages/graph/src/-private/-diff.ts

+91-11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { deprecate } from '@ember/debug';
22

3+
import { DEBUG_RELATIONSHIP_NOTIFICATIONS } from '@warp-drive/build-config/debugging';
34
import {
45
DEPRECATE_NON_UNIQUE_PAYLOADS,
56
DEPRECATE_RELATIONSHIP_REMOTE_UPDATE_CLEARING_LOCAL_STATE,
@@ -163,17 +164,29 @@ function _compare<T>(
163164
const finalLength = finalState.length;
164165
const prevLength = prevState.length;
165166
const iterationLength = Math.max(finalLength, prevLength);
166-
const equalLength = finalLength === prevLength;
167+
const equalLength = priorLocalState ? finalLength === priorLocalState.length : finalLength === prevLength;
167168
let remoteOrderChanged = finalSet.size !== prevSet.size;
168169
let changed: boolean = priorLocalState ? finalSet.size !== priorLocalState.length : remoteOrderChanged;
169170
const added = new Set<T>();
170171
const removed = new Set<T>();
171172
const priorLocalLength = priorLocalState?.length ?? 0;
172173

173-
changed &&
174-
console.log(
175-
`changed because ${priorLocalState ? 'finalSet.size !== priorLocalState.length' : 'finalSet.size !== prevSet.size'}`
176-
);
174+
if (DEBUG_RELATIONSHIP_NOTIFICATIONS) {
175+
if (changed) {
176+
// console.log({
177+
// priorState: priorLocalState?.slice(),
178+
// finalState: finalState.slice(),
179+
// prevState: prevState.slice(),
180+
// });
181+
}
182+
183+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
184+
changed &&
185+
// eslint-disable-next-line no-console
186+
console.log(
187+
`changed because ${priorLocalState ? 'finalSet.size !== priorLocalState.length' : 'finalSet.size !== prevSet.size'}`
188+
);
189+
}
177190

178191
for (let i = 0; i < iterationLength; i++) {
179192
let member: T | undefined;
@@ -186,7 +199,21 @@ function _compare<T>(
186199
if (i < priorLocalLength) {
187200
const priorLocalMember = priorLocalState![i];
188201
if (priorLocalMember !== member) {
189-
!changed && console.log(`changed because priorLocalMember !== member && !prevSet.has(member)`);
202+
if (DEBUG_RELATIONSHIP_NOTIFICATIONS) {
203+
if (!changed) {
204+
// console.log({
205+
// priorLocalMember,
206+
// member,
207+
// i,
208+
// priorState: priorLocalState?.slice(),
209+
// finalState: finalState.slice(),
210+
// prevState: prevState.slice(),
211+
// });
212+
}
213+
214+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions, no-console
215+
!changed && console.log(`changed because priorLocalMember !== member && !prevSet.has(member)`);
216+
}
190217
changed = true;
191218
}
192219
}
@@ -220,11 +247,19 @@ function _compare<T>(
220247
if (i < priorLocalLength) {
221248
const priorLocalMember = priorLocalState![i];
222249
if (priorLocalMember !== member) {
223-
!changed && console.log(`changed because priorLocalMember !== member && member !== prevMember`);
250+
if (DEBUG_RELATIONSHIP_NOTIFICATIONS) {
251+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions, no-console
252+
!changed && console.log(`changed because priorLocalMember !== member && member !== prevMember`);
253+
}
224254
changed = true;
225255
}
226-
} else {
227-
!changed && console.log(`changed because priorLocalMember !== member && index >= priorLocalLength`);
256+
} else if (i < finalLength) {
257+
// if we have exceeded the length of priorLocalState and we are within the range
258+
// of the finalState then we must have changed
259+
if (DEBUG_RELATIONSHIP_NOTIFICATIONS) {
260+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions, no-console
261+
!changed && console.log(`changed because priorMember !== member && index >= priorLocalLength`);
262+
}
228263
changed = true;
229264
}
230265
}
@@ -244,8 +279,53 @@ function _compare<T>(
244279
}
245280

246281
if (!finalSet.has(prevMember)) {
247-
!changed && console.log(`changed because !finalSet.has(prevMember)`);
248-
changed = true;
282+
// if we are within finalLength, we can only be "changed" if we've already exceeded
283+
// the index range of priorLocalState, as otherwise the previous member may still
284+
// be removed.
285+
//
286+
// prior local: [1, 2, 3, 4]
287+
// final state: [1, 2, 3]
288+
// prev remote state: [1, 2, 5, 3, 4]
289+
// i === 2
290+
// prevMember === 5
291+
// !finalSet.has(prevMember) === true
292+
//
293+
// because we will become changed at i===3,
294+
// we do not need to worry about becoming changed at i===2
295+
// as the arrays until now are still the same
296+
//
297+
// prior local: [1, 2, 3]
298+
// final state: [1, 2, 3, 4]
299+
// prev remote state: [1, 2, 5, 3, 4]
300+
// i === 2
301+
// prevMember === 5
302+
// !finalSet.has(prevMember) === true
303+
//
304+
// because we will become changed at i===3
305+
// we do not need to worry about becoming changed at i===2
306+
//
307+
// prior local: [1, 2, 3]
308+
// final state: [1, 2, 3]
309+
// prev remote state: [1, 2, 5, 3, 4]
310+
// i === 2
311+
// prevMember === 5
312+
// !finalSet.has(prevMember) === true
313+
//
314+
// because we have same length and same membership order
315+
// we do not need to worry about becoming changed at i===2
316+
//
317+
// if you do not have a priorLocalState you can't be changed
318+
// ergo, we never need to set changed in this branch.
319+
// this log can still be useful for debugging.
320+
if (DEBUG_RELATIONSHIP_NOTIFICATIONS) {
321+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
322+
!changed &&
323+
// eslint-disable-next-line no-console
324+
console.log(`changed because i >= priorLocalLength && i < finalLength && !finalSet.has(prevMember)`);
325+
}
326+
//
327+
// we do still set remoteOrderChanged as it has
328+
remoteOrderChanged = true;
249329
removed.add(prevMember);
250330
onDel(prevMember);
251331
}

‎packages/graph/src/-private/operations/replace-related-records.ts

+10
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,11 @@ function replaceRelatedRecordsRemote(graph: Graph, op: ReplaceRelatedRecordsOper
218218
if (relationship.additions?.has(identifier)) {
219219
relationship.additions.delete(identifier);
220220
} else {
221+
if (!relationship.isDirty) {
222+
console.log(
223+
`setting relationship to dirty because the remote addition was not in our previous list of local additions`
224+
);
225+
}
221226
relationship.isDirty = true;
222227
}
223228
addToInverse(graph, identifier, definition.inverseKey, op.record, isRemote);
@@ -229,6 +234,11 @@ function replaceRelatedRecordsRemote(graph: Graph, op: ReplaceRelatedRecordsOper
229234
if (relationship.removals?.has(identifier)) {
230235
relationship.removals.delete(identifier);
231236
} else {
237+
if (!relationship.isDirty) {
238+
console.log(
239+
`setting relationship to dirty because the remote removal was not in our previous list of local removals`
240+
);
241+
}
232242
relationship.isDirty = true;
233243
}
234244
removeFromInverse(graph, identifier, definition.inverseKey, op.record, isRemote);

‎tests/docs/fixtures/expected.js

+1
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,7 @@ module.exports = {
606606
'(public) @warp-drive/build-config/debugging DebugLogging#LOG_PAYLOADS',
607607
'(public) @warp-drive/build-config/debugging DebugLogging#LOG_REQUEST_STATUS',
608608
'(public) @warp-drive/build-config/debugging DebugLogging#LOG_REQUESTS',
609+
'(public) @warp-drive/build-config/debugging DebugLogging#DEBUG_RELATIONSHIP_NOTIFICATIONS',
609610
'(public) @warp-drive/build-config/deprecations CurrentDeprecations#DEPRECATE_COMPUTED_CHAINS',
610611
'(public) @warp-drive/build-config/deprecations CurrentDeprecations#DEPRECATE_EMBER_INFLECTOR',
611612
'(public) @warp-drive/build-config/deprecations CurrentDeprecations#DEPRECATE_LEGACY_IMPORTS',

‎tests/performance/app/routes/update-with-same-state-m2m.js

+47-21
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
1-
// FIXME: Don't merge disables
21
/* eslint-disable no-console */
32
/* eslint-disable no-undef */
43
import Route from '@ember/routing/route';
54
import { inject as service } from '@ember/service';
65

7-
const REMOVAL_COUNT = 1;
6+
const DEBUG = false;
87

98
export default Route.extend({
109
store: service(),
1110

1211
async model() {
13-
console.groupCollapsed('test-setup');
12+
DEBUG && console.groupCollapsed('test-setup');
1413
performance.mark('start-data-generation');
1514

1615
const initialPayload = await fetch('./fixtures/big-many-to-many.json').then((r) => r.json());
1716
const initialPayload2 = structuredClone(initialPayload);
1817
const payloadWithRemoval = await fetch('./fixtures/big-many-to-many-with-removal.json').then((r) => r.json());
18+
// our relationship has 10 * number of colors generated members,
19+
// so this keeps us in-sync with the fixture generation
20+
const REMOVAL_COUNT =
21+
initialPayload.data[0].relationships.colors.data.length -
22+
payloadWithRemoval.data[0].relationships.colors.data.length;
1923

2024
performance.mark('start-push-initial-payload');
2125
this.store.push(initialPayload);
@@ -34,43 +38,65 @@ export default Route.extend({
3438
seen.clear();
3539
const removedColors = [];
3640

37-
console.groupEnd();
38-
console.log(structuredClone(getWarpDriveMetricCounts()));
41+
DEBUG && console.groupEnd();
42+
DEBUG && console.log(structuredClone(getWarpDriveMetricCounts()));
3943

4044
performance.mark('start-local-removal');
4145
console.groupCollapsed('start-local-removal');
4246
for (const car of peekedCars) {
4347
const colors = car.colors;
4448
removedColors.push(colors.splice(0, REMOVAL_COUNT));
45-
iterateCar(car, seen);
4649
}
50+
peekedCars.forEach((car) => iterateCar(car, seen));
4751
seen.clear();
4852

49-
console.groupEnd();
50-
console.log(structuredClone(getWarpDriveMetricCounts()));
53+
DEBUG && console.groupEnd();
54+
DEBUG && console.log(structuredClone(getWarpDriveMetricCounts()));
5155

5256
performance.mark('start-push-minus-one-payload');
53-
console.groupCollapsed('start-push-minus-one-payload');
57+
DEBUG && console.groupCollapsed('start-push-minus-one-payload');
58+
5459
this.store.push(payloadWithRemoval);
55-
console.groupEnd();
56-
console.log(structuredClone(getWarpDriveMetricCounts()));
60+
DEBUG && console.groupEnd();
61+
DEBUG && console.log(structuredClone(getWarpDriveMetricCounts()));
5762

5863
performance.mark('start-local-addition');
59-
console.groupCollapsed('start-local-addition');
60-
peekedCars.forEach((car, index) => {
61-
car.colors = removedColors[index].concat(car.colors);
62-
iterateCar(car, seen);
63-
});
64+
DEBUG && console.groupCollapsed('start-local-addition');
65+
// note, due to their position, the cars relationship on 50% of
66+
// colors will end up reversed, causing us to generate a notification.
67+
// this is because when we initially remove the colors from cars,
68+
// we remove the car from wherever it occurs. When we re-add, that
69+
// counts as re-adding "to the end" of the relationship since the
70+
// removal was previously committed.
71+
// To not generate a notification in this benchmark, the API response
72+
// would need to account for the natural re-ordering of cars in the colors
73+
// relationship.
74+
//
75+
// colors.cars = [1, 2, 3]
76+
// cars.colors = [1, 2, 3]
77+
// remove color 1 from cars.colors
78+
// colors.cars = [2, 3]
79+
// cars.colors = [2, 3]
80+
// now add color-1 back to cars.colors at the front
81+
// cars.colors = [1, 2, 3]
82+
// colors.cars = [2, 3, 1] // notice order change
83+
// but our payload will be [1, 2, 3] so we will generate a notification
84+
// for colors.cars (we should not generate one for cars.colors)
85+
for (let i = 0; i < peekedCars.length; i++) {
86+
const car = peekedCars[i];
87+
car.colors = removedColors[i].concat(car.colors);
88+
}
89+
peekedCars.forEach((car) => iterateCar(car, seen));
6490
seen.clear();
6591

66-
console.groupEnd();
67-
console.log(structuredClone(getWarpDriveMetricCounts()));
92+
DEBUG && console.groupEnd();
93+
DEBUG && console.log(structuredClone(getWarpDriveMetricCounts()));
6894

6995
performance.mark('start-push-plus-one-payload');
70-
console.groupCollapsed('start-push-plus-one-payload');
96+
DEBUG && console.groupCollapsed('start-push-plus-one-payload');
7197
this.store.push(initialPayload2);
72-
console.groupEnd();
73-
console.log(structuredClone(getWarpDriveMetricCounts()));
98+
DEBUG && console.groupEnd();
99+
DEBUG && console.log(structuredClone(getWarpDriveMetricCounts()));
74100

75101
performance.mark('end-push-plus-one-payload');
76102
},

‎tests/performance/ember-cli-build.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ module.exports = async function (defaults) {
1111
setConfig(app, __dirname, {
1212
compatWith: '99',
1313
debug: {
14-
LOG_NOTIFICATIONS: true,
14+
// LOG_NOTIFICATIONS: true,
1515
// LOG_INSTANCE_CACHE: true,
16-
LOG_METRIC_COUNTS: true,
16+
// LOG_METRIC_COUNTS: true,
17+
// DEBUG_RELATIONSHIP_NOTIFICATIONS: true,
1718
},
1819
});
1920

Binary file not shown.
Binary file not shown.

‎tests/performance/fixtures/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ async function main() {
4747
// write('basic-record-materialization', createParentRecords(10000, 2, 3));
4848
// write('complex-record-materialization', await createComplexRecordsPayload(400));
4949

50-
const initialBigM2M = createCarsPayload(2, 2);
50+
const initialBigM2M = createCarsPayload(100, 100);
5151
write('big-many-to-many', initialBigM2M);
5252
write('big-many-to-many-with-removal', deleteHalfTheColors(initialBigM2M));
5353
}

‎tests/performance/vite.config.mjs

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export default defineConfig({
5959
negate_iife: false,
6060
sequences: 30,
6161
defaults: true,
62+
drop_debugger: false,
6263
arguments: false,
6364
keep_fargs: false,
6465
toplevel: false,

0 commit comments

Comments
 (0)