mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-07-10 23:11:47 +08:00
Merge pull request #3074 from InfinityPacer/feature/db
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -12,7 +12,7 @@ app/helper/*.bin
|
|||||||
app/plugins/**
|
app/plugins/**
|
||||||
!app/plugins/__init__.py
|
!app/plugins/__init__.py
|
||||||
config/cookies/**
|
config/cookies/**
|
||||||
config/user.db
|
config/user.db*
|
||||||
config/sites/**
|
config/sites/**
|
||||||
config/logs/
|
config/logs/
|
||||||
config/temp/
|
config/temp/
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ class ConfigModel(BaseModel):
|
|||||||
DB_MAX_OVERFLOW: int = 500
|
DB_MAX_OVERFLOW: int = 500
|
||||||
# SQLite 的 busy_timeout 参数,默认为 60 秒
|
# SQLite 的 busy_timeout 参数,默认为 60 秒
|
||||||
DB_TIMEOUT: int = 60
|
DB_TIMEOUT: int = 60
|
||||||
|
# SQLite 是否启用 WAL 模式,默认关闭
|
||||||
|
DB_WAL_ENABLE: bool = False
|
||||||
# 配置文件目录
|
# 配置文件目录
|
||||||
CONFIG_DIR: Optional[str] = None
|
CONFIG_DIR: Optional[str] = None
|
||||||
# 超级管理员
|
# 超级管理员
|
||||||
|
|||||||
@@ -1,22 +1,25 @@
|
|||||||
from typing import Any, Generator, List, Optional, Self, Tuple
|
from typing import Any, Generator, List, Optional, Self, Tuple
|
||||||
|
|
||||||
from sqlalchemy import NullPool, QueuePool, and_, create_engine, inspect
|
from sqlalchemy import NullPool, QueuePool, and_, create_engine, inspect, text
|
||||||
from sqlalchemy.orm import Session, as_declarative, declared_attr, scoped_session, sessionmaker
|
from sqlalchemy.orm import Session, as_declarative, declared_attr, scoped_session, sessionmaker
|
||||||
|
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
|
|
||||||
# 根据池类型设置 poolclass 和相关参数
|
# 根据池类型设置 poolclass 和相关参数
|
||||||
pool_class = NullPool if settings.DB_POOL_TYPE == "NullPool" else QueuePool
|
pool_class = NullPool if settings.DB_POOL_TYPE == "NullPool" else QueuePool
|
||||||
|
connect_args = {
|
||||||
|
"timeout": settings.DB_TIMEOUT
|
||||||
|
}
|
||||||
|
# 启用 WAL 模式时的额外配置
|
||||||
|
if settings.DB_WAL_ENABLE:
|
||||||
|
connect_args["check_same_thread"] = False
|
||||||
kwargs = {
|
kwargs = {
|
||||||
"url": f"sqlite:///{settings.CONFIG_PATH}/user.db",
|
"url": f"sqlite:///{settings.CONFIG_PATH}/user.db",
|
||||||
"pool_pre_ping": settings.DB_POOL_PRE_PING,
|
"pool_pre_ping": settings.DB_POOL_PRE_PING,
|
||||||
"echo": settings.DB_ECHO,
|
"echo": settings.DB_ECHO,
|
||||||
"poolclass": pool_class,
|
"poolclass": pool_class,
|
||||||
"pool_recycle": settings.DB_POOL_RECYCLE,
|
"pool_recycle": settings.DB_POOL_RECYCLE,
|
||||||
"connect_args": {
|
"connect_args": connect_args
|
||||||
# "check_same_thread": False,
|
|
||||||
"timeout": settings.DB_TIMEOUT
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
# 当使用 QueuePool 时,添加 QueuePool 特有的参数
|
# 当使用 QueuePool 时,添加 QueuePool 特有的参数
|
||||||
if pool_class == QueuePool:
|
if pool_class == QueuePool:
|
||||||
@@ -27,6 +30,11 @@ if pool_class == QueuePool:
|
|||||||
})
|
})
|
||||||
# 创建数据库引擎
|
# 创建数据库引擎
|
||||||
Engine = create_engine(**kwargs)
|
Engine = create_engine(**kwargs)
|
||||||
|
# 根据配置设置日志模式
|
||||||
|
journal_mode = "WAL" if settings.DB_WAL_ENABLE else "DELETE"
|
||||||
|
with Engine.connect() as connection:
|
||||||
|
current_mode = connection.execute(text(f"PRAGMA journal_mode={journal_mode};")).scalar()
|
||||||
|
print(f"Database journal mode set to: {current_mode}")
|
||||||
|
|
||||||
# 会话工厂
|
# 会话工厂
|
||||||
SessionFactory = sessionmaker(bind=Engine)
|
SessionFactory = sessionmaker(bind=Engine)
|
||||||
@@ -49,11 +57,34 @@ def get_db() -> Generator:
|
|||||||
db.close()
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
|
def perform_checkpoint(mode: str = "PASSIVE"):
|
||||||
|
"""
|
||||||
|
执行 SQLite 的 checkpoint 操作,将 WAL 文件内容写回主数据库
|
||||||
|
:param mode: checkpoint 模式,可选值包括 "PASSIVE"、"FULL"、"RESTART"、"TRUNCATE"
|
||||||
|
默认为 "PASSIVE",即不锁定 WAL 文件的轻量级同步
|
||||||
|
"""
|
||||||
|
if not settings.DB_WAL_ENABLE:
|
||||||
|
return
|
||||||
|
valid_modes = {"PASSIVE", "FULL", "RESTART", "TRUNCATE"}
|
||||||
|
if mode.upper() not in valid_modes:
|
||||||
|
raise ValueError(f"Invalid checkpoint mode '{mode}'. Must be one of {valid_modes}")
|
||||||
|
try:
|
||||||
|
# 使用指定的 checkpoint 模式,确保 WAL 文件数据被正确写回主数据库
|
||||||
|
with Engine.connect() as conn:
|
||||||
|
conn.execute(text(f"PRAGMA wal_checkpoint({mode.upper()});"))
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error during WAL checkpoint: {e}")
|
||||||
|
|
||||||
|
|
||||||
def close_database():
|
def close_database():
|
||||||
"""
|
"""
|
||||||
关闭所有数据库连接
|
关闭所有数据库连接并清理资源
|
||||||
"""
|
"""
|
||||||
Engine.dispose()
|
try:
|
||||||
|
# 释放连接池,SQLite 会自动清空 WAL 文件,这里不单独再调用 checkpoint
|
||||||
|
Engine.dispose()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error while disposing database connections: {e}")
|
||||||
|
|
||||||
|
|
||||||
def get_args_db(args: tuple, kwargs: dict) -> Optional[Session]:
|
def get_args_db(args: tuple, kwargs: dict) -> Optional[Session]:
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ DB_POOL_SIZE=100
|
|||||||
DB_MAX_OVERFLOW=500
|
DB_MAX_OVERFLOW=500
|
||||||
# SQLite 的 busy_timeout 参数,可适当增加如180以减少锁定错误
|
# SQLite 的 busy_timeout 参数,可适当增加如180以减少锁定错误
|
||||||
DB_TIMEOUT=60
|
DB_TIMEOUT=60
|
||||||
|
# SQLite 是否启用 WAL 模式,启用可提升读写并发性能,但可能在异常情况下增加数据丢失的风险
|
||||||
|
DB_WAL_ENABLE=false
|
||||||
# 【*】超级管理员,设置后一但重启将固化到数据库中,修改将无效(初始化超级管理员密码仅会生成一次,请在日志中查看并自行登录系统修改)
|
# 【*】超级管理员,设置后一但重启将固化到数据库中,修改将无效(初始化超级管理员密码仅会生成一次,请在日志中查看并自行登录系统修改)
|
||||||
SUPERUSER=admin
|
SUPERUSER=admin
|
||||||
# 辅助认证,允许通过外部服务进行认证、单点登录以及自动创建用户
|
# 辅助认证,允许通过外部服务进行认证、单点登录以及自动创建用户
|
||||||
|
|||||||
Reference in New Issue
Block a user