While investigating caching libraries for one of Core27's clients we slipped into a rabbit hole of finding the perfect caching library. A few days into trying out some of the popular ones, the need to onerously dictate how we envisioned a simple Redis backed caching client to behave, superseded all other pressing needs.
Our primary requirement was something that played well with FastAPI and was simple enough to update.
The quest to make it simple.
Thus began our indulgent journey into designing a caching layer for services. As developers, it's hard to not to be moved by developers who align to simplicity.
Simplicity comes at a cost which is almost always worth paying. The benefits are invariably never immediately apparent.
At the cost of trivializing the entire exercise we reduce the core problems to
When to set a value to the data store
When to invalidate the value from the data store
We add one layer of complexity to our problem statement by wanting to do all of this declaratively instead of manually triggering such a check at each point. So assuming our actual domain method looks something like this
We want to refrain from having to pepper our business logic with stuff like this
That instinctively leaves us with decorators as the solution. But without getting ahead of ourselves let's get to the core of setting and getting cache keys.
We imagine our APIs to support two main methods (or decorators since we've already arrived at it).
cache ( To set cache )
invalidate_cache( To invalidate the keys we've set )
We separate the actual task of setting and getting the values onto a backend much like any 2-tiered service.
So our backend could look something like this
If we ignore the cache expiry aspects of the code it's easy to notice that there isn't much to the class. It simply sets a key or deletes a key from Redis. The expiry tells the service how long to keep the key in cache but we can assume that to be a separate service call that returns an integer that denotes the seconds to expiry.
On to the meat of our services we define the cache and invalidate_cache methods as such.
We use these decorators to grant us all the helper methods we needed.
If this appears useful you can view the project here.
You can install and try it by running
pip install c27cache
poetry add c27cache