Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commite384ed6

Browse files
committed
Improve typcache: cache negative lookup results, add invalidation logic.
Previously, if the typcache had for example tried and failed to find a hashopclass for a given data type, it would nonetheless repeat the unsuccessfulcatalog lookup each time it was asked again. This can lead to asignificant amount of useless bufmgr traffic, as in a recent report fromScott Marlowe. Like the catalog caches, typcache should be able to cachenegative results. This patch arranges that by making use of separate flagbits to remember whether a particular item has been looked up, rather thantreating a zero OID as an indicator that no lookup has been done.Also, install a credible invalidation mechanism, namely watching for invalevents in pg_opclass. The sole advantage of the lack of negative cachingwas that the code would cope if operators or opclasses got added for a typemid-session; to preserve that behavior we have to be able to invalidatestale lookup results. Updates in pg_opclass should be pretty rare inproduction systems, so it seems sufficient to just invalidate all thedependent data whenever one happens.Adding proper invalidation also means that this code will now react sanelyif an opclass is dropped mid-session. Arguably, that's a back-patchablebug fix, but in view of the lack of complaints from the field I'll refrainfrom back-patching. (Probably, in most cases where an opclass is dropped,the data type itself is dropped soon after, so that this misfeasance hasno bad consequences.)
1 parent202cbdf commite384ed6

File tree

1 file changed

+127
-63
lines changed

1 file changed

+127
-63
lines changed

‎src/backend/utils/cache/typcache.c

Lines changed: 127 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,16 @@
1717
* is kept in the type cache.
1818
*
1919
* Once created, a type cache entry lives as long as the backend does, so
20-
* there is no need for a call to release a cache entry. (For present uses,
20+
* there is no need for a call to release a cache entry. If the type is
21+
* dropped, the cache entry simply becomes wasted storage. (For present uses,
2122
* it would be okay to flush type cache entries at the ends of transactions,
2223
* if we needed to reclaim space.)
2324
*
24-
* There is presently no provision for clearing out a cache entry if the
25-
* stored data becomes obsolete. (The code will work if a type acquires
26-
* opclasses it didn't have before while a backend runs --- but not if the
27-
* definition of an existing opclass is altered.) However, the relcache
28-
* doesn't cope with opclasses changing under it, either, so this seems
29-
* a low-priority problem.
30-
*
31-
* We do support clearing the tuple descriptor and operator/function parts
32-
* of a rowtype's cache entry, since those may need to change as a consequence
33-
* of ALTER TABLE.
25+
* We have some provisions for updating cache entries if the stored data
26+
* becomes obsolete. Information dependent on opclasses is cleared if we
27+
* detect updates to pg_opclass. We also support clearing the tuple
28+
* descriptor and operator/function parts of a rowtype's cache entry,
29+
* since those may need to change as a consequence of ALTER TABLE.
3430
*
3531
*
3632
* Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
@@ -70,13 +66,20 @@
7066
staticHTAB*TypeCacheHash=NULL;
7167

7268
/* Private flag bits in the TypeCacheEntry.flags field */
73-
#defineTCFLAGS_CHECKED_ELEM_PROPERTIES0x0001
74-
#defineTCFLAGS_HAVE_ELEM_EQUALITY0x0002
75-
#defineTCFLAGS_HAVE_ELEM_COMPARE0x0004
76-
#defineTCFLAGS_HAVE_ELEM_HASHING0x0008
77-
#defineTCFLAGS_CHECKED_FIELD_PROPERTIES0x0010
78-
#defineTCFLAGS_HAVE_FIELD_EQUALITY0x0020
79-
#defineTCFLAGS_HAVE_FIELD_COMPARE0x0040
69+
#defineTCFLAGS_CHECKED_BTREE_OPCLASS0x0001
70+
#defineTCFLAGS_CHECKED_HASH_OPCLASS0x0002
71+
#defineTCFLAGS_CHECKED_EQ_OPR0x0004
72+
#defineTCFLAGS_CHECKED_LT_OPR0x0008
73+
#defineTCFLAGS_CHECKED_GT_OPR0x0010
74+
#defineTCFLAGS_CHECKED_CMP_PROC0x0020
75+
#defineTCFLAGS_CHECKED_HASH_PROC0x0040
76+
#defineTCFLAGS_CHECKED_ELEM_PROPERTIES0x0080
77+
#defineTCFLAGS_HAVE_ELEM_EQUALITY0x0100
78+
#defineTCFLAGS_HAVE_ELEM_COMPARE0x0200
79+
#defineTCFLAGS_HAVE_ELEM_HASHING0x0400
80+
#defineTCFLAGS_CHECKED_FIELD_PROPERTIES0x0800
81+
#defineTCFLAGS_HAVE_FIELD_EQUALITY0x1000
82+
#defineTCFLAGS_HAVE_FIELD_COMPARE0x2000
8083

8184
/* Private information to support comparisons of enum values */
8285
typedefstruct
@@ -132,6 +135,7 @@ static bool record_fields_have_equality(TypeCacheEntry *typentry);
132135
staticboolrecord_fields_have_compare(TypeCacheEntry*typentry);
133136
staticvoidcache_record_field_properties(TypeCacheEntry*typentry);
134137
staticvoidTypeCacheRelCallback(Datumarg,Oidrelid);
138+
staticvoidTypeCacheOpcCallback(Datumarg,intcacheid,uint32hashvalue);
135139
staticvoidload_enum_cache_data(TypeCacheEntry*tcache);
136140
staticEnumItem*find_enumitem(TypeCacheEnumData*enumdata,Oidarg);
137141
staticintenum_oid_cmp(constvoid*left,constvoid*right);
@@ -166,8 +170,9 @@ lookup_type_cache(Oid type_id, int flags)
166170
TypeCacheHash=hash_create("Type information cache",64,
167171
&ctl,HASH_ELEM |HASH_FUNCTION);
168172

169-
/* Also set upa callbackfor relcache SI invalidations */
173+
/* Also set upcallbacksfor SI invalidations */
170174
CacheRegisterRelcacheCallback(TypeCacheRelCallback, (Datum)0);
175+
CacheRegisterSyscacheCallback(CLAOID,TypeCacheOpcCallback, (Datum)0);
171176

172177
/* Also make sure CacheMemoryContext exists */
173178
if (!CacheMemoryContext)
@@ -217,13 +222,14 @@ lookup_type_cache(Oid type_id, int flags)
217222
}
218223

