Storing Config in DB

We’re getting ahead of ourselves a little here, if you’re reading this manual straight through. But for reference sake this probably belongs here.

Settings Table

If you already have a Rattail DB, then it has a table named setting which is designed to store config values. It’s just a regular table so you can write settings via SQL if you like:

insert into setting (name, value) values ('rattail.app_title', 'Poser');

update setting set value = 'Something Else' where name = 'rattail.app_title';

Although the main reason for putting settings in the DB is usually so you can edit them via the web app.

Telling App to Read Config from DB

Well first of all we must assume that the DB connection itself is configured. More on that later but let’s say you have this in place:

[rattail.db]
default.url = postgresql://user:password@localhost/poser

Then you also must tell the Rattail config engine that a) it should read config values from the DB at all, but probably also b) it should prefer values from the DB over what was read from file. Do this within your config file:

[rattail.config]
usedb = true
preferdb = true

With this in place, when the app requests a config value, it will come from the DB if present, falling back to the file value if that exists.

File vs. DB Setting Names

You may have noticed that the SQL examples above, and the examples used in Config File Syntax are really for the same app_title setting, but there is a key difference in naming.

So in a config file you might have this snippet to define a setting:

[rattail]
app_title = Poser

But then that same setting is written in SQL as:

insert into setting (name, value) values ('rattail.app_title', 'Poser');

In other words the “section” and “option” names from the config file, are joined together with a dot, for the DB setting name.

When the app requests a config value, it must specify both a section and option. The config engine then will auto-join them as needed when doing DB lookups.

config.get('rattail', 'app_title')

Avoiding the DB

The app can request config from “file only” if it needs to. It just has to specify a flag when reading the value, for example:

config.get('rattail', 'app_title', usedb=False)

DB Settings Cache

Warning

Unfortunately at this time the caching mechanisms described below are not reliable. Docs remain in place for now, but it is very much not recommended to enable caching.

One downside of reading config values from the DB settings table, is the number of queries involved, as each call to config.get() would normally involve a separate SQL query. (Not only that, but unless a DB session is passed to config.get() a new one will be made just for the query.)

To help with this, you can enable caching for the DB settings. This should “just work” as expected; i.e. calls to config.get() need not change. (Although there’s no point in passing a DB session when caching is enabled.)

To turn on the caching, add to your config file:

[rattail.config]
beaker_cache.enabled = true

By default this cache will be file-based, storage for which is at e.g. /srv/envs/poser/app/cache/config/ although you can override the paths if you prefer:

[rattail.config]
beaker_cache.enabled = true
beaker_cache.data_dir = /somewhere/else/data
beaker_cache.lock_dir = /somewhere/else/lock

Note that by default there is no expiration for the cache values, meaning once a particular setting is cached, that value will never expire, and therefore the setting will never be re-fetched from DB (unless a new value is written for it, in which case the cache is invalidated for that setting, which causes a re-fetch next time it’s requested). You may want values to expire after say, an hour, so that settings will periodically be re-fetched regardless of write activity. Do that by specifying the number of seconds after which values should expire:

[rattail.config]
# nb. expire values after 1 hour
beaker_cache.expire = 3600

memcached

If you’re not crazy about caching to disk and would prefer to use memcached as the backend instead, first install that:

sudo apt install memcached

Then install the Python dependencies to your virtual environment:

cd /srv/envs/poser
source bin/activate
pip install 'rattail[memcached]'

And finally add to your config file:

[rattail.config]
beaker_cache.enabled = true
beaker_cache.type = ext:memcached
beaker_cache.url = 127.0.0.1:11211

Rattail will try to come up with a unique namespace to use for the cache; this is needed in situations where the same memcached service is handling multiple “separate” Rattail apps. You can also explicitly set the namespace to use, e.g.:

[rattail.config]
beaker_cache.namespace = my_custom_namespace