TanStack / query

🤖 Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.
https://tanstack.com/query
MIT License
42.3k stars 2.88k forks source link

experimental_createPersister is not cleaning up after itself #7049

Open rklomp opened 7 months ago

rklomp commented 7 months ago

Describe the bug

experimental_createPersister creates a separate storage entry for each cache item. If this item is not accessed anymore it will never be cleaned up and stays in storage indefinitely.

Your minimal, reproducible example

https://stackblitz.com/edit/github-1zcjsn?file=src%2Findex.jsx

Steps to reproduce

  1. make a query that is persisted.
  2. Never do that same query again
  3. Item will stay in storage indefinitely

Expected behavior

I expect there is some kind of garbage collection.

How often does this bug happen?

Every time

Screenshots or Videos

No response

Platform

Android, React Native

Tanstack Query adapter

react-query

TanStack Query version

5.25.0

TypeScript version

No response

Additional context

My quick solution for now

export function usePersistorGarbageCollect() {
    // Add AsyncStorage garbage collect
    useEffect(() => {
        const gcPersitor = async () => {
            try {
                //Remove the old offline cache
                AsyncStorage.removeItem("REACT_QUERY_OFFLINE_CACHE");

                const storage_keys = await AsyncStorage.getAllKeys();
                const keys = storage_keys.filter((key) =>
                    key.startsWith("tanstack-query-")
                );
                const items = await AsyncStorage.multiGet(keys);
                const expired_items = items.filter(
                    ([_, value]) =>
                        Date.now() - JSON.parse(value).state.dataUpdatedAt >
                        QUERY_GC_TIME
                );

                const expired_keys = expired_items.map((item) => item[0]);
                console.log("gcPersitor will cleanup", expired_keys);

                await AsyncStorage.multiRemove(expired_keys);
            } catch (e) {
                console.log("gcPersitor", e);
            }
        };

        gcPersitor();
    }, []);
}
hirbod commented 6 months ago

Let's link the discussion as well https://github.com/TanStack/query/discussions/6788:

And I also have two code examples here: https://gist.github.com/hirbod/bc1538990d1b47419c3cb1f6622f7625