Skip to content

Commit 2cc41ac

Browse files
committed
Fix hash index vs "snapshot too old" problemms
Hash indexes are not WAL-logged, and so do not maintain the LSN of index pages. Since the "snapshot too old" feature counts on detecting error conditions using the LSN of a table and all indexes on it, this makes it impossible to safely do early vacuuming on any table with a hash index, so add this to the tests for whether the xid used to vacuum a table can be adjusted based on old_snapshot_threshold. While at it, add a paragraph to the docs for old_snapshot_threshold which specifically mentions this and other aspects of the feature which may otherwise surprise users. Problem reported and patch reviewed by Amit Kapila
1 parent 9b66aa0 commit 2cc41ac

File tree

6 files changed

+62
-6
lines changed

6 files changed

+62
-6
lines changed

doc/src/sgml/config.sgml

+13
Original file line numberDiff line numberDiff line change
@@ -2077,6 +2077,19 @@ include_dir 'conf.d'
20772077
allowed, please note that in many workloads extreme bloat or
20782078
transaction ID wraparound may occur in much shorter time frames.
20792079
</para>
2080+
2081+
<para>
2082+
This setting does not attempt to guarantee that an error will be
2083+
generated under any particular circumstances. In fact, if the
2084+
correct results can be generated from (for example) a cursor which
2085+
has materialized a result set, no error will be generated even if the
2086+
underlying rows in the referenced table have been vacuumed away.
2087+
Some tables cannot safely be vacuumed early, and so will not be
2088+
affected by this setting. Examples include system catalogs and any
2089+
table which has a hash index. For such tables this setting will
2090+
neither reduce bloat nor create a possibility of a <literal>snapshot
2091+
too old</> error on scanning.
2092+
</para>
20802093
</listitem>
20812094
</varlistentry>
20822095
</variablelist>

src/backend/access/hash/hash.c

-1
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,6 @@ hashgettuple(IndexScanDesc scan, ScanDirection dir)
279279
buf = so->hashso_curbuf;
280280
Assert(BufferIsValid(buf));
281281
page = BufferGetPage(buf);
282-
TestForOldSnapshot(scan->xs_snapshot, rel, page);
283282
maxoffnum = PageGetMaxOffsetNumber(page);
284283
for (offnum = ItemPointerGetOffsetNumber(current);
285284
offnum <= maxoffnum;

src/backend/access/hash/hashsearch.c

-4
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,6 @@ _hash_first(IndexScanDesc scan, ScanDirection dir)
189189
/* Read the metapage */
190190
metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ, LH_META_PAGE);
191191
page = BufferGetPage(metabuf);
192-
TestForOldSnapshot(scan->xs_snapshot, rel, page);
193192
metap = HashPageGetMeta(page);
194193

195194
/*
@@ -243,7 +242,6 @@ _hash_first(IndexScanDesc scan, ScanDirection dir)
243242
/* Fetch the primary bucket page for the bucket */
244243
buf = _hash_getbuf(rel, blkno, HASH_READ, LH_BUCKET_PAGE);
245244
page = BufferGetPage(buf);
246-
TestForOldSnapshot(scan->xs_snapshot, rel, page);
247245
opaque = (HashPageOpaque) PageGetSpecialPointer(page);
248246
Assert(opaque->hasho_bucket == bucket);
249247

@@ -350,7 +348,6 @@ _hash_step(IndexScanDesc scan, Buffer *bufP, ScanDirection dir)
350348
_hash_readnext(rel, &buf, &page, &opaque);
351349
if (BufferIsValid(buf))
352350
{
353-
TestForOldSnapshot(scan->xs_snapshot, rel, page);
354351
maxoff = PageGetMaxOffsetNumber(page);
355352
offnum = _hash_binsearch(page, so->hashso_sk_hash);
356353
}
@@ -392,7 +389,6 @@ _hash_step(IndexScanDesc scan, Buffer *bufP, ScanDirection dir)
392389
_hash_readprev(rel, &buf, &page, &opaque);
393390
if (BufferIsValid(buf))
394391
{
395-
TestForOldSnapshot(scan->xs_snapshot, rel, page);
396392
maxoff = PageGetMaxOffsetNumber(page);
397393
offnum = _hash_binsearch_last(page, so->hashso_sk_hash);
398394
}

src/backend/utils/cache/relcache.c

+46
Original file line numberDiff line numberDiff line change
@@ -5312,6 +5312,52 @@ RelationIdIsInInitFile(Oid relationId)
53125312
return RelationSupportsSysCache(relationId);
53135313
}
53145314

5315+
/*
5316+
* Tells whether any index for the relation is unlogged.
5317+
*
5318+
* Any index using the hash AM is implicitly unlogged.
5319+
*
5320+
* Note: There doesn't seem to be any way to have an unlogged index attached
5321+
* to a permanent table except to create a hash index, but it seems best to
5322+
* keep this general so that it returns sensible results even when they seem
5323+
* obvious (like for an unlogged table) and to handle possible future unlogged
5324+
* indexes on permanent tables.
5325+
*/
5326+
bool
5327+
RelationHasUnloggedIndex(Relation rel)
5328+
{
5329+
List *indexoidlist;
5330+
ListCell *indexoidscan;
5331+
bool result = false;
5332+
5333+
indexoidlist = RelationGetIndexList(rel);
5334+
5335+
foreach(indexoidscan, indexoidlist)
5336+
{
5337+
Oid indexoid = lfirst_oid(indexoidscan);
5338+
HeapTuple tp;
5339+
Form_pg_class reltup;
5340+
5341+
tp = SearchSysCache1(RELOID, ObjectIdGetDatum(indexoid));
5342+
if (!HeapTupleIsValid(tp))
5343+
elog(ERROR, "cache lookup failed for relation %u", indexoid);
5344+
reltup = (Form_pg_class) GETSTRUCT(tp);
5345+
5346+
if (reltup->relpersistence == RELPERSISTENCE_UNLOGGED
5347+
|| reltup->relam == HASH_AM_OID)
5348+
result = true;
5349+
5350+
ReleaseSysCache(tp);
5351+
5352+
if (result == true)
5353+
break;
5354+
}
5355+
5356+
list_free(indexoidlist);
5357+
5358+
return result;
5359+
}
5360+
53155361
/*
53165362
* Invalidate (remove) the init file during commit of a transaction that
53175363
* changed one or more of the relation cache entries that are kept in the

src/backend/utils/time/snapmgr.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -1590,7 +1590,8 @@ TransactionIdLimitedForOldSnapshots(TransactionId recentXmin,
15901590
&& old_snapshot_threshold >= 0
15911591
&& RelationNeedsWAL(relation)
15921592
&& !IsCatalogRelation(relation)
1593-
&& !RelationIsAccessibleInLogicalDecoding(relation))
1593+
&& !RelationIsAccessibleInLogicalDecoding(relation)
1594+
&& !RelationHasUnloggedIndex(relation))
15941595
{
15951596
int64 ts = GetSnapshotCurrentTimestamp();
15961597
TransactionId xlimit = recentXmin;

src/include/utils/rel.h

+1
Original file line numberDiff line numberDiff line change
@@ -505,5 +505,6 @@ typedef struct ViewOptions
505505
/* routines in utils/cache/relcache.c */
506506
extern void RelationIncrementReferenceCount(Relation rel);
507507
extern void RelationDecrementReferenceCount(Relation rel);
508+
extern bool RelationHasUnloggedIndex(Relation rel);
508509

509510
#endif /* REL_H */

0 commit comments

Comments
 (0)