diff --git a/dbutils.c b/dbutils.c index e2b1b82c..c4486820 100644 --- a/dbutils.c +++ b/dbutils.c @@ -2966,3 +2966,50 @@ add_extension_tables_to_bdr_replication_set(PGconn *conn) return; } + + +RecordStatus +get_bdr_init_node_record(PGconn *conn, t_bdr_node_info *node_info) +{ + PQExpBufferData query; + PGresult *res; + + initPQExpBuffer(&query); + + appendPQExpBuffer( + &query, + " SELECT node_sysid, " + " node_timeline, " + " node_dboid, " + " node_status, " + " node_name, " + " node_local_dsn, " + " node_init_from_dsn, " + " node_read_only, " + " node_seq_id " + " FROM bdr.bdr_nodes " + " WHERE node_init_from_dsn IS NULL " + ); + + res = PQexec(conn, query.data); + termPQExpBuffer(&query); + + if (!res || PQresultStatus(res) != PGRES_TUPLES_OK) + { + // + } + else + { + strncpy(node_info->node_sysid, PQgetvalue(res, 0, 0), MAXLEN); + node_info->node_timeline = atoi(PQgetvalue(res, 0, 1)); + node_info->node_dboid = atoi(PQgetvalue(res, 0, 2)); + // node_status 3 + strncpy(node_info->node_name, PQgetvalue(res, 0, 4), MAXLEN); + strncpy(node_info->node_local_dsn, PQgetvalue(res, 0, 5), MAXLEN); + strncpy(node_info->node_init_from_dsn, PQgetvalue(res, 0, 6), MAXLEN); + } + + PQclear(res); + return RECORD_FOUND; +} + diff --git a/dbutils.h b/dbutils.h index 8253ecf5..757d6722 100644 --- a/dbutils.h +++ b/dbutils.h @@ -144,6 +144,27 @@ typedef struct s_connection_user bool is_superuser; } t_connection_user; + +typedef struct s_bdr_node_info +{ + char node_sysid[MAXLEN]; + uint32 node_timeline; + uint32 node_dboid; + char node_status; + char node_name[MAXLEN]; + char node_local_dsn[MAXLEN]; + char node_init_from_dsn[MAXLEN]; + bool read_only; + uint32 node_seq_id; +} t_bdr_node_info; + + +#define T_BDR_NODE_INFO_INITIALIZER { \ + "", InvalidOid, InvalidOid, \ + '?', "", "", "", \ + false, -1 \ +} + /* utility functions */ XLogRecPtr parse_lsn(const char *str); @@ -267,13 +288,14 @@ void reset_voting_status(PGconn *conn); XLogRecPtr get_last_wal_receive_location(PGconn *conn); /* BDR functions */ +RecordStatus get_bdr_init_node_record(PGconn *conn, t_bdr_node_info *node_info); +bool is_bdr_db(PGconn *conn); +bool is_bdr_repmgr(PGconn *conn); +bool is_table_in_bdr_replication_set(PGconn *conn, const char *tablename, const char *set); +bool add_table_to_bdr_replication_set(PGconn *conn, const char *tablename, const char *set); +void add_extension_tables_to_bdr_replication_set(PGconn *conn); -bool is_bdr_db(PGconn *conn); -bool is_bdr_repmgr(PGconn *conn); -bool is_table_in_bdr_replication_set(PGconn *conn, const char *tablename, const char *set); -bool add_table_to_bdr_replication_set(PGconn *conn, const char *tablename, const char *set); -bool bdr_node_exists(PGconn *conn, const char *node_name); -void add_extension_tables_to_bdr_replication_set(PGconn *conn); +bool bdr_node_exists(PGconn *conn, const char *node_name); #endif /* dbutils.h */ diff --git a/repmgr-action-bdr.c b/repmgr-action-bdr.c index da497748..c5836d52 100644 --- a/repmgr-action-bdr.c +++ b/repmgr-action-bdr.c @@ -68,11 +68,11 @@ do_bdr_register(void) log_error(_("repmgr metadatabase contains records for non-BDR nodes")); exit(ERR_BAD_CONFIG); } + } else { - //log_info(_("bdr register: creating database objects inside the %s schema"), - // get_repmgr_schema()); + log_info(_("creating repmgr extension")); begin_transaction(conn); @@ -87,6 +87,31 @@ do_bdr_register(void) commit_transaction(conn); } + // any other BDR nodes - if so connect to one where "node_init_from_dsn" is null, + // and copy repmgr.nodes + // (we'll assume all other nodes are up-to-date) + // don't copy other tables... + { + PGconn *init_node; + RecordStatus bdr_record_status; + t_bdr_node_info bdr_init_node_info = T_BDR_NODE_INFO_INITIALIZER; + + bdr_record_status = get_bdr_init_node_record(conn, &bdr_init_node_info); + + if (bdr_record_status == RECORD_FOUND) + { + if (strncmp(node_info.node_name, bdr_init_node_info.node_name, MAXLEN) != 0) + { + init_node = establish_db_connection_quiet(bdr_init_node_info.node_init_from_dsn); + + } + } + + } + + /* Add the repmgr extension tables to a replication set */ + add_extension_tables_to_bdr_replication_set(conn); + /* check for a matching BDR node */ { bool node_exists = bdr_node_exists(conn, config_file_options.node_name); @@ -217,5 +242,100 @@ do_bdr_register(void) void do_bdr_unregister(void) { + PGconn *conn; + ExtensionStatus extension_status; + int target_node_id; + t_node_info node_info = T_NODE_INFO_INITIALIZER; + RecordStatus record_status; + bool node_record_deleted; + PQExpBufferData event_details; + + /* sanity-check configuration for BDR-compatability */ + + if (config_file_options.replication_type != REPLICATION_TYPE_BDR) + { + log_error(_("cannot run BDR UNREGISTER on a non-BDR node")); + exit(ERR_BAD_CONFIG); + } + + conn = establish_db_connection(config_file_options.conninfo, true); + + if (!is_bdr_db(conn)) + { + /* TODO: name database */ + log_error(_("database is not BDR-enabled")); + exit(ERR_BAD_CONFIG); + } + + extension_status = get_repmgr_extension_status(conn); + if (extension_status != REPMGR_INSTALLED) + { + log_error(_("repmgr is not installed on this database")); + exit(ERR_BAD_CONFIG); + } + + if (!is_bdr_repmgr(conn)) + { + log_error(_("repmgr metadatabase contains records for non-BDR nodes")); + exit(ERR_BAD_CONFIG); + } + + initPQExpBuffer(&event_details); + if (runtime_options.node_id != UNKNOWN_NODE_ID) + target_node_id = runtime_options.node_id; + else + target_node_id = config_file_options.node_id; + + + /* Check node exists and is really a BDR node */ + record_status = get_node_record(conn, target_node_id, &node_info); + + if (record_status != RECORD_FOUND) + { + log_error(_("no record found for node %i"), target_node_id); + PQfinish(conn); + exit(ERR_BAD_CONFIG); + } + + // BDR node + + begin_transaction(conn); + + log_info(_("unregistering node %i"), target_node_id); + + node_record_deleted = delete_node_record(conn, target_node_id); + + if (node_record_deleted == false) + { + appendPQExpBuffer(&event_details, + "unable to delete node record for node \"%s\" (ID: %i)", + node_info.node_name, + target_node_id); + } + else + { + appendPQExpBuffer(&event_details, + "node record deleted for node \"%s\" (ID: %i)", + node_info.node_name, + target_node_id); + } + commit_transaction(conn); + + /* Log the event */ + create_event_notification( + conn, + &config_file_options, + config_file_options.node_id, + "bdr_unregister", + true, + event_details.data); + + PQfinish(conn); + + log_notice(_("bdr node \"%s\" (ID: %i) successfully unregistered"), + node_info.node_name, target_node_id); + + termPQExpBuffer(&event_details); + return; } diff --git a/repmgr-client.c b/repmgr-client.c index 03a660cd..255fcd82 100644 --- a/repmgr-client.c +++ b/repmgr-client.c @@ -1379,12 +1379,6 @@ create_repmgr_extension(PGconn *conn) PQclear(res); - /* For BDR, we'll need to add the repmgr extension tables to a replication set */ - if (config_file_options.replication_type == REPLICATION_TYPE_BDR) - { - add_extension_tables_to_bdr_replication_set(conn); - } - /* 5. If not superuser, grant usage */ if (is_superuser == false) {