From 20e561efdb32d4c1a8c38c6fb595b81611bf7b72 Mon Sep 17 00:00:00 2001 From: David Lawrence Date: Thu, 20 Aug 2026 17:41:39 -0400 Subject: [PATCH 1/2] Bug 2065244 - Fix several small issues with My Attention dashboard --- Bugzilla/Memcached.pm | 7 +++++ extensions/BMO/lib/Reports/Attention.pm | 38 ++++++++++++------------- 2 files changed, 26 insertions(+), 19 deletions(-) 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..6e22aee6d8 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}, @@ -405,31 +405,31 @@ sub report { 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} = {}; + # Here we load some values into cache that will be used later by the + # various 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 = $request_cache->{attention}; # classifications - $process_cache->{classification_ids} ||= $dbh->selectcol_arrayref(' + $lookup_cache->{classification_ids} = $dbh->selectcol_arrayref(' SELECT id FROM classifications WHERE name IN (' . join(', ', map { $dbh->quote($_) } CLASSIFICATIONS) . ')'); # 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 ' + $lookup_cache->{sec_group_ids} + = $dbh->selectcol_arrayref('SELECT id FROM ' . $dbh->quote_identifier('groups') . ' WHERE name LIKE \'%-security\''); @@ -443,8 +443,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}}) { From 792c604752d73c23a7846b1f674a94d3974caad0 Mon Sep 17 00:00:00 2001 From: David Lawrence Date: Fri, 21 Aug 2026 15:26:39 -0400 Subject: [PATCH 2/2] Review fixes --- extensions/BMO/lib/Reports/Attention.pm | 37 +++++++++++++------------ 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/extensions/BMO/lib/Reports/Attention.pm b/extensions/BMO/lib/Reports/Attention.pm index 6e22aee6d8..1f1eb12f20 100644 --- a/extensions/BMO/lib/Reports/Attention.pm +++ b/extensions/BMO/lib/Reports/Attention.pm @@ -396,26 +396,26 @@ 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. 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 = $request_cache->{attention}; - - # classifications - $lookup_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 $lookup_cache->{needinfo_flag_id} = $dbh->selectrow_array(" @@ -427,11 +427,14 @@ sub report { $lookup_cache->{sec_high_id} = $dbh->selectrow_array(" SELECT id FROM keyworddefs WHERE name = 'sec-high'"); - # Get a list of group ids that end in -security - $lookup_cache->{sec_group_ids} + # 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);