在 Web 应用程序 (Pyramid) 中,我在 POST
上创建了某些对象,这些对象需要对其进行一些处理(主要是从 Web 上获取一些东西)。这些对象在 SQLAlchemy 的帮助下保存到 PostgreSQL 数据库中。由于这些任务可能需要一段时间,所以它不是在请求处理程序内部完成的,而是卸载到不同主机上的守护进程。创建对象时,我获取它的 ID
(这是客户端生成的 UUID
)并通过ZeroMQ 到守护进程。守护进程接收ID
,并从数据库中获取对象,运行并将结果写入数据库。
问题:
守护进程可以在创建事务提交之前收到ID
。由于我们使用的是 pyramid_tm
,所以当请求处理程序无错误返回时,所有数据库事务都会被提交,我宁愿保持这种状态。在我的开发系统上,所有东西都在同一个盒子上运行,所以 ZeroMQ 快如闪电。在生产系统上,这很可能不是问题,因为 Web 应用程序和守护进程在不同的主机上运行,但我不想指望这一点。
这个问题最近才出现,因为我们之前使用的 MongoDB 的 write_convern
为 2。只有两个数据库服务器,实体上的 write
总是阻止网络请求直到实体被持久化(这显然不是最好的主意)。
- 有没有人遇到过类似的问题?
- 您是如何解决的?
我看到了多种可能的解决方案,但其中大部分都不令我满意:
- 在触发 ZMQ 消息之前手动刷新事务。但是,我目前使用 SQLAlchemy
after_created
事件来触发它,这非常好,因为它完全分离了这个过程,从而消除了“忘记”告诉守护进程工作的风险。还认为我仍然需要在守护进程端使用READ UNCOMMITTED
隔离级别,对吗? - 向 ZMQ 消息添加时间戳,使接收消息的工作线程在处理对象之前等待。这显然限制了吞吐量。
- Dish ZMQ 完全简单地轮询数据库。不!
最佳答案
我只会使用 PostgreSQL 的 LISTEN
and NOTIFY
功能。 worker 可以连接到 SQL 服务器(它已经必须这样做),并发出适当的 LISTEN
。然后 PostgreSQL 会在相关事务完成时通知它。您在 SQL 服务器中触发生成通知甚至可能会在有效负载中发送整行,因此工作人员甚至不必请求任何东西:
CREATE OR REPLACE FUNCTION magic_notifier() RETURNS trigger AS $$
BEGIN
PERFORM pg_notify('stuffdone', row_to_json(new)::text);
RETURN new;
END;
$$ LANGUAGE plpgsql;
有了它,一旦知道有工作要做,它就会获得必要的信息,因此它可以开始工作而无需另一次往返。
关于python - ZeroMQ 对于数据库事务来说太快了,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22245407/