MDL-66151 Performance: Session Manager modularisation

Storage of session metadata has moved into the session handler class.
This allows for other classes to fully control session handling and
removes the dependancy on the core sessions database table.

Previously, the standard method of interaction with the
session metadata was direct DB calls; this may break other plugins as there
are now proper APIs available through the session manager.

Co-authored-by: Darren Cocco <[email protected]>
Co-authored-by: Trisha Milan <[email protected]>
Co-authored-by: Andrew Nicols <[email protected]>
This commit is contained in:
Trisha Milan
2024-09-03 13:04:04 +10:00
committed by Matt Porritt
co-authored by Darren Cocco Andrew Nicols
parent 072fb90384
commit e52fbd2f84
30 changed files with 1408 additions and 700 deletions
+28 -88
View File
@@ -14,14 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Database based session handler.
*
* @package core
* @copyright 2013 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\session;
use SessionHandlerInterface;
@@ -34,7 +26,8 @@ use SessionHandlerInterface;
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class database extends handler implements SessionHandlerInterface {
/** @var \stdClass $record session record */
/** @var int $record session record */
protected $recordid = null;
/** @var \moodle_database $database session database */
@@ -62,9 +55,7 @@ class database extends handler implements SessionHandlerInterface {
}
}
/**
* Init session handler.
*/
#[\Override]
public function init() {
if (!$this->database->session_lock_supported()) {
throw new exception('sessionhandlerproblem', 'error', '', null, 'Database does not support session locking');
@@ -76,35 +67,36 @@ class database extends handler implements SessionHandlerInterface {
}
}
/**
* Check the backend contains data for this session id.
*
* Note: this is intended to be called from manager::session_exists() only.
*
* @param string $sid
* @return bool true if session found.
*/
#[\Override]
public function session_exists($sid) {
// It was already checked in the calling code that the record in sessions table exists.
return true;
}
/**
* Kill all active sessions, the core sessions table is
* purged afterwards.
*/
public function kill_all_sessions() {
// Nothing to do, the sessions table is cleared from core.
return;
}
#[\Override]
public function destroy(string $id): bool {
if (!$session = $this->get_session_by_sid($id)) {
if ($id == session_id()) {
$this->recordid = null;
$this->lasthash = null;
}
return true;
}
/**
* Kill one session, the session record is removed afterwards.
* @param string $sid
*/
public function kill_session($sid) {
// Nothing to do, the sessions table is purged afterwards.
return;
if ($this->recordid && ($session->id == $this->recordid)) {
try {
$this->database->release_session_lock($this->recordid);
} catch (\Exception $ex) {
// Log and ignore any problems.
mtrace('Failed to release session lock: '.$ex->getMessage());
}
$this->recordid = null;
$this->lasthash = null;
}
$this->database->delete_records('sessions', ['id' => $session->id]);
return true;
}
/**
@@ -151,7 +143,7 @@ class database extends handler implements SessionHandlerInterface {
*/
public function read(string $sid): string|false {
try {
if (!$record = $this->database->get_record('sessions', array('sid'=>$sid), 'id')) {
if (!$record = $this->get_session_by_sid($sid)) {
// Let's cheat and skip locking if this is the first access,
// do not create the record here, let the manager do it after session init.
$this->failed = false;
@@ -252,56 +244,4 @@ class database extends handler implements SessionHandlerInterface {
return true;
}
/**
* Destroy session handler.
*
* {@see http://php.net/manual/en/function.session-set-save-handler.php}
*
* @param string $id
* @return bool success
*/
public function destroy(string $id): bool {
if (!$session = $this->database->get_record('sessions', ['sid' => $id], 'id, sid')) {
if ($id == session_id()) {
$this->recordid = null;
$this->lasthash = null;
}
return true;
}
if ($this->recordid && ($session->id == $this->recordid)) {
try {
$this->database->release_session_lock($this->recordid);
} catch (\Exception $ex) {
// Ignore problems.
}
$this->recordid = null;
$this->lasthash = null;
}
$this->database->delete_records('sessions', ['id' => $session->id]);
return true;
}
/**
* GC session handler.
*
* {@see http://php.net/manual/en/function.session-set-save-handler.php}
*
* @param int $max_lifetime moodle uses special timeout rules
* @return bool success
*/
// phpcs:ignore moodle.NamingConventions.ValidVariableName.VariableNameUnderscore
public function gc(int $max_lifetime): int|false {
// This should do something only if cron is not running properly...
if (!$stalelifetime = ini_get('session.gc_maxlifetime')) {
return false;
}
$params = ['purgebefore' => (time() - $stalelifetime)];
$count = $this->database->count_records_select('sessions', 'userid = 0 AND timemodified < :purgebefore', $params);
$this->database->delete_records_select('sessions', 'userid = 0 AND timemodified < :purgebefore', $params);
return $count;
}
}