]> source.dussan.org Git - nextcloud-server.git/commitdiff
fix(session): Log when session_* calls are slow 47105/head
authorChristoph Wurst <christoph@winzerhof-wurst.at>
Tue, 25 Jun 2024 13:16:40 +0000 (15:16 +0200)
committerbackportbot[bot] <backportbot[bot]@users.noreply.github.com>
Wed, 7 Aug 2024 10:44:58 +0000 (10:44 +0000)
Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
cron.php
lib/base.php
lib/private/Server.php
lib/private/Session/Internal.php
lib/private/Session/Memory.php
lib/private/Session/Session.php
tests/lib/Session/CryptoSessionDataTest.php
tests/lib/Session/MemoryTest.php
tests/lib/User/SessionTest.php

index 4126200cc8e21370a6e10648ac68d7986a57c660..536d8bf8d2175e6c2b25621d1420b350dc7defd3 100644 (file)
--- a/cron.php
+++ b/cron.php
@@ -80,7 +80,7 @@ try {
        Server::get(ISession::class)->close();
 
        // initialize a dummy memory session
-       $session = new \OC\Session\Memory('');
+       $session = new \OC\Session\Memory();
        $cryptoWrapper = \OC::$server->getSessionCryptoWrapper();
        $session = $cryptoWrapper->wrapSession($session);
        \OC::$server->setSession($session);
index 1443726705202749ed456a486c74215d1bb092c5..f5f1588ed18159dc10dfceddaad43d5acd702133 100644 (file)
@@ -449,7 +449,10 @@ class OC {
 
                try {
                        // set the session name to the instance id - which is unique
-                       $session = new \OC\Session\Internal($sessionName);
+                       $session = new \OC\Session\Internal(
+                               $sessionName,
+                               logger('core'),
+                       );
 
                        $cryptoWrapper = Server::get(\OC\Session\CryptoWrapper::class);
                        $session = $cryptoWrapper->wrapSession($session);
index 3e7a7b51af1ee6ba2848285336477acb0c14cccd..ad1f188881a82ab45a6ff63af6b766997dd8de6a 100644 (file)
@@ -532,7 +532,7 @@ class Server extends ServerContainer implements IServerContainer {
 
                $this->registerService(\OC\User\Session::class, function (Server $c) {
                        $manager = $c->get(IUserManager::class);
-                       $session = new \OC\Session\Memory('');
+                       $session = new \OC\Session\Memory();
                        $timeFactory = new TimeFactory();
                        // Token providers might require a working database. This code
                        // might however be called when Nextcloud is not yet setup.
index bbc8ca83464d5bd6b6dff1a0497c1c08ccfff5fc..9002132e305151d32e5668ebdfc7bb18a52ae18a 100644 (file)
@@ -35,7 +35,11 @@ namespace OC\Session;
 
 use OC\Authentication\Token\IProvider;
 use OCP\Authentication\Exceptions\InvalidTokenException;
+use OCP\ILogger;
 use OCP\Session\Exceptions\SessionNotAvailableException;
+use Psr\Log\LoggerInterface;
+use function call_user_func_array;
+use function microtime;
 
 /**
  * Class Internal
@@ -49,7 +53,8 @@ class Internal extends Session {
         * @param string $name
         * @throws \Exception
         */
-       public function __construct(string $name) {
+       public function __construct(string $name,
+               private LoggerInterface $logger) {
                set_error_handler([$this, 'trapError']);
                $this->invoke('session_name', [$name]);
                $this->invoke('session_cache_limiter', ['']);
@@ -208,11 +213,31 @@ class Internal extends Session {
         */
        private function invoke(string $functionName, array $parameters = [], bool $silence = false) {
                try {
+                       $timeBefore = microtime(true);
                        if ($silence) {
-                               return @call_user_func_array($functionName, $parameters);
+                               $result = @call_user_func_array($functionName, $parameters);
                        } else {
-                               return call_user_func_array($functionName, $parameters);
+                               $result = call_user_func_array($functionName, $parameters);
                        }
+                       $timeAfter = microtime(true);
+                       $timeSpent = $timeAfter - $timeBefore;
+                       if ($timeSpent > 0.1) {
+                               $logLevel = match (true) {
+                                       $timeSpent > 25 => ILogger::ERROR,
+                                       $timeSpent > 10 => ILogger::WARN,
+                                       $timeSpent > 0.5 => ILogger::INFO,
+                                       default => ILogger::DEBUG,
+                               };
+                               $this->logger->log(
+                                       $logLevel,
+                                       "Slow session operation $functionName detected",
+                                       [
+                                               'parameters' => $parameters,
+                                               'timeSpent' => $timeSpent,
+                                       ],
+                               );
+                       }
+                       return $result;
                } catch (\Error $e) {
                        $this->trapError($e->getCode(), $e->getMessage());
                }
index fe71ec776924fdd97cefbf2bf516d3306645ce0f..a6cfa691d5dddf6416ae43278275a61946e5c608 100644 (file)
@@ -42,11 +42,6 @@ use OCP\Session\Exceptions\SessionNotAvailableException;
 class Memory extends Session {
        protected $data;
 
-       public function __construct(string $name) {
-               //no need to use $name since all data is already scoped to this instance
-               $this->data = [];
-       }
-
        /**
         * @param string $key
         * @param integer $value
index 04fc61fe610835ba1d135f7eff8d51d2b861869a..38d2706c1330e775a31c1aa4167d54dd93fa8ff4 100644 (file)
@@ -38,13 +38,6 @@ abstract class Session implements \ArrayAccess, ISession {
         */
        protected $sessionClosed = false;
 
-       /**
-        * $name serves as a namespace for the session keys
-        *
-        * @param string $name
-        */
-       abstract public function __construct(string $name);
-
        /**
         * @param mixed $offset
         * @return bool
index 10bc62e099ceb6e237647724289b033479163115..491630e0573c51a599ba89302440f00732216f20 100644 (file)
@@ -34,7 +34,7 @@ class CryptoSessionDataTest extends Session {
        protected function setUp(): void {
                parent::setUp();
 
-               $this->wrappedSession = new \OC\Session\Memory($this->getUniqueID());
+               $this->wrappedSession = new \OC\Session\Memory();
                $this->crypto = $this->createMock(ICrypto::class);
                $this->crypto->expects($this->any())
                        ->method('encrypt')
index fab7292ae001a8f398329f50de9c93f77d4b0ae5..54e73bb188426b5fdf4e8d324ea639d0e789a4e8 100644 (file)
@@ -12,10 +12,10 @@ namespace Test\Session;
 class MemoryTest extends Session {
        protected function setUp(): void {
                parent::setUp();
-               $this->instance = new \OC\Session\Memory($this->getUniqueID());
+               $this->instance = new \OC\Session\Memory();
        }
 
-       
+
        public function testThrowsExceptionOnGetId() {
                $this->expectException(\OCP\Session\Exceptions\SessionNotAvailableException::class);
 
index 619f6b3b8748ebdd79c75541a97d84fa5beddbbb..ad4eaccd9de5bb002db89eb29907833b498f9e5b 100644 (file)
@@ -115,7 +115,7 @@ class SessionTest extends \Test\TestCase {
         * @dataProvider isLoggedInData
         */
        public function testIsLoggedIn($isLoggedIn) {
-               $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock();
+               $session = $this->createMock(Memory::class);
 
                $manager = $this->createMock(Manager::class);
 
@@ -133,7 +133,7 @@ class SessionTest extends \Test\TestCase {
        }
 
        public function testSetUser() {
-               $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock();
+               $session = $this->createMock(Memory::class);
                $session->expects($this->once())
                        ->method('set')
                        ->with('user_id', 'foo');
@@ -152,7 +152,7 @@ class SessionTest extends \Test\TestCase {
        }
 
        public function testLoginValidPasswordEnabled() {
-               $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock();
+               $session = $this->createMock(Memory::class);
                $session->expects($this->once())
                        ->method('regenerateId');
                $this->tokenProvider->expects($this->once())
@@ -229,7 +229,7 @@ class SessionTest extends \Test\TestCase {
        public function testLoginValidPasswordDisabled() {
                $this->expectException(LoginException::class);
 
-               $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock();
+               $session = $this->createMock(Memory::class);
                $session->expects($this->never())
                        ->method('set');
                $session->expects($this->once())
@@ -271,7 +271,7 @@ class SessionTest extends \Test\TestCase {
        }
 
        public function testLoginInvalidPassword() {
-               $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock();
+               $session = $this->createMock(Memory::class);
                $managerMethods = get_class_methods(Manager::class);
                //keep following methods intact in order to ensure hooks are working
                $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']);
@@ -314,7 +314,7 @@ class SessionTest extends \Test\TestCase {
        }
 
        public function testPasswordlessLoginNoLastCheckUpdate(): void {
-               $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock();
+               $session = $this->createMock(Memory::class);
                $managerMethods = get_class_methods(Manager::class);
                // Keep following methods intact in order to ensure hooks are working
                $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']);
@@ -351,7 +351,7 @@ class SessionTest extends \Test\TestCase {
        }
 
        public function testLoginLastCheckUpdate(): void {
-               $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock();
+               $session = $this->createMock(Memory::class);
                $managerMethods = get_class_methods(Manager::class);
                // Keep following methods intact in order to ensure hooks are working
                $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']);
@@ -388,7 +388,7 @@ class SessionTest extends \Test\TestCase {
        }
 
        public function testLoginNonExisting() {
-               $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock();
+               $session = $this->createMock(Memory::class);
                $manager = $this->createMock(Manager::class);
                $userSession = new Session($manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher);
 
@@ -608,7 +608,7 @@ class SessionTest extends \Test\TestCase {
        }
 
        public function testRememberLoginValidToken() {
-               $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock();
+               $session = $this->createMock(Memory::class);
                $managerMethods = get_class_methods(Manager::class);
                //keep following methods intact in order to ensure hooks are working
                $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']);
@@ -697,7 +697,7 @@ class SessionTest extends \Test\TestCase {
        }
 
        public function testRememberLoginInvalidSessionToken() {
-               $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock();
+               $session = $this->createMock(Memory::class);
                $managerMethods = get_class_methods(Manager::class);
                //keep following methods intact in order to ensure hooks are working
                $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']);
@@ -761,7 +761,7 @@ class SessionTest extends \Test\TestCase {
        }
 
        public function testRememberLoginInvalidToken() {
-               $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock();
+               $session = $this->createMock(Memory::class);
                $managerMethods = get_class_methods(Manager::class);
                //keep following methods intact in order to ensure hooks are working
                $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']);
@@ -813,7 +813,7 @@ class SessionTest extends \Test\TestCase {
        }
 
        public function testRememberLoginInvalidUser() {
-               $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock();
+               $session = $this->createMock(Memory::class);
                $managerMethods = get_class_methods(Manager::class);
                //keep following methods intact in order to ensure hooks are working
                $mockedManagerMethods = array_diff($managerMethods, ['__construct', 'emit', 'listen']);
@@ -873,7 +873,7 @@ class SessionTest extends \Test\TestCase {
                                return $users[$uid];
                        });
 
-               $session = new Memory('');
+               $session = new Memory();
                $session->set('user_id', 'foo');
                $userSession = $this->getMockBuilder(Session::class)
                        ->setConstructorArgs([$manager, $session, $this->timeFactory, $this->tokenProvider, $this->config, $this->random, $this->lockdownManager, $this->logger, $this->dispatcher])
@@ -886,7 +886,7 @@ class SessionTest extends \Test\TestCase {
 
                $this->assertEquals($users['foo'], $userSession->getUser());
 
-               $session2 = new Memory('');
+               $session2 = new Memory();
                $session2->set('user_id', 'bar');
                $userSession->setSession($session2);
                $this->assertEquals($users['bar'], $userSession->getUser());