medikoo / memoizee

Complete memoize/cache solution for JavaScript
ISC License
1.73k stars 61 forks source link

Option to return prior cached value on update failure #132

Open cdhowie opened 1 year ago

cdhowie commented 1 year ago

We have a case where some network-loaded asset changes rarely, but we need to notice changes to it relatively quickly. Therefore, we use a memoized async function with a maxAge of 10 minutes.

However, if there is a brief network interruption when the item is requested after expiring, this causes an error to be returned and the cached value purged. This makes sense as a default behavior, but it would be nice to be able to opt-in to a behavior that will not purge the cache, but will return the last-cached value when the update function fails (synchronous exception, or returned promise becomes rejected).

In the case of async functions in particular, this may be somewhat complex because there can be two pieces of cached data: the last-resolved value, and a currently-awaiting promise. The desired behavior on a rejection is that the currently-awaiting promise is cleared and the last-cached value is returned instead of the rejected promise, but the item's age is unaffected, which will cause future fetches to retry immediately. Combined with a low timeout, this allows the application to continue using the last-known value while continually retrying for an updated value.

This is the widely-used "serve stale on error" pattern used by CDNs, applied to this module.

Since this causes errors to be swallowed by the memoized function, the documentation should probably hint that errors within the fetch function should be logged somewhere as they will not be observable otherwise.

Alternatively, there could be some way to communicate via the rejection error object (by error type or a flag on the object) whether the failure should be propagated to the caller or if the last-cached value should be used instead.

One potential way this could be implemented would be to introduce a mechanism by which the update function can be given the last-cached value, which it could choose to return. Combined with the ability for the update function to be able to indicate the TTL of the item (#23) this could easily give users their own way to implement this feature in a more flexible way.

medikoo commented 1 year ago

@cdhowie great, thanks for the input. That looks as a valid request. I've added it to v1 spec. Unfortunately, implementing this in the context of the current version doesn't look trivial, so we need to wait for v1, of which the ETA is unknown.

cdhowie commented 1 year ago

@medikoo Thanks. BTW I think you linked #117 but I think you meant to link to this issue?

medikoo commented 1 year ago

@cdhowie link seems fine, I've linked to v1 spec

cdhowie commented 1 year ago

Sorry, I meant on the spec I think the added text was intended to link to this issue but links to the other one (117) instead.

medikoo commented 1 year ago

@cdhowie ah.. I see now, thanks, fixed :)