fix: implement async database startup lock and refactor db configuration

This commit is contained in:
Lan
2026-02-13 23:30:48 +08:00
parent 4a9bf3a3a0
commit 843a237fad
2 changed files with 89 additions and 31 deletions
+73 -8
View File
@@ -1,6 +1,9 @@
import asyncio
import glob import glob
import importlib import importlib
import os import os
from contextlib import asynccontextmanager
from typing import IO
from tortoise import Tortoise from tortoise import Tortoise
@@ -8,18 +11,80 @@ from core.logger import logger
from core.settings import data_root from core.settings import data_root
async def init_db(): _DB_FILE = os.path.join(data_root, "filecodebox.db")
try: _STARTUP_LOCK_FILE = os.path.join(data_root, "filecodebox.startup.lock")
# 使用正确的Tortoise初始化配置格式
db_config = {
"db_url": f"sqlite://{data_root}/filecodebox.db", def get_db_config() -> dict:
"modules": {"models": ["apps.base.models"]}, return {
"connections": {
"default": {
"engine": "tortoise.backends.sqlite",
"credentials": {
"file_path": _DB_FILE,
"journal_mode": "WAL",
"busy_timeout": 10000,
"foreign_keys": "ON",
},
}
},
"apps": {
"models": {
"models": ["apps.base.models"],
"default_connection": "default",
}
},
"use_tz": False, "use_tz": False,
"timezone": "Asia/Shanghai" "timezone": "Asia/Shanghai",
} }
await Tortoise.init(**db_config)
def _lock_file(file_obj: IO[str]) -> None:
if os.name == "nt":
import msvcrt
# Windows 需要锁定至少 1 字节
if os.fstat(file_obj.fileno()).st_size == 0:
file_obj.write("0")
file_obj.flush()
msvcrt.locking(file_obj.fileno(), msvcrt.LK_LOCK, 1)
else:
import fcntl
fcntl.flock(file_obj.fileno(), fcntl.LOCK_EX)
def _unlock_file(file_obj: IO[str]) -> None:
if os.name == "nt":
import msvcrt
msvcrt.locking(file_obj.fileno(), msvcrt.LK_UNLCK, 1)
else:
import fcntl
fcntl.flock(file_obj.fileno(), fcntl.LOCK_UN)
@asynccontextmanager
async def db_startup_lock():
os.makedirs(data_root, exist_ok=True)
lock_file = open(_STARTUP_LOCK_FILE, "a+", encoding="utf-8")
try:
await asyncio.to_thread(_lock_file, lock_file)
yield
finally:
await asyncio.to_thread(_unlock_file, lock_file)
lock_file.close()
async def init_db():
try:
db_config = get_db_config()
if not Tortoise._inited:
await Tortoise.init(config=db_config)
async with db_startup_lock():
# 创建migrations表 # 创建migrations表
await Tortoise.get_connection("default").execute_script(""" await Tortoise.get_connection("default").execute_script("""
CREATE TABLE IF NOT EXISTS migrates ( CREATE TABLE IF NOT EXISTS migrates (
+5 -12
View File
@@ -17,10 +17,10 @@ from apps.admin.views import admin_api
from apps.base.models import KeyValue from apps.base.models import KeyValue
from apps.base.utils import ip_limit from apps.base.utils import ip_limit
from apps.base.views import share_api, chunk_api, presign_api from apps.base.views import share_api, chunk_api, presign_api
from core.database import init_db from core.database import db_startup_lock, get_db_config, init_db
from core.logger import logger from core.logger import logger
from core.response import APIResponse from core.response import APIResponse
from core.settings import data_root, settings, BASE_DIR, DEFAULT_CONFIG from core.settings import settings, BASE_DIR, DEFAULT_CONFIG
from core.tasks import delete_expire_files, clean_incomplete_uploads from core.tasks import delete_expire_files, clean_incomplete_uploads
from core.utils import hash_password, is_password_hashed from core.utils import hash_password, is_password_hashed
@@ -31,7 +31,8 @@ async def lifespan(app: FastAPI):
# 初始化数据库 # 初始化数据库
await init_db() await init_db()
# 加载配置 # 加载配置(多进程下串行化启动写操作)
async with db_startup_lock():
await load_config() await load_config()
app.mount( app.mount(
"/assets", "/assets",
@@ -97,15 +98,7 @@ app.add_middleware(
# 使用 register_tortoise 来添加异常处理器 # 使用 register_tortoise 来添加异常处理器
register_tortoise( register_tortoise(
app, app,
config={ config=get_db_config(),
"connections": {"default": f"sqlite://{data_root}/filecodebox.db"},
"apps": {
"models": {
"models": ["apps.base.models"],
"default_connection": "default",
},
},
},
generate_schemas=False, generate_schemas=False,
add_exception_handlers=True, add_exception_handlers=True,
) )