Redis doesn't do namespacing. This is my quick hack attempt to work around that in Perl, then a link to how to do it properly.
Background
I’ve got a project on the go that deals with processing subscription data out
of Microsoft and plugging it in to SQL. It’s a relatively small web server
component to receive a HTTP POST
request, which then fires off several rounds of
queued jobs to augment the data. Redis is the backend store for all of this
before it hits SQL.
Redis is fast, simple to understand, has an easy CLI which doesn’t require any extra
user setup etc to view data. It is ideal for this project.
There is however one minor issue. Redis doesn’t have a way to namespace the data.
For other projects which have used Redis this has never been a real issue, but for this project I want to run several completely isolated environments all from one machine and I’m using the same test account to trigger inbound data, which would definitely cause some clashes.
The code I have so far allow new environments/instances to be made by coping a config and starting the web server and worker daemon listening on a new pair of ports, if I can only solve the Redis bit.
Quick Hack: Perl’s AUTOLOAD
It is left up to the Redis client to decide how/if namespacing should be
implemented. The typical approach is to add a prefix to every key, think
dev:foo
and prod:foo
instead of just foo
. As the
Perl Redis lib doesn’t have a namespace option, I knocked one up. It’s super
simple, pretty dubious, but totally fine for my purposes. It relies on Perl trying to automatically load
undefined subs, which is basically everything in this case. I can then
intercept the arguments, add my string prefix to the Redis key (usually the first argument), then
call the real sub and return the result.
Use of Moo
is not at all necessary, but I’d already used extensively elsewhere
in the project so it was force of habit.
package Dubious::Redis;
# Redis doesn't do namespacing, we are going to do our own version by using a
# key prefix with environment name. This module is a super loose wrapper around
# the perl module 'Redis'. It tweaks the first argument to be '$env$key' instead
# of just '$key'.
use namespace::clean;
use Moo;
use Redis;
has _redis => (is => 'ro', default => sub { return Redis->new() });
has redis_namespace => (is => 'ro', required => 1);
sub AUTOLOAD {
my $self = shift;
my $key = shift;
our $AUTOLOAD; # keep 'use strict' happy
my $program = $AUTOLOAD;
my $pkg = __PACKAGE__ . '::';
my ($new_program) = ($program =~ m/^$pkg(.*)$/);
# Filter out specials, e.g. new, or DESTROY
if ($key && $new_program =~ m/new|[a-z]/) {
my ($new_program) = ($program =~ m/^$pkg(.*)$/);
my $new_key = $self->redis_namespace . $key;
return $self->_redis->$new_program($new_key, @_);
}
}
1;
I should make it clear that I have not super thought this through and
currently I am only using hget
,hset
and hdel
, which do all work with the above
hack. The module is safely namespaced under Dubious
so I’ll think twice before
using it any important (read production) capacity.
Proper Module = Redis::Namespace
After writing the above code (and using it for months!) I’ve realised there
is already a module on CPAN to add a namespace to Redis! I have no idea how I missed
this before, I must have checked the core Redis
module and nothing else.
Scanning the source, this module is built properly and considers the target method required arguments. It is therefore considerably more robust compared to my hack. I would strongly recommend using it unless you like living dangerously!