conflict_using_sigusr1.v1.patch
text/x-patch
Filename: conflict_using_sigusr1.v1.patch
Type: text/x-patch
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
Series: patch v1
| File | + | − |
|---|---|---|
| src/backend/storage/ipc/procarray.c | 2 | 0 |
| src/backend/storage/ipc/procsignal.c | 7 | 0 |
| src/backend/storage/ipc/standby.c | 29 | 0 |
| src/backend/tcop/postgres.c | 23 | 0 |
| src/backend/utils/init/globals.c | 2 | 0 |
| src/include/miscadmin.h | 2 | 0 |
| src/include/storage/procarray.h | 2 | 0 |
| src/include/storage/procsignal.h | 2 | 0 |
| src/include/storage/standby.h | 1 | 0 |
*** a/src/backend/storage/ipc/procarray.c
--- b/src/backend/storage/ipc/procarray.c
***************
*** 1704,1710 **** GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid,
* Returns pid of the process signaled, or 0 if not found.
*/
pid_t
! CancelVirtualTransaction(VirtualTransactionId vxid, int cancel_mode)
{
ProcArrayStruct *arrayP = procArray;
int index;
--- 1704,1710 ----
* Returns pid of the process signaled, or 0 if not found.
*/
pid_t
! CancelVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode)
{
ProcArrayStruct *arrayP = procArray;
int index;
***************
*** 1722,1733 **** CancelVirtualTransaction(VirtualTransactionId vxid, int cancel_mode)
if (procvxid.backendId == vxid.backendId &&
procvxid.localTransactionId == vxid.localTransactionId)
{
- /*
- * Issue orders for the proc to read next time it receives SIGINT
- */
- if (proc->recoveryConflictMode < cancel_mode)
- proc->recoveryConflictMode = cancel_mode;
-
pid = proc->pid;
break;
}
--- 1722,1727 ----
***************
*** 1741,1747 **** CancelVirtualTransaction(VirtualTransactionId vxid, int cancel_mode)
* Kill the pid if it's still here. If not, that's what we wanted
* so ignore any errors.
*/
! kill(pid, SIGINT);
}
return pid;
--- 1735,1741 ----
* Kill the pid if it's still here. If not, that's what we wanted
* so ignore any errors.
*/
! (void) SendProcSignal(pid, sigmode, vxid.backendId);
}
return pid;
*** a/src/backend/storage/ipc/procsignal.c
--- b/src/backend/storage/ipc/procsignal.c
***************
*** 24,29 ****
--- 24,30 ----
#include "storage/procsignal.h"
#include "storage/shmem.h"
#include "storage/sinval.h"
+ #include "storage/standby.h"
/*
***************
*** 258,262 **** procsignal_sigusr1_handler(SIGNAL_ARGS)
--- 259,269 ----
if (CheckProcSignal(PROCSIG_NOTIFY_INTERRUPT))
HandleNotifyInterrupt();
+ if (CheckProcSignal(PROCSIG_CONFLICT_ERROR_INTERRUPT))
+ HandleConflictInterrupt(ERROR);
+
+ if (CheckProcSignal(PROCSIG_CONFLICT_FATAL_INTERRUPT))
+ HandleConflictInterrupt(FATAL);
+
errno = save_errno;
}
*** a/src/backend/storage/ipc/standby.c
--- b/src/backend/storage/ipc/standby.c
***************
*** 218,229 **** ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
if (WaitExceedsMaxStandbyDelay())
{
pid_t pid;
/*
* Now find out who to throw out of the balloon.
*/
Assert(VirtualTransactionIdIsValid(*waitlist));
! pid = CancelVirtualTransaction(*waitlist, cancel_mode);
if (pid != 0)
{
--- 218,235 ----
if (WaitExceedsMaxStandbyDelay())
{
pid_t pid;
+ ProcSignalReason sigmode;
+
+ if (cancel_mode == CONFLICT_MODE_ERROR)
+ sigmode = PROCSIG_CONFLICT_ERROR_INTERRUPT;
+ else
+ sigmode = PROCSIG_CONFLICT_FATAL_INTERRUPT;
/*
* Now find out who to throw out of the balloon.
*/
Assert(VirtualTransactionIdIsValid(*waitlist));
! pid = CancelVirtualTransaction(*waitlist, sigmode);
if (pid != 0)
{
***************
*** 272,277 **** ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
--- 278,305 ----
}
}
+ void
+ HandleConflictInterrupt(int conflict_mode)
+ {
+ switch (conflict_mode)
+ {
+ case FATAL:
+ SessionCancelPending = true;
+ RecoveryConflictPending = true;
+ ProcessInterrupts(); /* Should not return */
+ return;
+
+ case ERROR:
+ TransactionCancelPending = true;
+ RecoveryConflictPending = true;
+ ProcessInterrupts();
+ return;
+
+ default:
+ elog(ERROR, "Unknown conflict mode");
+ }
+ }
+
/*
* -----------------------------------------------------
* Locking in Recovery Mode
*** a/src/backend/tcop/postgres.c
--- b/src/backend/tcop/postgres.c
***************
*** 2737,2795 **** ProcessInterrupts(void)
ereport(ERROR,
(errcode(ERRCODE_QUERY_CANCELED),
errmsg("canceling autovacuum task")));
! else
! {
! int cancelMode = MyProc->recoveryConflictMode;
!
! /*
! * XXXHS: We don't yet have a clean way to cancel an
! * idle-in-transaction session, so make it FATAL instead.
! * This isn't as bad as it looks because we don't issue a
! * CONFLICT_MODE_ERROR for a session with proc->xmin == 0
! * on cleanup conflicts. There's a possibility that we
! * marked somebody as a conflict and then they go idle.
! */
! if (DoingCommandRead && IsTransactionBlock() &&
! cancelMode == CONFLICT_MODE_ERROR)
! {
! cancelMode = CONFLICT_MODE_FATAL;
! }
!
! switch (cancelMode)
! {
! case CONFLICT_MODE_FATAL:
! Assert(RecoveryInProgress());
! ereport(FATAL,
! (errcode(ERRCODE_QUERY_CANCELED),
! errmsg("canceling session due to conflict with recovery")));
!
! case CONFLICT_MODE_ERROR:
! /*
! * We are aborting because we need to release
! * locks. So we need to abort out of all
! * subtransactions to make sure we release
! * all locks at whatever their level.
! *
! * XXX Should we try to examine the
! * transaction tree and cancel just enough
! * subxacts to remove locks? Doubt it.
! */
! Assert(RecoveryInProgress());
! AbortOutOfAnyTransaction();
! ereport(ERROR,
! (errcode(ERRCODE_QUERY_CANCELED),
! errmsg("canceling statement due to conflict with recovery")));
! return;
!
! default:
! /* No conflict pending, so fall through */
! break;
! }
!
ereport(ERROR,
(errcode(ERRCODE_QUERY_CANCELED),
errmsg("canceling statement due to user request")));
- }
}
if (TransactionCancelPending)
{
--- 2737,2746 ----
ereport(ERROR,
(errcode(ERRCODE_QUERY_CANCELED),
errmsg("canceling autovacuum task")));
! else
ereport(ERROR,
(errcode(ERRCODE_QUERY_CANCELED),
errmsg("canceling statement due to user request")));
}
if (TransactionCancelPending)
{
***************
*** 2802,2810 **** ProcessInterrupts(void)
DisableNotifyInterrupt();
DisableCatchupInterrupt();
! ereport(NOTICE,
! (errcode(ERRCODE_QUERY_CANCELED),
! errmsg("canceling idle transaction due to administrator request")));
AbortTransactionAndAnySubtransactions();
--- 2753,2766 ----
DisableNotifyInterrupt();
DisableCatchupInterrupt();
! if (RecoveryConflictPending)
! ereport(NOTICE,
! (errcode(ERRCODE_QUERY_CANCELED),
! errmsg("canceling transaction due to conflict with recovery")));
! else
! ereport(NOTICE,
! (errcode(ERRCODE_QUERY_CANCELED),
! errmsg("canceling idle transaction due to administrator request")));
AbortTransactionAndAnySubtransactions();
***************
*** 2815,2820 **** ProcessInterrupts(void)
--- 2771,2790 ----
set_ps_display("idle in transaction (aborted)", false);
pgstat_report_activity("<IDLE> in transaction (aborted)");
}
+ if (SessionCancelPending)
+ {
+ SessionCancelPending = false;
+ ImmediateInterruptOK = false; /* not idle anymore */
+
+ if (RecoveryConflictPending)
+ ereport(FATAL,
+ (errcode(ERRCODE_QUERY_CANCELED),
+ errmsg("canceling session due to conflict with recovery")));
+ else
+ ereport(FATAL,
+ (errcode(ERRCODE_QUERY_CANCELED),
+ errmsg("canceling session due to administrator request")));
+ }
/* If we get here, do nothing (probably, QueryCancelPending was reset) */
}
*** a/src/backend/utils/init/globals.c
--- b/src/backend/utils/init/globals.c
***************
*** 28,33 **** ProtocolVersion FrontendProtocol;
--- 28,35 ----
volatile bool InterruptPending = false;
volatile bool QueryCancelPending = false;
volatile bool TransactionCancelPending = false;
+ volatile bool SessionCancelPending = false;
+ volatile bool RecoveryConflictPending = false;
volatile bool ProcDiePending = false;
volatile bool ImmediateInterruptOK = false;
volatile uint32 InterruptHoldoffCount = 0;
*** a/src/include/miscadmin.h
--- b/src/include/miscadmin.h
***************
*** 69,74 ****
--- 69,76 ----
extern PGDLLIMPORT volatile bool InterruptPending;
extern volatile bool QueryCancelPending;
extern volatile bool TransactionCancelPending;
+ extern volatile bool SessionCancelPending;
+ extern volatile bool RecoveryConflictPending;
extern volatile bool ProcDiePending;
/* these are marked volatile because they are examined by signal handlers: */
*** a/src/include/storage/procarray.h
--- b/src/include/storage/procarray.h
***************
*** 15,20 ****
--- 15,21 ----
#define PROCARRAY_H
#include "storage/lock.h"
+ #include "storage/procsignal.h"
#include "storage/standby.h"
#include "utils/snapshot.h"
***************
*** 58,65 **** extern VirtualTransactionId *GetCurrentVirtualXIDs(TransactionId limitXmin,
int *nvxids);
extern VirtualTransactionId *GetConflictingVirtualXIDs(TransactionId limitXmin,
Oid dbOid, bool skipExistingConflicts);
! extern pid_t CancelVirtualTransaction(VirtualTransactionId vxid,
! int cancel_mode);
extern int CountActiveBackends(void);
extern int CountDBBackends(Oid databaseid);
--- 59,65 ----
int *nvxids);
extern VirtualTransactionId *GetConflictingVirtualXIDs(TransactionId limitXmin,
Oid dbOid, bool skipExistingConflicts);
! extern pid_t CancelVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode);
extern int CountActiveBackends(void);
extern int CountDBBackends(Oid databaseid);
*** a/src/include/storage/procsignal.h
--- b/src/include/storage/procsignal.h
***************
*** 31,36 **** typedef enum
--- 31,38 ----
{
PROCSIG_CATCHUP_INTERRUPT, /* sinval catchup interrupt */
PROCSIG_NOTIFY_INTERRUPT, /* listen/notify interrupt */
+ PROCSIG_CONFLICT_ERROR_INTERRUPT, /* recovery conflict error */
+ PROCSIG_CONFLICT_FATAL_INTERRUPT, /* recovery conflict fatal */
NUM_PROCSIGNALS /* Must be last! */
} ProcSignalReason;
*** a/src/include/storage/standby.h
--- b/src/include/storage/standby.h
***************
*** 26,31 **** extern int vacuum_defer_cleanup_age;
--- 26,32 ----
extern void ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
char *reason, int cancel_mode);
+ extern void HandleConflictInterrupt(int conflict_mode);
extern void InitRecoveryTransactionEnvironment(void);
extern void ShutdownRecoveryTransactionEnvironment(void);