Docs: [Cache Components] "use cache" pitfalls with map()
What is the documentation issue?
Here's a fix I'm going to deploy now:
const resolveDidToHandle = cache(async function resolveDidToHandle(did: string): Promise<string> {
"use cache: redis";
tagDid(did);
@@ -156,7 +160,7 @@ export const resolveHandleToDid = cache(async function resolveHandleToDid(
async function batchResolveHandles(dids: string[]): Promise<Map<string, string>> {
const unique = [...new Set(dids)];
- const results = await Promise.all(unique.map(resolveDidToHandle));
+ const results = await Promise.all(unique.map((did) => resolveDidToHandle(did)));
return new Map(unique.map((did, i) => [did, results[i]]));
}
@@ -191,8 +195,8 @@ async function batchResolveUsers(dids: string[]): Promise<Map<string, User>> {
if (unique.length === 0) return new Map();
const [handles, avatars] = await Promise.all([
- Promise.all(unique.map(resolveDidToHandle)),
- Promise.all(unique.map(tryFetchBskyAvatar)),
+ Promise.all(unique.map((did) => resolveDidToHandle(did))),
+ Promise.all(unique.map((did) => tryFetchBskyAvatar(did))),
]);
Without this fix, my Redis cache handler wasn't working properly.
Can you spot why?
Is there any context that might help us understand?
I'm honestly not sure if this is something to call out in the docs, if this could be linted against, or something else. But it was extremely confusing. (To clarify, the issue is that map sends the array and the index to the mapped function, which implicitly become cache keys, blowing up the cache size and making it miss often.)
For what it's worth, a mode that simply logs the captured cache keys would've helped here.
Does the docs page already exist? Please link to it.
https://nextjs.org/docs/app/api-reference/directives/use-cache#cache-keys
I guess this isn’t specific to “use cache” and would’ve been an issue in any approach with var arg memoization. And I could’ve logged it in my cache handler if I looked closer at the keys and figured out what’s going on.
I’m not sure if there’s anything actionable here but I do think a note in Troubleshooting could help.
Very good point. I guess something like this could help (with the map example included):
Since "use cache" will use the arguments given to the cached function as cache keys, it is a good practice to always send those arguments explicitly. Passing cached functions into callbacks could lead to the cache function retrieving additional unneeded arguments and thus overly specific cache keys, leading to a bad cache behaviour. Consider map for example:
...
For what it's worth, a mode that simply logs the captured cache keys would've helped here.
You can use NEXT_PRIVATE_DEBUG_CACHE=1 to log the keys. This is currently only documented in this context, I think. @icyJoseph We might wanna mention that in a 'use cache' context as well.
Hmm, actually, we should be able to ignore those arguments based on our static analysis that the parameters are not defined (and arguments being forbidden). 🤔 I'll see if I can come up with a fix.
As a side note: we're already ignoring unused arguments when cache functions are called from the client (via #72506), but not yet when called from the server. I have an idea on how to fix this at the compiler level.
I have an idea on how to fix this at the compiler level.
Implemented in #86920.