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.94.2) (envelope-from ) id 1tyGlr-004UYp-6L for pgsql-general@arkaria.postgresql.org; Fri, 28 Mar 2025 20:57:43 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1tyGlp-00GcNq-K0 for pgsql-general@arkaria.postgresql.org; Fri, 28 Mar 2025 20:57:41 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1tyGlo-00GcNh-OY for pgsql-general@lists.postgresql.org; Fri, 28 Mar 2025 20:57:41 +0000 Received: from fout-a2-smtp.messagingengine.com ([103.168.172.145]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1tyGlm-001gVb-21 for pgsql-general@postgresql.org; Fri, 28 Mar 2025 20:57:39 +0000 Received: from phl-compute-10.internal (phl-compute-10.phl.internal [10.202.2.50]) by mailfout.phl.internal (Postfix) with ESMTP id AB9EB1382D03; Fri, 28 Mar 2025 16:57:37 -0400 (EDT) Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-10.internal (MEProxy); Fri, 28 Mar 2025 16:57:37 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=aklaver.com; h= cc:content-transfer-encoding:content-type:content-type:date:date :from:from:in-reply-to:in-reply-to:message-id:mime-version :references:reply-to:subject:subject:to:to; s=fm1; t=1743195457; x=1743281857; bh=CdjMCp+Hq2STfwymQBTH1R1sHVhwfzwst6IKpNkp/SE=; b= QqRMTfySMnRr9BSFyVdM9KScwbCRoC6zrASOsXIqTjm5nzjzJYz3bS7X5LC7oaZj t1ZgWQRRBljo9ZA7HLO0IcpWcDUfMppx8vtv6t8BpUcZynm9pkLGV0HtZ43+E8Ex DmtjDrWOHRJrEQi/gbEstvbdyx8pJ2SgDc3FAaFImBsb1uF/+PQhajrzA/lry+8X W23xS+Yw5M1SiLeCpyz6eLLiPmBTGoN3heKomgY1O/4X8lu7OWNRSF+oSLmxrnSR ISoSWF0wS5C41nwxTaNjt6Cvfx83nm9CjFkq9IMM0TBfvltAkO4+OAk6YiI2Nw31 tIwl1NQP6S0lFQGZYCKZkg== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-transfer-encoding:content-type :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to:x-me-proxy:x-me-sender :x-me-sender:x-sasl-enc; s=fm2; t=1743195457; x=1743281857; bh=C djMCp+Hq2STfwymQBTH1R1sHVhwfzwst6IKpNkp/SE=; b=Y6pQ9jopIT9TnKsXf vPv9hgs35yL/K/NB6MrMpyxV+dH6cwjlxD4uqVDl6lXTwzbVaLccJo4qPMTX56hf S+ZZSdQYsF29ng3/rGv1ZB3IxfK9Rm6EJtkPu+4Jh1o0q1FvalLFbQ5wJSMBGROa g57IH4f0IVrOj7t/2YXMhijFVaQGHupXwFLy76/NtAuQTcvmFV2jbfIVAbvXYiP0 pW7d9rN/ArAQe+EYGKEKaWeBnTBaTnV3IRYx4pKWBH3DxBfskMm+EnN4ZyC7fLAc jzjTgRIIcpfxckE7pyI85vWOx1XR9eMzqLKQSftCT36VbMKx6Wrjh2FNX4dL8wxE 8EsFA== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddujedvfeduucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdggtfgfnhhsuhgsshgtrhhisggv pdfurfetoffkrfgpnffqhgenuceurghilhhouhhtmecufedttdenucesvcftvggtihhpih gvnhhtshculddquddttddmnecujfgurhepkfffgggfuffvfhfhjggtgfesthekredttddv jeenucfhrhhomheptegurhhirghnucfmlhgrvhgvrhcuoegrughrihgrnhdrkhhlrghvvg hrsegrkhhlrghvvghrrdgtohhmqeenucggtffrrghtthgvrhhnpeelgeevkeekkeeuiefg tdevieeluefhfedufeetkeejffekjeeujeehgeehgeektdenucffohhmrghinhepphhosh htghhrvghsqhhlrdhorhhgnecuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehm rghilhhfrhhomheprggurhhirghnrdhklhgrvhgvrhesrghklhgrvhgvrhdrtghomhdpnh gspghrtghpthhtohepvddpmhhouggvpehsmhhtphhouhhtpdhrtghpthhtohepshgrshgr vhhilhhitgesghhmrghilhdrtghomhdprhgtphhtthhopehpghhsqhhlqdhgvghnvghrrg hlsehpohhsthhgrhgvshhqlhdrohhrgh X-ME-Proxy: Feedback-ID: i76984098:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Fri, 28 Mar 2025 16:57:37 -0400 (EDT) Message-ID: <0902ed04-9752-4c1d-97cd-81ca448d490a@aklaver.com> Date: Fri, 28 Mar 2025 13:57:36 -0700 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: Concurrent delete and insert on same key, potentially a bug To: Sasa Vilic , pgsql-general@postgresql.org References: Content-Language: en-US From: Adrian Klaver In-Reply-To: Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk On 3/28/25 12:38, Sasa Vilic wrote: > Hello, > > Please excuse my ignorance, because I might be missing something > obvious, but how is this not a bug? > > I simply open 2 terminals and connect to the same database. > > **TERMINAL 1:** > > sasa=# create table tst1(id int primary key); > CREATE TABLE > sasa=# insert into tst1(id) values(1); > INSERT 0 1 > sasa=# begin; > BEGIN > sasa=# select * from tst1 where id = 1; >  id > ---- >   1 > (1 row) > > **TERMINAL 2:** > > sasa=# begin; > BEGIN > sasa=# select * from tst1 where id = 1; >  id > ---- >   1 > (1 row) > > **TERMINAL 1:** > > sasa=# delete from tst1 where id = 1; > DELETE 1 > sasa=# insert into tst1(id) values(1); > INSERT 0 1 > > **TERMINAL 2:** > > sasa=# delete from tst1 where id = 1; > // client is blocked due to row lock/index lock > > **TERMINAL 1:** > > sasa=# commit; > COMMIT > > **TERMINAL 2:** > // resumes because terminal 1 committed transaction > DELETE 0 > sasa=# insert into tst1(id) values(1); > ERROR:  duplicate key value violates unique constraint "tst1_pkey" > DETAIL:  Key (id)=(1) already exists. > > To my understanding, the second client (terminal 2) should already see > changes from the first client, because the transaction isolation level > is READ COMMITTED and the first client did commit its transaction. From here: https://www.postgresql.org/docs/current/transaction-iso.html#XACT-READ-COMMITTED 13.2.1. Read Committed Isolation Level "UPDATE, DELETE, SELECT FOR UPDATE, and SELECT FOR SHARE commands behave the same as SELECT in terms of searching for target rows: they will only find target rows that were committed as of the command start time. However, such a target row might have already been updated (or deleted or locked) by another concurrent transaction by the time it is found. In this case, the would-be updater will wait for the first updating transaction to commit or roll back (if it is still in progress). If the first updater rolls back, then its effects are negated and the second updater can proceed with updating the originally found row. If the first updater commits, the second updater will ignore the row if the first updater deleted it, otherwise it will attempt to apply its operation to the updated version of the row. The search condition of the command (the WHERE clause) is re-evaluated to see if the updated version of the row still matches the search condition. If so, the second updater proceeds with its operation using the updated version of the row. In the case of SELECT FOR UPDATE and SELECT FOR SHARE, this means it is the updated version of the row that is locked and returned to the client." The: delete from tst1 where id = 1; started before you did the COMMIT in terminal 1 so the DELETE query ends up doing: "[...] If the first updater commits, the second updater will ignore the row if the first updater deleted it, [...]" Hence the DELETE 0 and the PK violation because the INSERT in terminal 1 already created a row with id=1. > > Thanks in advance! > > Kind Regards > Sasa Vilic > > > -- Adrian Klaver adrian.klaver@aklaver.com