2017-02-13 8 views
3

Я пытаюсь использовать создание транзакционного блока внутри функции, поэтому моя цель - использовать эту функцию по одному, поэтому, если кто-то использует эту функцию, а другой хочет ее использовать он не может, пока первый один не конец я создаю эту функцию:PostgreSQL - Запуск A Блок транзакций IN Функция

CREATE OR REPLACE FUNCTION my_job(time_to_wait integer) RETURNS INTEGER AS $$ 
DECLARE 
    max INT; 
BEGIN 
    BEGIN; 
     SELECT MAX(max_value) INTO max FROM sch_lock.table_concurente; 
     INSERT INTO sch_lock.table_concurente(max_value, date_insertion) VALUES(max + 1, now()); 
     -- Sleep a wail 
     PERFORM pg_sleep(time_to_wait); 
     RETURN max; 
    COMMIT; 
END; 
$$ 
LANGUAGE plpgsql; 

но швы не работает, у меня есть ошибка синтаксиса ошибка BEGIN;

Без BEGIN; и COMMIT я получить правильный результат, я использую этот запрос для проверки:

-- First user should to wait 10 second 
SELECT my_job(10) as max_value; 

-- First user should to wait 3 second 
SELECT my_job(3) as max_value; 

Так результат:

+-----+----------------------------+------------+ 
| id |    date   | max_value | 
+-----+----------------------------+------------+ 
| 1 | 2017-02-13 13:03:58.12+00 |  1  | 
+-----|----------------------------+------------+ 
| 2 | 2017-02-13 13:10:00.291+00 |  2  | 
+-----+----------------------------+------------+ 
| 3 | 2017-02-13 13:10:00.291+00 |  2  | 
+-----+----------------------------+------------+ 

Но результат должен быть:

+-----+----------------------------+------------+ 
| id |    date   | max_value | 
+-----+----------------------------+------------+ 
| 1 | 2017-02-13 13:03:58.12+00 |  1  | 
+-----|----------------------------+------------+ 
| 2 | 2017-02-13 13:10:00.291+00 |  2  | 
+-----+----------------------------+------------+ 
| 3 | 2017-02-13 13:10:00.291+00 |  3  | 
+-----+----------------------------+------------+ 

поэтому третий один id = 3 должен иметь max_value = 3 и не 2, это происходит потому, что первый пользователь Выберите max = 1 и подождите 10 sec, а второй пользователь Выберите max = 1 и подождите 3 sec перед установкой, но правильным решением является: Я не могу использовать эту функцию, пока первая не закончит, для этого я хочу сделать что-то безопасное и защищенное.

Мои вопросы:

  • как я могу сделать блок транзакции внутри функции?
  • Есть ли у вас какие-либо предложения, как мы можем сделать это безопасным способом?

спасибо.

+0

К сожалению, это невозможно. Функция не может использовать фиксацию или откат. –

+0

О, мой бог, и что мне делать @a_horse_with_no_name какое-нибудь предложение? –

+0

Почему бы вам просто не использовать последовательность для генерации чисел. Это будет ** много ** быстрее и гораздо более масштабируемым. –

ответ

2

Хорошо, поэтому вы не можете COMMIT в функции. Однако вы можете сохранить точку сохранения и вернуться к точке сохранения.

Ваша самая маленькая возможная транзакция - это один оператор, проанализированный и выполняемый сервером от клиента, поэтому каждая транзакция является функцией. Однако в транзакции вы можете сохранять точки. В этом случае вы должны посмотреть на части обработки исключений PostgreSQL, чтобы справиться с этим.

Однако это не то, что вы хотите здесь. Вы хотите (я думаю?) Данные должны быть видны во время длительной работы на стороне сервера. Для этого вам не повезло. Вы не можете увеличить количество идентификаторов транзакций при запуске функции.

У вас есть несколько вариантов, в порядке, что я считал бы передовой практики (лучшего к худшему):

  1. ломаются вашей логики на более мелкие кусочки, которые каждый перемещают дб из одного непротиворечивого состояния в другой, и запускать их в отдельных транзакциях.
  2. Используйте очередь сообщений (например, pg_message_queue) в db, а также внешний работник и что-то, что выполняет шаг и дает сообщение для следующего шага. Недостатком этого является дополнительное обслуживание.
  3. Используйте функцию или фреймворк, например dblink или pl/python, или pl/perlu для подключения к db и выполнения транзакций там. Ик ....
+0

это очень грустно :(Хорошо, я постараюсь следовать вашим предложениям, спасибо вам :) –

+0

Chris Travers проверить мой ответ, я не знаю, хорошая ли это идея или нет, но это дает мне правильный результат –

1

Вы можете использовать dblink для этого. Что-то вроде:

CREATE OR REPLACE FUNCTION my_job(time_to_wait integer) RETURNS INTEGER AS $$ 
DECLARE 
    max INT; 
BEGIN 
    SELECT INTO RES dblink_connect('con','dbname=local'); 
    SELECT INTO RES dblink_exec('con', 'BEGIN'); 
    ... 
    SELECT INTO RES dblink_exec('con', 'COMMIT'); 
    SELECT INTO RES dblink_disconnect('con'); 
END; 
$$ 
LANGUAGE plpgsql; 
1

Я не знаю, если это хороший способ или нет, но то, что если мы будем использовать LOCK TABLE, например, так:

CREATE OR REPLACE FUNCTION my_job(time_to_wait integer) RETURNS INTEGER AS $$ 
DECLARE 
    max INT; 
    BEGIN 
     -- Lock table so no one will use it until the first one is finish 
     LOCK TABLE sch_lock.table_concurente IN ACCESS EXCLUSIVE MODE; 

     SELECT MAX(max_value) INTO max FROM sch_lock.table_concurente; 
     INSERT INTO sch_lock.table_concurente(max_value, date_insertion) VALUES(max + 1, now()); 
     PERFORM pg_sleep(time_to_wait); 
     RETURN max; 
    END; 
    $$ 
LANGUAGE plpgsql; 

Это дает мне правильный результат.

+0

Насколько я понимаю, вопрос заключается в том, что пользователю 2 нужно видеть вставки, которые в настоящее время происходят в запросе пользователя 1. Я не думаю, что это так. –

+0

@ChrisTravers это прекрасно работает, поэтому, когда первое использование заканчивается, вставьте 'max_value = 2', поэтому второй пользователь выбирает это значение и использует его для вставки' max_value = 3', поэтому у нас есть безопасный доступ к этому значению, это используемый только и только одним пользователем за время –

+0

А так ваше решение - это блокировка стола. Думаю, это тоже работает. –