Index: trunk/extensions/Translate/utils/TranslationHelpers.php |
— | — | @@ -139,10 +139,14 @@ |
140 | 140 | */ |
141 | 141 | protected function getTmBox() { |
142 | 142 | global $wgTranslateTM; |
| 143 | + |
143 | 144 | if ( $wgTranslateTM === false ) return null; |
144 | 145 | if ( !$this->targetLanguage ) return null; |
145 | 146 | if ( strval( $this->getDefinition() ) === '' ) return null; |
146 | 147 | |
| 148 | + $serviceName = 'tmserver'; |
| 149 | + if ( self::checkTranslationServiceFailure( $serviceName ) ) return null; |
| 150 | + |
147 | 151 | // Needed data |
148 | 152 | $code = $this->targetLanguage; |
149 | 153 | $definition = $this->getDefinition(); |
— | — | @@ -191,6 +195,9 @@ |
192 | 196 | $legend = implode( ' | ', $legend ); |
193 | 197 | $boxes[] = Html::rawElement( 'div', $params, self::legend( $legend ) . $text . self::clear() ) . "\n"; |
194 | 198 | } |
| 199 | + } else { |
| 200 | + // Assume timeout |
| 201 | + self::reportTranslationSerficeFailure( $serviceName ); |
195 | 202 | } |
196 | 203 | |
197 | 204 | // Limit to three max |
— | — | @@ -217,6 +224,9 @@ |
218 | 225 | protected function getGoogleSuggestion() { |
219 | 226 | global $wgProxyKey, $wgGoogleApiKey, $wgMemc; |
220 | 227 | |
| 228 | + $serviceName = 'Google'; |
| 229 | + if ( self::checkTranslationServiceFailure( $serviceName ) ) return null; |
| 230 | + |
221 | 231 | $code = $this->targetLanguage; |
222 | 232 | $definition = trim( strval( $this->getDefinition() ) ) ; |
223 | 233 | |
— | — | @@ -246,6 +256,8 @@ |
247 | 257 | |
248 | 258 | if ( $google_json === false ) { |
249 | 259 | wfWarn( __METHOD__ . ': Http::get failed' ); |
| 260 | + // Most likely a timeout or other general error |
| 261 | + self::reportTranslationSerficeFailure( $serviceName ); |
250 | 262 | return null; |
251 | 263 | } elseif ( !is_object( $response ) ) { |
252 | 264 | wfWarn( __METHOD__ . ': Unable to parse reply: ' . strval( $google_json ) ); |
— | — | @@ -259,6 +271,8 @@ |
260 | 272 | $unsupported[$code] = true; |
261 | 273 | $wgMemc->set( $memckey, $unsupported, 60 * 60 * 8 ); |
262 | 274 | } else { |
| 275 | + // Unknown error, assume the worst |
| 276 | + self::reportTranslationSerficeFailure( $serviceName ); |
263 | 277 | wfWarn( __METHOD__ . ': ' . $response->responseDetails ); |
264 | 278 | error_log( __METHOD__ . ': ' . $response->responseDetails ); |
265 | 279 | return null; |
— | — | @@ -269,6 +283,8 @@ |
270 | 284 | global $wgTranslateApertium, $wgMemc; |
271 | 285 | |
272 | 286 | if ( !$wgTranslateApertium ) return null; |
| 287 | + $serviceName = 'Apertium'; |
| 288 | + if ( self::checkTranslationServiceFailure( $serviceName ) ) return null; |
273 | 289 | |
274 | 290 | $page = $this->page; |
275 | 291 | $code = $this->targetLanguage; |
— | — | @@ -323,6 +339,7 @@ |
324 | 340 | |
325 | 341 | $response = Http::get( "$wgTranslateApertium?" . wfArrayToCgi( $query ), 3 ); |
326 | 342 | if ( $response === false ) { |
| 343 | + self::reportTranslationSerficeFailure( $serviceName ); |
327 | 344 | break; // Too slow, back off |
328 | 345 | } else { |
329 | 346 | $response = $this->suggestionField( Sanitizer::decodeCharReferences( $response ) ); |
— | — | @@ -625,4 +642,42 @@ |
626 | 643 | |
627 | 644 | return $wgUser->getSkin()->link( $target, $text, $jsEdit, $params ); |
628 | 645 | } |
| 646 | + |
| 647 | + /** |
| 648 | + * How many failures during failure period need to happen to consider |
| 649 | + * the service being temporarily off-line. */ |
| 650 | + protected static $serviceFailureCount = 5; |
| 651 | + /** |
| 652 | + * How long after the last detected failure we clear the status and |
| 653 | + * try again. |
| 654 | + */ |
| 655 | + protected static $serviceFailurePeriod = 300; |
| 656 | + |
| 657 | + /** |
| 658 | + * Checks whether the given service has exceeded failure count */ |
| 659 | + public static function checkTranslationServiceFailure( $service ) { |
| 660 | + global $wgMemc; |
| 661 | + $key = wfMemckey( "translate-service-$service" ); |
| 662 | + // Both false and null are converted to zero, which is desirable |
| 663 | + return intval( $wgMemc->get( $key ) ) >= self::$serviceFailureCount; |
| 664 | + } |
| 665 | + |
| 666 | + /** |
| 667 | + * Increases the failure count for a given service */ |
| 668 | + public static function reportTranslationSerficeFailure( $service ) { |
| 669 | + global $wgMemc; |
| 670 | + $key = wfMemckey( "translate-service-$service" ); |
| 671 | + // Both false and null are converted to zero, which is desirable. |
| 672 | + /* FIXME: not atomic, but the default incr() implemention seems to |
| 673 | + * ignore expiry time */ |
| 674 | + $count = intval( $wgMemc->get( $key ) ); |
| 675 | + $wgMemc->set( $key, $count + 1, self::$serviceFailurePeriod ); |
| 676 | + /* By using >= we expose if something is still increasing failure |
| 677 | + * count if we are over the limit */ |
| 678 | + if ( $count + 1 >= self::$serviceFailureCount ) { |
| 679 | + $language = Language::factory( 'en' ); |
| 680 | + $period = $language->formatTimePeriod( self::$serviceFailurePeriod ); |
| 681 | + error_log( "Translation service $service suspended for $period" ); |
| 682 | + } |
| 683 | + } |
629 | 684 | } |