Beispiel #1
0
 /**
  * Get the index of the reader connection, which may be a slave
  * This takes into account load ratios and lag times. It should
  * always return a consistent index during a given invocation
  *
  * Side effect: opens connections to databases
  */
 function getReaderIndex($group = false, $wiki = false)
 {
     global $wgReadOnly, $wgDBClusterTimeout, $wgDBAvgStatusPoll, $wgDBtype;
     # FIXME: For now, only go through all this for mysql databases
     if ($wgDBtype != 'mysql') {
         return $this->getWriterIndex();
     }
     if (count($this->mServers) == 1) {
         # Skip the load balancing if there's only one server
         return 0;
     } elseif ($group === false and $this->mReadIndex >= 0) {
         # Shortcut if generic reader exists already
         return $this->mReadIndex;
     }
     wfProfileIn(__METHOD__);
     $totalElapsed = 0;
     # convert from seconds to microseconds
     $timeout = $wgDBClusterTimeout * 1000000.0;
     # Find the relevant load array
     if ($group !== false) {
         if (isset($this->mGroupLoads[$group])) {
             $nonErrorLoads = $this->mGroupLoads[$group];
         } else {
             # No loads for this group, return false and the caller can use some other group
             wfDebug(__METHOD__ . ": no loads for group {$group}\n");
             wfProfileOut(__METHOD__);
             return false;
         }
     } else {
         $nonErrorLoads = $this->mLoads;
     }
     if (!$nonErrorLoads) {
         throw new MWException("Empty server array given to LoadBalancer");
     }
     # Scale the configured load ratios according to the dynamic load (if the load monitor supports it)
     $this->getLoadMonitor()->scaleLoads($nonErrorLoads, $group, $wiki);
     $i = false;
     $found = false;
     $laggedSlaveMode = false;
     # First try quickly looking through the available servers for a server that
     # meets our criteria
     do {
         $totalThreadsConnected = 0;
         $overloadedServers = 0;
         $currentLoads = $nonErrorLoads;
         while (count($currentLoads)) {
             if ($wgReadOnly || $this->mAllowLagged || $laggedSlaveMode) {
                 $i = $this->pickRandom($currentLoads);
             } else {
                 $i = $this->getRandomNonLagged($currentLoads, $wiki);
                 if ($i === false && count($currentLoads) != 0) {
                     # All slaves lagged. Switch to read-only mode
                     $wgReadOnly = wfMsgNoDBForContent('readonly_lag');
                     $i = $this->pickRandom($currentLoads);
                     $laggedSlaveMode = true;
                 }
             }
             if ($i === false) {
                 # pickRandom() returned false
                 # This is permanent and means the configuration or the load monitor
                 # wants us to return false.
                 wfDebugLog('connect', __METHOD__ . ": pickRandom() returned false\n");
                 wfProfileOut(__METHOD__);
                 return false;
             }
             wfDebugLog('connect', __METHOD__ . ": Using reader #{$i}: {$this->mServers[$i]['host']}...\n");
             $conn = $this->openConnection($i, $wiki);
             if (!$conn) {
                 wfDebugLog('connect', __METHOD__ . ": Failed connecting to {$i}/{$wiki}\n");
                 unset($nonErrorLoads[$i]);
                 unset($currentLoads[$i]);
                 continue;
             }
             // Perform post-connection backoff
             $threshold = isset($this->mServers[$i]['max threads']) ? $this->mServers[$i]['max threads'] : false;
             $backoff = $this->getLoadMonitor()->postConnectionBackoff($conn, $threshold);
             // Decrement reference counter, we are finished with this connection.
             // It will be incremented for the caller later.
             if ($wiki !== false) {
                 $this->reuseConnection($conn);
             }
             if ($backoff) {
                 # Post-connection overload, don't use this server for now
                 $totalThreadsConnected += $backoff;
                 $overloadedServers++;
                 unset($currentLoads[$i]);
             } else {
                 # Return this server
                 break 2;
             }
         }
         # No server found yet
         $i = false;
         # If all servers were down, quit now
         if (!count($nonErrorLoads)) {
             wfDebugLog('connect', "All servers down\n");
             break;
         }
         # Some servers must have been overloaded
         if ($overloadedServers == 0) {
             throw new MWException(__METHOD__ . ": unexpectedly found no overloaded servers");
         }
         # Back off for a while
         # Scale the sleep time by the number of connected threads, to produce a
         # roughly constant global poll rate
         $avgThreads = $totalThreadsConnected / $overloadedServers;
         $totalElapsed += $this->sleep($wgDBAvgStatusPoll * $avgThreads);
     } while ($totalElapsed < $timeout);
     if ($totalElapsed >= $timeout) {
         wfDebugLog('connect', "All servers busy\n");
         $this->mErrorConnection = false;
         $this->mLastError = 'All servers busy';
     }
     if ($i !== false) {
         # Slave connection successful
         # Wait for the session master pos for a short time
         if ($this->mWaitForPos && $i > 0) {
             if (!$this->doWait($i)) {
                 $this->mServers[$i]['slave pos'] = $conn->getSlavePos();
             }
         }
         if ($this->mReadIndex <= 0 && $this->mLoads[$i] > 0 && $i !== false) {
             $this->mReadIndex = $i;
         }
     }
     wfProfileOut(__METHOD__);
     return $i;
 }
