Skip to content

Commit

Permalink
Use weakMap for private parts
Browse files Browse the repository at this point in the history
  • Loading branch information
msand committed Aug 29, 2024
1 parent 475ff2f commit 0ac4b3e
Show file tree
Hide file tree
Showing 18 changed files with 368 additions and 225 deletions.
3 changes: 2 additions & 1 deletion scripts/memory/tests.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const assert = require("assert");
const {
$,
ApolloClient,
InMemoryCache,
gql,
Expand Down Expand Up @@ -182,7 +183,7 @@ describe("garbage collection", () => {
});

function register(suffix) {
const reader = cache["storeReader"];
const reader = $(cache)["storeReader"];
registry.register(reader, "StoreReader" + suffix);
registry.register(reader.canon, "ObjectCanon" + suffix);
}
Expand Down
5 changes: 3 additions & 2 deletions src/__tests__/ApolloClient.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import gql from "graphql-tag";

import {
$,
ApolloClient,
ApolloError,
DefaultOptions,
Expand Down Expand Up @@ -2745,15 +2746,15 @@ describe("ApolloClient", () => {
`,
});

expect((client.cache as any).data.data).toEqual({
expect($(client.cache).data["data"]).toEqual({
ROOT_QUERY: {
__typename: "Query",
a: 1,
},
});

await client.clearStore();
expect((client.cache as any).data.data).toEqual({});
expect($(client.cache).data["data"]).toEqual({});
});
});

Expand Down
3 changes: 3 additions & 0 deletions src/__tests__/__snapshots__/exports.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

exports[`exports of public entry points @apollo/client 1`] = `
Array [
"$",
"ApolloCache",
"ApolloClient",
"ApolloConsumer",
Expand Down Expand Up @@ -73,6 +74,7 @@ Array [

exports[`exports of public entry points @apollo/client/cache 1`] = `
Array [
"$",
"ApolloCache",
"Cache",
"EntityStore",
Expand All @@ -92,6 +94,7 @@ Array [

exports[`exports of public entry points @apollo/client/core 1`] = `
Array [
"$",
"ApolloCache",
"ApolloClient",
"ApolloError",
Expand Down
21 changes: 13 additions & 8 deletions src/__tests__/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
import { ApolloLink } from "../link/core";
import {
createFragmentRegistry,
EntityStore,
InMemoryCache,
makeVar,
PossibleTypesMap,
Expand All @@ -45,6 +46,8 @@ import {
} from "../testing";
import { spyOnConsole } from "../testing/internal";
import { waitFor } from "@testing-library/react";
import { $ } from "../cache/inmemory/privates";
import { LayerType } from "../cache/inmemory/entityStore";

describe("client", () => {
it("can be loaded via require", () => {
Expand Down Expand Up @@ -2434,19 +2437,20 @@ describe("client", () => {
});

{
const { data, optimisticData } = client.cache as any;
const { data, optimisticData } = $(client.cache);
expect(optimisticData).not.toBe(data);
expect(optimisticData.parent).toBe(data.stump);
expect(optimisticData.parent.parent).toBe(data);
const layer = optimisticData as unknown as LayerType;
expect(layer.parent).toBe((data as EntityStore.Root).stump);
expect((layer.parent as LayerType).parent).toBe(data);
}

mutatePromise
.then((_) => {
reject(new Error("Returned a result when it should not have."));
})
.catch((_: ApolloError) => {
const { data, optimisticData } = client.cache as any;
expect(optimisticData).toBe(data.stump);
const { data, optimisticData } = $(client.cache);
expect(optimisticData).toBe((data as EntityStore.Root).stump);
resolve();
});
}
Expand Down Expand Up @@ -3525,7 +3529,8 @@ describe("@connection", () => {
const aResults = watch(aQuery);
const bResults = watch(bQuery);

expect(cache["watches"].size).toBe(2);
const watches = $(cache)["watches"];
expect(watches.size).toBe(2);

expect(aResults).toEqual([]);
expect(bResults).toEqual([]);
Expand All @@ -3543,10 +3548,10 @@ describe("@connection", () => {
expect(aResults).toEqual([]);
expect(bResults).toEqual([]);

expect(cache["watches"].size).toBe(0);
expect(watches.size).toBe(0);
const abResults = watch(abQuery);
expect(abResults).toEqual([]);
expect(cache["watches"].size).toBe(1);
expect(watches.size).toBe(1);

await wait();

Expand Down
3 changes: 2 additions & 1 deletion src/__tests__/resultCacheCleaning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { makeExecutableSchema } from "@graphql-tools/schema";
import { ApolloClient, Resolvers, gql } from "../core";
import { InMemoryCache, NormalizedCacheObject } from "../cache";
import { SchemaLink } from "../link/schema";
import { $ } from "../cache/inmemory/privates";

describe("resultCache cleaning", () => {
const fragments = gql`
Expand Down Expand Up @@ -150,7 +151,7 @@ describe("resultCache cleaning", () => {
});

afterEach(() => {
const storeReader = (client.cache as InMemoryCache)["storeReader"];
const { storeReader } = $(client.cache);
expect(storeReader["executeSubSelectedArray"].size).toBeGreaterThan(0);
expect(storeReader["executeSelectionSet"].size).toBeGreaterThan(0);
client.cache.evict({
Expand Down
2 changes: 1 addition & 1 deletion src/cache/core/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ export abstract class ApolloCache<TSerialized> implements DataProxy {

// Make sure we compute the same (===) fragment query document every
// time we receive the same fragment in readFragment.
private getFragmentDoc = wrap(getFragmentQueryDocument, {
getFragmentDoc = wrap(getFragmentQueryDocument, {
max:
cacheSizes["cache.fragmentQueryDocuments"] ||
defaultCacheSizes["cache.fragmentQueryDocuments"],
Expand Down
1 change: 1 addition & 0 deletions src/cache/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export {
} from "./inmemory/helpers.js";

export { InMemoryCache } from "./inmemory/inMemoryCache.js";
export { $ } from "./inmemory/privates.js";

export type { ReactiveVar } from "./inmemory/reactiveVars.js";
export { makeVar, cacheSlot } from "./inmemory/reactiveVars.js";
Expand Down
64 changes: 36 additions & 28 deletions src/cache/inmemory/__tests__/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { ObjectCanon } from "../object-canon";
import { TypePolicies } from "../policies";
import { spyOnConsole } from "../../../testing/internal";
import { defaultCacheSizes } from "../../../utilities";
import { $ } from "../privates";

disableFragmentWarnings();

Expand Down Expand Up @@ -1498,13 +1499,15 @@ describe("Cache", () => {
}
`;

const originalReader = cache["storeReader"];
const privates = $(cache);

const originalReader = privates.storeReader;
expect(originalReader).toBeInstanceOf(StoreReader);

const originalWriter = cache["storeWriter"];
const originalWriter = privates.storeWriter;
expect(originalWriter).toBeInstanceOf(StoreWriter);

const originalMBW = cache["maybeBroadcastWatch"];
const originalMBW = privates.maybeBroadcastWatch;
expect(typeof originalMBW).toBe("function");

const originalCanon = originalReader.canon;
Expand Down Expand Up @@ -1534,12 +1537,12 @@ describe("Cache", () => {
c: "see",
});

expect(originalReader).not.toBe(cache["storeReader"]);
expect(originalWriter).not.toBe(cache["storeWriter"]);
expect(originalMBW).not.toBe(cache["maybeBroadcastWatch"]);
expect(originalReader).not.toBe(privates.storeReader);
expect(originalWriter).not.toBe(privates.storeWriter);
expect(originalMBW).not.toBe(privates.maybeBroadcastWatch);
// The cache.storeReader.canon is preserved by default, but can be dropped
// by passing resetResultIdentities:true to cache.gc.
expect(originalCanon).toBe(cache["storeReader"].canon);
expect(originalCanon).toBe(privates.storeReader.canon);
});
});

Expand Down Expand Up @@ -2122,10 +2125,11 @@ describe("Cache", () => {
describe("resultCacheMaxSize", () => {
it("uses default max size on caches if resultCacheMaxSize is not configured", () => {
const cache = new InMemoryCache();
expect(cache["maybeBroadcastWatch"].options.max).toBe(
const privates = $(cache);
expect(privates.maybeBroadcastWatch.options.max).toBe(
defaultCacheSizes["inMemoryCache.maybeBroadcastWatch"]
);
expect(cache["storeReader"]["executeSelectionSet"].options.max).toBe(
expect(privates.storeReader["executeSelectionSet"].options.max).toBe(
defaultCacheSizes["inMemoryCache.executeSelectionSet"]
);
expect(cache["getFragmentDoc"].options.max).toBe(
Expand All @@ -2136,8 +2140,9 @@ describe("resultCacheMaxSize", () => {
it("configures max size on caches when resultCacheMaxSize is set", () => {
const resultCacheMaxSize = 12345;
const cache = new InMemoryCache({ resultCacheMaxSize });
expect(cache["maybeBroadcastWatch"].options.max).toBe(resultCacheMaxSize);
expect(cache["storeReader"]["executeSelectionSet"].options.max).toBe(
const privates = $(cache);
expect(privates.maybeBroadcastWatch.options.max).toBe(resultCacheMaxSize);
expect(privates.storeReader["executeSelectionSet"].options.max).toBe(
resultCacheMaxSize
);
expect(cache["getFragmentDoc"].options.max).toBe(
Expand Down Expand Up @@ -2400,7 +2405,7 @@ describe("InMemoryCache#broadcastWatches", function () {
[canonicalCache, nonCanonicalCache].forEach((cache) => {
// Hack: delete every watch.lastDiff, so subsequent results will be
// broadcast, even though they are deeply equal to the previous results.
cache["watches"].forEach((watch) => {
$(cache)["watches"].forEach((watch) => {
delete watch.lastDiff;
});
});
Expand Down Expand Up @@ -3813,19 +3818,20 @@ describe("ReactiveVar and makeVar", () => {

expect(diffs.length).toBe(5);

expect(cache["watches"].size).toBe(5);
const watches = $(cache)["watches"];
expect(watches.size).toBe(5);
expect(spy).not.toBeCalled();

unwatchers.pop()!();
expect(cache["watches"].size).toBe(4);
expect(watches.size).toBe(4);
expect(spy).not.toBeCalled();

unwatchers.shift()!();
expect(cache["watches"].size).toBe(3);
expect(watches.size).toBe(3);
expect(spy).not.toBeCalled();

unwatchers.pop()!();
expect(cache["watches"].size).toBe(2);
expect(watches.size).toBe(2);
expect(spy).not.toBeCalled();

expect(diffs.length).toBe(5);
Expand All @@ -3835,7 +3841,7 @@ describe("ReactiveVar and makeVar", () => {
expect(unwatchers.length).toBe(3);
unwatchers.forEach((unwatch) => unwatch());

expect(cache["watches"].size).toBe(0);
expect(watches.size).toBe(0);
expect(spy).toBeCalledTimes(1);
expect(spy).toBeCalledWith(cache);
});
Expand Down Expand Up @@ -3865,7 +3871,8 @@ describe("ReactiveVar and makeVar", () => {
watch("a");
watch("d");

expect(cache["watches"].size).toBe(5);
const watches = $(cache)["watches"];
expect(watches.size).toBe(5);
expect(diffCounts).toEqual({
a: 2,
b: 1,
Expand All @@ -3875,7 +3882,7 @@ describe("ReactiveVar and makeVar", () => {

unwatchers.a.forEach((unwatch) => unwatch());
unwatchers.a.length = 0;
expect(cache["watches"].size).toBe(3);
expect(watches.size).toBe(3);

nameVar("Hugh");
expect(diffCounts).toEqual({
Expand All @@ -3886,7 +3893,7 @@ describe("ReactiveVar and makeVar", () => {
});

cache.reset({ discardWatches: true });
expect(cache["watches"].size).toBe(0);
expect(watches.size).toBe(0);

expect(diffCounts).toEqual({
a: 2,
Expand Down Expand Up @@ -3926,7 +3933,7 @@ describe("ReactiveVar and makeVar", () => {
});

nameVar("Trevor");
expect(cache["watches"].size).toBe(2);
expect(watches.size).toBe(2);
expect(diffCounts).toEqual({
a: 2,
b: 2,
Expand All @@ -3937,7 +3944,7 @@ describe("ReactiveVar and makeVar", () => {
});

cache.reset({ discardWatches: true });
expect(cache["watches"].size).toBe(0);
expect(watches.size).toBe(0);

nameVar("Danielle");
expect(diffCounts).toEqual({
Expand All @@ -3949,7 +3956,7 @@ describe("ReactiveVar and makeVar", () => {
f: 2,
});

expect(cache["watches"].size).toBe(0);
expect(watches.size).toBe(0);
});

it("should recall forgotten vars once cache has watches again", () => {
Expand All @@ -3974,22 +3981,23 @@ describe("ReactiveVar and makeVar", () => {
expect(diffs.length).toBe(3);
expect(names()).toEqual(["Ben", "Ben", "Ben"]);

expect(cache["watches"].size).toBe(3);
const watches = $(cache)["watches"];
expect(watches.size).toBe(3);
expect(spy).not.toBeCalled();

unwatchers.pop()!();
expect(cache["watches"].size).toBe(2);
expect(watches.size).toBe(2);
expect(spy).not.toBeCalled();

unwatchers.shift()!();
expect(cache["watches"].size).toBe(1);
expect(watches.size).toBe(1);
expect(spy).not.toBeCalled();

nameVar("Hugh");
expect(names()).toEqual(["Ben", "Ben", "Ben", "Hugh"]);

unwatchers.pop()!();
expect(cache["watches"].size).toBe(0);
expect(watches.size).toBe(0);
expect(spy).toBeCalledTimes(1);
expect(spy).toBeCalledWith(cache);

Expand All @@ -3999,7 +4007,7 @@ describe("ReactiveVar and makeVar", () => {

// Call watch(false) to avoid immediate delivery of the "ignored" name.
unwatchers.push(watch(false));
expect(cache["watches"].size).toBe(1);
expect(watches.size).toBe(1);
expect(names()).toEqual(["Ben", "Ben", "Ben", "Hugh"]);

// This is the test that would fail if cache.watch did not call
Expand Down
Loading

0 comments on commit 0ac4b3e

Please sign in to comment.