Compare commits

..

55 Commits
v3.3 ... v3.3.2

Author SHA1 Message Date
Ian Barwick
318f1dac40 Update HISTORY 2017-05-29 11:43:30 +09:00
Ian Barwick
bda4b0995c Patch Makefile from downstream
Per GitHub #282

Ref:
  https://anonscm.debian.org/cgit/pkg-postgresql/repmgr.git/tree/debian/patches/makefile-no-libs.patch
2017-05-22 22:26:27 +09:00
Ian Barwick
c14449f0a7 Fix build on PostgreSQL older than the current libpq
Make sure that the includedir_internal directory is used before the
includedir_server, otherwise the build may fail for PostgreSQL
version lower than the libpq version.

Backpatched from downstream:
  https://anonscm.debian.org/cgit/pkg-postgresql/repmgr.git/tree/debian/patches/makefile-libpq-internal.patch

Per GitHub #282
2017-05-22 22:26:21 +09:00
Ian Barwick
557e34b70c Add basic regression test from downstream
Per GitHub #282
Ref: https://anonscm.debian.org/cgit/pkg-postgresql/repmgr.git/tree/debian/patches/regress.patch
2017-05-22 22:26:15 +09:00
Ian Barwick
333083869b repmgrd: fix more PostgreSQL 10 WAL function renamings 2017-05-22 22:25:53 +09:00
Ian Barwick
57fae00844 repmgrd: support latest round of PostgreSQL 10 WAL function renamings 2017-05-22 11:02:22 +09:00
Ian Barwick
3de336f1c0 Update HISTORY 2017-05-22 08:44:54 +09:00
Ian Barwick
5493b57443 repmgr: parse --no-slot in pg_basebackup_options
From PostgreSQL 10 we'll need to know whether this is present when
performing sanity checks for available replication slots.

Add a sanity check for conflicting presence of -S/--slot while we're
at it so we can abort early.
2017-05-22 08:40:49 +09:00
Ian Barwick
e53f1bf844 repmgrd: support further renamed WAL function for PostgreSQL 10 2017-05-22 08:38:12 +09:00
Ian Barwick
90638811c8 repmgr: support further renamed WAL function for PostreSQL 10
pg_xlogfile_name() -> pg_walfile_name()
2017-05-22 08:37:44 +09:00
Ian Barwick
892e3b93d1 repmgr: support --wal-method (replacing --xlog-method) for pg_basebackup in PostgreSQL 10 2017-05-22 08:35:30 +09:00
Ian Barwick
6f15a7e52e repmgr: initial support for PostgreSQL 10
Handle directory name change from pg_xlog to pg_wal.

Note that some functions with "xlog" in their name may also change.
2017-05-22 08:26:17 +09:00
Ian Barwick
98998f73bf repmgrd: remove unnecessary inclusions
Per gripe in GitHub #303
2017-05-22 08:13:39 +09:00
Ian Barwick
34ac2d8141 Bump version
3.3.2
2017-05-15 08:46:21 +09:00
Ian Barwick
c820b61f28 Update HISTORY 2017-05-15 08:45:11 +09:00
Ian Barwick
9e620656c5 Minor log/comment fixes 2017-05-15 08:25:52 +09:00
Ian Barwick
2fa277cc53 repmgr: fix --replication-user option when using conninfo string
In "standby clone", if a conninfo string was provided, this was passed
as-is to pg_basebackup - rewrite conninfo string to include the
value passed with --replication-user, if provided.
2017-05-05 09:28:53 +09:00
Ian Barwick
6a4f5944a1 repmgr: add missing '-P' option
Addresses GitHub #293
2017-05-05 09:22:52 +09:00
Ian Barwick
c02a12a113 repmgrd: actually call repmgr_update_last_updated()
Function was created but never actually used, resulting in incorrect
values for "communication_time_lag" in the "repl_status" view.

This appears to have been an oversight in the original commit
( c3b58658ad ).

Addresses GitHub #290
2017-05-05 09:22:46 +09:00
Ian Barwick
01b3933922 repmgr: master register - remove superfluous transaction start
Also make quotation mark usage in logging output consistent.
2017-05-05 09:22:09 +09:00
Ian Barwick
39b3b32814 repmgr: improve detection of pg_rewind on remote server
If `pg_bindir` is not explicitly provided, the remote `ls` command
will be `ls pg_rewind`, which will very likely not find pg_rewind.
In this case execute `which pg_rewind` to confirm it's in the default
path.

Addresses GitHub #267.
2017-05-05 09:22:03 +09:00
Ian Barwick
846e0f73b2 repmgr: avoid spurious cluster name errors during 'standby switchover'
'standby restore-config' doesn't require a configuration file, but
pass it anyway.

Addresses GitHub #269
2017-05-05 09:21:56 +09:00
Ian Barwick
7467525c8d Add log_detail() method 2017-05-05 09:21:51 +09:00
Simon Riggs
b27a94ccbe Typo in failover docs, reported by VaoTsun 2017-05-05 09:21:47 +09:00
Simon Riggs
2e69d155da Typo in README.md reported by dhx 2017-05-05 09:21:43 +09:00
Ian Barwick
870a367d3b repmgr: prevent spurious error message when running 'standby switchover'
When 'repmgr standby follow' is run on a dormant server, with connection
parameters for the upstream node provided (which is done during the
switchover process to reintegrate the stopped former master into the
replication cluster), a spurious error message is generated about
a slot which cannot be deleted as it's active. During the switchover
process the current master's (former standby's) slot on the former master
is deleted at a later point so can be skipped here.

The error message is annoying but harmless and has no effect on the
switchover process.

Addresses GitHub #285.
2017-04-10 23:00:10 +09:00
Ian Barwick
9c28d3626b Update HISTORY 2017-03-20 14:20:52 +09:00
Ian Barwick
0916d8f2ad repmgr: disallow node ids which are not positive signed 32 bit integers
Fixes GitHub #280
2017-03-20 11:26:34 +09:00
Ian Barwick
1964f890be repmgr: enable master register --force with node foreign key dependency
Fixes GitHub #273
2017-03-20 10:03:38 +09:00
Ian Barwick
976a61005e Only attempt to set synchronous transaction mode with valid connection 2017-03-17 20:33:29 +09:00
Ian Barwick
0c82278fd4 repmgr: fix command line parsing with hostname as an additional argument
Check explicitly whether -h/--hostname provided, otherwise PGHOST,
if set, will be misinterpreted.
2017-03-17 20:21:13 +09:00
Ian Barwick
0abfde3773 repmgr: ensure that data dir permissions set correctly when cloning in barman mode 2017-03-17 16:37:49 +09:00
Ian Barwick
1746831486 repmgr: fix standby clone with barman
As of Barman commit 5ff62d3255, `pg_notify`
is also excluded from Barman backups.
2017-03-17 16:32:35 +09:00
Ian Barwick
8c8e368a69 repmgr: set "synchronous_commit" to "local" by default
Rather than set this for individual connections, we'll change the setting
each time a connection is made (except replication connections), which will
obviate the need to take this into consideration when making connections
in the application code.

Resolves GitHub #276.
2017-03-17 11:32:25 +09:00
Ian Barwick
0ef532dcff repmgr: improve standby clone when synchronous replication in use
Fixes GitHub #277
2017-03-16 16:46:08 +09:00
Ian Barwick
478407fd86 repmgr: fix standby clone with barman
As of Barman commit 5ff62d3255, `pg_notify`
is also excluded from Barman backups.
2017-03-16 10:29:50 +09:00
Ian Barwick
05bfdfab2c repmgr: fix command line parsing with hostname as an additional argument
Check explicitly whether -h/--hostname provided, otherwise PGHOST,
if set, will be misinterpreted.
2017-03-14 22:55:27 +09:00
Ian Barwick
29740dc41b Bump version
3.3.1
2017-03-13 16:00:44 +09:00
Ian Barwick
ad6ecef2ab repmgr: clean up standby follow code 2017-03-13 13:27:02 +09:00
Ian Barwick
5318d37462 README: fix typos 2017-03-13 13:20:56 +09:00
Ian Barwick
7244dda20f repmgr: fix typo 2017-03-13 13:20:35 +09:00
Ian Barwick
e651284927 Update documentation/sample configuration with references to --wal-method 2017-03-13 13:20:20 +09:00
Ian Barwick
72a2ac284a repmgr: improve logging of rsync actions
In particular, copy_remote_files() would report any kind of non-zero
exit status from rsync as an error, even though when cloning data
directories and tablespaces we explicitly ignore the "vanished
files" status (code 24) as it's expected behaviour for files in these
locations to disappear during the rsync copy process.

Conflicts:
	HISTORY
2017-03-13 13:16:44 +09:00
Ian Barwick
cec01c6620 repmgr: improve standby follow log output
In particular suppress any error messages encountered when trying to
connect to the old upstream node, as these are not critical and
will lead to confusion.
2017-03-13 13:14:19 +09:00
Ian Barwick
989f683bc6 repmgr: have "standby follow" delete old replication slot, if possible
Addresses GitHub #272
2017-03-13 13:14:05 +09:00
Ian Barwick
fa30382f2c When retrieving a node record, set upstream_node_id correctly.
-1 (NO_UPSTREAM_NODE) should be returned if the record's column is NULL.
2017-03-13 12:16:22 +09:00
Martin
defc2653e0 There where 2 barman configuration parameters missing in the repmgr.conf
sample file.

