Bug 553266: config.cgi?ctype=rdf spends most of its time loading flagtypes from the database

a=LpSolit (module owner)


git-svn-id: svn://10.0.0.236/branches/BUGZILLA-4_0-BRANCH@261430 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
mkanat%bugzilla.org 2010-10-20 00:48:51 +00:00
parent 8877996431
commit bd04563acd
5 changed files with 83 additions and 33 deletions

View File

@ -1 +1 @@
7445 7446

View File

@ -370,16 +370,14 @@ sub flag_types {
my $self = shift; my $self = shift;
if (!defined $self->{'flag_types'}) { if (!defined $self->{'flag_types'}) {
my $flagtypes = Bugzilla::FlagType::match({ product_id => $self->product_id,
component_id => $self->id });
$self->{'flag_types'} = {}; $self->{'flag_types'} = {};
$self->{'flag_types'}->{'bug'} = $self->{'flag_types'}->{'bug'} =
Bugzilla::FlagType::match({ 'target_type' => 'bug', [grep { $_->target_type eq 'bug' } @$flagtypes];
'product_id' => $self->product_id,
'component_id' => $self->id });
$self->{'flag_types'}->{'attachment'} = $self->{'flag_types'}->{'attachment'} =
Bugzilla::FlagType::match({ 'target_type' => 'attachment', [grep { $_->target_type eq 'attachment' } @$flagtypes];
'product_id' => $self->product_id,
'component_id' => $self->id });
} }
return $self->{'flag_types'}; return $self->{'flag_types'};
} }

View File

