diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index de061279a14d1f2f81d5280511cf1fc562da7d73..5cd4f005c6544a8a74208839d58c5cb779cf4a0d 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -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); diff --git a/src/backend/access/nbtree/nbtxlog.c b/src/backend/access/nbtree/nbtxlog.c index 55f05bdc2c9f1f0a280033d3a9fa61f7899f2db5..9e2ebd9a9f5fd70e3da06ebb515d9c2328c39cc9 100644 --- a/src/backend/access/nbtree/nbtxlog.c +++ b/src/backend/access/nbtree/nbtxlog.c @@ -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; - - /* - * 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); + xl_btree_delete *xlrec = (xl_btree_delete *) XLogRecGetData(record); - 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); } /* diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index 846f59244b4b0b70d143f41798d4b1a7cadf76f3..a45b351dd54ecd2d505073900ca7bb6c47266118 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -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); diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c index 1eaa0abeea2e6d2689a71982546a6ffda4519d52..2ff3835ab0f370d3e94efd2044ff8b4fdb13969d 100644 --- a/src/backend/commands/tablespace.c +++ b/src/backend/commands/tablespace.c @@ -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 diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c index 98a6ad6dd0bb4274bb8d3f433d3489c65fc0b293..bc9302c4bca5a0a53286a18f25873d517b569d2b 100644 --- a/src/backend/storage/ipc/standby.c +++ b/src/backend/storage/ipc/standby.c @@ -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 diff --git a/src/include/storage/standby.h b/src/include/storage/standby.h index a58d666f0de48d94cfaa76be1e784bab5855e064..3f2e2c2d85587fdd2701428d5aadcfb42a2aa977 100644 --- a/src/include/storage/standby.h +++ b/src/include/storage/standby.h @@ -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);