Skip to content

Commit

Permalink
Revamp db session storage to work in exclusive locking mode like nati…
Browse files Browse the repository at this point in the history
…ve file session storage works
  • Loading branch information
Shadow243 committed Nov 23, 2024
1 parent f87ac3b commit 518c456
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 2 deletions.
2 changes: 1 addition & 1 deletion config/database.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
| CREATE TABLE hm_user_session (hm_id varchar(250) primary key not null, data text, date timestamp);
|
| MySQL or SQLite:
| CREATE TABLE hm_user_session (hm_id varchar(180), data longblob, date timestamp, primary key (hm_id));
| CREATE TABLE hm_user_session (hm_id varchar(180), data longblob, lock INTEGER DEFAULT 0, date timestamp, primary key (hm_id));
|
|
| DB Authentication
Expand Down
106 changes: 106 additions & 0 deletions lib/session_db.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ class Hm_DB_Session extends Hm_PHP_Session {
/* DB handle */
protected $dbh;

/*
* Database driver type from site config
*/
private $db_driver;

/*
* Timeout for acquiring locks (seconds)
*/
private $lock_timeout = 10;

/**
* Create a new session
* @return boolean|integer|array
Expand All @@ -31,6 +41,7 @@ public function insert_session_row() {
* @return bool true on success
*/
public function connect() {
$this->db_driver = $this->site_config->get('db_driver', false);
return ($this->dbh = Hm_DB::connect($this->site_config)) ? true : false;
}

Expand Down Expand Up @@ -66,12 +77,17 @@ public function start_new($request) {
*/
public function start_existing($key) {
$this->session_key = $key;
if (!$this->acquire_lock($key)) {
Hm_Debug::add('DB SESSION: Failed to acquire lock');
return;
}
$data = $this->get_session_data($key);
if (is_array($data)) {
Hm_Debug::add('LOGGED IN');
$this->active = true;
$this->data = $data;
}
$this->release_lock($key);
}

/**
Expand Down Expand Up @@ -169,4 +185,94 @@ public function db_start($request) {
}
}
}

/**
* Acquire a lock for the session (unified for all DB types)
* @param string $key session key
* @return bool true if lock acquired, false otherwise
*/
private function acquire_lock($key) {
$lock_name = 'session_lock_' . substr(hash('sha256', $key), 0, 51);
$query = '';
$params = [];

switch ($this->db_driver) {
case 'mysql':
$query = 'SELECT GET_LOCK(:lock_name, :timeout)';
$params = [':lock_name' => $lock_name, ':timeout' => $this->lock_timeout];
break;

case 'pgsql':
$query = 'SELECT pg_try_advisory_lock(:hash_key)';
$params = [':hash_key' => crc32($lock_name)];
break;

case 'sqlite':
$query = 'UPDATE hm_user_session SET lock=1 WHERE hm_id=? AND lock=0';
$params = [$key];
break;

default:
Hm_Debug::add('DB SESSION: Unsupported db_driver for locking: ' . $this->db_driver);
return false;
}

$result = Hm_DB::execute($this->dbh, $query, $params);
if ($this->db_driver == 'mysql') {
return isset($result['GET_LOCK(?, ?)']) && $result['GET_LOCK(?, ?)'] == 1;
}
if ($this->db_driver == 'pgsql') {
return isset($result['pg_try_advisory_lock']) && $result['pg_try_advisory_lock'] === true;
}

if ($this->db_driver == 'sqlite') {
return isset($result[0]) && $result[0] == 1;
}
return false;
}

/**
* Release a lock for the session (unified for all DB types)
* @param string $key session key
* @return bool true if lock released, false otherwise
*/
private function release_lock($key) {
$query = '';
$params = [];

$lock_name = "session_lock_" . substr(hash('sha256', $key), 0, 51);
switch ($this->db_driver) {
case 'mysql':
$query = 'SELECT RELEASE_LOCK(:lock_name)';
$params = [':lock_name' => $lock_name];
break;

case 'pgsql':
$query = 'SELECT pg_advisory_unlock(:hash_key)';
$params = [':hash_key' => crc32($lock_name)];
break;

case 'sqlite':
$query = 'UPDATE hm_user_session SET lock=0 WHERE hm_id=?';
$params = [$key];
break;

default:
Hm_Debug::add('DB SESSION: Unsupported db_driver for unlocking: ' . $this->db_driver);
return false;
}
$result = Hm_DB::execute($this->dbh, $query, $params);
if ($this->db_driver == 'mysql') {
return isset($result['GET_LOCK(?, ?)']) && $result['GET_LOCK(?, ?)'] == 1;
}
if ($this->db_driver == 'pgsql') {
return isset($result['pg_advisory_unlock']) && $result['pg_advisory_unlock'] === true;
}

if ($this->db_driver == 'sqlite') {
return isset($result[0]) && $result[0] == 1;
}
Hm_Debug::add('DB SESSION: Lock release failed. Query: ' . $query . ' Parameters: ' . json_encode($params));
return false;
}
}
5 changes: 4 additions & 1 deletion scripts/setup_database.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,11 @@
if (strcasecmp($session_type, 'DB')==0) {
printf("Creating database table hm_user_session ...\n");

if ($db_driver == 'mysql' || $db_driver == 'sqlite') {
if ($db_driver == 'mysql') {
$stmt = "{$create_table} hm_user_session (hm_id varchar(255), data longblob, date timestamp, primary key (hm_id));";
} elseif($db_driver == 'sqlite') {
//0 means unlocked, 1 means locked
$stmt = "{$create_table} hm_user_session (hm_id varchar(255), data longblob, lock INTEGER DEFAULT 0, date timestamp, primary key (hm_id));";
} elseif ($db_driver == 'pgsql') {
$stmt = "{$create_table} hm_user_session (hm_id varchar(255) primary key not null, data text, date timestamp);";
} else {
Expand Down

0 comments on commit 518c456

Please sign in to comment.