Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cache abstraction layer with Redis support #19

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions lib/MogileFS/Cache.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package MogileFS::Cache;
use strict;
use MogileFS::Util qw(error);

my $have_memc_module = eval "use Cache::Memcached; 1;";
my $have_redis_module = eval "use Redis; 1;";

sub new {
my ($class) = @_;
return $class->new_from_config;
}

sub new_from_config {
my ($class) = @_;
return undef unless ($have_memc_module || $have_redis_module);

my ($type, $servers, $cache_ttl);

if ($servers = MogileFS::Config->server_setting_cached('memcache_servers')) {
$type = 'memcache';
$cache_ttl = MogileFS::Config->server_setting_cached('memcache_ttl') || 3600;
} else {
$servers = MogileFS->config('cache_servers');
$type = MogileFS->config('cache_type');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like it'd be more helpful to either default to something here or print a warning that cache_type must be specified with cache_servers.

$cache_ttl = MogileFS->config('cache_ttl') || 3600;
}

my $subclass;
if ($type eq 'none' || $type eq '') {
return undef;
} elsif ($type eq 'memcache' && $have_memc_module) {
$subclass = 'MogileFS::Cache::Memcache';
} elsif ($type eq 'redis' && $have_redis_module) {
$subclass = 'MogileFS::Cache::Redis';
} else {
error("Cache type not supported: $type");
return undef;
}
unless (eval "use $subclass; 1") {
error("Error loading $subclass: $@");
return undef;
}

my $self = bless {
servers => $servers,
ttl => $cache_ttl,
}, $subclass;
$self->init;
return $self;
}

sub init { 1 }

sub refresh {
my $self = shift;
return $self;
}

sub set {
my ($self, $key, $value) = @_;
die "set not implemented for $self";
}

sub get {
my ($self, $key) = @_;
die "get not implemented for $self";
}

sub delete {
my ($self, $key) = @_;
die "delete not implemented for $self";
}

sub hash {
my ($self, $key) = @_;
if ($key->{type} eq 'fid') {
return 'mogf:' . $key->{domain} . ':' . $key->{key};
} elsif ($key->{type} eq 'devid') {
return 'mogd:' . $key->{fid};
}
return $key;
}

1;

__END__

=head1 NAME

MogileFS::Cache - path cache provider. base class.

=head1 ABOUT

Caches key-path mappings for faster lookup.

=head1 SEE ALSO

L<MogileFS::Cache::Memcache>


64 changes: 64 additions & 0 deletions lib/MogileFS/Cache/Memcache.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package MogileFS::Cache::Memcache;
use strict;
use base 'MogileFS::Cache';
use Cache::Memcached;

# --------------------------------------------------------------------------
# Memcache mappings are as follows:
# mogf:<dmid>:<dkey> -> fidid
# mogd:<fidid> -> \@devids (and TODO: invalidate when deletion is run!)
# --------------------------------------------------------------------------

my $cache;

