fix: implement async database startup lock and refactor db configuration
This commit is contained in:
+73
-8
@@ -1,6 +1,9 @@
|
||||
import asyncio
|
||||
import glob
|
||||
import importlib
|
||||
import os
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import IO
|
||||
|
||||
from tortoise import Tortoise
|
||||
|
||||
@@ -8,18 +11,80 @@ from core.logger import logger
|
||||
from core.settings import data_root
|
||||
|
||||
|
||||
async def init_db():
|
||||
try:
|
||||
# 使用正确的Tortoise初始化配置格式
|
||||
db_config = {
|
||||
"db_url": f"sqlite://{data_root}/filecodebox.db",
|
||||
"modules": {"models": ["apps.base.models"]},
|
||||
_DB_FILE = os.path.join(data_root, "filecodebox.db")
|
||||
_STARTUP_LOCK_FILE = os.path.join(data_root, "filecodebox.startup.lock")
|
||||
|
||||
|
||||
def get_db_config() -> dict:
|
||||
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,
|
||||
"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表
|
||||
await Tortoise.get_connection("default").execute_script("""
|
||||
CREATE TABLE IF NOT EXISTS migrates (
|
||||
|
||||
@@ -17,10 +17,10 @@ from apps.admin.views import admin_api
|
||||
from apps.base.models import KeyValue
|
||||
from apps.base.utils import ip_limit
|
||||
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.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.utils import hash_password, is_password_hashed
|
||||
|
||||
@@ -31,7 +31,8 @@ async def lifespan(app: FastAPI):
|
||||
# 初始化数据库
|
||||
await init_db()
|
||||
|
||||
# 加载配置
|
||||
# 加载配置(多进程下串行化启动写操作)
|
||||
async with db_startup_lock():
|
||||
await load_config()
|
||||
app.mount(
|
||||
"/assets",
|
||||
@@ -97,15 +98,7 @@ app.add_middleware(
|
||||
# 使用 register_tortoise 来添加异常处理器
|
||||
register_tortoise(
|
||||
app,
|
||||
config={
|
||||
"connections": {"default": f"sqlite://{data_root}/filecodebox.db"},
|
||||
"apps": {
|
||||
"models": {
|
||||
"models": ["apps.base.models"],
|
||||
"default_connection": "default",
|
||||
},
|
||||
},
|
||||
},
|
||||
config=get_db_config(),
|
||||
generate_schemas=False,
|
||||
add_exception_handlers=True,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user