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 1vzaL7-0018cV-1b for pgsql-general@arkaria.postgresql.org; Mon, 09 Mar 2026 13:08:05 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vzaL5-00GDfU-2j for pgsql-general@arkaria.postgresql.org; Mon, 09 Mar 2026 13:08:04 +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 1vzaKf-00GA14-2R for pgsql-general@lists.postgresql.org; Mon, 09 Mar 2026 13:07:38 +0000 Received: from mail-oo1-xc32.google.com ([2607:f8b0:4864:20::c32]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1vzaKd-00000001neq-2CkF for pgsql-general@lists.postgresql.org; Mon, 09 Mar 2026 13:07:37 +0000 Received: by mail-oo1-xc32.google.com with SMTP id 006d021491bc7-662f30d3f1fso8053734eaf.1 for ; Mon, 09 Mar 2026 06:07:35 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1773061653; cv=none; d=google.com; s=arc-20240605; b=N2D/WnU/bPhXB8p4ZuuyLuV16fZ0zHOEaHERHLjg1VQXvzIyEqXdTOh5hhR8w4ywhR xBJZHnCrpczgGV9KT5ujaUKp0Ov+rH6J3c3NU4Nju8qQSnwsuOsKnXsn9KscyrskVeET oXFz0QQFBAf4CYmGKs1UmC5RCq9q1p+a8VzE17Y6J0aRfo7v6The3d9oTcl+kqfJ1ie8 /okM+CkS3UNU3WqkDKTxBREJUhHZT0AYLezV9w9Q+2+eVFtwvKarHMB3q+UibgcgAFat Ve28TtHnBn12Lq2RYAb1Rwy/rYuxbYsc2NexA700Xl4vgzYza9ZDvNhr3HDVU7yB6fOh 1MjA== 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=oP83KBWPdd/e5068I+FcPjFCooxpq18zs5RiJ07AWEk=; fh=WSEy9u5HCEv4363mS9qU35vFMtxQVwac7XA8dNXQx8w=; b=Xo556lsBrDV36NubMT2H0+l+Qq/khXiND3eRs+gZyIdnJJkB2aqwWUdeC+0qdCIPjd mM+Q45UzfyTv/sm8IgJtiuDc0Ej+RaDwRb7qSh6zRXa0aV1g0CluTj1HkVoHzZ1UOmlS p71AE+1yawtWm8mX33gyF3IzRQe52FWo8H/QF7P2WPFUvJFZBGZAj9469eWbenuvj6o7 U9iJedQJiKgjAYU2GNyJsHIatTStIXIqc0yuTSayjraoqZdHakT7z8JTXc3lar+zCAOl Nt9+fEbkBmEQ3YHsfRnuXJwinDJ9sLcwBItnKpVoaohiQkjysktG7oTbZ5JwWcM5V/X0 eYVg==; 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=1773061653; x=1773666453; 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=oP83KBWPdd/e5068I+FcPjFCooxpq18zs5RiJ07AWEk=; b=dSvrzRHav3zjjcSpQtbSqyDOAlE7jmhkhaeEkjppbt/es+rdCMp7NbId8bWi2i0Gmb Xf323jt7A+eA4NINumYHcOo5zfl98X8uVvw5gwPCwr01P5fHxj1l1Z4XTISg80iDuTbA hGoZV1c9ez1oyZAE5qQlxJhXFRGZynUokp/1lhBrXqfMw4JheD45OvltEuZQLkkkMvmi xu5vYSdq1be7MP4a1lv5+dRRALwsu+0+0D/mJ9LKICT9aaPmNW27em5k+LJvruPb0KxW rBa6OU7XIgQN2rA+z+L90JhTs9Fhg0NE5MrhyhwxXgKWOXIuLLHFd98P7l/xFpXnek6G n9ag== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1773061653; x=1773666453; 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=oP83KBWPdd/e5068I+FcPjFCooxpq18zs5RiJ07AWEk=; b=HGQfw32dPsPo17FlPuRV6fiw8y8pCDntm+LmDfxPEUv4Mz3qFyiFZfSVpus7837kqs GMYmpO03h4iCURGgMMWHEYmh7lscw8V0+AWE3C9GVNEngDrSp2+EDfiNCNlas94OlAL3 gkWQHdqknaKNK4dviEBENywZb3uL9OMKKjKLHzabj5VMf1O7QPktSzaKah5JSViOzKfL PfWaWKNF38ay+pOMWC2Pyr3txTmaQUM77kmGIImHu/uuqyehocJAaq0oFszwcxvL7cc8 HsOxhr7Ce7ciOqvR+TF66twmNdqp/l46CCXviApDx00vXJUt8mLIGrqbvi7AxBaSrGEO eqBw== X-Forwarded-Encrypted: i=1; AJvYcCWbtCzF87GIRJTWry87ZXf0Jh16+OZfRm5lTYBiSIY/phfdh5jhK0ZrQANxohvuapOfm4fTxSws26VxldAK@lists.postgresql.org X-Gm-Message-State: AOJu0YzLhVs4hz8gwrEgYhtcltSbNQcEErMVmEiVA3qvTFQOsi4ZKzp9 /QFkWE4c7C8cXAYsJa7ODAoeOHcEV+GYsgeAUOyUhOI3LjxpJCLVxsRbRvWHebC4NgHp44FdnxH Gs1XmLn30tvAcY64Zu3pJoiwa2SEVels= X-Gm-Gg: ATEYQzwqch6mtNNgW9zOd6V+4LBZKwQR7N7YvzgVs6Af2ynTSXGvzTAXArtH7fmgWa5 uaLkwjPppuKbfQOCJr9X3Aszmj6p0HmQQwNSH00YVrSG+arcYgEzM1xtncKclenAnSWtc3HUkol Elf1PvLUq8fCSMgmiLlZjxQI8DrIlAnd5v5l+1Sm3N0mEXi+MzdPuplGdnolqVrb1f8S3EY5auK S6BMJEd1wnB1q8ABTjXYqoGMNz0f2aYhhMrSmEPnmIOs2zIiQuFM6AL4ku1wIofnoZHMhauN8cc Hg/1KWwKcvlIyeoaKZJM2awKvxHnRScjd6epw/QP X-Received: by 2002:a05:6820:708f:20b0:679:c7d0:d4fe with SMTP id 006d021491bc7-67b9457e9fcmr7291065eaf.36.1773061653329; Mon, 09 Mar 2026 06:07:33 -0700 (PDT) MIME-Version: 1.0 References: <1164079167.6346688.1772982934392@mail.yahoo.com> <1656643760.2116109.1772994207524@mail.yahoo.com> In-Reply-To: From: Greg Sabino Mullane Date: Mon, 9 Mar 2026 09:06:56 -0400 X-Gm-Features: AaiRm50qDFbpcmk5nNcT1DVR0pPwjTr3qFQKVSvjXBm_24HcMwGkKsi8GtDtVXg Message-ID: Subject: Re: Unexpected deadlock across two separate rows, using Postgres 17 and Django's select_for_update() To: Shaheed Haque Cc: felix.quintgz@yahoo.com, pgsql-general@lists.postgresql.org Content-Type: multipart/alternative; boundary="00000000000002fbc1064c9716ed" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --00000000000002fbc1064c9716ed Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable 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 an= d > 2), so where does the multi-row bit come in? Is it because the two parent > 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 query= ? > 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 exists 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 values (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 --00000000000002fbc1064c9716ed Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
On Mon, Mar 9, 2026 at 5:55=E2=80=AFAM Sh= aheed Haque <shaheedhaque@gmai= l.com> wrote:
=
I'm not sure I understand. The two queries are refer= encing 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?=C2=A0Is it because the two = parent 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. Pro= cess B locks id 2, then attempts to lock id 1. Process A attempts to lock i= d 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 table= s need not be locked? Perhaps by omitting them from the query?
<= /div>

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

##= Run your program and get the deadlock error

alter= system reset log_statement;
select pg_reload_conf();
<= br>
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;

-- Pr= ocess B:
begin;
select * from t where id =3D 2 for upda= te;
select * from t where id =3D 1 for update; -- Hangs, waiting = for process A to finish

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

DEADLOCK!=

Logs:

2026-03-09 09:00:2= 3.885 EDT [242171] LOG: =C2=A0statement: alter system set log_statement =3D= 'all';
2026-03-09 09:00:25.804 EDT [242136] LOG: =C2=A0received= SIGHUP, reloading configuration files
2026-03-09 09:00:25.805 EDT [2421= 36] LOG: =C2=A0parameter "log_statement" changed to "all&quo= t;
2026-03-09 09:00:31.910 EDT [242171] LOG: =C2=A0statement: drop table= if exists t;
2026-03-09 09:00:36.649 EDT [242171] LOG: =C2=A0statement:= 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 EDT = [242171] LOG: =C2=A0statement: begin;
2026-03-09 09:00:50.788 EDT [24217= 1] LOG: =C2=A0statement: select * from t where id=3D1 for update;
2026-0= 3-09 09:00:59.755 EDT [242176] LOG: =C2=A0statement: begin;
2026-03-09 0= 9: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=A0statement: = select * from t where id=3D1 for update;
2026-03-09 09:01:14.278 EDT [24= 2171] LOG: =C2=A0statement: select * from t where id=3D2 for update;
202= 6-03-09 09:01:15.279 EDT [242171] ERROR: =C2=A0deadlock detected
2026-03= -09 09:01:15.279 EDT [242171] DETAIL: =C2=A0Process 242171 waits for ShareL= ock on transaction 15122348; blocked by process 242176.
=C2=A0 =C2=A0 = =C2=A0 =C2=A0 Process 242176 waits for ShareLock on transaction 15122347; b= locked by process 242171.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 Process 242171: se= lect * from t where id=3D2 for update;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 Proce= ss 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-0= 9 09:01:15.279 EDT [242171] CONTEXT: =C2=A0while locking tuple (0,2) in rel= ation "t"
2026-03-09 09:01:15.279 EDT [242171] STATEMENT: =C2= =A0select * from t where id=3D2 for update;


Cheers,
Greg

--
Enterprise Postgres Software Products & Tech = Support

--00000000000002fbc1064c9716ed--