use-main-command-loop-in-walsender-1.patch
text/x-diff
Filename: use-main-command-loop-in-walsender-1.patch
Type: text/x-diff
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: context
| File | + | − |
|---|---|---|
| src/backend/replication/basebackup.c | 2 | 1 |
| src/backend/replication/walsender.c | 47 | 0 |
| src/backend/tcop/postgres.c | 45 | 0 |
| src/include/replication/walsender.h | 3 | 1 |
*** a/src/backend/replication/basebackup.c
--- b/src/backend/replication/basebackup.c
***************
*** 22,27 ****
--- 22,28 ----
#include "lib/stringinfo.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
+ #include "miscadmin.h"
#include "nodes/pg_list.h"
#include "replication/basebackup.h"
#include "replication/walsender.h"
***************
*** 30,36 ****
#include "storage/ipc.h"
#include "utils/builtins.h"
#include "utils/elog.h"
- #include "utils/memutils.h"
#include "utils/ps_status.h"
typedef struct
--- 31,36 ----
***************
*** 370,388 **** void
SendBaseBackup(BaseBackupCmd *cmd)
{
DIR *dir;
- MemoryContext backup_context;
- MemoryContext old_context;
basebackup_options opt;
parse_basebackup_options(cmd->options, &opt);
- backup_context = AllocSetContextCreate(CurrentMemoryContext,
- "Streaming base backup context",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
- old_context = MemoryContextSwitchTo(backup_context);
-
WalSndSetState(WALSNDSTATE_BACKUP);
if (update_process_title)
--- 370,379 ----
***************
*** 403,411 **** SendBaseBackup(BaseBackupCmd *cmd)
perform_base_backup(&opt, dir);
FreeDir(dir);
-
- MemoryContextSwitchTo(old_context);
- MemoryContextDelete(backup_context);
}
static void
--- 394,399 ----
***************
*** 606,612 **** sendDir(char *path, int basepathlen, bool sizeonly)
* error in that case. The error handler further up will call
* do_pg_abort_backup() for us.
*/
! if (walsender_shutdown_requested || walsender_ready_to_stop)
ereport(ERROR,
(errmsg("shutdown requested, aborting active base backup")));
--- 594,600 ----
* error in that case. The error handler further up will call
* do_pg_abort_backup() for us.
*/
! if (ProcDiePending || walsender_ready_to_stop)
ereport(ERROR,
(errmsg("shutdown requested, aborting active base backup")));
*** a/src/backend/replication/walsender.c
--- b/src/backend/replication/walsender.c
***************
*** 113,133 **** static TimestampTz last_reply_timestamp;
/* Flags set by signal handlers for later service in main loop */
static volatile sig_atomic_t got_SIGHUP = false;
- volatile sig_atomic_t walsender_shutdown_requested = false;
volatile sig_atomic_t walsender_ready_to_stop = false;
/* Signal handlers */
static void WalSndSigHupHandler(SIGNAL_ARGS);
- static void WalSndShutdownHandler(SIGNAL_ARGS);
- static void WalSndQuickDieHandler(SIGNAL_ARGS);
static void WalSndXLogSendHandler(SIGNAL_ARGS);
static void WalSndLastCycleHandler(SIGNAL_ARGS);
/* Prototypes for private functions */
- static bool HandleReplicationCommand(const char *cmd_string);
static void WalSndLoop(void) __attribute__((noreturn));
! static void InitWalSnd(void);
! static void WalSndHandshake(void);
static void WalSndKill(int code, Datum arg);
static void XLogSend(char *msgbuf, bool *caughtup);
static void IdentifySystem(void);
--- 113,128 ----
/* Flags set by signal handlers for later service in main loop */
static volatile sig_atomic_t got_SIGHUP = false;
volatile sig_atomic_t walsender_ready_to_stop = false;
/* Signal handlers */
static void WalSndSigHupHandler(SIGNAL_ARGS);
static void WalSndXLogSendHandler(SIGNAL_ARGS);
static void WalSndLastCycleHandler(SIGNAL_ARGS);
/* Prototypes for private functions */
static void WalSndLoop(void) __attribute__((noreturn));
! static void InitWalSenderSlot(void);
static void WalSndKill(int code, Datum arg);
static void XLogSend(char *msgbuf, bool *caughtup);
static void IdentifySystem(void);
***************
*** 139,284 **** static void ProcessRepliesIfAny(void);
static void WalSndKeepalive(char *msgbuf);
! /* Main entry point for walsender process */
void
! WalSenderMain(void)
{
- MemoryContext walsnd_context;
-
am_cascading_walsender = RecoveryInProgress();
/* Create a per-walsender data structure in shared memory */
! InitWalSnd();
!
! /*
! * Create a memory context that we will do all our work in. We do this so
! * that we can reset the context during error recovery and thereby avoid
! * possible memory leaks. Formerly this code just ran in
! * TopMemoryContext, but resetting that would be a really bad idea.
! *
! * XXX: we don't actually attempt error recovery in walsender, we just
! * close the connection and exit.
! */
! walsnd_context = AllocSetContextCreate(TopMemoryContext,
! "Wal Sender",
! ALLOCSET_DEFAULT_MINSIZE,
! ALLOCSET_DEFAULT_INITSIZE,
! ALLOCSET_DEFAULT_MAXSIZE);
! MemoryContextSwitchTo(walsnd_context);
/* Set up resource owner */
CurrentResourceOwner = ResourceOwnerCreate(NULL, "walsender top-level resource owner");
- /* Unblock signals (they were blocked when the postmaster forked us) */
- PG_SETMASK(&UnBlockSig);
-
/*
* Use the recovery target timeline ID during recovery
*/
if (am_cascading_walsender)
ThisTimeLineID = GetRecoveryTargetTLI();
-
- /* Tell the standby that walsender is ready for receiving commands */
- ReadyForQuery(DestRemote);
-
- /* Handle handshake messages before streaming */
- WalSndHandshake();
-
- /* Initialize shared memory status */
- {
- /* use volatile pointer to prevent code rearrangement */
- volatile WalSnd *walsnd = MyWalSnd;
-
- SpinLockAcquire(&walsnd->mutex);
- walsnd->sentPtr = sentPtr;
- SpinLockRelease(&walsnd->mutex);
- }
-
- SyncRepInitConfig();
-
- /* Main loop of walsender */
- WalSndLoop();
}
/*
! * Execute commands from walreceiver, until we enter streaming mode.
*/
! static void
! WalSndHandshake(void)
{
! StringInfoData input_message;
! bool replication_started = false;
!
! initStringInfo(&input_message);
!
! while (!replication_started)
{
! int firstchar;
!
! WalSndSetState(WALSNDSTATE_STARTUP);
! set_ps_display("idle", false);
!
! /* Wait for a command to arrive */
! firstchar = pq_getbyte();
!
! /*
! * Emergency bailout if postmaster has died. This is to avoid the
! * necessity for manual cleanup of all postmaster children.
! */
! if (!PostmasterIsAlive())
! exit(1);
!
! /*
! * Check for any other interesting events that happened while we
! * slept.
! */
! if (got_SIGHUP)
! {
! got_SIGHUP = false;
! ProcessConfigFile(PGC_SIGHUP);
! }
!
! if (firstchar != EOF)
! {
! /*
! * Read the message contents. This is expected to be done without
! * blocking because we've been able to get message type code.
! */
! if (pq_getmessage(&input_message, 0))
! firstchar = EOF; /* suitable message already logged */
! }
!
! /* Handle the very limited subset of commands expected in this phase */
! switch (firstchar)
! {
! case 'Q': /* Query message */
! {
! const char *query_string;
!
! query_string = pq_getmsgstring(&input_message);
! pq_getmsgend(&input_message);
!
! if (HandleReplicationCommand(query_string))
! replication_started = true;
! }
! break;
!
! case 'X':
! /* standby is closing the connection */
! proc_exit(0);
!
! case EOF:
! /* standby disconnected unexpectedly */
! ereport(COMMERROR,
! (errcode(ERRCODE_PROTOCOL_VIOLATION),
! errmsg("unexpected EOF on standby connection")));
! proc_exit(0);
!
! default:
! ereport(FATAL,
! (errcode(ERRCODE_PROTOCOL_VIOLATION),
! errmsg("invalid standby handshake message type %d", firstchar)));
! }
}
}
--- 134,172 ----
static void WalSndKeepalive(char *msgbuf);
! /* Initialize walsender process before entering the main command loop */
void
! InitWalSender(void)
{
am_cascading_walsender = RecoveryInProgress();
/* Create a per-walsender data structure in shared memory */
! InitWalSenderSlot();
/* Set up resource owner */
CurrentResourceOwner = ResourceOwnerCreate(NULL, "walsender top-level resource owner");
/*
* Use the recovery target timeline ID during recovery
*/
if (am_cascading_walsender)
ThisTimeLineID = GetRecoveryTargetTLI();
}
/*
! * Clean up after an error.
! *
! * WAL sender processes don't use transactions like regular backends do.
! * This should do any cleanup required in a WAL sender process, similar to
! * what transaction abort does in a regular backend.
*/
! void
! WalSndErrorCleanup()
{
! if (sendFile >= 0)
{
! close(sendFile);
! sendFile = -1;
}
}
***************
*** 350,364 **** IdentifySystem(void)
pq_sendbytes(&buf, (char *) xpos, strlen(xpos));
pq_endmessage(&buf);
-
- /* Send CommandComplete and ReadyForQuery messages */
- EndCommand("SELECT", DestRemote);
- ReadyForQuery(DestRemote);
- /* ReadyForQuery did pq_flush for us */
}
/*
! * START_REPLICATION
*/
static void
StartReplication(StartReplicationCmd *cmd)
--- 238,250 ----
pq_sendbytes(&buf, (char *) xpos, strlen(xpos));
pq_endmessage(&buf);
}
/*
! * Handle START_REPLICATION command.
! *
! * At the moment, this never returns, but an ereport(ERROR) will take us back
! * to the main loop.
*/
static void
StartReplication(StartReplicationCmd *cmd)
***************
*** 435,449 **** StartReplication(StartReplicationCmd *cmd)
* be shipped from that position
*/
sentPtr = cmd->startpoint;
}
/*
* Execute an incoming replication command.
*/
! static bool
! HandleReplicationCommand(const char *cmd_string)
{
- bool replication_started = false;
int parse_rc;
Node *cmd_node;
MemoryContext cmd_context;
--- 321,349 ----
* be shipped from that position
*/
sentPtr = cmd->startpoint;
+
+ /* Also update the start position status in shared memory */
+ {
+ /* use volatile pointer to prevent code rearrangement */
+ volatile WalSnd *walsnd = MyWalSnd;
+
+ SpinLockAcquire(&walsnd->mutex);
+ walsnd->sentPtr = sentPtr;
+ SpinLockRelease(&walsnd->mutex);
+ }
+
+ SyncRepInitConfig();
+
+ /* Main loop of walsender */
+ WalSndLoop();
}
/*
* Execute an incoming replication command.
*/
! void
! exec_replication_command(const char *cmd_string)
{
int parse_rc;
Node *cmd_node;
MemoryContext cmd_context;
***************
*** 451,456 **** HandleReplicationCommand(const char *cmd_string)
--- 351,358 ----
elog(DEBUG1, "received replication command: %s", cmd_string);
+ CHECK_FOR_INTERRUPTS();
+
cmd_context = AllocSetContextCreate(CurrentMemoryContext,
"Replication command context",
ALLOCSET_DEFAULT_MINSIZE,
***************
*** 476,493 **** HandleReplicationCommand(const char *cmd_string)
case T_StartReplicationCmd:
StartReplication((StartReplicationCmd *) cmd_node);
-
- /* break out of the loop */
- replication_started = true;
break;
case T_BaseBackupCmd:
SendBaseBackup((BaseBackupCmd *) cmd_node);
-
- /* Send CommandComplete and ReadyForQuery messages */
- EndCommand("SELECT", DestRemote);
- ReadyForQuery(DestRemote);
- /* ReadyForQuery did pq_flush for us */
break;
default:
--- 378,387 ----
***************
*** 500,506 **** HandleReplicationCommand(const char *cmd_string)
MemoryContextSwitchTo(old_context);
MemoryContextDelete(cmd_context);
! return replication_started;
}
/*
--- 394,401 ----
MemoryContextSwitchTo(old_context);
MemoryContextDelete(cmd_context);
! /* Send CommandComplete message */
! EndCommand("SELECT", DestRemote);
}
/*
***************
*** 754,768 **** WalSndLoop(void)
SyncRepInitConfig();
}
! /* Normal exit from the walsender is here */
! if (walsender_shutdown_requested)
! {
! /* Inform the standby that XLOG streaming is done */
! pq_puttextmessage('C', "COPY 0");
! pq_flush();
!
! proc_exit(0);
! }
/* Check for input from the client */
ProcessRepliesIfAny();
--- 649,655 ----
SyncRepInitConfig();
}
! CHECK_FOR_INTERRUPTS();
/* Check for input from the client */
ProcessRepliesIfAny();
***************
*** 813,819 **** WalSndLoop(void)
XLogSend(output_message, &caughtup);
if (caughtup && !pq_is_send_pending())
{
! walsender_shutdown_requested = true;
continue; /* don't want to wait more */
}
}
--- 700,706 ----
XLogSend(output_message, &caughtup);
if (caughtup && !pq_is_send_pending())
{
! ProcDiePending = true;
continue; /* don't want to wait more */
}
}
***************
*** 854,861 **** WalSndLoop(void)
--- 741,751 ----
}
/* Sleep until something happens or replication timeout */
+ ImmediateInterruptOK = true;
+ CHECK_FOR_INTERRUPTS();
WaitLatchOrSocket(&MyWalSnd->latch, wakeEvents,
MyProcPort->sock, sleeptime);
+ ImmediateInterruptOK = false;
/*
* Check for replication timeout. Note we ignore the corner case
***************
*** 892,898 **** WalSndLoop(void)
/* Initialize a per-walsender data structure for this walsender process */
static void
! InitWalSnd(void)
{
int i;
--- 782,788 ----
/* Initialize a per-walsender data structure for this walsender process */
static void
! InitWalSenderSlot(void)
{
int i;
***************
*** 1284,1341 **** WalSndSigHupHandler(SIGNAL_ARGS)
errno = save_errno;
}
- /* SIGTERM: set flag to shut down */
- static void
- WalSndShutdownHandler(SIGNAL_ARGS)
- {
- int save_errno = errno;
-
- walsender_shutdown_requested = true;
- if (MyWalSnd)
- SetLatch(&MyWalSnd->latch);
-
- /*
- * Set the standard (non-walsender) state as well, so that we can abort
- * things like do_pg_stop_backup().
- */
- InterruptPending = true;
- ProcDiePending = true;
-
- errno = save_errno;
- }
-
- /*
- * WalSndQuickDieHandler() occurs when signalled SIGQUIT by the postmaster.
- *
- * Some backend has bought the farm,
- * so we need to stop what we're doing and exit.
- */
- static void
- WalSndQuickDieHandler(SIGNAL_ARGS)
- {
- PG_SETMASK(&BlockSig);
-
- /*
- * We DO NOT want to run proc_exit() callbacks -- we're here because
- * shared memory may be corrupted, so we don't want to try to clean up our
- * transaction. Just nail the windows shut and get out of town. Now that
- * there's an atexit callback to prevent third-party code from breaking
- * things by calling exit() directly, we have to reset the callbacks
- * explicitly to make this work as intended.
- */
- on_exit_reset();
-
- /*
- * Note we do exit(2) not exit(0). This is to force the postmaster into a
- * system reset cycle if some idiot DBA sends a manual SIGQUIT to a random
- * backend. This is necessary precisely because we don't clean up our
- * shared memory state. (The "dead man switch" mechanism in pmsignal.c
- * should ensure the postmaster sees this as a crash, too, but no harm in
- * being doubly sure.)
- */
- exit(2);
- }
-
/* SIGUSR1: set flag to send WAL records */
static void
WalSndXLogSendHandler(SIGNAL_ARGS)
--- 1174,1179 ----
***************
*** 1368,1375 **** WalSndSignals(void)
pqsignal(SIGHUP, WalSndSigHupHandler); /* set flag to read config
* file */
pqsignal(SIGINT, SIG_IGN); /* not used */
! pqsignal(SIGTERM, WalSndShutdownHandler); /* request shutdown */
! pqsignal(SIGQUIT, WalSndQuickDieHandler); /* hard crash time */
InitializeTimeouts(); /* establishes SIGALRM handler */
pqsignal(SIGPIPE, SIG_IGN);
pqsignal(SIGUSR1, WalSndXLogSendHandler); /* request WAL sending */
--- 1206,1213 ----
pqsignal(SIGHUP, WalSndSigHupHandler); /* set flag to read config
* file */
pqsignal(SIGINT, SIG_IGN); /* not used */
! pqsignal(SIGTERM, die); /* request shutdown */
! pqsignal(SIGQUIT, quickdie); /* hard crash time */
InitializeTimeouts(); /* establishes SIGALRM handler */
pqsignal(SIGPIPE, SIG_IGN);
pqsignal(SIGUSR1, WalSndXLogSendHandler); /* request WAL sending */
*** a/src/backend/tcop/postgres.c
--- b/src/backend/tcop/postgres.c
***************
*** 192,197 **** static int InteractiveBackend(StringInfo inBuf);
--- 192,198 ----
static int interactive_getc(void);
static int SocketBackend(StringInfo inBuf);
static int ReadCommand(StringInfo inBuf);
+ static void forbidden_in_wal_sender(char firstchar);
static List *pg_rewrite_query(Query *query);
static bool check_log_statement(List *stmt_list);
static int errdetail_execute(List *raw_parsetree_list);
***************
*** 3720,3731 **** PostgresMain(int argc, char *argv[], const char *username)
if (IsUnderPostmaster && Log_disconnections)
on_proc_exit(log_disconnections, 0);
! /* If this is a WAL sender process, we're done with initialization. */
if (am_walsender)
! {
! WalSenderMain(); /* does not return */
! abort();
! }
/*
* process any libraries that should be preloaded at backend start (this
--- 3721,3729 ----
if (IsUnderPostmaster && Log_disconnections)
on_proc_exit(log_disconnections, 0);
! /* Perform initialization specific to a WAL sender process. */
if (am_walsender)
! InitWalSender();
/*
* process any libraries that should be preloaded at backend start (this
***************
*** 3835,3840 **** PostgresMain(int argc, char *argv[], const char *username)
--- 3833,3841 ----
*/
AbortCurrentTransaction();
+ if (am_walsender)
+ WalSndErrorCleanup();
+
/*
* Now return to normal top-level context and clear ErrorContext for
* next time.
***************
*** 3969,3975 **** PostgresMain(int argc, char *argv[], const char *username)
query_string = pq_getmsgstring(&input_message);
pq_getmsgend(&input_message);
! exec_simple_query(query_string);
send_ready_for_query = true;
}
--- 3970,3979 ----
query_string = pq_getmsgstring(&input_message);
pq_getmsgend(&input_message);
! if (am_walsender)
! exec_replication_command(query_string);
! else
! exec_simple_query(query_string);
send_ready_for_query = true;
}
***************
*** 3982,3987 **** PostgresMain(int argc, char *argv[], const char *username)
--- 3986,3993 ----
int numParams;
Oid *paramTypes = NULL;
+ forbidden_in_wal_sender(firstchar);
+
/* Set statement_timestamp() */
SetCurrentStatementStartTimestamp();
***************
*** 4004,4009 **** PostgresMain(int argc, char *argv[], const char *username)
--- 4010,4017 ----
break;
case 'B': /* bind */
+ forbidden_in_wal_sender(firstchar);
+
/* Set statement_timestamp() */
SetCurrentStatementStartTimestamp();
***************
*** 4019,4024 **** PostgresMain(int argc, char *argv[], const char *username)
--- 4027,4034 ----
const char *portal_name;
int max_rows;
+ forbidden_in_wal_sender(firstchar);
+
/* Set statement_timestamp() */
SetCurrentStatementStartTimestamp();
***************
*** 4031,4036 **** PostgresMain(int argc, char *argv[], const char *username)
--- 4041,4048 ----
break;
case 'F': /* fastpath function call */
+ forbidden_in_wal_sender(firstchar);
+
/* Set statement_timestamp() */
SetCurrentStatementStartTimestamp();
***************
*** 4078,4083 **** PostgresMain(int argc, char *argv[], const char *username)
--- 4090,4097 ----
int close_type;
const char *close_target;
+ forbidden_in_wal_sender(firstchar);
+
close_type = pq_getmsgbyte(&input_message);
close_target = pq_getmsgstring(&input_message);
pq_getmsgend(&input_message);
***************
*** 4120,4125 **** PostgresMain(int argc, char *argv[], const char *username)
--- 4134,4141 ----
int describe_type;
const char *describe_target;
+ forbidden_in_wal_sender(firstchar);
+
/* Set statement_timestamp() (needed for xact) */
SetCurrentStatementStartTimestamp();
***************
*** 4201,4206 **** PostgresMain(int argc, char *argv[], const char *username)
--- 4217,4245 ----
} /* end of input-reading loop */
}
+ /*
+ * Throw an error if we're a WAL sender process.
+ *
+ * This is used to forbid anything else than simple query protocol messages
+ * in a WAL sender process. 'firstchar' specifies what kind of a forbidden
+ * message was received, and is used to construct the error message.
+ */
+ static void
+ forbidden_in_wal_sender(char firstchar)
+ {
+ if (am_walsender)
+ {
+ if (firstchar == 'F')
+ ereport(ERROR,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("fastpath function calls not supported in a replication connection")));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("extended query protocol not supported in a replication connection")));
+ }
+ }
+
/*
* Obtain platform stack depth limit (in bytes)
*** a/src/include/replication/walsender.h
--- b/src/include/replication/walsender.h
***************
*** 19,25 ****
/* global state */
extern bool am_walsender;
extern bool am_cascading_walsender;
- extern volatile sig_atomic_t walsender_shutdown_requested;
extern volatile sig_atomic_t walsender_ready_to_stop;
extern bool wake_wal_senders;
--- 19,24 ----
***************
*** 27,33 **** extern bool wake_wal_senders;
extern int max_wal_senders;
extern int replication_timeout;
! extern void WalSenderMain(void) __attribute__((noreturn));
extern void WalSndSignals(void);
extern Size WalSndShmemSize(void);
extern void WalSndShmemInit(void);
--- 26,34 ----
extern int max_wal_senders;
extern int replication_timeout;
! extern void InitWalSender(void);
! extern void exec_replication_command(const char *query_string);
! extern void WalSndErrorCleanup(void);
extern void WalSndSignals(void);
extern Size WalSndShmemSize(void);
extern void WalSndShmemInit(void);