Skip to content

Commit 40064a8

Browse files
committed
Optimize InvalidateAttoptCacheCallback() and TypeCacheTypCallback()
These callbacks are receiving hash values as arguments, which doesn't allow direct lookups for AttoptCacheHash and TypeCacheHash. This is why subject callbacks currently use full iteration over corresponding hashes. This commit avoids full hash iteration in InvalidateAttoptCacheCallback(), and TypeCacheTypCallback(). At first, we switch AttoptCacheHash and TypeCacheHash to use same hash function as syscache. As second, we use hash_seq_init_with_hash_value() to iterate only hash entries with matching hash value. Discussion: https://postgr.es/m/5812a6e5-68ae-4d84-9d85-b443176966a1%40sigaev.ru Author: Teodor Sigaev Reviewed-by: Aleksander Alekseev, Tom Lane, Michael Paquier, Roman Zharkov Reviewed-by: Andrei Lepikhov
1 parent d0f0200 commit 40064a8

File tree

2 files changed

+73
-21
lines changed

2 files changed

+73
-21
lines changed

src/backend/utils/cache/attoptcache.c

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,20 +44,27 @@ typedef struct
4444

4545
/*
4646
* InvalidateAttoptCacheCallback
47-
* Flush all cache entries when pg_attribute is updated.
47+
* Flush cache entry (or entries) when pg_attribute is updated.
4848
*
4949
* When pg_attribute is updated, we must flush the cache entry at least
50-
* for that attribute. Currently, we just flush them all. Since attribute
51-
* options are not currently used in performance-critical paths (such as
52-
* query execution), this seems OK.
50+
* for that attribute.
5351
*/
5452
static void
5553
InvalidateAttoptCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
5654
{
5755
HASH_SEQ_STATUS status;
5856
AttoptCacheEntry *attopt;
5957

60-
hash_seq_init(&status, AttoptCacheHash);
58+
/*
59+
* By convention, zero hash value is passed to the callback as a sign that
60+
* it's time to invalidate the whole cache. See sinval.c, inval.c and
61+
* InvalidateSystemCachesExtended().
62+
*/
63+
if (hashvalue == 0)
64+
hash_seq_init(&status, AttoptCacheHash);
65+
else
66+
hash_seq_init_with_hash_value(&status, AttoptCacheHash, hashvalue);
67+
6168
while ((attopt = (AttoptCacheEntry *) hash_seq_search(&status)) != NULL)
6269
{
6370
if (attopt->opts)
@@ -70,6 +77,18 @@ InvalidateAttoptCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
7077
}
7178
}
7279

80+
/*
81+
* Hash function compatible with two-arg system cache hash function.
82+
*/
83+
static uint32
84+
relatt_cache_syshash(const void *key, Size keysize)
85+
{
86+
const AttoptCacheKey *ckey = key;
87+
88+
Assert(keysize == sizeof(*ckey));
89+
return GetSysCacheHashValue2(ATTNUM, ckey->attrelid, ckey->attnum);
90+
}
91+
7392
/*
7493
* InitializeAttoptCache
7594
* Initialize the attribute options cache.
@@ -82,9 +101,17 @@ InitializeAttoptCache(void)
82101
/* Initialize the hash table. */
83102
ctl.keysize = sizeof(AttoptCacheKey);
84103
ctl.entrysize = sizeof(AttoptCacheEntry);
104+
105+
/*
106+
* AttoptCacheEntry takes hash value from the system cache. For
107+
* AttoptCacheHash we use the same hash in order to speedup search by hash
108+
* value. This is used by hash_seq_init_with_hash_value().
109+
*/
110+
ctl.hash = relatt_cache_syshash;
111+
85112
AttoptCacheHash =
86113
hash_create("Attopt cache", 256, &ctl,
87-
HASH_ELEM | HASH_BLOBS);
114+
HASH_ELEM | HASH_FUNCTION);
88115

89116
/* Make sure we've initialized CacheMemoryContext. */
90117
if (!CacheMemoryContext)

