]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - includes/poolcounter/PoolCounterWork.php
MediaWiki 1.30.2-scripts2
[autoinstallsdev/mediawiki.git] / includes / poolcounter / PoolCounterWork.php
1 <?php
2 /**
3  * Provides of semaphore semantics for restricting the number
4  * of workers that may be concurrently performing the same task.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  * http://www.gnu.org/copyleft/gpl.html
20  *
21  * @file
22  */
23
24 /**
25  * Class for dealing with PoolCounters using class members
26  */
27 abstract class PoolCounterWork {
28         /** @var string */
29         protected $type = 'generic';
30         /** @var bool */
31         protected $cacheable = false; // does this override getCachedWork() ?
32
33         /**
34          * @param string $type The class of actions to limit concurrency for (task type)
35          * @param string $key Key that identifies the queue this work is placed on
36          */
37         public function __construct( $type, $key ) {
38                 $this->type = $type;
39                 $this->poolCounter = PoolCounter::factory( $type, $key );
40         }
41
42         /**
43          * Actually perform the work, caching it if needed
44          * @return mixed Work result or false
45          */
46         abstract public function doWork();
47
48         /**
49          * Retrieve the work from cache
50          * @return mixed Work result or false
51          */
52         public function getCachedWork() {
53                 return false;
54         }
55
56         /**
57          * A work not so good (eg. expired one) but better than an error
58          * message.
59          * @return mixed Work result or false
60          */
61         public function fallback() {
62                 return false;
63         }
64
65         /**
66          * Do something with the error, like showing it to the user.
67          *
68          * @param Status $status
69          *
70          * @return bool
71          */
72         public function error( $status ) {
73                 return false;
74         }
75
76         /**
77          * Log an error
78          *
79          * @param Status $status
80          * @return void
81          */
82         public function logError( $status ) {
83                 $key = $this->poolCounter->getKey();
84
85                 wfDebugLog( 'poolcounter', "Pool key '$key' ({$this->type}): "
86                         . $status->getMessage()->inLanguage( 'en' )->useDatabase( false )->text() );
87         }
88
89         /**
90          * Get the result of the work (whatever it is), or the result of the error() function.
91          * This returns the result of the first applicable method that returns a non-false value,
92          * where the methods are checked in the following order:
93          *   - a) doWork()       : Applies if the work is exclusive or no another process
94          *                         is doing it, and on the condition that either this process
95          *                         successfully entered the pool or the pool counter is down.
96          *   - b) doCachedWork() : Applies if the work is cacheable and this blocked on another
97          *                         process which finished the work.
98          *   - c) fallback()     : Applies for all remaining cases.
99          * If these all fall through (by returning false), then the result of error() is returned.
100          *
101          * @param bool $skipcache
102          * @return mixed
103          */
104         public function execute( $skipcache = false ) {
105                 if ( $this->cacheable && !$skipcache ) {
106                         $status = $this->poolCounter->acquireForAnyone();
107                 } else {
108                         $status = $this->poolCounter->acquireForMe();
109                 }
110
111                 if ( !$status->isOK() ) {
112                         // Respond gracefully to complete server breakage: just log it and do the work
113                         $this->logError( $status );
114                         return $this->doWork();
115                 }
116
117                 switch ( $status->value ) {
118                         case PoolCounter::LOCK_HELD:
119                                 // Better to ignore nesting pool counter limits than to fail.
120                                 // Assume that the outer pool limiting is reasonable enough.
121                                 /* no break */
122                         case PoolCounter::LOCKED:
123                                 $result = $this->doWork();
124                                 $this->poolCounter->release();
125                                 return $result;
126
127                         case PoolCounter::DONE:
128                                 $result = $this->getCachedWork();
129                                 if ( $result === false ) {
130                                         /* That someone else work didn't serve us.
131                                          * Acquire the lock for me
132                                          */
133                                         return $this->execute( true );
134                                 }
135                                 return $result;
136
137                         case PoolCounter::QUEUE_FULL:
138                         case PoolCounter::TIMEOUT:
139                                 $result = $this->fallback();
140
141                                 if ( $result !== false ) {
142                                         return $result;
143                                 }
144                                 /* no break */
145
146                         /* These two cases should never be hit... */
147                         case PoolCounter::ERROR:
148                         default:
149                                 $errors = [
150                                         PoolCounter::QUEUE_FULL => 'pool-queuefull',
151                                         PoolCounter::TIMEOUT => 'pool-timeout' ];
152
153                                 $status = Status::newFatal( isset( $errors[$status->value] )
154                                         ? $errors[$status->value]
155                                         : 'pool-errorunknown' );
156                                 $this->logError( $status );
157                                 return $this->error( $status );
158                 }
159         }
160 }