Postgresql 在事务完成后执行触发器

Postgresql 在事务完成后执行触发器,postgresql,triggers,transactions,notifications,deferred-execution,Postgresql,Triggers,Transactions,Notifications,Deferred Execution,在PostgreSQL中,延迟触发器是在事务完成之前(之内)执行还是在事务完成之后执行 报告说: 可延期的 不可延期 这控制是否可以延迟约束。约束 不可延期的,将在每次 指挥部。可以推迟检查可推迟的约束 直到事务结束(使用命令) 它没有指定它是否仍在事务内部或外部。我的个人经验表明,它在交易中,我需要它在交易之外 延迟(或初始延迟)触发器是否在事务内部执行?如果是,我如何才能将其执行推迟到交易完成时 为了给你一个提示,我正在使用pg_notify和RabbitMQ()发送消息。我在外部应用程序中

在PostgreSQL中,延迟触发器是在事务完成之前(之内)执行还是在事务完成之后执行

报告说:

可延期的
不可延期

这控制是否可以延迟约束。约束 不可延期的,将在每次 指挥部。可以推迟检查可推迟的约束 直到事务结束(使用命令)

它没有指定它是否仍在事务内部或外部。我的个人经验表明,它在交易中,我需要它在交易之外

延迟
(或
初始延迟
)触发器是否在事务内部执行?如果是,我如何才能将其执行推迟到交易完成时

为了给你一个提示,我正在使用
pg_notify
和RabbitMQ()发送消息。我在外部应用程序中处理此类消息。现在我有一个触发器,它通过在消息中包含记录的id来通知外部应用程序新插入的记录。但以一种不确定的方式,有时,当我试图通过手头的id选择一条记录时,该记录无法找到。这是因为事务尚未完成,并且记录没有实际添加到表中。如果我只能在交易完成后推迟触发器的执行,那么一切都会解决

为了得到更好的答案,让我更接近真实世界来解释情况。实际情况比我之前解释的要复杂一些。如果有人感兴趣的话。由于我不想深入探讨的原因,我必须从另一个数据库发送通知,因此通知实际上是这样发送的:

PERFORM * FROM dblink('hq','SELECT pg_notify(''' || channel || ''', ''' || payload || ''')');
我确信这会使整个情况更加复杂。

触发器(包括各种延迟触发器)在事务内部触发

但这并不是问题所在,因为通知是在事务之间传递的

NOTIFY
以一些重要的方式与SQL事务交互。 首先,如果在事务内部执行
通知
,则通知 只有在事务完成后才会传递事件 坚信的这是适当的,因为如果事务中止, 其中的所有命令都无效,包括
NOTIFY
。但是 如果期望通知事件发生,则可能会令人不安 立即交货。其次,如果听力会话收到 通知信号在事务中时,通知 事件将不会传递到其连接的客户端,直到 事务已完成(提交或中止)。再说一次 理由是,如果通知是在 如果事务后来被中止,则希望通知 以某种方式被撤消-但服务器无法“收回”通知 一旦发送到客户端因此通知事件仅限于 在交易之间交付。结果是 使用
NOTIFY
进行实时信令的应用程序应尽量保持 他们的交易很短

我的

只是SQL
NOTIFY
命令的一个方便的包装函数

如果在收到通知后找不到某些行,则一定有其他原因!去找吧。可能的候选人:

  • 并发事务干扰
  • 触发器做的事情比你想象的要多或不同
  • 各种各样的编程错误
不管是哪种方式,正如手册所建议的,保持发送通知的事务简短都是一个好主意

dblink
至于你以后的补充:

PERFORM * FROM dblink('hq','SELECT pg_notify(''' || channel || ''', ''' || payload || ''')');
。。。应使用重写,以简化并确保语法安全:

PRERFORM dblink('hq', format('NOTIFY %I, %L', channel, payload));
dblink在这里是一个游戏规则改变者,因为它在另一个数据库中打开一个单独的事务。这有时用于伪造自主交易

dblink()
等待远程命令完成。因此,远程事务很可能首先提交:

函数返回查询生成的行

如果您可以从同一事务发送通知,那么这将是一个干净的解决方案

dblink的变通方法 如果必须从其他事务发送通知,则有一种解决方法:

dblink\u send\u query
发送一个异步执行的查询,即不立即等待结果

如果在事务结束之前执行此操作,本地事务将提前3秒()开始提交。选择适当的秒数

这种方法存在固有的不确定性,因为如果出现任何错误,您都不会收到错误消息。对于安全的解决方案,您需要不同的设计。但是,在成功发送命令后,仍然失败的可能性非常小。错过成功通知的几率似乎要高得多,但这已经内置到您当前的解决方案中

安全替代方案 一种更安全的替代方法是写入队列表并轮询它,如中所述。此相关答案演示了如何安全地进行投票:


我将此作为一个答案,假设您试图解决的实际问题是将外部流程的执行推迟到事务完成之后(而不是您试图使用触发功夫解决的X-Y“问题”)

让数据库告诉应用程序去做某件事是一种破坏性的模式。
DO  -- or plpgsql function
$$
BEGIN

-- do stuff

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
$$;