2015-03-31 6 views
0

Это вопрос о последующих действиях от этого one, поэтому я знаю, что могу использовать (блокирование) LOCK, но я хочу использовать блокировки предикатов и сериализуемую изоляцию транзакций.Generic обработчик 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() до 5-й попытки, где он будет поднимать исключение.

Так у меня есть две точки здесь (где, возможно, пункт 2 также аннулирует пункт 1):

  • myretest() не работает, как и ожидалось, каждые результаты итерации исключением serialiation_failure даже после приуроченных отделки резьбы: есть ли что я должен добавить для «перезагрузки» транзакции?

  • Как я могу сделать это (myretest() логика) общим, чтобы он применим к каждой вызываемой функции в системе без необходимости использовать функции «обертки»?

ответ

1

Сериализуемые сделок предоставить именно то, что вы ищете, если вы используете какую-то структуру, которая начинает транзакцию более, когда он получает сообщение об ошибке с SQLSTATE из 40001 или 40P01.

В PostgreSQL функция всегда работает в контексте транзакции. Вы не можете запустить новую транзакцию в контексте функции «обертка». Для этого потребуется немного другая функция, которая обычно называется «хранимой процедурой» - что-то, чего не существует в PostgreSQL. Поэтому вам необходимо поместить логику для управления перезагрузкой в ​​код, который отправляет транзакцию в базу данных. К счастью, для этого есть много коннекторов: Java, perl, python, tcl, ODBC и т. Д. Существует даже соединитель для отдельного подключения к базе данных PostgreSQL в процедуре PostgreSQL, что может позволить вам сделать что-то вроде что вы хотите:

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

Я видел это сделано в различных «клиентов» рамок. Очевидно, что это плохая идея распространять эту информацию во всех местах, где приложение логически работает с базой данных, но есть много веских причин для маршрутизации всех запросов к базе данных с помощью одного метода «доступа» (или, по крайней мере, очень небольшого числа из них), и большинство фреймворков обеспечивают способ справиться с этим на этом уровне. (Например, весной вы хотели бы создать диспетчер транзакций с использованием инъекции зависимостей.) Вероятно, это относится к некоторому языку, который вы используете для своей логики приложения, но если вы действительно хотели, вы, вероятно, могли бы использовать plpgsql и dblink; это, вероятно, не будет вашим самым простым путем.