From 3ef245b21a397e5253ba1bda607a1510245d5c09 Mon Sep 17 00:00:00 2001 From: Byron Jones Date: Mon, 7 Apr 2014 16:38:31 +0800 Subject: Bug 987032: allow memcached to cache bugzilla configuration information r=dkl, a=glob --- Bugzilla/Memcached.pm | 124 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 110 insertions(+), 14 deletions(-) (limited to 'Bugzilla/Memcached.pm') diff --git a/Bugzilla/Memcached.pm b/Bugzilla/Memcached.pm index 0752bcce9..2bf7f1393 100644 --- a/Bugzilla/Memcached.pm +++ b/Bugzilla/Memcached.pm @@ -40,6 +40,10 @@ sub _new { return bless($self, $class); } +sub enabled { + return $_[0]->{memcached} ? 1 : 0; +} + sub set { my ($self, $args) = @_; return unless $self->{memcached}; @@ -95,6 +99,32 @@ sub get { } } +sub set_config { + my ($self, $args) = @_; + return unless $self->{memcached}; + + if (exists $args->{key}) { + return $self->_set($self->_config_prefix . ':' . $args->{key}, $args->{data}); + } + else { + ThrowCodeError('params_required', { function => "Bugzilla::Memcached::set_config", + params => [ 'key' ] }); + } +} + +sub get_config { + my ($self, $args) = @_; + return unless $self->{memcached}; + + if (exists $args->{key}) { + return $self->_get($self->_config_prefix . ':' . $args->{key}); + } + else { + ThrowCodeError('params_required', { function => "Bugzilla::Memcached::get_config", + params => [ 'key' ] }); + } +} + sub clear { my ($self, $args) = @_; return unless $self->{memcached}; @@ -130,39 +160,61 @@ sub clear { sub clear_all { my ($self) = @_; - return unless my $memcached = $self->{memcached}; - if (!$memcached->incr("prefix", 1)) { - $memcached->add("prefix", time()); - } + return unless $self->{memcached}; + $self->_inc_prefix("global"); +} + +sub clear_config { + my ($self) = @_; + return unless $self->{memcached}; + $self->_inc_prefix("config"); } # in order to clear all our keys, we add a prefix to all our keys. when we # need to "clear" all current keys, we increment the prefix. sub _prefix { - my ($self) = @_; + my ($self, $name) = @_; # we don't want to change prefixes in the middle of a request my $request_cache = Bugzilla->request_cache; - if (!$request_cache->{memcached_prefix}) { + my $request_cache_key = "memcached_prefix_$name"; + if (!$request_cache->{$request_cache_key}) { my $memcached = $self->{memcached}; - my $prefix = $memcached->get("prefix"); + my $prefix = $memcached->get($name); if (!$prefix) { $prefix = time(); - if (!$memcached->add("prefix", $prefix)) { + if (!$memcached->add($name, $prefix)) { # if this failed, either another process set the prefix, or # memcached is down. assume we lost the race, and get the new # value. if that fails, memcached is down so use a dummy # prefix for this request. - $prefix = $memcached->get("prefix") || 0; + $prefix = $memcached->get($name) || 0; } } - $request_cache->{memcached_prefix} = $prefix; + $request_cache->{$request_cache_key} = $prefix; } - return $request_cache->{memcached_prefix}; + return $request_cache->{$request_cache_key}; +} + +sub _inc_prefix { + my ($self, $name) = @_; + my $memcached = $self->{memcached}; + if (!$memcached->incr($name, 1)) { + $memcached->add($name, time()); + } + delete Bugzilla->request_cache->{"memcached_prefix_$name"}; +} + +sub _global_prefix { + return $_[0]->_prefix("global"); +} + +sub _config_prefix { + return $_[0]->_prefix("config"); } sub _encode_key { my ($self, $key) = @_; - $key = $self->_prefix . ':' . uri_escape_utf8($key); + $key = $self->_global_prefix . ':' . uri_escape_utf8($key); return length($self->{namespace} . $key) > MAX_KEY_LENGTH ? undef : $key; @@ -175,6 +227,7 @@ sub _set { ThrowCodeError('param_invalid', { function => "Bugzilla::Memcached::set", param => "value" }); } + $key = $self->_encode_key($key) or return; return $self->{memcached}->set($key, $value); @@ -191,10 +244,19 @@ sub _get { # detaint returned values # hashes and arrays are detainted just one level deep if (ref($value) eq 'HASH') { - map { defined($_) && trick_taint($_) } values %$value; + _detaint_hashref($value); } elsif (ref($value) eq 'ARRAY') { - trick_taint($_) foreach @$value; + foreach my $value (@$value) { + next unless defined $value; + # arrays of hashes are common + if (ref($value) eq 'HASH') { + _detaint_hashref($value); + } + elsif (!ref($value)) { + trick_taint($value); + } + } } elsif (!ref($value)) { trick_taint($value); @@ -202,6 +264,15 @@ sub _get { return $value; } +sub _detaint_hashref { + my ($hashref) = @_; + foreach my $value (values %$hashref) { + if (defined($value) && !ref($value)) { + trick_taint($value); + } + } +} + sub _delete { my ($self, $key) = @_; $key = $self->_encode_key($key) @@ -266,6 +337,14 @@ Lmemcached()|Bugzilla/memcached>. =head1 METHODS +=over + +=item C + +Returns true if Memcached support is available and enabled. + +=back + =head2 Setting Adds a value to Memcached. @@ -285,6 +364,13 @@ to C. This is a convenience method which allows cached data to be later retrieved by specifying the C and either the C or C. +=item C $key, data =E $data })> + +Adds the C using the C while identifying the data as part of +Bugzilla's configuration (such as fields, products, components, groups, etc). +Values set with C are automatically cleared when changes are made +to Bugzilla's configuration. + =back =head2 Getting @@ -306,6 +392,11 @@ Return C with the specified C
and C. Return C with the specified C
and C. +=item C $key })> + +Return C with the specified C from the configuration cache. See +C for more information. + =back =head2 Clearing @@ -328,6 +419,11 @@ corresponding C
and C entry. Removes C with the specified C
and C, as well as the corresponding C
and C entry. +=item C + +Removes all configuration related values from the cache. See C for +more information. + =item C Removes all values from the cache. -- cgit v1.2.3-24-g4f1b