]> granicus.if.org Git - postgresql/blobdiff - src/backend/postmaster/bgwriter.c
Improve the recently-added support for properly pluralized error messages
[postgresql] / src / backend / postmaster / bgwriter.c
index 823c5243797f90782d9628a81cba11b7f32bb126..b2a90528b65ddd9240bcf77477e7ef64b4aa22df 100644 (file)
  * restart needs to be forced.)
  *
  *
- * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/postmaster/bgwriter.c,v 1.51 2008/08/11 11:05:11 heikki Exp $
+ *       $PostgreSQL: pgsql/src/backend/postmaster/bgwriter.c,v 1.59 2009/06/04 18:33:07 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include <unistd.h>
 
 #include "access/xlog_internal.h"
+#include "catalog/pg_control.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "postmaster/bgwriter.h"
 #include "storage/bufmgr.h"
 #include "storage/fd.h"
-#include "storage/freespace.h"
 #include "storage/ipc.h"
 #include "storage/lwlock.h"
 #include "storage/pmsignal.h"
@@ -398,7 +398,6 @@ BackgroundWriterMain(void)
                        ExitOnAnyError = true;
                        /* Close down the database */
                        ShutdownXLOG(0, 0);
-                       DumpFreeSpaceMap(0, 0);
                        /* Normal exit from the bgwriter is here */
                        proc_exit(0);           /* done */
                }
@@ -425,9 +424,19 @@ BackgroundWriterMain(void)
                 */
                if (do_checkpoint)
                {
+                       bool    ckpt_performed = false;
+                       bool    do_restartpoint;
+
                        /* use volatile pointer to prevent code rearrangement */
                        volatile BgWriterShmemStruct *bgs = BgWriterShmem;
 
+                       /*
+                        * Check if we should perform a checkpoint or a restartpoint.
+                        * As a side-effect, RecoveryInProgress() initializes
+                        * TimeLineID if it's not set yet.
+                        */
+                       do_restartpoint = RecoveryInProgress();
+
                        /*
                         * Atomically fetch the request flags to figure out what kind of a
                         * checkpoint we should perform, and increase the started-counter
@@ -446,25 +455,35 @@ BackgroundWriterMain(void)
                         * implementation will not generate warnings caused by
                         * CheckPointTimeout < CheckPointWarning.
                         */
-                       if ((flags & CHECKPOINT_CAUSE_XLOG) &&
+                       if (!do_restartpoint &&
+                               (flags & CHECKPOINT_CAUSE_XLOG) &&
                                elapsed_secs < CheckPointWarning)
                                ereport(LOG,
-                                               (errmsg("checkpoints are occurring too frequently (%d seconds apart)",
-                                                               elapsed_secs),
+                                               (errmsg_plural("checkpoints are occurring too frequently (%d second apart)",
+                                                                          "checkpoints are occurring too frequently (%d seconds apart)",
+                                                                          elapsed_secs,
+                                                                          elapsed_secs),
                                                 errhint("Consider increasing the configuration parameter \"checkpoint_segments\".")));
 
                        /*
                         * Initialize bgwriter-private variables used during checkpoint.
                         */
                        ckpt_active = true;
-                       ckpt_start_recptr = GetInsertRecPtr();
+                       if (!do_restartpoint)
+                               ckpt_start_recptr = GetInsertRecPtr();
                        ckpt_start_time = now;
                        ckpt_cached_elapsed = 0;
 
                        /*
                         * Do the checkpoint.
                         */
-                       CreateCheckPoint(flags);
+                       if (!do_restartpoint)
+                       {
+                               CreateCheckPoint(flags);
+                               ckpt_performed = true;
+                       }
+                       else
+                               ckpt_performed = CreateRestartPoint(flags);
 
                        /*
                         * After any checkpoint, close all smgr files.  This is so we
@@ -479,14 +498,27 @@ BackgroundWriterMain(void)
                        bgs->ckpt_done = bgs->ckpt_started;
                        SpinLockRelease(&bgs->ckpt_lck);
 
-                       ckpt_active = false;
+                       if (ckpt_performed)
+                       {
+                               /*
+                                * Note we record the checkpoint start time not end time as
+                                * last_checkpoint_time.  This is so that time-driven
+                                * checkpoints happen at a predictable spacing.
+                                */
+                               last_checkpoint_time = now;
+                       }
+                       else
+                       {
+                               /*
+                                * We were not able to perform the restartpoint (checkpoints
+                                * throw an ERROR in case of error).  Most likely because we
+                                * have not received any new checkpoint WAL records since the
+                                * last restartpoint. Try again in 15 s.
+                                */
+                               last_checkpoint_time = now - CheckPointTimeout + 15;
+                       }
 
-                       /*
-                        * Note we record the checkpoint start time not end time as
-                        * last_checkpoint_time.  This is so that time-driven checkpoints
-                        * happen at a predictable spacing.
-                        */
-                       last_checkpoint_time = now;
+                       ckpt_active = false;
                }
                else
                        BgBufferSync();
@@ -509,7 +541,7 @@ CheckArchiveTimeout(void)
        pg_time_t       now;
        pg_time_t       last_time;
 
-       if (XLogArchiveTimeout <= 0)
+       if (XLogArchiveTimeout <= 0 || RecoveryInProgress())
                return;
 
        now = (pg_time_t) time(NULL);
@@ -716,16 +748,19 @@ IsCheckpointOnSchedule(double progress)
         * However, it's good enough for our purposes, we're only calculating an
         * estimate anyway.
         */
-       recptr = GetInsertRecPtr();
-       elapsed_xlogs =
-               (((double) (int32) (recptr.xlogid - ckpt_start_recptr.xlogid)) * XLogSegsPerFile +
-                ((double) recptr.xrecoff - (double) ckpt_start_recptr.xrecoff) / XLogSegSize) /
-               CheckPointSegments;
-
-       if (progress < elapsed_xlogs)
+       if (!RecoveryInProgress())
        {
-               ckpt_cached_elapsed = elapsed_xlogs;
-               return false;
+               recptr = GetInsertRecPtr();
+               elapsed_xlogs =
+                       (((double) (int32) (recptr.xlogid - ckpt_start_recptr.xlogid)) * XLogSegsPerFile +
+                        ((double) recptr.xrecoff - (double) ckpt_start_recptr.xrecoff) / XLogSegSize) /
+                       CheckPointSegments;
+
+               if (progress < elapsed_xlogs)
+               {
+                       ckpt_cached_elapsed = elapsed_xlogs;
+                       return false;
+               }
        }
 
        /*
@@ -763,14 +798,22 @@ bg_quickdie(SIGNAL_ARGS)
        PG_SETMASK(&BlockSig);
 
        /*
-        * DO NOT proc_exit() -- we're here because shared memory may be
-        * corrupted, so we don't want to try to clean up our transaction. Just
-        * nail the windows shut and get out of town.
-        *
+        * We DO NOT want to run proc_exit() callbacks -- we're here because
+        * shared memory may be corrupted, so we don't want to try to clean up our
+        * transaction.  Just nail the windows shut and get out of town.  Now that
+        * there's an atexit callback to prevent third-party code from breaking
+        * things by calling exit() directly, we have to reset the callbacks
+        * explicitly to make this work as intended.
+        */
+       on_exit_reset();
+
+       /*
         * Note we do exit(2) not exit(0).      This is to force the postmaster into a
         * system reset cycle if some idiot DBA sends a manual SIGQUIT to a random
         * backend.  This is necessary precisely because we don't clean up our
-        * shared memory state.
+        * shared memory state.  (The "dead man switch" mechanism in pmsignal.c
+        * should ensure the postmaster sees this as a crash, too, but no harm
+        * in being doubly sure.)
         */
        exit(2);
 }
