From 876c42b5955e1b50d86765a95773f9a21d2d7377 Mon Sep 17 00:00:00 2001 From: David Lawrence Date: Wed, 8 Jul 2026 17:55:34 -0400 Subject: [PATCH 1/3] Bug 2053504 - Need to add index to tracking_flags_values table for better performance from /bzapi/bug and REST search --- extensions/TrackingFlags/Extension.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/TrackingFlags/Extension.pm b/extensions/TrackingFlags/Extension.pm index 12f577de87..b12e04886c 100644 --- a/extensions/TrackingFlags/Extension.pm +++ b/extensions/TrackingFlags/Extension.pm @@ -277,6 +277,7 @@ sub db_schema_abstract_schema { INDEXES => [ tracking_flags_values_idx => {FIELDS => ['tracking_flag_id', 'value'], TYPE => 'UNIQUE',}, + tracking_flag_id_idx => ['tracking_flag_id'] ], }; $args->{'schema'}->{'tracking_flags_bugs'} = { From 8c7270c0b7e27bcf3320e13649ca9fa78bd3e7df Mon Sep 17 00:00:00 2001 From: David Lawrence Date: Thu, 9 Jul 2026 16:29:15 -0400 Subject: [PATCH 2/3] Copilot review fixes --- extensions/TrackingFlags/Extension.pm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/extensions/TrackingFlags/Extension.pm b/extensions/TrackingFlags/Extension.pm index b12e04886c..cf5f457334 100644 --- a/extensions/TrackingFlags/Extension.pm +++ b/extensions/TrackingFlags/Extension.pm @@ -277,7 +277,7 @@ sub db_schema_abstract_schema { INDEXES => [ tracking_flags_values_idx => {FIELDS => ['tracking_flag_id', 'value'], TYPE => 'UNIQUE',}, - tracking_flag_id_idx => ['tracking_flag_id'] + tracking_flags_values_tracking_flag_id_idx => ['tracking_flag_id'], ], }; $args->{'schema'}->{'tracking_flags_bugs'} = { @@ -342,6 +342,10 @@ sub install_update_db { $dbh->bz_add_column('tracking_flags_values', 'comment', {TYPE => 'TEXT', NOTNULL => 0,}, ); + + $dbh->bz_add_index('tracking_flags_values', + 'tracking_flags_values_tracking_flag_id_idx', + [qw(tracking_flag_id)]); } sub install_filesystem { From 7a068181fb50dee4aa145c3139c0e91502898d86 Mon Sep 17 00:00:00 2001 From: David Lawrence Date: Wed, 19 Aug 2026 15:40:06 -0400 Subject: [PATCH 3/3] Drop unnecessary index and instead cache the flag values --- extensions/TrackingFlags/Extension.pm | 5 ---- extensions/TrackingFlags/lib/Flag.pm | 29 ++++++++++++++++++++-- extensions/TrackingFlags/lib/Flag/Value.pm | 4 +++ 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/extensions/TrackingFlags/Extension.pm b/extensions/TrackingFlags/Extension.pm index cf5f457334..12f577de87 100644 --- a/extensions/TrackingFlags/Extension.pm +++ b/extensions/TrackingFlags/Extension.pm @@ -277,7 +277,6 @@ sub db_schema_abstract_schema { INDEXES => [ tracking_flags_values_idx => {FIELDS => ['tracking_flag_id', 'value'], TYPE => 'UNIQUE',}, - tracking_flags_values_tracking_flag_id_idx => ['tracking_flag_id'], ], }; $args->{'schema'}->{'tracking_flags_bugs'} = { @@ -342,10 +341,6 @@ sub install_update_db { $dbh->bz_add_column('tracking_flags_values', 'comment', {TYPE => 'TEXT', NOTNULL => 0,}, ); - - $dbh->bz_add_index('tracking_flags_values', - 'tracking_flags_values_tracking_flag_id_idx', - [qw(tracking_flag_id)]); } sub install_filesystem { diff --git a/extensions/TrackingFlags/lib/Flag.pm b/extensions/TrackingFlags/lib/Flag.pm index 5a09d944fe..9322643b88 100644 --- a/extensions/TrackingFlags/lib/Flag.pm +++ b/extensions/TrackingFlags/lib/Flag.pm @@ -273,8 +273,7 @@ sub preload_all_the_things { return unless @flag_ids; # Preload values - my $value_objects = Bugzilla::Extension::TrackingFlags::Flag::Value->match( - {tracking_flag_id => \@flag_ids}); + my $value_objects = _values_for_flag_ids(\@flag_ids); # Now populate the tracking flags with this set of value objects. foreach my $obj (@$value_objects) { @@ -319,6 +318,32 @@ sub preload_all_the_things { @$flags = values %flag_hash; } +# Return value objects for the given tracking flag ids. +# +# The whole tracking_flags_values table is small and changes rarely, so it is +# read once per request (Flag::Value is IS_CONFIG, so the unfiltered select is +# served from memcached as well) and sliced in perl. Querying per call meant a +# query returning roughly half the table for every Flag->match, which happens +# once per bug whenever custom fields are collected - see the +# active_custom_fields call in Bugzilla::Bug::to_hash. +sub _values_for_flag_ids { + my ($flag_ids) = @_; + my $rows = Bugzilla->request_cache->{tracking_flags_value_rows} ||= do { + my %by_flag_id; + foreach my $value (Bugzilla::Extension::TrackingFlags::Flag::Value->get_all) { + push @{$by_flag_id{$value->tracking_flag_id}}, {%$value}; + } + \%by_flag_id; + }; + + # Callers store a back-reference to their own flag object on each value, so + # fresh objects are returned rather than the cached rows themselves. + return [ + map { Bugzilla::Extension::TrackingFlags::Flag::Value->new_from_hash({%$_}) } + map { @{$rows->{$_} || []} } @$flag_ids + ]; +} + ############################### #### Validators #### ############################### diff --git a/extensions/TrackingFlags/lib/Flag/Value.pm b/extensions/TrackingFlags/lib/Flag/Value.pm index 52d63970d2..0bf5ade50b 100644 --- a/extensions/TrackingFlags/lib/Flag/Value.pm +++ b/extensions/TrackingFlags/lib/Flag/Value.pm @@ -24,6 +24,10 @@ use Scalar::Util qw(blessed weaken); use constant DB_TABLE => 'tracking_flags_values'; +# Values change rarely, so unfiltered selects are cached in memcached and the +# cache is cleared automatically when a value is created, updated or deleted. +use constant IS_CONFIG => 1; + use constant DB_COLUMNS => qw( id tracking_flag_id