summaryrefslogtreecommitdiffstats
path: root/Bugzilla.pm
diff options
context:
space:
mode:
Diffstat (limited to 'Bugzilla.pm')
-rw-r--r--Bugzilla.pm286
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
+