-
-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Use `WeakRef` and `FinalizationRegistry` to avoid "leaking" values that are only referenced in the cache. A few thoughts: - I've set this on by default, but provided an option to opt-out because some users (Replay) won't want it. - `WeakRef` only works with objects, so e.g. large string values may still "leak" unless explicitly evicted. - I don't know if this makes sense to add to the streaming cache; maybe we just require explicit eviction there. - I'm not sure how this impacts runtime performance of the cache. - Browser support is [pretty good](https://caniuse.com/?search=weakref).
- Loading branch information
Showing
21 changed files
with
478 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
name: "Jest unit tests" | ||
on: [pull_request] | ||
jobs: | ||
tests_e2e: | ||
name: Run Jest (node based) unit tests | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v3 | ||
- uses: actions/setup-node@v3 | ||
- name: Initialize Yarn and install package dependencies | ||
run: yarn | ||
- name: Build NPM package | ||
run: yarn prerelease | ||
- name: Run tests | ||
run: cd packages/suspense && yarn test:node |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 11 additions & 0 deletions
11
packages/suspense-website/src/examples/createCache/cacheWithoutWeakRef.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { CacheLoadOptions, createCache } from "suspense"; | ||
|
||
const load = async () => null as any; | ||
|
||
// REMOVE_BEFORE | ||
|
||
createCache<[userId: string], JSON>({ | ||
config: { useWeakRef: false }, | ||
load, | ||
// ... | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
49 changes: 49 additions & 0 deletions
49
packages/suspense-website/src/routes/examples/memory-management.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { Link } from "react-router-dom"; | ||
import Block from "../../components/Block"; | ||
import Code from "../../components/Code"; | ||
import Container from "../../components/Container"; | ||
import { ExternalLink } from "../../components/ExternalLink"; | ||
import Header from "../../components/Header"; | ||
import Note from "../../components/Note"; | ||
import { createCache } from "../../examples"; | ||
import { CREATE_CACHE } from "../config"; | ||
|
||
export default function Route() { | ||
return ( | ||
<Container> | ||
<Block> | ||
<Header title="memory management" /> | ||
</Block> | ||
<Block> | ||
<p> | ||
Caches created with{" "} | ||
<code> | ||
<Link to={CREATE_CACHE}>createCache</Link> | ||
</code>{" "} | ||
use{" "} | ||
<code> | ||
<ExternalLink to="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef"> | ||
WeakRef | ||
</ExternalLink> | ||
</code>{" "} | ||
and{" "} | ||
<code> | ||
<ExternalLink to="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/FinalizationRegistry"> | ||
FinalizationRegistry | ||
</ExternalLink> | ||
</code>{" "} | ||
APIs to avoid leaking memory. This behavior can be disabled using the{" "} | ||
<code>useWeakRef</code> configuration flag. | ||
</p> | ||
<Code code={createCache.cacheWithoutWeakRef} /> | ||
<Note type="warn"> | ||
<p>Caches that don't use weak refs may leak memory over time.</p> | ||
<p> | ||
To avoid this, use the <code>evict</code> method to remove entries | ||
once you are done using them. | ||
</p> | ||
</Note> | ||
</Block> | ||
</Container> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
/** @type {import('ts-jest').JestConfigWithTsJest} */ | ||
module.exports = { | ||
preset: "ts-jest", | ||
testMatch: ["**/*.test.node.{ts,tsx}"], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { createCache } from "./createCache"; | ||
import { Cache, CacheLoadOptions } from "../types"; | ||
import { requestGC, waitForGC } from "../utils/test"; | ||
|
||
describe("createCache", () => { | ||
let cache: Cache<[string], Object>; | ||
let fetch: jest.Mock<Promise<Object> | Object, [string, CacheLoadOptions]>; | ||
|
||
beforeEach(() => { | ||
fetch = jest.fn(); | ||
fetch.mockImplementation((key: string) => { | ||
if (key.startsWith("async")) { | ||
return Promise.resolve(key); | ||
} else if (key.startsWith("error")) { | ||
return Promise.reject(key); | ||
} else { | ||
return key; | ||
} | ||
}); | ||
}); | ||
|
||
it("should use WeakRefs if requested", async () => { | ||
cache = createCache<[string], Object>({ | ||
config: { useWeakRef: true }, | ||
load: fetch, | ||
}); | ||
|
||
cache.cache(createObject(), "one"); | ||
cache.cache(createObject(), "two"); | ||
cache.cache(createObject(), "three"); | ||
|
||
expect(cache.getValueIfCached("one")).not.toBeUndefined(); | ||
expect(cache.getValueIfCached("two")).not.toBeUndefined(); | ||
expect(cache.getValueIfCached("three")).not.toBeUndefined(); | ||
|
||
await requestGC(); | ||
await waitForGC(); | ||
|
||
expect(cache.getValueIfCached("one")).toBeUndefined(); | ||
expect(cache.getValueIfCached("two")).toBeUndefined(); | ||
expect(cache.getValueIfCached("three")).toBeUndefined(); | ||
}); | ||
|
||
it("should not use WeakRefs if requested", async () => { | ||
cache = createCache<[string], Object>({ | ||
config: { useWeakRef: false }, | ||
load: fetch, | ||
}); | ||
|
||
cache.cache(createObject(), "one"); | ||
cache.cache(createObject(), "two"); | ||
|
||
expect(cache.getValueIfCached("one")).not.toBeUndefined(); | ||
expect(cache.getValueIfCached("two")).not.toBeUndefined(); | ||
|
||
await requestGC(); | ||
await waitForGC(); | ||
|
||
expect(cache.getValueIfCached("one")).not.toBeUndefined(); | ||
expect(cache.getValueIfCached("two")).not.toBeUndefined(); | ||
}); | ||
}); | ||
|
||
function createObject(): Object { | ||
return {}; | ||
} |
Oops, something went wrong.
5d17d21
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
suspense – ./
suspense-npm.vercel.app
suspense-git-main-bvaughn.vercel.app
suspense-bvaughn.vercel.app
suspense.vercel.app