Hi All

*Environment:*
CakePHP version: *2.4.0 stable*
latest PHP 5.3

We just recently pushed our app to AWS Beanstalk with ElastiCache + RDS as 
our combo session, and using the ComboSession technique described in 
here<http://book.cakephp.org/2.0/en/development/sessions.html#creating-a-custom-session-handler>.
 
At first everything was ok, but then out of sudden, our app keep running 
into some infinity loops that we can't really reproduce at that point of 
time. What we can do is basically remove the cookie key from RDS, or clear 
browser cookie. Clearly it has something to do with Session.

So these are our sample error_logs:

[Wed Sep 11 03:56:27 2013] [error] [client 10.251.91.107] PHP Fatal error: 
 Maximum execution time of 60 seconds exceeded in 
/var/app/current/lib/Cake/Model/Datasource/CakeSession.php on line 612
[Wed Sep 11 04:00:49 2013] [error] [client 10.251.91.107] PHP Fatal error: 
 Maximum execution time of 60 seconds exceeded in 
/var/app/current/lib/Cake/Model/Datasource/CakeSession.php on line 612
[Wed Sep 11 04:03:02 2013] [error] [client 10.251.91.107] PHP Fatal error: 
 Maximum execution time of 60 seconds exceeded in 
/var/app/current/lib/Cake/Model/Datasource/CakeSession.php on line 612
.... repeating ....
[Thu Sep 12 01:11:48 2013] [error] [client 10.251.91.107] PHP Fatal error: 
 Maximum execution time of 60 seconds exceeded in 
/var/app/current/lib/Cake/Model/Datasource/CakeSession.php on line 612
[Thu Sep 12 01:13:50 2013] [error] [client 10.251.91.107] PHP Fatal error: 
 Maximum execution time of 60 seconds exceeded in 
/var/app/current/lib/Cake/Model/Datasource/CakeSession.php on line 612
[Thu Sep 12 01:15:15 2013] [error] [client 10.251.91.107] PHP Fatal error: 
 Allowed memory size of 268435456 bytes exhausted (tried to allocate 523800 
bytes) in /var/app/current/lib/Cake/Model/Model.php on line 1368

So apparently CakeSession is trying to session_start in a loop that it 
can't resolve the session successfully.

Then we found out one serious issue with the *ComboSession.php* mentioned 
in the post 
http://book.cakephp.org/2.0/en/development/sessions.html#creating-a-custom-session-handler,
 
that, let's say, if Cache lost the session data, and Cache::delete($id) 
fails, parent::destroy() will simply won't be called to destroy expired 
session.

That says, we run into this kind of loop:

*Let's assume Cache lost the session data, and RDS retained a piece of 
expired session data:*
1. CakeSession::start()
2. CakeSession::_startSession() - session data get loaded from RDS because 
Cache::read returns false
3. CakeSession::_checkValid()
4. CakeSession::_validAgentAndTime() - obviously timeout, we got an invalid 
data here
5. CakeSession::destroy() - this calls destroy the session and trying to 
call session_destroy()
6. ComboSession::destroy($id) - this calls attempted to destroy the session 
data from Cache but failed (Memcache engine says not found!), and since 
it's failed, parent::destroy() will never get triggered, and so *old 
session data is survived*.
7. Go to step 1 and continues looping, and PHP will dies either the whole 
memory get filled up or time out.


To reproduce, we use this modified version of ComboSession to simulate the 
problem: *(The following code is to demonstrate the bug, not a working 
copy!)*

<?php
App::uses('DatabaseSession', 'Model/Datasource/Session');

class ComboSession extends DatabaseSession implements 
CakeSessionHandlerInterface {
    public $cacheKey;

    public function __construct() {
        $this->cacheKey = Configure::read('Session.handler.cache');
        parent::__construct();
    }

    // read data from the session.
    public function read($id) {
        $result = false; //simulate cache read fails
        if ($result) {
            return $result;
        }
        return parent::read($id);
    }

// write data into the session.
    public function write($id, $data) {
        $result = Cache::write($id, $data, $this->cacheKey);
        if ($result) {
            return parent::write($id, $data);
        }
        return false;
    }

    // destroy a session.
    public function destroy($id) {
        $result = false; //simulate Cache::delete fails
        if ($result) {
            return parent::destroy($id);
        }
        return false;
    }

    // removes expired sessions.
    public function gc($expires = null) {
        return Cache::gc($this->cacheKey) && parent::gc($expires);
    }
}


And in the database, try modify your current session with time (in data), 
and expires to a earlier time. You should run into infinity loop in the 
next run.

-----------------------------------

In the end, we fix it by issue delete on both Cache and RDS session data 
regardless if Cache succeed or not:

    public function destroy($id) {
//Delete, regardless of cache succeed or not (so to prevent infinity loop)
        Cache::delete($id, $this->cacheKey);
        return parent::destroy($id);
    }


*Suggestion: To prevent someone from having this issue again, I think it's 
best if you can update your documentation at *
http://book.cakephp.org/2.0/en/development/sessions.html#creating-a-custom-session-handler
.

Thanks!
/Lionel 

-- 
Like Us on FaceBook https://www.facebook.com/CakePHP
Find us on Twitter http://twitter.com/CakePHP

--- 
You received this message because you are subscribed to the Google Groups 
"CakePHP" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to cake-php+unsubscr...@googlegroups.com.
To post to this group, send email to cake-php@googlegroups.com.
Visit this group at http://groups.google.com/group/cake-php.
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to