perf: Add TTL-based caching to UserModel to prevent stale data
Cache optimization with automatic expiration: 1. New Cache Structure: - Changed from simple array to TTL-aware structure - Each entry: ['data' => ..., 'expires' => timestamp] - 5-minute (300s) TTL prevents indefinite stale data 2. Helper Methods: - getCached($key): Returns data if not expired, null otherwise - setCached($key, $data): Stores with expiration timestamp - invalidateCache($userId, $username): Manual cache clearing 3. Updated All Cache Access Points: - syncUserFromAuthelia() - User sync from Authelia - getSystemUser() - System user for daemon operations - getUserById() - User lookup by ID - getUserByUsername() - User lookup by username Benefits: - Prevents memory leaks from unlimited cache growth - Ensures user data refreshes periodically - Maintains performance benefits of caching - Automatic cleanup of expired entries Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,12 +4,50 @@
|
|||||||
*/
|
*/
|
||||||
class UserModel {
|
class UserModel {
|
||||||
private $conn;
|
private $conn;
|
||||||
private static $userCache = [];
|
private static $userCache = []; // ['key' => ['data' => ..., 'expires' => timestamp]]
|
||||||
|
private static $cacheTTL = 300; // 5 minutes
|
||||||
|
|
||||||
public function __construct($conn) {
|
public function __construct($conn) {
|
||||||
$this->conn = $conn;
|
$this->conn = $conn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cached user data if not expired
|
||||||
|
*/
|
||||||
|
private static function getCached($key) {
|
||||||
|
if (isset(self::$userCache[$key])) {
|
||||||
|
$cached = self::$userCache[$key];
|
||||||
|
if ($cached['expires'] > time()) {
|
||||||
|
return $cached['data'];
|
||||||
|
}
|
||||||
|
// Expired - remove from cache
|
||||||
|
unset(self::$userCache[$key]);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store user data in cache with expiration
|
||||||
|
*/
|
||||||
|
private static function setCached($key, $data) {
|
||||||
|
self::$userCache[$key] = [
|
||||||
|
'data' => $data,
|
||||||
|
'expires' => time() + self::$cacheTTL
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate specific user cache entry
|
||||||
|
*/
|
||||||
|
public static function invalidateCache($userId = null, $username = null) {
|
||||||
|
if ($userId !== null) {
|
||||||
|
unset(self::$userCache["user_id_$userId"]);
|
||||||
|
}
|
||||||
|
if ($username !== null) {
|
||||||
|
unset(self::$userCache["user_$username"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sync user from Authelia headers (create or update)
|
* Sync user from Authelia headers (create or update)
|
||||||
*
|
*
|
||||||
@@ -22,8 +60,9 @@ class UserModel {
|
|||||||
public function syncUserFromAuthelia($username, $displayName = '', $email = '', $groups = '') {
|
public function syncUserFromAuthelia($username, $displayName = '', $email = '', $groups = '') {
|
||||||
// Check cache first
|
// Check cache first
|
||||||
$cacheKey = "user_$username";
|
$cacheKey = "user_$username";
|
||||||
if (isset(self::$userCache[$cacheKey])) {
|
$cached = self::getCached($cacheKey);
|
||||||
return self::$userCache[$cacheKey];
|
if ($cached !== null) {
|
||||||
|
return $cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine if user is admin based on groups
|
// Determine if user is admin based on groups
|
||||||
@@ -72,8 +111,8 @@ class UserModel {
|
|||||||
|
|
||||||
$stmt->close();
|
$stmt->close();
|
||||||
|
|
||||||
// Cache user
|
// Cache user with TTL
|
||||||
self::$userCache[$cacheKey] = $user;
|
self::setCached($cacheKey, $user);
|
||||||
|
|
||||||
return $user;
|
return $user;
|
||||||
}
|
}
|
||||||
@@ -85,8 +124,9 @@ class UserModel {
|
|||||||
*/
|
*/
|
||||||
public function getSystemUser() {
|
public function getSystemUser() {
|
||||||
// Check cache first
|
// Check cache first
|
||||||
if (isset(self::$userCache['system'])) {
|
$cached = self::getCached('system');
|
||||||
return self::$userCache['system'];
|
if ($cached !== null) {
|
||||||
|
return $cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
$stmt = $this->conn->prepare("SELECT * FROM users WHERE username = 'system'");
|
$stmt = $this->conn->prepare("SELECT * FROM users WHERE username = 'system'");
|
||||||
@@ -95,7 +135,7 @@ class UserModel {
|
|||||||
|
|
||||||
if ($result->num_rows > 0) {
|
if ($result->num_rows > 0) {
|
||||||
$user = $result->fetch_assoc();
|
$user = $result->fetch_assoc();
|
||||||
self::$userCache['system'] = $user;
|
self::setCached('system', $user);
|
||||||
$stmt->close();
|
$stmt->close();
|
||||||
return $user;
|
return $user;
|
||||||
}
|
}
|
||||||
@@ -113,8 +153,9 @@ class UserModel {
|
|||||||
public function getUserById($userId) {
|
public function getUserById($userId) {
|
||||||
// Check cache first
|
// Check cache first
|
||||||
$cacheKey = "user_id_$userId";
|
$cacheKey = "user_id_$userId";
|
||||||
if (isset(self::$userCache[$cacheKey])) {
|
$cached = self::getCached($cacheKey);
|
||||||
return self::$userCache[$cacheKey];
|
if ($cached !== null) {
|
||||||
|
return $cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
$stmt = $this->conn->prepare("SELECT * FROM users WHERE user_id = ?");
|
$stmt = $this->conn->prepare("SELECT * FROM users WHERE user_id = ?");
|
||||||
@@ -124,7 +165,7 @@ class UserModel {
|
|||||||
|
|
||||||
if ($result->num_rows > 0) {
|
if ($result->num_rows > 0) {
|
||||||
$user = $result->fetch_assoc();
|
$user = $result->fetch_assoc();
|
||||||
self::$userCache[$cacheKey] = $user;
|
self::setCached($cacheKey, $user);
|
||||||
$stmt->close();
|
$stmt->close();
|
||||||
return $user;
|
return $user;
|
||||||
}
|
}
|
||||||
@@ -142,8 +183,9 @@ class UserModel {
|
|||||||
public function getUserByUsername($username) {
|
public function getUserByUsername($username) {
|
||||||
// Check cache first
|
// Check cache first
|
||||||
$cacheKey = "user_$username";
|
$cacheKey = "user_$username";
|
||||||
if (isset(self::$userCache[$cacheKey])) {
|
$cached = self::getCached($cacheKey);
|
||||||
return self::$userCache[$cacheKey];
|
if ($cached !== null) {
|
||||||
|
return $cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
$stmt = $this->conn->prepare("SELECT * FROM users WHERE username = ?");
|
$stmt = $this->conn->prepare("SELECT * FROM users WHERE username = ?");
|
||||||
@@ -153,7 +195,7 @@ class UserModel {
|
|||||||
|
|
||||||
if ($result->num_rows > 0) {
|
if ($result->num_rows > 0) {
|
||||||
$user = $result->fetch_assoc();
|
$user = $result->fetch_assoc();
|
||||||
self::$userCache[$cacheKey] = $user;
|
self::setCached($cacheKey, $user);
|
||||||
$stmt->close();
|
$stmt->close();
|
||||||
return $user;
|
return $user;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user