Кажется, все еще используется много старых версий CI, и я хотел добавить свои два цента, хотя эта ветка старая. Я просто потратил несколько дней на решение проблемы вызовов AJAX в Code Igniter, и у меня есть решение, которое охватывает основные проблемы, хотя некоторые решения не являются «замечательными». Версия CI, которую я (все еще) использую: 2.1.3
Мое приложение требует, чтобы вызовы AJAX обновляли поле last_activity для поддержания допустимого сеанса, поэтому для меня недостаточно просто отказаться от обновления сеанса при вызовах AJAX.
Проверка ошибок для sess_update и sess_read неадекватна в этой версии CI (я не исследовал более поздние версии), и многие проблемы начинаются там.
Часть первая: sess_update()
Несколько вызовов AJAX создают условия гонки, которые приводят к блокировке базы данных для последующих вызовов. Если мы пытаемся выполнить запрос на обновление, но база данных заблокирована, мы получаем ошибку, запрос возвращает false, но cookie все еще обновляется новыми данными? ... ПЛОХО! Кроме того, нам не нужен новый session_id для каждого вызова Ajax. Нам нужно только обновить last_activity. Попробуйте это:
function sess_update()
{
// We only update the session every five minutes by default
if (($this->userdata['last_activity'] + $this->sess_time_to_update) >= $this->now)
{
return;
}
// Save the old session id so we know which record to
// update in the database if we need it
$old_sessid = $this->userdata['session_id'];
//Assume this is an AJAX call... keep the same session_id
$new_sessid = $old_sessid;
if( !$this->CI->input->is_ajax_request() ){
//Then create a new session id
while (strlen($new_sessid) < 32)
{
$new_sessid .= mt_rand(0, mt_getrandmax());
}
// To make the session ID even more secure we'll combine it with the user's IP
$new_sessid .= $this->CI->input->ip_address();
// Turn it into a hash
$new_sessid = md5(uniqid($new_sessid, TRUE));
}
// _set_cookie() will handle this for us if we aren't using database sessions
// by pushing all userdata to the cookie.
$cookie_data = NULL;
// Update the session ID and last_activity field in the DB if needed
if ($this->sess_use_database === TRUE)
{
//TRY THE QUERY FIRST!
//Multiple simultaneous AJAX calls will not be able to update because the Database will be locked. ( Race Conditions )
//Besides... We don't want to update the cookie if the database didn't update
$query = $this->CI->db->query($this->CI->db->update_string($this->sess_table_name, array('last_activity' => $this->now, 'session_id' => $new_sessid), array('session_id' => $old_sessid)));
if( $query ){
// Update the session data in the session data array
$this->userdata['session_id'] = $new_sessid;
$this->userdata['last_activity'] = $this->now;
// set cookie explicitly to only have our session data
$cookie_data = array();
foreach (array('session_id','ip_address','user_agent','last_activity') as $val)
{
$cookie_data[$val] = $this->userdata[$val];
}
// Write the cookie
$this->_set_cookie($cookie_data);
}else{
//do nothing... we don't care, we still have an active retreivable session and the update didn't work
//debug: error_log( "ERROR::" . $this->CI->db->_error_message() ); //Shows locked session database
}
}else{
// Update the session data in the session data array
$this->userdata['session_id'] = $new_sessid;
$this->userdata['last_activity'] = $this->now;
// Write the cookie
$this->_set_cookie($cookie_data);
}
}
часть 2: sess_read()
Очень похожая проблема здесь ... База данных иногда блокируется во время запроса. За исключением того, что мы не можем игнорировать ошибки на этот раз. Мы пытаемся прочитать сессию, чтобы увидеть, существует ли она ... поэтому, если мы получим ошибку заблокированной базы данных, мы можем проверить ее и повторить попытку (пару раз, если потребуется). В моем тестировании я никогда не делал больше 2 попыток). Кроме того, я не знаю о вас, но я не хочу, чтобы php завершался с ошибкой из-за фатальной ошибки, не проверяя ложный результат запроса. Это понадобится в верхней части файла session.php, если вы хотите попробовать этот код напрямую:
var $sess_query_attempts = 5;
Также обратите внимание, что это не вся функция sess_read
$query = $this->CI->db->get($this->sess_table_name);
//Multiple AJAX calls checking
//But adding add a loop to check a couple more times has stopped premature session breaking
$counter = 0;
while( !$query && $counter < $this->sess_query_attempts ){
usleep(100000);//wait a tenth of a second
$this->CI->db->where('session_id', $session['session_id']);
if ($this->sess_match_ip == TRUE)
{
$this->CI->db->where('ip_address', $session['ip_address']);
}
if ($this->sess_match_useragent == TRUE)
{
$this->CI->db->where('user_agent', $session['user_agent']);
}
$query = $this->CI->db->get($this->sess_table_name);
$counter++;
}
if ( !$query || $query->num_rows() == 0)
{
$this->CI->db->where('session_id', $session['session_id']);
$query = $this->CI->db->get( $this->sess_table_name );
$this->sess_destroy();
return FALSE;
}
В любом случае, imho, там нет полного ответа на эту проблему, и я чувствовал, что должен поделиться своими результатами с теми, кто, возможно, все еще испытывает ранние тайм-ауты сеансов на сайтах, которые используют тонны AJAX, как мой.