Galera cluster doesn't support locking tables (MySQL, MariaDB), so the new Session library doesn't work with it (random segfaults).
File driver is not an option with load balancer and at the time being there's no support for Redis and Memcahed either so I need figure out other way lock the session.
I've been testing locking with extra table and it seems to work reasonably well even under tons of ajax calls (although can be little slow if lot's of stuff waiting).
Because there's no "real lock", sometimes one process can create lock while other is doing it too, resulting Duplicate Entry -error in other query. I can detect the error in the code below but I'm not too happy about script making query errors to my logs etc. I tried transactions for this too but while it worked, I got same error to my logs.
So this seems to work, but any ideas how to improve it? Or other ides how to do it?
Table:
PHP Code:
CREATE TABLE `ci_sessions_lock` (
`id` varchar(40) NOT NULL DEFAULT '',
`timestamp` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Get lock:
PHP Code:
if ($this->_platform === 'mysql')
{
$arg = $session_id . ($this->_config['match_ip'] ? '_' . $this->ip_address : '');
$attempt = 0;
$expired_lock_time = time() - 300;
do
{
// Check if we have locks
$this->_db
->select('id,timestamp')
->from('ci_sessions_lock')
->where('id', $arg);
$result = $this->_db->get()->row(0);
// If lock is too old, let's delete it
if ($result && $result->timestamp < $expired_lock_time)
{
$this->_db->delete('ci_sessions_lock', array('id' => $arg));
$result = false;
}
// If lock is found, we need to wait until it's deleted
if ($result)
{
sleep(1);
continue;
}
// Let's create the lock
$timestamp = time() + 300;
$query = $this->_db->query('INSERT INTO ci_sessions_lock (id, timestamp) VALUES (\'' . $arg . '\', ' . $timestamp . ')');
// Most likely if we have error at this point, it's because duplicate key exist
// So let's wait more
if ( ! $query)
{
sleep(1);
continue;
}
$this->_lock = $arg;
break;
}
while (++ $attempt < 30);
if ($attempt === 30)
{
log_message('error', 'Session: Unable to obtain lock for ' . $arg . ' after 30 attempts, aborting.');
return false;
}
return true;
}
Release lock:
PHP Code:
if ($this->_platform === 'mysql')
{
$this->_db->delete('ci_sessions_lock', array('id' => $this->_lock));
$this->_lock = false;
}