Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1pUzzn-0005z6-8T for pgsql-hackers@arkaria.postgresql.org; Thu, 23 Feb 2023 01:02:04 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1pUzzl-0007kE-TN for pgsql-hackers@arkaria.postgresql.org; Thu, 23 Feb 2023 01:02:01 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1pUzzk-0007k4-B3 for pgsql-hackers@lists.postgresql.org; Thu, 23 Feb 2023 01:02:01 +0000 Received: from wout4-smtp.messagingengine.com ([64.147.123.20]) by makus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1pUzzc-0008VC-N9 for pgsql-hackers@postgresql.org; Thu, 23 Feb 2023 01:01:59 +0000 Received: from compute5.internal (compute5.nyi.internal [10.202.2.45]) by mailout.west.internal (Postfix) with ESMTP id 4B267320046E; Wed, 22 Feb 2023 20:01:50 -0500 (EST) Received: from mailfrontend1 ([10.202.2.162]) by compute5.internal (MEProxy); Wed, 22 Feb 2023 20:01:50 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=anarazel.de; h= cc:cc:content-type:date:date:from:from:in-reply-to:message-id :mime-version:reply-to:sender:subject:subject:to:to; s=fm1; t= 1677114109; x=1677200509; bh=jY11q1CSLM0VH0xLj17LAneKD9Kro2ho5/T q5au1I24=; b=kgQqlynYtQ6CDM25a7UHtTJj+LXfM45SZtrivb6grvwM+XgD7M4 q44Yuu8SsYtQKHcWLWCYQ3sQ21Utpj8VKhT00Uql5xIIfz4jNLdRakNDkfvPIW1K wl7jPazLE8LgyM8KmG+/1F0acytJR4gFRdH9gxabKS9+eFxv9vuMj7w29ScyfL/0 3dAolfA/bHfEurD6mSRX7nDeSL23CR5MP0/iF9vbYEGzuavowT19KLCDpPHl9BpM FFazhcvbmC2udiq4/B05UQVts2CiqFTP31YXyJpOQaaKVbWR7VNvBBy1CavROXlE OrBrC+46ZIeSYDLWenKyWWZxZZ50AfRi/Dg== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-type:date:date:feedback-id :feedback-id:from:from:in-reply-to:message-id:mime-version :reply-to:sender:subject:subject:to:to:x-me-proxy:x-me-proxy :x-me-sender:x-me-sender:x-sasl-enc; s=fm1; t=1677114109; x= 1677200509; bh=jY11q1CSLM0VH0xLj17LAneKD9Kro2ho5/Tq5au1I24=; b=F LSBRxJu3WzH3q1Ebu24Ie9I8rtA/tGlFBSZZLAm81B/fqnzbqcLQv6arCZJMxWS8 IqewwW2iTIEk8ZoQDQ1BBVWG1JPUAgdxgnnkBGTSS7oXgrgI4jFj8UKrRLLQNGcx ZR68r6hbkwhBNBNef5FBlpK9nFPHUOxQk2rSiGucWhOupFAyBfl7iUw80IMwgCyb XKneQGxXBlWpa/h5qmP4rjLxycyaxWkvhjh4ypjVn1U1yeyv4OFIzeBMyLW3Mc3h Jrtu+M/1NwHlaGDCH6HIfDkUHs/kOF5LMCaneax3AecbY/10cTt5ID4umvH37u0Y t7G9NyhoeqP8y1syFYlVw== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvhedrudektddgvdekucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne cujfgurhepfffhvfevuffkgggtugesthdtredttddtvdenucfhrhhomheptehnughrvghs ucfhrhgvuhhnugcuoegrnhgurhgvshesrghnrghrrgiivghlrdguvgeqnecuggftrfgrth htvghrnhepkefhteeifedtfedvtdfgteefjeegvedvjeelkeetleelfeeukefhuedtjedv ffejnecuffhomhgrihhnpehpohhsthhgrhgvshhqlhdrohhrghenucevlhhushhtvghruf hiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhrohhmpegrnhgurhgvshesrghnrghrrgii vghlrdguvg X-ME-Proxy: Feedback-ID: id4a34324:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 22 Feb 2023 20:01:49 -0500 (EST) Date: Wed, 22 Feb 2023 17:01:47 -0800 From: Andres Freund To: Heikki Linnakangas , Tom Lane Cc: vignesh C , pgsql-hackers@postgresql.org, Thomas Munro , Melanie Plageman , Yura Sokolov , Robert Haas Subject: XLogReadBufferExtended() vs disconnected segments Message-ID: <20230223010147.32oir7sb66slqnjk@awork3.anarazel.de> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk Hi, I was trying to implement ExtendRelationBufferedTo(), responding to a review comment by Heikki, in https://www.postgresql.org/message-id/20230222203152.rh4s75aedj65hyjn@awork3.anarazel.de Which lead me to stare at the P_NEW do while loop in XLogReadBufferExtended(). I first started to reply on that thread, but it seems like a big enough issue that it seemed worth starting a separate thread. The relevant logic was added in 6f2aead1ffec, the relevant discussion is at https://www.postgresql.org/message-id/32313.1392231107%40sss.pgh.pa.us My understanding of what happend there is that we tried to extend a relation, sized one block below a segment boundary, and after that the relation was much larger, because the next segment file existed, and had a non-zero size. And because we extended blkno-lastblock times, we'd potentially blow up the relation size much more than intended. The actual cause of that in the reported case appears to have been a bug in wal-e. But I suspect it's possible to hit something like that without such problems, just due to crashes on the replica, or "skew" while taking a base backup. I find it pretty sketchy that we just leave the contents of the previously "disconnected" segment contents around, without using log_invalid_page() for the range, or warning, or ... Most of the time this issue would be fixed due to later WAL replay initializing the later segment. But I don't think there's a guarantee for that (see below). It'd be one thing if we accidentally used data in such a segment, if the situation is only caused by a bug in base backup tooling, or filesystem corruption, or ... But I think we can encounter the issue without anything like that being involved. Imagine this scenario: 1) filenode A gets extended to segment 3 2) basebackup starts, including performing a checkpoint 3) basebackup ends up copying A's segment 3 first, while in progress 4) filenode A is dropped 5) checkpoint happens, allowing smgrrel 10 to be used again 6) filenode 10 is created newly 7) basebackup ends At that point A will have segment 0, segment 3. The WAL replay for 4) won't drop segment 3, because an smgrnblocks() won't even see it, because segment 2 doesn't exist. If a replica starts from this base backup, we'll be fine until A again grows far enough to fill segment 2. At that point, we'll suddenly have completely bogus contents in 3. Obviously accesses to those contents could trivially crash at that point. I suspect there's an easier to hit version of this: Consider this path in ExecuteTruncateGuts(): /* * Normally, we need a transaction-safe truncation here. However, if * the table was either created in the current (sub)transaction or has * a new relfilenumber in the current (sub)transaction, then we can * just truncate it in-place, because a rollback would cause the whole * table or the current physical file to be thrown away anyway. */ if (rel->rd_createSubid == mySubid || rel->rd_newRelfilelocatorSubid == mySubid) { /* Immediate, non-rollbackable truncation is OK */ heap_truncate_one_rel(rel); Afaict that could easily lead to a version of the above that doesn't even require relfilenodes getting recycled. One way to to defend against this would be to make mdextend(), whenever it extends into the last block of a segment, unlink the next segment - it can't be a validly existing contents. But it seems scary to just unlink entire segments. Greetings, Andres Freund