diff --git a/Bugzilla/Memcached.pm b/Bugzilla/Memcached.pm index eccebdca54..4928a02494 100644 --- a/Bugzilla/Memcached.pm +++ b/Bugzilla/Memcached.pm @@ -300,6 +300,13 @@ sub should_rate_limit { $memcached->add($key, 0, $rate_seconds + 1); my $tokens = $memcached->get_multi(@keys); my $cas = $memcached->gets($key); + + # gets() returns undef when memcached is unreachable (or the key expired + # between the add and the gets). Don't let $cas->[1]++ autovivify an + # arrayref holding an undef cas id, which would then be passed to cas(). + # Fail open: no working memcached means no rate limiting. + return 0 unless ref $cas eq 'ARRAY' && defined $cas->[0]; + $tokens->{$key} = $cas->[1]++; return 1 if sum(values %$tokens) >= $rate_max; return 0 if $memcached->cas($key, @$cas, $rate_seconds + 1); diff --git a/extensions/BMO/lib/Reports/Attention.pm b/extensions/BMO/lib/Reports/Attention.pm index 7ec8d7b2b8..1f1eb12f20 100644 --- a/extensions/BMO/lib/Reports/Attention.pm +++ b/extensions/BMO/lib/Reports/Attention.pm @@ -161,7 +161,7 @@ sub critical_assigned_bugs { my $dbh = Bugzilla->dbh; # Preselected values for inserting into SQL - my $cache = Bugzilla->process_cache->{attention}; + my $cache = Bugzilla->request_cache->{attention}; my $keyword_id = $cache->{sec_critical_id}; my $class_ids = join ',', @{$cache->{classification_ids}}; my $bug_states = join ',', map { $dbh->quote($_) } BUG_STATE_OPEN; @@ -205,7 +205,7 @@ sub critical_needinfo_bugs { return [] if !exists $flags->{tracking}; # Preselected values for inserting into SQL - my $cache = Bugzilla->process_cache->{attention}; + my $cache = Bugzilla->request_cache->{attention}; my $needinfo_id = $cache->{needinfo_flag_id}; my $keyword_id = $cache->{sec_critical_id}; my $class_ids = join ',', @{$cache->{classification_ids}}; @@ -282,7 +282,7 @@ sub important_assigned_bugs { return [] if !exists $flags->{status}; # Preselected values for inserting into SQL - my $cache = Bugzilla->process_cache->{attention}; + my $cache = Bugzilla->request_cache->{attention}; my $class_ids = join ',', @{$cache->{classification_ids}}; my $bug_states = join ',', map { $dbh->quote($_) } BUG_STATE_OPEN; my $nightly_flag_id = $flags->{status}->{nightly}->flag_id; @@ -324,7 +324,7 @@ sub important_needinfo_bugs { my $dbh = Bugzilla->dbh; # Cached values for inserting into SQL - my $cache = Bugzilla->process_cache->{attention}; + my $cache = Bugzilla->request_cache->{attention}; my $needinfo_id = $cache->{needinfo_flag_id}; my $class_ids = join ',', @{$cache->{classification_ids}}; my $keyword_id = $cache->{sec_high_id}; @@ -358,7 +358,7 @@ sub other_needinfo_bugs { my $dbh = Bugzilla->dbh; # Cached values for inserting into SQL - my $cache = Bugzilla->process_cache->{attention}; + my $cache = Bugzilla->request_cache->{attention}; my $needinfo_id = $cache->{needinfo_flag_id}; my $class_ids = join ',', @{$cache->{classification_ids}}; @@ -374,7 +374,7 @@ sub other_needinfo_bugs { AND bugs.bug_severity NOT IN ('S1','S2') AND (keywords.keywordid IS NULL OR keywords.keywordid NOT IN (?, ?)) AND (bug_group_map.group_id IS NULL OR bug_group_map.group_id NOT IN (" - . join ',', @{$cache->{sec_group_ids}} . ')) + . (join ',', @{$cache->{sec_group_ids}}) . ')) ORDER BY bugs.delta_ts, bugs.bug_id'; my $bugs = get_bug_list($query, $user->id, $user->id, $cache->{sec_high_id}, @@ -396,42 +396,45 @@ sub report { = $input->{who} ? Bugzilla::User->check({name => $input->{who}}) : $user; $vars->{who} = $who->login; - # Create a global seen list of bugs (if not yet exists) to make sure - # we do not show a bug more than once across all lists. Request cache - # lasts for only this request. - my $request_cache = Bugzilla->request_cache; - $request_cache->{attention} = {}; - $request_cache->{attention}->{global_seen} = {}; - my $dbh = Bugzilla->dbh; - # Here we load some values into cache that will be used later - # by the various queries. Process cache lasts til server restart. - my $process_cache = Bugzilla->process_cache->{attention} = {}; - - # classifications - $process_cache->{classification_ids} ||= $dbh->selectcol_arrayref(' + # Set up the cache used by the various queries below. It holds a global + # seen list of bugs, so we do not show a bug more than once across all + # lists, along with values interpolated into the queries. Request cache + # lasts for this request only, so a keyword, flag type or security group + # added mid-session is picked up on the next page load rather than at + # the next server restart. + my $lookup_cache = Bugzilla->request_cache->{attention} + = {global_seen => {}}; + + # classifications. As with the security groups below, fall back to a + # non-existent id if none are found so the IN () clauses built from this + # list stay valid SQL and simply match nothing. + my $classification_ids = $dbh->selectcol_arrayref(' SELECT id FROM classifications WHERE name IN (' . join(', ', map { $dbh->quote($_) } CLASSIFICATIONS) . ')'); + $lookup_cache->{classification_ids} + = @{$classification_ids} ? $classification_ids : [0]; # needinfo flag - $process_cache->{needinfo_flag_id} ||= $dbh->selectrow_array(" + $lookup_cache->{needinfo_flag_id} = $dbh->selectrow_array(" SELECT id FROM flagtypes WHERE name = 'needinfo'"); # keyword ids - $process_cache->{sec_critical_id} ||= $dbh->selectrow_array(" + $lookup_cache->{sec_critical_id} = $dbh->selectrow_array(" SELECT id FROM keyworddefs WHERE name = 'sec-critical'"); - $process_cache->{sec_high_id} ||= $dbh->selectrow_array(" + $lookup_cache->{sec_high_id} = $dbh->selectrow_array(" SELECT id FROM keyworddefs WHERE name = 'sec-high'"); - $process_cache->{regression_id} ||= $dbh->selectrow_array(" - SELECT id FROM keyworddefs WHERE name = 'regression'"); - # Get a list of group ids that end in -security - $process_cache->{sec_group_ids} - ||= $dbh->selectcol_arrayref('SELECT id FROM ' + # Get a list of group ids that end in -security. Fall back to a + # non-existent id if there are none, so the IN () clauses built from + # this list stay valid SQL and simply match nothing. + my $sec_group_ids + = $dbh->selectcol_arrayref('SELECT id FROM ' . $dbh->quote_identifier('groups') . ' WHERE name LIKE \'%-security\''); + $lookup_cache->{sec_group_ids} = @{$sec_group_ids} ? $sec_group_ids : [0]; # build bug lists $vars->{critical_needinfo_bugs} = critical_needinfo_bugs($who); @@ -443,8 +446,8 @@ sub report { # count number of unique bugs my %bug_ids; foreach my $name (qw( - s1_bugs sec_crit_bugs critical_needinfo_bugs s2_bugs - sec_high_bugs important_needinfo_bugs other_needinfo_bugs + critical_needinfo_bugs critical_assigned_bugs important_needinfo_bugs + important_assigned_bugs other_needinfo_bugs )) { foreach my $bug (@{$vars->{$name}}) {