I'm starting to play with PostgreSQL and noticed that sequence
s never rollback, even on failed INSERT
.
I've read that it is as expected to prevent duplicated sequences on concurrent transactions and I found that weird as my database experience is only with GTM where transaction restarts are common and used precisely for this.
So I wanted to test restarts in PGSQL and loaded this in a database:
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;
So, function mytest()
will retrieve balance, wait 3 seconds (to let me launch the other process) and then update balance based on the saved variable.
I now launch 2 calls to this function directly from shell:
void$ psql -c "select * from account where id=1"
id | title | balance
----+--------------+---------
1 | Test Account | 0
(1 row)
void$ psql -c "select mytest()" & PIDA=$! && psql -c "select mytest()" && wait $PIDA
[1] 3312
NOTICE: Balance: 0
NOTICE: Balance: 0
mytest
--------
10
(1 row)
mytest
--------
10
(1 row)
[1]+ Done psql -c "select mytest()"
void$ psql -c "select * from account where id=1"
id | title | balance
----+--------------+---------
1 | Test Account | 10
(1 row)
I would expect balance to be 20, not 10, as the last transaction to be committed should restart as the "view" of balance from account where id=1
changed during processing...
I've read about transaction isolation in official documentation and it sounds to me that the default read committed
should enforce this behavior precisely..
I've also tested changing the isolation level to serializable
and then the last transaction committed does throw an exception but I would like to know if there isn't any "transaction restart" functionality (as I described) or if I'm missing something...