这是one的后续问题所以我知道我可以使用(阻塞)LOCK,但我想使用谓词锁和可序列化的事务隔离。
我想拥有的是序列化失败的通用处理程序,它会重试函数/查询X次。
例如,我有这个:
CREATE SEQUENCE account_id_seq;
CREATE TABLE account
(
id integer NOT NULL DEFAULT nextval('account_id_seq'),
title character varying(40) NOT NULL,
balance integer NOT NULL DEFAULT 0,
CONSTRAINT account_pkey PRIMARY KEY (id)
);
INSERT INTO account (title) VALUES ('Test Account');
CREATE OR REPLACE FUNCTION mytest() RETURNS integer AS $$
DECLARE
cc integer;
BEGIN
cc := balance from account where id=1;
RAISE NOTICE 'Balance: %', cc;
perform pg_sleep(3);
update account set balance = cc+10 where id=1 RETURNING balance INTO cc;
return cc;
END
$$
LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION myretest() RETURNS integer AS $$
DECLARE
tries integer := 5;
BEGIN
WHILE TRUE LOOP
BEGIN -- nested block for exception
RETURN mytest();
EXCEPTION
WHEN SQLSTATE '40001' THEN
IF tries > 0 THEN
tries := tries - 1;
RAISE NOTICE 'Restart! % left', tries;
ELSE
RAISE EXCEPTION 'NO RESTARTS LEFT';
END IF;
END;
END LOOP;
END
$$
LANGUAGE plpgsql;
因此,如果直接同时调用mytest()
,我会在最后一次提交时遇到序列化失败:
4SO$ psql -c "select mytest()" & PIDA=$! && psql -c "select mytest()" && wait $PIDA
[1] 4909
NOTICE: Balance: 0
NOTICE: Balance: 0
mytest
--------
10
(1 row)
ERROR: could not serialize access due to concurrent update
CONTEXT: SQL statement "update account set balance = cc+10 where id=1 RETURNING balance"
PL/pgSQL function mytest() line 10 at SQL statement
如果我致电myretest()
,它应该尝试执行mytest()
直到第5次尝试它会引发异常。
所以我在这里有两点(也许第2点也使第1点无效):
myretest()
无法按预期工作,即使在并发线程完成后,每次迭代都会导致serialiation_failure异常:是否应该添加一些内容" reset"交易?
如何使这个(myretest()
逻辑)通用,以便它适用于系统中的每个被调用函数,而不需要"包装器"这样的功能?
答案 0 :(得分:1)
只要您在收到SQLSTATE
40001
40P01
或{{1}}的错误时使用某个启动事务的框架,可序列化事务就能提供您正在寻找的内容。< / p>
在PostgreSQL中,函数总是在事务的上下文中运行。您无法在“包装器”功能的上下文中启动新事务。这需要稍微不同的功能,通常称为“存储过程” - 这在PostgreSQL中是不存在的。因此,您需要将逻辑管理重新启动到将数据提交到数据库的代码中。幸运的是,有许多连接器--Java,perl,python,tcl,ODBC等。甚至还有一个连接器,用于在PostgreSQL过程语言中单独连接到PostgreSQL数据库,这可能允许你做类似的事情。你想要什么:
http://www.postgresql.org/docs/current/static/dblink.html
我已经在各种“客户端”框架中看到过这种情况。很明显,将它传播到应用程序逻辑上处理数据库的所有位置是一个坏主意,但有很多很好的理由通过一种“访问器”方法(或至少非常少量的方法)路由所有数据库请求),大多数框架提供了在该层处理此问题的方法。 (例如,在Spring中,您可能希望使用依赖注入创建事务管理器。)这可能属于您用于应用程序逻辑的某种语言,但如果您真的想要,则可能使用plpgsql和dblink;但是,这可能不会是你最简单的道路。