Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vzeJo-001ByU-2O for pgsql-general@arkaria.postgresql.org; Mon, 09 Mar 2026 17:23:00 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vzeIo-000EgZ-0C for pgsql-general@arkaria.postgresql.org; Mon, 09 Mar 2026 17:21:58 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vzeIn-000EgO-2H for pgsql-general@lists.postgresql.org; Mon, 09 Mar 2026 17:21:58 +0000 Received: from mail-wr1-x42a.google.com ([2a00:1450:4864:20::42a]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1vzeIl-00000001pfS-1pfm for pgsql-general@lists.postgresql.org; Mon, 09 Mar 2026 17:21:57 +0000 Received: by mail-wr1-x42a.google.com with SMTP id ffacd0b85a97d-439bcec8613so6239508f8f.3 for ; Mon, 09 Mar 2026 10:21:55 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1773076915; cv=none; d=google.com; s=arc-20240605; b=A0V3P8CJ8SnwCQ7wm2D5whTqTgAFz6mO/vIV9MgSf20no4AZRvwuX5E2Imlnri54cZ 0FmcnJY4lQCaEz4INQN9XMsG1sd4L7gg1MpnHbm88cvBNtPrDHtjoyWdUGaCqFr5utuZ LMzsrYQ+skeAFD4sVZjbvCa0+kMtXxcODqLP4ipwWS8dR3iYpFv/yTJmDqQ09qJDE5yQ 1bPGQzyXRNSry6ngul1UovqPCgO7L4Hg54ZkZTP4ytmeN5YlTpm4gMZVtllaqi2lL7iv FYykFKazl7bOMfIN2IA4a0CT0kVQ36Xf3WLwxoywgVJlGf8U1zc8weiETOaD5TTM/qDa sxTw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:dkim-signature; bh=ZNlen0rajlwOrWvgOgVokiz4YtsrhZKbWrcRSJrYEL0=; fh=wLkmu0RP2DRU90OkfoSuuaO5VCczX3f7tLDCDhBQLOg=; b=goLRdm+jmGHE7a/W8eyh0TGJPqKrurSNgq0snfjWP2+WnfO+x6uY3zspAOvzfRBbz9 a63A8+wTdkKhK0Q9+QCQyEBFS2/KJ5AWmRuCotvSlHF8C8sgHGSTQMsXlfbQqTONRbIK w1v0rnSBJaGLrzvW9Ybf/mWh0PXutx4n6cbzKdnrUuvPActtmIDUz40OWC8QWxqtedqV 6I7lyCxmekpXecPJ3OCi6JnsWsnxEt8E28Wmz6Q3BBr75BmcJPtUSeyFFDmLMtyr3UsR DsgX3n+BlxoQEMn/vWHCFKIV7UQcaD3KQJfJ+j6efCOZIuYNeCH4+OjRLXD+eH2112w8 99QQ==; darn=lists.postgresql.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1773076915; x=1773681715; darn=lists.postgresql.org; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=ZNlen0rajlwOrWvgOgVokiz4YtsrhZKbWrcRSJrYEL0=; b=duWqrDadG1N3YqWGb+A0mHPJj213Ut/Ur8dzNiRVgTADSmBxbaaywsZZxSf9vwz1ZW Xde8KISoy2rkU/MHgXawqeKfDrrkbqvwhEclKIuh5kYk3TyGaB9ss+zvVE/4rctOkWXq la5BENYnqYTH/SZqSKNqJRO6XeRVfAHsEMXEd0XCvqnixPu4H2o5ssGUUlW8QVNnkdNN /CCUQrerzYxDN55ZADcu35KhOMYZ81+KD0fxAUJqOo46tms1HbOYL29ZyWI23/zBTPhx YzC2AB+206SyVKm3+mtYMebm+vzV7/uVi5xL6ZHYbSqHafpKhIbLBVRgGbagvVsnFTMl /uGA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1773076915; x=1773681715; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=ZNlen0rajlwOrWvgOgVokiz4YtsrhZKbWrcRSJrYEL0=; b=nBUypTgeZUuEUdjLWj5GhwiFtRSo0lBLMDJDQ1ITn188WwaONdEUiy7ihJQ4oIFpwC Uqc7PhNyIP3DHWygF0XzwknnDqIcX9qzloBCdqFZ3954CU5bnqN5XvPF6DqAoEqtxuhO bi77YKJ4QWtp/asgMqaPKTrRoVKzBeMIy3DbLvY9bfN1IyPFnSnzA5+wjOL1zZrV077e fWjgM3M4mztynrg0zqtPKpPpH2wmhDDGQmLUZOnEeDjUeQ6PFXYZC4Sw21aOjVBhXqKI XV82qOB+/YNt15EmO6enHl0Jhx4qfxWIwRLfuA7gpo6T+6RyxAVXlhE7nAroMS2YDs6t E9dg== X-Forwarded-Encrypted: i=1; AJvYcCXP3HQoFOrBd7FVSIRWb04Iy2S0vRdcHnVRyV0Y6sgpLOdHT1+gtrQC1Mcb3WvGOj6tEDR4IVxJ7yQQS61B@lists.postgresql.org X-Gm-Message-State: AOJu0YzNbg7I6In9ljbddvmgjDZXNsVHUaLRSZU6LMFXomPIQtXiEH89 g+HLkoAmRyQvZdv7knQr5e1RG79RW5t8n8lwN7FAYUf61Bem0vW98iWJvHkvUov2GzEEFmi15ot PRqeNaTsOq1J6RYEMR4Muh2PMEaGwgS4= X-Gm-Gg: ATEYQzwG+YIdGZ/eAqNKfpTPE4fqd3k25zBIs/0oRzDhYuI3Bz4ltHp7FOpOevUciew YcnHVqzhGU3pB8ntvwFMntu6WjIer/2PUpw8jVJlgztMeYkXA4GMrSrFJIokQg8yPPjZ2dc4V3A BRUfuCzodNL2be+AwJ3Z0+OfdX0KFdAe/SDwFwaQPCFBiOm3c1hOFu2jkQUeUJhB/Ke09RpCStD Xbemt2vpYNi8PyCNRXoF640tufraRX/fqeFA1RutknW62hzNkH7eG0nf6/VO9UGfZ14e3xnsjv7 p/w4glw= X-Received: by 2002:a05:6000:258a:b0:439:b666:6cab with SMTP id ffacd0b85a97d-439da679715mr20037212f8f.40.1773076914561; Mon, 09 Mar 2026 10:21:54 -0700 (PDT) MIME-Version: 1.0 References: <1164079167.6346688.1772982934392@mail.yahoo.com> <1656643760.2116109.1772994207524@mail.yahoo.com> In-Reply-To: From: Shaheed Haque Date: Mon, 9 Mar 2026 17:21:43 +0000 X-Gm-Features: AaiRm51xVWYp_Wea6VuD-Jg4Xt_fLwUPfRL_zoi1QuCEefSOURFTJIeFEbWKJXk Message-ID: Subject: Re: Unexpected deadlock across two separate rows, using Postgres 17 and Django's select_for_update() To: Greg Sabino Mullane Cc: felix.quintgz@yahoo.com, pgsql-general@lists.postgresql.org Content-Type: multipart/alternative; boundary="000000000000a6e717064c9aa35b" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --000000000000a6e717064c9aa35b Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Greg, That's extremely helpful, thank you. On Mon, 9 Mar 2026 at 13:07, Greg Sabino Mullane wrote= : > On Mon, Mar 9, 2026 at 5:55=E2=80=AFAM Shaheed Haque > wrote: > >> I'm not sure I understand. The two queries are referencing separate, >> single rows in the child table (primary keys payroll_endpoint.id =3D 1 a= nd >> 2), so where does the multi-row bit come in? Is it because the two paren= t >> tables are also being locked, in possibly different orders? >> > > This is unrelated to parent tables. What is not shown is the previous > locks. Process A locks id 1. Process B locks id 2, then attempts to lock = id > 1. Process A attempts to lock id 2. Hence, a deadlock, even if all four > lock attempts are not shown in the log. > > I'm not seeing "Tom's suggestion". Is there a way to specify that the >> parent tables need not be locked? Perhaps by omitting them from the quer= y? >> > > alter system set log_statement =3D 'all'; > select pg_reload_conf(); > > ## Run your program and get the deadlock error > > alter system reset log_statement; > select pg_reload_conf(); > > Now check your logs, find the PIDs involved in the deadlock, and trace > what actions they did before the deadlock occurred. > > Here's a real example, showing what the Postgres logs will look like: > > -- Process A: > create table t (id int); > insert into t values (1),(2); > begin; > select * from t where id =3D 1 for update; > > -- Process B: > begin; > select * from t where id =3D 2 for update; > select * from t where id =3D 1 for update; -- Hangs, waiting for process = A > to finish > > -- Process A: > select * from t where id =3D 2 for update; > > DEADLOCK! > > Logs: > > 2026-03-09 09:00:23.885 EDT [242171] LOG: statement: alter system set > log_statement =3D 'all'; > 2026-03-09 09:00:25.804 EDT [242136] LOG: received SIGHUP, reloading > configuration files > 2026-03-09 09:00:25.805 EDT [242136] LOG: parameter "log_statement" > changed to "all" > 2026-03-09 09:00:31.910 EDT [242171] LOG: statement: drop table if exist= s > t; > 2026-03-09 09:00:36.649 EDT [242171] LOG: statement: create table t(id > int); > 2026-03-09 09:00:39.522 EDT [242171] LOG: statement: insert into t value= s > (1),(2); > 2026-03-09 09:00:42.121 EDT [242171] LOG: statement: begin; > 2026-03-09 09:00:50.788 EDT [242171] LOG: statement: select * from t > where id=3D1 for update; > 2026-03-09 09:00:59.755 EDT [242176] LOG: statement: begin; > 2026-03-09 09:01:05.509 EDT [242176] LOG: statement: select * from t > where id=3D2 for update; > 2026-03-09 09:01:08.397 EDT [242176] LOG: statement: select * from t > where id=3D1 for update; > 2026-03-09 09:01:14.278 EDT [242171] LOG: statement: select * from t > where id=3D2 for update; > 2026-03-09 09:01:15.279 EDT [242171] ERROR: deadlock detected > 2026-03-09 09:01:15.279 EDT [242171] DETAIL: Process 242171 waits for > ShareLock on transaction 15122348; blocked by process 242176. > Process 242176 waits for ShareLock on transaction 15122347; > blocked by process 242171. > Process 242171: select * from t where id=3D2 for update; > Process 242176: select * from t where id=3D1 for update; > 2026-03-09 09:01:15.279 EDT [242171] HINT: See server log for query > details. > 2026-03-09 09:01:15.279 EDT [242171] CONTEXT: while locking tuple (0,2) > in relation "t" > 2026-03-09 09:01:15.279 EDT [242171] STATEMENT: select * from t where > id=3D2 for update; > > > Cheers, > Greg > > -- > Crunchy Data - https://www.crunchydata.com > Enterprise Postgres Software Products & Tech Support > > --000000000000a6e717064c9aa35b Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Greg,

That's extremely h= elpful, thank you.

On Mon, 9 Mar 2026 at 13:07, = Greg Sabino Mullane <htamfids@gmai= l.com> wrote:
On Mon, Mar 9, 2026 at 5:55=E2=80=AF= AM Shaheed Haque <shaheedhaque@gmail.com> wrote:
I'm not sure I understand. The two queries are refe= rencing separate, single rows in the child table (primary keys payroll_endpoint.id =3D 1 = and 2), so where does the multi-row bit come in?=C2=A0Is it because the two= parent tables are also being locked, in possibly different orders?

This is unrelated to paren= t tables. What is not shown is the previous locks. Process A locks id 1. Pr= ocess B locks id 2, then attempts to lock id 1. Process A attempts to lock = id 2. Hence, a deadlock, even if all four lock attempts are not shown in th= e log.

I'm not seeing "= ;Tom's suggestion". Is there a way to specify that the parent tabl= es need not be locked? Perhaps by omitting them from the query?
=

alter system set log_statement =3D &= #39;all';
select pg_reload_conf();

#= # Run your program and get the deadlock error

alte= r system reset log_statement;
select pg_reload_conf();
=
Now check your logs, find the PIDs involved in the deadlock,= and trace what actions they did before the deadlock occurred.
Here's a real example, showing what the Postgres logs will= look like:

-- Process A:
create table t= (id int);
insert into t values (1),(2);
begin;
select * from t where id =3D 1 for update;

-- P= rocess B:
begin;
select * from t where id =3D 2 for upd= ate;
select * from t where id =3D 1 for update; -- Hangs, waiting= for process A to finish

-- Process A:
s= elect * from t where id =3D 2 for update;

DEADLOCK= !

Logs:

2026-03-09 09:00:= 23.885 EDT [242171] LOG: =C2=A0statement: alter system set log_statement = =3D 'all';
2026-03-09 09:00:25.804 EDT [242136] LOG: =C2=A0recei= ved SIGHUP, reloading configuration files
2026-03-09 09:00:25.805 EDT [2= 42136] LOG: =C2=A0parameter "log_statement" changed to "all&= quot;
2026-03-09 09:00:31.910 EDT [242171] LOG: =C2=A0statement: drop ta= ble if exists t;
2026-03-09 09:00:36.649 EDT [242171] LOG: =C2=A0stateme= nt: create table t(id int);
2026-03-09 09:00:39.522 EDT [242171] LOG: = =C2=A0statement: insert into t values (1),(2);
2026-03-09 09:00:42.121 E= DT [242171] LOG: =C2=A0statement: begin;
2026-03-09 09:00:50.788 EDT [24= 2171] LOG: =C2=A0statement: select * from t where id=3D1 for update;
202= 6-03-09 09:00:59.755 EDT [242176] LOG: =C2=A0statement: begin;
2026-03-0= 9 09:01:05.509 EDT [242176] LOG: =C2=A0statement: select * from t where id= =3D2 for update;
2026-03-09 09:01:08.397 EDT [242176] LOG: =C2=A0stateme= nt: select * from t where id=3D1 for update;
2026-03-09 09:01:14.278 EDT= [242171] LOG: =C2=A0statement: select * from t where id=3D2 for update;2026-03-09 09:01:15.279 EDT [242171] ERROR: =C2=A0deadlock detected
202= 6-03-09 09:01:15.279 EDT [242171] DETAIL: =C2=A0Process 242171 waits for Sh= areLock on transaction 15122348; blocked by process 242176.
=C2=A0 =C2= =A0 =C2=A0 =C2=A0 Process 242176 waits for ShareLock on transaction 1512234= 7; blocked by process 242171.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 Process 242171= : select * from t where id=3D2 for update;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 P= rocess 242176: select * from t where id=3D1 for update;
2026-03-09 09:01= :15.279 EDT [242171] HINT: =C2=A0See server log for query details.
2026-= 03-09 09:01:15.279 EDT [242171] CONTEXT: =C2=A0while locking tuple (0,2) in= relation "t"
2026-03-09 09:01:15.279 EDT [242171] STATEMENT: = =C2=A0select * from t where id=3D2 for update;

Cheers,
<= div dir=3D"ltr">
Greg

--
Enterprise Postgres Software Products & Te= ch Support

--000000000000a6e717064c9aa35b--