Hi all,

As of now, pg_basebackup creates an empty repository for pg_replslot/
in a base backup, forcing the user to recreate slots on other nodes of
the cluster with pg_create_*_replication_slot, or copy pg_replslot
from another node. This is not really user-friendly especially after a
failover where a given slave may not have the replication slot
information of the master that it is replacing.

The simple patch attached adds a new option in pg_basebackup, called
--replication-slot, allowing to include replication slot information
in a base backup. This is done by extending the command BASE_BACKUP in
the replication protocol.

As it is too late for 9.4, I would like to add it to the first commit
fest of 9.5. Comments are welcome.

Regards,
-- 
Michael
diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml
index 36d16a5..4748d08 100644
--- a/doc/src/sgml/protocol.sgml
+++ b/doc/src/sgml/protocol.sgml
@@ -1837,7 +1837,7 @@ The commands accepted in walsender mode are:
   </varlistentry>
 
   <varlistentry>
-    <term>BASE_BACKUP [<literal>LABEL</literal> 
<replaceable>'label'</replaceable>] [<literal>PROGRESS</literal>] 
[<literal>FAST</literal>] [<literal>WAL</literal>] [<literal>NOWAIT</literal>] 
[<literal>MAX_RATE</literal> <replaceable>rate</replaceable>]</term>
+    <term>BASE_BACKUP [<literal>LABEL</literal> 
<replaceable>'label'</replaceable>] [<literal>PROGRESS</literal>] 
[<literal>FAST</literal>] [<literal>WAL</literal>] [<literal>NOWAIT</literal>] 
[<literal>REPLICATION_SLOT</literal>] [<literal>MAX_RATE</literal> 
<replaceable>rate</replaceable>]</term>
     <listitem>
      <para>
       Instructs the server to start streaming a base backup.
@@ -1909,6 +1909,18 @@ The commands accepted in walsender mode are:
        </varlistentry>
 
        <varlistentry>
+        <term><literal>REPLICATION_SLOT</literal></term>
+        <listitem>
+         <para>
+          By default, the backup will not include the replication slot
+          information in <filename>pg_replslot</> and is created as an
+          empty repository. Specifying <literal>REPLICATION_SLOT</literal>
+          permits to includes replication slot information in the backup.
+         </para>
+        </listitem>
+       </varlistentry>
+
+       <varlistentry>
         <term><literal>MAX_RATE</literal> <replaceable>rate</></term>
         <listitem>
          <para>
diff --git a/doc/src/sgml/ref/pg_basebackup.sgml 
b/doc/src/sgml/ref/pg_basebackup.sgml
index 6ce0c8c..be77f7b 100644
--- a/doc/src/sgml/ref/pg_basebackup.sgml
+++ b/doc/src/sgml/ref/pg_basebackup.sgml
@@ -210,6 +210,17 @@ PostgreSQL documentation
      </varlistentry>
 
      <varlistentry>
+      <term><option>--replication-slot</option></term>
+      <listitem>
+       <para>
+        Include replication slot information in the base backup. This is added
+        in <filename>pg_replslot</filename> by default empty if this option is
+        not specified.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><option>-R</option></term>
       <term><option>--write-recovery-conf</option></term>
       <listitem>
@@ -254,7 +265,7 @@ PostgreSQL documentation
       <term><option>--xlogdir=<replaceable 
class="parameter">xlogdir</replaceable></option></term>
       <listitem>
        <para>
-        Specifies the location for the transaction log directory. 
+        Specifies the location for the transaction log directory.
         <replaceable>xlogdir</replaceable> must be an absolute path.
         The transaction log directory can only be specified when
         the backup is in plain mode.
diff --git a/src/backend/replication/basebackup.c 
b/src/backend/replication/basebackup.c
index f611f59..4a86117 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -45,6 +45,7 @@ typedef struct
        bool            fastcheckpoint;
        bool            nowait;
        bool            includewal;
+       bool            replication_slot;
        uint32          maxrate;
 } basebackup_options;
 
@@ -71,6 +72,9 @@ static bool backup_started_in_recovery = false;
 /* Relative path of temporary statistics directory */
 static char *statrelpath = NULL;
 
+/* Include replication slot data in base backup? */
+static bool include_replication_slots = false;
+
 /*
  * Size of each block sent into the tar stream for larger files.
  */
@@ -131,6 +135,7 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir)
        datadirpathlen = strlen(DataDir);
 
        backup_started_in_recovery = RecoveryInProgress();
+       include_replication_slots = opt->replication_slot;
 
        startptr = do_pg_start_backup(opt->label, opt->fastcheckpoint, 
&starttli,
                                                                  &labelfile);
@@ -548,6 +553,7 @@ parse_basebackup_options(List *options, basebackup_options 
*opt)
        bool            o_nowait = false;
        bool            o_wal = false;
        bool            o_maxrate = false;
+       bool            o_replication_slot = false;
 
        MemSet(opt, 0, sizeof(*opt));
        foreach(lopt, options)
@@ -618,6 +624,15 @@ parse_basebackup_options(List *options, basebackup_options 
*opt)
                        opt->maxrate = (uint32) maxrate;
                        o_maxrate = true;
                }
+               else if (strcmp(defel->defname, "replication_slot") == 0)
+               {
+                       if (o_replication_slot)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_SYNTAX_ERROR),
+                                                errmsg("duplicate option 
\"%s\"", defel->defname)));
+                       opt->replication_slot = true;
+                       o_replication_slot = true;
+               }
                else
                        elog(ERROR, "option \"%s\" not recognized",
                                 defel->defname);