219224
/*
220-
* If we haven't already found the opclasses, try to do so
225+
* Look up opclasses if we haven't already and any dependent info is
226+
* requested.
221227
*/
222228
if ((flags& (TYPECACHE_EQ_OPR |TYPECACHE_LT_OPR |TYPECACHE_GT_OPR |
223229
TYPECACHE_CMP_PROC |
224230
TYPECACHE_EQ_OPR_FINFO |TYPECACHE_CMP_PROC_FINFO |
225231
TYPECACHE_BTREE_OPFAMILY))&&
226-
typentry->btree_opf==InvalidOid)
232+
!(typentry->flags&TCFLAGS_CHECKED_BTREE_OPCLASS))
227233
{
228234
Oidopclass;
229235

@@ -233,38 +239,36 @@ lookup_type_cache(Oid type_id, int flags)
233239
typentry->btree_opf=get_opclass_family(opclass);
234240
typentry->btree_opintype=get_opclass_input_type(opclass);
235241
}
236-
/* If no btree opclass, we force lookup of the hash opclass */
237-
if (typentry->btree_opf==InvalidOid)
238-
{
239-
if (typentry->hash_opf==InvalidOid)
240-
{
241-
opclass=GetDefaultOpClass(type_id,HASH_AM_OID);
242-
if (OidIsValid(opclass))
243-
{
244-
typentry->hash_opf=get_opclass_family(opclass);
245-
typentry->hash_opintype=get_opclass_input_type(opclass);
246-
}
247-
}
248-
}
249242
else
250243
{
251-
/*
252-
* In case we find a btree opclass where previously we only found
253-
* a hash opclass, reset eq_opr and derived information so that we
254-
* can fetch the btree equality operator instead of the hash
255-
* equality operator. (They're probably the same operator, but we
256-
* don't assume that here.)
257-
*/
258-
typentry->eq_opr=InvalidOid;
259-
typentry->eq_opr_finfo.fn_oid=InvalidOid;
260-
typentry->hash_proc=InvalidOid;
261-
typentry->hash_proc_finfo.fn_oid=InvalidOid;
244+
typentry->btree_opf=typentry->btree_opintype=InvalidOid;
262245
}
246+
247+
/*
248+
* Reset information derived from btree opclass. Note in particular
249+
* that we'll redetermine the eq_opr even if we previously found one;
250+
* this matters in case a btree opclass has been added to a type that
251+
* previously had only a hash opclass.
252+
*/
253+
typentry->flags &= ~(TCFLAGS_CHECKED_EQ_OPR |
254+
TCFLAGS_CHECKED_LT_OPR |
255+
TCFLAGS_CHECKED_GT_OPR |
256+
TCFLAGS_CHECKED_CMP_PROC);
257+
typentry->flags |=TCFLAGS_CHECKED_BTREE_OPCLASS;
263258
}
264259

