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 1upRCC-0058i6-TM for pgsql-hackers@arkaria.postgresql.org; Fri, 22 Aug 2025 12:48:42 +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 1upRCB-005rAD-7C for pgsql-hackers@arkaria.postgresql.org; Fri, 22 Aug 2025 12:48:39 +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.94.2) (envelope-from ) id 1upRCA-005r9L-Pd for pgsql-hackers@lists.postgresql.org; Fri, 22 Aug 2025 12:48:39 +0000 Received: from mail-vk1-xa35.google.com ([2607:f8b0:4864:20::a35]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1upRC8-001H4O-38 for pgsql-hackers@postgresql.org; Fri, 22 Aug 2025 12:48:39 +0000 Received: by mail-vk1-xa35.google.com with SMTP id 71dfb90a1353d-53b174ac3e5so748629e0c.2 for ; Fri, 22 Aug 2025 05:48:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1755866915; x=1756471715; darn=postgresql.org; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=9Ny+b4WLTFZCCQSVi2jvZSGAnflynY1whxd8jeTtKmM=; b=EyV8MTWO5vUpZO1dhS7BoprbY1zAYun27Sg6URwtFb+BwgayKTBVUiCVKPwm7naMQY nLojblsWWrlodHyD+V9e85T/CdkKRTmWnwBcCC8FQusGGt9IEoVBTKqpkBHrcY6iqtyI dIqvgSk25udJPb6faDJTAWKEVMc4xmgO0i2cKvQHhIfKbI0qvJl4aj1Uv2+A2QlJZ3XH WtxG0gy40J61v4FzUwEsIWoSOcqllWTueWlnHgtOAsMp6nAL44l5Xgbg74lQYje0eKsU c8ZH7SIWm08uA8DKezxHj6Gzjncq0wLAh9d251gmrynNzcxVPAlNFG6U1X0QHIbI29ew J90w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1755866915; x=1756471715; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=9Ny+b4WLTFZCCQSVi2jvZSGAnflynY1whxd8jeTtKmM=; b=U/13r2NN3RvkJPb3xksroG596TchVdazBxV90hN4pPGLcSZ+LWoaz1y437qL6WCYQ8 N9+8PFfc55qLrdqsRyQIx1f20OAyS13TaDIgMOAikk3gmeEPIUCSBgO4KZ1i18DRX67P Jq7Cn79wvb4cTAxoFA1r/aAWz93vaKVHO3OCPxrYMANtHr7Y31eY5o35YGSMxpnzlluW M5VvdjbL9kbI/F2q92uWjkbX1nornsP1VjBJD/9aIpZ42SDdHnEOwVSuF5VODqqI96Qs 6w+wmHekQXzSYozU65WpVLR4hw+HF1jmg/YmDyaFt2XOSLsg5rk0j/91Ev7OA9wyMPxv MW7Q== X-Forwarded-Encrypted: i=1; AJvYcCVMkZIZ9PfoGGGu6FHiIPuKwop/1uYbdEqSGyE6L4qergZaA7Ly6XwffMfY3yH8EtYHPCNRrwLHCfhwJefB@postgresql.org X-Gm-Message-State: AOJu0YzC9/FL6D0qez0DoGd0gPoMUq4jNgj0/7HunIDwgLMtBGbVtphj o7i4F7bOpnptcQMGwovLmw22Rki9ketUoQ+gh3NxF3aVJ59+OlR/PKWlLEqdgipmMapG0tp/SR6 kw+DdBcW2Pi/6rFRBbj9y35jjyEGmjZI= X-Gm-Gg: ASbGncuoe2ZL2OECnfHCS80eUjwTq27TDWS0rIQDyTDTd0D6RW2o+UmhClN6WDmCkfe sTv2mFvrIcOW9mSxCy9yvXPfz/nat2O+1+3WVcH2IOF+FeVtX4tXXhISikWI0g8ywqSQRC/8Lab PInPNS5sMcRJnuFzCs+WZcU3iCRhPbZczBs3E2BYyT4kl2U58lg2IwuG8lsGrLDS1JJqtpPglKF 7IovcDe X-Google-Smtp-Source: AGHT+IFt6kpsJUyMEF0dL6DGQ2mf31SCe9K0iCdGI9lsHMcjRveUsu60eW7fZeuWHI0VM3x7+ACQejH20/OrQlvaSRE= X-Received: by 2002:a05:6122:2008:b0:52f:47de:3700 with SMTP id 71dfb90a1353d-53c8a2daae4mr725625e0c.5.1755866914744; Fri, 22 Aug 2025 05:48:34 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: From: Mihail Nikalayeu Date: Fri, 22 Aug 2025 14:47:57 +0200 X-Gm-Features: Ac12FXwk-b3Oz0K7RIkbxV8ZnlRmPTO6oTDiaGnEfyhJDbhXUkso3G3gLXskJHI Message-ID: Subject: Re: [BUG?] check_exclusion_or_unique_constraint false negative To: Amit Kapila Cc: "Zhijie Hou (Fujitsu)" , Peter Geoghegan , "Hayato Kuroda (Fujitsu)" , PostgreSQL Hackers , Andres Freund Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk Hello, Amit, > IIUC, the problem you are worried about can happen with DELETE+INSERT It seems there was some misunderstanding due to my bad explanation and word= ing. I wrote "A concurrent transaction deletes a tuple and inserts a new one with a different TID" - but I mean logical UPDATE causing new TID in index page appear because HOT was applied... Lets try again, I hope that explanation is better: At the start, we have a table with a primary key and one extra index (to disable HOT), and a tuple with i=3D13: CREATE TABLE table (i int PRIMARY KEY, data text); CREATE INDEX no_more_hot_data_index ON table (data); INSERT INTO table (i, data) VALUES (13, 'data'); A btree scan using SnapshotDirty can miss tuples because of internal locking logic. Here=E2=80=99s how the bug shows up: 1) we have a tuple in the index (i=3D13), committed long ago 2) transaction A starts an index search for that tuple using SnapshotDirty (WHERE i =3D 13) 3) in parallel, transaction B updates that tuple (SET data=3D'updated' WHERE i=3D13) and commits (creating a new index entry because HOT is not applied) 4) the scan from step 2 returns nothing at all - as if the tuple never exis= ted In other words, if you start a SnapshotDirty btree scan for i=3D13 and update that row i=3D13 at the same physical moment, the scan may: * return the TID of the pre=E2=80=91update version - correct behavior * return the TID of the post=E2=80=91update version - also correct * return nothing - this is the broken case More broadly: any SnapshotDirty scan may completely miss existing data when there are concurrent updates. SnapshotDirty usage in Postgres is limited, so the impact isn=E2=80=99t hug= e, but every case I found is reproducible with the tests from the first commit from v10 in my previous email. * check_exclusion_or_unique_constraint: only a minor performance impact, handled by retry logic * logical replication TAP tests: multiple scenarios fail because RelationFindReplTupleByIndex cannot find existing committed tuples These scenarios look like: 1) logical replication tries to apply a change for tuple X received from the publisher 2) meanwhile, the subscriber updates the same tuple X and commits in parallel transaction 3) due to the bug, RelationFindReplTupleByIndex concludes the tuple X does not exist at all, leading to bad outcomes, including: * incorrect conflict=E2=80=91type messages (and, in the future, potentially wrong conflict=E2=80=91resolution choices) * lost updates (see scenario 2 from [0]) If you look at the tests and play with the $simulate_race_condition flag, you can see the behavior directly. The second commit (a possible fix) in v10 also includes documentation updates that try to explain the issue in a more appropriate context. I=E2=80=99m happy to provide additional reproducers or explanations if that= would help. [0]: https://www.postgresql.org/message-id/flat/CADzfLwWC49oanFSGPTf%3D6FJo= Tw-kAnpPZV8nVqAyR5KL68LrHQ%40mail.gmail.com#5f6b3be849f8d95c166decfae541df0= 9 Best regards, Mikhail.