MediaWiki REL1_34
ReplicatedBagOStuff.php
Go to the documentation of this file.
1<?php
21use Wikimedia\ObjectFactory;
22
36 private $writeStore;
38 private $readStore;
39
43 private $lastKeyWrites = [];
44
46 const MAX_WRITE_DELAY = 5;
47
60 public function __construct( $params ) {
61 parent::__construct( $params );
62
63 if ( !isset( $params['writeFactory'] ) ) {
64 throw new InvalidArgumentException(
65 __METHOD__ . ': the "writeFactory" parameter is required' );
66 } elseif ( !isset( $params['readFactory'] ) ) {
67 throw new InvalidArgumentException(
68 __METHOD__ . ': the "readFactory" parameter is required' );
69 }
70
71 $this->consistencyWindow = $params['sessionConsistencyWindow'] ?? self::MAX_WRITE_DELAY;
72 $this->writeStore = ( $params['writeFactory'] instanceof BagOStuff )
73 ? $params['writeFactory']
74 : ObjectFactory::getObjectFromSpec( $params['writeFactory'] );
75 $this->readStore = ( $params['readFactory'] instanceof BagOStuff )
76 ? $params['readFactory']
77 : ObjectFactory::getObjectFromSpec( $params['readFactory'] );
78 $this->attrMap = $this->mergeFlagMaps( [ $this->readStore, $this->writeStore ] );
79 }
80
81 public function setDebug( $enabled ) {
82 parent::setDebug( $enabled );
83 $this->writeStore->setDebug( $enabled );
84 $this->readStore->setDebug( $enabled );
85 }
86
87 public function get( $key, $flags = 0 ) {
88 return (
89 $this->hadRecentSessionWrite( [ $key ] ) ||
90 $this->fieldHasFlags( $flags, self::READ_LATEST )
91 )
92 ? $this->writeStore->get( $key, $flags )
93 : $this->readStore->get( $key, $flags );
94 }
95
96 public function set( $key, $value, $exptime = 0, $flags = 0 ) {
97 $this->remarkRecentSessionWrite( [ $key ] );
98
99 return $this->writeStore->set( $key, $value, $exptime, $flags );
100 }
101
102 public function delete( $key, $flags = 0 ) {
103 $this->remarkRecentSessionWrite( [ $key ] );
104
105 return $this->writeStore->delete( $key, $flags );
106 }
107
108 public function add( $key, $value, $exptime = 0, $flags = 0 ) {
109 $this->remarkRecentSessionWrite( [ $key ] );
110
111 return $this->writeStore->add( $key, $value, $exptime, $flags );
112 }
113
114 public function merge( $key, callable $callback, $exptime = 0, $attempts = 10, $flags = 0 ) {
115 $this->remarkRecentSessionWrite( [ $key ] );
116
117 return $this->writeStore->merge( $key, $callback, $exptime, $attempts, $flags );
118 }
119
120 public function changeTTL( $key, $exptime = 0, $flags = 0 ) {
121 $this->remarkRecentSessionWrite( [ $key ] );
122
123 return $this->writeStore->changeTTL( $key, $exptime, $flags );
124 }
125
126 public function lock( $key, $timeout = 6, $expiry = 6, $rclass = '' ) {
127 return $this->writeStore->lock( $key, $timeout, $expiry, $rclass );
128 }
129
130 public function unlock( $key ) {
131 return $this->writeStore->unlock( $key );
132 }
133
135 $timestamp,
136 callable $progress = null,
137 $limit = INF
138 ) {
139 return $this->writeStore->deleteObjectsExpiringBefore( $timestamp, $progress, $limit );
140 }
141
142 public function getMulti( array $keys, $flags = 0 ) {
143 return (
144 $this->hadRecentSessionWrite( $keys ) ||
145 $this->fieldHasFlags( $flags, self::READ_LATEST )
146 )
147 ? $this->writeStore->getMulti( $keys, $flags )
148 : $this->readStore->getMulti( $keys, $flags );
149 }
150
151 public function setMulti( array $data, $exptime = 0, $flags = 0 ) {
152 $this->remarkRecentSessionWrite( array_keys( $data ) );
153
154 return $this->writeStore->setMulti( $data, $exptime, $flags );
155 }
156
157 public function deleteMulti( array $keys, $flags = 0 ) {
158 $this->remarkRecentSessionWrite( $keys );
159
160 return $this->writeStore->deleteMulti( $keys, $flags );
161 }
162
163 public function changeTTLMulti( array $keys, $exptime, $flags = 0 ) {
164 $this->remarkRecentSessionWrite( $keys );
165
166 return $this->writeStore->changeTTLMulti( $keys, $exptime, $flags );
167 }
168
169 public function incr( $key, $value = 1, $flags = 0 ) {
170 $this->remarkRecentSessionWrite( [ $key ] );
171
172 return $this->writeStore->incr( $key, $value, $flags );
173 }
174
175 public function decr( $key, $value = 1, $flags = 0 ) {
176 $this->remarkRecentSessionWrite( [ $key ] );
177
178 return $this->writeStore->decr( $key, $value, $flags );
179 }
180
181 public function incrWithInit( $key, $exptime, $value = 1, $init = null, $flags = 0 ) {
182 $this->remarkRecentSessionWrite( [ $key ] );
183
184 return $this->writeStore->incrWithInit( $key, $exptime, $value, $init, $flags );
185 }
186
187 public function getLastError() {
188 return ( $this->writeStore->getLastError() !== self::ERR_NONE )
189 ? $this->writeStore->getLastError()
190 : $this->readStore->getLastError();
191 }
192
193 public function clearLastError() {
194 $this->writeStore->clearLastError();
195 $this->readStore->clearLastError();
196 }
197
198 public function makeKeyInternal( $keyspace, $args ) {
199 return $this->writeStore->makeKeyInternal( ...func_get_args() );
200 }
201
202 public function makeKey( $class, ...$components ) {
203 return $this->writeStore->makeKey( ...func_get_args() );
204 }
205
206 public function makeGlobalKey( $class, ...$components ) {
207 return $this->writeStore->makeGlobalKey( ...func_get_args() );
208 }
209
210 public function addBusyCallback( callable $workCallback ) {
211 $this->writeStore->addBusyCallback( $workCallback );
212 }
213
214 public function setMockTime( &$time ) {
215 parent::setMockTime( $time );
216 $this->writeStore->setMockTime( $time );
217 $this->readStore->setMockTime( $time );
218 }
219
224 private function hadRecentSessionWrite( array $keys ) {
225 $now = $this->getCurrentTime();
226 foreach ( $keys as $key ) {
227 $ts = $this->lastKeyWrites[$key] ?? 0;
228 if ( $ts && ( $now - $ts ) <= $this->consistencyWindow ) {
229 return true;
230 }
231 }
232
233 return false;
234 }
235
239 private function remarkRecentSessionWrite( array $keys ) {
240 $now = $this->getCurrentTime();
241 foreach ( $keys as $key ) {
242 unset( $this->lastKeyWrites[$key] ); // move to the end
243 $this->lastKeyWrites[$key] = $now;
244 }
245 // Prune out the map if the first key is obsolete
246 if ( ( $now - reset( $this->lastKeyWrites ) ) > $this->consistencyWindow ) {
247 $this->lastKeyWrites = array_filter(
248 $this->lastKeyWrites,
249 function ( $timestamp ) use ( $now ) {
250 return ( ( $now - $timestamp ) <= $this->consistencyWindow );
251 }
252 );
253 }
254 }
255}
if( $line===false) $args
Definition cdb.php:64
Class representing a cache/ephemeral data store.
Definition BagOStuff.php:63
mergeFlagMaps(array $bags)
Merge the flag maps of one or more BagOStuff objects into a "lowest common denominator" map.
fieldHasFlags( $field, $flags)
A cache class that directs writes to one set of servers and reads to another.
deleteObjectsExpiringBefore( $timestamp, callable $progress=null, $limit=INF)
Delete all objects expiring before a certain date.
add( $key, $value, $exptime=0, $flags=0)
Insert an item if it does not already exist.
__construct( $params)
Constructor.
changeTTLMulti(array $keys, $exptime, $flags=0)
Change the expiration of multiple keys that exist.
clearLastError()
Clear the "last error" registry.
makeKeyInternal( $keyspace, $args)
Construct a cache key.
incr( $key, $value=1, $flags=0)
Increase stored value of $key by $value while preserving its TTL.
lock( $key, $timeout=6, $expiry=6, $rclass='')
Acquire an advisory lock on a key string.
getLastError()
Get the "last error" registered; clearLastError() should be called manually.
deleteMulti(array $keys, $flags=0)
Batch deletion.
remarkRecentSessionWrite(array $keys)
getMulti(array $keys, $flags=0)
Get an associative array containing the item for each of the keys that have items.
changeTTL( $key, $exptime=0, $flags=0)
Change the expiration on a key if it exists.
decr( $key, $value=1, $flags=0)
Decrease stored value of $key by $value while preserving its TTL.
addBusyCallback(callable $workCallback)
Let a callback be run to avoid wasting time on special blocking calls.
makeGlobalKey( $class,... $components)
Make a global cache key.
int $consistencyWindow
Seconds to read from the master source for a key after writing to it.
setMulti(array $data, $exptime=0, $flags=0)
Batch insertion/replace.
merge( $key, callable $callback, $exptime=0, $attempts=10, $flags=0)
Merge changes into the existing cache value (possibly creating a new one)
makeKey( $class,... $components)
Make a cache key, scoped to this instance's keyspace.
float[] $lastKeyWrites
Map of (key => UNIX timestamp)
unlock( $key)
Release an advisory lock on a key string.
incrWithInit( $key, $exptime, $value=1, $init=null, $flags=0)
Increase the value of the given key (no TTL change) if it exists or create it otherwise.