Re: BUG #16172: failure of vacuum file truncation can cause permanent data corruption

TAKATSUKA Haruka <[email protected]> Fri, 20 Dec 2019 11:00:28 +0900
Newsgroups gmane.comp.db.postgresql.bugs
Message-ID <[email protected]>
I found moving DropRelFileNodeBuffers() from top to end in function
smgrtruncate() is a proper modification. It passed the regression test
and this reproduction test.

with best regards,
Haruka Takatsuka / SRA OSS, Inc. Japan



On Fri, 20 Dec 2019 10:19:52 +0900
TAKATSUKA Haruka <[email protected]> wrote:

> I also tested PostgreSQL with the attached patch avoided this data
> corruption. The patch just removes DropRelFileNodeBuffers() from
> smgrtruncate().
> 
> 
> On Thu, 19 Dec 2019 07:14:42 +0000
> PG Bug reporting form <[email protected]> wrote:
> 
> > The following bug has been logged on the website:
> > 
> > Bug reference:      16172
> > Logged by:          TAKATSUKA Haruka
> > Email address:      [email protected]
> > PostgreSQL version: 12.1
> > Operating system:   Windows/Linux
> > Description:        
> > 
> > Hello, pgsql hackers,
> > 
> > I found that failure of vacuum file truncation can cause permanent data
> > corruption.
> > I am reporting the reproduce steps below.
> > 
> > In Windows installation, the truncation sometime fails by permission
> > denied error because of anti-virus software. It has caused just ERROR
> > and people have offen dismissed it.
> > 
> > Truncation failure can also make the standby panic with the following
> > messages when replaying Heap2/VISIBLE or Heap2/CLEAN, because truncation
> > wal is emitted even if it doesn't complete actually in the primary.
> > 
> >  WARNING:  page .. of relation base/..../.... does not exist
> >  CONTEXT:  WAL redo at ..... for ....: cutoff xid ... flags ...
> >  PANIC:  WAL contains references to invalid pages
> > 
> > I think truncation failure is to be handled as more severe level.
> > Any thoughts?
> > 
> > with best regards,
> > Haruka Takatsuka / SRA OSS, Inc. Japan
> > 
> > 
> > reproduce steps (PG12)
> > ======================
> > 
> > $ psql -U postgres -d db1
> > Pager usage is off.
> > psql (12.1)
> > Type "help" for help.
> > 
> > db1=# 
> > 
> >   $ gdb -p {its backend process}
> > 
> >   (gdb) b FileTruncate
> >   Breakpoint 1 at 0x73d320: file fd.c, line 2057.
> >   (gdb) c
> >   Continuing.
> > 
> > db1=# SHOW autovacuum;
> >  autovacuum
> > ------------
> >  off
> > (1 row)
> > 
> > db1=# CREATE TABLE t1 (id int primary key, v text);
> > CREATE
> > 
> > db1=# INSERT INTO t1 SELECT g, md5(g::text) FROM generate_series(1, 10000)
> > as g;
> > INSERT 0 10000
> > 
> > db1=# CHECKPOINT;
> > 
> >   Program received signal SIGUSR1, User defined signal 1.
> >   0x00000036caae91a3 in __epoll_wait_nocancel () from /lib64/libc.so.6
> >   (gdb) c
> >   Continuing.
> > 
> > CHECKPOINT
> > 
> > db1=# DELETE FROM t1 WHERE id > 50;
> > DELETE 9950
> > 
> > db1=# VACUUM t1;
> > 
> >   Breakpoint 1, FileTruncate (file=59, offset=8192,
> > wait_event_info=167772175)
> >       at fd.c:2057
> >   2057    {
> >   (gdb) n
> >   2065            returnCode = FileAccess(file);
> >   (gdb) n
> >   2066            if (returnCode < 0)
> >   (gdb) p returnCode = -100
> >   $6 = -100
> >   (gdb) c
> >   Continuing.
> > 
> > ERROR:  could not truncate file "base/16384/16645" to 1 blocks: Success
> > 
> > db1=# SELECT count(*) FROM t1;
> >  count
> > -------
> >   9930
> > (1 row)
> > 
> (snip)


______________________________________________________________________
 高塚 遥  [email protected]  SRA OSS, Inc. http://www.sraoss.co.jp
 〒171-0022 東京都豊島区南池袋2-32-8 
 TEL: 03-5979-2701  FAX: 03-5979-2702  CellPhone: 080-1292-3396
12stable_move_bufferdrop.diff (text/plain, 1.1 KB)
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index dba8c39..1cc28b4 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -646,12 +646,6 @@ void
 smgrtruncate(SMgrRelation reln, ForkNumber forknum, BlockNumber nblocks)
 {
 	/*
-	 * Get rid of any buffers for the about-to-be-deleted blocks. bufmgr will
-	 * just drop them without bothering to write the contents.
-	 */
-	DropRelFileNodeBuffers(reln->smgr_rnode, forknum, nblocks);
-
-	/*
 	 * Send a shared-inval message to force other backends to close any smgr
 	 * references they may have for this rel.  This is useful because they
 	 * might have open file pointers to segments that got removed, and/or
@@ -667,6 +661,12 @@ smgrtruncate(SMgrRelation reln, ForkNumber forknum, BlockNumber nblocks)
 	 * Do the truncation.
 	 */
 	smgrsw[reln->smgr_which].smgr_truncate(reln, forknum, nblocks);
+
+	/*
+	 * Get rid of any buffers for the about-to-be-deleted blocks. bufmgr will
+	 * just drop them without bothering to write the contents.
+	 */
+	DropRelFileNodeBuffers(reln->smgr_rnode, forknum, nblocks);
 }
 
 /*