First part of refactoring of code for ResolveRecoveryConflict. Purposes

of this are to centralise the conflict code to allow further change,
as well as to allow passing through the full reason for the conflict
through to the conflicting backends. Backend state alters how we
can handle different types of conflict so this is now required.
As originally suggested by Heikki, no longer optional.
This commit is contained in:
Simon Riggs 2010-01-14 11:08:02 +00:00
parent 0fba3bef55
commit e99767bc28
6 changed files with 144 additions and 151 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.281 2010/01/10 04:26:36 rhaas Exp $
* $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.282 2010/01/14 11:08:00 sriggs Exp $
*
*
* INTERFACE ROUTINES
@ -4139,16 +4139,7 @@ heap_xlog_cleanup_info(XLogRecPtr lsn, XLogRecord *record)
xl_heap_cleanup_info *xlrec = (xl_heap_cleanup_info *) XLogRecGetData(record);
if (InHotStandby)
{
VirtualTransactionId *backends;
backends = GetConflictingVirtualXIDs(xlrec->latestRemovedXid,
InvalidOid,
true);
ResolveRecoveryConflictWithVirtualXIDs(backends,
"VACUUM index cleanup",
CONFLICT_MODE_ERROR);
}
ResolveRecoveryConflictWithSnapshot(xlrec->latestRemovedXid);
/*
* Actual operation is a no-op. Record type exists to provide a means
@ -4180,16 +4171,7 @@ heap_xlog_clean(XLogRecPtr lsn, XLogRecord *record, bool clean_move)
* no queries running for which the removed tuples are still visible.
*/
if (InHotStandby)
{
VirtualTransactionId *backends;
backends = GetConflictingVirtualXIDs(xlrec->latestRemovedXid,
InvalidOid,
true);
ResolveRecoveryConflictWithVirtualXIDs(backends,
"VACUUM heap cleanup",
CONFLICT_MODE_ERROR);
}
ResolveRecoveryConflictWithSnapshot(xlrec->latestRemovedXid);
RestoreBkpBlocks(lsn, record, true);
@ -4259,25 +4241,7 @@ heap_xlog_freeze(XLogRecPtr lsn, XLogRecord *record)
* consider the frozen xids as running.
*/
if (InHotStandby)
{
VirtualTransactionId *backends;
/*
* XXX: Using cutoff_xid is overly conservative. Even if cutoff_xid
* is recent enough to conflict with a backend, the actual values
* being frozen might not be. With a typical vacuum_freeze_min_age
* setting in the ballpark of millions of transactions, it won't make
* a difference, but it might if you run a manual VACUUM FREEZE.
* Typically the cutoff is much earlier than any recently deceased
* tuple versions removed by this vacuum, so don't worry too much.
*/
backends = GetConflictingVirtualXIDs(cutoff_xid,
InvalidOid,
true);
ResolveRecoveryConflictWithVirtualXIDs(backends,
"VACUUM heap freeze",
CONFLICT_MODE_ERROR);
}
ResolveRecoveryConflictWithSnapshot(cutoff_xid);
RestoreBkpBlocks(lsn, record, false);

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtxlog.c,v 1.57 2010/01/02 16:57:35 momjian Exp $
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtxlog.c,v 1.58 2010/01/14 11:08:00 sriggs Exp $
*
*-------------------------------------------------------------------------
*/
@ -822,28 +822,18 @@ btree_redo(XLogRecPtr lsn, XLogRecord *record)
* just once when that arrives. After that any we know that no conflicts
* exist from individual btree vacuum records on that index.
*/
if (InHotStandby)
if (InHotStandby && info == XLOG_BTREE_DELETE)
{
if (info == XLOG_BTREE_DELETE)
{
xl_btree_delete *xlrec = (xl_btree_delete *) XLogRecGetData(record);
VirtualTransactionId *backends;
xl_btree_delete *xlrec = (xl_btree_delete *) XLogRecGetData(record);
/*
* XXX Currently we put everybody on death row, because
* currently _bt_delitems() supplies InvalidTransactionId.
* This can be fairly painful, so providing a better value
* here is worth some thought and possibly some effort to
* improve.
*/
backends = GetConflictingVirtualXIDs(xlrec->latestRemovedXid,
InvalidOid,
true);
ResolveRecoveryConflictWithVirtualXIDs(backends,
"b-tree delete",
CONFLICT_MODE_ERROR);
}
/*
* XXX Currently we put everybody on death row, because
* currently _bt_delitems() supplies InvalidTransactionId.
* This can be fairly painful, so providing a better value
* here is worth some thought and possibly some effort to
* improve.
*/
ResolveRecoveryConflictWithSnapshot(xlrec->latestRemovedXid);
}
/*

View File

@ -13,7 +13,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.231 2010/01/10 15:44:28 sriggs Exp $
* $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.232 2010/01/14 11:08:00 sriggs Exp $
*
*-------------------------------------------------------------------------
*/
@ -1944,29 +1944,7 @@ dbase_redo(XLogRecPtr lsn, XLogRecord *record)
dst_path = GetDatabasePath(xlrec->db_id, xlrec->tablespace_id);
if (InHotStandby)
{
/*
* We don't do ResolveRecoveryConflictWithVirutalXIDs() here since
* that only waits for transactions and completely idle sessions
* would block us. This is rare enough that we do this as simply
* as possible: no wait, just force them off immediately.
*
* No locking is required here because we already acquired
* AccessExclusiveLock. Anybody trying to connect while we do this
* will block during InitPostgres() and then disconnect when they
* see the database has been removed.
*/
while (CountDBBackends(xlrec->db_id) > 0)
{
CancelDBBackends(xlrec->db_id);
/*
* Wait awhile for them to die so that we avoid flooding an
* unresponsive backend when system is heavily loaded.
*/
pg_usleep(10000);
}
}
ResolveRecoveryConflictWithDatabase(xlrec->db_id);
/* Drop pages for this database that are in the shared buffer cache */
DropDatabaseBuffers(xlrec->db_id);

