如何捕获具有UOW副作用的函数的PostgreSQL ROLLBACK?

时间:2016-12-01 16:24:39

标签: postgresql transactions rollback

我正在编写一个标量plpgsql函数,它包含一个在数据库外部有副作用的C函数。在调用函数时,在某些任意SQL(触发,选择,写入等)中,我希望在PostgreSQL工作单元(UOW)边界上提交或回滚副作用。我可以处理UOW提交,但我不知道如何“捕获”数据库ROLLBACK并回滚副作用。关键点在于我正在编写函数,但是无法控制它的调用方式,即我无法通过EXCEPTION处理程序“强制”调用该块。有什么想法吗?

对于提交,我计划将plpsql函数INSERT放入数据库TABLE中,该数据库具有触发器“... INSTER INSERT ... EXECUTE PROCEDURE commit_my_side_effect()”,因此当提交UOW时,该行将被提交,AFTER INSERT触发器触发并触发,副作用被提交;

到目前为止,我唯一的想法是将txid_current()传递给后台工作进程。然后在使用SPI的一些心跳上,检查txid是否没有飞行或已提交,那么它必须已经回滚。但这似乎很重要。

2 个答案:

答案 0 :(得分:0)

从广义上讲,交易被视为"回滚"如果它没有被承诺并且它不再运行;为了符合ACID标准,明确的ROLLBACK在功能上必须与服务器上的电源线相同,因此通常情况下,您可能无法进行与回滚相关的任何故意操作。能够进入。

回滚数据的实际删除由vacuuming处理,它或多或少与您提议的后台工作程序一样:由未执行且未提交的事务写入的任何内容都是删除的候选者。但是,还有更多内容,因为包含子事务(SAVEPOINT s或PL / pgSQL EXCEPTION块)的事务可以部分回滚。换句话说,仅txid_current()并不足以决定是否已提交更改,而且我不知道Postgres是否公开了有关子交易状态的足够信息以让您满足此要求。

我认为唯一合理的方法是将副作用的应用程序移动到外部进程,并在提交后触发它,一旦您知道实际提交的内容。我想到了两种方法:

  • 让您的PL / pgSQL函数插入由​​外部进程轮询的工作队列,或
  • 通过NOTIFY将流程更改添加到流程中(通知仅在提交时提供,并且来自已回滚的子流转换的通知将被丢弃)

通知更轻量级,延迟更低(它们异步传递,因此不需要轮询),但不如基于表的方法强大,因为通知队列在发生崩溃时被清除或意外断开连接。当然,如果你想要在没有轮询缺点的情况下确保碰撞安全,你可以简单地做到这两点。

答案 1 :(得分:0)

I found a feature called ON_ERROR_ROLLBACK, and looking at the implementation,https://github.com/postgres/postgres/blob/master/src/bin/psql/common.c, I think I can "wrap" all the SQL commands using the following pseudo-code to add "fake" savepoint, and "fake" rollback to savepoint and fire off a "rollback_side_effect()":

side_effect_fired = false; // set true if the side_effect udf called
run("SAVEPOINT _savepoint");
run($sqlcommand);
if (txn_status == ERROR && side_effect_fired) {
   run("ROLLBACK TO _savepoint"
   rollback_side_effect()); // rollback the side effect
}

I probably need a stack of _savepoint. I will run with that!