2
我寫了一個簡單的session_set_save_handler使用MySQL innoDB表作爲存儲後端。 MySQL是5.5。PHP MySQL session_set_save_handler競爭條件
代碼看起來像這樣(非常簡化,但希望能夠說明代碼流),我使用RedBean ORM與數據庫進行交互,但是在顯示正在寫入,讀取或刪除的內容時,命令應該清楚:
class MySession{
private $_database;
public function __construct($database){
//database object is injected using dependency injection then assigned to private var
//Hook up handlers
session_set_save_handler(
array($this, "open"),
array($this, "close"),
array($this, "read"),
array($this, "write"),
array($this, "destroy"),
array($this, "garbageCollection"));
}
//Register the shutdown function
register_shutdown_function('session_write_close');
//start
session_start();
//Regenerate session id
//(NOTE: in production code, the id is regenerated every 10 minutes,
//but regenerating allows us to trigger the race condition for demonstration.
session_regenerate_id(TRUE);
public function open($savePath, $sessionName){
$this->_database->exec('SET TRANSACTION ISOLATION LEVEL SERIALIZABLE');
$this->_database->begin(); //Begin Transaction
return true;
}
public function close(){
$this->_database->commit(); //Commit the transaction
return true;
}
public function read($id){
$this->_database->exec('SELECT * FROM session WHERE identifier = ? LOCK IN SHARE MODE', array($id));
$session = $this->_database->findOne('session', 'identifier = ?', array($id));
return $session->data;
}
public function write($id, $sessionData){
$this->_database->exec('SELECT * FROM session WHERE identifier = ? FOR UPDATE', array($id));
$session = $this->_database->findOne('session', 'identifier = ?', array($id));
//We need to create a new session
if ($session == NULL){
$session = $this->_database->dispense('session');
}
$this->_database->store($session);
return TRUE;
}
public function destroy($id){
$this->_database->exec('SELECT * FROM session WHERE identifier = ? FOR UPDATE', array($id));
$session = $this->_database->findOne('session', 'identifier = ?', array($id));
if ($session != NULL){
$this->_database->trash($session);
}
return TRUE;
}
public function garbageCollection($maxlifetime){
$old = ... //Calculate the time for expired sessions
$this->_database->exec("DELETE from session WHERE last_activity < ?", array($old));
return TRUE;
}
}
的問題是,即使在發生鎖定,如果我發送多個請求的應用程序(例如,使用AJAX),比賽條件仍然存在,並存儲在會話中的數據丟失。我已將其固定爲session_regenerate_id(TRUE)
,這會刪除以前的會話以解決問題的原因。但是,即使使用行鎖定,問題仍然存在。
我看過這個page,以及來自該作者的一些code,但是它使用了實現表鎖定的MyISAM表。
任何人都可以闡明爲什麼鎖定沒有改變,也許提供一些解決方案來解決這個問題?
'session_regenerate_id'總是有問題,並且容易受到客戶端上的競爭條件的影響。你確定它不是固有的cookie更新計時問題,而不是數據庫級別的問題嗎? – deceze 2012-04-23 02:14:33
實際上我不確定,現在你提到它。有沒有辦法使用dbug並檢查cookie更新時機問題? – F21 2012-04-23 02:16:00
這很棘手,很有趣。 ;)使用大量的日誌記錄和網絡流量自省來查看誰在什麼時候發送什麼。 – deceze 2012-04-23 02:18:30