Beispiel #2
0
 /**
  * Get the index of the reader connection, which may be a slave
  * This takes into account load ratios and lag times. It should
  * always return a consistent index during a given invocation
  *
  * Side effect: opens connections to databases
  */
 function getReaderIndex()
 {
     global $wgReadOnly, $wgDBClusterTimeout;
     $fname = 'LoadBalancer::getReaderIndex';
     wfProfileIn($fname);
     $i = false;
     if ($this->mForce >= 0) {
         $i = $this->mForce;
     } else {
         if ($this->mReadIndex >= 0) {
             $i = $this->mReadIndex;
         } else {
             # $loads is $this->mLoads except with elements knocked out if they
             # don't work
             $loads = $this->mLoads;
             $done = false;
             $totalElapsed = 0;
             do {
                 if ($wgReadOnly or $this->mAllowLagged) {
                     $i = $this->pickRandom($loads);
                 } else {
                     $i = $this->getRandomNonLagged($loads);
                     if ($i === false && count($loads) != 0) {
                         # All slaves lagged. Switch to read-only mode
                         $wgReadOnly = wfMsgNoDBForContent('readonly_lag');
                         $i = $this->pickRandom($loads);
                     }
                 }
                 $serverIndex = $i;
                 if ($i !== false) {
                     wfDebugLog('connect', "{$fname}: Using reader #{$i}: {$this->mServers[$i]['host']}...\n");
                     $this->openConnection($i);
                     if (!$this->isOpen($i)) {
                         wfDebug("{$fname}: Failed\n");
                         unset($loads[$i]);
                         $sleepTime = 0;
                     } else {
                         $status = $this->mConnections[$i]->getStatus("Thread%");
                         if (isset($this->mServers[$i]['max threads']) && $status['Threads_running'] > $this->mServers[$i]['max threads']) {
                             # Too much load, back off and wait for a while.
                             # The sleep time is scaled by the number of threads connected,
                             # to produce a roughly constant global poll rate.
                             $sleepTime = self::AVG_STATUS_POLL * $status['Threads_connected'];
                             # If we reach the timeout and exit the loop, don't use it
                             $i = false;
                         } else {
                             $done = true;
                             $sleepTime = 0;
                         }
                     }
                 } else {
                     $sleepTime = 500000;
                 }
                 if ($sleepTime) {
                     $totalElapsed += $sleepTime;
                     $x = "{$this->mServers[$serverIndex]['host']} [{$serverIndex}]";
                     wfProfileIn("{$fname}-sleep {$x}");
                     usleep($sleepTime);
                     wfProfileOut("{$fname}-sleep {$x}");
                 }
             } while (count($loads) && !$done && $totalElapsed / 1000000.0 < $wgDBClusterTimeout);
             if ($totalElapsed / 1000000.0 >= $wgDBClusterTimeout) {
                 $this->mErrorConnection = false;
                 $this->mLastError = 'All servers busy';
             }
             if ($i !== false && $this->isOpen($i)) {
                 # Wait for the session master pos for a short time
                 if ($this->mWaitForFile) {
                     if (!$this->doWait($i)) {
                         $this->mServers[$i]['slave pos'] = $this->mConnections[$i]->getSlavePos();
                     }
                 }
                 if ($i !== false) {
                     $this->mReadIndex = $i;
                 }
             } else {
                 $i = false;
             }
         }
     }
     wfProfileOut($fname);
     return $i;
 }