I don't understand how to reach errdetail_abort() with
MyProc->recoveryConflictPending set. If a recovery conflict signal is
received, ProcessRecoveryConflictInterrupt() raises an ERROR or FATAL
error to cancel the query or connection, and abort processing clears
the flag. The error message from ProcessRecoveryConflictInterrupt() is
very clear that the query or connection was terminated because of
recovery conflict.
The only way to reach it AFAICS is with a race condition, if the
startup process sends a recovery conflict signal when the transaction
has just entered aborted state for some other reason. And in that case
the detail would be misleading, as the transaction was already aborted
for some other reason, not because of the recovery conflict.
errdetail_abort() was the only user of the recoveryConflictPending
flag in PGPROC, so we can remove that and all the related code too.
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Discussion: https://www.postgresql.org/message-id/
4cc13ba1-4248-4884-b6ba-
4805349e7f39@iki.fi
/* be sure this is cleared in abort */
proc->delayChkptFlags = 0;
- proc->recoveryConflictPending = false;
-
/* must be cleared with xid/xmin: */
/* avoid unnecessarily dirtying shared cachelines */
if (proc->statusFlags & PROC_VACUUM_STATE_MASK)
/* be sure this is cleared in abort */
proc->delayChkptFlags = 0;
- proc->recoveryConflictPending = false;
-
/* must be cleared with xid/xmin: */
/* avoid unnecessarily dirtying shared cachelines */
if (proc->statusFlags & PROC_VACUUM_STATE_MASK)
proc->vxid.lxid = InvalidLocalTransactionId;
proc->xmin = InvalidTransactionId;
- proc->recoveryConflictPending = false;
Assert(!(proc->statusFlags & PROC_VACUUM_STATE_MASK));
Assert(!proc->delayChkptFlags);
}
/*
- * CancelVirtualTransaction - used in recovery conflict processing
+ * SignalVirtualTransaction - used in recovery conflict processing
*
* Returns pid of the process signaled, or 0 if not found.
*/
pid_t
-CancelVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode)
-{
- return SignalVirtualTransaction(vxid, sigmode, true);
-}
-
-pid_t
-SignalVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode,
- bool conflictPending)
+SignalVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode)
{
ProcArrayStruct *arrayP = procArray;
int index;
if (procvxid.procNumber == vxid.procNumber &&
procvxid.localTransactionId == vxid.localTransactionId)
{
- proc->recoveryConflictPending = conflictPending;
pid = proc->pid;
if (pid != 0)
{
* CancelDBBackends --- cancel backends that are using specified database
*/
void
-CancelDBBackends(Oid databaseid, ProcSignalReason sigmode, bool conflictPending)
+CancelDBBackends(Oid databaseid, ProcSignalReason sigmode)
{
ProcArrayStruct *arrayP = procArray;
int index;
GET_VXID_FROM_PGPROC(procvxid, *proc);
- proc->recoveryConflictPending = conflictPending;
pid = proc->pid;
if (pid != 0)
{
* Now find out who to throw out of the balloon.
*/
Assert(VirtualTransactionIdIsValid(*waitlist));
- pid = CancelVirtualTransaction(*waitlist, reason);
+ pid = SignalVirtualTransaction(*waitlist, reason);
/*
* Wait a little bit for it to die so that we avoid flooding
*/
while (CountDBBackends(dbid) > 0)
{
- CancelDBBackends(dbid, PROCSIG_RECOVERY_CONFLICT_DATABASE, true);
+ CancelDBBackends(dbid, PROCSIG_RECOVERY_CONFLICT_DATABASE);
/*
* Wait awhile for them to die so that we avoid flooding an
while (VirtualTransactionIdIsValid(*backends))
{
SignalVirtualTransaction(*backends,
- PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK,
- false);
+ PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
backends++;
}
/*
* We send signal to all backends to ask them if they are holding the
- * buffer pin which is delaying the Startup process. We must not set the
- * conflict flag yet, since most backends will be innocent. Let the
- * SIGUSR1 handling in each backend decide their own fate.
+ * buffer pin which is delaying the Startup process. Most of them will be
+ * innocent, but we let the SIGUSR1 handling in each backend decide their
+ * own fate.
*/
- CancelDBBackends(InvalidOid, reason, false);
+ CancelDBBackends(InvalidOid, reason);
}
/*
Assert(dlist_is_empty(&(MyProc->myProcLocks[i])));
}
#endif
- MyProc->recoveryConflictPending = false;
/* Initialize fields for sync rep */
MyProc->waitLSN = InvalidXLogRecPtr;
static bool check_log_statement(List *stmt_list);
static int errdetail_execute(List *raw_parsetree_list);
static int errdetail_params(ParamListInfo params);
-static int errdetail_abort(void);
static void bind_param_error_callback(void *arg);
static void start_xact_command(void);
static void finish_xact_command(void);
ereport(ERROR,
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
errmsg("current transaction is aborted, "
- "commands ignored until end of transaction block"),
- errdetail_abort()));
+ "commands ignored until end of transaction block")));
/* Make sure we are in a transaction command */
start_xact_command();
ereport(ERROR,
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
errmsg("current transaction is aborted, "
- "commands ignored until end of transaction block"),
- errdetail_abort()));
+ "commands ignored until end of transaction block")));
/*
* Create the CachedPlanSource before we do parse analysis, since it
ereport(ERROR,
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
errmsg("current transaction is aborted, "
- "commands ignored until end of transaction block"),
- errdetail_abort()));
+ "commands ignored until end of transaction block")));
/*
* Create the portal. Allow silent replacement of an existing portal only
ereport(ERROR,
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
errmsg("current transaction is aborted, "
- "commands ignored until end of transaction block"),
- errdetail_abort()));
+ "commands ignored until end of transaction block")));
/* Check for cancel signal before we start execution */
CHECK_FOR_INTERRUPTS();
return 0;
}
-/*
- * errdetail_abort
- *
- * Add an errdetail() line showing abort reason, if any.
- */
-static int
-errdetail_abort(void)
-{
- if (MyProc->recoveryConflictPending)
- errdetail("Abort reason: recovery conflict");
-
- return 0;
-}
-
/*
* errdetail_recovery_conflict
*
ereport(ERROR,
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
errmsg("current transaction is aborted, "
- "commands ignored until end of transaction block"),
- errdetail_abort()));
+ "commands ignored until end of transaction block")));
if (whereToSendOutput != DestRemote)
return; /* can't actually do anything... */
ereport(ERROR,
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
errmsg("current transaction is aborted, "
- "commands ignored until end of transaction block"),
- errdetail_abort()));
+ "commands ignored until end of transaction block")));
if (whereToSendOutput != DestRemote)
return; /* can't actually do anything... */
return;
}
- MyProc->recoveryConflictPending = true;
-
/* Intentional fall through to error handling */
/* FALLTHROUGH */
bool isRegularBackend; /* true if it's a regular backend. */
- /*
- * While in hot standby mode, shows that a conflict signal has been sent
- * for the current transaction. Set/cleared while holding ProcArrayLock,
- * though not required. Accessed without lock, if needed.
- */
- bool recoveryConflictPending;
-
/*
* Info about LWLock the process is currently waiting for, if any.
*
bool excludeXmin0, bool allDbs, int excludeVacuum,
int *nvxids);
extern VirtualTransactionId *GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid);
-extern pid_t CancelVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode);
-extern pid_t SignalVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode,
- bool conflictPending);
+extern pid_t SignalVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode);
extern bool MinimumActiveBackends(int min);
extern int CountDBBackends(Oid databaseid);
extern int CountDBConnections(Oid databaseid);
-extern void CancelDBBackends(Oid databaseid, ProcSignalReason sigmode, bool conflictPending);
+extern void CancelDBBackends(Oid databaseid, ProcSignalReason sigmode);
extern int CountUserBackends(Oid roleid);
extern bool CountOtherDBBackends(Oid databaseId,
int *nbackends, int *nprepared);