Concurrency Postgresql UPDATE

为君一笑 提交于 2019-12-25 00:15:02

问题


I have this situation with thread process. Have table like example :

id   |   type  | external_key | balance | amount  |      date
1       TOPUP       MT1                    10         2019-01-1 13:01:00.500110
2       USAGE       MT1                    -1         2019-01-1 13:01:01.300100
3       TOPUP       MT3                     5         2019-01-1 13:01:02.400300

every new data on each row i run this trigger to update field balance to store current balance every row :

create or replace function function_update_balance() returns trigger
    language plpgsql
as $$
BEGIN
    IF new.type = 'CBA_ADJ' THEN
    update example set balance=(SELECT SUM(case when amount >= 0 then amount when amount <= 0 then amount end) FROM example WHERE id=id and type=new.type and external_key =new.external_key ) WHERE id=new.id and date<= new.date;
    END IF;
return null;
END;
$$
;

if the row insert not to fast (not like thread), the balance have the correct balance like this:

id   |   type  | external_key | balance | amount  |      date
1       TOPUP       MT1            10       10         2019-01-1 13:01:00.500110
2       USAGE       MT1             9       -1         2019-01-1 13:01:01.300100
3       TOPUP       MT3             5        5         2019-01-1 13:01:02.400300

but when the data insert on thread (just different on per , milisecond the balance not have correct result like this:

id   |   type  | external_key | balance | amount  |      date
1       TOPUP       MT1            10       10         2019-01-1 13:01:00.500110
2       USAGE       MT1             9       -1         2019-01-1 13:01:01.300100
3       TOPUP       MT3             5        5         2019-01-1 13:01:02.400300
4       USAGE       MT1             8        -1        2019-01-1 13:01:03.404000
5       USAGE       MT1             8        -1        2019-01-1 13:01:02.405000
6       USAGE       MT1             8        -1        2019-01-1 13:01:03.407000
7       USAGE       MT1             8        -1        2019-01-1 13:01:03.408000
8       USAGE       MT1             4        -1        2019-01-1 13:01:05.612000

that's like, the select on update have read same value before last transaction was commit, i have try with isolation like this :

create or replace function function_update_balance() returns trigger
        language plpgsql
    as $$
    BEGIN
        IF new.type = 'CBA_ADJ' THEN
        start transaction isolation level repeatable read;
        update example set balance=(SELECT SUM(case when amount >= 0 then amount when amount <= 0 then amount end) FROM example WHERE id=id and type=new.type and external_key =new.external_key ) WHERE id=new.id and date<= new.date;
        END IF;
    return null;
    END;
    $$
    ;

then give me error :

ERROR: cannot begin/end transactions in PL/pgSQL
Hint: Use a BEGIN block with an EXCEPTION clause instead.
Where: PL/pgSQL function function_update_currentbalance() line 5 at SQL statement

any clue for this issue with concurrency process ?


回答1:


That is a weird trigger function.

However, as you have noticed, concurrent inserts will cause the trigger functions to run concurrently, so that they cannot see each other's data modifications.

If you need to avoid that, you have to serialize the execution using locks.

You could

LOCK example IN SHARE ROW EXCLUSIVE MODE;

in the trigger function, but since that will prevent concurrent autovacuum runs, it is not such a hot idea.

Better would be to use a row lock:

PERFORM 1 FROM example
WHERE type = NEW.type and external_key = NEW.external_key
ORDER BY id, date
FOR UPDATE OF example;

That will serialize all transactions that affect the same rows.



来源:https://stackoverflow.com/questions/57865130/concurrency-postgresql-update

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!