26use Psr\Log\LoggerAwareInterface;
27use Psr\Log\LoggerInterface;
28use Psr\Log\NullLogger;
29use Wikimedia\WaitConditionLoop;
67 const POSITION_TTL = 60;
69 const POSITION_COOKIE_TTL = 10;
71 const POS_STORE_WAIT_TIMEOUT = 5;
81 $this->clientId = $client[
'clientId'] ??
82 md5( $client[
'ip'] .
"\n" . $client[
'agent'] );
84 $this->waitForPosIndex = $posIndex;
86 $this->clientLogInfo = [
87 'clientIP' => $client[
'ip'],
88 'clientAgent' => $client[
'agent'],
89 'clientId' => $client[
'clientId'] ?? null
92 $this->logger =
new NullLogger();
143 isset( $this->startupPositions[$masterName] ) &&
144 $this->startupPositions[$masterName] instanceof
DBMasterPos
146 $pos = $this->startupPositions[$masterName];
147 $this->logger->debug( __METHOD__ .
": LB for '$masterName' set to pos $pos\n" );
160 if ( !$this->enabled ) {
171 $this->logger->debug( __METHOD__ .
": LB for '$masterName' has pos $pos\n" );
172 $this->shutdownPositions[$masterName] = $pos;
175 $this->logger->debug( __METHOD__ .
": DB '$masterName' touched\n" );
177 $this->shutdownTouchDBs[$masterName] = 1;
189 public function shutdown( callable $workCallback =
null, $mode =
'sync', &$cpIndex =
null ) {
190 if ( !$this->enabled ) {
197 foreach ( $this->shutdownTouchDBs as $dbName => $unused ) {
205 if ( !count( $this->shutdownPositions ) ) {
209 $this->logger->debug( __METHOD__ .
": saving master pos for " .
210 implode(
', ', array_keys( $this->shutdownPositions ) ) .
"\n"
218 if ( $workCallback ) {
227 $this->shutdownPositions,
231 ( $mode ===
'sync' ) ? $store::WRITE_SYNC : 0
242 $this->logger->warning( __METHOD__ .
": failed to save master pos for " .
243 implode(
', ', array_keys( $this->shutdownPositions ) ) .
"\n"
245 } elseif ( $mode ===
'sync' &&
246 $store->
getQoS( $store::ATTR_SYNCWRITES ) < $store::QOS_SYNCWRITES_BE
249 $this->logger->info( __METHOD__ .
": store may not support synchronous writes." );
252 $bouncedPositions = [];
255 return $bouncedPositions;
264 return $this->store->get( $this->
getTouchedKey( $this->store, $dbName ) );
280 if ( $this->initialized ) {
284 $this->initialized =
true;
291 if ( $this->waitForPosIndex > 0 ) {
293 $indexReached =
null;
294 $loop =
new WaitConditionLoop(
295 function () use ( &$data, &$indexReached ) {
296 $data = $this->store->get( $this->
key );
297 if ( !is_array( $data ) ) {
298 return WaitConditionLoop::CONDITION_CONTINUE;
299 } elseif ( !isset( $data[
'writeIndex'] ) ) {
300 return WaitConditionLoop::CONDITION_REACHED;
302 $indexReached = max( $data[
'writeIndex'], $indexReached );
304 return ( $data[
'writeIndex'] >= $this->waitForPosIndex )
305 ? WaitConditionLoop::CONDITION_REACHED
306 : WaitConditionLoop::CONDITION_CONTINUE;
310 $result = $loop->invoke();
311 $waitedMs = $loop->getLastWaitTime() * 1e3;
313 if ( $result == $loop::CONDITION_REACHED ) {
314 $this->logger->debug(
315 __METHOD__ .
": expected and found position index.",
317 'cpPosIndex' => $this->waitForPosIndex,
318 'waitTimeMs' => $waitedMs
319 ] + $this->clientLogInfo
322 $this->logger->warning(
323 __METHOD__ .
": expected but failed to find position index.",
325 'cpPosIndex' => $this->waitForPosIndex,
326 'indexReached' => $indexReached,
327 'waitTimeMs' => $waitedMs
328 ] + $this->clientLogInfo
332 $data = $this->store->get( $this->
key );
335 $this->startupPositions = $data ? $data[
'positions'] : [];
336 $this->logger->debug( __METHOD__ .
": key is {$this->key} (read)\n" );
338 $this->startupPositions = [];
339 $this->logger->debug( __METHOD__ .
": key is {$this->key} (unread)\n" );
351 $curPositions = $curValue[
'positions'] ?? [];
355 !isset( $curPositions[$db] ) ||
357 $pos->asOfTime() > $curPositions[$db]->asOfTime()
359 $curPositions[$db] = $pos;
363 $cpIndex = $curValue[
'writeIndex'] ?? 0;
366 'positions' => $curPositions,
367 'writeIndex' => ++$cpIndex
Class representing a cache/ephemeral data store.
unlock( $key)
Release an advisory lock on a key string.
lock( $key, $timeout=6, $expiry=6, $rclass='')
Acquire an advisory lock on a key string.
set( $key, $value, $exptime=0, $flags=0)
Set an item.
get( $key, $flags=0, $oldFlags=null)
Get an item with the given key.
addBusyCallback(callable $workCallback)
Let a callback be run to avoid wasting time on special blocking calls.
makeGlobalKey( $class, $component=null)
Make a global cache key.
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation use $formDescriptor instead default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message key
The wiki should then use memcached to cache various data To use multiple just add more items to the array To increase the weight of a make its entry a array("192.168.0.1:11211", 2))