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