25

Mys site works very slowly (and I didn't have any idea about why). It is based on Zend Application, I used to make about tens of such sites, so I'm sure that MY code is OK.

I installed xdebugger on server, tried to profile it and guess what? php::session_start() took 48.675 seconds. Fourty Eight and a Half Seconds! It's unbelievable! What could be the reason of this? It's common operation, why could it execute SO long? How to fix such behaviour, which configs to edit? Searched over Google, but found no good answer (almost everywhere there's a question, but no answer). Thanks in before!

xdebugger profiling results

ABTOMAT
  • 722
  • 3
  • 9
  • 20
  • 1
    48 seconds to start a session?? that's insane! What and how much data is in the session? Are you using any session handling libraries? saving sessions to an unusual location? Database (check your indexes)? non-local filesystem? What kinds of server is it running on? Do you have any session config in php.ini or .htaccess? – Spudley Dec 07 '12 at 22:39

9 Answers9

22

session_start (with sessions stored in files) is blocking in PHP, so this issue will appear if you try to start several server sessions for the same browser session (AJAX or multiple browser tabs/windows). Each session_start will wait until the other sessions have been closed.

See here: http://konrness.com/php5/how-to-prevent-blocking-php-requests/

Try changing from files to database storage of sessions.

Christian Davén
  • 13,902
  • 10
  • 53
  • 68
  • 1
    Thanks, it was it. [session_write_close](http://php.net/manual/en/function.session-write-close.php) helped me. – Oleg Dec 19 '14 at 09:10
  • 1
    Thanks, same problem with long pooling, so if a script is waiting, session_write_close before wait/sleep, and session_start again on wake up. – KyleK Mar 02 '15 at 18:26
  • Yep, in my case it was SSE which caused the long times. – Peter VARGA Nov 01 '20 at 22:35
19

My guess would be the garbage collection routine, which gets run inside of the native session_start() function. Maybe you've done something that keeps many old session files around, like changed the max life time? Or maybe you've decided it would be a good idea to store them in a database, but forgot to create a suitable index? The native GC routine stat()'s every single session file to check for expiration. This is time consuming if there's a lot of files built up.

edit: to help you for debugging only, disable garbage collection by temporarily setting session.gc-probability:

session.gc-probability = 0

Make sure the settings stick, I don't know what the zend framework might be doing here.

P.S. It's difficult to suggestion a fix without knowing the cause. My answer is meant to guide you towards identifying the cause.

icc97
  • 8,746
  • 6
  • 60
  • 75
goat
  • 29,650
  • 7
  • 65
  • 92
7

I have had this problem and am surprised that nobody has posted this specific response. It may not be it but it is worth checking.

PHP LOCKS THE SESSION FILE while a page is processing, so that page can have exclusive access to it. Think about it, the sess_184c9aciqoc file is not a database, so two calls in the same session can't access it simultaneously. So if you have a lot of ajax calls, you can get a "traffic jam". Once you start doing advanced scripting this is a gotcha to beware of. by the way, here is a function to store an array of timestamps. I used this to figure out session start was the culprit:

//time function for benchmarking
if( function_exists('gmicrotime')){
    function gmicrotime($n=''){
        #version 1.1, 2007-05-09
        //store array of all calls
        global $mT;
        list($usec, $sec) = explode(' ',microtime());
        if(!isset($mT['_base_']))$mT['_base_']=$sec;
    $t=round((float)$usec + (float)(substr($sec,-4)),6);
    $mT['all'][]=$t;
    if($n){
        if(isset($mT['indexed'][$n])){
            //store repeated calls with same index.  If in a loop, add a $i if needed
            if(is_array($mT['indexed'][$n])){
                $mT['indexed'][$n][]=$t;
            }else{
                $mT['indexed'][$n]=array($mT['indexed'][$n],$t);
            }
        }else $mT['indexed'][$n]=$t;    
    }
    //return elapsed since last call (in the local array)
    $u=$mT['all'];
    if(count($u)>1){
        $mT['_total_']=$u[count($u)-1] - $u[0];
        return round(1000*($u[count($u)-1]-$u[count($u)-2]),6);
    }
}
gmicrotime('pageStart');
}

then i call as follows:

gmicrotime('beforeSessionStart');
session_start();
gmicrotime('afterSessionStart');

do_something_slow();
gmicrotime('afterSlowProcess');
//etc..
echo '<pre>';
print_r($mT);  

Hope this is helpful!

Samuel Fullman
  • 1,036
  • 1
  • 13
  • 20
  • My issue turned out to be old sess_* files that may have been locked. I cleared out all the sess_* files and that seemed to do the trick. – dlporter98 Mar 08 '19 at 14:48
2

Another approach might be that you have set a large memory_limit in PHP.ini.

I did that for uploading huge mysql dumps into PHPMyAdmin and load time spiked, perhaps (as said above) a lot of session files piled up now that PHP had room to spare. The default is 128M, I think. I had quadrupled that.

PeerBr
  • 665
  • 10
  • 25
0

One way to avoid this problem is to ask PHP to store sessions in a database table instead of files.

Firstly, I will give you a few links as real credits for this solution:

http://www.tonymarston.net/php-mysql/session-handler.html

http://shiflett.org/articles/storing-sessions-in-a-database

http://culttt.com/2013/02/04/how-to-save-php-sessions-to-a-database/

Then a code implementation I derived from these readings:

<?php

class TLB_Sessions_in_Database
{
    private $debug;
    private $dbc;

    function __construct()
    {
        $this->debug = false;

        session_set_save_handler(
            array($this, '_open'),
            array($this, '_close'),
            array($this, '_read'),
            array($this, '_write'),
            array($this, '_destroy'),
            array($this, '_clean')
        );
    }

    function _open()
    {
        if( $this->debug ) echo '_open:'.PHP_EOL;

        if( ($this->dbc = mysql_connect(DB_HOST, DB_USER, DB_PASSWORD)) !== false )
        {
            $select_db = mysql_select_db(DB_NAME, $this->dbc);
            $set_charset = mysql_set_charset(DB_CHARSET, $this->dbc);

            if( $this->debug ) echo '- return: '.(( $select_db && $set_charset ) ? 'true' : 'false').PHP_EOL;

            return( $select_db && $set_charset );
        }
        else
        {
            if( $this->debug ) echo '- error: '.mysql_error($this->dbc).PHP_EOL;
        }

        return( false );
    }

    function _close()
    {
        if( $this->debug ) echo '_close:'.PHP_EOL;

        return( mysql_close($this->dbc) );
    }

    function _read($session_id)
    {
        if( $this->debug ) echo '_read:'.PHP_EOL;

        $session_id = mysql_real_escape_string($session_id);

        $sql = "SELECT `session_data` FROM `".DB_NAME."`.`php_sessions` WHERE `session_id` = '".$session_id."'";

        if( $this->debug ) echo '- query: '.$sql.PHP_EOL;

        if( ($result = mysql_query($sql, $this->dbc)) !== false )
        {
            if( !in_array(mysql_num_rows($result), array(0, false), true) )
            {
                $record = mysql_fetch_assoc($result);

                return( $record['session_data'] );
            }
        }
        else
        {
            if( $this->debug ) echo '- error: '.mysql_error($this->dbc).PHP_EOL;
        }

        return( '' );
    }

    function _write($session_id, $session_data)
    {
        if( $this->debug ) echo '_write:'.PHP_EOL;

        $session_id = mysql_real_escape_string($session_id);
        $session_data = mysql_real_escape_string($session_data);

        //$sql = "REPLACE INTO `php_sessions` (`session_id`, `last_updated`, `session_data`) VALUES ('".$session_id."', '".time()."', '".$session_data."')";
        $sql = "INSERT INTO `".DB_NAME."`.`php_sessions` (`session_id`, `date_created`, `session_data`) VALUES ('".$session_id."', NOW(), '".$session_data."') ON DUPLICATE KEY UPDATE `last_updated` = NOW(), `session_data` = '".$session_data."'";

        if( ($result = mysql_query($sql, $this->dbc)) === false )
        {
            if( $this->debug ) echo '- error: '.mysql_error($this->dbc).PHP_EOL;
        }

        return( $result );
    }

    function _destroy($session_id)
    {
        if( $this->debug ) echo '_destroy:'.PHP_EOL;

        $session_id = mysql_real_escape_string($session_id);

        $sql = "DELETE FROM `".DB_NAME."`.`php_sessions` WHERE `session_id` = '".$session_id."'";

        if( ($result = mysql_query($sql, $this->dbc)) === false )
        {
            if( $this->debug ) echo '- error: '.mysql_error($this->dbc).PHP_EOL;
        }

        return( $result );
    }

    function _clean($max)
    {
        if( $this->debug ) echo '_clean:'.PHP_EOL;

        $sql = 'DELETE FROM `'.DB_NAME.'`.`php_sessions` WHERE `last_updated` < DATE_SUB(NOW(), INTERVAL '.$max.' SECOND)';

        if( ($result = mysql_query($sql, $this->dbc)) === false )
        {
            if( $this->debug ) echo '- error: '.mysql_error($this->dbc).PHP_EOL;
        }

        return( $result );
    }
}

new TLB_Sessions_in_Database();

END.

0

If you have multiple concurrent ajax calls on the same page this situation may cause your problem.

Nicolas Finelli
  • 2,106
  • 1
  • 13
  • 9
0

In my case it was incorrect memcache server settings in /etc/php.d/memcached.ini Here is information on memcache properties and here is how to setup storage in memcache.

vikramaditya234
  • 1,038
  • 1
  • 13
  • 30
  • This is a bad answer because it doesn't tell what the incorrect, or correct, settings were. – KIKO Software Jun 10 '17 at 08:46
  • @kikosoftware memcached settings can vary with what you are trying to achieve. You can learn more about this [here](http://php.net/manual/en/memcached.configuration.php). In my case `extension` setting was missing to point to elasticache library. Request you to remove downvote – vikramaditya234 Jun 12 '17 at 02:52
  • Thanks for adding that information. I don't see an `extension` setting on the PHP manual page you link to. Do you mean `memcached.serializer`? At the moment I cannot remove my downvote: "You last voted on this answer yesterday. Your vote is now locked in unless this answer is edited.". That makes perfect sense. – KIKO Software Jun 12 '17 at 06:41
0

I just had this issue. session_start was taking about 5sec.

My issue was I had declared some variables above it.

I moved session_start to the top and it now takes a few milliseconds.

Shane Zammit
  • 3
  • 1
  • 6
0

My page opens concurrent sessions within many <img src="download_image.php"> tags where download_image.php run session_start() and then downloading the image.

Inserting a session_write_close() in download_image.php fixed my problem.

session_start();

session_write_close(); 

download_image();

I have tried memcached and session_start(['read_and_close'=>true]). But only session_write_close() works for me.

Liu Kc
  • 31
  • 3