@@ -984,10 +999,11 @@ sendDir(char *path, int basepathlen, bool sizeonly, List 
*tablespaces)
                }
 
                /*
-                * Skip pg_replslot, not useful to copy. But include it as an 
empty
-                * directory anyway, so we get permissions right.
+                * Skip pg_replslot and create it as an empty repository if not
+                * requested in the base backup so we get the permissions right.
                 */
-               if (strcmp(de->d_name, "pg_replslot") == 0)
+               if (!include_replication_slots &&
+                       strcmp(pathbuf, "./pg_replslot") == 0)
                {
                        if (!sizeonly)
                                _tarWriteHeader(pathbuf + basepathlen + 1, 
NULL, &statbuf);
diff --git a/src/backend/replication/repl_gram.y 
b/src/backend/replication/repl_gram.y
index 154aaac..6400a0f 100644
--- a/src/backend/replication/repl_gram.y
+++ b/src/backend/replication/repl_gram.y
@@ -70,6 +70,7 @@ Node *replication_parse_result;
 %token K_FAST
 %token K_NOWAIT
 %token K_MAX_RATE
+%token K_REPLICATION_SLOT
 %token K_WAL
 %token K_TIMELINE
 %token K_PHYSICAL
@@ -119,7 +120,8 @@ identify_system:
                        ;
 
 /*
- * BASE_BACKUP [LABEL '<label>'] [PROGRESS] [FAST] [WAL] [NOWAIT] [MAX_RATE %d]
+ * BASE_BACKUP [LABEL '<label>'] [PROGRESS] [FAST]
+ *                             [WAL] [NOWAIT] [MAX_RATE %d] [REPLICATION_SLOT]
  */
 base_backup:
                        K_BASE_BACKUP base_backup_opt_list
@@ -163,6 +165,11 @@ base_backup_opt:
                                  $$ = makeDefElem("nowait",
                                                                   (Node 
*)makeInteger(TRUE));
                                }
+                       | K_REPLICATION_SLOT
+                               {
+                                 $$ = makeDefElem("replication_slot",
+                                                                  (Node 
*)makeInteger(TRUE));
+                               }
                        | K_MAX_RATE UCONST
                                {
                                  $$ = makeDefElem("max_rate",
diff --git a/src/backend/replication/repl_scanner.l 
b/src/backend/replication/repl_scanner.l
index a257124..acc5092 100644
--- a/src/backend/replication/repl_scanner.l
+++ b/src/backend/replication/repl_scanner.l
@@ -87,6 +87,7 @@ LABEL                 { return K_LABEL; }
 NOWAIT                 { return K_NOWAIT; }
 PROGRESS                       { return K_PROGRESS; }
 MAX_RATE               { return K_MAX_RATE; }
+REPLICATION_SLOT       { return K_REPLICATION_SLOT; }
 WAL                    { return K_WAL; }
 TIMELINE                       { return K_TIMELINE; }
 START_REPLICATION      { return K_START_REPLICATION; }
diff --git a/src/bin/pg_basebackup/pg_basebackup.c 
b/src/bin/pg_basebackup/pg_basebackup.c
index 1a468fa..2ad2474 100644
--- a/src/bin/pg_basebackup/pg_basebackup.c
+++ b/src/bin/pg_basebackup/pg_basebackup.c
@@ -62,6 +62,7 @@ static int    compresslevel = 0;
 static bool    includewal = false;
 static bool    streamwal = false;
 static bool    fastcheckpoint = false;
+static bool    replicationslot = false;
 static bool    writerecoveryconf = false;
 static int     standby_message_timeout = 10 * 1000;            /* 10 sec = 
default */
 static pg_time_t last_progress_report = 0;
@@ -229,6 +230,7 @@ usage(void)
        printf(_("  -D, --pgdata=DIRECTORY receive base backup into 
directory\n"));
        printf(_("  -F, --format=p|t       output format (plain (default), 
tar)\n"));
        printf(_("  -r, --max-rate=RATE    maximum transfer rate to transfer 
data directory\n"));
+       printf(_("      --replication-slot include replication slot data in 
base backup\n"));
        printf(_("  -R, --write-recovery-conf\n"
                         "                         write recovery.conf after 
backup\n"));
        printf(_("  -T, --tablespace-mapping=OLDDIR=NEWDIR\n"
@@ -1659,13 +1661,14 @@ BaseBackup(void)
                maxrate_clause = psprintf("MAX_RATE %u", maxrate);
 
        basebkp =
-               psprintf("BASE_BACKUP LABEL '%s' %s %s %s %s %s",
+               psprintf("BASE_BACKUP LABEL '%s' %s %s %s %s %s %s",
                                 escaped_label,
                                 showprogress ? "PROGRESS" : "",
                                 includewal && !streamwal ? "WAL" : "",
                                 fastcheckpoint ? "FAST" : "",
                                 includewal ? "NOWAIT" : "",
-                                maxrate_clause ? maxrate_clause : "");
+                                maxrate_clause ? maxrate_clause : "",
+                                replicationslot ? "REPLICATION_SLOT" : "");
 
        if (PQsendQuery(conn, basebkp) == 0)
        {
@@ -1965,6 +1968,7 @@ main(int argc, char **argv)
                {"verbose", no_argument, NULL, 'v'},
                {"progress", no_argument, NULL, 'P'},
                {"xlogdir", required_argument, NULL, 1},
+               {"replication-slot", no_argument, NULL, 2},
                {NULL, 0, NULL, 0}
        };
        int                     c;
@@ -2058,6 +2062,9 @@ main(int argc, char **argv)
                        case 1:
                                xlog_dir = pg_strdup(optarg);
                                break;
+                       case 2:
+                               replicationslot = true;
+                               break;
                        case 'l':
                                label = pg_strdup(optarg);
                                break;
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to