prerequisite
- login DB
login for session A
psql -d play-ground
login for session B
psql -d play-ground
- DDL
create table account ( id bigint not null primary key, history jsonb, name varchar(255) );
- DML
insert into account values (1, null, 'john'); insert into account values (2, null, 'jacob');
buffer test
purpose
각 transaction은 buffer memory(disk로부터 가져온 data 저장소)가 아닌 각 자의 memory에서 작업하는 것을 확인한다.
prerequisite
play-ground=# select * from account where id = 1;
id | history | name
----+---------+------
1 | | john
(1 row)
test
- session A
play-ground=# start transaction;
START TRANSACTION
play-ground=# select * from account where id = 1;
id | history | name
----+---------+------
1 | | john
(1 row)
play-ground=# update account set name = 'tom' where id = 1;
UPDATE 1
play-ground=# select * from account where id = 1;
id | history | name
----+---------+------
1 | | tom
(1 row)
- session B
play-ground=# start transaction;
START TRANSACTION
play-ground=# select * from account where id = 1;
id | history | name
----+---------+------
1 | | john
(1 row)
- session A
play-ground=# commit;
COMMIT
- session B
play-ground=# select * from account where id = 1;
id | history | name
----+---------+------
1 | | tom
(1 row)
conclusion
session A는 자신의 memory에서 작업을 하였으므로 commit하기 전까지 session B는 id 1에 대한 name이 john으로 보이는 걸 확인 할 수 있다.
locking test (isolation level: READ COMMITTED)
purpose
두 transaction이 하나의 row에 접근하여 write할 경우에 먼저 접근한 transaction이 해당 row에 lock을 거는 것을 확인한다.
prerequisite
play-ground=# select * from account where id = 1;
id | history | name
----+---------+------
1 | | john
(1 row)
test
- session A
play-ground=# start transaction;
START TRANSACTION
play-ground=# update account set name = 'tom' where id = 1;
UPDATE 1
play-ground=# select * from account where id = 1;
id | history | name
----+---------+------
1 | | tom
(1 row)
- session B
play-ground=# start transaction;
START TRANSACTION
play-ground=# select * from account where id = 1;
id | history | name
----+---------+------
1 | | john
(1 row)
play-ground=# update account set name = 'lucas' where id = 1;
...(locking)
- session A
play-ground=# commit;
COMMIT
- session B
...(unlock)
UPDATE 1
conclusion
session A가 id 1에 대해서 transaction 안에서 write를 할 때 session B가 해당 row에 대해서 write를 시도하면 session B는 locking 되는 것을 확인 할 수 있고 session A가 commit 되는 순간 session B의 write 작업이 자동으로 실행되는 것을 확인 할 수 있다.
locking test (isolation level: REAPEATABLE READ)
purpose
isolation level이 REAPEATABLE READ인 두 transaction이 하나의 row에 접근하여 write 할 경우에 먼저 접근한 transaction이 commit하면 이후 접근한 transaction은 err를 발생시키는 것을 확인한다.
prerequisite
play-ground=# select * from account where id = 1;
id | history | name
----+---------+------
1 | | john
(1 row)
test
- session A
play-ground=# start transaction isolation level repeatable read; START TRANSACTION play-ground=# update account set name = 'John' where id = 1; UPDATE 1 play-ground=#
- session B
play-ground=# start transaction isolation level repeatable read; START TRANSACTION play-ground=# update account set name = 'Top' where id = 1; (waiting)
- session A
play-ground=# commit; COMMIT
- session B
(waiting) ERROR: could not serialize access due to concurrent update
conclusion
isolation level이 REAPEATABLE READ인 두 transaction이 하나의 row에 접근하여 write 할 경우에 먼저 접근한 transaction이 commit하면 이후 접근한 transaction의 statement는 err를 발생시키고 실행되지 않는다.
deadlock test
purpose
두 transaction 각각 하나의 row에 lock을 걸고 각각의 transaction이 lock이 걸린 row에 write를 하는 경우 deadlock err가 발생하는 것을 확인한다.
prerequisite
play-ground=# select id, name from account where id = 1 or id = 2;
id | name
----+-------
1 | john
2 | jacob
(2 rows)
test
- session A
play-ground=# start transaction;
START TRANSACTION
play-ground=# update account set name = 'tom' where id = 1;
UPDATE 1
- session B
START TRANSACTION
play-ground=# update account set name = 'lucas' where id = 2;
UPDATE 1
- session A
play-ground=# update account set name = 'jackson' where id = 2;
...(locking)
- session B
play-ground=# update account set name = 'peter' where id = 1;
ERROR: deadlock detected
DETAIL: Process 31106 waits for ShareLock on transaction 2383786; blocked by process 30713.
Process 30713 waits for ShareLock on transaction 2383787; blocked by process 31106.
HINT: See server log for query details.
CONTEXT: while updating tuple (0,21) in relation "account"
- session A
...(unlock)
UPDATE 1
play-ground=# commit;
COMMIT
play-ground=# select id, name from account where id = 1 or id = 2;
id | name
----+---------
1 | tom
2 | jackson
- session B
play-ground=# commit;
ROLLBACK
conclusion
session A는 id 1에 lock을 걸고 session B는 id 2에 lock을 걸고 있는 상황에서 session A가 id 2에 lock을 시도하면 waiting이 발생한다. 그 상황에서 session B가 id 1에 lock을 시도하면 두 session B는 dead lock err가 발생하게 된다. session A의 transaction은 commit되지만 session B의 transaction은 rollback이 된다.