View File

@ -40,7 +40,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.71 2010/01/12 02:42:51 momjian Exp $
* $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.72 2010/01/14 11:08:01 sriggs Exp $
*
*-------------------------------------------------------------------------
*/
@ -1377,33 +1377,7 @@ tblspc_redo(XLogRecPtr lsn, XLogRecord *record)
*/
if (!destroy_tablespace_directories(xlrec->ts_id, true))
{
VirtualTransactionId *temp_file_users;
/*
* Standby users may be currently using this tablespace for
* for their temporary files. We only care about current
* users because temp_tablespace parameter will just ignore
* tablespaces that no longer exist.
*
* Ask everybody to cancel their queries immediately so
* we can ensure no temp files remain and we can remove the
* tablespace. Nuke the entire site from orbit, it's the only
* way to be sure.
*
* XXX: We could work out the pids of active backends
* using this tablespace by examining the temp filenames in the
* directory. We would then convert the pids into VirtualXIDs
* before attempting to cancel them.
*
* We don't wait for commit because drop tablespace is
* non-transactional.
*/
temp_file_users = GetConflictingVirtualXIDs(InvalidTransactionId,
InvalidOid,
false);
ResolveRecoveryConflictWithVirtualXIDs(temp_file_users,
"drop tablespace",
CONFLICT_MODE_ERROR);
ResolveRecoveryConflictWithTablespace(xlrec->ts_id);
/*
* If we did recovery processing then hopefully the

View File

@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/ipc/standby.c,v 1.3 2010/01/02 16:57:51 momjian Exp $
* $PostgreSQL: pgsql/src/backend/storage/ipc/standby.c,v 1.4 2010/01/14 11:08:02 sriggs Exp $
*
*-------------------------------------------------------------------------
*/
@ -37,6 +37,9 @@ int vacuum_defer_cleanup_age;
static List *RecoveryLockList;
static void ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
char *reason, int cancel_mode);
static void ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid);
static void LogCurrentRunningXacts(RunningTransactions CurrRunningXacts);
static void LogAccessExclusiveLocks(int nlocks, xl_standby_lock *locks);
@ -162,7 +165,7 @@ WaitExceedsMaxStandbyDelay(void)
*
* We may ask for a specific cancel_mode, typically ERROR or FATAL.
*/
void
static void
ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
char *reason, int cancel_mode)
{
@ -272,6 +275,119 @@ ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
}
}
void
ResolveRecoveryConflictWithSnapshot(TransactionId latestRemovedXid)
{
VirtualTransactionId *backends;
backends = GetConflictingVirtualXIDs(latestRemovedXid,
InvalidOid,
true);
ResolveRecoveryConflictWithVirtualXIDs(backends,
"snapshot conflict",
CONFLICT_MODE_ERROR);
}
void
ResolveRecoveryConflictWithTablespace(Oid tsid)
{
VirtualTransactionId *temp_file_users;
/*
* Standby users may be currently using this tablespace for
* for their temporary files. We only care about current
* users because temp_tablespace parameter will just ignore
* tablespaces that no longer exist.
*
* Ask everybody to cancel their queries immediately so
* we can ensure no temp files remain and we can remove the
* tablespace. Nuke the entire site from orbit, it's the only
* way to be sure.
*
* XXX: We could work out the pids of active backends
* using this tablespace by examining the temp filenames in the
* directory. We would then convert the pids into VirtualXIDs
* before attempting to cancel them.
*
* We don't wait for commit because drop tablespace is
* non-transactional.
*/
temp_file_users = GetConflictingVirtualXIDs(InvalidTransactionId,
InvalidOid,
false);
ResolveRecoveryConflictWithVirtualXIDs(temp_file_users,
"drop tablespace",
CONFLICT_MODE_ERROR);
}
void
ResolveRecoveryConflictWithDatabase(Oid dbid)
{
/*
* We don't do ResolveRecoveryConflictWithVirutalXIDs() here since
* that only waits for transactions and completely idle sessions
* would block us. This is rare enough that we do this as simply
* as possible: no wait, just force them off immediately.
*
* No locking is required here because we already acquired
* AccessExclusiveLock. Anybody trying to connect while we do this
* will block during InitPostgres() and then disconnect when they
* see the database has been removed.
*/
while (CountDBBackends(dbid) > 0)
{
CancelDBBackends(dbid);
/*
* Wait awhile for them to die so that we avoid flooding an
* unresponsive backend when system is heavily loaded.
*/
pg_usleep(10000);
}
}
static void
ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid)
{
VirtualTransactionId *backends;
bool report_memory_error = false;
bool lock_acquired = false;
int num_attempts = 0;
LOCKTAG locktag;
SET_LOCKTAG_RELATION(locktag, dbOid, relOid);
/*
* If blowing away everybody with conflicting locks doesn't work,
* after the first two attempts then we just start blowing everybody
* away until it does work. We do this because its likely that we
* either have too many locks and we just can't get one at all,
* or that there are many people crowding for the same table.
* Recovery must win; the end justifies the means.
*/
while (!lock_acquired)
{
if (++num_attempts < 3)
backends = GetLockConflicts(&locktag, AccessExclusiveLock);
else
{
backends = GetConflictingVirtualXIDs(InvalidTransactionId,
InvalidOid,
true);
report_memory_error = true;
}
ResolveRecoveryConflictWithVirtualXIDs(backends,
"exclusive lock",
CONFLICT_MODE_ERROR);
if (LockAcquireExtended(&locktag, AccessExclusiveLock, true, true, false)
!= LOCKACQUIRE_NOT_AVAIL)
lock_acquired = true;
}
}
/*
* -----------------------------------------------------
* Locking in Recovery Mode
@ -303,8 +419,6 @@ StandbyAcquireAccessExclusiveLock(TransactionId xid, Oid dbOid, Oid relOid)
{
xl_standby_lock *newlock;
LOCKTAG locktag;
bool report_memory_error = false;
int num_attempts = 0;
/* Already processed? */
if (TransactionIdDidCommit(xid) || TransactionIdDidAbort(xid))
@ -323,41 +437,13 @@ StandbyAcquireAccessExclusiveLock(TransactionId xid, Oid dbOid, Oid relOid)
RecoveryLockList = lappend(RecoveryLockList, newlock);
/*
* Attempt to acquire the lock as requested.
* Attempt to acquire the lock as requested, if not resolve conflict
*/
SET_LOCKTAG_RELATION(locktag, newlock->dbOid, newlock->relOid);
/*
* Wait for lock to clear or kill anyone in our way.
*/
while (LockAcquireExtended(&locktag, AccessExclusiveLock,
true, true, report_memory_error)
if (LockAcquireExtended(&locktag, AccessExclusiveLock, true, true, false)
== LOCKACQUIRE_NOT_AVAIL)
{
VirtualTransactionId *backends;
/*
* If blowing away everybody with conflicting locks doesn't work,
* after the first two attempts then we just start blowing everybody
* away until it does work. We do this because its likely that we
* either have too many locks and we just can't get one at all,
* or that there are many people crowding for the same table.
* Recovery must win; the end justifies the means.
*/
if (++num_attempts < 3)
backends = GetLockConflicts(&locktag, AccessExclusiveLock);
else
{
backends = GetConflictingVirtualXIDs(InvalidTransactionId,
InvalidOid,
true);
report_memory_error = true;
}
ResolveRecoveryConflictWithVirtualXIDs(backends,
"exclusive lock",
CONFLICT_MODE_ERROR);
}
ResolveRecoveryConflictWithLock(newlock->dbOid, newlock->relOid);
}
static void

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/storage/standby.h,v 1.2 2010/01/02 16:58:08 momjian Exp $
* $PostgreSQL: pgsql/src/include/storage/standby.h,v 1.3 2010/01/14 11:08:02 sriggs Exp $
*
*-------------------------------------------------------------------------
*/
@ -24,8 +24,9 @@ extern int vacuum_defer_cleanup_age;
#define CONFLICT_MODE_ERROR 1 /* Conflict can be resolved by canceling query */
#define CONFLICT_MODE_FATAL 2 /* Conflict can only be resolved by disconnecting session */
extern void ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
char *reason, int cancel_mode);
extern void ResolveRecoveryConflictWithSnapshot(TransactionId latestRemovedXid);
extern void ResolveRecoveryConflictWithTablespace(Oid tsid);
extern void ResolveRecoveryConflictWithDatabase(Oid dbid);
extern void InitRecoveryTransactionEnvironment(void);
extern void ShutdownRecoveryTransactionEnvironment(void);