]> Lady’s Gitweb - Gitweb/commitdiff
gitweb: Load checking
authorJohn 'Warthog9' Hawley <redacted>
Sat, 30 Jan 2010 22:30:39 +0000 (23:30 +0100)
committerLady <redacted>
Mon, 6 Apr 2026 04:50:39 +0000 (00:50 -0400)
This changes slightly the behavior of gitweb, so that it verifies
that the box isn't inundated with before attempting to serve gitweb.
If the box is overloaded, it basically returns a 503 Server Unavailable
until the load falls below the defined threshold.  This helps dramatically
if you have a box that's I/O bound, reaches a certain load and you
don't want gitweb, the I/O hog that it is, increasing the pain the
server is already undergoing.

This behavior is controlled by $maxload configuration variable.
Default is a load of 300, which for most cases should never be hit.
Unset it (set it to undefined value, i.e. undef) to turn off checking.

Currently it requires that '/proc/loadavg' file exists, otherwise the
load check is bypassed (load is taken to be 0).  So platforms that do
not implement '/proc/loadavg' currently cannot use this feature
(provisions are included for additional checks to be added by others).

There is simple test in t/t9501-gitweb-standalone-http-status.sh to
check that it correctly returns "503 Service Unavailable" if load is
too high, and also if there are any Perl warnings or errors.

Signed-off-by: John 'Warthog9' Hawley <redacted>
Signed-off-by: Jakub Narebski <redacted>
Signed-off-by: Junio C Hamano <redacted>
README
gitweb.perl

diff --git a/README b/README
index 6b89a66cf20c6bc8b3adeca971431ad8964a7eff03210d7b123e1cebaa483eb7..659de2bd005e6ee1fcd2e660ab907016c6dd30b0d5c4cc71f9996d608b31007e 100644 (file)
--- a/README
+++ b/README
@@ -173,7 +173,7 @@ not include variables usually directly set during build):
    Base URL for relative URLs in pages generated by gitweb,
    (e.g. $logo, $favicon, @stylesheets if they are relative URLs),
    needed and used only for URLs with nonempty PATH_INFO via
-   <base href="$base_url>.  Usually gitweb sets its value correctly,
+   <base href="$base_url">.  Usually gitweb sets its value correctly,
    and there is no need to set this variable, e.g. to $my_uri or "/".
  * $home_link
    Target of the home link on top of all pages (the first part of view
@@ -227,6 +227,11 @@ not include variables usually directly set during build):
    repositories from launching cross-site scripting (XSS) attacks.  Set this
    to true if you don't trust the content of your repositories. The default
    is false.
+ * $maxload
+   Used to set the maximum load that we will still respond to gitweb queries.
+   If server load exceed this value then return "503 Service Unavaliable" error.
+   Server load is taken to be 0 if gitweb cannot determine its value.  Set it to
+   undefined value to turn it off.  The default is 300.
 
 
 Projects list file format
index 0f6336eb8fcf434c64fd597bc968f803f70b84cc114ad4a460ead5d8e1f8bd5a..3f88ed5fb724af2e41972aeb4d7e827dc1f042680e2188b9e4844e5a278716f7 100755 (executable)
@@ -222,6 +222,12 @@ our %avatar_size = (
        'double'  => 32
 );
 
+# Used to set the maximum load that we will still respond to gitweb queries.
+# If server load exceed this value then return "503 server busy" error.
+# If gitweb cannot determined server load, it is taken to be 0.
+# Leave it undefined (or set to 'undef') to turn off load checking.
+our $maxload = 300;
+
 # You define site-wide feature defaults here; override them with
 # $GITWEB_CONFIG as necessary.
 our %feature = (
@@ -552,12 +558,38 @@ if (-e $GITWEB_CONFIG) {
        do $GITWEB_CONFIG_SYSTEM if -e $GITWEB_CONFIG_SYSTEM;
 }
 
+# Get loadavg of system, to compare against $maxload.
+# Currently it requires '/proc/loadavg' present to get loadavg;
+# if it is not present it returns 0, which means no load checking.
+sub get_loadavg {
+       if( -e '/proc/loadavg' ){
+               open my $fd, '<', '/proc/loadavg'
+                       or return 0;
+               my @load = split(/\s+/, scalar <$fd>);
+               close $fd;
+
+               # The first three columns measure CPU and IO utilization of the last one,
+               # five, and 10 minute periods.  The fourth column shows the number of
+               # currently running processes and the total number of processes in the m/n
+               # format.  The last column displays the last process ID used.
+               return $load[0] || 0;
+       }
+       # additional checks for load average should go here for things that don't export
+       # /proc/loadavg
+
+       return 0;
+}
+
 # version of the core git binary
 our $git_version = qx("$GIT" --version) =~ m/git version (.*)$/ ? $1 : "unknown";
 $number_of_git_cmds++;
 
 $projects_list ||= $projectroot;
 
+if (defined $maxload && get_loadavg() > $maxload) {
+       die_error(503, "The load average on the server is too high");
+}
+
 # ======================================================================
 # input validation and dispatch
 
@@ -3329,7 +3361,8 @@ sub git_footer_html {
        }
 
        print qq!<script type="text/javascript" src="$javascript"></script>\n!;
-       if ($action eq 'blame_incremental') {
+       if (defined $action &&
+           $action eq 'blame_incremental') {
                print qq!<script type="text/javascript">\n!.
                      qq!startBlame("!. href(action=>"blame_data", -replay=>1) .qq!",\n!.
                      qq!           "!. href() .qq!");\n!.
@@ -3355,14 +3388,19 @@ sub git_footer_html {
 # 500: The server isn't configured properly, or
 #      an internal error occurred (e.g. failed assertions caused by bugs), or
 #      an unknown error occurred (e.g. the git binary died unexpectedly).
+# 503: The server is currently unavailable (because it is overloaded,
+#      or down for maintenance).  Generally, this is a temporary state.
 sub die_error {
        my $status = shift || 500;
        my $error = shift || "Internal server error";
 
-       my %http_responses = (400 => '400 Bad Request',
-                             403 => '403 Forbidden',
-                             404 => '404 Not Found',
-                             500 => '500 Internal Server Error');
+       my %http_responses = (
+               400 => '400 Bad Request',
+               403 => '403 Forbidden',
+               404 => '404 Not Found',
+               500 => '500 Internal Server Error',
+               503 => '503 Service Unavailable',
+       );
        git_header_html($http_responses{$status});
        print <<EOF;
 <div class="page_body">
This page took 0.3454 seconds and 4 git commands to generate.