]> granicus.if.org Git - postgresql/commitdiff
Flush unlogged table's buffers when copying or moving databases.
authorAndres Freund <andres@anarazel.de>
Mon, 20 Oct 2014 21:43:46 +0000 (23:43 +0200)
committerAndres Freund <andres@anarazel.de>
Mon, 20 Oct 2014 21:45:20 +0000 (23:45 +0200)
CREATE DATABASE and ALTER DATABASE .. SET TABLESPACE copy the source
database directory on the filesystem level. To ensure the on disk
state is consistent they block out users of the affected database and
force a checkpoint to flush out all data to disk. Unfortunately, up to
now, that checkpoint didn't flush out dirty buffers from unlogged
relations.

That bug means there could be leftover dirty buffers in either the
template database, or the database in its old location. Leading to
problems when accessing relations in an inconsistent state; and to
possible problems during shutdown in the SET TABLESPACE case because
buffers belonging files that don't exist anymore are flushed.

This was reported in bug #10675 by Maxim Boguk.

Fix by Pavan Deolasee, modified somewhat by me. Reviewed by MauMau and
Fujii Masao.

Backpatch to 9.1 where unlogged tables were introduced.

src/backend/access/transam/xlog.c
src/backend/commands/dbcommands.c
src/backend/storage/buffer/bufmgr.c
src/include/access/xlog.h

index f8ed8eb51708c1b6950af3e9f19645e6b7a5ead7..7e35e0f41f5983dfabc2c7b02b67f5e3657506ec 100644 (file)
@@ -7824,9 +7824,9 @@ LogCheckpointStart(int flags, bool restartpoint)
         * the main message, but what about all the flags?
         */
        if (restartpoint)
-               msg = "restartpoint starting:%s%s%s%s%s%s%s";
+               msg = "restartpoint starting:%s%s%s%s%s%s%s%s";
        else
-               msg = "checkpoint starting:%s%s%s%s%s%s%s";
+               msg = "checkpoint starting:%s%s%s%s%s%s%s%s";
 
        elog(LOG, msg,
                 (flags & CHECKPOINT_IS_SHUTDOWN) ? " shutdown" : "",
@@ -7835,7 +7835,8 @@ LogCheckpointStart(int flags, bool restartpoint)
                 (flags & CHECKPOINT_FORCE) ? " force" : "",
                 (flags & CHECKPOINT_WAIT) ? " wait" : "",
                 (flags & CHECKPOINT_CAUSE_XLOG) ? " xlog" : "",
-                (flags & CHECKPOINT_CAUSE_TIME) ? " time" : "");
+                (flags & CHECKPOINT_CAUSE_TIME) ? " time" : "",
+                (flags & CHECKPOINT_FLUSH_ALL) ? " flush-all" :"");
 }
 
 /*
index f1049d08f72bd8d7f0c334f60361da2616300a32..289310c59d4d987b139ecb6c19942ea3e7f42523 100644 (file)
@@ -527,15 +527,17 @@ createdb(const CreatedbStmt *stmt)
        InvokeObjectPostCreateHook(DatabaseRelationId, dboid, 0);
 
        /*
-        * Force a checkpoint before starting the copy. This will force dirty
-        * buffers out to disk, to ensure source database is up-to-date on disk
-        * for the copy. FlushDatabaseBuffers() would suffice for that, but we
-        * also want to process any pending unlink requests. Otherwise, if a
-        * checkpoint happened while we're copying files, a file might be deleted
-        * just when we're about to copy it, causing the lstat() call in copydir()
-        * to fail with ENOENT.
+        * Force a checkpoint before starting the copy. This will force all dirty
+        * buffers, including those of unlogged tables, out to disk, to ensure
+        * source database is up-to-date on disk for the copy.
+        * FlushDatabaseBuffers() would suffice for that, but we also want
+        * to process any pending unlink requests. Otherwise, if a checkpoint
+        * happened while we're copying files, a file might be deleted just when
+        * we're about to copy it, causing the lstat() call in copydir() to fail
+        * with ENOENT.
         */
-       RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_FORCE | CHECKPOINT_WAIT);
+       RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_FORCE | CHECKPOINT_WAIT
+                                         | CHECKPOINT_FLUSH_ALL);
 
        /*
         * Once we start copying subdirectories, we need to be able to clean 'em
@@ -1116,8 +1118,9 @@ movedb(const char *dbname, const char *tblspcname)
        dst_dbpath = GetDatabasePath(db_id, dst_tblspcoid);
 
        /*
-        * Force a checkpoint before proceeding. This will force dirty buffers out
-        * to disk, to ensure source database is up-to-date on disk for the copy.
+        * Force a checkpoint before proceeding. This will force all dirty
+        * buffers, including those of unlogged tables, out to disk, to ensure
+        * source database is up-to-date on disk for the copy.
         * FlushDatabaseBuffers() would suffice for that, but we also want to
         * process any pending unlink requests. Otherwise, the check for existing
         * files in the target directory might fail unnecessarily, not to mention
@@ -1125,7 +1128,8 @@ movedb(const char *dbname, const char *tblspcname)
         * On Windows, this also ensures that background procs don't hold any open
         * files, which would cause rmdir() to fail.
         */
-       RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_FORCE | CHECKPOINT_WAIT);
+       RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_FORCE | CHECKPOINT_WAIT
+                                         | CHECKPOINT_FLUSH_ALL);
 
        /*
         * Check for existence of files in the target directory, i.e., objects of
index d05d23744332af8927bb96aafb21b8fd1e0e81bc..f5da54472c18069f023e24a4a4cf246a9fc6a741 100644 (file)
@@ -1204,9 +1204,10 @@ UnpinBuffer(volatile BufferDesc *buf, bool fixOwner)
  *
  * This is called at checkpoint time to write out all dirty shared buffers.
  * The checkpoint request flags should be passed in.  If CHECKPOINT_IMMEDIATE
- * is set, we disable delays between writes; if CHECKPOINT_IS_SHUTDOWN is
- * set, we write even unlogged buffers, which are otherwise skipped.  The
- * remaining flags currently have no effect here.
+ * is set, we disable delays between writes; if CHECKPOINT_IS_SHUTDOWN,
+ * CHECKPOINT_END_OF_RECOVERY or CHECKPOINT_FLUSH_ALL is set, we write even
+ * unlogged buffers, which are otherwise skipped.  The remaining flags
+ * currently have no effect here.
  */
 static void
 BufferSync(int flags)
@@ -1221,11 +1222,12 @@ BufferSync(int flags)
        ResourceOwnerEnlargeBuffers(CurrentResourceOwner);
 
        /*
-        * Unless this is a shutdown checkpoint, we write only permanent, dirty
-        * buffers.  But at shutdown or end of recovery, we write all dirty
-        * buffers.
+        * Unless this is a shutdown checkpoint or we have been explicitly told,
+        * we write only permanent, dirty buffers.  But at shutdown or end of
+        * recovery, we write all dirty buffers.
         */
-       if (!((flags & CHECKPOINT_IS_SHUTDOWN) || (flags & CHECKPOINT_END_OF_RECOVERY)))
+       if (!((flags & (CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_END_OF_RECOVERY |
+                                       CHECKPOINT_FLUSH_ALL))))
                mask |= BM_PERMANENT;
 
        /*
index 86a1c40d2a2aee650284e1a967bc0ac29bef896a..84b1110870821830fa0fb979b4bbaa788ca4811b 100644 (file)
@@ -251,6 +251,8 @@ extern bool XLOG_DEBUG;
 /* These indicate the cause of a checkpoint request */
 #define CHECKPOINT_CAUSE_XLOG  0x0020  /* XLOG consumption */
 #define CHECKPOINT_CAUSE_TIME  0x0040  /* Elapsed time */
+#define CHECKPOINT_FLUSH_ALL   0x0080  /* Flush all pages, including those
+                                                                                * belonging to unlogged tables */
 
 /* Checkpoint statistics */
 typedef struct CheckpointStatsData