Added with some comments
2017-02-15 17:17:39 -03:00
Ian Barwick
67e8ca73b5 repmgrd: fix XLogRecPtr conversion function 2017-01-11 15:03:13 +09:00
Ian Barwick
a1a1d64e1f repmgrd: fix usage description
Matches the one provided by repmgr.
2017-01-11 15:03:07 +09:00
Ian Barwick
76509038cc repmgrd: prevent invalid apply lag value being written to the monitoring table 2017-01-11 15:03:02 +09:00
Ian Barwick
7f8e50c882 Update copyright notice to 2017
Also standardize case to "(c)"
2017-01-11 15:02:55 +09:00
Ian Barwick
5deb6c8ce4 rempgr: don't link to backend functions
The intent was to avoid maintaining duplicate code, but this approach
makes it difficult to build Debian packages (see GitHub #261).

As the functions in question are quite compact and unlikely to change,
we'll just use the adapted versions provided for 9.5 and earlier.
2017-01-04 16:55:09 +09:00
Ian Barwick
175ee8acfc README: update version information 2017-01-04 10:59:13 +09:00
Ian Barwick
d1491f51a3 Remove erroneously added configuration item from repmgr.conf.sample
Per GitHub #262
2017-01-04 09:35:39 +09:00
Ian Barwick
bc9febdc48 repmgr: fix error message string
Per GitHub #263.
2017-01-04 09:25:50 +09:00
34 changed files with 755 additions and 272 deletions

View File

@@ -2,7 +2,7 @@ License and Contributions
=========================
`repmgr` is licensed under the GPL v3. All of its code and documentation is
Copyright 2010-2016, 2ndQuadrant Limited. See the files COPYRIGHT and LICENSE for
Copyright 2010-2017, 2ndQuadrant Limited. See the files COPYRIGHT and LICENSE for
details.
The development of repmgr has primarily been sponsored by 2ndQuadrant customers.

View File

@@ -1,4 +1,4 @@
Copyright (c) 2010-2016, 2ndQuadrant Limited
Copyright (c) 2010-2017, 2ndQuadrant Limited
All rights reserved.
This program is free software: you can redistribute it and/or modify

24
HISTORY
View File

@@ -1,3 +1,27 @@
3.3.2 2017-06-01
Add support for PostgreSQL 10 (Ian)
repmgr: ensure --replication-user option is honoured when passing database
connection parameters as a conninfo string (Ian)
repmgr: improve detection of pg_rewind on remote server (Ian)
repmgr: add DETAIL log output for additional clarification of error messages (Ian)
repmgr: suppress various spurious error messages in `standby follow` and
`standby switchover` (Ian)
repmgr: add missing `-P` option (Ian)
repmgrd: monitoring statistic reporting fixes (Ian)
3.3.1 2017-03-13
repmgrd: prevent invalid apply lag value being written to the
monitoring table (Ian)
repmgrd: fix error in XLogRecPtr conversion when calculating
monitoring statistics (Ian)
repmgr: if replication slots in use, where possible delete slot on old
upstream node after following new upstream (Ian)
repmgr: improve logging of rsync actions (Ian)
repmgr: improve `standby clone` when synchronous replication in use (Ian)
repmgr: stricter checking of allowed node id values
repmgr: enable `master register --force` when there is a foreign key
dependency from a standby node (Ian)
3.3 2016-12-27
repmgr: always log to STDERR even if log facility defined (Ian)
repmgr: add --log-to-file to log repmgr output to the defined

View File

@@ -1,6 +1,6 @@
#
# Makefile
# Copyright (c) 2ndQuadrant, 2010-2016
# Copyright (c) 2ndQuadrant, 2010-2017
HEADERS = $(wildcard *.h)
@@ -8,8 +8,9 @@ repmgrd_OBJS = dbutils.o config.o repmgrd.o log.o strutil.o
repmgr_OBJS = dbutils.o check_dir.o config.o repmgr.o log.o strutil.o dirmod.o compat.o
DATA = repmgr.sql uninstall_repmgr.sql
REGRESS = repmgr_funcs repmgr_test
PG_CPPFLAGS = -I$(libpq_srcdir)
PG_CPPFLAGS = -I$(includedir_internal) -I$(libpq_srcdir)
PG_LIBS = $(libpq_pgport)
@@ -17,11 +18,11 @@ all: repmgrd repmgr
$(MAKE) -C sql
repmgrd: $(repmgrd_OBJS)
$(CC) -o repmgrd $(CFLAGS) $(repmgrd_OBJS) $(PG_LIBS) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS)
$(CC) -o repmgrd $(CFLAGS) $(repmgrd_OBJS) $(PG_LIBS) $(LDFLAGS) $(LDFLAGS_EX)
$(MAKE) -C sql
repmgr: $(repmgr_OBJS)
$(CC) -o repmgr $(CFLAGS) $(repmgr_OBJS) $(PG_LIBS) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS)
$(CC) -o repmgr $(CFLAGS) $(repmgr_OBJS) $(PG_LIBS) $(LDFLAGS) $(LDFLAGS_EX)
# Make all objects depend on all include files. This is a bit of a
# shotgun approach, but the codebase is small enough that a complete rebuild

View File

@@ -7,7 +7,7 @@ replication capabilities with utilities to set up standby servers, monitor
replication, and perform administrative tasks such as failover or switchover
operations.
The current `repmgr` version, 3.2, supports all PostgreSQL versions from
The current `repmgr` version (3.3) supports all PostgreSQL versions from
9.3 to 9.6.
Overview
@@ -333,10 +333,11 @@ The following replication settings may need to be adjusted:
archive_command = '/bin/true'
# If cloning using rsync, or you have configured `pg_basebackup_options`
# in `repmgr.conf` to include the setting `--xlog-method=fetch`, *and*
# you have not set `restore_command` in `repmgr.conf`to fetch WAL files
# from another source such as Barman, you'll need to set `wal_keep_segments`
# to a high enough value to ensure that all WAL files generated while
# in `repmgr.conf` to include the setting `--xlog-method=fetch` (from
# PostgreSQL 10 `--wal-method=fetch`), *and* you have not set
# `restore_command` in `repmgr.conf`to fetch WAL files from another
# source such as Barman, you'll need to set `wal_keep_segments` to a
# high enough value to ensure that all WAL files generated while
# the standby is being cloned are retained until the standby starts up.
# wal_keep_segments = 5000
@@ -390,7 +391,8 @@ least the following parameters:
- `cluster`: an arbitrary name for the replication cluster; this must be identical
on all nodes
- `node`: a unique integer identifying the node
- `node`: a unique integer identifying the node; note this must be a positive
32 bit signed integer between 1 and 2147483647
- `node_name`: a unique string identifying the node; we recommend a name
specific to the server (e.g. 'server_1'); avoid names indicating the
current replication role like 'master' or 'standby' as the server's
@@ -501,7 +503,8 @@ place. To ensure this happens when using the default `pg_basebackup` method,
`repmgr` will set `pg_basebackup`'s `--xlog-method` parameter to `stream`,
which will ensure all WAL files generated during the cloning process are
streamed in parallel with the main backup. Note that this requires two
replication connections to be available.
replication connections to be available (`repmgr` will verify sufficient
connections are available before attempting to clone).
To override this behaviour, in `repmgr.conf` set `pg_basebackup`'s
`--xlog-method` parameter to `fetch`:
@@ -513,6 +516,9 @@ See the `pg_basebackup` documentation for details:
https://www.postgresql.org/docs/current/static/app-pgbasebackup.html
> *NOTE*: From PostgreSQL 10, `pg_basebackup`'s `--xlog-method` parameter
> has been renamed to `--wal-method`.
Make any adjustments to the standby's PostgreSQL configuration files now,
then start the server.
@@ -1153,11 +1159,10 @@ Additionally the following `repmgrd` options must be set in `repmgr.conf`:
promote_command='repmgr standby promote -f /etc/repmgr.conf --log-to-file'
follow_command='repmgr standby follow -f /etc/repmgr.conf --log-to-file'
Note that the `--log-to-file` option will cause `repmgr` output to be logged to
the destination configured to receive log output `repmgrd`.
Note that the `--log-to-file` option will cause `repmgr`'s output to be logged to
the destination configured to receive log output for `repmgrd`.
See `repmgr.conf.sample` for further `repmgrd`-specific settings
When `failover` is set to `automatic`, upon detecting failure of the current
master, `repmgrd` will execute one of `promote_command` or `follow_command`,
depending on whether the current server is becoming the new master or
@@ -1461,7 +1466,7 @@ In general `repmgr` can be upgraded as-is without any further action required,
however feature releases may require the `repmgr` database to be upgraded.
An SQL script will be provided - please check the release notes for details:
* http://repmgr.org/release-notes-3.2.1.html#UPGRADING
* http://repmgr.org/release-notes-3.3.html#UPGRADING
Distribution-specific configuration

View File

@@ -1,6 +1,6 @@
/*
* check_dir.c - Directories management functions
* Copyright (C) 2ndQuadrant, 2010-2016
* Copyright (c) 2ndQuadrant, 2010-2017
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/*
* check_dir.h
* Copyright (c) 2ndQuadrant, 2010-2016
* Copyright (c) 2ndQuadrant, 2010-2017
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@@ -1,10 +1,12 @@
/*
*
* compat.c
* Provide backports of various functions not publicly
* exposed before PostgreSQL 9.6
* Provides a couple of useful string utility functions adapted
* from the backend code, which are not publicly exposed. They're
* unlikely to change but it would be worth keeping an eye on them
* for any fixes/improvements
*
* Copyright (C) 2ndQuadrant, 2010-2016
* Copyright (c) 2ndQuadrant, 2010-2017
*
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
@@ -24,8 +26,6 @@
*
*/
#if (PG_VERSION_NUM < 90600)
#include "repmgr.h"
#include "compat.h"
@@ -34,8 +34,8 @@
* the string as a value, in a keyword/pair value in a libpq connection
* string
*
* This function is copied from src/bin/pg_dump/dumputils.c
* as it is only publicly exposed from 9.6
* This function is adapted from src/fe_utils/string_utils.c (before 9.6
* located in: src/bin/pg_dump/dumputils.c)
*/
void
appendConnStrVal(PQExpBuffer buf, const char *str)
@@ -79,8 +79,6 @@ appendConnStrVal(PQExpBuffer buf, const char *str)
/*
* Adapted from: src/fe_utils/string_utils.c
*
* Function not publicly available before PostgreSQL 9.6.
*/
void
appendShellString(PQExpBuffer buf, const char *str)
@@ -107,5 +105,3 @@ appendShellString(PQExpBuffer buf, const char *str)
appendPQExpBufferChar(buf, '\'');
}
#endif

View File

