Merge pull request #5717 from cakephp/2.7-session-concurrency

2.7 session concurrency
This commit is contained in:
Mark Story 2015-01-23 13:16:49 -05:00
commit 4113eb0db4
2 changed files with 66 additions and 1 deletions

View file

@ -103,6 +103,9 @@ class DatabaseSession implements CakeSessionHandlerInterface {
/**
* Helper function called on write for database sessions.
*
* Will retry, once, if the save triggers a PDOException which
* can happen if a race condition is encountered
*
* @param int $id ID that uniquely identifies session in database
* @param mixed $data The value of the data to be saved.
* @return bool True for successful write, false otherwise.
@ -114,7 +117,17 @@ class DatabaseSession implements CakeSessionHandlerInterface {
$expires = time() + $this->_timeout;
$record = compact('id', 'data', 'expires');
$record[$this->_model->primaryKey] = $id;
return $this->_model->save($record);
$options = array(
'validate' => false,
'callbacks' => false,
'counterCache' => false
);
try {
return $this->_model->save($record, $options);
} catch (PDOException $e) {
return $this->_model->save($record, $options);
}
}
/**

View file

@ -191,4 +191,56 @@ class DatabaseSessionTest extends CakeTestCase {
$storage->gc();
$this->assertFalse($storage->read('foo'));
}
/**
* testConcurrentInsert
*
* @return void
*/
public function testConcurrentInsert() {
$this->skipIf(
$this->db instanceof Sqlite,
'Sqlite does not throw exceptions when attempting to insert a duplicate primary key'
);
ClassRegistry::removeObject('Session');
$mockedModel = $this->getMockForModel(
'SessionTestModel',
array('exists'),
array('alias' => 'MockedSessionTestModel', 'table' => 'sessions')
);
Configure::write('Session.handler.model', 'MockedSessionTestModel');
$counter = 0;
// First save
$mockedModel->expects($this->at($counter++))
->method('exists')
->will($this->returnValue(false));
// Second save
$mockedModel->expects($this->at($counter++))
->method('exists')
->will($this->returnValue(false));
// Second save retry
$mockedModel->expects($this->at($counter++))
->method('exists')
->will($this->returnValue(true));
// Datasource exists check
$mockedModel->expects($this->at($counter++))
->method('exists')
->will($this->returnValue(true));
$this->storage = new DatabaseSession();
$this->storage->write('foo', 'Some value');
$return = $this->storage->read('foo');
$this->assertSame('Some value', $return);
$this->storage->write('foo', 'Some other value');
$return = $this->storage->read('foo');
$this->assertSame('Some other value', $return);
}
}