Garbage collection and cache eviction
Apollo Client 3 enables you to selectively remove cached data that is no longer useful. The default garbage collection strategy of the gc
method is suitable for most applications, but the evict
method provides more fine-grained control for applications that require it.
You call these methods directly on the
InMemoryCache
object, not on theApolloClient
object.
cache.gc()
The gc
method removes all objects from the normalized cache that are not reachable:
cache.gc();
To determine whether an object is reachable, the cache starts from all known root objects and uses a tracing strategy to recursively visit all available child references. Any normalized objects that are not visited during this process are removed. The cache.gc()
method returns a list of the IDs of the removed objects.
Configuring garbage collection
You can use the retain
method to prevent an object (and its children) from being garbage collected, even if the object isn't reachable:
cache.retain('my-object-id');
If you later want a retain
ed object to be garbage collected, use the release
method:
cache.release('my-object-id');
If the object is unreachable, it will be garbage collected during next call to gc
.
cache.evict()
You can remove any normalized object from the cache using the evict
method:
cache.evict({ id: 'my-object-id' })
You can also remove a single field from a cached object by providing the name of the field to remove:
cache.evict({ id: 'my-object-id', fieldName: 'yearOfFounding' });
Evicting an object often makes other cached objects unreachable. Because of this, you should call cache.gc
after evict
ing one or more objects from the cache.
Dangling references
When an object is evict
ed from the cache, references to that object might remain in other cached objects. Apollo Client preserves these dangling references by default, because the referenced object might be written back to the cache at a later time. This means the reference might still be useful.
You can customize behavior for dangling references by defining a custom read
function for any field that might contain one. This function can perform whatever cleanup is necessary when the field's referenced object is missing. For example, the read
function might:
- Filter the referenced object out of a list of available objects
- Set the field's value to
null
- Return a particular default value
Every read
function is passed a canRead
function that helps it detect when its field currently contains a dangling reference.
The following code defines two read
functions (one for Query.ruler
and one for Deity.offspring
) that both use canRead
:
new InMemoryCache({
typePolicies: {
Query: {
fields: {
ruler(existingRuler, { canRead, toReference }) {
// If there is no existing ruler, Apollo becomes the ruling deity
return canRead(existingRuler) ? existingRuler : toReference({
__typename: "Deity",
name: "Apollo",
});
},
},
},
Deity: {
keyFields: ["name"],
fields: {
offspring(existingOffspring: Reference[], { canRead }) {
// Filter out any dangling references left over from removing
// offspring, supplying a default empty array if there are no
// offspring left.
return existingOffspring
? existingOffspring.filter(canRead)
: [];
},
},
},
},
})
- The
read
function forQuery.ruler
returns a default ruler (Apollo) if theexistingRuler
has been deposed. - The
read
function forDeity.offspring
filters its array to return only offspring that are alive and well in the cache.
Filtering dangling references out of a cached array field (like the Deity.offspring
example above) is so common that Apollo Client performs this filtering automatically for array fields that don't define a read
function. You can define a read
function to override this behavior.
There isn't a similarly common solution for a field that contains a single dangling reference (like the Query.ruler
example above), so this is where writing a custom read
function comes in handy most often.