From 1263b567b10923f5c1de99f901638fbf06c7c7dd Mon Sep 17 00:00:00 2001 From: =?utf8?q?Tim=20D=C3=BCsterhus?= Date: Tue, 18 May 2021 11:06:11 +0200 Subject: [PATCH] Prevent duplicate key errors during creation of legacy sessions Fixes #4214. see 7878eb77952c506e5818587b49f9a64773b87fb1 --- .../system/session/SessionHandler.class.php | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/wcfsetup/install/files/lib/system/session/SessionHandler.class.php b/wcfsetup/install/files/lib/system/session/SessionHandler.class.php index 4bbb3a90d8..28d8c818f4 100644 --- a/wcfsetup/install/files/lib/system/session/SessionHandler.class.php +++ b/wcfsetup/install/files/lib/system/session/SessionHandler.class.php @@ -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; + } + } } } -- 2.20.1