v1-0001-Add-Standby-Recovery-Conflicts-Info.patch
text/plain
Filename: v1-0001-Add-Standby-Recovery-Conflicts-Info.patch
Type: text/plain
Part: 0
Patch
Format: unified
Series: patch v1-0001
| File | + | − |
|---|---|---|
| src/backend/access/transam/xlog.c | 3 | 0 |
| src/backend/postmaster/pgstat.c | 31 | 0 |
| src/backend/storage/ipc/procsignal.c | 36 | 0 |
| src/backend/storage/ipc/standby.c | 73 | 0 |
| src/backend/tcop/postgres.c | 56 | 0 |
| src/include/access/xlog.h | 1 | 0 |
| src/include/pgstat.h | 1 | 0 |
| src/include/storage/procsignal.h | 1 | 0 |
src/backend/access/transam/xlog.c | 3 ++
src/backend/postmaster/pgstat.c | 31 +++++++++++++++
src/backend/storage/ipc/procsignal.c | 36 ++++++++++++++++++
src/backend/storage/ipc/standby.c | 73 ++++++++++++++++++++++++++++++++++++
src/backend/tcop/postgres.c | 56 +++++++++++++++++++++++++++
src/include/access/xlog.h | 1 +
src/include/pgstat.h | 1 +
src/include/storage/procsignal.h | 1 +
8 files changed, 202 insertions(+)
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 55cac186dc..d3f923f951 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -880,6 +880,8 @@ static bool holdingAllLocks = false;
#ifdef WAL_DEBUG
static MemoryContext walDebugCxt = NULL;
#endif
+/* For LogBlockedWalRecordInfo */
+XLogReaderState *current_replayed_xlog = NULL;
static void readRecoverySignalFile(void);
static void validateRecoveryParameters(void);
@@ -7319,6 +7321,7 @@ StartupXLOG(void)
TransactionIdIsValid(record->xl_xid))
RecordKnownAssignedTransactionIds(record->xl_xid);
+ current_replayed_xlog = xlogreader;
/* Now apply the WAL record itself */
RmgrTable[record->xl_rmid].rm_redo(xlogreader);
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 309378ae54..513c9cd643 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -6633,6 +6633,37 @@ pgstat_db_requested(Oid databaseid)
return false;
}
+/* ----------
+ * pgstat_get_state_name() -
+ *
+ * Return a string representing the current backend state.
+ * ----------
+ */
+const char *
+pgstat_get_state_name(BackendState backend_state)
+{
+ switch (backend_state)
+ {
+ case STATE_IDLE:
+ return "idle";
+ case STATE_RUNNING:
+ return "active";
+ case STATE_IDLEINTRANSACTION:
+ return "idle in transaction";
+ case STATE_FASTPATH:
+ return "fastpath function call";
+ case STATE_IDLEINTRANSACTION_ABORTED:
+ return "idle in transaction (aborted)";
+ case STATE_DISABLED:
+ return "disabled";
+ case STATE_UNDEFINED:
+ return "";
+ default:
+ Assert(0);
+ }
+ return "";
+}
+
/*
* Convert a potentially unsafely truncated activity string (see
* PgBackendStatus.st_activity_raw's documentation) into a correctly truncated
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index c809196d06..2d5886ff80 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -576,3 +576,39 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
errno = save_errno;
}
+
+extern const char *get_procsignal_reason_desc(ProcSignalReason reason)
+ {
+ const char *reasonDesc = "unknown reason";
+
+ switch (reason)
+ {
+ case PROCSIG_RECOVERY_CONFLICT_BUFFERPIN:
+ reasonDesc = "User was holding shared buffer pin for too long.";
+ break;
+ case PROCSIG_RECOVERY_CONFLICT_LOCK:
+ reasonDesc = "User was holding a relation lock for too long.";
+ break;
+ case PROCSIG_RECOVERY_CONFLICT_TABLESPACE:
+ reasonDesc = "User was or might have been using tablespace that must be dropped.";
+ break;
+ case PROCSIG_RECOVERY_CONFLICT_SNAPSHOT:
+ reasonDesc = "User query might have needed to see row versions that must be removed.";
+ break;
+ case PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK:
+ reasonDesc = "User transaction caused buffer deadlock with recovery.";
+ break;
+ case PROCSIG_RECOVERY_CONFLICT_DATABASE:
+ reasonDesc = "User was connected to a database that must be dropped.";
+ break;
+ case PROCSIG_CATCHUP_INTERRUPT:
+ case PROCSIG_NOTIFY_INTERRUPT:
+ case PROCSIG_PARALLEL_MESSAGE:
+ case PROCSIG_WALSND_INIT_STOPPING:
+ case NUM_PROCSIGNALS:
+ break;
+ default:
+ break;
+ }
+ return reasonDesc;
+}
diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c
index 9e0d5ec257..ac85211be0 100644
--- a/src/backend/storage/ipc/standby.c
+++ b/src/backend/storage/ipc/standby.c
@@ -16,6 +16,7 @@
*-------------------------------------------------------------------------
*/
#include "postgres.h"
+#include "access/rmgr.h"
#include "access/transam.h"
#include "access/twophase.h"
#include "access/xact.h"
@@ -34,6 +35,7 @@
#include "utils/ps_status.h"
#include "utils/timeout.h"
#include "utils/timestamp.h"
+#include "access/xlog_internal.h"
/* User-settable GUC parameters */
int vacuum_defer_cleanup_age;
@@ -215,6 +217,57 @@ WaitExceedsMaxStandbyDelay(uint32 wait_event_info)
return false;
}
+/*
+ * Display information about the wal record
+ * apply being blocked
+ */
+static void
+LogBlockedWalRecordInfo(int num_waitlist_entries, ProcSignalReason reason)
+{
+ RelFileNode rnode;
+ ForkNumber forknum;
+ BlockNumber blknum;
+ uint64 wal_record_lsn;
+ TimestampTz rtime;
+ bool fromStream;
+ char *receipt_time_str;
+ RmgrId rmgrId = -1;
+ uint8 info = 0;
+ const char *rm_name;
+ const char *rm_info;
+ char *wal_record_info_str;
+
+ if (num_waitlist_entries > 0)
+ ereport(LOG, (errmsg("wal record apply is blocked by %d connection(s), reason: %s",
+ num_waitlist_entries, get_procsignal_reason_desc(reason))));
+ else
+ ereport(LOG, (errmsg("wal record apply is blocked, reason: %s",
+ get_procsignal_reason_desc(reason))));
+
+ wal_record_lsn = current_replayed_xlog->ReadRecPtr;
+ rmgrId = XLogRecGetRmid(current_replayed_xlog);
+ info = XLogRecGetInfo(current_replayed_xlog) & ~XLR_INFO_MASK;
+
+ rm_name = RmgrTable[rmgrId].rm_name;
+ rm_info = RmgrTable[rmgrId].rm_identify(info);
+
+ GetXLogReceiptTime(&rtime, &fromStream);
+ receipt_time_str = pstrdup(timestamptz_to_str(rtime));
+ wal_record_info_str = psprintf("blocked wal record rmgr: %s, lsn: %X/%08X, received at: %s, desc: %s", rm_name, (uint32) (wal_record_lsn >> 32), (uint32) wal_record_lsn, receipt_time_str, rm_info);
+
+ if (XLogRecGetBlockTag(current_replayed_xlog, 0, &rnode, &forknum, &blknum))
+ ereport(LOG, (errmsg("%s, relation: rel %u/%u/%u fork %s blk %u",
+ wal_record_info_str,
+ rnode.spcNode, rnode.dbNode, rnode.relNode,
+ forkNames[forknum],
+ blknum)));
+ else
+ ereport(LOG, (errmsg("%s", wal_record_info_str)));
+
+ pfree(wal_record_info_str);
+ pfree(receipt_time_str);
+}
+
/*
* This is the main executioner for any query backend that conflicts with
* recovery processing. Judgement has already been passed on it within
@@ -232,6 +285,8 @@ ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
{
TimestampTz waitStart = 0;
char *new_status;
+ VirtualTransactionId *tmpWaitlist;
+ int num_waitlist_entries;
/* Fast exit, to avoid a kernel call if there's no work to be done. */
if (!VirtualTransactionIdIsValid(*waitlist))
@@ -241,6 +296,17 @@ ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
waitStart = GetCurrentTimestamp();
new_status = NULL; /* we haven't changed the ps display */
+ tmpWaitlist = waitlist;
+ while (VirtualTransactionIdIsValid(*tmpWaitlist))
+ {
+ tmpWaitlist++;
+ }
+
+ num_waitlist_entries = (tmpWaitlist - waitlist);
+
+ /* display wal record information */
+ LogBlockedWalRecordInfo(num_waitlist_entries, reason);
+
while (VirtualTransactionIdIsValid(*waitlist))
{
/* reset standbyWait_us for each xact we wait for */
@@ -370,6 +436,10 @@ ResolveRecoveryConflictWithDatabase(Oid dbid)
* block during InitPostgres() and then disconnect when they see the
* database has been removed.
*/
+
+ /* display wal record information */
+ LogBlockedWalRecordInfo(CountDBBackends(dbid), PROCSIG_RECOVERY_CONFLICT_DATABASE);
+
while (CountDBBackends(dbid) > 0)
{
CancelDBBackends(dbid, PROCSIG_RECOVERY_CONFLICT_DATABASE, true);
@@ -609,6 +679,9 @@ StandbyTimeoutHandler(void)
/* forget any pending STANDBY_DEADLOCK_TIMEOUT request */
disable_timeout(STANDBY_DEADLOCK_TIMEOUT, false);
+ /* display wal record information */
+ LogBlockedWalRecordInfo(-1, PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
+
SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index c9424f167c..192aa1dc52 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -126,6 +126,8 @@ char *stack_base_ptr = NULL;
char *register_stack_base_ptr = NULL;
#endif
+#define UINT32_ACCESS_ONCE(var) ((uint32)(*((volatile uint32 *)&(var))))
+
/*
* Flag to keep track of whether we have started a transaction.
* For extended query protocol this has to be remembered across messages.
@@ -191,6 +193,7 @@ static void drop_unnamed_stmt(void);
static void log_disconnections(int code, Datum arg);
static void enable_statement_timeout(void);
static void disable_statement_timeout(void);
+static void LogBlockingWalRecordApplyInfo(void);
/* ----------------------------------------------------------------
@@ -2886,6 +2889,58 @@ FloatExceptionHandler(SIGNAL_ARGS)
"invalid operation, such as division by zero.")));
}
+/*
+ * Display information about the backend
+ * about to be interrupted due to recovery conflict
+ */
+void
+LogBlockingWalRecordApplyInfo(void)
+{
+
+ PgBackendStatus *beentry;
+ int num_backends = pgstat_fetch_stat_numbackends();
+ int curr_backend;
+ uint32 raw_wait_event;
+ const char *wait_event = NULL;
+ const char *wait_event_type = NULL;
+ char *clipped_activity;
+ const char *activity_start;
+ const char *backend_state_name;
+ const char *backend_type;
+
+ Assert(MyProc != NULL);
+ /*
+ * Make sure any stats snapshot is thrown away
+ */
+ pgstat_clear_snapshot();
+
+ for (curr_backend = 1; curr_backend <= num_backends; curr_backend++)
+ {
+ beentry = pgstat_fetch_stat_beentry(curr_backend);
+ if (beentry->st_procpid != MyProc->pid)
+ continue;
+
+ backend_state_name = pgstat_get_state_name(beentry->st_state);
+ backend_type = GetBackendTypeDesc(beentry->st_backendType);
+ activity_start = timestamptz_to_str(beentry->st_activity_start_timestamp);
+ clipped_activity = pgstat_clip_activity(beentry->st_activity_raw);
+
+ raw_wait_event = UINT32_ACCESS_ONCE(MyProc->wait_event_info);
+ wait_event = pgstat_get_wait_event(raw_wait_event);
+ wait_event_type = pgstat_get_wait_event_type(raw_wait_event);
+
+ ereport(LOG,
+ (errmsg("about to interrupt pid: %d, backend_type: %s, state: %s, wait_event_type: %s, wait_event: %s, query_start: %s",
+ MyProc->pid, backend_type, backend_state_name, wait_event_type ? wait_event_type : "none", wait_event ? wait_event : "none", activity_start),
+ errdetail("statement: %s", clipped_activity)));
+ pfree(clipped_activity);
+ }
+ /*
+ * Make sure any stats snapshot is thrown away
+ */
+ pgstat_clear_snapshot();
+}
+
/*
* RecoveryConflictInterrupt: out-of-line portion of recovery conflict
* handling following receipt of SIGUSR1. Designed to be similar to die()
@@ -2986,6 +3041,7 @@ RecoveryConflictInterrupt(ProcSignalReason reason)
(int) reason);
}
+ LogBlockingWalRecordApplyInfo();
Assert(RecoveryConflictPending && (QueryCancelPending || ProcDiePending));
/*
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index e917dfe92d..eafd4201db 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -100,6 +100,7 @@ typedef enum
extern XLogRecPtr ProcLastRecPtr;
extern XLogRecPtr XactLastRecEnd;
extern PGDLLIMPORT XLogRecPtr XactLastCommitEnd;
+extern XLogReaderState *current_replayed_xlog;
extern bool reachedConsistency;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index c55dc1481c..2fca769bc9 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -1334,6 +1334,7 @@ extern PgStat_TableStatus *find_tabstat_entry(Oid rel_id);
extern PgStat_BackendFunctionEntry *find_funcstat_entry(Oid func_id);
extern void pgstat_initstats(Relation rel);
+extern const char *pgstat_get_state_name(BackendState backend_state);
extern char *pgstat_clip_activity(const char *raw_activity);
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index a0c0bc3ce5..c57413b06d 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -70,5 +70,6 @@ extern void WaitForProcSignalBarrier(uint64 generation);
extern void ProcessProcSignalBarrier(void);
extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
+extern const char *get_procsignal_reason_desc(ProcSignalReason reason);
#endif /* PROCSIGNAL_H */