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 1r8o52-00BoBH-AW for pgsql-hackers@arkaria.postgresql.org; Thu, 30 Nov 2023 20:56:17 +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 1r8o4z-0031M2-8S for pgsql-hackers@arkaria.postgresql.org; Thu, 30 Nov 2023 20:56:13 +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 1r8o4y-0031Lu-V9 for pgsql-hackers@lists.postgresql.org; Thu, 30 Nov 2023 20:56:12 +0000 Received: from out2-smtp.messagingengine.com ([66.111.4.26]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1r8o4u-008ADU-G6 for pgsql-hackers@postgresql.org; Thu, 30 Nov 2023 20:56:11 +0000 Received: from compute7.internal (compute7.nyi.internal [10.202.2.48]) by mailout.nyi.internal (Postfix) with ESMTP id A28155C0282; Thu, 30 Nov 2023 15:56:06 -0500 (EST) Received: from mailfrontend2 ([10.202.2.163]) by compute7.internal (MEProxy); Thu, 30 Nov 2023 15:56:06 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=anarazel.de; h= cc:cc:content-type:content-type:date:date:from:from:in-reply-to :message-id:mime-version:reply-to:sender:subject:subject:to:to; s=fm1; t=1701377766; x=1701464166; bh=hbKFa61i/YiJyCy5BtJbwQRwk QiJRNMcB/GPMReYwlg=; b=D8f5NBiRdsJ4e0u35m43SzP/BHx9s4i9se9Vj9J4w jX4erpoHji62yRY2OksTRVJzZSPF4B84myE81fSnJRYkaOHWLOjFYzila8ZOnF3e yxZ3tKV4parCCgded1bnzWyIXATwyiTitViUNklD0IXzY5QInkGNu5OhwQGl0TJw Gc0oNjjj0UpABua+i4Nc51LYyZf7Tn83eK/igJPf7HmpL/o6PSWEMhnq0PnJ5iFx W42iQFF7rAKNjWjWh8Hb6989fJ3/RCQt/CnkG2PGWQn+qblGAIHNhpSwHNBsyFhh VLuaVs+UZtzXWk7D6XlsXj8kLSsgIt0SdDO22XJwtIFeg== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-type: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= 1701377766; x=1701464166; bh=hbKFa61i/YiJyCy5BtJbwQRwkQiJRNMcB/G PMReYwlg=; b=ab88EivYWOsgSy4Bmh+GtOXlas2OGq5BFgG/nqm1gBHkPeuqOhn 9aOo3Pm5/3dYo7ISkrYC+xxi4Uyhju2g73mwG81DLSFzemWnpPURX0eWPQO+cfej 4bQgfPYnNUZ17XcI0YvaNIICw2EP+ZgQwEMvYHN1NmK0Jl9FQOYm1ykCjWltY5o7 w7gSCm27Yi9myZhuO2OMArNk5YYBppmvutFQ0RrMOMxTSQN52uuTin9NFnJUTFys iN0RVLUjTlrfV0DAZXtUqfUVCNEbhJ0VQp7FpVKLdBr/H30GosCYTQQgfOYXxpAq A7Zfl4wcU+9EzrHHHl4f8z/QfPmuiP+5Z9A== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvkedrudeijedgudegvdcutefuodetggdotefrod ftvfcurfhrohhfihhlvgemucfhrghsthforghilhdpqfgfvfdpuffrtefokffrpgfnqfgh necuuegrihhlohhuthemuceftddtnecusecvtfgvtghiphhivghnthhsucdlqddutddtmd enucfjughrpeffhffvvefukfggtggusehttdertddttddvnecuhfhrohhmpeetnhgurhgv shcuhfhrvghunhguuceorghnughrvghssegrnhgrrhgriigvlhdruggvqeenucggtffrrg htthgvrhhnpeejffetueefiedvkeeuueekgeehleduudevteekheduleeutdduleeiffet gfdtffenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhrohhmpe grnhgurhgvshesrghnrghrrgiivghlrdguvg X-ME-Proxy: Feedback-ID: id4a34324:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Thu, 30 Nov 2023 15:56:06 -0500 (EST) Date: Thu, 30 Nov 2023 12:56:05 -0800 From: Andres Freund To: pgsql-hackers@postgresql.org, Robert Haas , David Steele , Heikki Linnakangas Cc: Michael Paquier Subject: Detecting some cases of missing backup_label Message-ID: <20231130205605.slaaw2ny5sjmukn3@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 recently mentioned to Robert (and also Heikki earlier), that I think I see a way to detect an omitted backup_label in a relevant subset of the cases (it'd apply to the pg_control as well, if we moved to that). Robert encouraged me to share the idea, even though it does not provide complete protection. The subset I think we can address is the following: a) An omitted backup_label would lead to corruption, i.e. without the backup_label we won't start recovery at the right position. Obviously it'd be better to also catch a wrong procedure when it'd not cause corruption - perhaps my idea can be extended to handle that, with a small bit of overhead. b) The backup has been taken from a primary. Unfortunately that probably can't be addressed - but the vast majority of backups are taken from a primary, so I think it's still a worthwhile protection. Here's my approach 1) We add a XLOG_BACKUP_START WAL record when starting a base backup on a primary, emitted just *after* the checkpoint completed 2) When replaying a base backup start record, we create a state file that includes the corresponding LSN in the filename 3) On the primary, the state file for XLOG_BACKUP_START is *not* created at that time. Instead the state file is created during pg_backup_stop(). 4) When replaying a XLOG_BACKUP_END record, we verif that the state file created by XLOG_BACKUP_START is present, and error out if not. Backups that started before the redo LSN from backup_label are ignored (necessitates remembering that LSN, but we've been discussing that anyway). Because the backup state file on the primary is only created during pg_backup_stop(), a copy of the data directory taken between pg_backup_start() and pg_backup_stop() does *not* contain the corresponding "backup state file". Because of this, an omitted backup_label is detected if recovery does not start early enough - recovery won't encounter the XLOG_BACKUP_START record and thus would not create the state file, leading to an error in 4). It is not a problem that the primary does not create the state file before the pg_backup_stop() - if the primary crashes before pg_backup_stop(), there is no XLOG_BACKUP_END and thus no error will be raised. It's a bit odd that the sequence differs between normal processing and recovery, but I think that's nothing a good comment couldn't explain. I haven't worked out the details, but I think we might be able extend this to catch errors even if there is no checkpoint during the base backup, by emitting the WAL record *before* the RequestCheckpoint(), and creating the corresponding state file during backup_label processing at the start of recovery. That'd probably make the logic for when we can remove the backup state files a bit more complicated, but I think we could deal with that. Comments? Swear words? Greetings, Andres Freund