Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for negative caching to cache.Cache #93

Merged
merged 1 commit into from
Feb 16, 2024

Conversation

nickstenning
Copy link
Member

This commit adds support for negative caching, that is: caching the non-existence of a specific key. This can help to reduce the cost of cache misses when a client is repeatedly requesting a non-existent object, and may also help mitigate denial of service attacks which target the relatively expensive cache fill operation.

The change is backwards compatible with the existing interface for NewCache through the magic of functional options. Negative caching is disabled by default, but can be enabled by specifying a WithNegativeCaching option at initialization:

fresh := 30 * time.Second
stale := 10 * time.Minute
negative := 10 * time.Second

c := cache.NewCache[User](redis, "user", fresh, stale, cache.WithNegativeCaching(negative))

To use negative caching, the cache fetcher function must return a sentinel error value, cache.ErrDoesNotExist. This is also the error that will be returned for a negative result. Clients should use errors.Is(err, cache.ErrDoesNotExist) to identify a negative result.

@nickstenning nickstenning requested a review from a team February 16, 2024 11:45
cache/cache.go Outdated Show resolved Hide resolved
Copy link
Contributor

@philandstuff philandstuff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks fine. I agree that functional options are the bee's knees (they're sort of CRDTs if you squint).

I think the one thing I feel uneasy about is that non-existence isn't an error and I'd rather not treat it as such. We already enforce that nil cannot be set in the cache, so we could safely return nil to represent nonexistence, which feels more Go-ish.

If you disagree, feel free to merge (after removing the debug print).

This commit adds support for negative caching, that is: caching the
non-existence of a specific key. This can help to reduce the cost of
cache misses when a client is repeatedly requesting a non-existent
object, and may also help mitigate denial of service attacks which
target the relatively expensive cache fill operation.

The change is backwards compatible with the existing interface for
NewCache through the magic of functional options. Negative caching is
disabled by default, but can be enabled by specifying a
`WithNegativeCaching` option at initialization:

    fresh := 30 * time.Second
    stale := 10 * time.Minute
    negative := 10 * time.Second

    c := cache.NewCache[User](redis, "user", fresh, stale, cache.WithNegativeCaching(negative))

To use negative caching, the cache fetcher function must return a
sentinel error value, `cache.ErrDoesNotExist`. This is also the error
that will be returned for a negative result. Clients should use
`errors.Is(err, cache.ErrDoesNotExist)` to identify a negative result.
@nickstenning
Copy link
Member Author

we could safely return nil to represent nonexistence, which feels more Go-ish.

We discussed this in person. This isn't that easy because T isn't necessarily a pointer. I'll see later if we can make a change that rejects all zero values rather than just literal nils.

@nickstenning nickstenning merged commit 8b49466 into main Feb 16, 2024
2 checks passed
@nickstenning nickstenning deleted the negative-caching branch February 16, 2024 14:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants