diff options
Diffstat (limited to 'src/backend/utils/cache/spccache.c')
-rw-r--r-- | src/backend/utils/cache/spccache.c | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/src/backend/utils/cache/spccache.c b/src/backend/utils/cache/spccache.c new file mode 100644 index 0000000..5870f43 --- /dev/null +++ b/src/backend/utils/cache/spccache.c @@ -0,0 +1,236 @@ +/*------------------------------------------------------------------------- + * + * spccache.c + * Tablespace cache management. + * + * We cache the parsed version of spcoptions for each tablespace to avoid + * needing to reparse on every lookup. Right now, there doesn't appear to + * be a measurable performance gain from doing this, but that might change + * in the future as we add more options. + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/utils/cache/spccache.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/reloptions.h" +#include "catalog/pg_tablespace.h" +#include "commands/tablespace.h" +#include "miscadmin.h" +#include "optimizer/optimizer.h" +#include "storage/bufmgr.h" +#include "utils/catcache.h" +#include "utils/hsearch.h" +#include "utils/inval.h" +#include "utils/spccache.h" +#include "utils/syscache.h" + + +/* Hash table for information about each tablespace */ +static HTAB *TableSpaceCacheHash = NULL; + +typedef struct +{ + Oid oid; /* lookup key - must be first */ + TableSpaceOpts *opts; /* options, or NULL if none */ +} TableSpaceCacheEntry; + + +/* + * InvalidateTableSpaceCacheCallback + * Flush all cache entries when pg_tablespace is updated. + * + * When pg_tablespace is updated, we must flush the cache entry at least + * for that tablespace. Currently, we just flush them all. This is quick + * and easy and doesn't cost much, since there shouldn't be terribly many + * tablespaces, nor do we expect them to be frequently modified. + */ +static void +InvalidateTableSpaceCacheCallback(Datum arg, int cacheid, uint32 hashvalue) +{ + HASH_SEQ_STATUS status; + TableSpaceCacheEntry *spc; + + hash_seq_init(&status, TableSpaceCacheHash); + while ((spc = (TableSpaceCacheEntry *) hash_seq_search(&status)) != NULL) + { + if (spc->opts) + pfree(spc->opts); + if (hash_search(TableSpaceCacheHash, + (void *) &spc->oid, + HASH_REMOVE, + NULL) == NULL) + elog(ERROR, "hash table corrupted"); + } +} + +/* + * InitializeTableSpaceCache + * Initialize the tablespace cache. + */ +static void +InitializeTableSpaceCache(void) +{ + HASHCTL ctl; + + /* Initialize the hash table. */ + ctl.keysize = sizeof(Oid); + ctl.entrysize = sizeof(TableSpaceCacheEntry); + TableSpaceCacheHash = + hash_create("TableSpace cache", 16, &ctl, + HASH_ELEM | HASH_BLOBS); + + /* Make sure we've initialized CacheMemoryContext. */ + if (!CacheMemoryContext) + CreateCacheMemoryContext(); + + /* Watch for invalidation events. */ + CacheRegisterSyscacheCallback(TABLESPACEOID, + InvalidateTableSpaceCacheCallback, + (Datum) 0); +} + +/* + * get_tablespace + * Fetch TableSpaceCacheEntry structure for a specified table OID. + * + * Pointers returned by this function should not be stored, since a cache + * flush will invalidate them. + */ +static TableSpaceCacheEntry * +get_tablespace(Oid spcid) +{ + TableSpaceCacheEntry *spc; + HeapTuple tp; + TableSpaceOpts *opts; + + /* + * Since spcid is always from a pg_class tuple, InvalidOid implies the + * default. + */ + if (spcid == InvalidOid) + spcid = MyDatabaseTableSpace; + + /* Find existing cache entry, if any. */ + if (!TableSpaceCacheHash) + InitializeTableSpaceCache(); + spc = (TableSpaceCacheEntry *) hash_search(TableSpaceCacheHash, + (void *) &spcid, + HASH_FIND, + NULL); + if (spc) + return spc; + + /* + * Not found in TableSpace cache. Check catcache. If we don't find a + * valid HeapTuple, it must mean someone has managed to request tablespace + * details for a non-existent tablespace. We'll just treat that case as + * if no options were specified. + */ + tp = SearchSysCache1(TABLESPACEOID, ObjectIdGetDatum(spcid)); + if (!HeapTupleIsValid(tp)) + opts = NULL; + else + { + Datum datum; + bool isNull; + + datum = SysCacheGetAttr(TABLESPACEOID, + tp, + Anum_pg_tablespace_spcoptions, + &isNull); + if (isNull) + opts = NULL; + else + { + bytea *bytea_opts = tablespace_reloptions(datum, false); + + opts = MemoryContextAlloc(CacheMemoryContext, VARSIZE(bytea_opts)); + memcpy(opts, bytea_opts, VARSIZE(bytea_opts)); + } + ReleaseSysCache(tp); + } + + /* + * Now create the cache entry. It's important to do this only after + * reading the pg_tablespace entry, since doing so could cause a cache + * flush. + */ + spc = (TableSpaceCacheEntry *) hash_search(TableSpaceCacheHash, + (void *) &spcid, + HASH_ENTER, + NULL); + spc->opts = opts; + return spc; +} + +/* + * get_tablespace_page_costs + * Return random and/or sequential page costs for a given tablespace. + * + * This value is not locked by the transaction, so this value may + * be changed while a SELECT that has used these values for planning + * is still executing. + */ +void +get_tablespace_page_costs(Oid spcid, + double *spc_random_page_cost, + double *spc_seq_page_cost) +{ + TableSpaceCacheEntry *spc = get_tablespace(spcid); + + Assert(spc != NULL); + + if (spc_random_page_cost) + { + if (!spc->opts || spc->opts->random_page_cost < 0) + *spc_random_page_cost = random_page_cost; + else + *spc_random_page_cost = spc->opts->random_page_cost; + } + + if (spc_seq_page_cost) + { + if (!spc->opts || spc->opts->seq_page_cost < 0) + *spc_seq_page_cost = seq_page_cost; + else + *spc_seq_page_cost = spc->opts->seq_page_cost; + } +} + +/* + * get_tablespace_io_concurrency + * + * This value is not locked by the transaction, so this value may + * be changed while a SELECT that has used these values for planning + * is still executing. + */ +int +get_tablespace_io_concurrency(Oid spcid) +{ + TableSpaceCacheEntry *spc = get_tablespace(spcid); + + if (!spc->opts || spc->opts->effective_io_concurrency < 0) + return effective_io_concurrency; + else + return spc->opts->effective_io_concurrency; +} + +/* + * get_tablespace_maintenance_io_concurrency + */ +int +get_tablespace_maintenance_io_concurrency(Oid spcid) +{ + TableSpaceCacheEntry *spc = get_tablespace(spcid); + + if (!spc->opts || spc->opts->maintenance_io_concurrency < 0) + return maintenance_io_concurrency; + else + return spc->opts->maintenance_io_concurrency; +} |