@@ -1,6 +1,6 @@
/*
* compat.h
* Copyright (c) 2ndQuadrant, 2010-2016
* Copyright (c) 2ndQuadrant, 2010-2017
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@@ -1,7 +1,7 @@
/*
* config.c - Functions to parse the config file
*
* Copyright (C) 2ndQuadrant, 2010-2016
* Copyright (c) 2ndQuadrant, 2010-2017
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -448,6 +448,10 @@ _parse_config(t_configuration_options *options, ItemList *error_list)
{
item_list_append(error_list, _("\"node\": must be greater than zero"));
}
else if (options->node < 0)
{
item_list_append(error_list, _("\"node\": must be a positive signed 32 bit integer, i.e. 2147483647 or less"));
}
if (strlen(options->conninfo))
{

View File

@@ -1,7 +1,7 @@
/*
* config.h
*
* Copyright (c) 2ndQuadrant, 2010-2016
* Copyright (c) 2ndQuadrant, 2010-2017
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

137
dbutils.c
View File

@@ -1,7 +1,7 @@
/*
* dbutils.c - Database connection/management functions
*
* Copyright (C) 2ndQuadrant, 2010-2016
* Copyright (c) 2ndQuadrant, 2010-2017
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -33,6 +33,15 @@ char repmgr_schema[MAXLEN] = "";
char repmgr_schema_quoted[MAXLEN] = "";
static int _get_node_record(PGconn *conn, char *cluster, char *sqlquery, t_node_info *node_info);
static bool _set_config(PGconn *conn, const char *config_param, const char *sqlquery);
/*
* _establish_db_connection()
*
* Connect to a database using a conninfo string.
*
* NOTE: *do not* use this for replication connections; use establish_db_connection_by_params() instead.
*/
PGconn *
_establish_db_connection(const char *conninfo, const bool exit_on_error, const bool log_notice, const bool verbose_only)
@@ -77,6 +86,19 @@ _establish_db_connection(const char *conninfo, const bool exit_on_error, const b
}
}
/*
* set "synchronous_commit" to "local" in case synchronous replication is in use
*/
else if (set_config(conn, "synchronous_commit", "local") == false)
{
if (exit_on_error)
{
PQfinish(conn);
exit(ERR_DB_CON);
}
}
return conn;
}
@@ -116,8 +138,12 @@ PGconn *
establish_db_connection_by_params(const char *keywords[], const char *values[],
const bool exit_on_error)
{
/* Make a connection to the database */
PGconn *conn = PQconnectdbParams(keywords, values, true);
PGconn *conn;
bool replication_connection = false;
int i;
/* Connect to the database using the provided parameters */
conn = PQconnectdbParams(keywords, values, true);
/* Check to see that the backend connection was successfully made */
if ((PQstatus(conn) != CONNECTION_OK))
@@ -130,6 +156,28 @@ establish_db_connection_by_params(const char *keywords[], const char *values[],
exit(ERR_DB_CON);
}
}
else
{
/*
* set "synchronous_commit" to "local" in case synchronous replication is in
* use (provided this is not a replication connection)
*/
for (i = 0; keywords[i]; i++)
{
if (strcmp(keywords[i], "replication") == 0)
replication_connection = true;
}
if (replication_connection == false && set_config(conn, "synchronous_commit", "local") == false)
{
if (exit_on_error)
{
PQfinish(conn);
exit(ERR_DB_CON);
}
}
}
return conn;
}
@@ -1084,15 +1132,25 @@ drop_replication_slot(PGconn *conn, char *slot_name)
bool
start_backup(PGconn *conn, char *first_wal_segment, bool fast_checkpoint)
start_backup(PGconn *conn, char *first_wal_segment, bool fast_checkpoint, int server_version_num)
{
char sqlquery[QUERY_STR_LEN];
PGresult *res;
sqlquery_snprintf(sqlquery,
"SELECT pg_catalog.pg_xlogfile_name(pg_catalog.pg_start_backup('repmgr_standby_clone_%ld', %s))",
time(NULL),
fast_checkpoint ? "TRUE" : "FALSE");
if (server_version_num >= 100000)
{
sqlquery_snprintf(sqlquery,
"SELECT pg_catalog.pg_walfile_name(pg_catalog.pg_start_backup('repmgr_standby_clone_%ld', %s))",
time(NULL),
fast_checkpoint ? "TRUE" : "FALSE");
}
else
{
sqlquery_snprintf(sqlquery,
"SELECT pg_catalog.pg_xlogfile_name(pg_catalog.pg_start_backup('repmgr_standby_clone_%ld', %s))",
time(NULL),
fast_checkpoint ? "TRUE" : "FALSE");
}
log_verbose(LOG_DEBUG, "start_backup():\n%s\n", sqlquery);
@@ -1120,12 +1178,19 @@ start_backup(PGconn *conn, char *first_wal_segment, bool fast_checkpoint)
bool
stop_backup(PGconn *conn, char *last_wal_segment)
stop_backup(PGconn *conn, char *last_wal_segment, int server_version_num)
{
char sqlquery[QUERY_STR_LEN];
PGresult *res;
sqlquery_snprintf(sqlquery, "SELECT pg_catalog.pg_xlogfile_name(pg_catalog.pg_stop_backup())");
if (server_version_num >= 100000)
{
sqlquery_snprintf(sqlquery, "SELECT pg_catalog.pg_walfile_name(pg_catalog.pg_stop_backup())");
}
else
{
sqlquery_snprintf(sqlquery, "SELECT pg_catalog.pg_xlogfile_name(pg_catalog.pg_stop_backup())");
}
res = PQexec(conn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
@@ -1150,19 +1215,12 @@ stop_backup(PGconn *conn, char *last_wal_segment)
}
bool
set_config_bool(PGconn *conn, const char *config_param, bool state)
_set_config(PGconn *conn, const char *config_param, const char *sqlquery)
{
char sqlquery[QUERY_STR_LEN];
PGresult *res;
sqlquery_snprintf(sqlquery,
"SET %s TO %s",
config_param,
state ? "TRUE" : "FALSE");
log_verbose(LOG_DEBUG, "set_config_bool():\n%s\n", sqlquery);
res = PQexec(conn, sqlquery);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
@@ -1177,6 +1235,36 @@ set_config_bool(PGconn *conn, const char *config_param, bool state)
return true;
}
bool
set_config(PGconn *conn, const char *config_param, const char *config_value)
{
char sqlquery[QUERY_STR_LEN];
sqlquery_snprintf(sqlquery,
"SET %s TO '%s'",
config_param,
config_value);
log_verbose(LOG_DEBUG, "set_config():\n%s\n", sqlquery);
return _set_config(conn, config_param, sqlquery);
}
bool
set_config_bool(PGconn *conn, const char *config_param, bool state)
{
char sqlquery[QUERY_STR_LEN];
sqlquery_snprintf(sqlquery,
"SET %s TO %s",
config_param,
state ? "TRUE" : "FALSE");
log_verbose(LOG_DEBUG, "set_config_bool():\n%s\n", sqlquery);
return _set_config(conn, config_param, sqlquery);
}
/*
* witness_copy_node_records()
@@ -1872,7 +1960,16 @@ _get_node_record(PGconn *conn, char *cluster, char *sqlquery, t_node_info *node_
node_info->node_id = atoi(PQgetvalue(res, 0, 0));
node_info->type = parse_node_type(PQgetvalue(res, 0, 1));
node_info->upstream_node_id = atoi(PQgetvalue(res, 0, 2));
if (PQgetisnull(res, 0, 2))
{
node_info->upstream_node_id = NO_UPSTREAM_NODE;
}
else
{
node_info->upstream_node_id = atoi(PQgetvalue(res, 0, 2));
}
strncpy(node_info->name, PQgetvalue(res, 0, 3), MAXLEN);
strncpy(node_info->conninfo_str, PQgetvalue(res, 0, 4), MAXLEN);
strncpy(node_info->slot_name, PQgetvalue(res, 0, 5), MAXLEN);

View File

@@ -1,7 +1,7 @@
/*
* dbutils.h
*
* Copyright (c) 2ndQuadrant, 2010-2016
* Copyright (c) 2ndQuadrant, 2010-2017
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -122,8 +122,9 @@ char *get_repmgr_schema_quoted(PGconn *conn);
bool create_replication_slot(PGconn *conn, char *slot_name, int server_version_num, PQExpBufferData *error_msg);
int get_slot_record(PGconn *conn, char *slot_name, t_replication_slot *record);
bool drop_replication_slot(PGconn *conn, char *slot_name);
bool start_backup(PGconn *conn, char *first_wal_segment, bool fast_checkpoint);
bool stop_backup(PGconn *conn, char *last_wal_segment);
bool start_backup(PGconn *conn, char *first_wal_segment, bool fast_checkpoint, int server_version_num);
bool stop_backup(PGconn *conn, char *last_wal_segment, int server_version_num);
bool set_config(PGconn *conn, const char *config_param, const char *config_value);
bool set_config_bool(PGconn *conn, const char *config_param, bool state);
bool witness_copy_node_records(PGconn *masterconn, PGconn *witnessconn, char *cluster_name);
bool create_node_record(PGconn *conn, char *action, int node, char *type, int upstream_node, char *cluster_name, char *node_name, char *conninfo, int priority, char *slot_name, bool active);

View File

@@ -3,7 +3,7 @@
* dirmod.c
* directory handling functions
*
* Copyright (C) 2ndQuadrant, 2010-2016
* Copyright (c) 2ndQuadrant, 2010-2017
*
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California

View File

@@ -1,6 +1,6 @@
/*
* dirmod.h
* Copyright (c) 2ndQuadrant, 2010-2016
* Copyright (c) 2ndQuadrant, 2010-2017
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@@ -53,7 +53,7 @@ will be carried out:
e.g. if a replication cluster is spread over multiple data centres, a split-brain
situation does not occur if there is a network failure between datacentres. Note
that if nodes are split evenly between data centres, a witness server can be
used to establish the "majority" daat centre.
used to establish the "majority" data centre.
* `repmgrd` polls all visible servers and waits for each node to return a valid LSN;
it updates the LSN previously stored for this node if it has increased since

View File

@@ -1,6 +1,6 @@
/*
* errcode.h
* Copyright (C) 2ndQuadrant, 2010-2016
* Copyright (c) 2ndQuadrant, 2010-2017
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

18
expected/repmgr_funcs.out Normal file
View File

@@ -0,0 +1,18 @@
/*
* repmgr_function.sql
* Copyright (c) 2ndQuadrant, 2010-2017
*
*/
-- SET SEARCH_PATH TO 'repmgr';
CREATE FUNCTION repmgr_update_standby_location(text) RETURNS boolean
AS '$libdir/repmgr_funcs', 'repmgr_update_standby_location'
LANGUAGE C STRICT;
CREATE FUNCTION repmgr_get_last_standby_location() RETURNS text
AS '$libdir/repmgr_funcs', 'repmgr_get_last_standby_location'
LANGUAGE C STRICT;
CREATE FUNCTION repmgr_update_last_updated() RETURNS TIMESTAMP WITH TIME ZONE
AS '$libdir/repmgr_funcs', 'repmgr_update_last_updated'
LANGUAGE C STRICT;
CREATE FUNCTION repmgr_get_last_updated() RETURNS TIMESTAMP WITH TIME ZONE
AS '$libdir/repmgr_funcs', 'repmgr_get_last_updated'
LANGUAGE C STRICT;

24
expected/repmgr_test.out Normal file
View File

@@ -0,0 +1,24 @@
select * from repmgr_update_standby_location('');
repmgr_update_standby_location
--------------------------------
f
(1 row)
select * from repmgr_get_last_standby_location();
repmgr_get_last_standby_location
----------------------------------
(1 row)
select * from repmgr_update_last_updated();
repmgr_update_last_updated
----------------------------
(1 row)
select * from repmgr_get_last_updated();
repmgr_get_last_updated
-------------------------
(1 row)

18
log.c
View File

@@ -1,6 +1,6 @@
/*
* log.c - Logging methods
* Copyright (C) 2ndQuadrant, 2010-2016
* Copyright (c) 2ndQuadrant, 2010-2017
*
* This module is a set of methods for logging (currently only syslog)
*
@@ -71,7 +71,7 @@ _stderr_log_with_level(const char *level_name, int level, const char *fmt, va_li
/*
* Store the requested level so that if there's a subsequent
* log_hint(), we can suppress that if appropriate.
* log_hint() or log_detail(), we can suppress that if appropriate.
*/
last_log_level = level;
@@ -113,6 +113,20 @@ log_hint(const char *fmt, ...)
}
void
log_detail(const char *fmt, ...)
{
va_list ap;
if (terse_logging == false)
{
va_start(ap, fmt);
_stderr_log_with_level("DETAIL", last_log_level, fmt, ap);
va_end(ap);
}
}
void
log_verbose(int level, const char *fmt, ...)
{

4
log.h
View File

@@ -1,6 +1,6 @@
/*
* log.h
* Copyright (c) 2ndQuadrant, 2010-2016
* Copyright (c) 2ndQuadrant, 2010-2017
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -126,6 +126,8 @@ bool logger_shutdown(void);
void logger_set_verbose(void);
void logger_set_terse(void);
void log_detail(const char *fmt, ...)
__attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 2)));
void log_hint(const char *fmt, ...)
__attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 2)));
void log_verbose(int level, const char *fmt, ...)

498
repmgr.c
View File

@@ -1,7 +1,7 @@
/*
* repmgr.c - Command interpreter for the repmgr package
*
* Copyright (C) 2ndQuadrant, 2010-2016
* Copyright (c) 2ndQuadrant, 2010-2017
*
* This module is a command-line utility to easily setup a cluster of
* hot standby servers for an HA environment
@@ -62,13 +62,7 @@
#include "check_dir.h"
#include "strutil.h"
#include "version.h"
#if (PG_VERSION_NUM < 90600)
#include "compat.h"
#else
#include "fe_utils/string_utils.h"
#include "postgres_fe.h"
#endif
#ifndef RECOVERY_COMMAND_FILE
#define RECOVERY_COMMAND_FILE "recovery.conf"
@@ -102,7 +96,7 @@
static int test_ssh_connection(char *host, char *remote_user);
static int copy_remote_files(char *host, char *remote_user, char *remote_path,
char *local_path, bool is_directory, int server_version_num);
static int run_basebackup(const char *data_dir, int server_version);
static int run_basebackup(const char *data_dir, int server_version_num);
static void check_parameters_for_action(const int action);
static bool create_schema(PGconn *conn);
static bool create_recovery_file(const char *data_dir, t_conninfo_param_list *recovery_conninfo);
@@ -164,7 +158,8 @@ static void param_set(t_conninfo_param_list *param_list, const char *param, cons
static char *param_get(t_conninfo_param_list *param_list, const char *param);
static bool parse_conninfo_string(const char *conninfo_str, t_conninfo_param_list *param_list, char *errmsg, bool ignore_application_name);
static void conn_to_param_list(PGconn *conn, t_conninfo_param_list *param_list);
static void parse_pg_basebackup_options(const char *pg_basebackup_options, t_basebackup_options *backup_options);
static char *param_list_to_string(t_conninfo_param_list *param_list);
static bool parse_pg_basebackup_options(const char *pg_basebackup_options, t_basebackup_options *backup_options, int server_version_num, ItemList *error_list);
static void config_file_list_init(t_configfile_list *list, int max_size);
static void config_file_list_add(t_configfile_list *list, const char *file, const char *filename, bool in_data_dir);
@@ -172,6 +167,8 @@ static void config_file_list_add(t_configfile_list *list, const char *file, cons
static void matrix_set_node_status(t_node_matrix_rec **matrix_rec_list, int n, int node_id, int connection_node_id, int connection_status);
static void cube_set_node_status(t_node_status_cube **cube, int n, int node_id, int matrix_node_id, int connection_node_id, int connection_status);
static void drop_replication_slot_if_exists(PGconn *conn, int node_id, char *slot_name);
/* Global variables */
static PQconninfoOption *opts = NULL;
@@ -224,14 +221,14 @@ main(int argc, char **argv)
{"rsync-only", no_argument, NULL, 'r'},
{"fast-checkpoint", no_argument, NULL, 'c'},
{"log-level", required_argument, NULL, 'L'},
{"terse", required_argument, NULL, 't'},
{"terse", no_argument, NULL, 't'},
{"mode", required_argument, NULL, 'm'},
{"pwprompt", no_argument, NULL, 'P'},
{"remote-config-file", required_argument, NULL, 'C'},
{"help", no_argument, NULL, OPT_HELP},
{"check-upstream-config", no_argument, NULL, OPT_CHECK_UPSTREAM_CONFIG},
{"recovery-min-apply-delay", required_argument, NULL, OPT_RECOVERY_MIN_APPLY_DELAY},
{"pg_rewind", optional_argument, NULL, OPT_PG_REWIND},
{"pwprompt", optional_argument, NULL, OPT_PWPROMPT},
{"csv", no_argument, NULL, OPT_CSV},
{"node", required_argument, NULL, OPT_NODE},
{"without-barman", no_argument, NULL, OPT_WITHOUT_BARMAN},
@@ -342,7 +339,7 @@ main(int argc, char **argv)
strncpy(runtime_options.dbname, runtime_options.username, MAXLEN);
}
while ((c = getopt_long(argc, argv, "?Vd:h:p:U:S:D:f:R:w:k:FWIvb:rcL:tm:C:l:", long_options,
while ((c = getopt_long(argc, argv, "?Vd:h:p:U:S:D:f:R:w:k:FWIvb:rcL:tm:C:l:P", long_options,
&optindex)) != -1)
{
/*
@@ -468,6 +465,9 @@ main(int argc, char **argv)
case 'C':
strncpy(runtime_options.remote_config_file, optarg, MAXLEN);
break;
case 'P':
runtime_options.witness_pwprompt = true;
break;
case OPT_CHECK_UPSTREAM_CONFIG:
check_upstream_config = true;
break;
@@ -518,9 +518,6 @@ main(int argc, char **argv)
}
runtime_options.pg_rewind_supplied = true;
break;
case OPT_PWPROMPT:
runtime_options.witness_pwprompt = true;
break;
case OPT_CSV:
runtime_options.csv_mode = true;
break;
@@ -762,7 +759,7 @@ main(int argc, char **argv)
{
if (optind < argc)
{
if (runtime_options.host[0])
if (runtime_options.host_param_provided == true)
{
PQExpBufferData additional_host_arg;
initPQExpBuffer(&additional_host_arg);
@@ -881,7 +878,6 @@ main(int argc, char **argv)
if (runtime_options.terse)
logger_set_terse();
/*
* Node configuration information is not needed for all actions, with
* STANDBY CLONE being the main exception.
@@ -946,7 +942,7 @@ main(int argc, char **argv)
*/
if (strcmp(repmgr_schema, DEFAULT_REPMGR_SCHEMA_PREFIX) == 0 && !runtime_options.force)
{
log_err(_("unable to determine cluster name - please provide a valid configuration file with -c/--config-file\n"));
log_err(_("unable to determine cluster name - please provide a valid configuration file with -f/--config-file\n"));
log_hint(_("Use -F/--force to continue anyway\n"));
exit(ERR_BAD_CONFIG);
}
@@ -1877,6 +1873,7 @@ do_master_register(void)
int primary_node_id = UNKNOWN_NODE_ID;
bool record_created;
t_node_info node_info = T_NODE_INFO_INITIALIZER;
conn = establish_db_connection(options.conninfo, true);
@@ -1902,7 +1899,7 @@ do_master_register(void)
if (!schema_exists)
{
log_info(_("master register: creating database objects inside the %s schema\n"),
log_info(_("master register: creating database objects inside the '%s' schema\n"),
get_repmgr_schema());
begin_transaction(conn);
@@ -1949,42 +1946,49 @@ do_master_register(void)
exit(ERR_BAD_CONFIG);
}
/* Delete any existing record for this node if --force set */
if (runtime_options.force)
/*
* Check whether there's an existing record for this node, and
* update it if --force set
*/
if (get_node_record(conn, options.cluster_name, options.node, &node_info))
{
bool node_record_deleted;
t_node_info node_info = T_NODE_INFO_INITIALIZER;
begin_transaction(conn);
if (get_node_record(conn, options.cluster_name, options.node, &node_info))
if (!runtime_options.force)
{
log_notice(_("deleting existing master record with id %i\n"), options.node);
node_record_deleted = delete_node_record(conn,
options.node,
"master register");
if (node_record_deleted == false)
{
rollback_transaction(conn);
PQfinish(conn);
exit(ERR_BAD_CONFIG);
}
log_err(_("this node is already registered\n"));
log_hint(_("use -F/--force to overwrite the existing node record\n"));
rollback_transaction(conn);
PQfinish(conn);
exit(ERR_BAD_CONFIG);
}
}
/* Now register the master */
record_created = create_node_record(conn,
"master register",
options.node,
"master",
NO_UPSTREAM_NODE,
options.cluster_name,
options.node_name,
options.conninfo,
options.priority,
repmgr_slot_name_ptr,
true);
record_created = update_node_record(conn,
"master register",
options.node,
"master",
NO_UPSTREAM_NODE,
options.cluster_name,
options.node_name,
options.conninfo,
options.priority,
repmgr_slot_name_ptr,
true);
}
else
{
/* Now register the master */
record_created = create_node_record(conn,
"master register",
options.node,
"master",
NO_UPSTREAM_NODE,
options.cluster_name,
options.node_name,
options.conninfo,
options.priority,
repmgr_slot_name_ptr,
true);
}
if (record_created == false)
{
@@ -2005,7 +2009,7 @@ do_master_register(void)
PQfinish(conn);
log_notice(_("master node correctly registered for cluster %s with id %d (conninfo: %s)\n"),
log_notice(_("master node correctly registered for cluster '%s' with id %d (conninfo: %s)\n"),
options.cluster_name, options.node, options.conninfo);
return;
}
@@ -2146,7 +2150,7 @@ do_standby_register(void)
log_err(_("no record found for upstream node %i\n"),
options.upstream_node);
/* footgun alert - only do this if you know what you're doing */
log_hint(_("use option -F/--force to create a dummy upstream record"));
log_hint(_("use option -F/--force to create a dummy upstream record\n"));
PQfinish(master_conn);
if (PQstatus(conn) == CONNECTION_OK)
PQfinish(conn);
@@ -2846,11 +2850,18 @@ do_standby_clone(void)
maxlen_snprintf(local_repmgr_directory, "%s/repmgr", local_data_directory );
maxlen_snprintf(datadir_list_filename, "%s/data.txt", local_repmgr_directory);
if (!create_pg_dir(local_data_directory, runtime_options.force))
{
log_err(_("unable to use directory %s ...\n"),
local_data_directory);
log_hint(_("use -F/--force option to force this directory to be overwritten\n"));
exit(ERR_BAD_CONFIG);
}
if (!create_pg_dir(local_repmgr_directory, runtime_options.force))
{
log_err(_("unable to use directory \"%s\" ...\n"),
log_err(_("unable to create directory \"%s\" ...\n"),
local_repmgr_directory);
log_hint(_("use -F/--force option to force this directory to be overwritten\n"));
exit(ERR_BAD_CONFIG);
}
@@ -2958,8 +2969,6 @@ do_standby_clone(void)
primary_conn = source_conn;
}
/*
* Sanity-check that the master node has a repmgr schema - if not
* present, fail with an error (unless -F/--force is used)
@@ -2979,7 +2988,6 @@ do_standby_clone(void)
log_warning(_("expected repmgr schema '%s' not found on master server\n"), get_repmgr_schema());
}
/* Fetch the source's data directory */
if (get_pg_setting(source_conn, "data_directory", master_data_directory) == false)
{
@@ -3001,8 +3009,6 @@ do_standby_clone(void)
log_hint(_("use -D/--data-dir to explicitly specify a data directory\n"));
}
/*
* Copy the source connection so that we have some default values,
* particularly stuff like passwords extracted from PGPASSFILE;
@@ -3587,6 +3593,9 @@ do_standby_clone(void)
/*
* We must create some PGDATA subdirectories because they are
* not included in the Barman backup.
*
* See class RsyncBackupExecutor in the Barman source (barman/backup_executor.py)
* for a definitive list of excluded directories.
*/
{
const char* const dirs[] = {
@@ -3597,14 +3606,14 @@ do_standby_clone(void)
/* Only from 9.4 */
"pg_dynshmem", "pg_logical", "pg_logical/snapshots", "pg_logical/mappings", "pg_replslot",
/* Already in 9.3 */
"pg_serial", "pg_snapshots", "pg_stat", "pg_stat_tmp", "pg_tblspc",
"pg_notify", "pg_serial", "pg_snapshots", "pg_stat", "pg_stat_tmp", "pg_tblspc",
"pg_twophase", "pg_xlog", 0
};
const int vers[] = {
100000,
90500,
90400, 90400, 90400, 90400, 90400,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, -100000, 0
};
for (i = 0; dirs[i]; i++)
@@ -3614,7 +3623,7 @@ do_standby_clone(void)
continue;
/* directory existed in earlier versions than this server but has been removed/renamed - skip */
if (vers[1] < 0 && server_version_num >= abs(vers[i]))
if (vers[i] < 0 && server_version_num >= abs(vers[i]))
continue;
maxlen_snprintf(filename, "%s/%s", local_data_directory, dirs[i]);
@@ -3645,7 +3654,7 @@ do_standby_clone(void)
goto stop_backup;
}
if (start_backup(source_conn, first_wal_segment, runtime_options.fast_checkpoint) == false)
if (start_backup(source_conn, first_wal_segment, runtime_options.fast_checkpoint, server_version_num) == false)
{
r = ERR_BAD_BASEBACKUP;
retval = ERR_BAD_BASEBACKUP;
@@ -3674,15 +3683,16 @@ do_standby_clone(void)
master_data_directory, local_data_directory,
true, server_version_num);
/*
Exit code 0 means no error, but we want to ignore exit code 24 as well
as rsync returns that code on "Partial transfer due to vanished source files".
It's quite common for this to happen on the data directory, particularly
with long running rsync on a busy server.
*/
if (!WIFEXITED(r) && WEXITSTATUS(r) != 24)
* Exit code 0 means no error, but we want to ignore exit code 24 as well
* as rsync returns that code on "Partial transfer due to vanished source files".
* It's quite common for this to happen on the data directory, particularly
* with long running rsync on a busy server.
*/
if (WIFEXITED(r) && WEXITSTATUS(r) && WEXITSTATUS(r) != 24)
{
log_warning(_("standby clone: failed copying master data directory '%s'\n"),
log_err(_("standby clone: failed copying master data directory '%s'\n"),
master_data_directory);
r = retval = ERR_BAD_RSYNC;
goto stop_backup;
}
@@ -3766,15 +3776,16 @@ do_standby_clone(void)
true, server_version_num);
/*
Exit code 0 means no error, but we want to ignore exit code 24 as well
as rsync returns that code on "Partial transfer due to vanished source files".
It's quite common for this to happen on the data directory, particularly
with long running rsync on a busy server.
*/
if (!WIFEXITED(r) && WEXITSTATUS(r) != 24)
* Exit code 0 means no error, but we want to ignore exit code 24 as well
* as rsync returns that code on "Partial transfer due to vanished source files".
* It's quite common for this to happen on the data directory, particularly
* with long running rsync on a busy server.
*/
if (WIFEXITED(r) && WEXITSTATUS(r) && WEXITSTATUS(r) != 24)
{
log_warning(_("standby clone: failed copying tablespace directory '%s'\n"),
cell_t->location);
log_err(_("standby clone: failed copying tablespace directory '%s'\n"),
cell_t->location);
r = retval = ERR_BAD_RSYNC;
goto stop_backup;
}
}
@@ -3782,7 +3793,7 @@ do_standby_clone(void)
/*
* If a valid mapping was provide for this tablespace, arrange for it to
* be remapped
* (if no tablespace mappings was provided, the link will be copied as-is
* (if no tablespace mapping was provided, the link will be copied as-is
* by pg_basebackup or rsync and no action is required)
*/
if (mapping_found == true || mode == barman)
@@ -3941,7 +3952,7 @@ do_standby_clone(void)
r = copy_remote_files(runtime_options.host, runtime_options.remote_user,
file->filepath, dest_path, false, server_version_num);
if (r != 0)
if (WEXITSTATUS(r))
{
log_err(_("standby clone: unable to copy config file '%s'\n"),
file->filename);
@@ -3975,7 +3986,7 @@ do_standby_clone(void)
r = copy_remote_files(runtime_options.host, runtime_options.remote_user,
master_control_file, local_control_file,
false, server_version_num);
if (r != 0)
if (WEXITSTATUS(r))
{
log_warning(_("standby clone: failed copying master control file '%s'\n"),
master_control_file);
@@ -3989,7 +4000,7 @@ stop_backup:
if (mode == rsync && pg_start_backup_executed)
{
log_notice(_("notifying master about backup completion...\n"));
if (stop_backup(source_conn, last_wal_segment) == false)
if (stop_backup(source_conn, last_wal_segment, server_version_num) == false)
{
r = ERR_BAD_BASEBACKUP;
retval = ERR_BAD_BASEBACKUP;
@@ -4533,7 +4544,8 @@ do_standby_follow(void)
char script[MAXLEN];
char master_conninfo[MAXLEN];
PGconn *master_conn;
int master_id = 0;
int master_id = UNKNOWN_NODE_ID,
original_upstream_node_id = UNKNOWN_NODE_ID;
int r,
retval;
@@ -4680,7 +4692,11 @@ do_standby_follow(void)
}
/* Fetch our node record so we can write application_name, if set */
/*
* Fetch our node record so we can write application_name, if set,
* and to get the upstream node ID, which we'll need to know if
* replication slots are in use and we want to delete the old slot.
*/
query_result = get_node_record(master_conn,
options.cluster_name,
options.node,
@@ -4713,6 +4729,15 @@ do_standby_follow(void)
if (application_name != NULL && strlen(application_name))
param_set(&recovery_conninfo, "application_name", application_name);
}
if (local_node_record.upstream_node_id != UNKNOWN_NODE_ID)
{
original_upstream_node_id = local_node_record.upstream_node_id;
}
else
{
original_upstream_node_id = master_id;
}
}
log_info(_("changing standby's master to node %i\n"), master_id);
@@ -4742,6 +4767,54 @@ do_standby_follow(void)
exit(ERR_NO_RESTART);
}
/*
* If replication slots are in use, and an inactive one for this node
* (a former standby) exists on the former upstream, drop it.
*/
if (options.use_replication_slots && runtime_options.host_param_provided == false && original_upstream_node_id != UNKNOWN_NODE_ID)
{
t_node_info upstream_node_record = T_NODE_INFO_INITIALIZER;
int upstream_query_result;
log_verbose(LOG_INFO, "attempting to remove replication slot from old upstream node %i\n",
original_upstream_node_id);
/* XXX should we poll for server restart? */
conn = establish_db_connection(options.conninfo, true);
upstream_query_result = get_node_record(conn,
options.cluster_name,
original_upstream_node_id,
&upstream_node_record);
PQfinish(conn);
if (upstream_query_result != 1)
{
log_warning(_("unable to retrieve node record for old upstream node %i"),
original_upstream_node_id);
}
else
{
conn = establish_db_connection_quiet(upstream_node_record.conninfo_str);
if (PQstatus(conn) != CONNECTION_OK)
{
log_info(_("unable to connect to old upstream node %i to remove replication slot\n"),
original_upstream_node_id);
log_hint(_("if reusing this node, you should manually remove any inactive replication slots\n"));
}
else
{
drop_replication_slot_if_exists(conn,
original_upstream_node_id,
local_node_record.slot_name);
}
}
}
/*
* It's possible this node was an inactive primary - update the
* relevant fields to ensure it's marked as an active standby
@@ -4759,6 +4832,8 @@ do_standby_follow(void)
exit(ERR_BAD_CONFIG);
}
log_notice(_("STANDBY FOLLOW successful\n"));
create_event_record(master_conn,
&options,
options.node,
@@ -4794,8 +4869,6 @@ do_standby_follow(void)
*
* TODO:
* - make connection test timeouts/intervals configurable (see below)
* - add command line option --remote_pg_bindir or similar to
* optionally handle cases where the remote pg_bindir is different
*/
static void
@@ -5004,7 +5077,11 @@ do_standby_switchover(void)
initPQExpBuffer(&remote_command_str);
appendPQExpBuffer(&remote_command_str, "ls ");
if (strcmp(remote_pg_rewind, "pg_rewind") == 0)
appendPQExpBuffer(&remote_command_str, "which ");
else
appendPQExpBuffer(&remote_command_str, "ls ");
appendShellString(&remote_command_str, remote_pg_rewind);
appendPQExpBuffer(&remote_command_str, " >/dev/null 2>&1 && echo 1 || echo 0");
@@ -5021,7 +5098,11 @@ do_standby_switchover(void)
if (*command_output.data == '0')
{
log_err(_("unable to find pg_rewind on the remote server\n"));
log_err(_("expected location is: %s\n"), remote_pg_rewind);
if (strcmp(remote_pg_rewind, "pg_rewind") == 0)
log_hint(_("set pg_bindir in repmgr.conf or provide with -b/--pg_bindir\n"));
else
log_detail(_("expected location is: %s\n"), remote_pg_rewind);
exit(ERR_BAD_CONFIG);
}
@@ -5201,7 +5282,7 @@ do_standby_switchover(void)
appendPQExpBuffer(&remote_command_str,
"%s standby archive-config -f ",
make_pg_path("repmgr"));
appendShellString(&remote_command_str, runtime_options.remote_config_file);
appendShellString(&remote_command_str, runtime_options.remote_config_file);
appendPQExpBuffer(&remote_command_str,
" --config-archive-dir=");
appendShellString(&remote_command_str, remote_archive_config_dir);
@@ -5375,12 +5456,22 @@ do_standby_switchover(void)
/* Restore any previously archived config files */
initPQExpBuffer(&remote_command_str);
/* --force */
appendPQExpBuffer(&remote_command_str,
"%s standby restore-config -D ",
make_pg_path("repmgr"));
appendShellString(&remote_command_str, remote_data_directory);
/*
* append pass the configuration file to prevent spurious errors
* about missing cluster_name
*/
appendPQExpBuffer(&remote_command_str,
" --config-archive-dir=");
" -f ");
appendShellString(&remote_command_str, runtime_options.remote_config_file);
appendPQExpBuffer(&remote_command_str,
" --config-archive-dir=");
appendShellString(&remote_command_str, remote_archive_config_dir);
initPQExpBuffer(&command_output);
@@ -5529,6 +5620,8 @@ do_standby_switchover(void)
/* Check for entry in the new master's pg_stat_replication */
local_conn = establish_db_connection(options.conninfo, true);
{
int i,
replication_check_timeout = 60,
@@ -5538,8 +5631,6 @@ do_standby_switchover(void)
initPQExpBuffer(&event_details);
local_conn = establish_db_connection(options.conninfo, true);
i = 0;
for (;;)
{
@@ -5635,30 +5726,9 @@ do_standby_switchover(void)
}
else
{
t_replication_slot slot_info;
int query_res;
query_res = get_slot_record(remote_conn, local_node_record.slot_name, &slot_info);
if (query_res)
{
if (slot_info.active == false)
{
if (drop_replication_slot(remote_conn, local_node_record.slot_name) == true)
{
log_notice(_("replication slot \"%s\" deleted on former master\n"), local_node_record.slot_name);
}
else
{
log_err(_("unable to delete replication slot \"%s\" on former master\n"), local_node_record.slot_name);
}
}
/* if active replication slot exists, call Houston as we have a problem */
else
{
log_err(_("replication slot \"%s\" is still active on former master\n"), local_node_record.slot_name);
}
}
drop_replication_slot_if_exists(remote_conn,
remote_node_id,
local_node_record.slot_name);
}
PQfinish(remote_conn);
@@ -5855,11 +5925,13 @@ do_standby_restore_config(void)
if (rmdir(runtime_options.config_archive_dir) != 0 && errno != EEXIST)
{
log_err(_("Unable to delete %s\n"), runtime_options.config_archive_dir);
exit(ERR_BAD_CONFIG);
log_warning(_("unable to delete %s\n"), runtime_options.config_archive_dir);
log_detail(_("directory may need to be manually removed\n"));
}
else
{
log_verbose(LOG_NOTICE, "directory %s deleted\n", runtime_options.config_archive_dir);
}
log_verbose(LOG_NOTICE, "Directory %s deleted\n", runtime_options.config_archive_dir);
return;
}
@@ -6168,7 +6240,7 @@ do_witness_create(void)
r = copy_remote_files(runtime_options.host, runtime_options.remote_user,
master_hba_file, runtime_options.dest_dir, false, -1);
if (r != 0)
if (WEXITSTATUS(r))
{
char *errmsg = _("Unable to copy pg_hba.conf from master");
log_err("%s\n", errmsg);
@@ -6934,16 +7006,18 @@ copy_remote_files(char *host, char *remote_user, char *remote_path,
r = system(script);
if (r != 0)
log_err(_("unable to rsync from remote host (%s:%s)\n"),
host_string, remote_path);
log_debug("copy_remote_files(): r = %i; WIFEXITED: %i; WEXITSTATUS: %i\n", r, WIFEXITED(r), WEXITSTATUS(r));
/* exit code 24 indicates vanished files, which isn't a problem for us */
if (WEXITSTATUS(r) && WEXITSTATUS(r) != 24)
log_verbose(LOG_WARNING, "copy_remote_files(): rsync returned unexpected exit status %i \n", WEXITSTATUS(r));
return r;
}
static int
run_basebackup(const char *data_dir, int server_version)
run_basebackup(const char *data_dir, int server_version_num)
{
char script[MAXLEN];
int r = 0;
@@ -6955,7 +7029,7 @@ run_basebackup(const char *data_dir, int server_version)
* Parse the pg_basebackup_options provided in repmgr.conf - we'll want
* to check later whether certain options were set by the user
*/
parse_pg_basebackup_options(options.pg_basebackup_options, &backup_options);
parse_pg_basebackup_options(options.pg_basebackup_options, &backup_options, server_version_num, NULL);
/* Create pg_basebackup command line options */
@@ -6971,7 +7045,22 @@ run_basebackup(const char *data_dir, int server_version)
*/
if (runtime_options.conninfo_provided == true)
{
appendPQExpBuffer(&params, " -d '%s'", runtime_options.dbname);
t_conninfo_param_list conninfo;
char *conninfo_str;
initialize_conninfo_params(&conninfo, false);
/* string will already have been parsed */
(void) parse_conninfo_string(runtime_options.dbname, &conninfo, NULL, false);
if (*runtime_options.replication_user)
param_set(&conninfo, "user", runtime_options.replication_user);
conninfo_str = param_list_to_string(&conninfo);
appendPQExpBuffer(&params, " -d '%s'", conninfo_str);
pfree(conninfo_str);
}
/*
@@ -7019,6 +7108,7 @@ run_basebackup(const char *data_dir, int server_version)
* From 9.6, if replication slots are in use, we'll have previously
* created a slot with reserved LSN, and will stream from that slot to avoid
* WAL buildup on the master using the -S/--slot, which requires -X/--xlog-method=stream
* (from 10, -X/--wal-method=stream)
*/
if (!strlen(backup_options.xlog_method))
{
@@ -7036,17 +7126,17 @@ run_basebackup(const char *data_dir, int server_version)
*
* NOTE:
* It's possible to set 'pg_basebackup_options' with an invalid combination
* of values for --xlog-method and --slot - we're not checking that, just that
* of values for --wal-method (--xlog-method) and --slot - we're not checking that, just that
* we're not overriding any user-supplied values
*/
if (server_version >= 90600 && options.use_replication_slots)
if (server_version_num >= 90600 && options.use_replication_slots)
{
bool slot_add = true;
/*
* Check whether 'pg_basebackup_options' in repmgr.conf has the --slot option set,
* or if --xlog-method is set to a value other than "stream" (in which case we can't
* use --slot).
* or if --wal-method (--xlog-method) is set to a value other than "stream"
* (in which case we can't use --slot).
*/
if (strlen(backup_options.slot) || (strlen(backup_options.xlog_method) && strcmp(backup_options.xlog_method, "stream") != 0)) {
slot_add = false;
@@ -7674,7 +7764,7 @@ create_schema(PGconn *conn)
* to perform additional cleanup
*
* char *server_version_string
* passed to get_server_version(), which will place the human-readble
* passed to get_server_version(), which will place the human-readable
* server version string there (e.g. "9.4.0")
*/
static int
@@ -7762,6 +7852,8 @@ check_upstream_config(PGconn *conn, int server_version_num, bool exit_on_error)
bool config_ok = true;
char *wal_error_message = NULL;
t_basebackup_options backup_options = T_BASEBACKUP_OPTIONS_INITIALIZER;
bool backup_options_ok = true;
ItemList backup_option_errors = { NULL, NULL };
bool xlog_stream = true;
enum {
@@ -7787,7 +7879,24 @@ check_upstream_config(PGconn *conn, int server_version_num, bool exit_on_error)
* this will influence some checks
*/
parse_pg_basebackup_options(options.pg_basebackup_options, &backup_options);
backup_options_ok = parse_pg_basebackup_options(
options.pg_basebackup_options,
&backup_options, server_version_num,
&backup_option_errors);
if (backup_options_ok == false)
{
if (exit_on_error == true)
{
log_err(_("error(s) encountered parsing 'pg_basebackup_options'\n"));
print_error_list(&backup_option_errors, LOG_ERR);
log_hint(_("'pg_basebackup_options' is: '%s'\n"), options.pg_basebackup_options);
exit(ERR_BAD_CONFIG);
}
config_ok = false;
}
if (strlen(backup_options.xlog_method) && strcmp(backup_options.xlog_method, "stream") != 0)
xlog_stream = false;
@@ -8637,8 +8746,43 @@ conn_to_param_list(PGconn *conn, t_conninfo_param_list *param_list)
}
static void
parse_pg_basebackup_options(const char *pg_basebackup_options, t_basebackup_options *backup_options)
static char *
param_list_to_string(t_conninfo_param_list *param_list)
{
int c;
PQExpBufferData conninfo_buf;
char *conninfo_str;
int len;
initPQExpBuffer(&conninfo_buf);
for (c = 0; c < param_list->size && param_list->keywords[c] != NULL; c++)
{
if (param_list->values[c] != NULL && param_list->values[c][0] != '\0')
{
if (c > 0)
appendPQExpBufferChar(&conninfo_buf, ' ');
appendPQExpBuffer(&conninfo_buf,
"%s=%s",
param_list->keywords[c],
param_list->values[c]);
}
}
len = strlen(conninfo_buf.data) + 1;
conninfo_str = pg_malloc0(len);
strncpy(conninfo_str, conninfo_buf.data, len);
termPQExpBuffer(&conninfo_buf);
return conninfo_str;
}
static bool
parse_pg_basebackup_options(const char *pg_basebackup_options, t_basebackup_options *backup_options, int server_version_num, ItemList *error_list)
{
int options_len = strlen(pg_basebackup_options) + 1;
char *options_string = pg_malloc(options_len);
@@ -8658,17 +8802,38 @@ parse_pg_basebackup_options(const char *pg_basebackup_options, t_basebackup_opti
int optindex = 0;
struct option *long_options;
bool backup_options_ok = true;
/* We're only interested in these options */
static struct option long_options[] =
static struct option long_options_9[] =
{
{"slot", required_argument, NULL, 'S'},
{"xlog-method", required_argument, NULL, 'X'},
{NULL, 0, NULL, 0}
};
/*
* From PostgreSQL 10, --xlog-method is renamed --wal-method
* and there's also --no-slot, which we'll want to consider.
*/
static struct option long_options_10[] =
{
{"slot", required_argument, NULL, 'S'},
{"wal-method", required_argument, NULL, 'X'},
{"no-slot", no_argument, NULL, 1},
{NULL, 0, NULL, 0}
};
/* Don't attempt to tokenise an empty string */
if (!strlen(pg_basebackup_options))
return;
return backup_options_ok;
if (server_version_num >= 100000)
long_options = long_options_10;
else
long_options = long_options_9;
/* Copy the string before operating on it with strtok() */
strncpy(options_string, pg_basebackup_options, options_len);
@@ -8715,6 +8880,9 @@ parse_pg_basebackup_options(const char *pg_basebackup_options, t_basebackup_opti
/* Reset getopt's optind variable */
optind = 0;
/* Prevent getopt from emitting errors */
opterr = 0;
while ((c = getopt_long(argc_item, argv_array, "S:X:", long_options,
&optindex)) != -1)
{
@@ -8726,10 +8894,32 @@ parse_pg_basebackup_options(const char *pg_basebackup_options, t_basebackup_opti
case 'X':
strncpy(backup_options->xlog_method, optarg, MAXLEN);
break;
case 1:
backup_options->no_slot = true;
break;
case '?':
if (server_version_num >= 100000 && optopt == 1)
{
if (error_list != NULL)
{
item_list_append(error_list, "invalid use of --no-slot");
}
backup_options_ok = false;
}
break;
}
}
return;
if (backup_options->no_slot == true && backup_options->slot[0] != '\0')
{
if (error_list != NULL)
{
item_list_append(error_list, "--no-slot cannot be used with -S/--slot");
}
backup_options_ok = false;
}
return backup_options_ok;
}
static void
@@ -8760,3 +8950,33 @@ config_file_list_add(t_configfile_list *list, const char *file, const char *file
list->entries ++;
}
static void
drop_replication_slot_if_exists(PGconn *conn, int node_id, char *slot_name)
{
t_replication_slot slot_info;
int query_res;
query_res = get_slot_record(conn,slot_name, &slot_info);
if (query_res)
{
if (slot_info.active == false)
{
if (drop_replication_slot(conn, slot_name) == true)
{
log_notice(_("replication slot \"%s\" deleted on node %i\n"), slot_name, node_id);
}
else
{
log_err(_("unable to delete replication slot \"%s\" on node %i\n"), slot_name, node_id);
}
}
/* if active replication slot exists, call Houston as we have a problem */
else
{
log_err(_("replication slot \"%s\" is still active on node %i\n"), slot_name, node_id);
}
}
}

View File

@@ -80,12 +80,6 @@
#
#logfile='/var/log/repmgr/repmgr.log'
# By default only repmgrd log output will be written to a file,
# if defined in "logfile"
# enable this to restore old behaviour where output from the repmgr
# client will be written to the logfile too
#log_repmgr_to_file = 0
# event notifications can be passed to an arbitrary external program
# together with the following parameters:
#
@@ -149,8 +143,15 @@
# external command arguments. Values shown are examples.
#pg_ctl_options='-s'
#pg_basebackup_options='--xlog-method=s'
#pg_basebackup_options='--label=repmgr_backup'
# This is the host name of the barman server, which is used for connecting over
# to the barman server (passwordless ssh keys should be in place)
#barman_server='backup_server'
# If you are placing the barman.conf file in a non-standard path, or using
# a name other than barman.conf, use this parameter to specify the path and
# name of the barman configuration file.
#barman_config='/path/to/barman.conf'
# Standby clone settings
# ----------------------

View File

@@ -1,6 +1,6 @@
/*
* repmgr.h
* Copyright (c) 2ndQuadrant, 2010-2016
* Copyright (c) 2ndQuadrant, 2010-2017
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -55,7 +55,6 @@
#define OPT_COPY_EXTERNAL_CONFIG_FILES 4
#define OPT_CONFIG_ARCHIVE_DIR 5
#define OPT_PG_REWIND 6
#define OPT_PWPROMPT 7
#define OPT_CSV 8
#define OPT_NODE 9
#define OPT_WITHOUT_BARMAN 10
@@ -118,7 +117,7 @@ typedef struct
char recovery_min_apply_delay[MAXLEN];
/* standby register paarameters */
/* standby register parameters */
bool wait_register_sync;
int wait_register_sync_seconds;
@@ -193,9 +192,10 @@ typedef struct
{
char slot[MAXLEN];
char xlog_method[MAXLEN];
bool no_slot; /* from PostgreSQL 10 */
} t_basebackup_options;
#define T_BASEBACKUP_OPTIONS_INITIALIZER { "", "" }
#define T_BASEBACKUP_OPTIONS_INITIALIZER { "", "", false }
typedef struct
{

View File

@@ -1,7 +1,7 @@
/*
* repmgr.sql
*
* Copyright (C) 2ndQuadrant, 2010-2016
* Copyright (c) 2ndQuadrant, 2010-2017
*
*/

174
repmgrd.c
View File

@@ -1,7 +1,7 @@
/*
* repmgrd.c - Replication manager daemon
*
* Copyright (C) 2ndQuadrant, 2010-2016
* Copyright (c) 2ndQuadrant, 2010-2017
*
* This module connects to the nodes of a replication cluster and monitors
* how far are they from master
@@ -30,18 +30,10 @@
#include <stdlib.h>
#include <unistd.h>
#include "repmgr.h"
#include "config.h"
#include "log.h"
#include "strutil.h"
#include "version.h"
/* Required PostgreSQL headers */
#include "access/xlogdefs.h"
#include "pqexpbuffer.h"
/* Message strings passed in repmgrSharedState->location */
#define PASSIVE_NODE "PASSIVE_NODE"
@@ -71,6 +63,7 @@ bool failover_done = false;
bool manual_mode_upstream_disconnected = false;
char *pid_file = NULL;
int server_version_num = 0;
static void help(void);
static void usage(void);
@@ -145,8 +138,6 @@ main(int argc, char **argv)
FILE *fd;
int server_version_num = 0;
set_progname(argv[0]);
/* Disallow running as root to prevent directory ownership problems */
@@ -718,26 +709,46 @@ witness_monitor(void)
return;
}
strcpy(monitor_witness_timestamp, PQgetvalue(res, 0, 0));
strncpy(monitor_witness_timestamp, PQgetvalue(res, 0, 0), MAXLEN);
PQclear(res);
/*
* Build the SQL to execute on master
*/
sqlquery_snprintf(sqlquery,
"INSERT INTO %s.repl_monitor "
" (primary_node, standby_node, "
" last_monitor_time, last_apply_time, "
" last_wal_primary_location, last_wal_standby_location, "
" replication_lag, apply_lag )"
" VALUES(%d, %d, "
" '%s'::TIMESTAMP WITH TIME ZONE, NULL, "
" pg_catalog.pg_current_xlog_location(), NULL, "
" 0, 0) ",
get_repmgr_schema_quoted(my_local_conn),
master_options.node,
local_options.node,
monitor_witness_timestamp);
if (server_version_num >= 100000)
{
sqlquery_snprintf(sqlquery,
"INSERT INTO %s.repl_monitor "
" (primary_node, standby_node, "
" last_monitor_time, last_apply_time, "
" last_wal_primary_location, last_wal_standby_location, "
" replication_lag, apply_lag )"
" VALUES(%d, %d, "
" '%s'::TIMESTAMP WITH TIME ZONE, NULL, "
" pg_catalog.pg_current_wal_lsn(), NULL, "
" 0, 0) ",
get_repmgr_schema_quoted(my_local_conn),
master_options.node,
local_options.node,
monitor_witness_timestamp);
}
else
{
sqlquery_snprintf(sqlquery,
"INSERT INTO %s.repl_monitor "
" (primary_node, standby_node, "
" last_monitor_time, last_apply_time, "
" last_wal_primary_location, last_wal_standby_location, "
" replication_lag, apply_lag )"
" VALUES(%d, %d, "
" '%s'::TIMESTAMP WITH TIME ZONE, NULL, "
" pg_catalog.pg_current_xlog_location(), NULL, "
" 0, 0) ",
get_repmgr_schema_quoted(my_local_conn),
master_options.node,
local_options.node,
monitor_witness_timestamp);
}
/*
* Execute the query asynchronously, but don't check for a result. We will
@@ -1125,21 +1136,42 @@ standby_monitor(void)
* If receive_location is less than replay location, we were streaming WAL but are
* somehow disconnected and evidently in archive recovery
*/
sqlquery_snprintf(sqlquery,
" SELECT ts, "
" CASE WHEN (receive_location IS NULL OR receive_location < replay_location) "
" THEN replay_location "
" ELSE receive_location"
" END AS receive_location,"
" replay_location, "
" replay_timestamp, "
" COALESCE(receive_location, '0/0') >= replay_location AS receiving_streamed_wal "
" FROM (SELECT CURRENT_TIMESTAMP AS ts, "
" pg_catalog.pg_last_xlog_receive_location() AS receive_location, "
" pg_catalog.pg_last_xlog_replay_location() AS replay_location, "
" pg_catalog.pg_last_xact_replay_timestamp() AS replay_timestamp "
" ) q ");
if (server_version_num >= 100000)
{
sqlquery_snprintf(sqlquery,
" SELECT ts, "
" CASE WHEN (receive_location IS NULL OR receive_location < replay_location) "
" THEN replay_location "
" ELSE receive_location"
" END AS receive_location,"
" replay_location, "
" replay_timestamp, "
" COALESCE(receive_location, '0/0') >= replay_location AS receiving_streamed_wal "
" FROM (SELECT CURRENT_TIMESTAMP AS ts, "
" pg_catalog.pg_last_wal_receive_lsn() AS receive_location, "
" pg_catalog.pg_last_wal_replay_lsn() AS replay_location, "
" pg_catalog.pg_last_xact_replay_timestamp() AS replay_timestamp "
" ) q ");
}
else
{
sqlquery_snprintf(sqlquery,
" SELECT ts, "
" CASE WHEN (receive_location IS NULL OR receive_location < replay_location) "
" THEN replay_location "
" ELSE receive_location"
" END AS receive_location,"
" replay_location, "
" replay_timestamp, "
" COALESCE(receive_location, '0/0') >= replay_location AS receiving_streamed_wal "
" FROM (SELECT CURRENT_TIMESTAMP AS ts, "
" pg_catalog.pg_last_xlog_receive_location() AS receive_location, "
" pg_catalog.pg_last_xlog_replay_location() AS replay_location, "
" pg_catalog.pg_last_xact_replay_timestamp() AS replay_timestamp "
" ) q ");
}
res = PQexec(my_local_conn, sqlquery);
@@ -1151,9 +1183,9 @@ standby_monitor(void)
return;
}
strncpy(monitor_standby_timestamp, PQgetvalue(res, 0, 0), MAXLEN);
strncpy(monitor_standby_timestamp, PQgetvalue(res, 0, 0), MAXLEN);
strncpy(last_xlog_receive_location, PQgetvalue(res, 0, 1), MAXLEN);
strncpy(last_xlog_replay_location, PQgetvalue(res, 0, 2), MAXLEN);
strncpy(last_xlog_replay_location, PQgetvalue(res, 0, 2), MAXLEN);
strncpy(last_xact_replay_timestamp, PQgetvalue(res, 0, 3), MAXLEN);
receiving_streamed_wal = (strcmp(PQgetvalue(res, 0, 4), "t") == 0)
@@ -1173,7 +1205,11 @@ standby_monitor(void)
* TODO: investigate whether pg_current_xlog_insert_location() would be a better
* choice; see: https://github.com/2ndQuadrant/repmgr/issues/189
*/
sqlquery_snprintf(sqlquery, "SELECT pg_catalog.pg_current_xlog_location()");
if (server_version_num >= 100000)
sqlquery_snprintf(sqlquery, "SELECT pg_catalog.pg_current_wal_lsn()");
else
sqlquery_snprintf(sqlquery, "SELECT pg_catalog.pg_current_xlog_location()");
res = PQexec(master_conn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
@@ -1187,10 +1223,22 @@ standby_monitor(void)
PQclear(res);
lsn_master_current_xlog_location = lsn_to_xlogrecptr(last_wal_primary_location, NULL);
lsn_last_xlog_replay_location = lsn_to_xlogrecptr(last_xlog_replay_location, NULL);
lsn_last_xlog_receive_location = lsn_to_xlogrecptr(last_xlog_receive_location, NULL);
lsn_last_xlog_replay_location = lsn_to_xlogrecptr(last_xlog_replay_location, NULL);
if (lsn_last_xlog_receive_location >= lsn_last_xlog_replay_location)
{
apply_lag = (long long unsigned int)lsn_last_xlog_receive_location - lsn_last_xlog_replay_location;
}
else
{
/* This should never happen, but in case it does set apply lag to zero */
log_warning("Standby receive (%s) location appears less than standby replay location (%s)\n",
last_xlog_receive_location,
last_xlog_replay_location);
apply_lag = 0;
}
apply_lag = (long long unsigned int)lsn_last_xlog_receive_location - lsn_last_xlog_replay_location;
/* Calculate replication lag */
if (lsn_master_current_xlog_location >= lsn_last_xlog_receive_location)
@@ -1199,7 +1247,7 @@ standby_monitor(void)
}
else
{
/* This should never happen, but in case it does set lag to zero */
/* This should never happen, but in case it does set replication lag to zero */
log_warning("Master xlog (%s) location appears less than standby receive location (%s)\n",
last_wal_primary_location,
last_xlog_receive_location);
@@ -1244,8 +1292,23 @@ standby_monitor(void)
log_verbose(LOG_DEBUG, "standby_monitor:() %s\n", sqlquery);
if (PQsendQuery(master_conn, sqlquery) == 0)
log_warning(_("query could not be sent to master. %s\n"),
{
log_warning(_("query could not be sent to master: %s\n"),
PQerrorMessage(master_conn));
}
else
{
sqlquery_snprintf(sqlquery,
"SELECT %s.repmgr_update_last_updated();",
get_repmgr_schema_quoted(my_local_conn));
res = PQexec(my_local_conn, sqlquery);
/* not critical if the above query fails*/
if (PQresultStatus(res) != PGRES_TUPLES_OK)
log_warning(_("unable to set last_updated: %s\n"), PQerrorMessage(my_local_conn));
PQclear(res);
}
}
@@ -1437,7 +1500,11 @@ do_master_failover(void)
terminate(ERR_FAILOVER_FAIL);
}
sqlquery_snprintf(sqlquery, "SELECT pg_catalog.pg_last_xlog_receive_location()");
if (server_version_num >= 100000)
sqlquery_snprintf(sqlquery, "SELECT pg_catalog.pg_last_wal_receive_lsn()");
else
sqlquery_snprintf(sqlquery, "SELECT pg_catalog.pg_last_xlog_receive_location()");
res = PQexec(node_conn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
@@ -1469,7 +1536,12 @@ do_master_failover(void)
}
/* last we get info about this node, and update shared memory */
sprintf(sqlquery, "SELECT pg_catalog.pg_last_xlog_receive_location()");
if (server_version_num >= 100000)
sprintf(sqlquery, "SELECT pg_catalog.pg_last_wal_receive_lsn()");
else
sprintf(sqlquery, "SELECT pg_catalog.pg_last_xlog_receive_location()");
res = PQexec(my_local_conn, sqlquery);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
@@ -2327,13 +2399,13 @@ lsn_to_xlogrecptr(char *lsn, bool *format_ok)
if (format_ok != NULL)
*format_ok = true;
return (((XLogRecPtr) xlogid * 16 * 1024 * 1024 * 255) + xrecoff);
return (XLogRecPtr) ((uint64) xlogid) << 32 | (uint64) xrecoff;
}
void
usage(void)
{
log_err(_("%s: Replicator manager daemon \n"), progname());
log_err(_("%s: replication management daemon for PostgreSQL\n"), progname());
log_err(_("Try \"%s --help\" for more information.\n"), progname());
}

View File

@@ -1,7 +1,7 @@
#
# Makefile
#
# Copyright (c) 2ndQuadrant, 2010-2016
# Copyright (c) 2ndQuadrant, 2010-2017
#
MODULE_big = repmgr_funcs

View File

@@ -1,6 +1,6 @@
/*
* repmgr_function.sql
* Copyright (c) 2ndQuadrant, 2010-2016
* Copyright (c) 2ndQuadrant, 2010-2017
*
*/

4
sql/repmgr_test.sql Normal file
View File

@@ -0,0 +1,4 @@
select * from repmgr_update_standby_location('');
select * from repmgr_get_last_standby_location();
select * from repmgr_update_last_updated();
select * from repmgr_get_last_updated();

View File

@@ -1,6 +1,6 @@
/*
* uninstall_repmgr_funcs.sql
* Copyright (c) 2ndQuadrant, 2010-2016
* Copyright (c) 2ndQuadrant, 2010-2017
*
*/

View File

@@ -1,7 +1,7 @@
/*
* strutil.c
*
* Copyright (C) 2ndQuadrant, 2010-2016
* Copyright (c) 2ndQuadrant, 2010-2017
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/*
* strutil.h
* Copyright (C) 2ndQuadrant, 2010-2016
* Copyright (c) 2ndQuadrant, 2010-2017
*
*
* This program is free software: you can redistribute it and/or modify

View File

@@ -1,7 +1,7 @@
/*
* uninstall_repmgr.sql
*
* Copyright (C) 2ndQuadrant, 2010-2016
* Copyright (c) 2ndQuadrant, 2010-2017
*
*/

View File

@@ -1,6 +1,6 @@
#ifndef _VERSION_H_
#define _VERSION_H_
#define REPMGR_VERSION "3.3"
#define REPMGR_VERSION "3.3.2"
#endif