50
50
* - Detects inactive waiting processes to prevent false-positives in concurrency throttling.
51
51
*/
52
52
53
+ use Cm \RedisSession \Handler \ClusterConfigInterface ;
53
54
use Cm \RedisSession \Handler \ConfigInterface ;
54
55
use Cm \RedisSession \Handler \ConfigSentinelPasswordInterface ;
55
56
use Cm \RedisSession \Handler \LoggerInterface ;
57
+ use Cm \RedisSession \Handler \TlsOptionsConfigInterface ;
58
+ use Cm \RedisSession \Handler \UsernameConfigInterface ;
56
59
57
60
class Handler implements \SessionHandlerInterface
58
61
{
@@ -162,10 +165,20 @@ class Handler implements \SessionHandlerInterface
162
165
const DEFAULT_LIFETIME = 60 ;
163
166
164
167
/**
165
- * @var \Credis_Client
168
+ * @var \Credis_Client|\Credis_Cluster
166
169
*/
167
170
protected $ _redis ;
168
171
172
+ /**
173
+ * @var bool
174
+ */
175
+ protected readonly bool $ _usePipeline ;
176
+
177
+ /**
178
+ * @var bool
179
+ */
180
+ protected readonly bool $ _useCluster ;
181
+
169
182
/**
170
183
* @var int
171
184
*/
@@ -278,10 +291,12 @@ public function __construct(ConfigInterface $config, LoggerInterface $logger, $r
278
291
$ host = $ this ->config ->getHost () ?: self ::DEFAULT_HOST ;
279
292
$ port = $ this ->config ->getPort () ?: self ::DEFAULT_PORT ;
280
293
$ pass = $ this ->config ->getPassword () ?: null ;
294
+ $ username = $ this ->config instanceof UsernameConfigInterface ? $ this ->config ->getUsername () : null ;
281
295
$ timeout = $ this ->config ->getTimeout () ?: self ::DEFAULT_TIMEOUT ;
282
296
$ retries = $ this ->config ->getRetries () ?: self ::DEFAULT_RETRIES ;
283
297
$ persistent = $ this ->config ->getPersistentIdentifier () ?: '' ;
284
298
$ this ->_dbNum = $ this ->config ->getDatabase () ?: self ::DEFAULT_DATABASE ;
299
+ $ tlsOptions = $ this ->config instanceof TlsOptionsConfigInterface ? $ this ->config ->getTlsOptions () : null ;
285
300
286
301
// General config
287
302
$ this ->_readOnly = $ readOnly ;
@@ -307,6 +322,8 @@ public function __construct(ConfigInterface $config, LoggerInterface $logger, $r
307
322
308
323
// Connect and authenticate
309
324
if ($ sentinelServers && $ sentinelMaster ) {
325
+ $ this ->_usePipeline = true ;
326
+ $ this ->_useCluster = false ;
310
327
$ servers = preg_split ('/\s*,\s*/ ' , trim ($ sentinelServers ), -1 , PREG_SPLIT_NO_EMPTY );
311
328
$ sentinel = NULL ;
312
329
$ exception = NULL ;
@@ -322,35 +339,35 @@ public function __construct(ConfigInterface $config, LoggerInterface $logger, $r
322
339
} catch (\CredisException $ e ) {
323
340
// Prevent throwing exception if Sentinel has no password set (error messages are different between redis 5 and redis 6)
324
341
if ($ e ->getCode () !== 0 || (
325
- strpos ($ e ->getMessage (), 'ERR Client sent AUTH, but no password is set ' ) === false &&
342
+ strpos ($ e ->getMessage (), 'ERR Client sent AUTH, but no password is set ' ) === false &&
326
343
strpos ($ e ->getMessage (), 'ERR AUTH <password> called without any password configured for the default user. Are you sure your configuration is correct? ' ) === false )
327
344
) {
328
345
throw $ e ;
329
346
}
330
347
}
331
348
}
332
-
349
+
333
350
$ sentinel = new \Credis_Sentinel ($ sentinelClient );
334
351
$ sentinel
335
352
->setClientTimeout ($ timeout )
336
353
->setClientPersistent ($ persistent );
337
354
$ redisMaster = $ sentinel ->getMasterClient ($ sentinelMaster );
338
- if ($ pass ) $ redisMaster ->auth ($ pass );
355
+ if ($ pass ) $ redisMaster ->auth ($ pass, $ username );
339
356
340
357
// Verify connected server is actually master as per Sentinel client spec
341
358
if ($ sentinelVerifyMaster ) {
342
359
$ roleData = $ redisMaster ->role ();
343
360
if ( ! $ roleData || $ roleData [0 ] != 'master ' ) {
344
361
usleep (100000 ); // Sleep 100ms and try again
345
362
$ redisMaster = $ sentinel ->getMasterClient ($ sentinelMaster );
346
- if ($ pass ) $ redisMaster ->auth ($ pass );
363
+ if ($ pass ) $ redisMaster ->auth ($ pass, $ username );
347
364
$ roleData = $ redisMaster ->role ();
348
365
if ( ! $ roleData || $ roleData [0 ] != 'master ' ) {
349
366
throw new \Exception ('Unable to determine master redis server. ' );
350
367
}
351
368
}
352
369
}
353
- if ($ this ->_dbNum || $ persistent ) $ redisMaster ->select (0 );
370
+ if (( $ this ->_dbNum || $ persistent) && ! $ this -> _useCluster ) $ redisMaster ->select (0 );
354
371
355
372
$ this ->_redis = $ redisMaster ;
356
373
break 2 ;
@@ -366,24 +383,61 @@ public function __construct(ConfigInterface $config, LoggerInterface $logger, $r
366
383
}
367
384
}
368
385
else {
369
- $ this ->_redis = new \Credis_Client ($ host , $ port , $ timeout , $ persistent , 0 , $ pass );
386
+ if (($ config instanceof ClusterConfigInterface) && ($ config ->isCluster ())) {
387
+ $ this ->_redis = new \Credis_Cluster (
388
+ $ config ->getClusterName (),
389
+ $ config ->getClusterSeeds (),
390
+ $ timeout ,
391
+ 0 ,
392
+ $ config ->getClusterUsePersistentConnection (),
393
+ $ pass ,
394
+ $ username ,
395
+ $ tlsOptions ,
396
+ );
397
+ $ this ->_usePipeline = false ;
398
+ $ this ->_useCluster = true ;
399
+ } else {
400
+ $ this ->_redis = new \Credis_Client (
401
+ $ host ,
402
+ $ port ,
403
+ $ timeout ,
404
+ $ persistent ,
405
+ 0 ,
406
+ $ pass ,
407
+ $ username ,
408
+ $ tlsOptions
409
+ );
410
+ $ this ->_usePipeline = true ;
411
+ $ this ->_useCluster = false ;
412
+ }
370
413
$ this ->_redis ->setMaxConnectRetries ($ retries );
371
414
if ($ this ->hasConnection () == false ) {
372
415
throw new ConnectionFailedException ('Unable to connect to Redis ' );
373
416
}
374
417
}
375
-
376
418
// Destructor order cannot be predicted
377
419
$ this ->_redis ->setCloseOnDestruct (false );
378
- $ this ->_log (
379
- sprintf (
380
- "%s initialized for connection to %s:%s after %.5f seconds " ,
381
- get_class ($ this ),
382
- $ this ->_redis ->getHost (),
383
- $ this ->_redis ->getPort (),
384
- (microtime (true ) - $ timeStart )
385
- )
386
- );
420
+ if ($ this ->_useCluster ) {
421
+ $ this ->_log (
422
+ sprintf (
423
+ "%s initialized for connection to %s after %.5f seconds " ,
424
+ get_class ($ this ),
425
+ (!empty ($ this ->_redis ->getClusterSeeds ())) ?
426
+ var_export ($ this ->_redis ->getClusterSeeds (), true ) : $ this ->_redis ->getClusterName (),
427
+ (microtime (true ) - $ timeStart )
428
+ )
429
+ );
430
+ } else {
431
+ $ this ->_log (
432
+ sprintf (
433
+ "%s initialized for connection to %s:%s after %.5f seconds " ,
434
+ get_class ($ this ),
435
+ $ this ->_redis ->getHost (),
436
+ $ this ->_redis ->getPort (),
437
+ (microtime (true ) - $ timeStart )
438
+ )
439
+ );
440
+ }
387
441
}
388
442
389
443
/**
@@ -459,7 +513,7 @@ public function read($sessionId)
459
513
$ timeStart = microtime (true );
460
514
$ this ->_log (sprintf ("Attempting to take lock on ID %s " , $ sessionId ));
461
515
462
- $ this ->_redis ->select ($ this ->_dbNum );
516
+ if (! $ this -> _useCluster ) $ this ->_redis ->select ($ this ->_dbNum );
463
517
while ($ this ->_useLocking && !$ this ->_readOnly )
464
518
{
465
519
// Increment lock value for this session and retrieve the new value
@@ -639,18 +693,19 @@ public function read($sessionId)
639
693
);
640
694
}
641
695
}
642
-
643
- // Set session data and expiration
644
- $ this ->_redis ->pipeline ();
696
+ if ($ this ->_usePipeline ) {
697
+ // Set session data and expiration
698
+ $ this ->_redis ->pipeline ();
699
+ }
645
700
if ( ! empty ($ setData )) {
646
701
$ this ->_redis ->hMSet ($ sessionId , $ setData );
647
702
}
648
703
$ this ->_redis ->expire ($ sessionId , 3600 *6 ); // Expiration will be set to correct value when session is written
649
- $ this ->_redis ->exec ();
650
-
704
+ if ($ this ->_usePipeline ) {
705
+ $ this ->_redis ->exec ();
706
+ }
651
707
// Reset flag in case of multiple session read/write operations
652
708
$ this ->_sessionWritten = false ;
653
-
654
709
return $ sessionData ? (string ) $ this ->_decodeData ($ sessionData ) : '' ;
655
710
}
656
711
@@ -673,7 +728,7 @@ public function write($sessionId, $sessionData)
673
728
674
729
// Do not overwrite the session if it is locked by another pid
675
730
try {
676
- if ($ this ->_dbNum ) $ this ->_redis ->select ($ this ->_dbNum ); // Prevent conflicts with other connections?
731
+ if ($ this ->_dbNum && ! $ this -> _useCluster ) $ this ->_redis ->select ($ this ->_dbNum ); // Prevent conflicts with other connections?
677
732
678
733
if ( ! $ this ->_useLocking
679
734
|| ( ! ($ pid = $ this ->_redis ->hGet ('sess_ ' .$ sessionId , 'pid ' )) || $ pid == $ this ->_getPid ())
@@ -711,10 +766,14 @@ public function write($sessionId, $sessionData)
711
766
public function destroy ($ sessionId )
712
767
{
713
768
$ this ->_log (sprintf ("Destroying ID %s " , $ sessionId ));
714
- $ this ->_redis ->pipeline ();
715
- if ($ this ->_dbNum ) $ this ->_redis ->select ($ this ->_dbNum );
769
+ if ($ this ->_usePipeline ) {
770
+ $ this ->_redis ->pipeline ();
771
+ }
772
+ if ($ this ->_dbNum && !$ this ->_useCluster ) $ this ->_redis ->select ($ this ->_dbNum );
716
773
$ this ->_redis ->unlink (self ::SESSION_PREFIX .$ sessionId );
717
- $ this ->_redis ->exec ();
774
+ if ($ this ->_usePipeline ) {
775
+ $ this ->_redis ->exec ();
776
+ }
718
777
return true ;
719
778
}
720
779
@@ -832,7 +891,7 @@ protected function _encodeData($data)
832
891
case 'lz4 ' : $ data = lz4_compress ($ data ); $ prefix = ':l4: ' ; break ;
833
892
case 'gzip ' : $ data = gzcompress ($ data , 1 ); break ;
834
893
}
835
- if ($ data ) {
894
+ if ($ data ) {
836
895
$ data = $ prefix .$ data ;
837
896
$ this ->_log (
838
897
sprintf (
@@ -880,15 +939,19 @@ protected function _decodeData($data)
880
939
protected function _writeRawSession ($ id , $ data , $ lifetime )
881
940
{
882
941
$ sessionId = 'sess_ ' . $ id ;
883
- $ this ->_redis ->pipeline ()
884
- ->select ($ this ->_dbNum )
885
- ->hMSet ($ sessionId , array (
942
+ if ($ this ->_usePipeline ) {
943
+ $ this ->_redis ->pipeline ();
944
+ }
945
+ if (!$ this ->_useCluster ) $ this ->_redis ->select ($ this ->_dbNum );
946
+ $ this ->_redis ->hMSet ($ sessionId , array (
886
947
'data ' => $ this ->_encodeData ($ data ),
887
948
'lock ' => 0 , // 0 so that next lock attempt will get 1
888
- ))
889
- ->hIncrBy ($ sessionId , 'writes ' , 1 )
890
- ->expire ($ sessionId , min ((int )$ lifetime , (int )$ this ->_maxLifetime ))
891
- ->exec ();
949
+ ));
950
+ $ this ->_redis ->hIncrBy ($ sessionId , 'writes ' , 1 );
951
+ $ this ->_redis ->expire ($ sessionId , min ((int )$ lifetime , (int )$ this ->_maxLifetime ));
952
+ if ($ this ->_usePipeline ) {
953
+ $ this ->_redis ->exec ();
954
+ }
892
955
}
893
956
894
957
/**
0 commit comments