A Manageable Multi-Database Redis Development Setup
Redis has quickly become the companion to Postgres in most of our Rails apps. This creates a problem for development, since a Redis server only uses numbered databases. That’s 1-16 by default. Hardly memorable, unless your app is called “5”, in which case you’re golden. On a single machine alone, it’s easy enough to lose track of them once you have a few apps connecting to redis for both development and testing databases. Once you have multiple developers and designers working on an app, it’s even worse. The solution I’ve found is to run a separate Redis server for each app, listening via a named socket.
This is easy to set up. First, take your standard Redis config file (mine is /usr/local/etc/redis.conf
, thanks to Homebrew) and copy it to redis-common.conf
. Edit this new file and change its port
to port 0
, ensuring it doesn’t listen over TCP.
Then, create a separate config file for each app you want to use Redis. I call them /usr/local/etc/redis-server-<app_name>.conf
. These files are nice and short:
include /usr/local/etc/redis-common.conf
pidfile /usr/local/var/run/redis-app_name.pid
unixsocket /tmp/redis-app_name.sock
dbfilename dump-app_name.rdb
vm-swap-file /tmp/redis-app_name.swap
These per-app config files are enough to run an entire Redis instance: they load the common config, followed by the app-specific paths that allow the server to run in isolation from other apps.
You can then start the Redis server like this:
redis-server /usr/local/etc/redis-server-app_name.conf
Currently, I have 5 different app-specific configs like this. This makes bringing Redis up and down a bit of a chore. I simplified the process with a couple of shell functions:
function redstart {
redstop
for file in `ls /usr/local/etc/redis-server-*.conf`; do
redis-server $file
done
}
function redstop {
for file in `ls /usr/local/etc/redis-server-*.conf`; do
pidfile=`grep pidfile $file | awk '{print $2}'`
if [ -f $pidfile ]; then
kill `cat $pidfile`
fi
done
}
Easy. Redis up and Redis down in one hit.
There’s another benefit to running Redis like this: loading production data onto your development machine. Most cloud-hosted Redis providers give you backups in Redis’ .rdb
format, which is the data file for the whole server. You can’t load this for a single numbered database. Splitting your local Redis servers by app means you can just replace the file specified in dbfilename
with this backup file and now your app has the latest production data without interfering with any other app’s Redis data.
Bonus: Redis config management functions
Want an easier way to build and manage those per-app config files? Here you go:
function redls {
ls /usr/local/etc/redis-server-*.conf
}
function redgen {
if [ -z "$1" ]; then
echo "You need to specify an app name, eg. redgen decafsucks"
return 1
fi
app=$1
config="/usr/local/etc/redis-server-${app}.conf"
redstop
echo "include /usr/local/etc/redis-common.conf" > $config
echo "pidfile /usr/local/var/run/redis-${app}.pid" >> $config
echo "unixsocket /tmp/redis-${app}.sock" >> $config
echo "dbfilename dump-${app}.rdb" >> $config
echo "vm-swap-file /tmp/redis-${app}.swap" >> $config
redstart
}
function redrm {
if [ -z "$1" ]; then
echo "You need to specify an app name, eg. redrm decafsucks"
return 1
fi
redstop
rm -f "/usr/local/etc/redis-server-${app}.conf"
redstart
}
Bonus: Rails redis initializer
Here is the do-it-all Redis initializer I use in my Rails apps:
require "redis"
if (redis_url = ENV["REDIS_URL"] || ENV["REDISTOGO_URL"]).present?
uri = URI.parse(redis_url)
REDIS = Redis.new(
host: uri.host,
port: uri.port,
password: uri.password)
else
redis_socket = "/tmp/redis-myappname.sock"
if File.exist?(redis_socket)
REDIS = Redis.new(
path: redis_socket,
db: !Rails.env.test? ? 0 : 1)
else
REDIS = Redis.new(
host: "localhost",
port: 6379,
db: !Rails.env.test? ? 0 : 1)
end
end
This looks for Redis URL ENV
vars first, which are used on Heroku to specify the Redis location. If they’re not found, we must be in development, so it looks for the named Redis socket for the app (change redis_socket
to make it your own). If that’s there, it uses database 0 for Rails development and production modes, and database 1 for test. Finally, we don’t want to fail completely if someone hasn’t yet starting using the named redis sockets, so we fall back to the standard Redis TCP port.