]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 956233: enable USE_MEMCACHE on most objects
authorByron Jones <bjones@mozilla.com>
Fri, 31 Jan 2014 07:18:51 +0000 (15:18 +0800)
committerByron Jones <bjones@mozilla.com>
Fri, 31 Jan 2014 07:18:51 +0000 (15:18 +0800)
r=dkl, a=glob

23 files changed:
Bugzilla/Attachment.pm
Bugzilla/Auth/Verify.pm
Bugzilla/Auth/Verify/DB.pm
Bugzilla/Bug.pm
Bugzilla/Classification.pm
Bugzilla/Comment/TagWeights.pm
Bugzilla/Field.pm
Bugzilla/Flag.pm
Bugzilla/FlagType.pm
Bugzilla/Install/Requirements.pm
Bugzilla/Memcached.pm
Bugzilla/Milestone.pm
Bugzilla/Object.pm
Bugzilla/Search/Recent.pm
Bugzilla/Search/Saved.pm
Bugzilla/User.pm
buglist.cgi
contrib/merge-users.pl
editclassifications.cgi
editusers.cgi
query.cgi
sanitycheck.cgi
userprefs.cgi

index c0521c9b4cf580989e55a8ed98f353d36ff00a23..d0bd8e4c2c0011cbd8fca67eda8e6910ea7458b3 100644 (file)
@@ -908,6 +908,9 @@ sub update {
         $dbh->do('UPDATE bugs SET delta_ts = ? WHERE bug_id = ?',
                  undef, ($timestamp, $self->bug_id));
         $self->{modification_time} = $timestamp;
+        # because we updated the attachments table after SUPER::update(), we
+        # need to ensure the cache is flushed.
+        Bugzilla->memcached->clear({ table => 'attachments', id => $self->id });
     }
 
     return $changes;
