我正在编写一个标量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是否没有飞行或已提交,那么它必须已经回滚。但这似乎很重要。
答案 0 :(得分:0)
从广义上讲,交易被视为"回滚"如果它没有被承诺并且它不再运行;为了符合ACID标准,明确的ROLLBACK
在功能上必须与服务器上的电源线相同,因此通常情况下,您可能无法进行与回滚相关的任何故意操作。能够进入。
回滚数据的实际删除由vacuuming处理,它或多或少与您提议的后台工作程序一样:由未执行且未提交的事务写入的任何内容都是删除的候选者。但是,还有更多内容,因为包含子事务(SAVEPOINT
s或PL / pgSQL EXCEPTION
块)的事务可以部分回滚。换句话说,仅txid_current()
并不足以决定是否已提交更改,而且我不知道Postgres是否公开了有关子交易状态的足够信息以让您满足此要求。
我认为唯一合理的方法是将副作用的应用程序移动到外部进程,并在提交后触发它,一旦您知道实际提交的内容。我想到了两种方法:
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!