From 5cdf9953cdbf38fca3bb8e7c19d6e1425b456877 Mon Sep 17 00:00:00 2001 From: Craig Ringer Date: Wed, 21 Dec 2016 11:21:46 +0800 Subject: [PATCH 07/10] Make snapshot export on logical slot creation optional Allow logical decoding slot creation via the walsender protocol's CREATE_REPLICATION_SLOT command to optionally suppress exporting of a snapshot when the WITHOUT_SNAPSHOT option is passed. This means that when we allow creation of replication slots on standbys, which cannot export snapshots, we don't have to silently omit the snapshot creation. It also allows clients like pg_recvlogical, which neither need nor can use the exported snapshot, to suppress its creation. Since snapshot exporting can fail this improves reliability. --- doc/src/sgml/logicaldecoding.sgml | 13 ++++++++++--- doc/src/sgml/protocol.sgml | 17 +++++++++++++++-- src/backend/replication/repl_gram.y | 13 ++++++++++--- src/backend/replication/repl_scanner.l | 1 + src/backend/replication/walsender.c | 9 ++++++++- src/bin/pg_basebackup/streamutil.c | 5 +++++ src/include/nodes/replnodes.h | 1 + 7 files changed, 50 insertions(+), 9 deletions(-) diff --git a/doc/src/sgml/logicaldecoding.sgml b/doc/src/sgml/logicaldecoding.sgml index 484915d..c0b6987 100644 --- a/doc/src/sgml/logicaldecoding.sgml +++ b/doc/src/sgml/logicaldecoding.sgml @@ -268,11 +268,12 @@ $ pg_recvlogical -d postgres --slot test --drop-slot - + Exported Snapshots - When a new replication slot is created using the streaming replication interface, - a snapshot is exported + When a new replication + slot is created using the streaming replication interface, a snapshot + is exported (see ), which will show exactly the state of the database after which all changes will be included in the change stream. This can be used to create a new replica by @@ -282,6 +283,12 @@ $ pg_recvlogical -d postgres --slot test --drop-slot database's state at that point in time, which afterwards can be updated using the slot's contents without losing any changes. + + Creation of a snapshot is not always possible - in particular, it will + fail when connected to a hot standby. Applications that do not require + snapshot export may suppress it with the WITHOUT_SNAPSHOT + option. + diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml index 9ba147c..e41c650 100644 --- a/doc/src/sgml/protocol.sgml +++ b/doc/src/sgml/protocol.sgml @@ -1433,8 +1433,8 @@ The commands accepted in walsender mode are: - - CREATE_REPLICATION_SLOT slot_name [ TEMPORARY ] { PHYSICAL [ RESERVE_WAL ] | LOGICAL output_plugin } + + CREATE_REPLICATION_SLOT slot_name [ TEMPORARY ] { PHYSICAL [ RESERVE_WAL ] | LOGICAL output_plugin [WITHOUT_SNAPSHOT] } CREATE_REPLICATION_SLOT @@ -1485,6 +1485,19 @@ The commands accepted in walsender mode are: + + + WITHOUT_SNAPSHOT + + + By default, logical replication slot creation exports a snapshot for + use in initialization; see . + Because not all clients need an exported snapshot its creation can + be suppressed with WITHOUT_SNAPSHOT. + + + + diff --git a/src/backend/replication/repl_gram.y b/src/backend/replication/repl_gram.y index 8cc9edd..edf2ca4 100644 --- a/src/backend/replication/repl_gram.y +++ b/src/backend/replication/repl_gram.y @@ -78,6 +78,7 @@ Node *replication_parse_result; %token K_SLOT %token K_RESERVE_WAL %token K_TEMPORARY +%token K_WITHOUT_SNAPSHOT %type command %type base_backup start_replication start_logical_replication @@ -90,7 +91,7 @@ Node *replication_parse_result; %type plugin_opt_elem %type plugin_opt_arg %type opt_slot -%type opt_reserve_wal opt_temporary +%type opt_reserve_wal opt_temporary opt_without_snapshot %% @@ -194,8 +195,8 @@ create_replication_slot: cmd->reserve_wal = $5; $$ = (Node *) cmd; } - /* CREATE_REPLICATION_SLOT slot TEMPORARY LOGICAL plugin */ - | K_CREATE_REPLICATION_SLOT IDENT opt_temporary K_LOGICAL IDENT + /* CREATE_REPLICATION_SLOT slot TEMPORARY LOGICAL plugin WITHOUT_SNAPSHOT */ + | K_CREATE_REPLICATION_SLOT IDENT opt_temporary K_LOGICAL IDENT opt_without_snapshot { CreateReplicationSlotCmd *cmd; cmd = makeNode(CreateReplicationSlotCmd); @@ -203,6 +204,7 @@ create_replication_slot: cmd->slotname = $2; cmd->temporary = $3; cmd->plugin = $5; + cmd->without_snapshot = $6; $$ = (Node *) cmd; } ; @@ -283,6 +285,11 @@ opt_temporary: | /* EMPTY */ { $$ = false; } ; +opt_without_snapshot: + K_WITHOUT_SNAPSHOT { $$ = true; } + | /* EMPTY */ { $$ = false; } + ; + opt_slot: K_SLOT IDENT { $$ = $2; } diff --git a/src/backend/replication/repl_scanner.l b/src/backend/replication/repl_scanner.l index 9f50ce6..9874f18 100644 --- a/src/backend/replication/repl_scanner.l +++ b/src/backend/replication/repl_scanner.l @@ -96,6 +96,7 @@ DROP_REPLICATION_SLOT { return K_DROP_REPLICATION_SLOT; } TIMELINE_HISTORY { return K_TIMELINE_HISTORY; } PHYSICAL { return K_PHYSICAL; } RESERVE_WAL { return K_RESERVE_WAL; } +WITHOUT_SNAPSHOT { return K_WITHOUT_SNAPSHOT; } LOGICAL { return K_LOGICAL; } SLOT { return K_SLOT; } TEMPORARY { return K_TEMPORARY; } diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c index acb3370..04f9adb 100644 --- a/src/backend/replication/walsender.c +++ b/src/backend/replication/walsender.c @@ -849,7 +849,14 @@ CreateReplicationSlot(CreateReplicationSlotCmd *cmd) * Export a plain (not of the snapbuild.c type) snapshot to the user * that can be imported into another session. */ - snapshot_name = SnapBuildExportSnapshot(ctx->snapshot_builder); + if (cmd->without_snapshot) + snapshot_name = ""; + else if (RecoveryInProgress()) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("cannot export a snapshot from a standby"))); + else + snapshot_name = SnapBuildExportSnapshot(ctx->snapshot_builder); /* don't need the decoding context anymore */ FreeDecodingContext(ctx); diff --git a/src/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c index 595eaff..7b1b2ee 100644 --- a/src/bin/pg_basebackup/streamutil.c +++ b/src/bin/pg_basebackup/streamutil.c @@ -346,8 +346,13 @@ CreateReplicationSlot(PGconn *conn, const char *slot_name, const char *plugin, appendPQExpBuffer(query, "CREATE_REPLICATION_SLOT \"%s\" PHYSICAL", slot_name); else + { appendPQExpBuffer(query, "CREATE_REPLICATION_SLOT \"%s\" LOGICAL \"%s\"", slot_name, plugin); + if (PQserverVersion(conn) >= 100000) + /* pg_recvlogical doesn't use an exported snapshot, so suppress */ + appendPQExpBuffer(query, " WITHOUT_SNAPSHOT"); + } res = PQexec(conn, query->data); if (PQresultStatus(res) != PGRES_TUPLES_OK) diff --git a/src/include/nodes/replnodes.h b/src/include/nodes/replnodes.h index 024b965..3504b59 100644 --- a/src/include/nodes/replnodes.h +++ b/src/include/nodes/replnodes.h @@ -57,6 +57,7 @@ typedef struct CreateReplicationSlotCmd char *plugin; bool temporary; bool reserve_wal; + bool without_snapshot; } CreateReplicationSlotCmd; -- 2.5.5