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 1nJU6C-00031l-HN for pgsql-hackers@arkaria.postgresql.org; Mon, 14 Feb 2022 05:40:32 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1nJU6B-00018Q-8I for pgsql-hackers@arkaria.postgresql.org; Mon, 14 Feb 2022 05:40:31 +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 1nJU6A-00018G-P6 for pgsql-hackers@lists.postgresql.org; Mon, 14 Feb 2022 05:40:30 +0000 Received: from mail-pl1-x633.google.com ([2607:f8b0:4864:20::633]) by makus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1nJU67-0001jq-V8 for pgsql-hackers@lists.postgresql.org; Mon, 14 Feb 2022 05:40:29 +0000 Received: by mail-pl1-x633.google.com with SMTP id j4so9684011plj.8 for ; Sun, 13 Feb 2022 21:40:27 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=date:message-id:to:cc:subject:from:in-reply-to:references :user-agent:mime-version:content-transfer-encoding; bh=T8ESUr79KaQ+wDHfbGR0dkXprCehPekuMg5ovrxGHvk=; b=bwxBwFDyWnMFCC65mYl+VCDYjL4J/rv0zKYNopOb+y5yApnj/6iGj5aEN0lY+uTdAB KIdZjdiarq1Aaxs36UZEYRQ9Z9DIJGfJLIIx2ASiibmVg4XQe0HM04FU+3tna4c1p9Cy ELjmgP3uirVsIWrlRK95WI4h4LbURgr7VkRaFiUoTeTiVhi/+5S7eAJHu8a0sPC/TllD fxGYtoLz/JRJRR1XdRE3FWBRgV4QbXyNB3hQEinatb4BtRAn6gbPDx9jnQsNBRs3GO2Y zDnHz3M7nnQYR6ZJXkZWKhB5Ti9/vP3AJSqMzoUZGTi0uTizH+wE+G2HffJNTyhM4zdN 8K/Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:date:message-id:to:cc:subject:from:in-reply-to :references:user-agent:mime-version:content-transfer-encoding; bh=T8ESUr79KaQ+wDHfbGR0dkXprCehPekuMg5ovrxGHvk=; b=pu/e/Xby4392HuleI6tCPEc70YBOMd/J8ZGbS6riZOgNovz5CVyeNP09/YnSqI5cPW PtFqVXSSafIY7gvE69l/9tFFHVDiMDx0n4KfXzhTOp9igAvanps6WyGD4q5zgqL8E0tv 0eE7LYFdI013Kr7IlagHDA2trsgihIPyty9xGM+r5HAklNITa6NUj30wXNZoEG1UWLBx F5EtV8ntMBcpboy6N7QoNRZJMum0N7DS5u49Plx9vpCHeZoQcSWNO7wgQsQTjhUYjcAk vY82x3D+z04/aCd+pLc24Bp2DzIQVVktj1Jq/dt6+RhJxGMrkoT2U43iLt8WDb2EGtRX ZdSg== X-Gm-Message-State: AOAM533uPymzlMTEPvqPOTRiv4eewia26h7pvhOm3t+eNZycHCIR2ADd WxEM0iYPYjWenqm//AsvShs= X-Google-Smtp-Source: ABdhPJxnC+TQ5R44ALBPXmTHgQpytS1TQHf1HLUR+4x3EoiZ5NQYKraipqg3ZuOtllnTovdmBDks4g== X-Received: by 2002:a17:90a:680d:: with SMTP id p13mr12738946pjj.39.1644817226620; Sun, 13 Feb 2022 21:40:26 -0800 (PST) Received: from localhost (KD036014041111.ppp-bb.dion.ne.jp. [36.14.41.111]) by smtp.gmail.com with ESMTPSA id 13sm21636917pfx.122.2022.02.13.21.40.23 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Sun, 13 Feb 2022 21:40:25 -0800 (PST) Date: Mon, 14 Feb 2022 14:40:22 +0900 (JST) Message-Id: <20220214.144022.2233054833762514893.horikyota.ntt@gmail.com> To: masao.fujii@oss.nttdata.com Cc: bharath.rupireddyforpostgres@gmail.com, nathandbossart@gmail.com, sfrost@snowman.net, bossartn@amazon.com, rjuju123@gmail.com, michael@paquier.xyz, pgsql-hackers@lists.postgresql.org Subject: Re: Add checkpoint and redo LSN to LogCheckpointEnd log message From: Kyotaro Horiguchi In-Reply-To: References: <20220209.115204.1794224638476710282.horikyota.ntt@gmail.com> User-Agent: Mew version 6.8 on Emacs 26.1 Mime-Version: 1.0 Content-Type: Multipart/Mixed; boundary="--Next_Part(Mon_Feb_14_14_40_22_2022_983)--" Content-Transfer-Encoding: 7bit List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk ----Next_Part(Mon_Feb_14_14_40_22_2022_983)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit At Fri, 11 Feb 2022 01:00:03 +0900, Fujii Masao wrote in > > > On 2022/02/09 11:52, Kyotaro Horiguchi wrote: > > 0001: The fix of CreateRestartPoint > > This patch might be ok for the master branch. But since concurrent > checkpoint and restartpoint can happen in v14 or before, we would need > another patch based on that assumption, for the backport. How about > fixing the bug all the branches at first, then apply this patch in the > master to improve the code? For backbranches, the attached for pg14 does part of the full patch. Of the following, I think we should do (a) and (b) to make future backpatchings easier. a) Use RedoRecPtr and PriorRedoPtr after they are assigned. b) Move assignment to PriorRedoPtr into the ControlFileLock section. c) Skip udpate of minRecoveryPoint only when the checkpoint gets old. d) Skip call to UpdateCheckPointDistanceEstimate() when RedoRecPtr <= PriorRedoPtr. # Mmm. The v9-0001 contains a silly mistake here.. Still I'm not sure whether that case really happens and how checkpoint behaves *after* that happenes, but at least it protects database from the possible unrecoverable state due to the known issue here.. It doesn't apply even on pg13 (due to LSN_FORMAT_ARGS). I will make the per-version patches if you are fine with this. regards. regards. -- Kyotaro Horiguchi NTT Open Source Software Center ----Next_Part(Mon_Feb_14_14_40_22_2022_983)-- Content-Type: Text/X-Patch; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="v2-0001-Correctly-update-contfol-file-at-the-end-of-archi.patch" From df516b487617c570332ea076b91773b810629a11 Mon Sep 17 00:00:00 2001 From: Kyotaro Horiguchi Date: Mon, 14 Feb 2022 13:04:33 +0900 Subject: [PATCH v2] Correctly update contfol file at the end of archive recovery CreateRestartPoint runs WAL file cleanup basing on the checkpoint just have finished in the function. If the database has exited DB_IN_ARCHIVE_RECOVERY state when the function is going to update control file, the function refrains from updating the file at all then proceeds to WAL cleanup having the latest REDO LSN, which is now inconsistent with the control file. As the result, the succeeding cleanup procedure overly removes WAL files against the control file and leaves unrecoverable database until the next checkpoint finishes. Since all buffers have flushed out, we may safely regard that restart point established regardless of the recovery state. Thus we may and should update checkpoint LSNs of checkpoint file in that case. Still we update minRecoveryPoint only during archive recovery but explicitly clear if we have exited recovery. Addition to that fix, this commit makes some cosmetic changes that consist with the changes we are going to make on the master branch. --- src/backend/access/transam/xlog.c | 81 ++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 28 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 6208e123e5..28c3c4b7cf 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -9653,7 +9653,7 @@ CreateRestartPoint(int flags) /* Also update the info_lck-protected copy */ SpinLockAcquire(&XLogCtl->info_lck); - XLogCtl->RedoRecPtr = lastCheckPoint.redo; + XLogCtl->RedoRecPtr = RedoRecPtr; SpinLockRelease(&XLogCtl->info_lck); /* @@ -9672,7 +9672,10 @@ CreateRestartPoint(int flags) /* Update the process title */ update_checkpoint_display(flags, true, false); - CheckPointGuts(lastCheckPoint.redo, flags); + CheckPointGuts(RedoRecPtr, flags); + + /* Update pg_control */ + LWLockAcquire(ControlFileLock, LW_EXCLUSIVE); /* * Remember the prior checkpoint's redo ptr for @@ -9681,52 +9684,74 @@ CreateRestartPoint(int flags) PriorRedoPtr = ControlFile->checkPointCopy.redo; /* - * Update pg_control, using current time. Check that it still shows - * DB_IN_ARCHIVE_RECOVERY state and an older checkpoint, else do nothing; - * this is a quick hack to make sure nothing really bad happens if somehow - * we get here after the end-of-recovery checkpoint. + * Update pg_control, using current time if no later checkpoints have been + * performed. */ - LWLockAcquire(ControlFileLock, LW_EXCLUSIVE); - if (ControlFile->state == DB_IN_ARCHIVE_RECOVERY && - ControlFile->checkPointCopy.redo < lastCheckPoint.redo) + if (PriorRedoPtr < RedoRecPtr) { ControlFile->checkPoint = lastCheckPointRecPtr; ControlFile->checkPointCopy = lastCheckPoint; ControlFile->time = (pg_time_t) time(NULL); /* - * Ensure minRecoveryPoint is past the checkpoint record. Normally, - * this will have happened already while writing out dirty buffers, - * but not necessarily - e.g. because no buffers were dirtied. We do - * this because a non-exclusive base backup uses minRecoveryPoint to - * determine which WAL files must be included in the backup, and the - * file (or files) containing the checkpoint record must be included, - * at a minimum. Note that for an ordinary restart of recovery there's - * no value in having the minimum recovery point any earlier than this + * Ensure minRecoveryPoint is past the checkpoint record while archive + * recovery is still ongoing. Normally, this will have happened + * already while writing out dirty buffers, but not necessarily - + * e.g. because no buffers were dirtied. We do this because a + * non-exclusive base backup uses minRecoveryPoint to determine which + * WAL files must be included in the backup, and the file (or files) + * containing the checkpoint record must be included, at a + * minimum. Note that for an ordinary restart of recovery there's no + * value in having the minimum recovery point any earlier than this * anyway, because redo will begin just after the checkpoint record. + * This is a quick hack to make sure nothing really bad happens if + * somehow we get here after the end-of-recovery checkpoint. */ - if (ControlFile->minRecoveryPoint < lastCheckPointEndPtr) + if (ControlFile->state == DB_IN_ARCHIVE_RECOVERY) { - ControlFile->minRecoveryPoint = lastCheckPointEndPtr; - ControlFile->minRecoveryPointTLI = lastCheckPoint.ThisTimeLineID; + if (ControlFile->minRecoveryPoint < lastCheckPointEndPtr) + { + ControlFile->minRecoveryPoint = lastCheckPointEndPtr; + ControlFile->minRecoveryPointTLI = lastCheckPoint.ThisTimeLineID; - /* update local copy */ - minRecoveryPoint = ControlFile->minRecoveryPoint; - minRecoveryPointTLI = ControlFile->minRecoveryPointTLI; + /* update local copy */ + minRecoveryPoint = ControlFile->minRecoveryPoint; + minRecoveryPointTLI = ControlFile->minRecoveryPointTLI; + } + if (flags & CHECKPOINT_IS_SHUTDOWN) + ControlFile->state = DB_SHUTDOWNED_IN_RECOVERY; } - if (flags & CHECKPOINT_IS_SHUTDOWN) - ControlFile->state = DB_SHUTDOWNED_IN_RECOVERY; + else + { + /* + * Aarchive recovery has ended. Crash recovery ever after should + * always recover to the end of WAL + */ + ControlFile->minRecoveryPoint = InvalidXLogRecPtr; + ControlFile->minRecoveryPointTLI = 0; + } + UpdateControlFile(); } LWLockRelease(ControlFileLock); /* * Update the average distance between checkpoints/restartpoints if the - * prior checkpoint exists. + * prior checkpoint exists and we have advanced REDO LSN. */ - if (PriorRedoPtr != InvalidXLogRecPtr) + if (PriorRedoPtr != InvalidXLogRecPtr && RedoRecPtr > PriorRedoPtr) UpdateCheckPointDistanceEstimate(RedoRecPtr - PriorRedoPtr); + /* + * Now we can safely regard this restart point as established. + * + * Since all buffers have been flushed so we regard this restart point as + * established. We could omit some of the following steps in the case + * where we have omitted control file updates, but we don't bother avoid + * them from performing since that case rarely happens and they don't harm + * even if they take place. + */ + /* * Delete old log files, those no longer needed for last restartpoint to * prevent the disk holding the xlog from growing full. @@ -9804,7 +9829,7 @@ CreateRestartPoint(int flags) xtime = GetLatestXTime(); ereport((log_checkpoints ? LOG : DEBUG2), (errmsg("recovery restart point at %X/%X", - LSN_FORMAT_ARGS(lastCheckPoint.redo)), + LSN_FORMAT_ARGS(RedoRecPtr)), xtime ? errdetail("Last completed transaction was at log time %s.", timestamptz_to_str(xtime)) : 0)); -- 2.27.0 ----Next_Part(Mon_Feb_14_14_40_22_2022_983)----