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 1u1UXh-003IB6-Ti for pgsql-bugs@arkaria.postgresql.org; Sun, 06 Apr 2025 18:16:26 +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 1u1UXf-000NhX-Ug for pgsql-bugs@arkaria.postgresql.org; Sun, 06 Apr 2025 18:16:24 +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.94.2) (envelope-from ) id 1u1UXf-000NhP-KJ for pgsql-bugs@lists.postgresql.org; Sun, 06 Apr 2025 18:16:23 +0000 Received: from mail-pg1-x52f.google.com ([2607:f8b0:4864:20::52f]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1u1UXa-003lL4-36 for pgsql-bugs@lists.postgresql.org; Sun, 06 Apr 2025 18:16:23 +0000 Received: by mail-pg1-x52f.google.com with SMTP id 41be03b00d2f7-af28bc68846so3247319a12.1 for ; Sun, 06 Apr 2025 11:16:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=leadboat.com; s=google; t=1743963376; x=1744568176; darn=lists.postgresql.org; h=user-agent:in-reply-to:content-disposition:mime-version:references :message-id:subject:cc:to:from:date:from:to:cc:subject:date :message-id:reply-to; bh=EVf/u7EHrRD4o2bvDg/DIjAbijqWuKWz8iHkMN5zsLQ=; b=QWkWfY5OMQRJR71Vcq2qg1Mb1qcazaUeO863JRpPKyQZQM7Ft9RK2kcetAadmL/tQf /JjTedLw+NaWXOYimXnVFafF5AvnRXOgPbhv/+9D8pgwgPsrarCO1Fo5S8Xfw1zOYHzG CkjaLpykVebjqOdT+xgLQ/p016movVV5JzPOo= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1743963376; x=1744568176; h=user-agent:in-reply-to:content-disposition:mime-version:references :message-id:subject:cc:to:from:date:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=EVf/u7EHrRD4o2bvDg/DIjAbijqWuKWz8iHkMN5zsLQ=; b=oDvHWR645uEDYMAcwUodFU1Y1DxQ31W/5icITrT4BA+NExad09Vm3pD2xp51nqa1ZH dzRmcuJdUAHZyC9o9d6d9TOPUMy+Qck9ijEwkADNimgVLWrQS/ecoKV2KtRWCFJz061M Abu1AgDCu2uZFmK+o1CYxPpy6B3A/nVzyK3q6punW4s7csZMMM1N6/iGPL9Bpw88HVyz odDgXC8BztPmY3gCOIB7+zH00eiMtp+Z1o9xlzAQ759TCP1U2PT/ukxHiRFFxny0CQNY QgvduRbiWjEUrZCEbGs0av7HPPk/xIwPRACUmcae+DXcsbNvOR9b6XqaOVrjGIIgwlKN wBVQ== X-Forwarded-Encrypted: i=1; AJvYcCUJRSU6ggrlG0F5fu3kqgvHI/HGrQVM/cMBjTYY0UFReLIDDmyxqLuAsOJjjc/Q/Jg859yu7ncEkA2H@lists.postgresql.org X-Gm-Message-State: AOJu0YxMTQciiPHlT8b6o4Or5+51zfkns7FvLQFXmWcYCdoCLuGsiKiL lDJijk7MVOj2Ry5KQZOXsY3bfeSBP0XO1Z3XTN4y2CUw31GaxwqXQarWnkYKyQ== X-Gm-Gg: ASbGncsT+uYnKylxyBh/U2RNRfOYkAEFeEodueBMb0ktvyRLboTkxbR7+fNMsi3nu5R g98WpJ+l7MCGNCTsXx18obFtiuOHjtXxOnM3dXVmUHGDuitVTMyvGYH+DqV0VHd68ZkG2OxOsPw 4Kymvt6jg6D1AYubamVFn+64uApGEvx8nqUqFbk2bJBoibYub2cpucCpFazuLsklCyeRAZTTuJg AfcEtF6up54OmLlkuU+/enkrtSqA4YvEx74PrFXdTItGDfHo7h8gpPNTjYgljhL9ko7z8NpH8PG 0xJtbnbESJZPKZ7aPXTVugOKvtRSNpZpxLlDJbFO9A== X-Google-Smtp-Source: AGHT+IE+sTLDyNAfPMy+H1DIQacsFtU9pSUHByFZOrXMKH7OsOq6TKXg2J5I7DUjh8YtEtALw7JC6Q== X-Received: by 2002:a17:90b:3d46:b0:2fe:6942:3710 with SMTP id 98e67ed59e1d1-306a6120847mr12711281a91.3.1743963376020; Sun, 06 Apr 2025 11:16:16 -0700 (PDT) Received: from google.com ([2601:647:5600:80d0::31cd]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3057ca1eb96sm7458697a91.9.2025.04.06.11.16.14 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sun, 06 Apr 2025 11:16:15 -0700 (PDT) Date: Sun, 6 Apr 2025 11:16:13 -0700 From: Noah Misch To: Thomas Munro Cc: Heikki Linnakangas , Alexander Lakhin , Robert Haas , Michael Paquier , Tom Lane , Laurenz Albe , rootcause000@gmail.com, pgsql-bugs@lists.postgresql.org Subject: Re: BUG #18146: Rows reappearing in Tables after Auto-Vacuum Failure in PostgreSQL on Windows Message-ID: <20250406181613.61.nmisch@google.com> References: <9749e358-60e5-51e8-aa05-1eaf39ef339e@gmail.com> <8e499d50-09ac-479a-b5b2-8a8bca83d057@iki.fi> <00477c52-3a35-4621-b80b-58a5a48b113f@iki.fi> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: 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 On Fri, Sep 06, 2024 at 11:05:26AM +1200, Thomas Munro wrote: > Subject: [PATCH v3 1/3] RelationTruncate() must set DELAY_CHKPT_START. > > Previously, it set only DELAY_CHKPT_COMPLETE. That was important, > because it meant that if the XLOG_SMGR_TRUNCATE record preceded a > XLOG_CHECKPOINT_ONLINE record in the WAL, then the truncation would also > happen on disk before the XLOG_CHECKPOINT_ONLINE record was > written. I read this commit (75818b3) with intense interest, mostly to see if it found a rule that commit 8e7e672 needs to follow and didn't (no). I did want to find or make an example of interleaved events where DELAY_CHKPT_COMPLETE is still necessary. Do any of you have such an example? Here were my unsuccessful attempts: ==== Attempt: from upthread On Wed, Oct 11, 2023 at 12:59:39PM -0400, Robert Haas wrote: > Suppose that RelationTruncate set both DELAY_CHKPT_START and > DELAY_CHKPT_COMPLETE. I think that would prevent this problem. P2 > could still choose the redo LSN after P1 logged the truncate, but it > wouldn't then be able to reach CheckPointBuffers() until after P1 had > reached RegisterSyncRequest. Note that setting *only* > DELAY_CHKPT_START isn't good enough, because then we can get this > history: > > P1: log truncate > P2: choose redo LSN > P2: CheckPointBuffers() > P1: DropRelationBuffers() > P2: ProcessSyncRequests() > P2: log checkpoint > *** system loses power *** If "choose redo LSN" happens at the point shown there, DELAY_CHKPT_START won't allow CheckPointBuffers() at the point shown there, so this interleaving doesn't happen. ==== Attempt: XLOG_SMGR_TRUNCATE+DropRelationBuffers() just after checkpoint waits for DELAY_CHKPT_START P2: choose redo LSN 1 P2: CheckPointBuffers() enter P1: DELAY_CHKPT_START enter P1: log truncate P1: DropRelationBuffers() P2: CheckPointBuffers() actual flushes P2: ProcessSyncRequests() P2: log checkpoint P2: choose redo LSN 2 (next checkpoint) *** system loses power, below were still in future *** P1: ftruncate() P1: RegisterSyncRequest() P1: DELAY_CHKPT_START exit DropRelationBuffers() made CheckPointBuffers() do less work, and the validity of the checkpoint is now tied to truncate happening eventually. However, if XLOG_CHECKPOINT_ONLINE reaches disk, that implies XLOG_SMGR_TRUNCATE reached disk first. Any recovery starting at or before LSN 1 will redo XLOG_SMGR_TRUNCATE. Data integrity is fine. ==== Attempt: replay finding "older contents than expected" A key RelationTruncate() code comment mentions this: * First, the truncation operation might drop buffers that the checkpoint * otherwise would have flushed. If it does, then it's essential that the * files actually get truncated on disk before the checkpoint record is * written. Otherwise, if replay begins from that checkpoint, the * to-be-truncated blocks might still exist on disk but have older * contents than expected, which can cause replay to fail. It's OK for the * blocks to not exist on disk at all, but not for them to have the wrong * contents. For this reason, we need to set DELAY_CHKPT_COMPLETE while * this code executes. However, I can't see how to make that happen. RelationTruncate() has AccessExclusiveLock on the relation, so no other WAL records for the truncated block range are happening while we hold DELAY_CHKPT_START. In the previous attempt, any WAL records for the truncated block range must be before XLOG_SMGR_TRUNCATE (such records tolerate older content) or after XLOG_CHECKPOINT_ONLINE (since RelationTruncate() held AccessExclusiveLock at least that late). ==== Attempt: all truncate steps just after XLOG_CHECKPOINT_REDO P1: DELAY_CHKPT_START enter P2: choose redo LSN P1: log truncate P1: DropRelationBuffers() P1: ftruncate() P1: RegisterSyncRequest() P1: DELAY_CHKPT_START exit P2: CheckPointBuffers() P2: ProcessSyncRequests() P2: log checkpoint *** system loses power *** Recovery sees the XLOG_CHECKPOINT_ONLINE, which implies it also sees and replays the XLOG_SMGR_TRUNCATE. Data integrity is fine. ==== Attempt: XLOG_SMGR_TRUNCATE before XLOG_CHECKPOINT_REDO P1: DELAY_CHKPT_START enter P1: log truncate P2: choose redo LSN P1: DropRelationBuffers() P1: ftruncate() P1: RegisterSyncRequest() P1: DELAY_CHKPT_START exit P2: CheckPointBuffers() P2: ProcessSyncRequests() P2: log checkpoint *** system loses power *** XLOG_SMGR_TRUNCATE precedes the redo point, so recovery finds no need to redo the XLOG_SMGR_TRUNCATE. Data integrity is fine.