Feature #8
openAdd some sort of caching for config from DB settings
0%
Description
I've long been aware of inefficiencies caused by constant querying of the setting
table for real-time config values. It was originally noticed because of the sheer number of queries required e.g. for a single page load in the web app.
Accordingly, I've long intended to add some sort of caching mechanism to cut down on the number of queries. However there is a new problem, illustrated by #6 and #7: Not only are there too many queries, but they require too many DB sessions!
So the caching mechanism needs to allow for:
- no change in calling code, e.g.
config.get('foo', 'bar')
should "just work" - caller should get "current value" without need for DB session/query
- changes to settings in DB must take effect "immediately" (more or less)
I think two approaches make sense:
Basic / Native Caching¶
First, the RattailConfig
class should implement a basic caching mechanism. This should keep track of values already fetched from the DB, and when each was fetched. If an already-fetched value is requested from caller, and it was fetched "recently" enough (e.g. within last 30 seconds?) then the cached value is returned to caller as-is. If the fetch was not recent enough, it is re-fetched via DB query.
This has the advantage of being built-in to Rattail and therefore can work out of the box with no setup needed. May still want to allow config to disable the caching (perhaps it should even be off by default?), as well as config to determine cache expiry timeout, i.e. TTL for the cached values.
But it has the disadvantage of still tying up DB sessions and running queries semi-regularly. The cutoff of 30 seconds is striking a balance: We don't want to keep running queries if the same values are requested frequently; however the values may be changed by admin user at any time, so it's not safe to "always" return the same value from cache. It's hoped that 30 seconds is often enough to reflect actual changes "quickly" but infrequent enough to help somewhat with session/query overhead.
Beaker Caching¶
Ultimately I think the best fix here is to split out the config caching to its own "service". Since the web app already uses Beaker (for user sessions) it probably makes sense to use that here as well, since it provides a Caching system with support for multiple back-ends (e.g. file, memcached, redis).
Assuming a "normal" back-end (e.g. file, memcached, redis) then I believe all running Rattail apps could effectively leverage the same cache. For a given app, this would mean not only that it need run fewer queries to fetch settings, but in fact (potentially) zero queries.
Depending on the desired back-end, this may require some more setup. But hopefully a basic file-based cache would require only turning on a single config flag; i.e. the default file path for cache storage could be determined automatically (or overridden via config).
One gotcha here is the question of when/how to "invalidate" the cache for a given setting. This is especially important if multiple apps are leveraging a single cache. Consider:
- an admin user can modify Raw Settings via the web app
- some apps will auto-write settings to DB, e.g. datasync lastrun times
Anytime a setting is modified in the DB, the cache system needs to know about it so that it will re-fetch the new value when it is next requested by a caller.
I believe that since settings are usually written via dedicated function, we can add some hook to invalidate cache for the setting which is being written. But this will need closer attention when I get to that point.