1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-23 14:01:44 +03:00

Create a "relation mapping" infrastructure to support changing the relfilenodes

of shared or nailed system catalogs.  This has two key benefits:

* The new CLUSTER-based VACUUM FULL can be applied safely to all catalogs.

* We no longer have to use an unsafe reindex-in-place approach for reindexing
  shared catalogs.

CLUSTER on nailed catalogs now works too, although I left it disabled on
shared catalogs because the resulting pg_index.indisclustered update would
only be visible in one database.

Since reindexing shared system catalogs is now fully transactional and
crash-safe, the former special cases in REINDEX behavior have been removed;
shared catalogs are treated the same as non-shared.

This commit does not do anything about the recently-discussed problem of
deadlocks between VACUUM FULL/CLUSTER on a system catalog and other
concurrent queries; will address that in a separate patch.  As a stopgap,
parallel_schedule has been tweaked to run vacuum.sql by itself, to avoid
such failures during the regression tests.
This commit is contained in:
Tom Lane
2010-02-07 20:48:13 +00:00
parent 7fc30c488f
commit b9b8831ad6
54 changed files with 2316 additions and 585 deletions

View File

@ -80,7 +80,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/inval.c,v 1.93 2010/02/03 01:14:17 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/cache/inval.c,v 1.94 2010/02/07 20:48:10 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -96,6 +96,7 @@
#include "utils/inval.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/relmapper.h"
#include "utils/syscache.h"
@ -325,6 +326,21 @@ AddCatcacheInvalidationMessage(InvalidationListHeader *hdr,
AddInvalidationMessage(&hdr->cclist, &msg);
}
/*
* Add a whole-catalog inval entry
*/
static void
AddCatalogInvalidationMessage(InvalidationListHeader *hdr,
Oid dbId, Oid catId)
{
SharedInvalidationMessage msg;
msg.cat.id = SHAREDINVALCATALOG_ID;
msg.cat.dbId = dbId;
msg.cat.catId = catId;
AddInvalidationMessage(&hdr->cclist, &msg);
}
/*
* Add a relcache inval entry
*/
@ -406,6 +422,18 @@ RegisterCatcacheInvalidation(int cacheId,
cacheId, hashValue, tuplePtr, dbId);
}
/*
* RegisterCatalogInvalidation
*
* Register an invalidation event for all catcache entries from a catalog.
*/
static void
RegisterCatalogInvalidation(Oid dbId, Oid catId)
{
AddCatalogInvalidationMessage(&transInvalInfo->CurrentCmdInvalidMsgs,
dbId, catId);
}
/*
* RegisterRelcacheInvalidation
*
@ -443,30 +471,32 @@ RegisterRelcacheInvalidation(Oid dbId, Oid relId)
static void
LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg)
{
int i;
if (msg->id >= 0)
{
if (msg->cc.dbId == MyDatabaseId || msg->cc.dbId == 0)
if (msg->cc.dbId == MyDatabaseId || msg->cc.dbId == InvalidOid)
{
CatalogCacheIdInvalidate(msg->cc.id,
msg->cc.hashValue,
&msg->cc.tuplePtr);
for (i = 0; i < syscache_callback_count; i++)
{
struct SYSCACHECALLBACK *ccitem = syscache_callback_list + i;
CallSyscacheCallbacks(msg->cc.id, &msg->cc.tuplePtr);
}
}
else if (msg->id == SHAREDINVALCATALOG_ID)
{
if (msg->cat.dbId == MyDatabaseId || msg->cat.dbId == InvalidOid)
{
CatalogCacheFlushCatalog(msg->cat.catId);
if (ccitem->id == msg->cc.id)
(*ccitem->function) (ccitem->arg,
msg->cc.id, &msg->cc.tuplePtr);
}
/* CatalogCacheFlushCatalog calls CallSyscacheCallbacks as needed */
}
}
else if (msg->id == SHAREDINVALRELCACHE_ID)
{
if (msg->rc.dbId == MyDatabaseId || msg->rc.dbId == InvalidOid)
{
int i;
RelationCacheInvalidateEntry(msg->rc.relId);
for (i = 0; i < relcache_callback_count; i++)
@ -485,6 +515,14 @@ LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg)
*/
smgrclosenode(msg->sm.rnode);
}
else if (msg->id == SHAREDINVALRELMAP_ID)
{
/* We only care about our own database and shared catalogs */
if (msg->rm.dbId == InvalidOid)
RelationMapInvalidate(true);
else if (msg->rm.dbId == MyDatabaseId)
RelationMapInvalidate(false);
}
else
elog(FATAL, "unrecognized SI message id: %d", msg->id);
}
@ -506,7 +544,7 @@ InvalidateSystemCaches(void)
int i;
ResetCatalogCaches();
RelationCacheInvalidate(); /* gets smgr cache too */
RelationCacheInvalidate(); /* gets smgr and relmap too */
for (i = 0; i < syscache_callback_count; i++)
{
@ -874,7 +912,7 @@ ProcessCommittedInvalidationMessages(SharedInvalidationMessage *msgs,
else
{
/*
* Invalidation message is a SHAREDINVALSMGR_ID
* Invalidation message is a catalog or nontransactional inval,
* which never cause relcache file invalidation,
* so we ignore them, no matter which db they're for.
*/
@ -1182,6 +1220,30 @@ CacheInvalidateHeapTuple(Relation relation, HeapTuple tuple)
PrepareForTupleInvalidation(relation, tuple);
}
/*
* CacheInvalidateCatalog
* Register invalidation of the whole content of a system catalog.
*
* This is normally used in VACUUM FULL/CLUSTER, where we haven't so much
* changed any tuples as moved them around. Some uses of catcache entries
* expect their TIDs to be correct, so we have to blow away the entries.
*
* Note: we expect caller to verify that the rel actually is a system
* catalog. If it isn't, no great harm is done, just a wasted sinval message.
*/
void
CacheInvalidateCatalog(Oid catalogId)
{
Oid databaseId;
if (IsSharedRelation(catalogId))
databaseId = InvalidOid;
else
databaseId = MyDatabaseId;
RegisterCatalogInvalidation(databaseId, catalogId);
}
/*
* CacheInvalidateRelcache
* Register invalidation of the specified relation's relcache entry
@ -1277,6 +1339,31 @@ CacheInvalidateSmgr(RelFileNode rnode)
SendSharedInvalidMessages(&msg, 1);
}
/*
* CacheInvalidateRelmap
* Register invalidation of the relation mapping for a database,
* or for the shared catalogs if databaseId is zero.
*
* Sending this type of invalidation msg forces other backends to re-read
* the indicated relation mapping file. It is also necessary to send a
* relcache inval for the specific relations whose mapping has been altered,
* else the relcache won't get updated with the new filenode data.
*
* Note: because these messages are nontransactional, they won't be captured
* in commit/abort WAL entries. Instead, calls to CacheInvalidateRelmap()
* should happen in low-level relmapper.c routines, which are executed while
* replaying WAL as well as when creating it.
*/
void
CacheInvalidateRelmap(Oid databaseId)
{
SharedInvalidationMessage msg;
msg.rm.id = SHAREDINVALRELMAP_ID;
msg.rm.dbId = databaseId;
SendSharedInvalidMessages(&msg, 1);
}
/*
* CacheRegisterSyscacheCallback
@ -1323,3 +1410,23 @@ CacheRegisterRelcacheCallback(RelcacheCallbackFunction func,
++relcache_callback_count;
}
/*
* CallSyscacheCallbacks
*
* This is exported so that CatalogCacheFlushCatalog can call it, saving
* this module from knowing which catcache IDs correspond to which catalogs.
*/
void
CallSyscacheCallbacks(int cacheid, ItemPointer tuplePtr)
{
int i;
for (i = 0; i < syscache_callback_count; i++)
{
struct SYSCACHECALLBACK *ccitem = syscache_callback_list + i;
if (ccitem->id == cacheid)
(*ccitem->function) (ccitem->arg, cacheid, tuplePtr);
}
}