python - sqlalchemy 什么时候对象变得“不持久”
问题描述
我有一个函数,它有一个半长时间运行的会话,我将它用于一堆数据库行......并且在某个时候我想重新加载或“刷新”其中一个行以确保没有任何状态发生变化. 大多数情况下这段代码工作正常,但时不时地我得到这个错误
sqlalchemy.exc.InvalidRequestError: Instance '<Event at 0x58cb790>' is not persistent within this Session
我一直在阅读状态,但不明白为什么一个对象会停止持久化?我还在一个会话中,所以我不确定为什么我会停止坚持。
有人可以解释什么可能导致我的对象在会话中“不持久”吗?在此之前,我没有对对象进行任何写入。
下面的db_event是变得“不持久”的对象
async def event_white_check_mark_handler(
self: Events, ctx, channel: TextChannel, member: discord.Member, message: Message
):
"""
This reaction is for completing an event
"""
session = database_objects.SESSION()
try:
message_id = message.id
db_event = self.get_event(session, message_id)
if not db_event:
return
logger.debug(f"{member.display_name} wants to complete an event {db_event.id}")
db_guild = await db.get_or_create(
session, db.Guild, name=channel.guild.name, discord_id=channel.guild.id
)
db_member = await db.get_or_create(
session,
db.Member,
name=member.name,
discord_id=member.id,
nick=member.display_name,
guild_id=db_guild.discord_id,
)
db_scheduler_config: db.SchedulerConfig = (
session.query(db.SchedulerConfig)
.filter(db.SchedulerConfig.guild_id == channel.guild.id)
.one()
)
# reasons to not complete the event
if len(db_event) == 0:
await channel.send(
f"{member.display_name} you cannot complete an event with no one on it!"
)
elif (
db_member.discord_id == db_event.creator_id
or await db_scheduler_config.check_permission(
ctx, db_event.event_name, member, db_scheduler_config.MODIFY
)
):
async with self.EVENT_LOCKS[db_event.id]:
session.refresh(db_event) ########### <---- right here is when I get the error thrown
db_event.status = const.COMPLETED
session.commit()
self.DIRTY_EVENTS.add(db_event.id)
member_list = ",".join(
filter(
lambda x: x not in const.MEMBER_FIELD_DEFAULT,
[str(x.mention) for x in db_event.members],
)
)
await channel.send(f"Congrats on completing a event {member_list}!")
logger.info(f"Congrats on completing a event {member_list}!")
# await self.stop_tracking_event(db_event)
del self.REMINDERS_BY_EVENT_ID[db_event.id]
else:
await channel.send(
f"{member.display_name} you did not create this event and do not have permission to delete the event!"
)
logger.warning(f"{member.display_name} you did not create this event!")
except Exception as _e:
logger.error(format_exc())
session.rollback()
finally:
database_objects.SESSION.remove()
解决方案
我相当肯定,在这种情况下,根本原因是竞争条件。在其默认配置中使用作用域会话仅基于线程管理作用域。在顶部使用协程可能意味着 2 个或更多最终共享同一个会话,如果event_white_check_mark_handler
他们随后竞相提交/回滚并从范围会话注册表中删除会话,则有效地关闭它并从现在清除所有剩余实例-defunct session,让其他协程不开心。
一个解决方案是根本不使用范围会话event_white_check_mark_handler
,因为它完全管理其会话的生命周期,并且似乎将会话作为参数传递。另一方面,如果有一些路径使用作用域会话database_objects.SESSION
而不是接收会话作为参数,scopefunc
则在创建注册表时定义一个合适的:
推荐阅读
- bootstrap-4 - 在 BS4 中对齐并排嵌套的 div
- java - 集成测试:java.lang.IllegalStateException:无法解开代理对象
- docker - 从家庭 TIBCO EMS 服务器切换到辅助 TIBCO EMS 服务器后,WSO2 ESB 无法重新连接到 TIBCO JMS 服务器
- javascript - Socket.io 无法连接到 Android 浏览器上的 localhost
- php - Swift - PHP:发送到 PHP 的文本字段值被接收为 null
- python - 我们可以通过理解来做到这一点吗?
- kubernetes - 需要使用带有凭证的驱动程序名称创建 K8 环境
- javascript - 正确使用 appendChild 避免 XSS
- python-3.x - 有没有一种基于规则的 spacy 匹配方法来匹配模式?
- excel - 有没有办法使用 Excel 中的 VBA 复制 Word 页面?