files in the server log.
Heikki Linnakangas
<!--
-$PostgreSQL: pgsql/doc/src/sgml/maintenance.sgml,v 1.41 2005/02/20 02:21:26 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/maintenance.sgml,v 1.42 2005/05/02 18:26:52 momjian Exp $
-->
<chapter id="maintenance">
</para>
</sect1>
+ <sect1 id="check-files-after-crash">
+ <title>Check files after crash</title>
+
+ <indexterm zone="check-files-after-crash">
+ <primary>stale file</primary>
+ </indexterm>
+
+ <para>
+ <productname>PostgreSQL</productname> recovers automatically after crash
+ using the write-ahead log (see <xref linkend="wal">) and no manual
+ operations are normally needed. However, if there was a transaction running
+ when the crash occured that created or dropped a relation, the
+ transaction might have left a stale file in the data directory. If this
+ happens, you will get a notice in the log file stating which files can be
+ deleted.
+ </para>
+ </sect1>
<sect1 id="logfile-maintenance">
<title>Log File Maintenance</title>
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.189 2005/04/28 21:47:10 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.190 2005/05/02 18:26:52 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "utils/builtins.h"
#include "utils/guc.h"
#include "utils/relcache.h"
+#include "utils/flatfiles.h"
/*
CreateCheckPoint(true, true);
+ CheckStaleRelFiles();
+
/*
* Close down recovery environment
*/
*/
remove_backup_label();
}
+ else
+ {
+ XLogInitRelationCache();
+ CheckStaleRelFiles();
+ XLogCloseRelationCache();
+ }
/*
* Preallocate additional log files, if wanted.
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/catalog.c,v 1.59 2005/04/14 20:03:23 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/catalog.c,v 1.60 2005/05/02 18:26:53 momjian Exp $
*
*-------------------------------------------------------------------------
*/
return path;
}
+/*
+ * GetTablespacePath - construct path to a tablespace symbolic link
+ *
+ * Result is a palloc'd string.
+ *
+ * XXX this must agree with relpath and GetDatabasePath!
+ */
+char *
+GetTablespacePath(Oid spcNode)
+{
+ int pathlen;
+ char *path;
+
+ Assert(spcNode != GLOBALTABLESPACE_OID);
+
+ if (spcNode == DEFAULTTABLESPACE_OID)
+ {
+ /* The default tablespace is {datadir}/base */
+ pathlen = strlen(DataDir) + 5 + 1;
+ path = (char *) palloc(pathlen);
+ snprintf(path, pathlen, "%s/base",
+ DataDir);
+ }
+ else
+ {
+ /* All other tablespaces have symlinks in pg_tblspc */
+ pathlen = strlen(DataDir) + 11 + OIDCHARS + 1;
+ path = (char *) palloc(pathlen);
+ snprintf(path, pathlen, "%s/pg_tblspc/%u",
+ DataDir, spcNode);
+ }
+ return path;
+}
/*
* IsSystemRelation
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.17 2005/04/14 20:03:24 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.18 2005/05/02 18:26:53 momjian Exp $
*
*-------------------------------------------------------------------------
*/
/*
* All seems well, create the symlink
*/
- linkloc = (char *) palloc(strlen(DataDir) + 11 + 10 + 1);
- sprintf(linkloc, "%s/pg_tblspc/%u", DataDir, tablespaceoid);
+ linkloc = GetTablespacePath(tablespaceoid);
if (symlink(location, linkloc) < 0)
ereport(ERROR,
char *subfile;
struct stat st;
- location = (char *) palloc(strlen(DataDir) + 11 + 10 + 1);
- sprintf(location, "%s/pg_tblspc/%u", DataDir, tablespaceoid);
+ location = GetTablespacePath(tablespaceoid);
/*
* Check if the tablespace still contains any files. We try to rmdir
set_short_version(location);
/* Create the symlink if not already present */
- linkloc = (char *) palloc(strlen(DataDir) + 11 + 10 + 1);
- sprintf(linkloc, "%s/pg_tblspc/%u", DataDir, xlrec->ts_id);
+ linkloc = GetTablespacePath(xlrec->ts_id);
if (symlink(location, linkloc) < 0)
{
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/misc.c,v 1.40 2004/12/31 22:01:22 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/misc.c,v 1.41 2005/05/02 18:26:53 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "funcapi.h"
#include "catalog/pg_type.h"
#include "catalog/pg_tablespace.h"
+#include "catalog/catalog.h"
#define atooid(x) ((Oid) strtoul((x), NULL, 10))
fctx = palloc(sizeof(ts_db_fctx));
- /*
- * size = path length + tablespace dirname length + 2 dir sep
- * chars + oid + terminator
- */
- fctx->location = (char *) palloc(strlen(DataDir) + 11 + 10 + 1);
if (tablespaceOid == GLOBALTABLESPACE_OID)
{
fctx->dirdesc = NULL;
}
else
{
- if (tablespaceOid == DEFAULTTABLESPACE_OID)
- sprintf(fctx->location, "%s/base", DataDir);
- else
- sprintf(fctx->location, "%s/pg_tblspc/%u", DataDir,
- tablespaceOid);
-
+ fctx->location = GetTablespacePath(tablespaceOid);
fctx->dirdesc = AllocateDir(fctx->location);
if (!fctx->dirdesc)
# Makefile for utils/init
#
# IDENTIFICATION
-# $PostgreSQL: pgsql/src/backend/utils/init/Makefile,v 1.18 2005/02/20 02:22:00 tgl Exp $
+# $PostgreSQL: pgsql/src/backend/utils/init/Makefile,v 1.19 2005/05/02 18:26:53 momjian Exp $
#
#-------------------------------------------------------------------------
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = flatfiles.o globals.o miscinit.o postinit.o
+OBJS = flatfiles.o globals.o miscinit.o postinit.o checkfiles.o
all: SUBSYS.o
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * checkfiles.c
+ * support to clean up stale relation files on crash recovery
+ *
+ * If a backend crashes while in a transaction that has created or
+ * deleted a relfilenode, a stale file can be left over in the data
+ * directory. This file contains routines to clean up those stale
+ * files on recovery.
+ *
+ * This adds a 17% increase in startup cost for 100 empty databases. bjm
+ * One optimization would be to create a 'dirty' file on a postmaster recovery
+ * and remove the dirty flag only when a clean startup detects no unreferenced
+ * files, and use the 'dirty' flag to determine if we should run this on
+ * a clean startup.
+ *
+ * $PostgreSQL: pgsql/src/backend/utils/init/checkfiles.c,v 1.1 2005/05/02 18:26:53 momjian Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "storage/fd.h"
+
+#include "utils/flatfiles.h"
+#include "miscadmin.h"
+#include "catalog/pg_tablespace.h"
+#include "catalog/catalog.h"
+#include "access/skey.h"
+#include "utils/fmgroids.h"
+#include "access/relscan.h"
+#include "access/heapam.h"
+#include "utils/resowner.h"
+
+static void CheckStaleRelFilesFrom(Oid tablespaceoid, Oid dboid);
+static void CheckStaleRelFilesFromTablespace(Oid tablespaceoid);
+
+/* Like AllocateDir, but ereports on failure */
+static DIR *
+AllocateDirChecked(char *path)
+{
+ DIR *dirdesc = AllocateDir(path);
+
+ if (dirdesc == NULL)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not open directory \"%s\": %m",
+ path)));
+ return dirdesc;
+}
+
+/*
+ * Scan through all tablespaces for relations left over
+ * by aborted transactions.
+ *
+ * For example, if a transaction issues
+ * BEGIN; CREATE TABLE foobar ();
+ * and then the backend crashes, the file is left in the
+ * tablespace until CheckStaleRelFiles deletes it.
+ */
+void
+CheckStaleRelFiles(void)
+{
+ DIR *dirdesc;
+ struct dirent *de;
+ char *path;
+ int pathlen;
+
+ pathlen = strlen(DataDir) + 11 + 1;
+ path = (char *) palloc(pathlen);
+ snprintf(path, pathlen, "%s/pg_tblspc/", DataDir);
+ dirdesc = AllocateDirChecked(path);
+ while ((de = readdir(dirdesc)) != NULL)
+ {
+ char *invalid;
+ Oid tablespaceoid;
+
+ /* Check that the directory name looks like valid tablespace link. */
+ tablespaceoid = (Oid) strtol(de->d_name, &invalid, 10);
+ if (invalid[0] == '\0')
+ CheckStaleRelFilesFromTablespace(tablespaceoid);
+ }
+ FreeDir(dirdesc);
+ pfree(path);
+
+ CheckStaleRelFilesFromTablespace(DEFAULTTABLESPACE_OID);
+}
+
+/* Scan a specific tablespace for stale relations */
+static void
+CheckStaleRelFilesFromTablespace(Oid tablespaceoid)
+{
+ DIR *dirdesc;
+ struct dirent *de;
+ char *path;
+
+ path = GetTablespacePath(tablespaceoid);
+
+ dirdesc = AllocateDirChecked(path);
+ while ((de = readdir(dirdesc)) != NULL)
+ {
+ char *invalid;
+ Oid dboid;
+
+ dboid = (Oid) strtol(de->d_name, &invalid, 10);
+ if (invalid[0] == '\0')
+ CheckStaleRelFilesFrom(tablespaceoid, dboid);
+ }
+ FreeDir(dirdesc);
+ pfree(path);
+}
+
+/* Scan a specific database in a specific tablespace for stale relations.
+ *
+ * First, pg_class for the database is opened, and the relfilenodes of all
+ * relations mentioned there are stored in a hash table.
+ *
+ * Then the directory is scanned. Every file in the directory that's not
+ * found in pg_class (the hash table) is logged.
+ */
+static void
+CheckStaleRelFilesFrom(Oid tablespaceoid, Oid dboid)
+{
+ DIR *dirdesc;
+ struct dirent *de;
+ HASHCTL hashctl;
+ HTAB *relfilenodeHash;
+ MemoryContext mcxt;
+ RelFileNode rnode;
+ char *path;
+
+ /*
+ * We create a private memory context so that we can easily deallocate the
+ * hash table and its contents
+ */
+ mcxt = AllocSetContextCreate(TopMemoryContext, "CheckStaleRelFiles",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+
+ hashctl.hash = tag_hash;
+
+ /*
+ * The entry contents is not used for anything, we just check if an oid is
+ * in the hash table or not.
+ */
+ hashctl.keysize = sizeof(Oid);
+ hashctl.entrysize = 1;
+ hashctl.hcxt = mcxt;
+ relfilenodeHash = hash_create("relfilenodeHash", 100, &hashctl,
+ HASH_FUNCTION
+ | HASH_ELEM | HASH_CONTEXT);
+
+ /* Read all relfilenodes from pg_class into the hash table */
+ {
+ ResourceOwner owner,
+ oldowner;
+ Relation rel;
+ HeapScanDesc scan;
+ HeapTuple tuple;
+
+ /* Need a resowner to keep the heapam and buffer code happy */
+ owner = ResourceOwnerCreate(NULL, "CheckStaleRelFiles");
+ oldowner = CurrentResourceOwner;
+ CurrentResourceOwner = owner;
+
+ rnode.spcNode = tablespaceoid;
+ rnode.dbNode = dboid;
+ rnode.relNode = RelationRelationId;
+ rel = XLogOpenRelation(true, 0, rnode);
+
+ scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
+ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ {
+ Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
+
+ hash_search(relfilenodeHash, &classform->relfilenode,
+ HASH_ENTER, NULL);
+ }
+ heap_endscan(scan);
+
+ XLogCloseRelation(rnode);
+ CurrentResourceOwner = oldowner;
+ ResourceOwnerDelete(owner);
+ }
+
+ /* Scan the directory */
+ path = GetDatabasePath(dboid, tablespaceoid);
+
+ dirdesc = AllocateDirChecked(path);
+ while ((de = readdir(dirdesc)) != NULL)
+ {
+ char *invalid;
+ Oid relfilenode;
+
+ relfilenode = strtol(de->d_name, &invalid, 10);
+ if (invalid[0] == '\0')
+ {
+ /*
+ * Filename was a valid number, check if pg_class knows about it
+ */
+ if (hash_search(relfilenodeHash, &relfilenode,
+ HASH_FIND, NULL) == NULL)
+ {
+ char *filepath;
+
+ rnode.spcNode = tablespaceoid;
+ rnode.dbNode = dboid;
+ rnode.relNode = relfilenode;
+
+ filepath = relpath(rnode);
+
+ ereport(LOG,
+ (errcode_for_file_access(),
+ errmsg("The table or index file \"%s\" is stale and can be safely removed",
+ filepath)));
+ pfree(filepath);
+ }
+ }
+ }
+ FreeDir(dirdesc);
+ pfree(path);
+ hash_destroy(relfilenodeHash);
+ MemoryContextDelete(mcxt);
+}
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/catalog.h,v 1.30 2004/12/31 22:03:24 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catalog.h,v 1.31 2005/05/02 18:26:54 momjian Exp $
*
*-------------------------------------------------------------------------
*/
extern char *relpath(RelFileNode rnode);
extern char *GetDatabasePath(Oid dbNode, Oid spcNode);
+extern char *GetTablespacePath(Oid spcNode);
extern bool IsSystemRelation(Relation relation);
extern bool IsToastRelation(Relation relation);
* Routines for maintaining "flat file" images of the shared catalogs.
*
*
- * $PostgreSQL: pgsql/src/include/utils/flatfiles.h,v 1.1 2005/02/20 02:22:07 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/flatfiles.h,v 1.2 2005/05/02 18:26:54 momjian Exp $
*
*-------------------------------------------------------------------------
*/
extern Datum flatfile_update_trigger(PG_FUNCTION_ARGS);
+/* from checkfiles.c */
+extern void CheckStaleRelFiles(void);
+
#endif /* FLATFILES_H */