API: (bug 20554) Expose average slave lag (avglag) as well as maxlag. Patch by Sam...
authorRoan Kattouw <catrope@users.mediawiki.org>
Fri, 6 Nov 2009 14:38:55 +0000 (14:38 +0000)
committerRoan Kattouw <catrope@users.mediawiki.org>
Fri, 6 Nov 2009 14:38:55 +0000 (14:38 +0000)
RELEASE-NOTES
includes/GlobalFunctions.php
includes/Wiki.php
includes/api/ApiBase.php
includes/api/ApiHelp.php
includes/api/ApiMain.php
includes/api/ApiQuery.php
includes/api/ApiQuerySiteinfo.php
includes/db/LoadBalancer.php
index.php

index a0cadd5..bf4aebc 100644 (file)
@@ -684,6 +684,7 @@ Hopefully we will remove this configuration var soon)
 * (bug 19004) Added support for tags
 * (bug 21083) list=allusers no longer returns current timestamp for users
   without registration date
+* (bug 20554) Expose average slave lag (avglag) as well as maxlag
 
 === Languages updated in 1.16 ===
 
index b92e48f..60197ee 100644 (file)
@@ -3113,6 +3113,21 @@ function wfMaxlagError( $host, $lag, $maxLag ) {
        }
 }
 
+/**
+ * Displays a avglag error
+ *
+ * @param $lag Integer: avglag (actual)
+ * @param $avgLag Integer: avglag (requested)
+ */
+function wfAvglagError( $lag, $avgLag ) {
+       header( 'HTTP/1.1 503 Service Unavailable' );
+       header( 'Retry-After: ' . max( intval( $avgLag ), 5 ) );
+       header( 'X-Database-Lag: ' . intval( $lag ) );
+       header( 'Content-Type: text/plain' );
+
+       echo "Lagged: $lag seconds average\n";
+}
+
 /**
  * Throws a warning that $function is deprecated
  * @param $function String
index 286bc8e..c7d80c2 100644 (file)
@@ -95,6 +95,24 @@ class MediaWiki {
                        return true;
                }
        }
+       
+       /**
+        * Check if the average lag of database slaves is higher that $avgLag, and
+        * if it's the case, output an error message
+        *
+        * @param $avgLag int: maximum lag allowed for the request, as supplied by
+        *                the client
+        * @return bool true if the request can continue
+        */
+       function checkAvgLag( $avgLag ) {
+               list( $host, $lag ) = wfGetLB()->getAvgLag();
+               if( $lag > $avgLag ) {
+                       wfAvglagError( $lag, $avgLag );
+                       return false;
+               } else {
+                       return true;
+               }
+       }
 
        /**
         * Checks some initial queries
index dd496b8..0d1db32 100644 (file)
@@ -935,6 +935,14 @@ abstract class ApiBase {
        public function shouldCheckMaxlag() {
                return true;
        }
+       
+       /**
+        * Indicates if this module needs avglag to be checked
+        * @return bool
+        */
+       public function shouldCheckAvglag() {
+               return true;
+       }
 
        /**
         * Indicates whether this module requires read rights
index 982bf4d..0200062 100644 (file)
@@ -49,6 +49,10 @@ class ApiHelp extends ApiBase {
        public function shouldCheckMaxlag() {
                return false;
        }
+       
+       public function shouldCheckAvglag() {
+               return false;
+       }
 
        public function isReadMode() {
                return false;
index 6867193..e930e7f 100644 (file)
@@ -394,6 +394,20 @@ class ApiMain extends ApiBase {
                                return;
                        }
                }
+               
+               if( $module->shouldCheckAvglag() && isset( $params['avglag'] ) ) {
+                       // Check for avglag
+                       global $wgShowHostnames;
+                       $avgLag = $params['avglag'];
+                       $lag = wfGetLB()->getAvgLag();
+                       if ( $lag > $avgLag ) {
+                               header( 'Retry-After: ' . max( intval( $avgLag ), 5 ) );
+                               header( 'X-Database-Lag: ' . intval( $lag ) );
+
+                               $this->dieUsage( "Lag: $lag seconds average", 'avglag' );
+                               return;
+                       }
+               }
 
                global $wgUser;
                if ($module->isReadMode() && !$wgUser->isAllowed('read'))
@@ -477,6 +491,9 @@ class ApiMain extends ApiBase {
                        'maxlag'  => array (
                                ApiBase :: PARAM_TYPE => 'integer'
                        ),
+                       'avglag'  => array (
+                               ApiBase :: PARAM_TYPE => 'integer'
+                       ),
                        'smaxage' => array (
                                ApiBase :: PARAM_TYPE => 'integer',
                                ApiBase :: PARAM_DFLT => 0
@@ -498,6 +515,7 @@ class ApiMain extends ApiBase {
                        'action' => 'What action you would like to perform',
                        'version' => 'When showing help, include version for each module',
                        'maxlag' => 'Maximum lag',
+                       'avglag' => 'Average lag',
                        'smaxage' => 'Set the s-maxage header to this many seconds. Errors are never cached',
                        'maxage' => 'Set the max-age header to this many seconds. Errors are never cached',
                        'requestid' => 'Request ID to distinguish requests. This will just be output back to you',
index e15edb6..f2e90b1 100644 (file)
@@ -548,6 +548,10 @@ class ApiQuery extends ApiBase {
        public function shouldCheckMaxlag() {
                return true;
        }
+       
+       public function shouldCheckAvglag() {
+               return true;
+       }
 
        public function getParamDescription() {
                return array (
index 8439e47..287887b 100644 (file)
@@ -295,6 +295,7 @@ class ApiQuerySiteinfo extends ApiQueryBase {
                        );
                }
 
+               $data['avglag'] = wfGetLB()->getAvgLag();
                $result = $this->getResult();
                $result->setIndexedTagName( $data, 'db' );
                return $this->getResult()->addValue( 'query', $property, $data );
index 0b8ef05..5e91f1e 100644 (file)
@@ -898,6 +898,27 @@ class LoadBalancer {
                }
                return array( $host, $maxLag );
        }
+       
+       /**
+        * Gets the average lag of slaves.
+        * May attempt to open connections to slaves on the default DB.
+        */
+       function getAvgLag() {
+               $lag = 0;
+               $count = 0;
+               foreach ( $this->mServers as $i => $conn ) {
+                       $conn = $this->getAnyOpenConnection( $i );
+                       if ( !$conn ) {
+                               $conn = $this->openConnection( $i );
+                       }
+                       if ( !$conn ) {
+                               continue;
+                       }
+                       $lag += $conn->getLag();
+                       $count++;
+               }
+               return ($count > 1) ? $lag / $count : $lag;
+       }
 
        /**
         * Get lag time for each server
index 1cbba1e..bbc77e5 100644 (file)
--- a/index.php
+++ b/index.php
@@ -53,6 +53,11 @@ if( !is_null( $maxLag ) && !$mediaWiki->checkMaxLag( $maxLag ) ) {
        exit;
 }
 
+$avgLag = $wgRequest->getVal( 'avglag' );
+if( !is_null( $avgLag ) && !$mediaWiki->checkAvgLag( $avgLag ) ) {
+       exit;
+}
+
 # Query string fields
 $action = $wgRequest->getVal( 'action', 'view' );
 $title = $wgRequest->getVal( 'title' );