src/backend/utils/cache/typcache.c

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,16 @@ static dsa_pointer share_tupledesc(dsa_area *area, TupleDesc tupdesc,
331331
uint32 typmod);
332332

333333

334+
/*
335+
* Hash function compatible with one-arg system cache hash function.
336+
*/
337+
static uint32
338+
type_cache_syshash(const void *key, Size keysize)
339+
{
340+
Assert(keysize == sizeof(Oid));
341+
return GetSysCacheHashValue1(TYPEOID, ObjectIdGetDatum(*(const Oid *) key));
342+
}
343+
334344
/*
335345
* lookup_type_cache
336346
*
@@ -355,8 +365,16 @@ lookup_type_cache(Oid type_id, int flags)
355365

356366
ctl.keysize = sizeof(Oid);
357367
ctl.entrysize = sizeof(TypeCacheEntry);
368+
369+
/*
370+
* TypeEntry takes hash value from the system cache. For TypeCacheHash
371+
* we use the same hash in order to speedup search by hash value. This
372+
* is used by hash_seq_init_with_hash_value().
373+
*/
374+
ctl.hash = type_cache_syshash;
375+
358376
TypeCacheHash = hash_create("Type information cache", 64,
359-
&ctl, HASH_ELEM | HASH_BLOBS);
377+
&ctl, HASH_ELEM | HASH_FUNCTION);
360378

361379
/* Also set up callbacks for SI invalidations */
362380
CacheRegisterRelcacheCallback(TypeCacheRelCallback, (Datum) 0);
@@ -407,8 +425,7 @@ lookup_type_cache(Oid type_id, int flags)
407425

408426
/* These fields can never change, by definition */
409427
typentry->type_id = type_id;
410-
typentry->type_id_hash = GetSysCacheHashValue1(TYPEOID,
411-
ObjectIdGetDatum(type_id));
428+
typentry->type_id_hash = get_hash_value(TypeCacheHash, &type_id);
412429

413430
/* Keep this part in sync with the code below */
414431
typentry->typlen = typtup->typlen;
@@ -2358,20 +2375,28 @@ TypeCacheTypCallback(Datum arg, int cacheid, uint32 hashvalue)
23582375
TypeCacheEntry *typentry;
23592376

23602377
/* TypeCacheHash must exist, else this callback wouldn't be registered */
2361-
hash_seq_init(&status, TypeCacheHash);
2378+
2379+
/*
2380+
* By convention, zero hash value is passed to the callback as a sign that
2381+
* it's time to invalidate the whole cache. See sinval.c, inval.c and
2382+
* InvalidateSystemCachesExtended().
2383+
*/
2384+
if (hashvalue == 0)
2385+
hash_seq_init(&status, TypeCacheHash);
2386+
else
2387+
hash_seq_init_with_hash_value(&status, TypeCacheHash, hashvalue);
2388+
23622389
while ((typentry = (TypeCacheEntry *) hash_seq_search(&status)) != NULL)
23632390
{
2364-
/* Is this the targeted type row (or it's a total cache flush)? */
2365-
if (hashvalue == 0 || typentry->type_id_hash == hashvalue)
2366-
{
2367-
/*
2368-
* Mark the data obtained directly from pg_type as invalid. Also,
2369-
* if it's a domain, typnotnull might've changed, so we'll need to
2370-
* recalculate its constraints.
2371-
*/
2372-
typentry->flags &= ~(TCFLAGS_HAVE_PG_TYPE_DATA |
2373-
TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS);
2374-
}
2391+
Assert(hashvalue == 0 || typentry->type_id_hash == hashvalue);
2392+
2393+
/*
2394+
* Mark the data obtained directly from pg_type as invalid. Also, if
2395+
* it's a domain, typnotnull might've changed, so we'll need to
2396+
* recalculate its constraints.
2397+
*/
2398+
typentry->flags &= ~(TCFLAGS_HAVE_PG_TYPE_DATA |
2399+
TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS);
23752400
}
23762401
}
23772402

0 commit comments

Comments
 (0)