magento2: session_start(): Failed to read session data: user (path: /var/lib/php/sessions)
While viewing the orders page in the admin area and opening several orders I’m facing an error message.
Preconditions
- PHP 7.1.17-1
- Redis server v=3.0.6
- set redis as the session handler in env.php 3.1 see session config:
`'session' => [
'save' => 'redis',
'redis' => [
'host' => '<IP>',
'port' => '<PORT>',
'password' => '',
'timeout' => '2.5',
'persistent_identifier' => '',
'database' => '0',
'compression_threshold' => '2048',
'compression_library' => 'gzip',
'log_level' => '1',
'max_concurrency' => '6',
'break_after_frontend' => '5',
'break_after_adminhtml' => '30',
'first_lifetime' => '600',
'bot_first_lifetime' => '60',
'bot_lifetime' => '7200',
'disable_locking' => '0',
'min_lifetime' => '60',
'max_lifetime' => '2592000'
]
],`
- have orders in your system
Steps to reproduce
- Browse the order pages in the admin area
- click on an order
- go back to the order list
- click on an other order again
- retry several times
- see error message in the admin area:
Expected result
- Browsing pages in the admin area shouldn’t throw an error
Actual result
Message in step 6 of reproduction appears, in the log I can find the following:
{"0":"Warning: session_start(): Failed to read session data: user (path: /var/lib/php/sessions) in /vendor/magento/framework/Session/SessionManager.php on line 193","1":"#0 [internal function]: Magento\Framework\App\ErrorHandler->handler(2, 'session_start()...', '/home/cloudpane...', 193, Array)\n#1 /vendor/magento/framework/Session/SessionManager.php(193): session_start()\n#2 /generated/code/Magento/Backend/Model/Session/Interceptor.php(63): Magento\Framework\Session\SessionManager->start()\n#3 /vendor/magento/framework/Session/SessionManager.php(130): Magento\Backend\Model\Session\Interceptor->start()\n#4 /generated/code/Magento/Backend/Model/Session/Interceptor.php(14): Magento\Framework\Session\SessionManager->__construct(Object(Magento\Framework\App\Request\Http), Object(Magento\Framework\Session\SidResolver\Proxy), Object(Magento\Backend\Model\Session\AdminConfig), Object(Magento\Framework\Session\SaveHandler), Object(Magento\Framework\Session\Validator), Object(Magento\Framework\Session\Storage), Object(Magento\Framework\Stdlib\Cookie\PhpCookieManager), Object(Magento\Framework\Stdlib\Cookie\CookieMetadataFactory), Object(Magento\Framework\App\State))\n#5 /vendor/magento/framework/ObjectManager/Factory/AbstractFactory.php(111): Magento\Backend\Model\Session\Interceptor->__construct(Object(Magento\Framework\App\Request\Http), Object(Magento\Framework\Session\SidResolver\Proxy), Object(Magento\Backend\Model\Session\AdminConfig), Object(Magento\Framework\Session\SaveHandler), Object(Magento\Framework\Session\Validator), Object(Magento\Framework\Session\Storage), Object(Magento\Framework\Stdlib\Cookie\PhpCookieManager), Object(Magento\Framework\Stdlib\Cookie\CookieMetadataFactory), Object(Magento\Framework\App\State))\n#6 /vendor/magento/framework/ObjectManager/Factory/Dynamic/Developer.php(66): Magento\Framework\ObjectManager\Factory\AbstractFactory->createObject('Magento\\Backend...', Array)\n#7 /vendor/magento/framework/ObjectManager/ObjectManager.php(70): Magento\Framework\ObjectManager\Factory\Dynamic\Developer->create('Magento\\Backend...')\n#8 /vendor/magento/framework/ObjectManager/Factory/AbstractFactory.php(144): Magento\Framework\ObjectManager\ObjectManager->get('Magento\\Backend...')\n#9 /vendor/magento/framework/ObjectManager/Factory/AbstractFactory.php(230): Magento\Framework\ObjectManager\Factory\AbstractFactory->resolveArgument(Array, 'Magento\\Framewo...', NULL, 'session', 'Magento\\Store\\A...')\n#10 /vendor/magento/framework/ObjectManager/Factory/Dynamic/Developer.php(34): Magento\Framework\ObjectManager\Factory\AbstractFactory->resolveArgumentsInRuntime('Magento\\Store\\A...', Array, Array)\n#11 /vendor/magento/framework/ObjectManager/Factory/Dynamic/Developer.php(59): Magento\Framework\ObjectManager\Factory\Dynamic\Developer->_resolveArguments('Magento\\Store\\A...', Array, Array)\n#12 /vendor/magento/framework/ObjectManager/ObjectManager.php(70): Magento\Framework\ObjectManager\Factory\Dynamic\Developer->create('Magento\\Store\\A...')\n#13 /vendor/magento/framework/ObjectManager/Factory/AbstractFactory.php(144): Magento\Framework\ObjectManager\ObjectManager->get('Magento\\Store\\A...')\n#14 /vendor/magento/framework/ObjectManager/Factory/AbstractFactory.php(230): Magento\Framework\ObjectManager\Factory\AbstractFactory->resolveArgument(Array, 'Magento\\Framewo...', NULL, 'redirect', 'Magento\\Backend...')\n#15 /vendor/magento/framework/ObjectManager/Factory/Dynamic/Developer.php(34): Magento\Framework\ObjectManager\Factory\AbstractFactory->resolveArgumentsInRuntime('Magento\\Backend...', Array, Array)\n#16 /vendor/magento/framework/ObjectManager/Factory/Dynamic/Developer.php(59): Magento\Framework\ObjectManager\Factory\Dynamic\Developer->_resolveArguments('Magento\\Backend...', Array, Array)\n#17 /vendor/magento/framework/ObjectManager/ObjectManager.php(70): Magento\Framework\ObjectManager\Factory\Dynamic\Developer->create('Magento\\Backend...')\n#18 /vendor/magento/framework/ObjectManager/Factory/AbstractFactory.php(144): Magento\Framework\ObjectManager\ObjectManager->get('Magento\\Backend...')\n#19 /vendor/magento/framework/ObjectManager/Factory/AbstractFactory.php(230): Magento\Framework\ObjectManager\Factory\AbstractFactory->resolveArgument(Array, 'Magento\\Backend...', NULL, 'context', 'Magento\\Ui\\Cont...')\n#20 /vendor/magento/framework/ObjectManager/Factory/Dynamic/Developer.php(34): Magento\Framework\ObjectManager\Factory\AbstractFactory->resolveArgumentsInRuntime('Magento\\Ui\\Cont...', Array, Array)\n#21 /vendor/magento/framework/ObjectManager/Factory/Dynamic/Developer.php(59): Magento\Framework\ObjectManager\Factory\Dynamic\Developer->_resolveArguments('Magento\\Ui\\Cont...', Array, Array)\n#22 /vendor/magento/framework/ObjectManager/ObjectManager.php(56): Magento\Framework\ObjectManager\Factory\Dynamic\Developer->create('Magento\\Ui\\Cont...', Array)\n#23 /vendor/magento/framework/App/ActionFactory.php(40): Magento\Framework\ObjectManager\ObjectManager->create('Magento\\Ui\\Cont...')\n#24 /vendor/magento/framework/App/Router/Base.php(297): Magento\Framework\App\ActionFactory->create('Magento\\Ui\\Cont...')\n#25 /vendor/magento/framework/App/Router/Base.php(158): Magento\Framework\App\Router\Base->matchAction(Object(Magento\Framework\App\Request\Http), Array)\n#26 /vendor/magento/framework/App/FrontController.php(50): Magento\Framework\App\Router\Base->match(Object(Magento\Framework\App\Request\Http))\n#27 /vendor/magento/framework/Interception/Interceptor.php(58): Magento\Framework\App\FrontController->dispatch(Object(Magento\Framework\App\Request\Http))\n#28 /vendor/magento/framework/Interception/Interceptor.php(138): Magento\Framework\App\FrontController\Interceptor->___callParent('dispatch', Array)\n#29 /vendor/magento/framework/Interception/Interceptor.php(153): Magento\Framework\App\FrontController\Interceptor->Magento\Framework\Interception\{closure}(Object(Magento\Framework\App\Request\Http))\n#30 /generated/code/Magento/Framework/App/FrontController/Interceptor.php(26): Magento\Framework\App\FrontController\Interceptor->___callPlugins('dispatch', Array, Array)\n#31 /vendor/magento/framework/App/Http.php(135): Magento\Framework\App\FrontController\Interceptor->dispatch(Object(Magento\Framework\App\Request\Http))\n#32 /generated/code/Magento/Framework/App/Http/Interceptor.php(24): Magento\Framework\App\Http->launch()\n#33 /vendor/magento/framework/App/Bootstrap.php(256): Magento\Framework\App\Http\Interceptor->launch()\n#34 /pub/index.php(37): Magento\Framework\App\Bootstrap->run(Object(Magento\Framework\App\Http\Interceptor))\n#35 {main}","url":"/admin/mui/index/render/key/[MASKED]/order_id/[MASKED]]?namespace=payone_sales_order_view_transactionstatus_grid&sorting%5Bfield%5D=sequencenumber&sorting%5Bdirection%5D=asc&isAjax=true","script_name":"/index.php"}
The path /var/lib/php/sessions
was never set anywhere. I actually can’t understand why Magento is trying to use the path when redis is configured. It may be related to SessionManager->registerSaveHandler()
?
About this issue
- Original URL
- State: open
- Created 6 years ago
- Reactions: 9
- Comments: 61 (25 by maintainers)
Commits related to this issue
- Fix redis session lock error by increasing default max_concurrency value The default redis session concurrency limit is fairly low. Every magento page load triggers many ansync requests, combined wit... — committed to marvinhinz/magento2 by marvinhinz 4 years ago
- Update Session.php The default redis session concurrency limit is fairly low. Every magento page load triggers many ansync requests, combined with moderate traffic this causes frequent errors. I see... — committed to marvinhinz/magento2 by marvinhinz 4 years ago
@AykutCevik I believe this is indeed due to the
max_concurrency
value in env.php under the session => redis section. Magento is failing to save/retrieve sessions from the redis back-end based on the value of this config. However the error that’s being reported here is stating that having failed to access Redis, the session also couldn’t be accessed from the native PHP session directory (it may not exist on the server in the php.ini specified location, have a check on your server).@engcom-backlog-nickolas, to consistently replicate the issue please try the following steps:
Preconditions
Steps to reproduce
max_concurrency
to a value greater than the number of tabs, reload them all again and none will fail.* if you aren’t seeing this error, check in your PHP sessions directory. It should be empty (as Redis is the session back-end of course), however if there are session files building up in the directory, you are seeing a symptom of the same problem here, although it hides the error because PHP handles the session as a fallback. @AykutCevik try a
php -i | grep session
and check your session save path is available and has the correct permissions. Once corrected you’ll likely see PHP handling the sessions rather than your error.This is all happening because of contention for accessing session data from single threaded Redis. The
max_concurency
config value defines the size of the queue that can form to access Redis and retrieve the session. In the event of lots of traffic hitting the server at once, some users will receive this behavior because the queue is full, so the request to access the Redis session back-end is denied. PHP session handling kicks in here and if configured correctly will start creating sessions (this is still an issue as Redis is meant to be doing this for a reason, PHP will be saving to local disk so sessions wont be available across multiple web servers etc).This same error will happen also on the front-end of Magento2. Try it by opening 10 or more tabs of different pages; clear the FPC (or just disable full page cache to emulate hitting un-cached pages); reload all the tabs at once as above and again some will fail. This is particularly bad when you have items in your cart as the local storage is not synched, resulting in the “I have no items in my cart, but there are items in my mini-cart” scenario I’m sure you’ve come across.
I would suggest that “6” as a default for
max_concurrency
is no good for most sites and that a better explanation of how to derive the correct value for the config should be made available. The Devdocs state “at least 10% of the number of PHP processes” but not why. I would assume that with 10% covered, in the unlikely(ish) event all your users hit pages at the same time (think Flash sale, Black Friday etc), 90% would receive this behavior still. I’m guessing the 10% suggestion is a stab in the dark at the likely percentage of requests that might all hit at the same time. This value should probably be derived from themax_children
/max_requests
values of the server’s PHP-FPM pools, looking to accommodate as many requests as might come in concurrently.Would be great to get a view from Magento on this!
Thanks
@AykutCevik, we are closing this issue due to inactivity. If you’d like to update it, please reopen the issue.
We are still getting this issue even after increasing the
max_concurrency
to18
. We have set it now to54
and are observing if this issue still occurs.However - I still do not quite get why the native session fallback does not work. If I disable Redis for sessions, sessions with the native session storage work fine. But why does the fallback not work, when using Redis as the session storage?
After reading through this thread multiple times and looking into the relevant Magento code, I think that the following is going on:
\Magento\Framework\Session\SaveHandler::callSafely
), but cannot handle the situation when the session is already created in Redis, but it can’t be read from, due to a concurrency issue. Looking at\Magento\Framework\Session\SaveHandler\Redis::read
, we can see that onConcurrentConnectionsExceededException
Magento immediately redirects to a 503 page. It doesn’t even try to fall back to the file based session handling.Warning: session_start(): Failed to read session data: user
) is not related to permission issues. When configuring Magento save the sessions tofiles
, instead ofredis
, it can perfectly read and write from/to the same location.Considering all this, I ended up subclassing
\Magento\Framework\Session\SaveHandler\Redis
, and I added some logic to retry the read onConcurrentConnectionsExceededException
, with exponential backoff. This is what I came up with: https://gist.github.com/pkarsai/0dcc35294870c057b9be9803cba891b6. The tests look promising so far, but it’s not yet in production. BTW,max_concurrency
was set to 30 and were still having this problem, that’s why I ended up implementing this custom solution.Just came here to see that the issue is not resolved for over 3 years. 😔
This happens for me sometimes on the frontend product page. Magento 2.4.3 with redis-server 6.2.5
Any help?
This issue still happening in Magento 2.3.4 @magento-engcom-team please give us your opinion.
HI @AykutCevik Thank you for you report, the fix for this issue has already available in 2.2-develop and 2.3-develop branch, fixed by this pr -> https://github.com/magento/magento2/pull/17608
Why this is P4 ? this issue is easy reproduce in any page just pressing multiple times F5 button and then we got
Hi,
Due to this issue, we faced a few issues in the custom extensions that were resulting in the partially broken product page (custom options and add to cart blocks) were missing AND then cached in Varnish.
How it happened - in the view file we had the code
$block->isLoggedIn()
, that inside was creating customer session and checking if the customer is logged in.In the custom extension we had code similar to this:
I strongly believe we should change severity & priority to S1 P2. @sidolov @gabrieldagama what do you think?
The investigation showed that basically when you’re just creating any session object - it tries to start the session and as result tries to connect to Redis.
In the constructor, it just executes the
start
method: https://github.com/magento/magento2/blob/caefb4e63f0c726c996118c2707afaf63ed3c665/lib/internal/Magento/Framework/Session/SessionManager.php#L117-L142This method runs
session_start
. https://github.com/magento/magento2/blob/caefb4e63f0c726c996118c2707afaf63ed3c665/lib/internal/Magento/Framework/Session/SessionManager.php#L179-L222It means example code from the extension that had just 2 lines were doing session_start 2 times.
Unfortunately, I found a similar code in many places in Magento. https://github.com/magento/magento2/blob/a31f4a35c018ad09654e5bd5871086b73fbd3d2d/app/code/Magento/Persistent/Observer/RenewCookieObserver.php#L83-L87 https://github.com/magento/magento2/blob/a31f4a35c018ad09654e5bd5871086b73fbd3d2d/app/code/Magento/Persistent/Observer/ClearExpiredCronJobObserver.php#L54
Seems like the correct fix for this issue would be one of those options, or maybe even both:
@ihor-sviziev thanks for investigation and detailed report! I changed priority to P2
I confirm, this issue exists in Magento 2.3.5-p1.
A Magento core dev may have a look since we can’t provide any updates.
Agree with @AykutCevik we have this appearing in
var/report
every now and then, its very difficult to track down and/or replicate but is happening. We are also using Redis for session saves.I was able to replicate using the view order > view order grid as @AykutCevik suggested, for a split second I saw the error on the Order view page, before being taken to the order grid.
Here is the stack trace generated:
From searching online, it appears to be either a) a red herring message and some session
read
data is being returned as NULL or FALSE instead of ‘’, b) or Magento is falling back to the native session handler in\Magento\Framework\Session\SaveHandlerFactory::create
c) or something else 😃Unfortunately I still know very little about Magento’s session handling implementation at this point, so it might take some time 😉.
Hello everyone and @engcom-backlog-nazar
I got some help from senior engineers at the company I host with.
They agreed and changed max_concurrency from 6 to 20.
Since then, I haven’t seen the problem again - fingers crossed!
Apologies for not replying sooner.
@chris-pook This seems to have gone away after increasing max_concurrency but it is very worrying
Update on the above comment
Having looked further in to this, I think potentially my above comment may not be completely accurate. The key being the code implementation of the function below:
\Cm\RedisSession\Handler::read()
This function implements the locking mechanism and appears to hold the lock per $sessionId. So this issue is not a problem globally across multiple sessions after all. I.e on a flash sale day, it would not be the case that the first 6 concurrent requests globally would get through and the rest bounce. Instead you would see all requests served but for each individual user’s session only 6 concurrent requests allowed through.
To test this, I have repeated the front-end mass concurrent reload steps from before (with
max_concurrency
set to 6 still) only this time usingab
:Here 100 concurrent requests are served no problem with no failures despite sessions being started.
Having discovered this I am happy that the 10% of PHP processes (php-fpm
max_children
) suggestion is probably quite sound. You don’t want a single user session hogging the Redis instance making everyone else wait, so better to serve them 503 errors beyond their first X concurrent requests.The likelihood is that this limit will only ever be exceeded by admin users trying to do too much at once, or abuse of the front-end.
It would still be great to get a line from Magento here to confirm. Apologies if this is going back over old ground that may have been covered elsewhere.
Thanks
May this be related to
max_concurrency
where the fallback is the default PHP session handling? 🤔 (https://devdocs.magento.com/guides/v2.2/config-guide/redis/redis-session.html)