repmgr: enable "witness unregister" to be run on any node

Provide the ID of the witness node with --node-id=...

Implements GitHub #472.
This commit is contained in:
Ian Barwick
2018-07-13 17:37:19 +09:00
parent 5acb3e6790
commit 69782cf703
6 changed files with 108 additions and 62 deletions

11
HISTORY
View File

@@ -11,11 +11,12 @@
repmgr: add -q/--quiet option to suppress non-error output; GitHub #468 (Ian) repmgr: add -q/--quiet option to suppress non-error output; GitHub #468 (Ian)
repmgr: "node status" returns non-zero value if an issue encountered (Ian) repmgr: "node status" returns non-zero value if an issue encountered (Ian)
repmgr: enable "recovery_min_apply_delay" to be 0; GitHub #448 (Ian) repmgr: enable "recovery_min_apply_delay" to be 0; GitHub #448 (Ian)
repmgr: "cluster cleanup" - add missing help options; GitHub #461/#462 (gclough) repmgr: "cluster cleanup" - add missing help options; GitHub #461/#462 (gclough)
repmgr: ensure witness node follows new primary after switchover; repmgr: ensure witness node follows new primary after switchover;
GitHub #453 (Ian) GitHub #453 (Ian)
repmgr: fix witness node handling in "node check"/"node status"; repmgr: fix witness node handling in "node check"/"node status";
GitHub #451 (Ian) GitHub #451 (Ian)
repmgr: enable "witness unregister" to be run on any node; GitHub #472 (Ian)
repmgrd: create a PID file by default; GitHub #457 (Ian) repmgrd: create a PID file by default; GitHub #457 (Ian)
repmgrd: daemonize process by default; GitHub #458 (Ian) repmgrd: daemonize process by default; GitHub #458 (Ian)

View File

