24 use Psr\Log\LoggerAwareInterface;
25 use 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 ) ) {
334 $this->logger->error(
335 'Redis exception on server "{redis_server}"',
337 'redis_server' => $server,
341 foreach ( $this->connections[$server]
as $key => $connection ) {
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 ]
388 $conn->setOption( Redis::OPT_READ_TIMEOUT, $timeout ?: $this->readTimeout );
395 foreach ( $this->connections
as $server => &$serverConnections ) {
396 foreach ( $serverConnections
as $key => &$connection ) {
399 $conn = $connection[
'conn'];
401 }
catch ( RedisException
$e ) {