Proposal: Redis Cache support #355
Replies: 10 comments 18 replies
-
What if I don't want my values to expire ever? |
Beta Was this translation helpful? Give feedback.
-
In Cache Keys:
How does type Foo struct {
Bar *Bar
}
type Bar struct {
Foo *Foo
}
key := &Foo{}
key.Bar = &Bar{}
key.Bar.Foo = key
var someKeyspace = cache.NewIntKeyspace[Foo](...) |
Beta Was this translation helpful? Give feedback.
-
Would Encore provide a generic version like this? A large use-case for Caching is to cache expensive objects and being able to do the following would be a large usecase: type ExpensiveThingToCompute struct { ... }
var expensiveObject = cache.NewKeyspace[String, ExpensiveThingToCompute](...)
func DoExpensiveCompute() (ExpensiveThingToCompute, error) { ... }
expensiveObject.GetExistingOrSet("BTC/USD Historical Rates", DoExpensiveCompute) |
Beta Was this translation helpful? Give feedback.
-
One really useful capability to have is to be able to set TTL per record. For instance I might want to expire values at midnight every day. Which means when I initially set it I'd want to set the TTL as the number of seconds to midnight or a specific time. I feel like we might need to change this API design to; func (*Keyspace[K, V]) Set(context.Context, K, V, cache.Options...) error Where options allows overrides on a per key basis; keyspace.Set(ctx, "Blah", "foo", cache.ExpireIn(20 * time.Second)) |
Beta Was this translation helpful? Give feedback.
-
Can a cache cluster be shared between services? |
Beta Was this translation helpful? Give feedback.
-
Do we need to declare a |
Beta Was this translation helpful? Give feedback.
-
What's the plan for support cache in tests and local development? |
Beta Was this translation helpful? Give feedback.
-
I love the idea of providing this primitive out of the box! ❤️ One minor question - have you thought through any possible implications for the API if encore itself wants to build on top of this type with other primitives? For example, I could see a future where it would be nice to offer a distributed rate limiter primitive. Those are usually implemented with something like redis as the backing store. In that case, are there any API changes you might want to make to allow the platform to have have access to this primitive internally? I'm thinking of things like reserving specific keys or prefixes so that you can't have collisions? |
Beta Was this translation helpful? Give feedback.
-
Perhaps you're going to deal with this later, but what about allowing a custom cluster URI for a Redis instance not managed by Encore? For example if I wanted to use a managed Redis service like https://cloud.google.com/memorystore on GCP or a third-party service like Redis Enterprise Cloud. As with databases, I'd probably want to specify this per environment e.g. use Redis Enterprise Cloud in prod but still have Encore manage the local dev environment with Redis docker/miniredis. |
Beta Was this translation helpful? Give feedback.
-
For visibility: This has now been added (a long time ago!) |
Beta Was this translation helpful? Give feedback.
-
Background
Caching is a common approach to improving user experiences through faster response times and lower latencies. While it's possible to implement a simple in-memory cache already today with Encore, it doesn't work very well when deploying Encore in a serverless environment with zero to many instances running, causing poor cache hit rates.
Many backend applications would benefit from easier and better cache support in Encore. This proposal offers a way to easily use Redis as a cache, in a way that keeps with Encore's philosophy around simplicity and ease of use. The proposed API is type-safe and portable across cloud providers.
Design
We propose adding a new package,
encore.dev/storage/cache
that defines aNewCluster
function that creates a new Redis cluster, similarly topubsub.NewTopic
:Cache Keyspaces
When using a cache, each cached item is stored at a particular key, which is typically an arbitrary string. If you use a cache cluster to cache different sets of data, it's important that distinct data set have non-overlapping keys.
Each value stored in the cache also has a specific type, and certain cache operations can only be performed on certain types. For example, a common cache operation is to increment an integer value that is stored in the cache. If you try to apply this operation on a value that is not an integer, an error is returned.
We propose solving both of these challenges by introducing the notion of a Cache Keyspace.
In order to use the cache, you must define a keyspace that specifies a pattern that cache keys take, as well as the data type of the values you store.
It looks like this:
Beyond
StringKeyspace
Encore will provide other keyspace types likeIntKeyspace
,FloatKeyspace
,SetKeyspace
, and other Redis data types. These are different types instead of a singleKeyspace[Key, Value any]
generic type since the supported operations differ depending on the type.Specifying cache keys
As seen above, keyspaces accept a
K
type parameter that represents the key type.K
can either be a basic type likestring
orint
, or it can be a namedstruct
type that contains multiple fields.This type gets combined with the
KeyPattern
configuration setting to produce the actual cache key values are stored in. TheKeyPattern
can be thought of as a formatting pattern that describes how theK
value should be turned into a string key.Through static analysis Encore can then reason about the different cache keyspaces and ensure they all use non-overlapping keys.
Example
For example, here's how one might define a cache cluster and a per-user id cache of the number of incoming requests:
Beta Was this translation helpful? Give feedback.
All reactions