mirror of
https://github.com/postgres/postgres.git
synced 2025-11-10 17:42:29 +03:00
Add option to control snapshot export to CREATE_REPLICATION_SLOT
We used to export snapshots unconditionally in CREATE_REPLICATION_SLOT in the replication protocol, but several upcoming patches want more control over what happens. Suppress snapshot export in pg_recvlogical, which neither needs nor can use the exported snapshot. Since snapshot exporting can fail this improves reliability. This also paves the way for allowing the creation of replication slots on standbys, which cannot export snapshots because they cannot allocate new XIDs. Author: Petr Jelinek <petr.jelinek@2ndquadrant.com>
This commit is contained in:
@@ -68,6 +68,7 @@ static void libpqrcv_send(WalReceiverConn *conn, const char *buffer,
|
||||
static char *libpqrcv_create_slot(WalReceiverConn *conn,
|
||||
const char *slotname,
|
||||
bool temporary,
|
||||
bool export_snapshot,
|
||||
XLogRecPtr *lsn);
|
||||
static bool libpqrcv_command(WalReceiverConn *conn,
|
||||
const char *cmd, char **err);
|
||||
@@ -720,7 +721,7 @@ libpqrcv_send(WalReceiverConn *conn, const char *buffer, int nbytes)
|
||||
*/
|
||||
static char *
|
||||
libpqrcv_create_slot(WalReceiverConn *conn, const char *slotname,
|
||||
bool temporary, XLogRecPtr *lsn)
|
||||
bool temporary, bool export_snapshot, XLogRecPtr *lsn)
|
||||
{
|
||||
PGresult *res;
|
||||
StringInfoData cmd;
|
||||
@@ -728,13 +729,19 @@ libpqrcv_create_slot(WalReceiverConn *conn, const char *slotname,
|
||||
|
||||
initStringInfo(&cmd);
|
||||
|
||||
appendStringInfo(&cmd, "CREATE_REPLICATION_SLOT \"%s\" ", slotname);
|
||||
appendStringInfo(&cmd, "CREATE_REPLICATION_SLOT \"%s\"", slotname);
|
||||
|
||||
if (temporary)
|
||||
appendStringInfo(&cmd, "TEMPORARY ");
|
||||
appendStringInfo(&cmd, " TEMPORARY");
|
||||
|
||||
if (conn->logical)
|
||||
appendStringInfo(&cmd, "LOGICAL pgoutput");
|
||||
{
|
||||
appendStringInfo(&cmd, " LOGICAL pgoutput");
|
||||
if (export_snapshot)
|
||||
appendStringInfo(&cmd, " EXPORT_SNAPSHOT");
|
||||
else
|
||||
appendStringInfo(&cmd, " NOEXPORT_SNAPSHOT");
|
||||
}
|
||||
|
||||
res = libpqrcv_PQexec(conn->streamConn, cmd.data);
|
||||
pfree(cmd.data);
|
||||
|
||||
@@ -79,6 +79,8 @@ Node *replication_parse_result;
|
||||
%token K_SLOT
|
||||
%token K_RESERVE_WAL
|
||||
%token K_TEMPORARY
|
||||
%token K_EXPORT_SNAPSHOT
|
||||
%token K_NOEXPORT_SNAPSHOT
|
||||
|
||||
%type <node> command
|
||||
%type <node> base_backup start_replication start_logical_replication
|
||||
@@ -91,7 +93,9 @@ Node *replication_parse_result;
|
||||
%type <defelt> plugin_opt_elem
|
||||
%type <node> plugin_opt_arg
|
||||
%type <str> opt_slot var_name
|
||||
%type <boolval> opt_reserve_wal opt_temporary
|
||||
%type <boolval> opt_temporary
|
||||
%type <list> create_slot_opt_list
|
||||
%type <defelt> create_slot_opt
|
||||
|
||||
%%
|
||||
|
||||
@@ -202,18 +206,18 @@ base_backup_opt:
|
||||
|
||||
create_replication_slot:
|
||||
/* CREATE_REPLICATION_SLOT slot TEMPORARY PHYSICAL RESERVE_WAL */
|
||||
K_CREATE_REPLICATION_SLOT IDENT opt_temporary K_PHYSICAL opt_reserve_wal
|
||||
K_CREATE_REPLICATION_SLOT IDENT opt_temporary K_PHYSICAL create_slot_opt_list
|
||||
{
|
||||
CreateReplicationSlotCmd *cmd;
|
||||
cmd = makeNode(CreateReplicationSlotCmd);
|
||||
cmd->kind = REPLICATION_KIND_PHYSICAL;
|
||||
cmd->slotname = $2;
|
||||
cmd->temporary = $3;
|
||||
cmd->reserve_wal = $5;
|
||||
cmd->options = $5;
|
||||
$$ = (Node *) cmd;
|
||||
}
|
||||
/* CREATE_REPLICATION_SLOT slot TEMPORARY LOGICAL plugin */
|
||||
| K_CREATE_REPLICATION_SLOT IDENT opt_temporary K_LOGICAL IDENT
|
||||
| K_CREATE_REPLICATION_SLOT IDENT opt_temporary K_LOGICAL IDENT create_slot_opt_list
|
||||
{
|
||||
CreateReplicationSlotCmd *cmd;
|
||||
cmd = makeNode(CreateReplicationSlotCmd);
|
||||
@@ -221,10 +225,36 @@ create_replication_slot:
|
||||
cmd->slotname = $2;
|
||||
cmd->temporary = $3;
|
||||
cmd->plugin = $5;
|
||||
cmd->options = $6;
|
||||
$$ = (Node *) cmd;
|
||||
}
|
||||
;
|
||||
|
||||
create_slot_opt_list:
|
||||
create_slot_opt_list create_slot_opt
|
||||
{ $$ = lappend($1, $2); }
|
||||
| /* EMPTY */
|
||||
{ $$ = NIL; }
|
||||
;
|
||||
|
||||
create_slot_opt:
|
||||
K_EXPORT_SNAPSHOT
|
||||
{
|
||||
$$ = makeDefElem("export_snapshot",
|
||||
(Node *)makeInteger(TRUE), -1);
|
||||
}
|
||||
| K_NOEXPORT_SNAPSHOT
|
||||
{
|
||||
$$ = makeDefElem("export_snapshot",
|
||||
(Node *)makeInteger(FALSE), -1);
|
||||
}
|
||||
| K_RESERVE_WAL
|
||||
{
|
||||
$$ = makeDefElem("reserve_wal",
|
||||
(Node *)makeInteger(TRUE), -1);
|
||||
}
|
||||
;
|
||||
|
||||
/* DROP_REPLICATION_SLOT slot */
|
||||
drop_replication_slot:
|
||||
K_DROP_REPLICATION_SLOT IDENT
|
||||
@@ -291,11 +321,6 @@ opt_physical:
|
||||
| /* EMPTY */
|
||||
;
|
||||
|
||||
opt_reserve_wal:
|
||||
K_RESERVE_WAL { $$ = true; }
|
||||
| /* EMPTY */ { $$ = false; }
|
||||
;
|
||||
|
||||
opt_temporary:
|
||||
K_TEMPORARY { $$ = true; }
|
||||
| /* EMPTY */ { $$ = false; }
|
||||
|
||||
@@ -100,6 +100,8 @@ RESERVE_WAL { return K_RESERVE_WAL; }
|
||||
LOGICAL { return K_LOGICAL; }
|
||||
SLOT { return K_SLOT; }
|
||||
TEMPORARY { return K_TEMPORARY; }
|
||||
EXPORT_SNAPSHOT { return K_EXPORT_SNAPSHOT; }
|
||||
NOEXPORT_SNAPSHOT { return K_NOEXPORT_SNAPSHOT; }
|
||||
|
||||
"," { return ','; }
|
||||
";" { return ';'; }
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
|
||||
#include "catalog/pg_type.h"
|
||||
#include "commands/dbcommands.h"
|
||||
#include "commands/defrem.h"
|
||||
#include "funcapi.h"
|
||||
#include "libpq/libpq.h"
|
||||
#include "libpq/pqformat.h"
|
||||
@@ -737,6 +738,48 @@ logical_read_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr, int req
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process extra options given to CREATE_REPLICATION_SLOT.
|
||||
*/
|
||||
static void
|
||||
parseCreateReplSlotOptions(CreateReplicationSlotCmd *cmd,
|
||||
bool *reserve_wal,
|
||||
bool *export_snapshot)
|
||||
{
|
||||
ListCell *lc;
|
||||
bool snapshot_action_given = false;
|
||||
bool reserve_wal_given = false;
|
||||
|
||||
/* Parse options */
|
||||
foreach (lc, cmd->options)
|
||||
{
|
||||
DefElem *defel = (DefElem *) lfirst(lc);
|
||||
|
||||
if (strcmp(defel->defname, "export_snapshot") == 0)
|
||||
{
|
||||
if (snapshot_action_given || cmd->kind != REPLICATION_KIND_LOGICAL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("conflicting or redundant options")));
|
||||
|
||||
snapshot_action_given = true;
|
||||
*export_snapshot = defGetBoolean(defel);
|
||||
}
|
||||
else if (strcmp(defel->defname, "reserve_wal") == 0)
|
||||
{
|
||||
if (reserve_wal_given || cmd->kind != REPLICATION_KIND_PHYSICAL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("conflicting or redundant options")));
|
||||
|
||||
reserve_wal_given = true;
|
||||
*reserve_wal = true;
|
||||
}
|
||||
else
|
||||
elog(ERROR, "unrecognized option: %s", defel->defname);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new replication slot.
|
||||
*/
|
||||
@@ -746,6 +789,8 @@ CreateReplicationSlot(CreateReplicationSlotCmd *cmd)
|
||||
const char *snapshot_name = NULL;
|
||||
char xpos[MAXFNAMELEN];
|
||||
char *slot_name;
|
||||
bool reserve_wal = false;
|
||||
bool export_snapshot = true;
|
||||
DestReceiver *dest;
|
||||
TupOutputState *tstate;
|
||||
TupleDesc tupdesc;
|
||||
@@ -754,6 +799,8 @@ CreateReplicationSlot(CreateReplicationSlotCmd *cmd)
|
||||
|
||||
Assert(!MyReplicationSlot);
|
||||
|
||||
parseCreateReplSlotOptions(cmd, &reserve_wal, &export_snapshot);
|
||||
|
||||
/* setup state for XLogReadPage */
|
||||
sendTimeLineIsHistoric = false;
|
||||
sendTimeLine = ThisTimeLineID;
|
||||
@@ -799,10 +846,13 @@ CreateReplicationSlot(CreateReplicationSlotCmd *cmd)
|
||||
DecodingContextFindStartpoint(ctx);
|
||||
|
||||
/*
|
||||
* Export a plain (not of the snapbuild.c type) snapshot to the user
|
||||
* that can be imported into another session.
|
||||
* Export the snapshot if we've been asked to do so.
|
||||
*
|
||||
* NB. We will convert the snapbuild.c kind of snapshot to normal
|
||||
* snapshot when doing this.
|
||||
*/
|
||||
snapshot_name = SnapBuildExportSnapshot(ctx->snapshot_builder);
|
||||
if (export_snapshot)
|
||||
snapshot_name = SnapBuildExportSnapshot(ctx->snapshot_builder);
|
||||
|
||||
/* don't need the decoding context anymore */
|
||||
FreeDecodingContext(ctx);
|
||||
@@ -810,7 +860,7 @@ CreateReplicationSlot(CreateReplicationSlotCmd *cmd)
|
||||
if (!cmd->temporary)
|
||||
ReplicationSlotPersist();
|
||||
}
|
||||
else if (cmd->kind == REPLICATION_KIND_PHYSICAL && cmd->reserve_wal)
|
||||
else if (cmd->kind == REPLICATION_KIND_PHYSICAL && reserve_wal)
|
||||
{
|
||||
ReplicationSlotReserveWal();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user