Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
TTMServerMessageUpdateJob.php
Go to the documentation of this file.
1<?php
10use MediaWiki\Logger\LoggerFactory;
11use MediaWiki\MediaWikiServices;
12
33class TTMServerMessageUpdateJob extends Job {
38 protected const MAX_ERROR_RETRY = 4;
39
45 protected const WRITE_BACKOFF_EXPONENT = 7;
47 private $jobQueueGroup;
48
54 public static function newJob( MessageHandle $handle, $command ) {
55 $job = new self( $handle->getTitle(), [ 'command' => $command ] );
56
57 return $job;
58 }
59
64 public function __construct( $title, $params = [] ) {
65 parent::__construct(
66 __CLASS__,
67 $title,
68 $params + [
69 'command' => 'rebuild',
70 'service' => null,
71 'errorCount' => 0,
72 ]
73 );
74
75 $this->jobQueueGroup = MediaWikiServices::getInstance()->getJobQueueGroup();
76 }
77
82 public function run() {
83 global $wgTranslateTranslationServices,
84 $wgTranslateTranslationDefaultService;
85
86 $service = $this->params['service'];
87 $writeToMirrors = false;
88
89 if ( $service === null ) {
90 $service = $wgTranslateTranslationDefaultService;
91 $writeToMirrors = true;
92 }
93
94 if ( !isset( $wgTranslateTranslationServices[$service] ) ) {
95 LoggerFactory::getInstance( 'TTMServerUpdates' )->warning(
96 'Received update job for a an unknown service {service}.',
97 [ 'service' => $service ]
98 );
99 return true;
100 }
101
102 $services = [ $service ];
103 if ( $writeToMirrors ) {
104 $config = $wgTranslateTranslationServices[$service];
105 $server = TTMServer::factory( $config );
106 $services = array_unique(
107 array_merge( $services, $server->getMirrors() )
108 );
109 }
110
111 foreach ( $services as $service ) {
112 $this->runCommandWithRetry( $service );
113 }
114 return true;
115 }
116
118 public function allowRetries() {
119 return false;
120 }
121
127 private function runCommandWithRetry( $serviceName ) {
128 global $wgTranslateTranslationServices;
129
130 if ( !isset( $wgTranslateTranslationServices[$serviceName] ) ) {
131 LoggerFactory::getInstance( 'TTMServerUpdates' )->warning(
132 'Cannot write to {service}: service is unknown.',
133 [ 'service' => $serviceName ]
134 );
135 return;
136 }
137 $ttmserver = TTMServer::factory( $wgTranslateTranslationServices[$serviceName] );
138
139 if ( $serviceName === null || !( $ttmserver instanceof WritableTTMServer ) ) {
140 LoggerFactory::getInstance( 'TTMServerUpdates' )->warning(
141 'Received update job for a service that does not implement ' .
142 'WritableTTMServer, please check config for {service}.',
143 [ 'service' => $serviceName ]
144 );
145 return;
146 }
147
148 try {
149 $this->runCommand( $ttmserver );
150 } catch ( Exception $e ) {
151 $this->requeueError( $serviceName, $e );
152 }
153 }
154
159 private function requeueError( $serviceName, $e ) {
160 LoggerFactory::getInstance( 'TTMServerUpdates' )->warning(
161 'Exception thrown while running {command} on ' .
162 'service {service}: {errorMessage}',
163 [
164 'command' => $this->params['command'],
165 'service' => $serviceName,
166 'errorMessage' => $e->getMessage(),
167 'exception' => $e,
168 ]
169 );
170 if ( $this->params['errorCount'] >= self::MAX_ERROR_RETRY ) {
171 LoggerFactory::getInstance( 'TTMServerUpdates' )->warning(
172 'Dropping failing job {command} for service {service} ' .
173 'after repeated failure',
174 [
175 'command' => $this->params['command'],
176 'service' => $serviceName,
177 ]
178 );
179 return;
180 }
181
182 $delay = self::backoffDelay( $this->params['errorCount'] );
183 $job = clone $this;
184 $job->params['errorCount']++;
185 $job->params['service'] = $serviceName;
186 $job->setDelay( $delay );
187 LoggerFactory::getInstance( 'TTMServerUpdates' )->info(
188 'Update job reported failure on service {service}. ' .
189 'Requeueing job with delay of {delay}.',
190 [
191 'service' => $serviceName,
192 'delay' => $delay
193 ]
194 );
195 $this->resend( $job );
196 }
197
202 protected function resend( self $job ) {
203 $this->jobQueueGroup->push( $job );
204 }
205
206 private function runCommand( WritableTTMServer $ttmserver ) {
207 $handle = $this->getHandle();
208 $command = $this->params['command'];
209
210 if ( $command === 'delete' ) {
211 $this->updateItem( $ttmserver, $handle, null, false );
212 } elseif ( $command === 'rebuild' ) {
213 $this->updateMessage( $ttmserver, $handle );
214 } elseif ( $command === 'refresh' ) {
215 $this->updateTranslation( $ttmserver, $handle );
216 }
217 }
218
224 protected function getHandle() {
225 return new MessageHandle( $this->title );
226 }
227
234 protected function getTranslation( MessageHandle $handle ) {
235 return TranslateUtils::getMessageContent(
236 $handle->getKey(),
237 $handle->getCode(),
238 $handle->getTitle()->getNamespace()
239 );
240 }
241
242 private function updateMessage( WritableTTMServer $ttmserver, MessageHandle $handle ) {
243 // Base page update, e.g. group change. Update everything.
244 $translations = TranslateUtils::getTranslations( $handle );
245 foreach ( $translations as $page => $data ) {
246 $tTitle = Title::makeTitle( $this->title->getNamespace(), $page );
247 $tHandle = new MessageHandle( $tTitle );
248 $this->updateItem( $ttmserver, $tHandle, $data[0], $tHandle->isFuzzy() );
249 }
250 }
251
252 private function updateTranslation( WritableTTMServer $ttmserver, MessageHandle $handle ) {
253 // Update only this translation
254 $translation = $this->getTranslation( $handle );
255 $this->updateItem( $ttmserver, $handle, $translation, $handle->isFuzzy() );
256 }
257
258 private function updateItem( WritableTTMServer $ttmserver, MessageHandle $handle, $text, $fuzzy ) {
259 if ( $fuzzy ) {
260 $text = null;
261 }
262 $ttmserver->update( $handle, $text );
263 }
264
276 public function setDelay( $delay ) {
277 $jobQueue = $this->jobQueueGroup->get( $this->getType() );
278 if ( !$delay || !$jobQueue->delayedJobsEnabled() ) {
279 return;
280 }
281 $oldTime = $this->getReleaseTimestamp();
282 $newTime = time() + $delay;
283 if ( $oldTime !== null && $oldTime >= $newTime ) {
284 return;
285 }
286 $this->params[ 'jobReleaseTimestamp' ] = $newTime;
287 }
288
295 public static function backoffDelay( $errorCount ) {
296 return ceil( pow(
297 2,
298 static::WRITE_BACKOFF_EXPONENT + rand( 0, min( $errorCount, 4 ) )
299 ) );
300 }
301}
Class for pointing to messages, like Title class is for titles.
isFuzzy()
Check if a title is marked as fuzzy.
getTitle()
Get the original title.
getCode()
Returns the language code.
getKey()
Returns the identified or guessed message key.
Job for updating translation memory.
const MAX_ERROR_RETRY
Number of retries allowed, 4 means we attempt to run the job 5 times (1 initial attempt + 4 retries).
static newJob(MessageHandle $handle, $command)
getTranslation(MessageHandle $handle)
Extracted for testing purpose.
run()
Fetch all the translations and update them.
resend(self $job)
Extracted for testing purpose.
getHandle()
Extracted for testing purpose.
setDelay( $delay)
Set a delay for this job.
const WRITE_BACKOFF_EXPONENT
Constant used by backoffDelay().
static factory(array $config)
Definition TTMServer.php:35
Interface for TTMServer that can be updated.
update(MessageHandle $handle, $targetText)
Shovels the new translation into translation memory.