Skip to content

Commit 727044a

Browse files
committed
Retain queries if data in store
1 parent c794eeb commit 727044a

File tree

5 files changed

+146
-20
lines changed

5 files changed

+146
-20
lines changed

libs/isograph-react/src/core/IsographEnvironment.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
type StableIdForFragmentReference,
1111
type UnknownTReadFromStore,
1212
} from './FragmentReference';
13-
import { RetainedQuery } from './garbageCollection';
13+
import { RetainedQuery, type ReadyRetainedQuery } from './garbageCollection';
1414
import { LogFunction, WrappedLogFunction } from './logging';
1515
import { PromiseWrapper, wrapPromise } from './PromiseWrapper';
1616
import { WithEncounteredRecords } from './read';
@@ -71,7 +71,7 @@ export type IsographEnvironment = {
7171
PromiseWrapper<IsographEntrypoint<any, any, any>>
7272
>;
7373
readonly retainedQueries: Set<RetainedQuery>;
74-
readonly gcBuffer: Array<RetainedQuery>;
74+
readonly gcBuffer: Array<ReadyRetainedQuery>;
7575
readonly gcBufferSize: number;
7676
readonly loggers: Set<WrappedLogFunction>;
7777
};

libs/isograph-react/src/core/garbageCollection.ts

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { getParentRecordKey } from './cache';
2-
import { NormalizationAstNodes } from './entrypoint';
2+
import { NormalizationAstNodes, type NormalizationAst } from './entrypoint';
33
import { Variables } from './FragmentReference';
44
import {
55
assertLink,
@@ -12,20 +12,40 @@ import {
1212
} from './IsographEnvironment';
1313

1414
export type RetainedQuery = {
15-
readonly normalizationAst: NormalizationAstNodes;
15+
normalizationAst:
16+
| {
17+
kind: 'Loading';
18+
}
19+
| {
20+
kind: 'Ready';
21+
value: NormalizationAst;
22+
};
1623
readonly variables: {};
1724
readonly root: StoreLink;
1825
};
1926

27+
export interface ReadyRetainedQuery extends RetainedQuery {
28+
readonly normalizationAst: {
29+
kind: 'Ready';
30+
value: NormalizationAst;
31+
};
32+
}
33+
34+
function isRetainedQueryReady(
35+
query: RetainedQuery,
36+
): query is ReadyRetainedQuery {
37+
return query.normalizationAst.kind === 'Ready';
38+
}
39+
2040
export type DidUnretainSomeQuery = boolean;
2141
export function unretainQuery(
2242
environment: IsographEnvironment,
2343
retainedQuery: RetainedQuery,
2444
): DidUnretainSomeQuery {
2545
environment.retainedQueries.delete(retainedQuery);
26-
environment.gcBuffer.push(retainedQuery);
27-
28-
if (environment.gcBuffer.length > environment.gcBufferSize) {
46+
if (isRetainedQueryReady(retainedQuery)) {
47+
environment.gcBuffer.push(retainedQuery);
48+
} else if (environment.gcBuffer.length > environment.gcBufferSize) {
2949
environment.gcBuffer.shift();
3050
return true;
3151
}
@@ -47,7 +67,12 @@ export function garbageCollectEnvironment(environment: IsographEnvironment) {
4767
const retainedIds: RetainedIds = {};
4868

4969
for (const query of environment.retainedQueries) {
50-
recordReachableIds(environment.store, query, retainedIds);
70+
if (isRetainedQueryReady(query)) {
71+
recordReachableIds(environment.store, query, retainedIds);
72+
} else {
73+
// if we have any queries with loading normalizationAst, we can't garbage collect
74+
return;
75+
}
5176
}
5277
for (const query of environment.gcBuffer) {
5378
recordReachableIds(environment.store, query, retainedIds);
@@ -82,7 +107,7 @@ interface RetainedIds {
82107

83108
function recordReachableIds(
84109
store: IsographStore,
85-
retainedQuery: RetainedQuery,
110+
retainedQuery: ReadyRetainedQuery,
86111
mutableRetainedIds: RetainedIds,
87112
) {
88113
const record =
@@ -98,7 +123,7 @@ function recordReachableIds(
98123
store,
99124
record,
100125
mutableRetainedIds,
101-
retainedQuery.normalizationAst,
126+
retainedQuery.normalizationAst.value.selections,
102127
retainedQuery.variables,
103128
);
104129
}

libs/isograph-react/src/core/makeNetworkRequest.ts

Lines changed: 99 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,11 @@ export function maybeMakeNetworkRequest<
6060
);
6161
}
6262
case 'No': {
63-
return [wrapResolvedValue(undefined), () => {}];
63+
return loadNormalizationAstAndRetainQuery(
64+
environment,
65+
artifact,
66+
variables,
67+
);
6468
}
6569
case 'IfNecessary': {
6670
if (
@@ -82,7 +86,11 @@ export function maybeMakeNetworkRequest<
8286
);
8387

8488
if (result.kind === 'EnoughData') {
85-
return [wrapResolvedValue(undefined), () => {}];
89+
return loadNormalizationAstAndRetainQuery(
90+
environment,
91+
artifact,
92+
variables,
93+
);
8694
} else {
8795
return makeNetworkRequest(
8896
environment,
@@ -109,6 +117,90 @@ function loadNormalizationAst(
109117
}
110118
}
111119

120+
type NormalizationAstRequestStatus =
121+
| {
122+
readonly kind: 'Disposed';
123+
}
124+
| {
125+
readonly kind: 'Undisposed';
126+
readonly retainedQuery: RetainedQuery;
127+
};
128+
129+
function loadNormalizationAstAndRetainQuery<
130+
TReadFromStore extends UnknownTReadFromStore,
131+
TClientFieldValue,
132+
TArtifact extends
133+
| RefetchQueryNormalizationArtifact
134+
| IsographEntrypoint<TReadFromStore, TClientFieldValue, TNormalizationAst>,
135+
TNormalizationAst extends NormalizationAst | NormalizationAstLoader,
136+
>(
137+
environment: IsographEnvironment,
138+
artifact: TArtifact,
139+
variables: ExtractParameters<TReadFromStore>,
140+
): ItemCleanupPair<PromiseWrapper<void, AnyError>> {
141+
const root = { __link: ROOT_ID, __typename: artifact.concreteType };
142+
143+
const retainedQuery: RetainedQuery = {
144+
normalizationAst: {
145+
kind: 'Loading',
146+
},
147+
variables,
148+
root,
149+
};
150+
let status: NormalizationAstRequestStatus = {
151+
kind: 'Undisposed',
152+
retainedQuery,
153+
};
154+
retainQuery(environment, retainedQuery);
155+
156+
switch (artifact.networkRequestInfo.normalizationAst.kind) {
157+
case 'NormalizationAst': {
158+
retainedQuery.normalizationAst = {
159+
kind: 'Ready',
160+
value: artifact.networkRequestInfo.normalizationAst,
161+
};
162+
break;
163+
}
164+
case 'NormalizationAstLoader': {
165+
artifact.networkRequestInfo.normalizationAst
166+
.loader()
167+
.then((normalizationAst) => {
168+
retainedQuery.normalizationAst = {
169+
kind: 'Ready',
170+
value: normalizationAst,
171+
};
172+
})
173+
.catch(() => {
174+
const didUnretainSomeQuery = unretainQuery(
175+
environment,
176+
retainedQuery,
177+
);
178+
if (didUnretainSomeQuery) {
179+
garbageCollectEnvironment(environment);
180+
}
181+
});
182+
}
183+
}
184+
185+
return [
186+
wrapResolvedValue(undefined),
187+
() => {
188+
if (status.kind === 'Undisposed') {
189+
const didUnretainSomeQuery = unretainQuery(
190+
environment,
191+
status.retainedQuery,
192+
);
193+
if (didUnretainSomeQuery) {
194+
garbageCollectEnvironment(environment);
195+
}
196+
}
197+
status = {
198+
kind: 'Disposed',
199+
};
200+
},
201+
];
202+
}
203+
112204
export function makeNetworkRequest<
113205
TReadFromStore extends UnknownTReadFromStore,
114206
TClientFieldValue,
@@ -173,8 +265,11 @@ export function makeNetworkRequest<
173265
variables,
174266
root,
175267
);
176-
const retainedQuery = {
177-
normalizationAst: normalizationAst.selections,
268+
const retainedQuery: RetainedQuery = {
269+
normalizationAst: {
270+
kind: 'Ready',
271+
value: normalizationAst,
272+
},
178273
variables,
179274
root,
180275
};

libs/isograph-react/src/tests/garbageCollection.test.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { describe, expect, test } from 'vitest';
22
import {
33
garbageCollectEnvironment,
44
retainQuery,
5+
type RetainedQuery,
56
} from '../core/garbageCollection';
67
import {
78
createIsographEnvironment,
@@ -55,9 +56,11 @@ export const meNameField = iso(`
5556
`)(() => {});
5657

5758
const meNameEntrypoint = iso(`entrypoint Query.meName`);
58-
const meNameRetainedQuery = {
59-
normalizationAst:
60-
meNameEntrypoint.networkRequestInfo.normalizationAst.selections,
59+
const meNameRetainedQuery: RetainedQuery = {
60+
normalizationAst: {
61+
kind: 'Ready',
62+
value: meNameEntrypoint.networkRequestInfo.normalizationAst,
63+
},
6164
variables: {},
6265
root: { __link: ROOT_ID, __typename: 'Query' },
6366
};

libs/isograph-react/src/tests/meNameSuccessor.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { RetainedQuery } from '../core/garbageCollection';
12
import { ROOT_ID } from '../core/IsographEnvironment';
23
import { iso } from './__isograph/iso';
34

@@ -14,9 +15,11 @@ export const meNameField = iso(`
1415
}
1516
`)(() => {});
1617
const meNameSuccessorEntrypoint = iso(`entrypoint Query.meNameSuccessor`);
17-
export const meNameSuccessorRetainedQuery = {
18-
normalizationAst:
19-
meNameSuccessorEntrypoint.networkRequestInfo.normalizationAst.selections,
18+
export const meNameSuccessorRetainedQuery: RetainedQuery = {
19+
normalizationAst: {
20+
kind: 'Ready',
21+
value: meNameSuccessorEntrypoint.networkRequestInfo.normalizationAst,
22+
},
2023
variables: {},
2124
root: {
2225
__link: ROOT_ID,

0 commit comments

Comments
 (0)