@ -48,7 +48,6 @@ whose names start with _ or are specifically noted as being private.
=cut =cut
use Bugzilla::User;
use Bugzilla::Error; use Bugzilla::Error;
use Bugzilla::Util; use Bugzilla::Util;
use Bugzilla::Group; use Bugzilla::Group;
@ -223,6 +222,7 @@ explicitly excluded from the flagtype.
sub grant_list { sub grant_list {
my $self = shift; my $self = shift;
require Bugzilla::User;
my @custusers; my @custusers;
my @allusers = @{Bugzilla->user->get_userlist}; my @allusers = @{Bugzilla->user->get_userlist};
foreach my $user (@allusers) { foreach my $user (@allusers) {
@ -264,15 +264,33 @@ sub flag_count {
sub inclusions { sub inclusions {
my $self = shift; my $self = shift;
$self->{'inclusions'} ||= get_clusions($self->id, 'in'); if (!defined $self->{inclusions}) {
return $self->{'inclusions'}; ($self->{inclusions}, $self->{inclusions_as_hash}) = get_clusions($self->id, 'in');
}
return $self->{inclusions};
}
sub inclusions_as_hash {
my $self = shift;
$self->inclusions unless defined $self->{inclusions_as_hash};
return $self->{inclusions_as_hash};
} }
sub exclusions { sub exclusions {
my $self = shift; my $self = shift;
$self->{'exclusions'} ||= get_clusions($self->id, 'ex'); if (!defined $self->{exclusions}) {
return $self->{'exclusions'}; ($self->{exclusions}, $self->{exclusions_as_hash}) = get_clusions($self->id, 'ex');
}
return $self->{exclusions};
}
sub exclusions_as_hash {
my $self = shift;
$self->exclusions unless defined $self->{exclusions_as_hash};
return $self->{exclusions_as_hash};
} }
###################################################################### ######################################################################
@ -310,7 +328,7 @@ sub get_clusions {
"WHERE flagtypes.id = ? " . "WHERE flagtypes.id = ? " .
" AND flag${type}clusions.type_id = flagtypes.id", " AND flag${type}clusions.type_id = flagtypes.id",
undef, $id); undef, $id);
my %clusions; my (%clusions, %clusions_as_hash);
foreach my $data (@$list) { foreach my $data (@$list) {
my ($product_id, $product_name, $component_id, $component_name) = @$data; my ($product_id, $product_name, $component_id, $component_name) = @$data;
$product_id ||= 0; $product_id ||= 0;
@ -318,8 +336,9 @@ sub get_clusions {
$component_id ||= 0; $component_id ||= 0;
$component_name ||= "__Any__"; $component_name ||= "__Any__";
$clusions{"$product_name:$component_name"} = "$product_id:$component_id"; $clusions{"$product_name:$component_name"} = "$product_id:$component_id";
$clusions_as_hash{$product_id}->{$component_id} = 1;
} }
return \%clusions; return (\%clusions, \%clusions_as_hash);
} }
=pod =pod
@ -423,15 +442,13 @@ sub sqlify_criteria {
my $is_active = $criteria->{is_active} ? "1" : "0"; my $is_active = $criteria->{is_active} ? "1" : "0";
push(@criteria, "flagtypes.is_active = $is_active"); push(@criteria, "flagtypes.is_active = $is_active");
} }
if ($criteria->{product_id} && $criteria->{'component_id'}) { if ($criteria->{product_id}) {
my $product_id = $criteria->{product_id}; my $product_id = $criteria->{product_id};
my $component_id = $criteria->{component_id};
# Add inclusions to the query, which simply involves joining the table # Add inclusions to the query, which simply involves joining the table
# by flag type ID and target product/component. # by flag type ID and target product/component.
push(@$tables, "INNER JOIN flaginclusions AS i ON flagtypes.id = i.type_id"); push(@$tables, "INNER JOIN flaginclusions AS i ON flagtypes.id = i.type_id");
push(@criteria, "(i.product_id = $product_id OR i.product_id IS NULL)"); push(@criteria, "(i.product_id = $product_id OR i.product_id IS NULL)");
push(@criteria, "(i.component_id = $component_id OR i.component_id IS NULL)");
# Add exclusions to the query, which is more complicated. First of all, # Add exclusions to the query, which is more complicated. First of all,
# we do a LEFT JOIN so we don't miss flag types with no exclusions. # we do a LEFT JOIN so we don't miss flag types with no exclusions.
@ -439,9 +456,19 @@ sub sqlify_criteria {
# component. However, since we want flag types that *aren't* on the # component. However, since we want flag types that *aren't* on the
# exclusions list, we add a WHERE criteria to use only records with # exclusions list, we add a WHERE criteria to use only records with
# NULL exclusion type, i.e. without any exclusions. # NULL exclusion type, i.e. without any exclusions.
my $join_clause = "flagtypes.id = e.type_id " . my $join_clause = "flagtypes.id = e.type_id ";
"AND (e.product_id = $product_id OR e.product_id IS NULL) " .
"AND (e.component_id = $component_id OR e.component_id IS NULL)"; my $addl_join_clause = "";
if ($criteria->{component_id}) {
my $component_id = $criteria->{component_id};
push(@criteria, "(i.component_id = $component_id OR i.component_id IS NULL)");
$join_clause .= "AND (e.component_id = $component_id OR e.component_id IS NULL) ";
}
else {
$addl_join_clause = "AND e.component_id IS NULL OR (i.component_id != e.component_id) ";
}
$join_clause .= "AND ((e.product_id = $product_id $addl_join_clause) OR e.product_id IS NULL)";
push(@$tables, "LEFT JOIN flagexclusions AS e ON ($join_clause)"); push(@$tables, "LEFT JOIN flagexclusions AS e ON ($join_clause)");
push(@criteria, "e.type_id IS NULL"); push(@criteria, "e.type_id IS NULL");
} }

View File

@ -31,6 +31,7 @@ use Bugzilla::Install::Requirements;
use Bugzilla::Mailer; use Bugzilla::Mailer;
use Bugzilla::Series; use Bugzilla::Series;
use Bugzilla::Hook; use Bugzilla::Hook;
use Bugzilla::FlagType;
use Scalar::Util qw(blessed); use Scalar::Util qw(blessed);
@ -113,7 +114,7 @@ sub create {
# for each product in the list, particularly with hundreds or thousands # for each product in the list, particularly with hundreds or thousands
# of products. # of products.
sub preload { sub preload {
my ($products) = @_; my ($products, $preload_flagtypes) = @_;
my %prods = map { $_->id => $_ } @$products; my %prods = map { $_->id => $_ } @$products;
my @prod_ids = keys %prods; my @prod_ids = keys %prods;
return unless @prod_ids; return unless @prod_ids;
@ -130,6 +131,9 @@ sub preload {
push(@{$prods{$product_id}->{"${field}s"}}, $obj); push(@{$prods{$product_id}->{"${field}s"}}, $obj);
} }
} }
if ($preload_flagtypes) {
$_->flag_types foreach @$products;
}
} }
sub update { sub update {
@ -775,18 +779,35 @@ sub user_has_access {
sub flag_types { sub flag_types {
my $self = shift; my $self = shift;
if (!defined $self->{'flag_types'}) { return $self->{'flag_types'} if defined $self->{'flag_types'};
$self->{'flag_types'} = {};
# We cache flag types to avoid useless calls to get_clusions().
my $cache = Bugzilla->request_cache->{flag_types_per_product} ||= {};
$self->{flag_types} = {};
my $prod_id = $self->id;
my $flagtypes = Bugzilla::FlagType::match({ product_id => $prod_id });
foreach my $type ('bug', 'attachment') { foreach my $type ('bug', 'attachment') {
my %flagtypes; my @flags = grep { $_->target_type eq $type } @$flagtypes;
foreach my $component (@{$self->components}) { $self->{flag_types}->{$type} = \@flags;
foreach my $flagtype (@{$component->flag_types->{$type}}) {
$flagtypes{$flagtype->{'id'}} ||= $flagtype; # Also populate component flag types, while we are here.
foreach my $comp (@{$self->components}) {
$comp->{flag_types} ||= {};
my $comp_id = $comp->id;
foreach my $flag (@flags) {
my $flag_id = $flag->id;
$cache->{$flag_id} ||= $flag;
my $i = $cache->{$flag_id}->inclusions_as_hash;
my $e = $cache->{$flag_id}->exclusions_as_hash;
my $included = $i->{0}->{0} || $i->{0}->{$comp_id}
|| $i->{$prod_id}->{0} || $i->{$prod_id}->{$comp_id};
my $excluded = $e->{0}->{0} || $e->{0}->{$comp_id}
|| $e->{$prod_id}->{0} || $e->{$prod_id}->{$comp_id};
push(@{$comp->{flag_types}->{$type}}, $flag) if ($included && !$excluded);
} }
} }
$self->{'flag_types'}->{$type} = [sort { $a->{'sortkey'} <=> $b->{'sortkey'}
|| $a->{'name'} cmp $b->{'name'} } values %flagtypes];
}
} }
return $self->{'flag_types'}; return $self->{'flag_types'};
} }
@ -1040,6 +1061,9 @@ When passed an arrayref of C<Bugzilla::Product> objects, preloads their
L</milestones>, L</components>, and L</versions>, which is much faster L</milestones>, L</components>, and L</versions>, which is much faster
than calling those accessors on every item in the array individually. than calling those accessors on every item in the array individually.
If the 2nd argument passed to C<preload> is true, flag types for these
products and their components are also preloaded.
This function is not exported, so must be called like This function is not exported, so must be called like
C<Bugzilla::Product::preload($products)>. C<Bugzilla::Product::preload($products)>.

View File

@ -81,7 +81,8 @@ if ($cgi->param('product')) {
$vars->{'products'} = $user->get_selectable_products; $vars->{'products'} = $user->get_selectable_products;
} }
Bugzilla::Product::preload($vars->{'products'}); # We set the 2nd argument to 1 to also preload flag types.
Bugzilla::Product::preload($vars->{'products'}, 1);
# Allow consumers to specify whether or not they want flag data. # Allow consumers to specify whether or not they want flag data.
if (defined $cgi->param('flags')) { if (defined $cgi->param('flags')) {