77 */
88class RedisSessionHandler extends \SessionHandler
99{
10+ /**
11+ * Wait time (1ms) after first locking attempt. It doubles
12+ * for every unsuccessful retry until it either reaches
13+ * MAX_WAIT_TIME or succeeds.
14+ */
15+ const MIN_WAIT_TIME = 1000 ;
16+
17+ /**
18+ * Maximum wait time (128ms) between locking attempts.
19+ */
20+ const MAX_WAIT_TIME = 128000 ;
21+
1022 /**
1123 * @var \Redis
1224 */
@@ -161,7 +173,15 @@ public function gc($maxlifetime)
161173 */
162174 private function acquireLockOn ($ session_id )
163175 {
164- while (false === $ this ->redis ->set ("{$ session_id }_lock " , '' , ['nx ' , 'ex ' => $ this ->lock_ttl ]));
176+ $ wait = self ::MIN_WAIT_TIME ;
177+
178+ while (false === $ this ->redis ->set ("{$ session_id }_lock " , '' , ['nx ' , 'ex ' => $ this ->lock_ttl ])) {
179+ usleep ($ wait );
180+
181+ if (self ::MAX_WAIT_TIME > $ wait ) {
182+ $ wait *= 2 ;
183+ }
184+ }
165185
166186 $ this ->open_sessions [] = $ session_id ;
167187 }
@@ -179,9 +199,9 @@ private function releaseLocks()
179199 * A session ID must be regenerated when it came from the HTTP
180200 * request and can not be found in Redis.
181201 *
182- * When that happens it either means that an old session expired in Redis
183- * but not in the browser, or a malicious client is trying to pull off
184- * a session fixation attack.
202+ * When that happens it either means that old session data expired in Redis
203+ * before the cookie with the session ID in the browser, or a malicious
204+ * client is trying to pull off a session fixation attack.
185205 *
186206 * @param string $session_id
187207 *
0 commit comments