How to deal with missing values with createBatch?
rosswarren opened this issue · comments
Hi, thanks for creating this very useful library!
In the following example:
import type { CacheEntry } from 'cachified';
import LRUCache from 'lru-cache';
import { cachified, createBatch } from 'cachified';
type Entry = any;
const lru = new LRUCache<string, CacheEntry<string>>({ max: 1000 });
function getEntries(ids: number[]): Promise<Entry[]> {
const batch = createBatch(getFreshValues);
return Promise.all(
ids.map((id) =>
cachified({
key: `entry-${id}`,
cache: lru,
getFreshValue: batch.add(id),
}),
),
);
}
async function getFreshValues(idsThatAreNotInCache: number[]): Entry[] {
const res = await fetch(
`https://example.org/api?ids=${idsThatAreNotInCache.join(',')}`,
);
const data = await res.json();
return data as Entry[];
}
Imagine a scenario where some of the IDs that were requested in the fetch
request do not return any result and are therefore missing from the data array. How should we deal with these missing values? Should we add null
to the array or perhaps undefined
?
Thanks!
That's an interesting question! And I would like to take some time for discussion and to think about this before presenting a possible "goto" solution.
Here's whats coming to mind. (I don't have time to fully validate this right now, will do in the coming days)
Adding null
or undefined
as option for the Entry
is definitely the way to go. But currently that would also store these values in cache and only revalidate them once their TTL is over. If that's what you intend you're good to go 👍
A scenario that is not possible right now (I think) would be when you want to NOT store the missing null
in cache and have getFreshValues
try to get them again asap.
I've added/updated two sections about this to the readme and added the functionality to fine-tune cache-metadata in batch requests in the latest release v3.2.0.
see Fine-tuning cache metadata based on fresh values (This was already possible but not well documented, though this does not use createBatch
the concept will also apply there)
I've also updated Batch requesting values to show the new onValue
callback.
With this in mind here's what I'd do.
- First and foremost, yes you want to add
null
,undefined
or similar to the array you're expecting from thePromise.all
- If you want to cache the information that you received an empty values exactly the same as the non-empty values, you don't need anything else.
If you want to not cache empty values or cache them for a much shorter time do something like this:
const batch = createBatch((ids): Promise<(string | null)[]> => {
/* ... */
});
const values: (string | null)[] = await Promise.all(
ids.map((id) =>
cachified({
/* cache and other options... */
key: `entry-${id}`,
ttl: 60_000,
getFreshValue: batch.add(id, ({ value, metadata }) => {
if (value === null) {
/* -1 disables caching, you could also just set to a shorter time */
metadata.ttl = -1;
}
}),
})
)
);