Start with these two tables and this an initial record for c:
create table c
(
id serial primary key,
name varchar not null
);
create table e
(
id varchar not null,
c_id bigint references c (id) not null,
name varchar not null,
primary key (id, c_id)
);
insert into c (name) values ('deadlock test');
Thread 1:
begin;
select * from c where id = 1 for update;
insert into e (id, c_id, name) VALUES ('bar', 1, 'second') on conflict do nothing ;
commit;
Thread 2:
begin;
insert into e (id, c_id, name) VALUES ('bar', 1, 'first') on conflict do nothing ;
commit;
Execution order is:
- Thread 1: begin
- Thread 2: begin
- Thread 1: lock
c - Thread 2: insert
e - Thread 1: insert
e<-- deadlock
Why does this happen?
Adding a lock to c on Thread 2 of course avoids the deadlock, but it's not clear to me why. Also interesting is that if the row in e exists before Thread 1 or 2 run, then no deadlock happens.
I suspect there are at least two things going on:
- The primary key creates a unique constraint that causes some sort of locking on
ethat I don't understand, even with theON CONFLICT DO NOTHING. - The foreign key on
c_idresults in some sort of trigger causing a lock oncwhen a new record is inserted (or whenc_idis updated, I presume).
Thanks!