260+
/*
261+
* If we need to look up equality operator, and there's no btree opclass,
262+
* force lookup of hash opclass.
263+
*/
264+
if ((flags& (TYPECACHE_EQ_OPR |TYPECACHE_EQ_OPR_FINFO))&&
265+
!(typentry->flags&TCFLAGS_CHECKED_EQ_OPR)&&
266+
typentry->btree_opf==InvalidOid)
267+
flags |=TYPECACHE_HASH_OPFAMILY;
268+
265269
if ((flags& (TYPECACHE_HASH_PROC |TYPECACHE_HASH_PROC_FINFO |
266270
TYPECACHE_HASH_OPFAMILY))&&
267-
typentry->hash_opf==InvalidOid)
271+
!(typentry->flags&TCFLAGS_CHECKED_HASH_OPCLASS))
268272
{
269273
Oidopclass;
270274

@@ -274,11 +278,25 @@ lookup_type_cache(Oid type_id, int flags)
274278
typentry->hash_opf=get_opclass_family(opclass);
275279
typentry->hash_opintype=get_opclass_input_type(opclass);
276280
}
281+
else
282+
{
283+
typentry->hash_opf=typentry->hash_opintype=InvalidOid;
284+
}
285+
286+
/*
287+
* Reset information derived from hash opclass. We do *not* reset the
288+
* eq_opr; if we already found one from the btree opclass, that
289+
* decision is still good.
290+
*/
291+
typentry->flags &= ~(TCFLAGS_CHECKED_HASH_PROC);
292+
typentry->flags |=TCFLAGS_CHECKED_HASH_OPCLASS;
277293
}
278294

279-
/* Look for requested operators and functions */
295+
/*
296+
* Look for requested operators and functions, if we haven't already.
297+
*/
280298
if ((flags& (TYPECACHE_EQ_OPR |TYPECACHE_EQ_OPR_FINFO))&&
281-
typentry->eq_opr==InvalidOid)
299+
!(typentry->flags&TCFLAGS_CHECKED_EQ_OPR))
282300
{
283301
Oideq_opr=InvalidOid;
284302

@@ -307,17 +325,22 @@ lookup_type_cache(Oid type_id, int flags)
307325
!record_fields_have_equality(typentry))
308326
eq_opr=InvalidOid;
309327

328+
/* Force update of eq_opr_finfo only if we're changing state */
329+
if (typentry->eq_opr!=eq_opr)
330+
typentry->eq_opr_finfo.fn_oid=InvalidOid;
331+
310332
typentry->eq_opr=eq_opr;
311333

312334
/*
313335
* Reset info about hash function whenever we pick up new info about
314336
* equality operator. This is so we can ensure that the hash function
315337
* matches the operator.
316338
*/
317-
typentry->hash_proc=InvalidOid;
318-
typentry->hash_proc_finfo.fn_oid=InvalidOid;
339+
typentry->flags &= ~(TCFLAGS_CHECKED_HASH_PROC);
340+
typentry->flags |=TCFLAGS_CHECKED_EQ_OPR;
319341
}
320-
if ((flags&TYPECACHE_LT_OPR)&&typentry->lt_opr==InvalidOid)
342+
if ((flags&TYPECACHE_LT_OPR)&&
343+
!(typentry->flags&TCFLAGS_CHECKED_LT_OPR))
321344
{
322345
Oidlt_opr=InvalidOid;
323346

@@ -336,8 +359,10 @@ lookup_type_cache(Oid type_id, int flags)
336359
lt_opr=InvalidOid;
337360

338361
typentry->lt_opr=lt_opr;
362+
typentry->flags |=TCFLAGS_CHECKED_LT_OPR;
339363
}
340-
if ((flags&TYPECACHE_GT_OPR)&&typentry->gt_opr==InvalidOid)
364+
if ((flags&TYPECACHE_GT_OPR)&&
365+
!(typentry->flags&TCFLAGS_CHECKED_GT_OPR))
341366
{
342367
Oidgt_opr=InvalidOid;
343368

@@ -356,9 +381,10 @@ lookup_type_cache(Oid type_id, int flags)
356381
gt_opr=InvalidOid;
357382

358383
typentry->gt_opr=gt_opr;
384+
typentry->flags |=TCFLAGS_CHECKED_GT_OPR;
359385
}
360386
if ((flags& (TYPECACHE_CMP_PROC |TYPECACHE_CMP_PROC_FINFO))&&
361-
typentry->cmp_proc==InvalidOid)
387+
!(typentry->flags&TCFLAGS_CHECKED_CMP_PROC))
362388
{
363389
Oidcmp_proc=InvalidOid;
364390

@@ -376,10 +402,15 @@ lookup_type_cache(Oid type_id, int flags)
376402
!record_fields_have_compare(typentry))
377403
cmp_proc=InvalidOid;
378404