sub init {
my $self = shift;
$self->SUPER::init;

my @servers = grep(!/^$/, grep(s/^\s*|\s*$//g, split(',', $self->{servers})));
@servers = ('127.0.0.1:11211') unless @servers;

$cache = Cache::Memcached->new;
$cache->set_servers(\@servers);
return $self;
}

sub refresh {
my $self = shift;

# backwards compatibility with previous cache implementation
my @servers = grep(!/^$/, grep(s/^\s*|\s*$//g, split(',', MogileFS::Config->server_setting_cached("memcache_servers"))));
return undef unless @servers;

$cache->set_servers(\@servers);
return $self;
}

sub set {
my ($self, $key, $value) = @_;
return $cache->set($self->hash($key), $value, $self->{ttl});
}

sub get {
my ($self, $key) = @_;
return $cache->get($self->hash($key));
}

sub delete {
my ($self, $key) = @_;
return $cache->delete($self->hash($key));
}

1;

__END__

=head1 NAME

MogileFS::Cache::Memcache - Memcache path cache for MogileFS

=head1 SEE ALSO

L<MogileFS::Cache>


83 changes: 83 additions & 0 deletions lib/MogileFS/Cache/Redis.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package MogileFS::Cache::Redis;
use strict;
use base 'MogileFS::Cache';
use Redis;

# --------------------------------------------------------------------------
# Redis mappings:
# mogf:<dmid>:<dkey> -> fidid
# mogd:<fidid> -> \@devids (Redis set)
# --------------------------------------------------------------------------

my $cache;

sub init {
my $self = shift;
$self->SUPER::init;

my $server = $self->{servers};
$server =~ s/^\s+//;
$server =~ s/\s+$//;
$server ||= '127.0.0.1:6379';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling the option "servers" but not handling multiple servers specified seems wrong. :-/ At least read/parse them and warn that only one is supported for Redis, I think.


if ($server =~ m!^/!) {
$cache = Redis->new(sock => $server, encoding => undef, reconnect => 60);
} else {
$cache = Redis->new(server => $server, encoding => undef, reconnect => 60);
}
return $self;
}

sub refresh {
my $self = shift;
return $self;
}

sub set {
# We use simple keys and sets since Redis only supports
# expiry based on key rather than individual elements of
# an advanced data structure like hashes.
my ($self, $key, $value) = @_;
my $hash = $self->hash($key);
my $response;
if ($key->{type} eq 'devid') {
# requires Redis >= 2.4
$response = $cache->sadd($hash, @$value);
} else {
$response = $cache->set($hash => $value);
}
# Unlike memcache, setting ttl to 0 will clear the key
if ($self->{ttl}) {
$cache->expire($hash, $self->{ttl});
}
return $response;
}

sub get {
my ($self, $key) = @_;
my $hash = $self->hash($key);
if ($key->{type} eq 'devid') {
my $members = $cache->smembers($hash);
return @$members ? $members : undef;
}
return $cache->get($hash);
}

sub delete {
my ($self, $key) = @_;
return $cache->del($self->hash($key));
}

1;

__END__

=head1 NAME

MogileFS::Cache::Redis - Redis path cache for MogileFS

=head1 SEE ALSO

L<MogileFS::Cache>


28 changes: 7 additions & 21 deletions lib/MogileFS/Config.pm
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ sub load_config {
'repl_use_get_port=i' => \$cmdline{repl_use_get_port},
'local_network=s' => \$cmdline{local_network},
'mogstored_stream_port' => \$cmdline{mogstored_stream_port},
'cache_type' => \$cmdline{cache_type},
'cache_servers' => \$cmdline{cache_servers},
'cache_ttl' => \$cmdline{cache_ttl},
);

# warn of old/deprecated options
Expand Down Expand Up @@ -166,6 +169,10 @@ sub load_config {
$node_timeout = choose_value( 'node_timeout', 2 );
$conn_timeout = choose_value( 'conn_timeout', 2 );

choose_value( 'cache_type', "" );
choose_value( 'cache_servers', "" );
choose_value( 'cache_ttl', 3600 );

choose_value( 'rebalance_ignore_missing', 0 );
$repl_use_get_port = choose_value( 'repl_use_get_port', 0 );
$local_network = choose_value( 'local_network', '' );
Expand Down Expand Up @@ -289,27 +296,6 @@ sub server_setting_cached {
return $server_settings{$key};
}

my $memc;
my $last_memc_server_fetch = 0;
my $have_memc_module = eval "use Cache::Memcached; 1;";
sub memcache_client {
return undef unless $have_memc_module;

# only reload the server list every 30 seconds
my $now = time();
return $memc if $last_memc_server_fetch > $now - 30;

my @servers = grep(/:\d+$/, split(/\s*,\s*/, MogileFS::Config->server_setting_cached("memcache_servers") || ""));
$last_memc_server_fetch = $now;

return ($memc = undef) unless @servers;

$memc ||= Cache::Memcached->new;
$memc->set_servers(\@servers);

return $memc;
}

my $cache_hostname;
sub hostname {
return $cache_hostname ||= Sys::Hostname::hostname();
Expand Down
4 changes: 2 additions & 2 deletions lib/MogileFS/DevFID.pm
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ sub add_to_db {

my $sto = Mgd::get_store();
if ($sto->add_fidid_to_devid($self->{fidid}, $self->{devid})) {
if (my $memc = MogileFS::Config->memcache_client) {
$memc->delete("mogdevids:$self->{fidid}");
if (my $cache = Mgd::get_cache()) {
$cache->delete({ type => 'devid', fid => $self->{fidid} });
}
return $self->fid->update_devcount(no_lock => $no_lock);
} else {
Expand Down
8 changes: 4 additions & 4 deletions lib/MogileFS/FID.pm
Original file line number Diff line number Diff line change
Expand Up @@ -160,13 +160,13 @@ sub mark_unreachable {
sub delete {
my $fid = shift;
my $sto = Mgd::get_store();
my $memc = MogileFS::Config->memcache_client;
if ($memc) {
my $cache = Mgd::get_cache();
if ($cache) {
$fid->_tryload;
}
$sto->delete_fidid($fid->id);
if ($memc && $fid->{_loaded}) {
$memc->delete("mogfid:$fid->{dmid}:$fid->{dkey}");
if ($cache && $fid->{_loaded}) {
$cache->delete({ type => 'fid', domain => $fid->{dmid}, key => $fid->{dkey} });
}
}

Expand Down
19 changes: 19 additions & 0 deletions lib/MogileFS/Server.pm
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ use MogileFS::FID;
use MogileFS::DevFID;

use MogileFS::Store;
use MogileFS::Cache;

use MogileFS::ReplicationPolicy::MultipleHosts;

Expand Down Expand Up @@ -246,6 +247,24 @@ sub set_store {
$store_pid = $$;
}

# cache abstraction layer
my ($cache, $cache_pid, $last_cache_refresh);
sub get_cache {
return undef if MogileFS->config('cache_type') eq 'none';
my $now = time();
if (!$last_cache_refresh && $cache && $cache_pid == $$) {
return $cache;
} elsif ($last_cache_refresh && $last_cache_refresh > ($now - 30)) {
return $cache;
}
# backwards compatibility with previous cache implementation
if (MogileFS->config('cache_type') eq '') {
$last_cache_refresh = $now;
}
$cache_pid = $$;
return $cache = $cache ? $cache->refresh() : MogileFS::Cache->new;
}

sub domain_factory {
return MogileFS::Factory::Domain->get_factory;
}
Expand Down
Loading