Hi!
At the current master i found that if not the last page of
the FSM bottom layer was corrupted it is not restored after zeroing.
Here is reproduction like that:
1) Create a table with FSM of 4 pages:
create table t (int) as select * from generate_series(1, 1E6);
delete from t where ctid in (select ctid from t tablesample bernoulli (20));
SELECT pg_relation_filepath('t'); -- to know the filename with FSM
vacuum t;
2) Do checkpoint and stop the server.
3) Corrupt a byte in the third page. For instance, the lower byte of the CRC:
printf '\xAA' | dd of=/usr/local/pg12252-vanm/data/base/5/<filename_fsm> bs=1
seek=$((2*8192+8)) count=1 conv=notrunc
4) start server and execute: vacuum t; twice: to ensure that corrupted page
is fixed in memory, zeroed and a new header was written on it.
postgres=# vacuum t;
WARNING: page verification failed, calculated checksum 13869 but expected 13994
WARNING: invalid page in block 2 of relation base/5/16384_fsm; zeroing out page
VACUUM
postgres=# vacuum t; -- without warnings
VACUUM
5) Do checkpoint and restart the server. After vacuum t; the warnings appeared
again:
postgres=# vacuum t;
WARNING: page verification failed, calculated checksum 13869 but expected 13994
WARNING: invalid page in block 2 of relation base/5/16384_fsm; zeroing out page
VACUUM
I noticed that the updated page is not written to disk because the
buffer where it is located is not marked dirty. Moreover MarkBufferDirtyHint(),
which is called for modified FSM pages, seems is not suitable here,
since as i suppose the corrupted page must be rewritten certainly, not for hint.
Therefore, maybe mark it dirty immediately after writing the new header?
Here is a small patch that does it and eliminates multiple warnings.
Would be glad if you take a look on it.
With the best regards,
--
Anton A. Melnikov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
From 99f0ab536f180a1c5a57246e0929acfeb915d88e Mon Sep 17 00:00:00 2001
From: "Anton A. Melnikov" <a.melni...@postgrespro.ru>
Date: Fri, 7 Feb 2025 02:57:34 +0300
Subject: [PATCH] Mark buffer dirty for FSM pages that were newly inited. E.g.
after zeroing damaged pages.
---
src/backend/storage/freespace/freespace.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/backend/storage/freespace/freespace.c b/src/backend/storage/freespace/freespace.c
index 4773a9cc65e..a6479977c6d 100644
--- a/src/backend/storage/freespace/freespace.c
+++ b/src/backend/storage/freespace/freespace.c
@@ -614,7 +614,10 @@ fsm_readbuf(Relation rel, FSMAddress addr, bool extend)
{
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
if (PageIsNew(BufferGetPage(buf)))
+ {
PageInit(BufferGetPage(buf), BLCKSZ, 0);
+ MarkBufferDirty(buf);
+ }
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
}
return buf;
--
2.48.1