Prevent duplicate key errors during creation of legacy sessions
authorTim Düsterhus <duesterhus@woltlab.com>
Tue, 18 May 2021 09:06:11 +0000 (11:06 +0200)
committerTim Düsterhus <duesterhus@woltlab.com>
Tue, 18 May 2021 09:06:11 +0000 (11:06 +0200)
Fixes #4214.
see 7878eb77952c506e5818587b49f9a64773b87fb1

wcfsetup/install/files/lib/system/session/SessionHandler.class.php

index 4bbb3a90d82383c67301612e8955b33e1d16b5fc..28d8c818f4dae482118c03f550d6f7825332b76e 100644 (file)
@@ -11,7 +11,7 @@ use wcf\system\application\ApplicationHandler;
 use wcf\system\cache\builder\SpiderCacheBuilder;
 use wcf\system\cache\builder\UserGroupOptionCacheBuilder;
 use wcf\system\cache\builder\UserGroupPermissionCacheBuilder;
-use wcf\system\database\DatabaseException;
+use wcf\system\database\exception\DatabaseQueryExecutionException;
 use wcf\system\database\util\PreparedStatementConditionBuilder;
 use wcf\system\event\EventHandler;
 use wcf\system\exception\PermissionDeniedException;
@@ -657,12 +657,28 @@ final class SessionHandler extends SingletonFactory
             $sql = "SELECT  *
                     FROM    wcf" . WCF_N . "_session
                     " . $condition;
-            $statement = WCF::getDB()->prepareStatement($sql);
-            $statement->execute($condition->getParameters());
-            $this->legacySession = $statement->fetchSingleObject(LegacySession::class);
+            $legacySessionStatement = WCF::getDB()->prepareStatement($sql);
+            $legacySessionStatement->execute($condition->getParameters());
+            $this->legacySession = $legacySessionStatement->fetchSingleObject(LegacySession::class);
 
             if (!$this->legacySession) {
-                $this->legacySession = $this->createLegacySession();
+                try {
+                    $this->legacySession = $this->createLegacySession();
+                } catch (DatabaseQueryExecutionException $e) {
+                    // Creation of the legacy session might fail due to duplicate key errors for
+                    // concurrent requests.
+                    if ($e->getCode() == '23000' && $e->getDriverCode() == '1062') {
+                        // Attempt to load the legacy session once again. If the legacy session for some
+                        // reason *still* is null then we simply continue without a legacy session. It is
+                        // not required for proper request processing and consumers of the values stored
+                        // within the legacy session (`page*`) cannot rely on any (valid) values being stored
+                        // anyway.
+                        $legacySessionStatement->execute($condition->getParameters());
+                        $this->legacySession = $legacySessionStatement->fetchSingleObject(LegacySession::class);
+                    } else {
+                        throw $e;
+                    }
+                }
             }
         }