@@ -866,6 +909,7 @@ RequestCheckpoint(int flags)
 {
        /* use volatile pointer to prevent code rearrangement */
        volatile BgWriterShmemStruct *bgs = BgWriterShmem;
+       int                     ntries;
        int                     old_failed,
                                old_started;
 
@@ -907,15 +951,38 @@ RequestCheckpoint(int flags)
        SpinLockRelease(&bgs->ckpt_lck);
 
        /*
-        * Send signal to request checkpoint.  When not waiting, we consider
-        * failure to send the signal to be nonfatal.
+        * Send signal to request checkpoint.  It's possible that the bgwriter
+        * hasn't started yet, or is in process of restarting, so we will retry
+        * a few times if needed.  Also, if not told to wait for the checkpoint
+        * to occur, we consider failure to send the signal to be nonfatal and
+        * merely LOG it.
         */
-       if (BgWriterShmem->bgwriter_pid == 0)
-               elog((flags & CHECKPOINT_WAIT) ? ERROR : LOG,
-                        "could not request checkpoint because bgwriter not running");
-       if (kill(BgWriterShmem->bgwriter_pid, SIGINT) != 0)
-               elog((flags & CHECKPOINT_WAIT) ? ERROR : LOG,
-                        "could not signal for checkpoint: %m");
+       for (ntries = 0; ; ntries++)
+       {
+               if (BgWriterShmem->bgwriter_pid == 0)
+               {
+                       if (ntries >= 20)               /* max wait 2.0 sec */
+                       {
+                               elog((flags & CHECKPOINT_WAIT) ? ERROR : LOG,
+                                        "could not request checkpoint because bgwriter not running");
+                               break;
+                       }
+               }
+               else if (kill(BgWriterShmem->bgwriter_pid, SIGINT) != 0)
+               {
+                       if (ntries >= 20)               /* max wait 2.0 sec */
+                       {
+                               elog((flags & CHECKPOINT_WAIT) ? ERROR : LOG,
+                                        "could not signal for checkpoint: %m");
+                               break;
+                       }
+               }
+               else
+                       break;                          /* signal sent successfully */
+
+               CHECK_FOR_INTERRUPTS();
+               pg_usleep(100000L);             /* wait 0.1 sec, then retry */
+       }
 
        /*
         * If requested, wait for completion.  We detect completion according to
@@ -1014,6 +1081,7 @@ ForwardFsyncRequest(RelFileNode rnode, ForkNumber forknum, BlockNumber segno)
        }
        request = &BgWriterShmem->requests[BgWriterShmem->num_requests++];
        request->rnode = rnode;
+       request->forknum = forknum;
        request->segno = segno;
        LWLockRelease(BgWriterCommLock);
        return true;