24use Psr\Log\LoggerAwareInterface;
25use Psr\Log\LoggerInterface;
79 if ( !class_exists(
'Redis' ) ) {
80 throw new RuntimeException(
81 __CLASS__ .
' requires a Redis client library. ' .
82 'See https://www.mediawiki.org/wiki/Redis#Setup' );
84 $this->logger =
$options[
'logger'] ?? new \Psr\Log\NullLogger();
85 $this->connectTimeout =
$options[
'connectTimeout'];
86 $this->readTimeout =
$options[
'readTimeout'];
87 $this->persistent =
$options[
'persistent'];
88 $this->password =
$options[
'password'];
89 if ( !isset(
$options[
'serializer'] ) ||
$options[
'serializer'] ===
'php' ) {
90 $this->serializer = Redis::SERIALIZER_PHP;
91 } elseif (
$options[
'serializer'] ===
'igbinary' ) {
92 $this->serializer = Redis::SERIALIZER_IGBINARY;
93 } elseif (
$options[
'serializer'] ===
'none' ) {
94 $this->serializer = Redis::SERIALIZER_NONE;
96 throw new InvalidArgumentException(
"Invalid serializer specified." );
114 if ( !isset(
$options[
'connectTimeout'] ) ) {
117 if ( !isset(
$options[
'readTimeout'] ) ) {
120 if ( !isset(
$options[
'persistent'] ) ) {
123 if ( !isset(
$options[
'password'] ) ) {
151 if ( !isset( self::$instances[
$id] ) ) {
155 return self::$instances[
$id];
163 self::$instances = [];
180 if ( isset( $this->downServers[$server] ) ) {
182 if ( $now > $this->downServers[$server] ) {
184 unset( $this->downServers[$server] );
188 'Server "{redis_server}" is marked down for another ' .
189 ( $this->downServers[$server] - $now ) .
'seconds',
190 [
'redis_server' => $server ]
198 if ( isset( $this->connections[$server] ) ) {
199 foreach ( $this->connections[$server] as &$connection ) {
200 if ( $connection[
'free'] ) {
201 $connection[
'free'] =
false;
205 $this, $server, $connection[
'conn'],
$logger
212 throw new InvalidArgumentException(
213 __CLASS__ .
": invalid configured server \"$server\"" );
214 } elseif ( substr( $server, 0, 1 ) ===
'/' ) {
222 if ( preg_match(
'/^\[(.+)\]:(\d+)$/', $server, $m ) ) {
223 list( $host, $port ) = [ $m[1], (int)$m[2] ];
224 } elseif ( preg_match(
'/^([^:]+):(\d+)$/', $server, $m ) ) {
225 list( $host, $port ) = [ $m[1], (int)$m[2] ];
227 list( $host, $port ) = [ $server, 6379 ];
233 if ( $this->persistent ) {
234 $result = $conn->pconnect( $host, $port, $this->connectTimeout, $this->
id );
236 $result = $conn->connect( $host, $port, $this->connectTimeout );
240 'Could not connect to server "{redis_server}"',
241 [
'redis_server' => $server ]
248 if ( $this->password !==
null ) {
249 if ( !$conn->auth( $this->password ) ) {
251 'Authentication error connecting to "{redis_server}"',
252 [
'redis_server' => $server ]
256 }
catch ( RedisException
$e ) {
259 'Redis exception connecting to "{redis_server}"',
261 'redis_server' => $server,
270 $conn->setOption( Redis::OPT_READ_TIMEOUT, $this->readTimeout );
271 $conn->setOption( Redis::OPT_SERIALIZER, $this->serializer );
272 $this->connections[$server][] = [
'conn' => $conn,
'free' =>
false ];
290 foreach ( $this->connections[$server] as &$connection ) {
291 if ( $connection[
'conn'] === $conn && !$connection[
'free'] ) {
292 $connection[
'free'] =
true;
307 if ( $this->idlePoolSize <= count( $this->connections ) ) {
311 foreach ( $this->connections as &$serverConnections ) {
312 foreach ( $serverConnections as $key => &$connection ) {
313 if ( $connection[
'free'] ) {
314 unset( $serverConnections[$key] );
315 if ( --$this->idlePoolSize <= count( $this->connections ) ) {
333 $server = $cref->getServer();
334 $this->logger->error(
335 'Redis exception on server "{redis_server}"',
337 'redis_server' => $server,
341 foreach ( $this->connections[$server] as $key => $connection ) {
342 if ( $cref->isConnIdentical( $connection[
'conn'] ) ) {
343 $this->idlePoolSize -= $connection[
'free'] ? 1 : 0;
344 unset( $this->connections[$server][$key] );
367 if ( $this->password !==
null ) {
368 if ( !$conn->auth( $this->password ) ) {
369 $this->logger->error(
370 'Authentication error connecting to "{redis_server}"',
371 [
'redis_server' => $server ]
395 foreach ( $this->connections as $server => &$serverConnections ) {
396 foreach ( $serverConnections as $key => &$connection ) {
399 $conn = $connection[
'conn'];
401 }
catch ( RedisException
$e ) {
Helper class to handle automatically marking connectons as reusable (via RAII pattern)
Helper class to manage Redis connections.
static applyDefaultConfig(array $options)
string $connectTimeout
Connection timeout in seconds.
__construct(array $options, $id)
int $serializer
Serializer to use (Redis::SERIALIZER_*)
reauthenticateConnection( $server, Redis $conn)
Re-send an AUTH request to the redis server (useful after disconnects).
array $connections
(server name => ((connection info array),...)
getConnection( $server, LoggerInterface $logger=null)
Get a connection to a redis server.
__destruct()
Make sure connections are closed for sanity.
string $id
ID for persistent connections.
closeExcessIdleConections()
Close any extra idle connections if there are more than the limit.
handleError(RedisConnRef $cref, RedisException $e)
The redis extension throws an exception in response to various read, write and protocol errors.
static destroySingletons()
Destroy all singleton() instances.
freeConnection( $server, Redis $conn)
Mark a connection to a server as free to return to the pool.
static array $instances
(pool ID => RedisConnectionPool)
resetTimeout(Redis $conn, $timeout=null)
Adjust or reset the connection handle read timeout value.
string $password
Plaintext auth password.
int $idlePoolSize
Current idle pool size.
bool $persistent
Whether connections persist.
const SERVER_DOWN_TTL
integer; seconds to cache servers as "down".
array $downServers
(server name => UNIX timestamp)
string $readTimeout
Read timeout in seconds.
static singleton(array $options)
setLogger(LoggerInterface $logger)
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
processing should stop and the error should be shown to the user * false
returning false will NOT prevent logging $e
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))