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 1u445h-0079X1-Dq for pgsql-hackers@arkaria.postgresql.org; Sun, 13 Apr 2025 20:38:09 +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 1u445e-009wrb-0O for pgsql-hackers@arkaria.postgresql.org; Sun, 13 Apr 2025 20:38:06 +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 1u445d-009wnl-AB for pgsql-hackers@lists.postgresql.org; Sun, 13 Apr 2025 20:38:06 +0000 Received: from mail-pl1-x631.google.com ([2607:f8b0:4864:20::631]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1u445b-000ATa-2J for pgsql-hackers@postgresql.org; Sun, 13 Apr 2025 20:38:04 +0000 Received: by mail-pl1-x631.google.com with SMTP id d9443c01a7336-2260c91576aso29573845ad.3 for ; Sun, 13 Apr 2025 13:38:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=leadboat.com; s=google; t=1744576682; x=1745181482; darn=postgresql.org; h=user-agent:in-reply-to:content-disposition:mime-version:references :message-id:subject:to:from:date:from:to:cc:subject:date:message-id :reply-to; bh=ME3Si7TnE1o6PKdMchmkq/KYG/dj5XtE03grXhDYWbQ=; b=NWcHrvbDsnKIbP4AyKQwrdI+fMzYRuR6XlBAetRJUu+SK/RC+4cRK0KkF5WYnefson WCinPoR/NEY+6zQYglAKGRj+wVEYvyb+JB9CsdW2cWuGGYKClo4Mpwob7j/zmsZtXr40 On2KR05Fe+Ymx2bPhwqhgA/7KhnfXfOVQNu2w= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1744576682; x=1745181482; h=user-agent:in-reply-to:content-disposition:mime-version:references :message-id:subject:to:from:date:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=ME3Si7TnE1o6PKdMchmkq/KYG/dj5XtE03grXhDYWbQ=; b=QiAHqU3lUGJfwhXkbi60z92VjfmXOS0KK0VZQ27rwi4s74tl8HvvcjmXWbC3/dXrOJ OhS2tZfRUdE6TKYs8kbDVkv79Inn4DroMbvI+UQ+pm02A0SDyXXK0hPiqcik7JAR0Sel x1rY2wW6L2bPHUHnO/7Qo7IHwd7coYb0cXjj4gqbUeagUUUqCiUmQRrFHeZddDIXHijF NxnllwmKSNOKiayUvT0+A8z9RTS97d582qRULYjbm8T87C7PbWQYVJ+3vcfjWXSZ4GIG +rBKjhP7ih6/2JcByIsBFNv5USW6z1XrONdHPy3opYzob6x/lRR05eK0xjlMto/ZObhM zGlg== X-Gm-Message-State: AOJu0Yz6VvM4Az3J3XNsspRLHv5D2bNm/HJGGxyhAmrSNYeuNGJIvMHx T1w92uWsXUIR9yCj3Wtf++9QfcfCbHEHpFGcgBM3uD4B6l4ssLIcKfoDdt6yjJk0uiPuA+txrVo = X-Gm-Gg: ASbGncsG7MpegcbEUV1rFfBYmI5qZCXtZ8sqTYmJjDUfI62W2fHoQX2Axb5JtgPr7g6 ZFwMUFLPz4CcGscZYiKcA2oOmUjUiKDkzQTvx62r8qOYWFynPXQRSxzRPlUt71RhZ+5Vr+0Dlnn fPvWFLnOkM/RiGdqiK4IkuKhfLOALo/mqAxUtg0b606PrpYrsMV3DqqENwvsf7dlpV1E9he6U7f ZbTLokvxwzGwuVQab4MovUCEI7sj4YCR2WeCTNVi8u0AgshNj9IpY77DmFiC2jTvWcxvJQrRb2Q vfKZ8ub8K6EhPOHIlLgQvejJfQV/CKlczNadMQ== X-Google-Smtp-Source: AGHT+IFrOLgLxZ+DXiS7A7B/8eoLikDExbZfGl/3+hbiQPTOBgqL1LjaLm/fJbkQDtsx6g+lRdJuPQ== X-Received: by 2002:a17:902:ef03:b0:21f:2ded:76ea with SMTP id d9443c01a7336-22bea4effefmr149683625ad.36.1744576681935; Sun, 13 Apr 2025 13:38:01 -0700 (PDT) Received: from google.com ([2601:647:5600:80d0::31cd]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-22ac7b628a3sm86182715ad.45.2025.04.13.13.38.00 for (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sun, 13 Apr 2025 13:38:00 -0700 (PDT) Date: Sun, 13 Apr 2025 13:37:58 -0700 From: Noah Misch To: pgsql-hackers@postgresql.org Subject: Re: datfrozenxid > relfrozenxid w/ crash before XLOG_HEAP_INPLACE Message-ID: <20250413203758.ef.nmisch@google.com> References: <20240620012908.92.nmisch@google.com> <20250406180054.26.nmisch@google.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="JXhpWNzUANjeTRAd" Content-Disposition: inline In-Reply-To: <20250406180054.26.nmisch@google.com> User-Agent: Mutt/2.2.12 (2023-09-09) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --JXhpWNzUANjeTRAd Content-Type: text/plain; charset=us-ascii Content-Disposition: inline On Sun, Apr 06, 2025 at 11:00:54AM -0700, Noah Misch wrote: > I pushed that as commit 8e7e672 (2024-10-25). I now think DELAY_CHKPT_START > is superfluous here, per this proc.h comment: > > * (In the > * extremely common case where the data being modified is in shared buffers > * and we acquire an exclusive content lock on the relevant buffers before > * writing WAL, this mechanism is not needed, because phase 2 will block > * until we release the content lock and then flush the modified data to > * disk.) > > heap_inplace_update_and_unlock() meets those conditions. Its closest > precedent is XLogSaveBufferForHint(), which does need DELAY_CHKPT_START due to > having only BUFFER_LOCK_SHARED. True so far, but ... > heap_inplace_update_and_unlock() has > BUFFER_LOCK_EXCLUSIVE, so it doesn't need DELAY_CHKPT_START. ... not so, because we've not yet done MarkBufferDirty(). transam/README cites SyncOneBuffer(), which says: * Check whether buffer needs writing. * * We can make this check without taking the buffer content lock so long * as we mark pages dirty in access methods *before* logging changes with * XLogInsert(): if someone marks the buffer dirty just after our check we * don't worry because our checkpoint.redo points before log record for * upcoming changes and so we are not required to write such dirty buffer. The attached patch updates the aforementioned proc.h comment and the heap_inplace_update_and_unlock() comment that my last message proposed. --JXhpWNzUANjeTRAd Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="inplace280-comment-delay-v1.patch" From: Noah Misch Comment on need to MarkBufferDirty() if omitting DELAY_CHKPT_START. Blocking checkpoint phase 2 requires MarkBufferDirty() and BUFFER_LOCK_EXCLUSIVE; neither suffices by itself. transam/README documents this, citing SyncOneBuffer(). Update the DELAY_CHKPT_START documentation to say this. Expand the heap_inplace_update_and_unlock() comment that cites XLogSaveBufferForHint() as precedent, since heap_inplace_update_and_unlock() could have opted not to use DELAY_CHKPT_START. Commit 8e7e672cdaa6bfec85d4d5dd9be84159df23bb41 added DELAY_CHKPT_START to heap_inplace_update_and_unlock(). Since commit bc6bad88572501aecaa2ac5d4bc900ac0fd457d5 reverted it in non-master branches, no back-patch. Discussion: https://postgr.es/m/20250406180054.26.nmisch@google.com diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index ed2e302..c1a4de1 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -6507,9 +6507,17 @@ heap_inplace_update_and_unlock(Relation relation, * [crash] * [recovery restores datfrozenxid w/o relfrozenxid] * - * Like in MarkBufferDirtyHint() subroutine XLogSaveBufferForHint(), copy - * the buffer to the stack before logging. Here, that facilitates a FPI - * of the post-mutation block before we accept other sessions seeing it. + * Mimic MarkBufferDirtyHint() subroutine XLogSaveBufferForHint(). + * Specifically, use DELAY_CHKPT_START, and copy the buffer to the stack. + * The stack copy facilitates a FPI of the post-mutation block before we + * accept other sessions seeing it. DELAY_CHKPT_START allows us to + * XLogInsert() before MarkBufferDirty(). Since XLogSaveBufferForHint() + * can operate under BUFFER_LOCK_SHARED, it can't avoid DELAY_CHKPT_START. + * This function, however, likely could avoid it with the following order + * of operations: MarkBufferDirty(), XLogInsert(), memcpy(). Opt to use + * DELAY_CHKPT_START here, too, as a way to have fewer distinct code + * patterns to analyze. Inplace update isn't so frequent that it should + * pursue the small optimization of skipping DELAY_CHKPT_START. */ Assert((MyProc->delayChkptFlags & DELAY_CHKPT_START) == 0); START_CRIT_SECTION(); diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h index f51b03d..86c5f99 100644 --- a/src/include/storage/proc.h +++ b/src/include/storage/proc.h @@ -110,10 +110,10 @@ extern PGDLLIMPORT int FastPathLockGroupsPerBackend; * is inserted prior to the new redo point, the corresponding data changes will * also be flushed to disk before the checkpoint can complete. (In the * extremely common case where the data being modified is in shared buffers - * and we acquire an exclusive content lock on the relevant buffers before - * writing WAL, this mechanism is not needed, because phase 2 will block - * until we release the content lock and then flush the modified data to - * disk.) + * and we acquire an exclusive content lock and MarkBufferDirty() on the + * relevant buffers before writing WAL, this mechanism is not needed, because + * phase 2 will block until we release the content lock and then flush the + * modified data to disk. See transam/README and SyncOneBuffer().) * * Setting DELAY_CHKPT_COMPLETE prevents the system from moving from phase 2 * to phase 3. This is useful if we are performing a WAL-logged operation that --JXhpWNzUANjeTRAd--