修复 SystemConfig潜在的资源竞争问题

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
This commit is contained in:
景大侠
2026-01-13 14:33:53 +08:00
co-authored by gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
parent 0469ad46d6
commit f478958943
2 changed files with 100 additions and 41 deletions
+59 -41
View File
@@ -1,4 +1,6 @@
import asyncio
import copy import copy
import threading
from typing import Any, Optional, Union from typing import Any, Optional, Union
from app.db import DbOper from app.db import DbOper
@@ -17,6 +19,8 @@ class SystemConfigOper(DbOper, metaclass=Singleton):
""" """
super().__init__() super().__init__()
self.__SYSTEMCONF = {} self.__SYSTEMCONF = {}
self._rlock = threading.RLock()
self._alock = asyncio.Lock()
for item in SystemConfig.list(self._db): for item in SystemConfig.list(self._db):
self.__SYSTEMCONF[item.key] = item.value self.__SYSTEMCONF[item.key] = item.value
@@ -29,23 +33,24 @@ class SystemConfigOper(DbOper, metaclass=Singleton):
""" """
if isinstance(key, SystemConfigKey): if isinstance(key, SystemConfigKey):
key = key.value key = key.value
# 旧值 with self._rlock:
old_value = self.__SYSTEMCONF.get(key) # 旧值
# 更新内存(deepcopy避免内存共享) old_value = self.__SYSTEMCONF.get(key)
self.__SYSTEMCONF[key] = copy.deepcopy(value) # 更新内存(deepcopy避免内存共享)
conf = SystemConfig.get_by_key(self._db, key) self.__SYSTEMCONF[key] = copy.deepcopy(value)
if conf: conf = SystemConfig.get_by_key(self._db, key)
if old_value != value: if conf:
if value: if old_value != value:
conf.update(self._db, {"value": value}) if value:
else: conf.update(self._db, {"value": value})
conf.delete(self._db, conf.id) else:
conf.delete(self._db, conf.id)
return True
return None
else:
conf = SystemConfig(key=key, value=value)
conf.create(self._db)
return True return True
return None
else:
conf = SystemConfig(key=key, value=value)
conf.create(self._db)
return True
async def async_set(self, key: Union[str, SystemConfigKey], value: Any) -> Optional[bool]: async def async_set(self, key: Union[str, SystemConfigKey], value: Any) -> Optional[bool]:
""" """
@@ -56,22 +61,32 @@ class SystemConfigOper(DbOper, metaclass=Singleton):
""" """
if isinstance(key, SystemConfigKey): if isinstance(key, SystemConfigKey):
key = key.value key = key.value
# 旧值 async with self._alock:
old_value = self.__SYSTEMCONF.get(key) conf = await SystemConfig.async_get_by_key(self._db, key)
# 更新内存(deepcopy避免内存共享) # 确定是否需要更新数据库
self.__SYSTEMCONF[key] = copy.deepcopy(value) needs_db_update = False
conf = await SystemConfig.async_get_by_key(self._db, key) if conf:
if conf: if conf.value != value:
if old_value != value: needs_db_update = True
else: # 记录不存在,总是需要创建/更新
needs_db_update = True
if not needs_db_update:
# 即使数据库值相同,也要确保缓存同步
with self._rlock:
self.__SYSTEMCONF[key] = copy.deepcopy(value)
return None
# 执行数据库更新
if conf:
if value: if value:
conf.update(self._db, {"value": value}) await conf.async_update(self._db, {"value": value})
else: else:
conf.delete(self._db, conf.id) await conf.async_delete(self._db, conf.id)
return True else:
return None conf = SystemConfig(key=key, value=value)
else: await conf.async_create(self._db)
conf = SystemConfig(key=key, value=value) # 数据库更新成功后,再更新缓存
await conf.async_create(self._db) with self._rlock:
self.__SYSTEMCONF[key] = copy.deepcopy(value)
return True return True
def get(self, key: Union[str, SystemConfigKey] = None) -> Any: def get(self, key: Union[str, SystemConfigKey] = None) -> Any:
@@ -82,15 +97,17 @@ class SystemConfigOper(DbOper, metaclass=Singleton):
key = key.value key = key.value
if not key: if not key:
return self.all() return self.all()
# 避免将__SYSTEMCONF内的值引用出去,会导致set时误判没有变动 with self._rlock:
return copy.deepcopy(self.__SYSTEMCONF.get(key)) # 避免将__SYSTEMCONF内的值引用出去,会导致set时误判没有变动
return copy.deepcopy(self.__SYSTEMCONF.get(key))
def all(self): def all(self):
""" """
获取所有系统设置 获取所有系统设置
""" """
# 避免将__SYSTEMCONF内的值引用出去,会导致set时误判没有变动 with self._rlock:
return copy.deepcopy(self.__SYSTEMCONF) # 避免将__SYSTEMCONF内的值引用出去,会导致set时误判没有变动
return copy.deepcopy(self.__SYSTEMCONF)
def delete(self, key: Union[str, SystemConfigKey]) -> bool: def delete(self, key: Union[str, SystemConfigKey]) -> bool:
""" """
@@ -98,10 +115,11 @@ class SystemConfigOper(DbOper, metaclass=Singleton):
""" """
if isinstance(key, SystemConfigKey): if isinstance(key, SystemConfigKey):
key = key.value key = key.value
# 更新内存 with self._rlock:
self.__SYSTEMCONF.pop(key, None) # 更新内存
# 写入数据库 self.__SYSTEMCONF.pop(key, None)
conf = SystemConfig.get_by_key(self._db, key) # 写入数据库
if conf: conf = SystemConfig.get_by_key(self._db, key)
conf.delete(self._db, conf.id) if conf:
return True conf.delete(self._db, conf.id)
return True
+41
View File
@@ -0,0 +1,41 @@
"""2.2.2
Revision ID: 41ef1dd7467c
Revises: a946dae52526
Create Date: 2026-01-13 13:02:41.614029
"""
from app.db import ScopedSession
from app.db.models.systemconfig import SystemConfig
from app.log import logger
# revision identifiers, used by Alembic.
revision = "41ef1dd7467c"
down_revision = "a946dae52526"
branch_labels = None
depends_on = None
def upgrade() -> None:
# systemconfig表 去重
with ScopedSession() as db:
try:
seen_keys = set()
# 按ID降序查询,以便保留最新的配置
for item in db.query(SystemConfig).order_by(SystemConfig.id.desc()).all():
if item.key in seen_keys:
logger.warn(
f"已删除重复的SystemConfig项:{item.key} 值:{item.value}"
)
db.delete(item)
else:
seen_keys.add(item.key)
db.commit()
except Exception as e:
logger.error(e)
db.rollback()
def downgrade() -> None:
pass