2015-03-31 85 views
0

这是从这个one后续问题,所以我知道我可以使用(阻塞)LOCK,但我想使用谓词锁和可序列化的事务隔离。PostgreSQL通用处理程序的序列化失败

我想要的是一个序列化失败的通用处理程序,它会重试函数/查询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()直到第五次尝试它会引发异常。

所以,我有两个点在这里(其中也许2点也无效1点):

  • myretest()不按预期工作,每次迭代的结果,即使在并发线程结束后serialiation_failure异常:有我应该添加到“重置”事务?

  • 我怎么能使这个(myretest()逻辑)通用,以便它适用于系统中的每个被调用的函数,而不需要像这样的“包装器”函数?

回答

1

序列化交易提供你正在寻找什么,只要你使用一些框架,当它收到的4000140P01一个SQLSTATE错误在开始交易。

在PostgreSQL中,函数总是在事务的上下文中运行。您不能在“包装器”功能的上下文中启动新的事务。这需要一个稍微不同的特性,通常称为“存储过程” - 这在PostgreSQL中不存在。因此,您需要将逻辑管理重新启动为将事务提交到数据库的代码。幸运的是,有很多连接器 - Java,perl,python,tcl,ODBC等。甚至还有一个连接器用于在PostgreSQL过程语言中单独连接到PostgreSQL数据库,这可能允许您执行类似你想要什么:

http://www.postgresql.org/docs/current/static/dblink.html

我已经看到了这在不同的“客户端”架构完成。很显然,将它分散到应用程序在逻辑上处理数据库的所有位置是一个坏主意,但有很多很好的理由通过一个“访问器”方法来路由所有数据库请求(或者至少它们的数目很少),并且大多数框架都提供了一种在该层处理此问题的方法。 (例如,在Spring中你想创建一个使用依赖注入的事务管理器)。这可能属于你用于应用程序逻辑的某种语言,但如果你真的想要的话,你可以使用plpgsql和dblink;这可能不会是你最简单的路径,但。

相关问题