mirror of
https://github.com/EnterpriseDB/repmgr.git
synced 2026-03-27 08:56:29 +00:00
Dropping phase2 from repmgr, it now is an independent tool
called pg_blender
This commit is contained in:
@@ -1,47 +0,0 @@
|
|||||||
Phase 2
|
|
||||||
=======
|
|
||||||
|
|
||||||
Idea is that we keep a transaction running on primary with a snapshot
|
|
||||||
that prevents VACUUM from ever removing records that a query on the
|
|
||||||
standby can see. So there are never any query conflicts.
|
|
||||||
|
|
||||||
Make two connections to primary, P1 and P2. One to Standby, S.
|
|
||||||
|
|
||||||
P1:
|
|
||||||
BEGIN;
|
|
||||||
SELECT txid_current(); INTO Pn->xid
|
|
||||||
SELECT pg_sleep(10000000000);
|
|
||||||
|
|
||||||
P2:
|
|
||||||
BEGIN;
|
|
||||||
SELECT txid_current(); INTO Pn->xid
|
|
||||||
SELECT pg_sleep(10000000000);
|
|
||||||
|
|
||||||
Then every 1sec
|
|
||||||
|
|
||||||
S: GetOldestXmin() INTO S->xmin
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If both P1 and P2 are older than oldest xmin on S
|
|
||||||
* then we cancel the oldest of P1 and P2, and
|
|
||||||
* start a new xid on that channel. So that P1 and P2
|
|
||||||
* slowly step forwards, as queries complete on the
|
|
||||||
* standby.
|
|
||||||
*/
|
|
||||||
if (TransactionIdPrecedes(P1->xid, S->xmin) &&
|
|
||||||
TransactionIdPrecedes(P2->xid, S->xmin))
|
|
||||||
{
|
|
||||||
if (TransactionIdPrecedes(P1->xid, P2->xid))
|
|
||||||
cancel_and_rerun(1)
|
|
||||||
else
|
|
||||||
cancel_and_rerun(2)
|
|
||||||
)
|
|
||||||
|
|
||||||
cancel_and_rerun()
|
|
||||||
{
|
|
||||||
cancel Pn
|
|
||||||
|
|
||||||
BEGIN;
|
|
||||||
SELECT txid_current(); INTO Pn->xid
|
|
||||||
SELECT pg_sleep(10000000000);
|
|
||||||
}
|
|
||||||
156
main.c
156
main.c
@@ -12,7 +12,6 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "repmgr.h"
|
#include "repmgr.h"
|
||||||
#include "access/transam.h"
|
|
||||||
|
|
||||||
char myClusterName[MAXLEN];
|
char myClusterName[MAXLEN];
|
||||||
|
|
||||||
@@ -36,7 +35,6 @@ void getLocalMonitoredInfo(char *currTimestamp, char *xlogLocation,
|
|||||||
|
|
||||||
void MonitorCheck(void);
|
void MonitorCheck(void);
|
||||||
void MonitorExecute(void);
|
void MonitorExecute(void);
|
||||||
TransactionId startSleepingTransaction(PGconn * conn, bool stop_current);
|
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
@@ -199,92 +197,13 @@ getLocalMonitoredInfo(char *currTimestamp, char *xlogLocation, char *xlogTimesta
|
|||||||
|
|
||||||
void
|
void
|
||||||
MonitorCheck(void) {
|
MonitorCheck(void) {
|
||||||
PGconn *p1;
|
/*
|
||||||
PGconn *p2;
|
* Every 3 seconds, insert monitor info
|
||||||
|
|
||||||
TransactionId p1_xid;
|
|
||||||
TransactionId p2_xid;
|
|
||||||
TransactionId local_xmin;
|
|
||||||
|
|
||||||
int cicles = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We are trying to avoid cleanup on primary for records that are still
|
|
||||||
* visible on standby
|
|
||||||
*/
|
|
||||||
p1 = establishDBConnection(primaryConninfo, false);
|
|
||||||
p2 = establishDBConnection(primaryConninfo, false);
|
|
||||||
|
|
||||||
p1_xid = startSleepingTransaction(p1, false);
|
|
||||||
if (p1_xid == InvalidTransactionId)
|
|
||||||
{
|
|
||||||
PQfinish(myLocalConn);
|
|
||||||
PQfinish(primaryConn);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
p2_xid = startSleepingTransaction(p2, false);
|
|
||||||
if (p2_xid == InvalidTransactionId)
|
|
||||||
{
|
|
||||||
PQfinish(p1);
|
|
||||||
PQfinish(myLocalConn);
|
|
||||||
PQfinish(primaryConn);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We are using two long running transactions on primary to avoid
|
|
||||||
* cleanup of records that are still visible on this standby.
|
|
||||||
* Every second check if we can let advance the cleanup to avoid
|
|
||||||
* bloat on primary.
|
|
||||||
*
|
|
||||||
* Every 3 cicles (around 3 seconds), insert monitor info
|
|
||||||
*/
|
*/
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
PGresult *res;
|
MonitorExecute();
|
||||||
|
sleep(3);
|
||||||
res = PQexec(myLocalConn, "SELECT get_oldest_xmin()");
|
|
||||||
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "PQexec failed: %s", PQerrorMessage(myLocalConn));
|
|
||||||
PQclear(res);
|
|
||||||
}
|
|
||||||
local_xmin = atol(PQgetvalue(res, 0, 0));
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
if (TransactionIdPrecedes(p1_xid, local_xmin) &&
|
|
||||||
TransactionIdPrecedes(p2_xid, local_xmin))
|
|
||||||
{
|
|
||||||
if (TransactionIdPrecedes(p1_xid, p2_xid))
|
|
||||||
{
|
|
||||||
p1_xid = startSleepingTransaction(p1, true);
|
|
||||||
if (p1_xid == InvalidTransactionId)
|
|
||||||
{
|
|
||||||
PQfinish(p1);
|
|
||||||
PQfinish(p2);
|
|
||||||
PQfinish(myLocalConn);
|
|
||||||
PQfinish(primaryConn);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
p2_xid = startSleepingTransaction(p2, true);
|
|
||||||
if (p2_xid == InvalidTransactionId)
|
|
||||||
{
|
|
||||||
PQfinish(p1);
|
|
||||||
PQfinish(p2);
|
|
||||||
PQfinish(myLocalConn);
|
|
||||||
PQfinish(primaryConn);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
if (cicles++ >= 3)
|
|
||||||
MonitorExecute();
|
|
||||||
sleep(1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -407,70 +326,3 @@ checkNodeConfiguration(char *conninfo)
|
|||||||
}
|
}
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TransactionId
|
|
||||||
startSleepingTransaction(PGconn *conn, bool stop_current) {
|
|
||||||
TransactionId txid;
|
|
||||||
PGresult *res;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* if stop_current is set we cancel any query that is currently
|
|
||||||
* executing on this connection
|
|
||||||
*/
|
|
||||||
if (stop_current)
|
|
||||||
{
|
|
||||||
char errbuf[256];
|
|
||||||
|
|
||||||
if (PQcancel(PQgetCancel(conn), errbuf, 256) == 0)
|
|
||||||
fprintf(stderr, "Can't stop current query: %s", errbuf);
|
|
||||||
|
|
||||||
if (PQisBusy(conn) == 1)
|
|
||||||
return InvalidTransactionId;
|
|
||||||
else
|
|
||||||
PQexec(conn, "ROLLBACK");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PQexec(conn, "BEGIN"))
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Can't start a transaction on primary. Error: %s",
|
|
||||||
PQerrorMessage(conn));
|
|
||||||
return InvalidTransactionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
res = PQexec(conn, "SELECT txid_current()");
|
|
||||||
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "PQexec failed: %s", PQerrorMessage(conn));
|
|
||||||
PQclear(res);
|
|
||||||
return InvalidTransactionId;
|
|
||||||
}
|
|
||||||
txid = atol(PQgetvalue(res, 0, 0));
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
/* Let this transaction sleep */
|
|
||||||
PQsendQuery(conn, "SELECT pg_sleep(10000000000)");
|
|
||||||
return txid;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* TransactionIdPrecedes --- is id1 logically < id2?
|
|
||||||
* This function was copied from src/backend/access/transam/transam.c
|
|
||||||
*/
|
|
||||||
bool
|
|
||||||
TransactionIdPrecedes(TransactionId id1, TransactionId id2)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* If either ID is a permanent XID then we can just do unsigned
|
|
||||||
* comparison. If both are normal, do a modulo-2^31 comparison.
|
|
||||||
*/
|
|
||||||
int32 diff;
|
|
||||||
|
|
||||||
if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
|
|
||||||
return (id1 < id2);
|
|
||||||
|
|
||||||
diff = (int32) (id1 - id2);
|
|
||||||
return (diff < 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,12 +8,10 @@
|
|||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
#include "fmgr.h"
|
#include "fmgr.h"
|
||||||
#include "access/xlog.h"
|
#include "access/xlog.h"
|
||||||
#include "storage/procarray.h"
|
|
||||||
|
|
||||||
PG_MODULE_MAGIC;
|
PG_MODULE_MAGIC;
|
||||||
|
|
||||||
Datum last_xlog_replay_timestamp(PG_FUNCTION_ARGS);
|
Datum last_xlog_replay_timestamp(PG_FUNCTION_ARGS);
|
||||||
Datum oldest_xmin(PG_FUNCTION_ARGS);
|
|
||||||
|
|
||||||
PG_FUNCTION_INFO_V1(last_xlog_replay_timestamp);
|
PG_FUNCTION_INFO_V1(last_xlog_replay_timestamp);
|
||||||
|
|
||||||
@@ -31,12 +29,3 @@ bool fromSource;
|
|||||||
PG_RETURN_TIMESTAMPTZ(rTime);
|
PG_RETURN_TIMESTAMPTZ(rTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PG_FUNCTION_INFO_V1(oldest_xmin);
|
|
||||||
|
|
||||||
Datum
|
|
||||||
oldest_xmin(PG_FUNCTION_ARGS)
|
|
||||||
{
|
|
||||||
PG_RETURN_INT64(GetOldestXmin(false, false));
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -7,7 +7,3 @@
|
|||||||
CREATE FUNCTION get_last_xlog_replay_timestamp() RETURNS timestamp with time zone
|
CREATE FUNCTION get_last_xlog_replay_timestamp() RETURNS timestamp with time zone
|
||||||
AS 'MODULE_PATHNAME', 'last_xlog_replay_timestamp'
|
AS 'MODULE_PATHNAME', 'last_xlog_replay_timestamp'
|
||||||
LANGUAGE C STRICT;
|
LANGUAGE C STRICT;
|
||||||
|
|
||||||
CREATE FUNCTION get_oldest_xmin() RETURNS bigint
|
|
||||||
AS 'MODULE_PATHNAME', 'oldest_xmin'
|
|
||||||
LANGUAGE C STRICT;
|
|
||||||
|
|||||||
@@ -1,2 +1 @@
|
|||||||
DROP FUNCTION get_last_xlog_replay_timestamp();
|
DROP FUNCTION get_last_xlog_replay_timestamp();
|
||||||
DROP FUNCTION get_oldest_xmin();
|
|
||||||
|
|||||||
Reference in New Issue
Block a user