405+
/* Force update of cmp_proc_finfo only if we're changing state */
406+
if (typentry->cmp_proc!=cmp_proc)
407+
typentry->cmp_proc_finfo.fn_oid=InvalidOid;
408+
379409
typentry->cmp_proc=cmp_proc;
410+
typentry->flags |=TCFLAGS_CHECKED_CMP_PROC;
380411
}
381412
if ((flags& (TYPECACHE_HASH_PROC |TYPECACHE_HASH_PROC_FINFO))&&
382-
typentry->hash_proc==InvalidOid)
413+
!(typentry->flags&TCFLAGS_CHECKED_HASH_PROC))
383414
{
384415
Oidhash_proc=InvalidOid;
385416

@@ -407,7 +438,12 @@ lookup_type_cache(Oid type_id, int flags)
407438
!array_element_has_hashing(typentry))
408439
hash_proc=InvalidOid;
409440

441+
/* Force update of hash_proc_finfo only if we're changing state */
442+
if (typentry->hash_proc!=hash_proc)
443+
typentry->hash_proc_finfo.fn_oid=InvalidOid;
444+
410445
typentry->hash_proc=hash_proc;
446+
typentry->flags |=TCFLAGS_CHECKED_HASH_PROC;
411447
}
412448

413449
/*
@@ -416,6 +452,11 @@ lookup_type_cache(Oid type_id, int flags)
416452
* Note: we tell fmgr the finfo structures live in CacheMemoryContext,
417453
* which is not quite right (they're really in the hash table's private
418454
* memory context) but this will do for our purposes.
455+
*
456+
* Note: the code above avoids invalidating the finfo structs unless the
457+
* referenced operator/function OID actually changes. This is to prevent
458+
* unnecessary leakage of any subsidiary data attached to an finfo, since
459+
* that would cause session-lifespan memory leaks.
419460
*/
420461
if ((flags&TYPECACHE_EQ_OPR_FINFO)&&
421462
typentry->eq_opr_finfo.fn_oid==InvalidOid&&
@@ -928,15 +969,38 @@ TypeCacheRelCallback(Datum arg, Oid relid)
928969
typentry->tupDesc=NULL;
929970
}
930971

931-
/* Reset equality/comparison/hashing information */
932-
typentry->eq_opr=InvalidOid;
933-
typentry->lt_opr=InvalidOid;
934-
typentry->gt_opr=InvalidOid;
935-
typentry->cmp_proc=InvalidOid;
936-
typentry->hash_proc=InvalidOid;
937-
typentry->eq_opr_finfo.fn_oid=InvalidOid;
938-
typentry->cmp_proc_finfo.fn_oid=InvalidOid;
939-
typentry->hash_proc_finfo.fn_oid=InvalidOid;
972+
/* Reset equality/comparison/hashing validity information */
973+
typentry->flags=0;
974+
}
975+
}
976+
977+
/*
978+
* TypeCacheOpcCallback
979+
*Syscache inval callback function
980+
*
981+
* This is called when a syscache invalidation event occurs for any pg_opclass
982+
* row. In principle we could probably just invalidate data dependent on the
983+
* particular opclass, but since updates on pg_opclass are rare in production
984+
* it doesn't seem worth a lot of complication: we just mark all cached data
985+
* invalid.
986+
*
987+
* Note that we don't bother watching for updates on pg_amop or pg_amproc.
988+
* This should be safe because ALTER OPERATOR FAMILY ADD/DROP OPERATOR/FUNCTION
989+
* is not allowed to be used to add/drop the primary operators and functions
990+
* of an opclass, only cross-type members of a family; and the latter sorts
991+
* of members are not going to get cached here.
992+
*/
993+
staticvoid
994+
TypeCacheOpcCallback(Datumarg,intcacheid,uint32hashvalue)
995+
{
996+
HASH_SEQ_STATUSstatus;
997+
TypeCacheEntry*typentry;
998+
999+
/* TypeCacheHash must exist, else this callback wouldn't be registered */
1000+
hash_seq_init(&status,TypeCacheHash);
1001+
while ((typentry= (TypeCacheEntry*)hash_seq_search(&status))!=NULL)
1002+
{
1003+
/* Reset equality/comparison/hashing validity information */
9401004
typentry->flags=0;
9411005
}
9421006
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp