Fix recently-identified PITR recovery hazard: the base backup could contain
authorTom Lane
Sun, 5 Nov 2006 23:40:31 +0000 (23:40 +0000)
committerTom Lane
Sun, 5 Nov 2006 23:40:31 +0000 (23:40 +0000)
stale relcache init files (pg_internal.init), and there is no mechanism for
updating them during WAL replay.  Easiest solution is just to delete the init
files at conclusion of startup, and let the first backend started in each
database take care of rebuilding the init file.  Simon Riggs and Tom Lane.

Back-patched to 8.1.  Arguably this should be fixed in 8.0 too, but it would
require significantly more code since 8.0 has no handy startup-time scan of
pg_database to piggyback on.  Manual solution of the problem is possible
in 8.0 (just delete the pg_internal.init files before starting WAL replay),
so that may be a sufficient answer.

src/backend/utils/cache/relcache.c
src/backend/utils/init/flatfiles.c
src/include/utils/relcache.h

index 40e71d513d256b76f39705369dfaa24e2a3098d1..65cd1e72907b155695271c030159445300832c4e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.249 2006/10/04 00:30:00 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.250 2006/11/05 23:40:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -3586,3 +3586,25 @@ RelationCacheInitFileInvalidate(bool beforeSend)
        LWLockRelease(RelCacheInitLock);
    }
 }
+
+/*
+ * Remove the init file for a given database during postmaster startup.
+ *
+ * We used to keep the init file across restarts, but that is unsafe in PITR
+ * scenarios, and even in simple crash-recovery cases there are windows for
+ * the init file to become out-of-sync with the database.  So now we just
+ * remove it during startup and expect the first backend launch to rebuild it.
+ * Of course, this has to happen in each database of the cluster.  For
+ * simplicity this is driven by flatfiles.c, which has to scan pg_database
+ * anyway.
+ */
+void
+RelationCacheInitFileRemove(const char *dbPath)
+{
+   char        initfilename[MAXPGPATH];
+
+   snprintf(initfilename, sizeof(initfilename), "%s/%s",
+            dbPath, RELCACHE_INIT_FILENAME);
+   unlink(initfilename);
+   /* ignore any error, since it might not be there at all */
+}
index dc117145fc0182479803bf906e2f1f16b311e9bf..ec104bb52a7c3a7a1bf0723539a18ecfc7d3acef 100644 (file)
@@ -23,7 +23,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/utils/init/flatfiles.c,v 1.22 2006/11/05 22:42:09 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/init/flatfiles.c,v 1.23 2006/11/05 23:40:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -36,6 +36,7 @@
 #include "access/transam.h"
 #include "access/twophase_rmgr.h"
 #include "access/xact.h"
+#include "catalog/catalog.h"
 #include "catalog/pg_auth_members.h"
 #include "catalog/pg_authid.h"
 #include "catalog/pg_database.h"
@@ -47,6 +48,7 @@
 #include "storage/pmsignal.h"
 #include "utils/builtins.h"
 #include "utils/flatfiles.h"
+#include "utils/relcache.h"
 #include "utils/resowner.h"
 
 
@@ -165,9 +167,14 @@ name_okay(const char *str)
  *
  * A side effect is to determine the oldest database's datfrozenxid
  * so we can set or update the XID wrap limit.
+ *
+ * Also, if "startup" is true, we tell relcache.c to clear out the relcache
+ * init file in each database.  That's a bit nonmodular, but scanning
+ * pg_database twice during system startup seems too high a price for keeping
+ * things better separated.
  */
 static void
-write_database_file(Relation drel)
+write_database_file(Relation drel, bool startup)
 {
    char       *filename,
               *tempname;
@@ -248,6 +255,17 @@ write_database_file(Relation drel)
        fputs_quote(datname, fp);
        fprintf(fp, " %u %u %u\n",
                datoid, dattablespace, datfrozenxid);
+
+       /*
+        * Also clear relcache init file for each DB if starting up.
+        */
+       if (startup)
+       {
+           char *dbpath = GetDatabasePath(datoid, dattablespace);
+
+           RelationCacheInitFileRemove(dbpath);
+           pfree(dbpath);
+       }
    }
    heap_endscan(scan);
 
@@ -669,6 +687,9 @@ write_auth_file(Relation rel_authid, Relation rel_authmem)
  * policy means we need not force initdb to change the format of the
  * flat files.
  *
+ * We also cause relcache init files to be flushed, for largely the same
+ * reasons.
+ *
  * In a standalone backend we pass database_only = true to skip processing
  * the auth file.  We won't need it, and building it could fail if there's
  * something corrupt in the authid/authmem catalogs.
@@ -699,7 +720,7 @@ BuildFlatFiles(bool database_only)
 
    /* No locking is needed because no one else is alive yet */
    rel_db = XLogOpenRelation(rnode);
-   write_database_file(rel_db);
+   write_database_file(rel_db, true);
 
    if (!database_only)
    {
@@ -811,7 +832,7 @@ AtEOXact_UpdateFlatFiles(bool isCommit)
    if (database_file_update_subid != InvalidSubTransactionId)
    {
        database_file_update_subid = InvalidSubTransactionId;
-       write_database_file(drel);
+       write_database_file(drel, false);
        heap_close(drel, NoLock);
    }
 
index aa2b1608c19d951875ffd576aa316c8d6fb6bc40..ae09f4aa82fc6ce17deb72a6676f87a8a932a576 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/relcache.h,v 1.55 2006/07/31 20:09:10 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/relcache.h,v 1.56 2006/11/05 23:40:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -69,6 +69,7 @@ extern void AtEOSubXact_RelationCache(bool isCommit, SubTransactionId mySubid,
  */
 extern bool RelationIdIsInInitFile(Oid relationId);
 extern void RelationCacheInitFileInvalidate(bool beforeSend);
+extern void RelationCacheInitFileRemove(const char *dbPath);
 
 /* should be used only by relcache.c and catcache.c */
 extern bool criticalRelcachesBuilt;