diff options
Diffstat (limited to 'Bugzilla.pm')
-rw-r--r-- | Bugzilla.pm | 286 |
1 files changed, 229 insertions, 57 deletions
diff --git a/Bugzilla.pm b/Bugzilla.pm index 5b39e4c81..52a44e375 100644 --- a/Bugzilla.pm +++ b/Bugzilla.pm @@ -35,23 +35,29 @@ BEGIN { } } -use Bugzilla::Config; -use Bugzilla::Constants; use Bugzilla::Auth; use Bugzilla::Auth::Persist::Cookie; use Bugzilla::CGI; -use Bugzilla::Extension; +use Bugzilla::Config; +use Bugzilla::Constants; use Bugzilla::DB; +use Bugzilla::Error; +use Bugzilla::Extension; +use Bugzilla::Field; +use Bugzilla::Flag; +use Bugzilla::Hook; use Bugzilla::Install::Localconfig qw(read_localconfig); use Bugzilla::Install::Requirements qw(OPTIONAL_MODULES); -use Bugzilla::Install::Util qw(init_console); +use Bugzilla::Install::Util qw(init_console include_languages); +use Bugzilla::Memcached; use Bugzilla::Template; +use Bugzilla::Token; use Bugzilla::User; -use Bugzilla::Error; use Bugzilla::Util; -use Bugzilla::Field; -use Bugzilla::Flag; -use Bugzilla::Token; + +use Bugzilla::Metrics::Collector; +use Bugzilla::Metrics::Template; +use Bugzilla::Metrics::Memcached; use File::Basename; use File::Spec::Functions; @@ -84,7 +90,7 @@ use constant SHUTDOWNHTML_RETRY_AFTER => 3600; # Global Code ##################################################################### -# $::SIG{__DIE__} = i_am_cgi() ? \&CGI::Carp::confess : \&Carp::confess; +#$::SIG{__DIE__} = i_am_cgi() ? \&CGI::Carp::confess : \&Carp::confess; # Note that this is a raw subroutine, not a method, so $class isn't available. sub init_page { @@ -125,6 +131,30 @@ sub init_page { my $script = basename($0); + # BMO - init metrics collection if required + if (i_am_cgi() && $script eq 'show_bug.cgi') { + # we need to measure loading the params, so default to on + Bugzilla->metrics_enabled(1); + Bugzilla->metrics($script); + # we can now hit params to check if we really should be enabled. + # note - we can't use anything which uses templates or the database, as + # that would initialise those modules with metrics enabled. + if (!Bugzilla->params->{metrics_enabled}) { + Bugzilla->metrics_enabled(0); + } + else { + # to avoid generating massive amounts of data, we're only interested in + # a small subset of users + my $user_id = Bugzilla->cgi->cookie('Bugzilla_login'); + if (!$user_id + || !grep { $user_id == $_ } + split(/\s*,\s*/, Bugzilla->params->{metrics_user_ids})) + { + Bugzilla->metrics_enabled(0); + } + } + } + # Because of attachment_base, attachment.cgi handles this itself. if ($script ne 'attachment.cgi') { do_ssl_redirect_if_required(); @@ -193,9 +223,12 @@ sub init_page { ##################################################################### sub template { - my $class = shift; - $class->request_cache->{template} ||= Bugzilla::Template->create(); - return $class->request_cache->{template}; + # BMO - use metrics subclass if required + if (Bugzilla->metrics_enabled) { + return $_[0]->request_cache->{template} ||= Bugzilla::Metrics::Template->create(); + } else { + return $_[0]->request_cache->{template} ||= Bugzilla::Template->create(); + } } sub template_inner { @@ -203,9 +236,7 @@ sub template_inner { my $cache = $class->request_cache; my $current_lang = $cache->{template_current_lang}->[0]; $lang ||= $current_lang || ''; - $class->request_cache->{"template_inner_$lang"} - ||= Bugzilla::Template->create(language => $lang); - return $class->request_cache->{"template_inner_$lang"}; + return $cache->{"template_inner_$lang"} ||= Bugzilla::Template->create(language => $lang); } our $extension_packages; @@ -264,9 +295,7 @@ sub feature { } sub cgi { - my $class = shift; - $class->request_cache->{cgi} ||= new Bugzilla::CGI(); - return $class->request_cache->{cgi}; + return $_[0]->request_cache->{cgi} ||= new Bugzilla::CGI(); } sub input_params { @@ -286,21 +315,15 @@ sub input_params { } sub localconfig { - my $class = shift; - $class->request_cache->{localconfig} ||= read_localconfig(); - return $class->request_cache->{localconfig}; + return $_[0]->process_cache->{localconfig} ||= read_localconfig(); } sub params { - my $class = shift; - $class->request_cache->{params} ||= Bugzilla::Config::read_param_file(); - return $class->request_cache->{params}; + return $_[0]->request_cache->{params} ||= Bugzilla::Config::read_param_file(); } sub user { - my $class = shift; - $class->request_cache->{user} ||= new Bugzilla::User; - return $class->request_cache->{user}; + return $_[0]->request_cache->{user} ||= new Bugzilla::User; } sub set_user { @@ -309,8 +332,7 @@ sub set_user { } sub sudoer { - my $class = shift; - return $class->request_cache->{sudoer}; + return $_[0]->request_cache->{sudoer}; } sub sudo_request { @@ -388,6 +410,12 @@ sub login { $class->set_user($authenticated_user); } + if (Bugzilla->sudoer) { + Bugzilla->sudoer->update_last_seen_date(); + } else { + $class->user->update_last_seen_date(); + } + return $class->user; } @@ -426,31 +454,27 @@ sub logout_request { } sub job_queue { - my $class = shift; require Bugzilla::JobQueue; - $class->request_cache->{job_queue} ||= Bugzilla::JobQueue->new(); - return $class->request_cache->{job_queue}; + return $_[0]->request_cache->{job_queue} ||= Bugzilla::JobQueue->new(); } sub dbh { - my $class = shift; # If we're not connected, then we must want the main db - $class->request_cache->{dbh} ||= $class->dbh_main; - - return $class->request_cache->{dbh}; + return $_[0]->request_cache->{dbh} ||= $_[0]->dbh_main; } sub dbh_main { - my $class = shift; - $class->request_cache->{dbh_main} ||= Bugzilla::DB::connect_main(); - return $class->request_cache->{dbh_main}; + return $_[0]->request_cache->{dbh_main} ||= Bugzilla::DB::connect_main(); } sub languages { - my $class = shift; return Bugzilla::Install::Util::supported_languages(); } +sub current_language { + return $_[0]->request_cache->{current_language} ||= (include_languages())[0]; +} + sub error_mode { my ($class, $newval) = @_; if (defined $newval) { @@ -490,6 +514,9 @@ sub usage_mode { elsif ($newval == USAGE_MODE_TEST) { $class->error_mode(ERROR_MODE_TEST); } + elsif ($newval == USAGE_MODE_REST) { + $class->error_mode(ERROR_MODE_REST); + } else { ThrowCodeError('usage_mode_invalid', {'invalid_usage_mode', $newval}); @@ -597,12 +624,20 @@ sub fields { } sub active_custom_fields { - my $class = shift; - if (!exists $class->request_cache->{active_custom_fields}) { - $class->request_cache->{active_custom_fields} = - Bugzilla::Field->match({ custom => 1, obsolete => 0 }); + my ($class, $params) = @_; + my $cache_id = 'active_custom_fields'; + if ($params) { + $cache_id .= ($params->{product} ? '_p' . $params->{product}->id : '') . + ($params->{component} ? '_c' . $params->{component}->id : ''); } - return @{$class->request_cache->{active_custom_fields}}; + if (!exists $class->request_cache->{$cache_id}) { + my $fields = Bugzilla::Field->match({ custom => 1, obsolete => 0}); + @$fields = grep($_->type ne FIELD_TYPE_EXTENSION, @$fields); + Bugzilla::Hook::process('active_custom_fields', + { fields => \$fields, params => $params }); + $class->request_cache->{$cache_id} = $fields; + } + return @{$class->request_cache->{$cache_id}}; } sub has_flags { @@ -615,13 +650,8 @@ sub has_flags { } sub local_timezone { - my $class = shift; - - if (!defined $class->request_cache->{local_timezone}) { - $class->request_cache->{local_timezone} = - DateTime::TimeZone->new(name => 'local'); - } - return $class->request_cache->{local_timezone}; + return $_[0]->process_cache->{local_timezone} + ||= DateTime::TimeZone->new(name => 'local'); } # This creates the request cache for non-mod_perl installations. @@ -642,11 +672,70 @@ sub request_cache { return $_request_cache; } +sub clear_request_cache { + $_request_cache = {}; + if ($ENV{MOD_PERL}) { + require Apache2::RequestUtil; + my $request = eval { Apache2::RequestUtil->request }; + if ($request) { + my $pnotes = $request->pnotes; + delete @$pnotes{(keys %$pnotes)}; + } + } +} + +# This is a per-process cache. Under mod_cgi it's identical to the +# request_cache. When using mod_perl, items in this cache live until the +# worker process is terminated. +our $_process_cache = {}; + +sub process_cache { + return $_process_cache; +} + +# BMO - Instrumentation + +sub metrics_enabled { + if (defined $_[1]) { + if (!$_[1] + && $_[0]->request_cache->{metrics_enabled} + && $_[0]->request_cache->{metrics}) + { + $_[0]->request_cache->{metrics}->cancel(); + delete $_[0]->request_cache->{metrics}; + } + $_[0]->request_cache->{metrics_enabled} = $_[1]; + } + else { + return $_[0]->request_cache->{metrics_enabled}; + } +} + +sub metrics { + return $_[0]->request_cache->{metrics} ||= Bugzilla::Metrics::Collector->new($_[1]); +} + +# This is a memcached wrapper, which provides cross-process and cross-system +# caching. +sub memcached { + # BMO - use metrics subclass if required + if (Bugzilla->metrics_enabled) { + return $_[0]->request_cache->{memcached} ||= Bugzilla::Metrics::Memcached->_new(); + } else { + return $_[0]->request_cache->{memcached} ||= Bugzilla::Memcached->_new(); + } +} + # Private methods # Per-process cleanup. Note that this is a plain subroutine, not a method, # so we don't have $class available. sub _cleanup { + # BMO - finalise and report on metrics + if (Bugzilla->metrics_enabled) { + Bugzilla->metrics->finish(); + } + my $main = Bugzilla->request_cache->{dbh_main}; my $shadow = Bugzilla->request_cache->{dbh_shadow}; foreach my $dbh ($main, $shadow) { @@ -654,7 +743,7 @@ sub _cleanup { $dbh->bz_rollback_transaction() if $dbh->bz_in_transaction; $dbh->disconnect; } - undef $_request_cache; + clear_request_cache(); # These are both set by CGI.pm but need to be undone so that # Apache can actually shut down its children if it needs to. @@ -775,10 +864,10 @@ not an arrayref. =item C<user> -C<undef> if there is no currently logged in user or if the login code has not -yet been run. If an sudo session is in progress, the C<Bugzilla::User> -corresponding to the person who is being impersonated. If no session is in -progress, the current C<Bugzilla::User>. +Default C<Bugzilla::User> object if there is no currently logged in user or +if the login code has not yet been run. If an sudo session is in progress, +the C<Bugzilla::User> corresponding to the person who is being impersonated. +If no session is in progress, the current C<Bugzilla::User>. =item C<set_user> @@ -915,6 +1004,10 @@ The main database handle. See L<DBI>. Currently installed languages. Returns a reference to a list of RFC 1766 language tags of installed languages. +=item C<current_language> + +The currently active language. + =item C<switch_to_shadow_db> Switch from using the main database to using the shadow database. @@ -947,3 +1040,82 @@ Tells you whether or not a specific feature is enabled. For names of features, see C<OPTIONAL_MODULES> in C<Bugzilla::Install::Requirements>. =back + +=head1 B<CACHING> + +Bugzilla has several different caches available which provide different +capabilities and lifetimes. + +The keys of all caches are unregulated; use of prefixes is suggested to avoid +collisions. + +=over + +=item B<Request Cache> + +The request cache is a hashref which supports caching any perl variable for the +duration of the current request. At the end of the current request the contents +of this cache are cleared. + +Examples of its use include caching objects to avoid re-fetching the same data +from the database, and passing data between otherwise unconnected parts of +Bugzilla. + +=over + +=item C<request_cache> + +Returns a hashref which can be checked and modified to store any perl variable +for the duration of the current request. + +=item C<clear_request_cache> + +Removes all entries from the C<request_cache>. + +=back + +=item B<Process Cache> + +The process cache is a hashref which support caching of any perl variable. If +Bugzilla is configured to run using Apache mod_perl, the contents of this cache +are persisted across requests for the lifetime of the Apache worker process +(which varies depending on the SizeLimit configuration in mod_perl.pl). + +If Bugzilla isn't running under mod_perl, the process cache's contents are +cleared at the end of the request. + +The process cache is only suitable for items which never change while Bugzilla +is running (for example the path where Bugzilla is installed). + +=over + +=item C<process_cache> + +Returns a hashref which can be checked and modified to store any perl variable +for the duration of the current process (mod_perl) or request (mod_cgi). + +=back + +=item B<Memcached> + +If Memcached is installed and configured, Bugzilla can use it to cache data +across requests and between webheads. Unlike the request and process caches, +only scalars, hashrefs, and arrayrefs can be stored in Memcached. + +Memcached integration is only required for large installations of Bugzilla -- if +you have multiple webheads then configuring Memcached is recommended. + +=over + +=item C<memcached> + +Returns a C<Bugzilla::Memcached> object. An object is always returned even if +Memcached is not available. + +See the documentation for the C<Bugzilla::Memcached> module for more +information. + +=back + +=back + |