Avoid catalog lookups in RelationAllowsEarlyPruning().

RelationAllowsEarlyPruning() performed a catalog scan, but is used
in two contexts where that was a bad idea:

1.  In heap_page_prune_opt(), which runs very frequently in some large
    scans.  This caused major performance problems in a field report
    that was easy to reproduce.

2.  In TestForOldSnapshot(), which runs while we hold a buffer content
    lock.  It's not clear if this was guaranteed to be free of buffer
    deadlock risk.

The check was introduced in commit 2cc41acd8 and defended against a
real problem: 9.6's hash indexes have no page LSN and so we can't
allow early pruning (ie the snapshot-too-old feature).  We can remove
the check from all later releases though: hash indexes are now logged,
and there is no way to create UNLOGGED indexes on regular logged
tables.

If a future release allows such a combination, it might need to put
a similar check in place, but it'll need some more thought.

Back-patch to 10.

Author: Thomas Munro
Reviewed-by: Tom Lane, who spotted the second problem
Discussion: https://postgr.es/m/CA%2BhUKGKT8oTkp5jw_U4p0S-7UG9zsvtw_M47Y285bER6a2gD%2Bg%40mail.gmail.com
Discussion: https://postgr.es/m/CAA4eK1%2BWy%2BN4eE5zPm765h68LrkWc3Biu_8rzzi%2BOYX4j%2BiHRw%40mail.gmail.com
This commit is contained in:
Thomas Munro 2019-08-28 13:37:03 +12:00
parent 80d0e5ba3f
commit 720b59b55b
3 changed files with 0 additions and 44 deletions

View File

@ -5913,48 +5913,6 @@ RelationIdIsInInitFile(Oid relationId)
return RelationSupportsSysCache(relationId);
}
/*
* Tells whether any index for the relation is unlogged.
*
* Note: There doesn't seem to be any way to have an unlogged index attached
* to a permanent table, but it seems best to keep this general so that it
* returns sensible results even when they seem obvious (like for an unlogged
* table) and to handle possible future unlogged indexes on permanent tables.
*/
bool
RelationHasUnloggedIndex(Relation rel)
{
List *indexoidlist;
ListCell *indexoidscan;
bool result = false;
indexoidlist = RelationGetIndexList(rel);
foreach(indexoidscan, indexoidlist)
{
Oid indexoid = lfirst_oid(indexoidscan);
HeapTuple tp;
Form_pg_class reltup;
tp = SearchSysCache1(RELOID, ObjectIdGetDatum(indexoid));
if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for relation %u", indexoid);
reltup = (Form_pg_class) GETSTRUCT(tp);
if (reltup->relpersistence == RELPERSISTENCE_UNLOGGED)
result = true;
ReleaseSysCache(tp);
if (result == true)
break;
}
list_free(indexoidlist);
return result;
}
/*
* Invalidate (remove) the init file during commit of a transaction that
* changed one or more of the relation cache entries that are kept in the

View File

@ -605,6 +605,5 @@ typedef struct ViewOptions
/* routines in utils/cache/relcache.c */
extern void RelationIncrementReferenceCount(Relation rel);
extern void RelationDecrementReferenceCount(Relation rel);
extern bool RelationHasUnloggedIndex(Relation rel);
#endif /* REL_H */

View File

@ -40,7 +40,6 @@
RelationNeedsWAL(rel) \
&& !IsCatalogRelation(rel) \
&& !RelationIsAccessibleInLogicalDecoding(rel) \
&& !RelationHasUnloggedIndex(rel) \
)
#define EarlyPruningEnabled(rel) (old_snapshot_threshold >= 0 && RelationAllowsEarlyPruning(rel))