@@ -98,6 +98,14 @@
</para> </para>
</listitem> </listitem>
<listitem>
<para>
<command><link linkend="repmgr-witness-unregister">repmgr witness unregister</link></command>
can be run on any node, by providing the ID of the witness node with <option>--node-id</option>.
(GitHub #472).
</para>
</listitem>
</itemizedlist> </itemizedlist>
</para> </para>
</sect2> </sect2>

View File

@@ -17,7 +17,7 @@
<title>Description</title> <title>Description</title>
<para> <para>
<command>repmgr primary register</command> registers a primary node in a <command>repmgr primary register</command> registers a primary node in a
streaming replication cluster, and configures it for use with repmgr, including streaming replication cluster, and configures it for use with &repmgr;, including
installing the &repmgr; extension. This command needs to be executed before any installing the &repmgr; extension. This command needs to be executed before any
standby nodes are registered. standby nodes are registered.
</para> </para>

View File

@@ -20,7 +20,10 @@
</para> </para>
<para> <para>
The node does not have to be running to be unregistered, however if this is the The node does not have to be running to be unregistered, however if this is the
case then connection information for the primary server must be provided. case then either provide connection information for the primary server, or
execute <command>repmgr witness unregister</command> on a running node and
provide the parameter <option>--node-id</option> with the node ID of the
witness server.
</para> </para>
<para> <para>
Execute with the <literal>--dry-run</literal> option to check what would happen Execute with the <literal>--dry-run</literal> option to check what would happen
@@ -36,17 +39,17 @@
INFO: connecting to witness node "node3" (ID: 3) INFO: connecting to witness node "node3" (ID: 3)
INFO: unregistering witness node 3 INFO: unregistering witness node 3
INFO: witness unregistration complete INFO: witness unregistration complete
DETAIL: witness node with id 3 (conninfo: host=node3 dbname=repmgr user=repmgr port=5499) successfully unregistered</programlisting> DETAIL: witness node with UD 3 successfully unregistered</programlisting>
</para> </para>
<para> <para>
Unregistering a non-running witness node: Unregistering a non-running witness node:
<programlisting> <programlisting>
$ repmgr -f /etc/repmgr.conf witness unregister -h node1 -p 5501 -F $ repmgr -f /etc/repmgr.conf witness unregister -h node1 -p 5501 -F
INFO: connecting to witness node "node3" (ID: 3) INFO: connecting to node "node3" (ID: 3)
NOTICE: unable to connect to witness node "node3" (ID: 3), removing node record on cluster primary only NOTICE: unable to connect to node "node3" (ID: 3), removing node record on cluster primary only
INFO: unregistering witness node 3 INFO: unregistering witness node 3
INFO: witness unregistration complete INFO: witness unregistration complete
DETAIL: witness node with id 3 (conninfo: host=node3 dbname=repmgr user=repmgr port=5499) successfully unregistered</programlisting> DETAIL: witness node with id ID 3 successfully unregistered</programlisting>
</para> </para>
</refsect1> </refsect1>
@@ -62,6 +65,32 @@
</para> </para>
</refsect1> </refsect1>
<refsect1>
<title>Options</title>
<variablelist>
<varlistentry>
<term><option>--dry-run</option></term>
<listitem>
<para>
Check prerequisites but don't actually unregister the witness.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--node-id</option></term>
<listitem>
<para>
Unregister witness server with the specified node ID.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1> <refsect1>
<title>Event notifications</title> <title>Event notifications</title>

View File

@@ -310,55 +310,59 @@ do_witness_register(void)
void void
do_witness_unregister(void) do_witness_unregister(void)
{ {
PGconn *witness_conn = NULL; PGconn *local_conn = NULL;
PGconn *primary_conn = NULL; PGconn *primary_conn = NULL;
t_node_info node_record = T_NODE_INFO_INITIALIZER; t_node_info node_record = T_NODE_INFO_INITIALIZER;
RecordStatus record_status = RECORD_NOT_FOUND; RecordStatus record_status = RECORD_NOT_FOUND;
bool node_record_deleted = false; bool node_record_deleted = false;
bool witness_available = true; bool local_node_available = true;
int witness_node_id = UNKNOWN_NODE_ID;
log_info(_("connecting to witness node \"%s\" (ID: %i)"), if (runtime_options.node_id != UNKNOWN_NODE_ID)
{
/* user has specified the witness node id */
witness_node_id = runtime_options.node_id;
}
else
{
/* assume witness node is local node */
witness_node_id = config_file_options.node_id;
}
log_info(_("connecting to node \"%s\" (ID: %i)"),
config_file_options.node_name, config_file_options.node_name,
config_file_options.node_id); config_file_options.node_id);
witness_conn = establish_db_connection_quiet(config_file_options.conninfo); local_conn = establish_db_connection_quiet(config_file_options.conninfo);
if (PQstatus(witness_conn) != CONNECTION_OK) if (PQstatus(local_conn) != CONNECTION_OK)
{ {
if (!runtime_options.force) if (!runtime_options.force)
{ {
log_error(_("unable to connect to witness node \"%s\" (ID: %i)"), log_error(_("unable to connect to node \"%s\" (ID: %i)"),
config_file_options.node_name, config_file_options.node_name,
config_file_options.node_id); config_file_options.node_id);
log_detail("%s", PQerrorMessage(witness_conn)); log_detail("%s", PQerrorMessage(local_conn));
log_hint(_("provide -F/--force to remove the witness record if the server is not running"));
exit(ERR_BAD_CONFIG); exit(ERR_BAD_CONFIG);
} }
log_notice(_("unable to connect to witness node \"%s\" (ID: %i), removing node record on cluster primary only"), log_notice(_("unable to connect to witness node \"%s\" (ID: %i), removing node record on cluster primary only"),
config_file_options.node_name, config_file_options.node_name,
config_file_options.node_id); config_file_options.node_id);
witness_available = false; local_node_available = false;
} }
if (witness_available == true) if (local_node_available == true)
{ {
primary_conn = get_primary_connection_quiet(witness_conn, NULL, NULL); primary_conn = get_primary_connection_quiet(local_conn, NULL, NULL);
} }
else else
{ {
/* /*
* Extract the repmgr user and database names from the conninfo string * Assume user has provided connection details for the primary server
* provided in repmgr.conf
*/ */
get_conninfo_value(config_file_options.conninfo, "user", repmgr_user);
get_conninfo_value(config_file_options.conninfo, "dbname", repmgr_db);
param_set_ine(&source_conninfo, "user", repmgr_user);
param_set_ine(&source_conninfo, "dbname", repmgr_db);
primary_conn = establish_db_connection_by_params(&source_conninfo, false); primary_conn = establish_db_connection_by_params(&source_conninfo, false);
} }
if (PQstatus(primary_conn) != CONNECTION_OK) if (PQstatus(primary_conn) != CONNECTION_OK)
@@ -366,26 +370,26 @@ do_witness_unregister(void)
log_error(_("unable to connect to primary")); log_error(_("unable to connect to primary"));
log_detail("%s", PQerrorMessage(primary_conn)); log_detail("%s", PQerrorMessage(primary_conn));
if (witness_available == true) if (local_node_available == true)
{ {
PQfinish(witness_conn); PQfinish(local_conn);
} }
else else if (runtime_options.connection_param_provided == false)
{ {
log_hint(_("provide connection details to primary server")); log_hint(_("provide connection details for the primary server"));
} }
exit(ERR_BAD_CONFIG); exit(ERR_BAD_CONFIG);
} }
/* Check node exists and is really a witness */ /* Check node exists and is really a witness */
record_status = get_node_record(primary_conn, config_file_options.node_id, &node_record); record_status = get_node_record(primary_conn, witness_node_id, &node_record);
if (record_status != RECORD_FOUND) if (record_status != RECORD_FOUND)
{ {
log_error(_("no record found for node %i"), config_file_options.node_id); log_error(_("no record found for node %i"), witness_node_id);
if (witness_available == true) if (local_node_available == true)
PQfinish(witness_conn); PQfinish(local_conn);
PQfinish(primary_conn); PQfinish(primary_conn);
exit(ERR_BAD_CONFIG); exit(ERR_BAD_CONFIG);
@@ -393,11 +397,17 @@ do_witness_unregister(void)
if (node_record.type != WITNESS) if (node_record.type != WITNESS)
{ {
/*
* The node (either explicitly provided with --node-id, or the local node)
* is not a witness.
*
* TODO: scan node list and print hint about identity of known witness servers.
*/
log_error(_("node %i is not a witness node"), config_file_options.node_id); log_error(_("node %i is not a witness node"), config_file_options.node_id);
log_detail(_("node %i is a %s node"), config_file_options.node_id, get_node_type_string(node_record.type)); log_detail(_("node %i is a %s node"), config_file_options.node_id, get_node_type_string(node_record.type));
if (witness_available == true) if (local_node_available == true)
PQfinish(witness_conn); PQfinish(local_conn);
PQfinish(primary_conn); PQfinish(primary_conn);
exit(ERR_BAD_CONFIG); exit(ERR_BAD_CONFIG);
@@ -406,49 +416,43 @@ do_witness_unregister(void)
if (runtime_options.dry_run == true) if (runtime_options.dry_run == true)
{ {
log_info(_("prerequisites for unregistering the witness node are met")); log_info(_("prerequisites for unregistering the witness node are met"));
if (witness_available == true) if (local_node_available == true)
PQfinish(witness_conn); PQfinish(local_conn);
PQfinish(primary_conn); PQfinish(primary_conn);
exit(SUCCESS); exit(SUCCESS);
} }
log_info(_("unregistering witness node %i"), config_file_options.node_id); log_info(_("unregistering witness node %i"), witness_node_id);
node_record_deleted = delete_node_record(primary_conn, node_record_deleted = delete_node_record(primary_conn,
config_file_options.node_id); witness_node_id);
if (node_record_deleted == false) if (node_record_deleted == false)
{ {
PQfinish(primary_conn); PQfinish(primary_conn);
PQfinish(witness_conn);
exit(ERR_BAD_CONFIG);
}
/* sync records from primary */ if (local_node_available == true)
if (witness_available == true && witness_copy_node_records(primary_conn, witness_conn) == false) PQfinish(local_conn);
{ PQfinish(local_conn);
log_error(_("unable to copy repmgr node records from primary"));
PQfinish(primary_conn);
PQfinish(witness_conn);
exit(ERR_BAD_CONFIG); exit(ERR_BAD_CONFIG);
} }
/* Log the event */ /* Log the event */
create_event_record(primary_conn, create_event_record(primary_conn,
&config_file_options, &config_file_options,
config_file_options.node_id, witness_node_id,
"witness_unregister", "witness_unregister",
true, true,
NULL); NULL);
PQfinish(primary_conn); PQfinish(primary_conn);
if (witness_available == true) if (local_node_available == true)
PQfinish(witness_conn); PQfinish(local_conn);
log_info(_("witness unregistration complete")); log_info(_("witness unregistration complete"));
log_detail(_("witness node with id %i (conninfo: %s) successfully unregistered"), log_detail(_("witness node with ID %i successfully unregistered"),
config_file_options.node_id, config_file_options.conninfo); witness_node_id);
return; return;
} }
@@ -468,16 +472,19 @@ void do_witness_help(void)
puts(""); puts("");
printf(_(" Requires provision of connection information for the primary\n")); printf(_(" Requires provision of connection information for the primary\n"));
puts(""); puts("");
printf(_(" --dry-run check prerequisites but don't make any changes\n")); printf(_(" --dry-run check prerequisites but don't make any changes\n"));
printf(_(" -F, --force overwrite an existing node record\n")); printf(_(" -F, --force overwrite an existing node record\n"));
puts(""); puts("");
printf(_("WITNESS UNREGISTER\n")); printf(_("WITNESS UNREGISTER\n"));
puts(""); puts("");
printf(_(" \"witness register\" unregisters a witness node.\n")); printf(_(" \"witness register\" unregisters a witness node.\n"));
puts(""); puts("");
printf(_(" --dry-run check prerequisites but don't make any changes\n")); printf(_(" --dry-run check prerequisites but don't make any changes\n"));
printf(_(" -F, --force unregister when witness node not running\n")); printf(_(" -F, --force unregister when witness node not running\n"));
printf(_(" --node-id node ID of the witness node (provide if executing on\n"));
printf(_(" another node)\n"));
puts(""); puts("");
return; return;

View File

@@ -1519,6 +1519,7 @@ check_cli_parameters(const int action)
{ {
case PRIMARY_UNREGISTER: case PRIMARY_UNREGISTER:
case STANDBY_UNREGISTER: case STANDBY_UNREGISTER:
case WITNESS_UNREGISTER:
case CLUSTER_EVENT: case CLUSTER_EVENT:
case CLUSTER_MATRIX: case CLUSTER_MATRIX:
case CLUSTER_CROSSCHECK: case CLUSTER_CROSSCHECK: