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 1rhHMv-00Aazr-0c for pgsql-bugs@arkaria.postgresql.org; Mon, 04 Mar 2024 23:05:13 +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 1rhHMt-00BY1K-Ej for pgsql-bugs@arkaria.postgresql.org; Mon, 04 Mar 2024 23:05:11 +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 1rhHMs-00BY1B-UN for pgsql-bugs@lists.postgresql.org; Mon, 04 Mar 2024 23:05:11 +0000 Received: from mail-yw1-x1136.google.com ([2607:f8b0:4864:20::1136]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.94.2) (envelope-from ) id 1rhHMq-002nJw-9r for pgsql-bugs@lists.postgresql.org; Mon, 04 Mar 2024 23:05:10 +0000 Received: by mail-yw1-x1136.google.com with SMTP id 00721157ae682-6096ab005c0so46653687b3.1 for ; Mon, 04 Mar 2024 15:05:08 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=leadboat.com; s=google; t=1709593507; x=1710198307; darn=lists.postgresql.org; h=user-agent:in-reply-to:content-transfer-encoding :content-disposition:mime-version:references:message-id:subject:cc :to:from:date:from:to:cc:subject:date:message-id:reply-to; bh=wncn4jQkH/ldigbTxEwr3IsRRrNJJstJekMwHQ8LdxM=; b=O3U2g9x4Q1gXtQFK1f7xaml3FNvmgIlAIjyTPyuqiWZJYm45sX7IJshzxZb3AdLcRi 4bX6NkXr5G94Lbpgd/Ak7/5DjlvhyKOXFtHMtcMd6LuCVMfgz+N89NO4+glJTRmqvol+ kc7RERKKUoDVXZQadu0s/7uOJlBADuZBw9YBo= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1709593507; x=1710198307; h=user-agent:in-reply-to:content-transfer-encoding :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=wncn4jQkH/ldigbTxEwr3IsRRrNJJstJekMwHQ8LdxM=; b=FWC1MD4FOElwkJOdxl94riMg6vd/w3Dzl/71uE76cxP0utGLzmDJAqttGzjw7cTydd zt/olt1zlhn4SIkhZQblhz4h4c+bQUDT8xJJ1UDD0649sm3ycGGC0ldTeKn8faD/SYkH /5uh3bPzW0zVU+BU63WX66aFENvBEBODsbKo8FvUFZaeG8NYbun1KyquZSHR6Xglnl/v CGsZBA/n4e0B300lzEq2u3hsFY2TbDpU1fGDt8wQ4C1b4oEm9Kls0r7uNq8synG974I4 NfZOX15PDggk5ZKYjXeEm3E3NjSVS+Bs6Qr7JZp9CPcPntDiw6GdM861La4tpEcJpoMn 3i4g== X-Gm-Message-State: AOJu0YwVTg5hSekNWaKbgbhFDLMeTSgh/LEp6oN/mnY/yPUyXS3l+jRt 7NRvZOlfu3pbVT6HPzToJO1hTxSk8kncCUqT/trPHLmNSYuYHzaiqZ8CLM7tyw== X-Google-Smtp-Source: AGHT+IFt+jspzSAY1vKeh58cY3g7SURJz5GWQV8EPUAkUitUjjrpgzgmX+SULV7oho64KUjG8i0tGg== X-Received: by 2002:a0d:cf42:0:b0:608:b543:2d3d with SMTP id r63-20020a0dcf42000000b00608b5432d3dmr10508352ywd.38.1709593506652; Mon, 04 Mar 2024 15:05:06 -0800 (PST) Received: from google.com ([2600:1702:a20:5750::48]) by smtp.gmail.com with ESMTPSA id r7-20020a814407000000b00609498508acsm2874231ywa.42.2024.03.04.15.05.05 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Mon, 04 Mar 2024 15:05:05 -0800 (PST) Date: Mon, 4 Mar 2024 15:05:03 -0800 From: Noah Misch To: Ronan Dunklau Cc: pgsql-bugs Subject: Re: FSM Corruption (was: Could not read block at end of the relation) Message-ID: <20240304230503.a4.nmisch@google.com> References: <1925490.taCxCBeP46@aivenlaptop> <20240304190312.b6.nmisch@google.com> <3549385.iIbC2pHGDl@aivenlaptop> MIME-Version: 1.0 Content-Type: text/plain; charset=iso-8859-1 Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: <3549385.iIbC2pHGDl@aivenlaptop> 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 Mon, Mar 04, 2024 at 11:21:07PM +0100, Ronan Dunklau wrote: > Le lundi 4 mars 2024, 20:03:12 CET Noah Misch a écrit : > > Is this happening after an OS crash, a replica promote, or a PITR restore? > > I need to double check all occurences, but I wouldn't be surprised if they all > went trough a replica promotion. > > > If so, I think I see the problem. We have an undocumented rule that FSM > > shall not contain references to pages past the end of the relation. To > > facilitate that, relation truncation WAL-logs FSM truncate. However, > > there's no similar protection for relation extension, which is not > > WAL-logged. We break the rule whenever we write FSM for block X before > > some WAL record initializes block X. data_checksums makes the trouble > > easier to hit, since it creates FPI_FOR_HINT records for FSM changes. A > > replica promote or PITR ending just after the FSM FPI_FOR_HINT would yield > > this broken state. While v16 RelationAddBlocks() made this easier to hit, > > I suspect it's reproducible in all supported branches. For example, > > lazy_scan_new_or_empty() and multiple index AMs break the rule via > > RecordPageWithFreeSpace() on a PageIsNew() page. > > Very interesting. I understand the reasoning, and am now able to craft a very > crude test case, which I will refine into something more actionable: > > - start a session 1, which will just vacuum our table continuously > - start a ession 2, which will issue checkpoints continuously > - in a session 3, COPY enough data so that we prealllocate too many pages. In > my example, I 'm copying 33150 single integer rows, which fit on a bit more > than 162 pages. The idea behind that is to make sure we don't fall on a round > number of pages, and we leave several iterations of the extend mechanism > running to issue a full page write of the fsm. > > So to trigger the bug, it seems to me one needs to run in parallel: > - COPY > - VACUUM > - CHECKPOINT > > Which would explain why a surprisingly large number of occurences I've seen > seemed to involve a pg_restore invocation. Does that procedure alone reproduce the ERROR, without any replica promote, postmaster restart, etc.? That, I do not have reason to expect. > > I think the fix is one of: > > > > - Revoke the undocumented rule. Make FSM consumers resilient to the FSM > > returning a now-too-large block number. > > Performance wise, that seems to be the better answer I would guess this one is more risky from a performance perspective, since we'd be adding to a hotter path under RelationGetBufferForTuple(). Still, it's likely fine. > but won't this kind of > built-in resilience encourage subtle bugs creeping in ? Since the !wal_log_hints case avoids WAL for FSM, we've already accepted subtle bugs. I bet an unlucky torn FSM page could reach the ERROR you reached, even if we introduced a "main-fork WAL before FSM" rule. We'll have plenty of need for resilience. Hence, I'd lean toward this approach. > > - Enforce a new "main-fork WAL before FSM" rule for logged rels. For > > example, in each PageIsNew() case, either don't update FSM or WAL-log an > > init (like lazy_scan_new_or_empty() does when PageIsEmpty()). > > The FSM is not updated by the caller: in the bulk insert case, we > intentionally don't add them to the FSM. So having VACUUM WAL-log the page in > lazy_scan_new_or_empty in the New case as you propose seems a very good idea. > > Performance wise that would keep the WAL logging outside of the backend > performing the preventive work for itself or others, and it should not be that > many pages that it gives VACUUM too much work. A drawback is that this approach touches several access methods, so out-of-tree access methods also likely need changes. Still, this is a good backup if the first option measurably slows INSERT throughput. > I can try my hand at such a patch it if looks like a good idea. Please do. > Thank you very much for your insights. Thanks for the detailed report that made it possible.