fix(database): 修复 detached 对象延迟加载导致的 greenlet 错误
问题: - CRUD 返回的对象在 session 关闭后变为 detached 状态 - 访问属性时 SQLAlchemy 尝试延迟加载,但没有 session - 导致: greenlet_spawn has not been called 根本原因: - SQLAlchemy 对象在 session 外被访问 - 延迟加载机制尝试在非异步上下文中执行异步操作 修复方案: 1. CRUDBase.get_by(): 在 session 内预加载所有列 2. CRUDBase.get_multi(): 在 session 内预加载所有实例的所有列 3. PersonInfo.get_value(): 添加异常处理,防御性编程 影响: - 所有通过 CRUD 获取的对象现在都完全加载 - 避免了 detached 对象的延迟加载问题 - 可能略微增加初始查询时间,但避免了运行时错误
This commit is contained in:
@@ -113,10 +113,19 @@ class CRUDBase:
|
|||||||
result = await session.execute(stmt)
|
result = await session.execute(stmt)
|
||||||
instance = result.scalar_one_or_none()
|
instance = result.scalar_one_or_none()
|
||||||
|
|
||||||
# 写入缓存
|
if instance is not None:
|
||||||
if instance is not None and use_cache:
|
# 触发所有列的加载,避免 detached 后的延迟加载问题
|
||||||
cache = await get_cache()
|
# 遍历所有列属性以确保它们被加载到内存中
|
||||||
await cache.set(cache_key, instance)
|
for column in self.model.__table__.columns:
|
||||||
|
try:
|
||||||
|
getattr(instance, column.name)
|
||||||
|
except Exception:
|
||||||
|
pass # 忽略访问错误
|
||||||
|
|
||||||
|
# 写入缓存
|
||||||
|
if use_cache:
|
||||||
|
cache = await get_cache()
|
||||||
|
await cache.set(cache_key, instance)
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
@@ -166,6 +175,14 @@ class CRUDBase:
|
|||||||
result = await session.execute(stmt)
|
result = await session.execute(stmt)
|
||||||
instances = result.scalars().all()
|
instances = result.scalars().all()
|
||||||
|
|
||||||
|
# 触发所有实例的列加载,避免 detached 后的延迟加载问题
|
||||||
|
for instance in instances:
|
||||||
|
for column in self.model.__table__.columns:
|
||||||
|
try:
|
||||||
|
getattr(instance, column.name)
|
||||||
|
except Exception:
|
||||||
|
pass # 忽略访问错误
|
||||||
|
|
||||||
# 写入缓存
|
# 写入缓存
|
||||||
if use_cache:
|
if use_cache:
|
||||||
cache = await get_cache()
|
cache = await get_cache()
|
||||||
|
|||||||
@@ -563,10 +563,6 @@ class PersonInfoManager:
|
|||||||
logger.debug("get_value获取失败:person_id不能为空")
|
logger.debug("get_value获取失败:person_id不能为空")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# 使用CRUD进行查询
|
|
||||||
crud = CRUDBase(PersonInfo)
|
|
||||||
record = await crud.get_by(person_id=person_id)
|
|
||||||
|
|
||||||
model_fields = [column.name for column in PersonInfo.__table__.columns]
|
model_fields = [column.name for column in PersonInfo.__table__.columns]
|
||||||
|
|
||||||
if field_name not in model_fields:
|
if field_name not in model_fields:
|
||||||
@@ -577,11 +573,21 @@ class PersonInfoManager:
|
|||||||
logger.debug(f"get_value查询失败:字段'{field_name}'未在SQLAlchemy模型和默认配置中定义。")
|
logger.debug(f"get_value查询失败:字段'{field_name}'未在SQLAlchemy模型和默认配置中定义。")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# 使用CRUD进行查询
|
||||||
|
crud = CRUDBase(PersonInfo)
|
||||||
|
record = await crud.get_by(person_id=person_id)
|
||||||
|
|
||||||
if record:
|
if record:
|
||||||
value = getattr(record, field_name)
|
# 在访问属性前确保对象已加载所有数据
|
||||||
if value is not None:
|
# 使用 try-except 捕获可能的延迟加载错误
|
||||||
return value
|
try:
|
||||||
else:
|
value = getattr(record, field_name)
|
||||||
|
if value is not None:
|
||||||
|
return value
|
||||||
|
else:
|
||||||
|
return copy.deepcopy(person_info_default.get(field_name))
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"访问字段 {field_name} 失败: {e}, 使用默认值")
|
||||||
return copy.deepcopy(person_info_default.get(field_name))
|
return copy.deepcopy(person_info_default.get(field_name))
|
||||||
else:
|
else:
|
||||||
return copy.deepcopy(person_info_default.get(field_name))
|
return copy.deepcopy(person_info_default.get(field_name))
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ class RelationshipFetcher:
|
|||||||
"user_aliases": relationship.user_aliases,
|
"user_aliases": relationship.user_aliases,
|
||||||
"relationship_text": relationship.relationship_text,
|
"relationship_text": relationship.relationship_text,
|
||||||
"preference_keywords": relationship.preference_keywords,
|
"preference_keywords": relationship.preference_keywords,
|
||||||
"relationship_score": relationship.affinity,
|
"relationship_score": relationship.relationship_score,
|
||||||
}
|
}
|
||||||
|
|
||||||
# 5.1 用户别名
|
# 5.1 用户别名
|
||||||
|
|||||||
Reference in New Issue
Block a user