触发器(包括所有延迟触发器)在事务内部触发。
但这不是问题所在,因为通知无论如何都是在事务之间传递的。
关于NOTIFY
的手册:
《通知(NOTIFY)》与SQL事务有一些重要的交互方式。首先,如果在事务内部执行了一个《通知(NOTIFY)》,则除非提交该事务,否则不会传递通知事件。这是合适的,因为如果事务被中止,则其中所有命令均无效,包括《通知(NOTIFY)》。但是,如果期望立即传递通知事件,这可能会让人不安。其次,如果正在监听某个会话,并在事务内收到通知信号,则直到完成该事务(提交或中止)后才将通知事件传递给连接的客户端。同样,推理是,如果在稍后中止的事务中传递了通知,则希望以某种方式撤消通知 - 但是一旦服务器将其发送到客户端,就无法“收回”通知。 因此,《通知(NOTIFY)》事件只在事务之间传递。由此产生的结果是,使用《通知(NOTIFY)》进行实时信令的应用程序应尝试保持事务短小。
pg_notify()
是 SQL 的 NOTIFY
命令的方便包装函数。
如果在接收通知后找不到某些行,则必须有不同的原因!去找出它。可能的候选者:
- 并发事务干扰
- 触发器执行更多或与您认为的不同的操作。
- 各种编程错误。
无论如何,像手册建议的那样,保持发送通知的事务短暂。
dblink
更新: 在 Postgres 11 或更高版本中,PROCEDURE
或 DO
语句中的事务控制使此过程变得更加简单。只需进行 COMMIT;
就可以(也)发送等待的通知。
原始回答(主要适用于Postgres 10或更早版本):
PERFORM * FROM dblink('hq','SELECT pg_notify(''' || channel || ''', ''' || payload || ''')');
应该使用
format()
对其进行重写,以简化并使语法更安全。
PRERFORM dblink('hq', format('NOTIFY %I, %L', channel, payload));
"dblink"在这里是一个革命性的工具,因为它在另一个数据库中打开了一个单独的事务。有时会用于伪造“自主事务”。
dblink()
等待远程命令完成。所以远程事务很可能会先提交。
手册:
该函数返回查询产生的行。
如果您可以从
同一事务发送通知,那将是一个
干净的解决方案。
dblink
的解决方法
如果通知必须从不同的事务中发送,则可以使用
dblink_send_query()
进行解决:
dblink_send_query
将查询异步地发送到要执行的查询,即不会立即等待结果。
DO
$$
BEGIN
PERFORM dblink_connect ('hq', 'your_connstr_or_foreign_server_here');
PERFORM dblink_send_query('con1', format('SELECT pg_sleep(3); NOTIFY %I, %L ', 'Channel', 'payload'));
PERFORM dblink_disconnect('con1');
END
$$;
如果您在事务结束之前正确执行此操作,则本地事务将获得 3 秒(
pg_sleep(3)
)的先行时间来提交。选择适当的秒数。
这种方法存在固有的不确定性,因为如果出现任何问题,您将得不到错误消息。对于一个
安全的解决方案,您需要一个不同的设计。但是,成功发送命令后,它仍然失败的机会极小。错过成功通知的机会似乎
更高,但这已经内置在您当前的解决方案中了。
安全的替代方案
更安全的替代方案是写入队列表并像
@Bohemian's answer中讨论的那样进行轮询。这个相关的答案演示了如何安全地轮询: