mirror of
https://github.com/postgres/postgres.git
synced 2025-04-24 10:47:04 +03:00
Up to now async.c has used TransactionIdIsInProgress() to detect whether a notify message's source transaction is still running. However, that function has a quick-exit path that reports that XIDs before RecentXmin are no longer running. If a listening backend is doing nothing but listening, and not running any queries, there is nothing that will advance its value of RecentXmin. Once 2 billion transactions elapse, the RecentXmin check causes active transactions to be reported as not running. If they aren't committed yet according to CLOG, async.c decides they aborted and discards their messages. The timing for that is a bit tight but it can happen when multiple backends are sending notifies concurrently. The net symptom therefore is that a sufficiently-long-surviving listen-only backend starts to miss some fraction of NOTIFY traffic, but only under heavy load. The only function that updates RecentXmin is GetSnapshotData(). A brute-force fix would therefore be to take a snapshot before processing incoming notify messages. But that would add cycles, as well as contention for the ProcArrayLock. We can be smarter: having taken the snapshot, let's use that to check for running XIDs, and not call TransactionIdIsInProgress() at all. In this way we reduce the number of ProcArrayLock acquisitions from one per message to one per notify interrupt; that's the same under light load but should be a benefit under heavy load. Light testing says that this change is a wash performance-wise for normal loads. I looked around for other callers of TransactionIdIsInProgress() that might be at similar risk, and didn't find any; all of them are inside transactions that presumably have already taken a snapshot. Problem report and diagnosis by Marko Tiikkaja, patch by me. Back-patch to all supported branches, since it's been like this since 9.0. Discussion: https://postgr.es/m/20170926182935.14128.65278@wrigleys.postgresql.org
124 lines
4.5 KiB
C
124 lines
4.5 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* tqual.h
|
|
* POSTGRES "time qualification" definitions, ie, tuple visibility rules.
|
|
*
|
|
* Should be moved/renamed... - vadim 07/28/98
|
|
*
|
|
* Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
* src/include/utils/tqual.h
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#ifndef TQUAL_H
|
|
#define TQUAL_H
|
|
|
|
#include "utils/snapshot.h"
|
|
#include "access/xlogdefs.h"
|
|
|
|
|
|
/* Static variables representing various special snapshot semantics */
|
|
extern PGDLLIMPORT SnapshotData SnapshotSelfData;
|
|
extern PGDLLIMPORT SnapshotData SnapshotAnyData;
|
|
extern PGDLLIMPORT SnapshotData CatalogSnapshotData;
|
|
|
|
#define SnapshotSelf (&SnapshotSelfData)
|
|
#define SnapshotAny (&SnapshotAnyData)
|
|
|
|
/* This macro encodes the knowledge of which snapshots are MVCC-safe */
|
|
#define IsMVCCSnapshot(snapshot) \
|
|
((snapshot)->satisfies == HeapTupleSatisfiesMVCC || \
|
|
(snapshot)->satisfies == HeapTupleSatisfiesHistoricMVCC)
|
|
|
|
/*
|
|
* HeapTupleSatisfiesVisibility
|
|
* True iff heap tuple satisfies a time qual.
|
|
*
|
|
* Notes:
|
|
* Assumes heap tuple is valid.
|
|
* Beware of multiple evaluations of snapshot argument.
|
|
* Hint bits in the HeapTuple's t_infomask may be updated as a side effect;
|
|
* if so, the indicated buffer is marked dirty.
|
|
*/
|
|
#define HeapTupleSatisfiesVisibility(tuple, snapshot, buffer) \
|
|
((*(snapshot)->satisfies) (tuple, snapshot, buffer))
|
|
|
|
/* Result codes for HeapTupleSatisfiesVacuum */
|
|
typedef enum
|
|
{
|
|
HEAPTUPLE_DEAD, /* tuple is dead and deletable */
|
|
HEAPTUPLE_LIVE, /* tuple is live (committed, no deleter) */
|
|
HEAPTUPLE_RECENTLY_DEAD, /* tuple is dead, but not deletable yet */
|
|
HEAPTUPLE_INSERT_IN_PROGRESS, /* inserting xact is still in progress */
|
|
HEAPTUPLE_DELETE_IN_PROGRESS /* deleting xact is still in progress */
|
|
} HTSV_Result;
|
|
|
|
/* These are the "satisfies" test routines for the various snapshot types */
|
|
extern bool HeapTupleSatisfiesMVCC(HeapTuple htup,
|
|
Snapshot snapshot, Buffer buffer);
|
|
extern bool HeapTupleSatisfiesSelf(HeapTuple htup,
|
|
Snapshot snapshot, Buffer buffer);
|
|
extern bool HeapTupleSatisfiesAny(HeapTuple htup,
|
|
Snapshot snapshot, Buffer buffer);
|
|
extern bool HeapTupleSatisfiesToast(HeapTuple htup,
|
|
Snapshot snapshot, Buffer buffer);
|
|
extern bool HeapTupleSatisfiesDirty(HeapTuple htup,
|
|
Snapshot snapshot, Buffer buffer);
|
|
extern bool HeapTupleSatisfiesNonVacuumable(HeapTuple htup,
|
|
Snapshot snapshot, Buffer buffer);
|
|
extern bool HeapTupleSatisfiesHistoricMVCC(HeapTuple htup,
|
|
Snapshot snapshot, Buffer buffer);
|
|
|
|
/* Special "satisfies" routines with different APIs */
|
|
extern HTSU_Result HeapTupleSatisfiesUpdate(HeapTuple htup,
|
|
CommandId curcid, Buffer buffer);
|
|
extern HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup,
|
|
TransactionId OldestXmin, Buffer buffer);
|
|
extern bool HeapTupleIsSurelyDead(HeapTuple htup,
|
|
TransactionId OldestXmin);
|
|
extern bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);
|
|
|
|
extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
|
|
uint16 infomask, TransactionId xid);
|
|
extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple);
|
|
|
|
/*
|
|
* To avoid leaking too much knowledge about reorderbuffer implementation
|
|
* details this is implemented in reorderbuffer.c not tqual.c.
|
|
*/
|
|
struct HTAB;
|
|
extern bool ResolveCminCmaxDuringDecoding(struct HTAB *tuplecid_data,
|
|
Snapshot snapshot,
|
|
HeapTuple htup,
|
|
Buffer buffer,
|
|
CommandId *cmin, CommandId *cmax);
|
|
|
|
/*
|
|
* We don't provide a static SnapshotDirty variable because it would be
|
|
* non-reentrant. Instead, users of that snapshot type should declare a
|
|
* local variable of type SnapshotData, and initialize it with this macro.
|
|
*/
|
|
#define InitDirtySnapshot(snapshotdata) \
|
|
((snapshotdata).satisfies = HeapTupleSatisfiesDirty)
|
|
|
|
/*
|
|
* Similarly, some initialization is required for a NonVacuumable snapshot.
|
|
* The caller must supply the xmin horizon to use (e.g., RecentGlobalXmin).
|
|
*/
|
|
#define InitNonVacuumableSnapshot(snapshotdata, xmin_horizon) \
|
|
((snapshotdata).satisfies = HeapTupleSatisfiesNonVacuumable, \
|
|
(snapshotdata).xmin = (xmin_horizon))
|
|
|
|
/*
|
|
* Similarly, some initialization is required for SnapshotToast. We need
|
|
* to set lsn and whenTaken correctly to support snapshot_too_old.
|
|
*/
|
|
#define InitToastSnapshot(snapshotdata, l, w) \
|
|
((snapshotdata).satisfies = HeapTupleSatisfiesToast, \
|
|
(snapshotdata).lsn = (l), \
|
|
(snapshotdata).whenTaken = (w))
|
|
|
|
#endif /* TQUAL_H */
|