OSDN Git Service

Introduce standby_fencing_command which is used to shoot the standby
authorMasaoFujii <masao.fujii@gmail.com>
Fri, 3 Dec 2010 10:02:50 +0000 (19:02 +0900)
committerMasaoFujii <masao.fujii@gmail.com>
Fri, 3 Dec 2010 10:02:50 +0000 (19:02 +0900)
in the head when replication is terminated for a reason other than
the master server shutdown and emergency bailout (i.e., unexpected
death of postmaster).

src/backend/replication/walsender.c
src/backend/utils/misc/guc.c
src/backend/utils/misc/postgresql.conf.sample
src/include/replication/walsender.h

index bfcb49f..026ab96 100644 (file)
@@ -71,6 +71,7 @@ bool          am_walsender = false;           /* Am I a walsender process ? */
 int                    max_wal_senders = 0;    /* the maximum number of concurrent walsenders */
 int                    WalSndDelay = 200;      /* max sleep time between some actions */
 int                    replication_timeout = 0;        /* maximum time to wait for the Ack from the standby */
+char      *standby_fencing_command = NULL;     /* command to shoot the standby in the head */
 
 /*
  * Buffer for WAL sending
@@ -131,6 +132,7 @@ static void WalSndKill(int code, Datum arg);
 static void XLogRead(char *buf, XLogRecPtr recptr, Size nbytes);
 static bool XLogSend(bool *caughtup, bool *pending);
 static void ProcessStreamMsgs(StringInfo inMsg);
+static void ExecuteStandbyFencingCommand(void);
 
 static void RegisterWalSndWaiter(BackendId backendId, XLogRecPtr record,
                                                                 Latch *latch);
@@ -706,6 +708,16 @@ WalSndKill(int code, Datum arg)
 {
        Assert(MyWalSnd != NULL);
 
+       /*
+        * If replication was terminated for a reason other than the master
+        * server shutdown or emergency bailout (i.e., unexpected death of
+        * postmaster), we can expect this server can work standalone,
+        * so we call standby_fencing_command to shoot the standby server
+        * in the head if it's specified.
+        */
+       if (!ready_to_stop && PostmasterIsAlive(true))
+               ExecuteStandbyFencingCommand();
+
        /* Wake up the backends that this walsender had been blocking */
        MyWalSnd->rplMode = REPLICATION_MODE_ASYNC;
        WakeupWalSndWaiters(GetOldestAckdPtr());
@@ -1026,6 +1038,91 @@ updt:
        return true;
 }
 
+/*
+ * Attempt to execute standby_fencing_command at the end of replication.
+ */
+static void
+ExecuteStandbyFencingCommand(void)
+{
+       char            standbyFencingCmd[MAXPGPATH];
+       char       *dp;
+       char       *endp;
+       const char *sp;
+       int                     rc;
+
+       /* Do nothing if no command supplied */
+       if (standby_fencing_command[0] == '\0')
+               return;
+
+       /*
+        * construct the command to be executed
+        */
+       dp = standbyFencingCmd;
+       endp = standbyFencingCmd + MAXPGPATH - 1;
+       *endp = '\0';
+
+       for (sp = standby_fencing_command; *sp; sp++)
+       {
+               if (*sp == '%')
+               {
+                       switch (sp[1])
+                       {
+                               case 'a':
+                               {
+                                       /* %a: application_name */
+                                       const char *appname = application_name;
+
+                                       if (appname == NULL || *appname == '\0')
+                                               appname = _("[unknown]");
+
+                                       sp++;
+                                       strlcpy(dp, appname, endp - dp);
+                                       dp += strlen(dp);
+                                       break;
+                               }
+                               case '%':
+                                       /* convert %% to a single % */
+                                       sp++;
+                                       if (dp < endp)
+                                               *dp++ = *sp;
+                                       break;
+                               default:
+                                       /* otherwise treat the % as not special */
+                                       if (dp < endp)
+                                               *dp++ = *sp;
+                                       break;
+                       }
+               }
+               else
+               {
+                       if (dp < endp)
+                               *dp++ = *sp;
+               }
+       }
+       *dp = '\0';
+
+       ereport(DEBUG3,
+                       (errmsg_internal("executing standby fencing command \"%s\"",
+                                                        standbyFencingCmd)));
+
+       /*
+        * execute the constructed command
+        */
+       rc = system(standbyFencingCmd);
+       if (rc != 0)
+       {
+               /*
+                * No matter what code is returned, walsender can't stop exiting.
+                * We don't need to care about the return code of the command here.
+                */
+               ereport(WARNING,
+                               (errmsg("standby fencing command failed with return code %d",
+                                               rc),
+                                errdetail("The failed standby fencing command was: %s",
+                                                  standbyFencingCmd)));
+       }
+}
+
 /* SIGHUP: set flag to re-read config file at next convenient time */
 static void
 WalSndSigHupHandler(SIGNAL_ARGS)
index 59dd697..9fd92c6 100644 (file)
@@ -163,6 +163,7 @@ static bool assign_transaction_read_only(bool newval, bool doit, GucSource sourc
 static const char *assign_canonical_path(const char *newval, bool doit, GucSource source);
 static const char *assign_timezone_abbreviations(const char *newval, bool doit, GucSource source);
 static const char *show_archive_command(void);
+static const char *show_standby_fencing_command(void);
 static bool assign_tcp_keepalives_idle(int newval, bool doit, GucSource source);
 static bool assign_tcp_keepalives_interval(int newval, bool doit, GucSource source);
 static bool assign_tcp_keepalives_count(int newval, bool doit, GucSource source);
@@ -2245,6 +2246,15 @@ static struct config_string ConfigureNamesString[] =
        },
 
        {
+               {"standby_fencing_command", PGC_SIGHUP, WAL_REPLICATION,
+                       gettext_noop("Sets the shell command that will be called to shoot the standby in the head."),
+                       NULL
+               },
+               &standby_fencing_command,
+               "", NULL, show_standby_fencing_command
+       },
+
+       {
                {"client_encoding", PGC_USERSET, CLIENT_CONN_LOCALE,
                        gettext_noop("Sets the client's character set encoding."),
                        NULL,
@@ -7931,6 +7941,15 @@ show_archive_command(void)
                return "(disabled)";
 }
 
+static const char *
+show_standby_fencing_command(void)
+{
+       if (max_wal_senders > 0)
+               return standby_fencing_command;
+       else
+               return "(disabled)";
+}
+
 static bool
 assign_tcp_keepalives_idle(int newval, bool doit, GucSource source)
 {
index 5cb6664..967b71c 100644 (file)
 #wal_keep_segments = 0         # in logfile segments, 16MB each; 0 disables
 #vacuum_defer_cleanup_age = 0  # number of xacts by which cleanup is delayed
 #replication_timeout = 0 # in milliseconds, 0 is disabled
+#standby_fencing_command = ''  # command to use to shoot standby
 
 # - Standby Servers -
 
index 95a90cc..b08b6e3 100644 (file)
@@ -67,6 +67,7 @@ extern bool am_walsender;
 extern int     WalSndDelay;
 extern int     max_wal_senders;
 extern int     replication_timeout;
+extern char    *standby_fencing_command;
 
 extern int     WalSenderMain(void);
 extern void WalSndSignals(void);