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 1wp1Fn-001NgV-0o for pgsql-hackers@arkaria.postgresql.org; Wed, 29 Jul 2026 10:11:11 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wp1Fm-0056sT-0F for pgsql-hackers@arkaria.postgresql.org; Wed, 29 Jul 2026 10:11:10 +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 1wp1Fl-0056sK-2V for pgsql-hackers@lists.postgresql.org; Wed, 29 Jul 2026 10:11:09 +0000 Received: from mail-pf1-x42c.google.com ([2607:f8b0:4864:20::42c]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1wp1Fj-00000000thp-19X4 for pgsql-hackers@postgresql.org; Wed, 29 Jul 2026 10:11:09 +0000 Received: by mail-pf1-x42c.google.com with SMTP id d2e1a72fcca58-8485b358552so842954b3a.2 for ; Wed, 29 Jul 2026 03:11:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785319865; x=1785924665; darn=postgresql.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to:content-type; bh=n1brFu543Z4yRja0VbjFnbjo5FLWpUsx2T7BPWH8Y3c=; b=YOIGM3rDyk5NorTSoG7bFSpuVTMrhn/IQihSQHz/cH16KS4skbtgIOYxn85WcEfWFP cIpLkCTve3yQKzh43KQbTh9/y8ljxdQ0Yo8H1lC+JpdL8DpcoSb/NLVVBf0TE6O1ajOC pZW9G378bIrssqJxk6dUsEITGlUWR1KXhH619zcfiDjLxxaH27fxJVT3GIIXXffJ9w3d CtwY7Q/E3xoZELb0SDfR5oVaBve8a/2Vlq31g/gFrWo3kUiTQqca4i/uVCYKHY9xpRtw 57rzjbFybQtiEyJ68XvTXtVikz7zOIwrNXjl9kw8TQgy1kqzI7DyjyJ6pFI7HKnc9jk7 aeng== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785319865; x=1785924665; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=n1brFu543Z4yRja0VbjFnbjo5FLWpUsx2T7BPWH8Y3c=; b=pt91czfOBQWRk/ebHDBf5hEaE5zOCLpQX2g2CSEpA0AdTXJl8BRW10UJRPpz8okV48 YfyDk7N7I3aBkGyBvRx66wrh3vYfsDTR0ImRsLFknKaFryt1aFG4LhQGBS2cjVzqun3c NsqWJIIEpTF3YzCWXr0vHe8awkFNM+5HiZ8/ohPod5/5KJiPgedt8M/rMRfXY7n3irtL 97Eq1WbHQxrQSYlv8KxZ6oI0+0/WSdTZJddQx8ZrkZ8OG7lO3o1UZR6kb7eweMbOqHM9 obyCK2IILhFWC0hbigNMIo11JXUIJH1ynpRU6S+SHfVkEJi4PUvPSY5PFzEh6+ahhKRH JxSQ== X-Gm-Message-State: AOJu0YwsYUj9KHBEgLJ3NylJwI2ZIk6+d+5MM9mxcMSJA9+HFgXfX3oT 3OcnRznL/ZbB4Zh7vyuGHhpJ09eX8M5MGZ5tJXRVscEZ/yW8Zj2sTgZ5hz24Ye5k X-Gm-Gg: AR+sD12CZm+5/maLPmHc5tbp6iafng62LI1QaRLgkRns53ebCuLC662EOP+Io7oW+NU qPIF5jjWCEXtf3qYmlCtCWAVXcymJjI190zAitoF9PnFJi11Eo3ym1TPH6tBy6WwDEZqYasiU7G O47AkVBoxFjSU0OVDAy+Lbkj8JS/p3JD3brFRzICmBtNE6UnKO9fruBJrsAEpa0FHs0Igp/S5xA /B3Y45evb0VMXSjAnGKQ7uMLJlcSSkPRBLZtDon1n+hbZJcGEHeTSr1CvVevVAcqYaKl0RE2Q+N 0smpoWPcN89a95RgNRXgblBZEnllvZ8xkHLVvV0ilsDe8LrouWESDrw2tdkAvU/FhIecD8NARgS UHzpNkNIp1Fl1POzwl6LrpJOE1N+28OZLZxNT78B40xj5Tbq2yEx8UDm4zQFQ6t+6aJGoG05xdP kBbEZzn+p3VDOAsQE+p3zXQvOFrbCOqUiGdrv8uZFao4O+q/lDoza5suHbkg== X-Received: by 2002:a05:6a20:258e:b0:3b4:b2d7:c146 with SMTP id adf61e73a8af0-3c8aaf8d2fbmr7593007637.21.1785319864606; Wed, 29 Jul 2026 03:11:04 -0700 (PDT) Received: from redmi.lan ([45.32.69.234]) by smtp.gmail.com with ESMTPSA id a92af1059eb24-13e72735084sm7822212c88.12.2026.07.29.03.11.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 29 Jul 2026 03:11:04 -0700 (PDT) From: "chee.wooson" To: pgsql-hackers@postgresql.org Cc: "chee.wooson" Subject: [PATCH v1 0/2] Fix exported snapshot xmin handoff race Date: Wed, 29 Jul 2026 18:10:43 +0800 Message-ID: <20260729101045.422679-1-chee.wooson@gmail.com> X-Mailer: git-send-email 2.43.0 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk Hi, ProcArrayInstallImportedXmin() holds ProcArrayLock in shared mode while it checks that the exporting transaction is still running and installs the imported xmin. That normally prevents transaction end from removing the source from the proc array. However, a read-only exporting transaction has no assigned XID, so ProcArrayEndTransaction() clears its xmin without ProcArrayLock. This permits a horizon scan holding ProcArrayLock shared to pass the importer before the imported xmin is installed. The importer can then validate the source under another shared lock, while the source concurrently clears its xmin lock-free. When the horizon scan reaches the source, it can therefore miss both xmins and allow VACUUM to remove a tuple that is still visible to the imported snapshot. Tom Lane described the same general hazard in 2016: https://postgr.es/m/6078.1478985619@sss.pgh.pa.us That discussion suspected it might not be a live bug because an exported snapshot keeps the source xmin until transaction end. The missing case is that an XID-less source can clear xmin at transaction end even while another backend holds ProcArrayLock shared. This series adds a dedicated atomic three-state field to PGPROC. Snapshot export publishes EXPORTED before the snapshot file is made visible. An importer changes EXPORTED to REFERENCED before installing the xmin. At transaction end, an unreferenced export retains the existing lock-free cleanup path, while a referenced export acquires ProcArrayLock exclusively before clearing xmin. If transaction end changes EXPORTED to ENDING first, the importer fails safely. Patch 1 adds deterministic injection points and a TAP test. With patch 1 alone, the test reproduces the bug by failing both the expected lock wait and the final visibility check. Patch 2 implements the atomic handoff; all five subtests then pass. The test also covers the source-wins case, repeated exports in one transaction, and the lock-free path for an export that was never imported. The series is based on PostgreSQL master at c12c101b0846b1e6488f2dc986a852fbc6bf2e3b. I also reproduced the issue on REL_17_STABLE. Validation on current master: - ninja -C build - meson test -C build test_misc/015_export_snapshot - meson test -C build --suite regress --suite isolation --suite injection_points --suite test_misc - pgindent --check on all modified C and header files All tests pass. Performance impact is limited to one four-byte atomic field per PGPROC, an atomic read when an XID-less transaction ends, and state transitions during snapshot export/import. ProcArrayLock exclusive is added only when an imported snapshot references the ending XID-less source. I have not run a dedicated performance benchmark. chee.wooson (2): Add test for exported snapshot xmin race Fix exported snapshot xmin handoff race src/backend/access/transam/twophase.c | 1 + src/backend/storage/ipc/procarray.c | 99 ++++- src/backend/storage/lmgr/proc.c | 4 + src/backend/utils/time/snapmgr.c | 13 + src/include/storage/proc.h | 21 + src/test/modules/test_misc/meson.build | 1 + .../test_misc/t/015_export_snapshot.pl | 394 ++++++++++++++++++ 7 files changed, 525 insertions(+), 8 deletions(-) create mode 100644 src/test/modules/test_misc/t/015_export_snapshot.pl -- 2.43.0