litefs.session.db 源代码

#!/usr/bin/env python
# coding: utf-8

"""
Database Session 后端

提供基于 SQLite 数据库的 Session 实现
"""

import sqlite3
import time
import json
from typing import Optional

try:
    from sqlalchemy import create_engine, text
    from sqlalchemy.pool import QueuePool, StaticPool
    SQLALCHEMY_AVAILABLE = True
except ImportError:
    SQLALCHEMY_AVAILABLE = False

from .base import SessionStoreBase
from .session import Session


[文档] class DatabaseSession(SessionStoreBase): """ 数据库 Session 实现 使用 SQLite 数据库作为 Session 存储,支持持久化存储 """ def __init__( self, db_path: str = ":memory:", table_name: str = "sessions", expiration_time: int = 3600, pool_size: int = 5, max_overflow: int = 10, pool_timeout: int = 30, pool_recycle: int = 3600, use_pool: bool = True, **kwargs ): """ 初始化 Database Session Args: db_path: 数据库文件路径,默认为内存数据库 table_name: Session 表名 expiration_time: 默认过期时间(秒) pool_size: 连接池大小(默认5) max_overflow: 连接池最大溢出数(默认10) pool_timeout: 连接池超时时间(秒,默认30) pool_recycle: 连接回收时间(秒,默认3600) use_pool: 是否使用连接池(默认True) **kwargs: 其他配置参数 """ self._db_path = db_path self._table_name = table_name self._expiration_time = expiration_time self._pool_size = pool_size self._max_overflow = max_overflow self._pool_timeout = pool_timeout self._pool_recycle = pool_recycle self._use_pool = use_pool and SQLALCHEMY_AVAILABLE self._engine = None self._conn = None if self._use_pool: self._initialize_pool() else: self._create_table() def _initialize_pool(self): """初始化SQLAlchemy连接池""" if not SQLALCHEMY_AVAILABLE: raise ImportError("SQLAlchemy未安装,无法使用连接池功能") # 对于内存数据库,使用StaticPool if self._db_path == ":memory:": self._engine = create_engine( "sqlite:///:memory:", connect_args={"check_same_thread": False}, poolclass=StaticPool, echo=False ) else: # 对于文件数据库,使用QueuePool self._engine = create_engine( f"sqlite:///{self._db_path}", poolclass=QueuePool, pool_size=self._pool_size, max_overflow=self._max_overflow, pool_timeout=self._pool_timeout, pool_recycle=self._pool_recycle, connect_args={"check_same_thread": False}, echo=False ) # 创建表 with self._engine.connect() as conn: conn.execute(text(f""" CREATE TABLE IF NOT EXISTS {self._table_name} ( session_id TEXT PRIMARY KEY, data TEXT NOT NULL, created_at INTEGER NOT NULL, expires_at INTEGER NOT NULL ) """)) conn.commit() def _connect(self): """连接数据库""" if self._use_pool: # 使用连接池时,返回引擎 return self._engine else: # 不使用连接池时,使用传统的sqlite3连接 if self._conn is None: self._conn = sqlite3.connect(self._db_path) self._conn.row_factory = sqlite3.Row return self._conn def _create_table(self): """创建 Session 表""" conn = self._connect() cursor = conn.cursor() # 只在表不存在时创建表 cursor.execute(f""" CREATE TABLE IF NOT EXISTS {self._table_name} ( session_id TEXT PRIMARY KEY, data TEXT NOT NULL, created_at INTEGER NOT NULL, expires_at INTEGER NOT NULL ) """) conn.commit() def _make_key(self, key: str) -> str: """生成键""" return key
[文档] def put(self, session_id: str, session: Session) -> None: """ 存储 Session Args: session_id: Session ID session: Session 对象 """ # 序列化 Session 数据 try: data = json.dumps(dict(session)) except Exception as e: raise ValueError(f"无法序列化 Session 数据: {e}") created_at = int(time.time()) expires_at = created_at + self._expiration_time if self._use_pool: # 使用连接池 with self._engine.connect() as conn: conn.execute(text(f""" INSERT OR REPLACE INTO {self._table_name} (session_id, data, created_at, expires_at) VALUES (:session_id, :data, :created_at, :expires_at) """), { 'session_id': session_id, 'data': data, 'created_at': created_at, 'expires_at': expires_at }) conn.commit() else: # 不使用连接池 conn = self._connect() cursor = conn.cursor() cursor.execute(f""" INSERT OR REPLACE INTO {self._table_name} (session_id, data, created_at, expires_at) VALUES (?, ?, ?, ?) """, (session_id, data, created_at, expires_at)) conn.commit()
[文档] def get(self, session_id: str) -> Optional[Session]: """ 获取 Session Args: session_id: Session ID Returns: Session 对象,如果不存在或已过期则返回 None """ if self._use_pool: # 使用连接池 with self._engine.connect() as conn: result = conn.execute(text(f""" SELECT data, expires_at FROM {self._table_name} WHERE session_id = :session_id """), {'session_id': session_id}) row = result.fetchone() else: # 不使用连接池 conn = self._connect() cursor = conn.cursor() cursor.execute(f""" SELECT data, expires_at FROM {self._table_name} WHERE session_id = ? """, (session_id,)) row = cursor.fetchone() if row is None: return None # 检查是否过期 if self._use_pool: expires_at = row[1] else: expires_at = row['expires_at'] if int(time.time()) > expires_at: # 删除过期 Session self.delete(session_id) return None # 反序列化 Session 数据 try: if self._use_pool: data = json.loads(row[0]) else: data = json.loads(row['data']) except Exception: # 如果反序列化失败,删除损坏的 Session self.delete(session_id) return None # 创建 Session 对象 session = Session(session_id) session.update(data) return session
[文档] def delete(self, session_id: str) -> None: """ 删除 Session Args: session_id: Session ID """ if self._use_pool: # 使用连接池 with self._engine.connect() as conn: conn.execute(text(f""" DELETE FROM {self._table_name} WHERE session_id = :session_id """), {'session_id': session_id}) conn.commit() else: # 不使用连接池 conn = self._connect() cursor = conn.cursor() cursor.execute(f""" DELETE FROM {self._table_name} WHERE session_id = ? """, (session_id,)) conn.commit()
[文档] def exists(self, session_id: str) -> bool: """ 检查 Session 是否存在 Args: session_id: Session ID Returns: Session 是否存在且未过期 """ if self._use_pool: # 使用连接池 with self._engine.connect() as conn: result = conn.execute(text(f""" SELECT expires_at FROM {self._table_name} WHERE session_id = :session_id """), {'session_id': session_id}) row = result.fetchone() else: # 不使用连接池 conn = self._connect() cursor = conn.cursor() cursor.execute(f""" SELECT expires_at FROM {self._table_name} WHERE session_id = ? """, (session_id,)) row = cursor.fetchone() if row is None: return False # 检查是否过期 if self._use_pool: expires_at = row[0] else: expires_at = row['expires_at'] return int(time.time()) <= expires_at
[文档] def expire(self, session_id: str, expiration: int) -> bool: """ 设置 Session 过期时间 Args: session_id: Session ID expiration: 过期时间(秒) Returns: 是否设置成功 """ # 检查 Session 是否存在 if not self.exists(session_id): return False # 更新过期时间 expires_at = int(time.time()) + expiration if self._use_pool: # 使用连接池 with self._engine.connect() as conn: conn.execute(text(f""" UPDATE {self._table_name} SET expires_at = :expires_at WHERE session_id = :session_id """), { 'expires_at': expires_at, 'session_id': session_id }) conn.commit() else: # 不使用连接池 conn = self._connect() cursor = conn.cursor() cursor.execute(f""" UPDATE {self._table_name} SET expires_at = ? WHERE session_id = ? """, (expires_at, session_id)) conn.commit() return True
[文档] def ttl(self, session_id: str) -> int: """ 获取 Session 剩余过期时间 Args: session_id: Session ID Returns: 剩余过期时间(秒),如果 Session 不存在则返回 -2,已过期则返回 -1 """ if self._use_pool: # 使用连接池 with self._engine.connect() as conn: result = conn.execute(text(f""" SELECT expires_at FROM {self._table_name} WHERE session_id = :session_id """), {'session_id': session_id}) row = result.fetchone() else: # 不使用连接池 conn = self._connect() cursor = conn.cursor() cursor.execute(f""" SELECT expires_at FROM {self._table_name} WHERE session_id = ? """, (session_id,)) row = cursor.fetchone() if row is None: return -2 # 计算剩余过期时间 if self._use_pool: expires_at = row[0] else: expires_at = row['expires_at'] current_time = int(time.time()) ttl = expires_at - current_time if ttl <= 0: # 删除过期 Session self.delete(session_id) return -1 return ttl
[文档] def clear(self) -> None: """ 清空所有 Session """ if self._use_pool: # 使用连接池 with self._engine.connect() as conn: conn.execute(text(f"DELETE FROM {self._table_name}")) conn.commit() else: # 不使用连接池 conn = self._connect() cursor = conn.cursor() cursor.execute(f"DELETE FROM {self._table_name}") conn.commit()
def __len__(self) -> int: """ 获取 Session 数量 Returns: Session 数量 """ if self._use_pool: # 使用连接池 with self._engine.connect() as conn: result = conn.execute(text(f"SELECT COUNT(*) FROM {self._table_name}")) return result.fetchone()[0] else: # 不使用连接池 conn = self._connect() cursor = conn.cursor() cursor.execute(f"SELECT COUNT(*) FROM {self._table_name}") return cursor.fetchone()[0]
[文档] def close(self) -> None: """ 关闭数据库连接 """ if self._use_pool: # 关闭连接池 if self._engine is not None: try: self._engine.dispose() except Exception: pass self._engine = None else: # 关闭传统连接 if self._conn is not None: try: self._conn.close() except Exception: pass self._conn = None
def __enter__(self): """支持上下文管理器""" return self def __exit__(self, exc_type, exc_val, exc_tb): """支持上下文管理器""" self.close()
[文档] def create(self) -> Session: """ 创建新的 Session 对象 Returns: Session 对象 """ session = Session(store=self) return session
[文档] def save(self, session: Session) -> None: """ 保存 Session 数据 Args: session: Session 对象 """ self.put(session.id, session)
__all__ = [ 'DatabaseSession', ]