@@ -932,7 +935,10 @@ sub remove_from_db {
     my $dbh = Bugzilla->dbh;
 
     $dbh->bz_start_transaction();
-    $dbh->do('DELETE FROM flags WHERE attach_id = ?', undef, $self->id);
+    my @flag_ids = $dbh->selectrow_array(
+        'SELECT id FROM flags WHERE attach_id = ?', undef, $self->id);
+    $dbh->do('DELETE FROM flags WHERE ' . $dbh->sql_in('id', \@flag_ids))
+        if @flag_ids;
     $dbh->do('DELETE FROM attach_data WHERE id = ?', undef, $self->id);
     $dbh->do('UPDATE attachments SET mimetype = ?, ispatch = ?, isobsolete = ?
               WHERE attach_id = ?', undef, ('text/plain', 0, 1, $self->id));
@@ -942,6 +948,13 @@ sub remove_from_db {
     if (-e $filename) {
         unlink $filename or warn "Couldn't unlink $filename: $!";
     }
+
+    # As we don't call SUPER->remove_from_db we need to manually clear
+    # memcached here.
+    Bugzilla->memcached->clear({ table => 'attachments', id => $self->id });
+    foreach my $flag_id (@flag_ids) {
+        Bugzilla->memcached->clear({ table => 'flags', id => $flag_id });
+    }
 }
 
 ###############################
index 0c552f40b492433dafba26fc85d8509db3d1e020..ecb64e22a749149699a4e9cc863e242a95933e0b 100644 (file)
@@ -89,6 +89,7 @@ sub create_or_update_user {
         if ($extern_id && $username_user_id && !$extern_user_id) {
             $dbh->do('UPDATE profiles SET extern_id = ? WHERE userid = ?',
                      undef, $extern_id, $username_user_id);
+            Bugzilla->memcached->clear({ table => 'profiles', id => $username_user_id });
         }
 
         # Finally, at this point, one of these will give us a valid user id.
index 82fa662dc8fc3123cf595d635087c0616e7b51f8..a5b78797bc2c68ba51acaeee47a88dde5963fa53 100644 (file)
@@ -95,6 +95,7 @@ sub change_password {
     my $cryptpassword = bz_crypt($password);
     $dbh->do("UPDATE profiles SET cryptpassword = ? WHERE userid = ?",
              undef, $cryptpassword, $user->id);
+    Bugzilla->memcached->clear({ table => 'profiles', id => $user->id });
 }
 
 1;
index 95bf293a5ec0a671574df72dc657aa43b0ac87d3..3e48312a9bedf08ee625ff2f80cfd1675d7c3ba2 100644 (file)
@@ -54,6 +54,8 @@ use constant LIST_ORDER => ID_FIELD;
 # Bugs have their own auditing table, bugs_activity.
 use constant AUDIT_CREATES => 0;
 use constant AUDIT_UPDATES => 0;
+# This will be enabled later
+use constant USE_MEMCACHED => 0;
 
 # This is a sub because it needs to call other subroutines.
 sub DB_COLUMNS {
index 771a894b4ab56c0947d0d0fe4b2e0305c9b1054b..5d488e3b9bc24426638a678c45357600d99500dc 100644 (file)
@@ -55,9 +55,18 @@ sub remove_from_db {
     ThrowUserError("classification_not_deletable") if ($self->id == 1);
 
     $dbh->bz_start_transaction();
+
     # Reclassify products to the default classification, if needed.
-    $dbh->do("UPDATE products SET classification_id = 1
-              WHERE classification_id = ?", undef, $self->id);
+    my @product_ids = $dbh->selectrow_array(
+        "SELECT id FROM products WHERE classification_id = ?",
+        undef, $self->id);
+    if (@product_ids) {
+        $dbh->do("UPDATE products SET classification_id = 1 WHERE " .
+            $dbh->sql_in('id', \@product_ids));
+        foreach my $id (@product_ids) {
+            Bugzilla->memcached->clear({ table => 'products', id => $id });
+        }
+    }
 
     $self->SUPER::remove_from_db();
 
index 5835efbc4f5c8e1c0241bc23f0fc275b2f8bf160..f1a220a47f80dcb487cef7d5c977353cef979dc3 100644 (file)
@@ -35,6 +35,9 @@ use constant NAME_FIELD => 'tag';
 use constant LIST_ORDER => 'weight DESC';
 use constant VALIDATORS => { };
 
+# There's no gain to caching these objects
+use constant USE_MEMCACHED => 0;
+
 sub tag    { return $_[0]->{'tag'} }
 sub weight { return $_[0]->{'weight'} }
 
index 82c82161af7176efe80530e54a1d294a73f08045..dce16f83b9d89afc6140353300989ba4cba7e914 100644 (file)
@@ -1075,6 +1075,8 @@ sub create {
         # Restore the original obsolete state of the custom field.
         $dbh->do('UPDATE fielddefs SET obsolete = 0 WHERE id = ?', undef, $field->id)
           unless $is_obsolete;
+
+        Bugzilla->memcached->clear({ table => 'fielddefs', id => $field->id });
     }
 
     return $field;
index 8931649e40990552c042df95b892aecc451a70b2..f365afd981188c3311527cc1689a33fa50220f39 100644 (file)
@@ -461,6 +461,7 @@ sub update {
         $dbh->do('UPDATE flags SET modification_date = ? WHERE id = ?',
                  undef, ($timestamp, $self->id));
         $self->{'modification_date'} = format_time($timestamp, '%Y.%m.%d %T');
+        Bugzilla->memcached->clear({ table => 'flags', id => $self->id });
     }
     return $changes;
 }
@@ -607,6 +608,7 @@ sub force_retarget {
         if ($is_retargetted) {
             $dbh->do('UPDATE flags SET type_id = ? WHERE id = ?',
                      undef, ($flag->type_id, $flag->id));
+            Bugzilla->memcached->clear({ table => 'flags', id => $flag->id });
         }
         else {
             # Track deleted attachment flags.
index 5fee86b300bfda415be9da9146266f6dbe5f90af..c70c1598c1c7cf0343799603bec4eccf9850442d 100644 (file)
@@ -185,8 +185,13 @@ sub update {
     # Silently remove requestees from flags which are no longer
     # specifically requestable.
     if (!$self->is_requesteeble) {
-        $dbh->do('UPDATE flags SET requestee_id = NULL WHERE type_id = ?',
-                  undef, $self->id);
+        my @ids = $dbh->selectrow_array(
+            "SELECT id FROM flags WHERE type_id = ?", undef, $self->id);
+        $dbh->do("UPDATE flags SET requestee_id = NULL WHERE "
+            . $dbh->sql_in('type_id', \@ids));
+        foreach my $id (@ids) {
+            Bugzilla->memcached->clear({ table => 'flags', id => $id });
+        }
     }
 
     $dbh->bz_commit_transaction();
index cd8978eb1db78cbce47dd560393ee0868433b2bc..e7212915318e1f645169bcb074215da0e058ad02 100644 (file)
@@ -396,6 +396,12 @@ sub OPTIONAL_MODULES {
     },
 
     # memcached
+    {
+        package => 'URI-Escape',
+        module  => 'URI::Escape',
+        version => 0,
+        feature => ['memcached'],
+    },
     {
         package => 'Cache-Memcached',
         module  => 'Cache::Memcached',
index b1b10311ba479cc80cc72223a8b911d740aff3f0..0752bcce98cf77e30b5b0de5334152307cddee18 100644 (file)
@@ -14,6 +14,10 @@ use warnings;
 use Bugzilla::Error;
 use Bugzilla::Util qw(trick_taint);
 use Scalar::Util qw(blessed);
+use URI::Escape;
+
+# memcached keys have a maximum length of 250 bytes
+use constant MAX_KEY_LENGTH => 250;
 
 sub _new {
     my $invocant = shift;
@@ -26,10 +30,11 @@ sub _new {
         && Bugzilla->params->{memcached_servers})
     {
         require Cache::Memcached;
+        $self->{namespace} = Bugzilla->params->{memcached_namespace} || '';
         $self->{memcached} =
             Cache::Memcached->new({
                 servers   => [ split(/[, ]+/, Bugzilla->params->{memcached_servers}) ],
-                namespace => Bugzilla->params->{memcached_namespace} || '',
+                namespace => $self->{namespace},
             });
     }
     return bless($self, $class);
@@ -155,6 +160,14 @@ sub _prefix {
     return $request_cache->{memcached_prefix};
 }
 
+sub _encode_key {
+    my ($self, $key) = @_;
+    $key = $self->_prefix . ':' . uri_escape_utf8($key);
+    return length($self->{namespace} . $key) > MAX_KEY_LENGTH
+        ? undef
+        : $key;
+}
+
 sub _set {
     my ($self, $key, $value) = @_;
     if (blessed($value)) {
@@ -162,13 +175,17 @@ sub _set {
         ThrowCodeError('param_invalid', { function => "Bugzilla::Memcached::set",
                                           param    => "value" });
     }
-    return $self->{memcached}->set($self->_prefix . ':' . $key, $value);
+    $key = $self->_encode_key($key)
+        or return;
+    return $self->{memcached}->set($key, $value);
 }
 
 sub _get {
     my ($self, $key) = @_;
 
-    my $value = $self->{memcached}->get($self->_prefix . ':' . $key);
+    $key = $self->_encode_key($key)
+        or return;
+    my $value = $self->{memcached}->get($key);
     return unless defined $value;
 
     # detaint returned values
@@ -187,7 +204,9 @@ sub _get {
 
 sub _delete {
     my ($self, $key) = @_;
-    return $self->{memcached}->delete($self->_prefix . ':' . $key);
+    $key = $self->_encode_key($key)
+        or return;
+    return $self->{memcached}->delete($key);
 }
 
 1;
index 6d6465e4d692481038db2eeeb74ca79c9ac3dbcd..b1783de88cc69878202b321037e24bee0c3b085d 100644 (file)
@@ -113,6 +113,7 @@ sub update {
         $dbh->do('UPDATE products SET defaultmilestone = ?
                   WHERE id = ? AND defaultmilestone = ?',
                  undef, ($self->name, $self->product_id, $changes->{value}->[0]));
+        Bugzilla->memcached->clear({ table => 'produles', id => $self->product_id });
     }
     $dbh->bz_commit_transaction();
 
index c4dfe8cf7ffe71296f5918ec4e5b6c1c9010d90e..ff1454539ec49e612042354d5aa95afe4855bc6c 100644 (file)
@@ -35,9 +35,8 @@ use constant AUDIT_UPDATES => 1;
 use constant AUDIT_REMOVES => 1;
 
 # When USE_MEMCACHED is true, the class is suitable for serialisation to
-# Memcached. This will be flipped to true by default once the majority of
-# Bugzilla Object have been tested with Memcached.
-use constant USE_MEMCACHED => 0;
+# Memcached.  See documentation in Bugzilla::Memcached for more information.
+use constant USE_MEMCACHED => 1;
 
 # This allows the JSON-RPC interface to return Bugzilla::Object instances
 # as though they were hashes. In the future, this may be modified to return
index 6d349d129dc81233c20b09f0222a735c72fe79d0..cc1c3c582e8e6b53cf592693576b70d9d202e0cd 100644 (file)
@@ -42,6 +42,9 @@ use constant VALIDATORS => {
 
 use constant UPDATE_COLUMNS => qw(bug_list list_order);
 
+# There's no gain to caching these objects
+use constant USE_MEMCACHED => 0;
+
 ###################
 # DB Manipulation #
 ###################
index e81035adadca619bb81c6c7f958098090040d9da..5dfce611baab569060901a0838ec9e1291e7b9ca 100644 (file)
@@ -187,6 +187,7 @@ sub rename_field_value {
         }
         $dbh->do("UPDATE $table SET query = ? WHERE $id_field = ?",
                  undef, $query, $id);
+        Bugzilla->memcached->clear({ table => $table, id => $id });
     }
 
     $dbh->bz_commit_transaction();
index 5ded9f06e53c27ca336ddf55ff0a516d2df04dde..872c742db6d4b21c0c4760345c2855c132514556 100644 (file)
@@ -278,6 +278,7 @@ sub update_last_seen_date {
         # pending changes
         $dbh->do("UPDATE profiles SET last_seen_date = ? WHERE userid = ?",
                  undef, $date, $self->id);
+        Bugzilla->memcached->clear({ table => 'profiles', id => $self->id });
     }
 }
 
index 281504c661d596b8f26a730a3368e2382fecc03f..600e14ef7074c248f9f369db47ab2a08bec72ecd 100755 (executable)
@@ -382,6 +382,7 @@ if ($cmdtype eq "dorem") {
             $dbh->do('DELETE FROM namedquery_group_map
                             WHERE namedquery_id = ?',
                      undef, $query_id);
+            Bugzilla->memcached->clear({ table => 'namedqueries', id => $query_id });
         }
 
         # Now reset the cached queries
index 19cb8d4c9c45711e8ab35f3e2fcf4529af93ef0b..eb205cac2035367fbb8b2b426f9fb599901c681d 100755 (executable)
@@ -222,4 +222,9 @@ $user->derive_regexp_groups();
 # Commit the transaction
 $dbh->bz_commit_transaction();
 
+# It's complex to determine which items now need to be flushed from memcached.
+# As user merge is expected to be a rare event, we just flush the entire cache
+# when users are merged.
+Bugzilla->memcached->clear_all();
+
 print "Done.\n";
index 3d93057b910e345416852a516bea625818f4a089..d47853133e09631dc9a092594f7f4bf3c5702349 100755 (executable)
@@ -188,9 +188,10 @@ if ($action eq 'update') {
 
 if ($action eq 'reclassify') {
     my $classification = Bugzilla::Classification->check($class_name);
-   
+
     my $sth = $dbh->prepare("UPDATE products SET classification_id = ?
                              WHERE name = ?");
+    my @names;
 
     if (defined $cgi->param('add_products')) {
         check_token_data($token, 'reclassify_classifications');
@@ -198,6 +199,7 @@ if ($action eq 'reclassify') {
             foreach my $prod ($cgi->param("prodlist")) {
                 trick_taint($prod);
                 $sth->execute($classification->id, $prod);
+                push @names, $prod;
             }
         }
         delete_token($token);
@@ -206,7 +208,8 @@ if ($action eq 'reclassify') {
         if (defined $cgi->param('myprodlist')) {
             foreach my $prod ($cgi->param("myprodlist")) {
                 trick_taint($prod);
-                $sth->execute(1,$prod);
+                $sth->execute(1, $prod);
+                push @names, $prod;
             }
         }
         delete_token($token);
@@ -216,6 +219,10 @@ if ($action eq 'reclassify') {
     $vars->{'classification'} = $classification;
     $vars->{'token'} = issue_session_token('reclassify_classifications');
 
+    foreach my $name (@names) {
+        Bugzilla->memcached->clear({ table => 'products', name => $name });
+    }
+
     LoadTemplate($action);
 }
 
index f4e3c08417d039e561d7b57e2287774b78f123ab..83f364528f326bd8815484870fd0a5a71faa2f2d 100755 (executable)
@@ -650,6 +650,11 @@ if ($action eq 'search') {
     $dbh->bz_commit_transaction();
     delete_token($token);
 
+    # It's complex to determine which items now need to be flushed from
+    # memcached.  As user deletion is expected to be a rare event, we just
+    # flush the entire cache when a user is deleted.
+    Bugzilla->memcached->clear_all();
+
     $vars->{'message'} = 'account_deleted';
     $vars->{'otheruser'}{'login'} = $otherUser->login;
     $vars->{'restrictablegroups'} = $user->bless_groups();
index 65a7d3c3d15e01a82e59f8bbc609fb2afc133d7e..620cf2737d748534c76bb6198b7ef54e702898dd 100755 (executable)
--- a/query.cgi
+++ b/query.cgi
@@ -14,6 +14,7 @@ use Bugzilla;
 use Bugzilla::Bug;
 use Bugzilla::Constants;
 use Bugzilla::Search;
+use Bugzilla::Search::Saved;
 use Bugzilla::User;
 use Bugzilla::Util;
 use Bugzilla::Error;
@@ -76,9 +77,11 @@ if ($cgi->param('nukedefaultquery')) {
     if ($userid) {
         my $token = $cgi->param('token');
         check_hash_token($token, ['nukedefaultquery']);
-        $dbh->do("DELETE FROM namedqueries" .
-                 " WHERE userid = ? AND name = ?", 
-                 undef, ($userid, DEFAULT_QUERY_NAME));
+        my $named_queries = Bugzilla::Search::Saved->match(
+            { userid => $userid, name => DEFAULT_QUERY_NAME });
+        if (@$named_queries) {
+            $named_queries->[0]->remove_from_db();
+        }
     }
     $buffer = "";
 }
index 256438bd875d6ddcb6b13fdceca70335d63c90bd..2f87fb828e7c558cdbc2a1675f017223ed57b263 100755 (executable)
@@ -73,6 +73,7 @@ else {
     }
 }
 my $vars = {};
+my $clear_memcached = 0;
 
 print $cgi->header() unless Bugzilla->usage_mode == USAGE_MODE_CMDLINE;
 
@@ -149,6 +150,7 @@ if ($cgi->param('createmissinggroupcontrolmapentries')) {
     }
 
     Status('group_control_map_entries_repaired', {counter => $counter});
+    $clear_memcached = 1 if $counter;
 }
 
 ###########################################################################
@@ -175,6 +177,7 @@ if ($cgi->param('repair_creation_date')) {
         $sth_UpdateDate->execute($date, $bugid);
     }
     Status('bug_creation_date_fixed', {bug_count => scalar(@$bug_ids)});
+    $clear_memcached = 1 if @$bug_ids;
 }
 
 ###########################################################################
@@ -191,6 +194,7 @@ if ($cgi->param('repair_everconfirmed')) {
     $dbh->do("UPDATE bugs SET everconfirmed = 1 WHERE bug_status IN ($confirmed_open_states)");
 
     Status('everconfirmed_end');
+    $clear_memcached = 1;
 }
 
 ###########################################################################
@@ -206,11 +210,12 @@ if ($cgi->param('repair_bugs_fulltext')) {
                                             ON bugs_fulltext.bug_id = bugs.bug_id
                                             WHERE bugs_fulltext.bug_id IS NULL');
 
-   foreach my $bugid (@$bug_ids) {
-       Bugzilla::Bug->new($bugid)->_sync_fulltext( new_bug => 1 );
-   }
+    foreach my $bugid (@$bug_ids) {
+        Bugzilla::Bug->new($bugid)->_sync_fulltext( new_bug => 1 );
+    }
 
-   Status('bugs_fulltext_fixed', {bug_count => scalar(@$bug_ids)});
+    Status('bugs_fulltext_fixed', {bug_count => scalar(@$bug_ids)});
+    $clear_memcached = 1 if @$bug_ids;
 }
 
 ###########################################################################
@@ -246,7 +251,10 @@ if ($cgi->param('rescanallBugMail')) {
         Bugzilla::BugMail::Send($bugid, $vars);
     }
 
-    Status('send_bugmail_end') if scalar(@$list);
+    if (@$list) {
+        Status('send_bugmail_end');
+        Bugzilla->memcached->clear_all();
+    }
 
     unless (Bugzilla->usage_mode == USAGE_MODE_CMDLINE) {
         $template->process('global/footer.html.tmpl', $vars)
@@ -280,6 +288,7 @@ if ($cgi->param('remove_invalid_bug_references')) {
 
         if (scalar(@$bug_ids)) {
             $dbh->do("DELETE FROM $table WHERE $field IN (" . join(',', @$bug_ids) . ")");
+            $clear_memcached = 1;
         }
     }
 
@@ -310,6 +319,7 @@ if ($cgi->param('remove_invalid_attach_references')) {
 
     $dbh->bz_commit_transaction();
     Status('attachment_reference_deletion_end');
+    $clear_memcached = 1 if @$attach_ids;
 }
 
 ###########################################################################
@@ -336,12 +346,17 @@ if ($cgi->param('remove_old_whine_targets')) {
             $dbh->do("DELETE FROM whine_schedules
                        WHERE mailto_type = $type AND mailto IN (" .
                        join(',', @$old_ids) . ")");
+            $clear_memcached = 1;
         }
     }
     $dbh->bz_commit_transaction();
     Status('whines_obsolete_target_deletion_end');
 }
 
+# If any repairs were attempted or made, we need to clear memcached to ensure
+# state is consistent.
+Bugzilla->memcached->clear_all() if $clear_memcached;
+
 ###########################################################################
 # Repair hook
 ###########################################################################
@@ -717,6 +732,7 @@ if (scalar(@invalid_flags)) {
         # Silently delete these flags, with no notification to requesters/setters.
         $dbh->do('DELETE FROM flags WHERE id IN (' . join(',', @flag_ids) .')');
         Status('flag_deletion_end');
+        Bugzilla->memcached->clear_all();
     }
     else {
         foreach my $flag (@$invalid_flags) {
index 638004c1e8a71787ab2948a717c5470096c991de..8a93bf5d38ddf9da1b7f40501f2a167476680dd2 100755 (executable)
@@ -491,12 +491,13 @@ sub SaveSavedSearches {
     }
 
     $user->flush_queries_cache;
-    
+
     # Update profiles.mybugslink.
     my $showmybugslink = defined($cgi->param("showmybugslink")) ? 1 : 0;
     $dbh->do("UPDATE profiles SET mybugslink = ? WHERE userid = ?",
-             undef, ($showmybugslink, $user->id));    
+             undef, ($showmybugslink, $user->id));
     $user->{'showmybugslink'} = $showmybugslink;
+    Bugzilla->memcached->clear({ table => 'profiles', id => $user->id });
 }