我正在使用一种模式在 Redis 中缓存 SQLAlchemy 对象。每当修改和提交实例时,我想清除相关的缓存,以便下次获取时重新加载它。这种清除必须在提交后进行,以避免竞争条件(另一个线程查询缓存、丢失并将过时的数据重新加载到缓存中)。
我已经与这个问题斗争了很长时间,提出了各种有时有效的解决方案,但没有什么万无一失的。这似乎是一个足够简单的问题,应该有一个解决方案。每次向 SQLAlchemy 实例提交更改时,如何触发一些代码?
我尝试将一些 SQLAlchemy 事件拼接在一起以实现我的目标,并取得了不同程度的成功。监听“after_insert”和“after_update”将告诉我何时修改对象,“after_commit”告诉我修改的内容已保存,因此我有一个方案,其中前两个事件将为“after_commit”注册侦听器,其中依次将对象传递给我的缓存清除函数。像这样:
def _register_after_commit(_: Mapper, __: Connection, target: MyClass) -> None:
""" Generic callback that adds this function for a target change without params """
targets.add(target) # Clear cache uses this set to know which instances to clear
event.listen(get_session(), "after_commit", clear_cache)
event.listen(MyClass, "after_insert", _register_after_commit)
event.listen(MyClass, "after_update", _register_after_commit)
Run Code Online (Sandbox Code Playgroud)
这在大多数情况下都有效,但DetachedInstanceError在访问目标上的属性时,我偶尔会得到我需要知道的属性以从缓存中清除它们(例如id)。我读到,发生这种情况是因为提交期间自动过期,这导致 SQLAlchemy 想要刷新所有属性。我无法关闭自动过期功能,也无法删除通过此处的每个对象,其中任何一个都可能最终破坏代码库的其他部分。
我创建了自己的会话类,如下所示:
class SessionWithCallback(scoped_session):
""" A version of orm.Session which can call …Run Code Online (Sandbox Code Playgroud)