Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 3e2f3c2

Browse files
committed
Prevent "snapshot too old" from trying to return pruned TOAST tuples.
Previously, we tested for MVCC snapshots to see whether they were too old, but not TOAST snapshots, which can lead to complaints about missing TOAST chunks if those chunks are subject to early pruning. Ideally, the threshold lsn and timestamp for a TOAST snapshot would be that of the corresponding MVCC snapshot, but since we have no way of deciding which MVCC snapshot was used to fetch the TOAST pointer, use the oldest active or registered snapshot instead. Reported by Andres Freund, who also sketched out what the fix should look like. Patch by me, reviewed by Amit Kapila.
1 parent a3c7a99 commit 3e2f3c2

File tree

6 files changed

+96
-16
lines changed

6 files changed

+96
-16
lines changed

src/backend/access/heap/tuptoaster.c

+33-4
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "utils/expandeddatum.h"
4141
#include "utils/fmgroids.h"
4242
#include "utils/rel.h"
43+
#include "utils/snapmgr.h"
4344
#include "utils/typcache.h"
4445
#include "utils/tqual.h"
4546

@@ -81,6 +82,7 @@ static int toast_open_indexes(Relation toastrel,
8182
int *num_indexes);
8283
static void toast_close_indexes(Relation *toastidxs, int num_indexes,
8384
LOCKMODE lock);
85+
static void init_toast_snapshot(Snapshot toast_snapshot);
8486

8587

8688
/* ----------
@@ -1665,6 +1667,7 @@ toast_delete_datum(Relation rel, Datum value)
16651667
HeapTuple toasttup;
16661668
int num_indexes;
16671669
int validIndex;
1670+
SnapshotData SnapshotToast;
16681671

16691672
if (!VARATT_IS_EXTERNAL_ONDISK(attr))
16701673
return;
@@ -1696,8 +1699,9 @@ toast_delete_datum(Relation rel, Datum value)
16961699
* sequence or not, but since we've already locked the index we might as
16971700
* well use systable_beginscan_ordered.)
16981701
*/
1702+
init_toast_snapshot(&SnapshotToast);
16991703
toastscan = systable_beginscan_ordered(toastrel, toastidxs[validIndex],
1700-
SnapshotToast, 1, &toastkey);
1704+
&SnapshotToast, 1, &toastkey);
17011705
while ((toasttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL)
17021706
{
17031707
/*
@@ -1730,6 +1734,7 @@ toastrel_valueid_exists(Relation toastrel, Oid valueid)
17301734
int num_indexes;
17311735
int validIndex;
17321736
Relation *toastidxs;
1737+
SnapshotData SnapshotToast;
17331738

17341739
/* Fetch a valid index relation */
17351740
validIndex = toast_open_indexes(toastrel,
@@ -1748,9 +1753,10 @@ toastrel_valueid_exists(Relation toastrel, Oid valueid)
17481753
/*
17491754
* Is there any such chunk?
17501755
*/
1756+
init_toast_snapshot(&SnapshotToast);
17511757
toastscan = systable_beginscan(toastrel,
17521758
RelationGetRelid(toastidxs[validIndex]),
1753-
true, SnapshotToast, 1, &toastkey);
1759+
true, &SnapshotToast, 1, &toastkey);
17541760

17551761
if (systable_getnext(toastscan) != NULL)
17561762
result = true;
@@ -1813,6 +1819,7 @@ toast_fetch_datum(struct varlena * attr)
18131819
int32 chunksize;
18141820
int num_indexes;
18151821
int validIndex;
1822+
SnapshotData SnapshotToast;
18161823

18171824
if (!VARATT_IS_EXTERNAL_ONDISK(attr))
18181825
elog(ERROR, "toast_fetch_datum shouldn't be called for non-ondisk datums");
@@ -1859,8 +1866,9 @@ toast_fetch_datum(struct varlena * attr)
18591866
*/
18601867
nextidx = 0;
18611868

1869+
init_toast_snapshot(&SnapshotToast);
18621870
toastscan = systable_beginscan_ordered(toastrel, toastidxs[validIndex],
1863-
SnapshotToast, 1, &toastkey);
1871+
&SnapshotToast, 1, &toastkey);
18641872
while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL)
18651873
{
18661874
/*
@@ -1990,6 +1998,7 @@ toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length)
19901998
int32 chcpyend;
19911999
int num_indexes;
19922000
int validIndex;
2001+
SnapshotData SnapshotToast;
19932002

19942003
if (!VARATT_IS_EXTERNAL_ONDISK(attr))
19952004
elog(ERROR, "toast_fetch_datum_slice shouldn't be called for non-ondisk datums");
@@ -2082,9 +2091,10 @@ toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length)
20822091
*
20832092
* The index is on (valueid, chunkidx) so they will come in order
20842093
*/
2094+
init_toast_snapshot(&SnapshotToast);
20852095
nextidx = startchunk;
20862096
toastscan = systable_beginscan_ordered(toastrel, toastidxs[validIndex],
2087-
SnapshotToast, nscankeys, toastkey);
2097+
&SnapshotToast, nscankeys, toastkey);
20882098
while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL)
20892099
{
20902100
/*
@@ -2289,3 +2299,22 @@ toast_close_indexes(Relation *toastidxs, int num_indexes, LOCKMODE lock)
22892299
index_close(toastidxs[i], lock);
22902300
pfree(toastidxs);
22912301
}
2302+
2303+
/* ----------
2304+
* init_toast_snapshot
2305+
*
2306+
* Initialize an appropriate TOAST snapshot. We must use an MVCC snapshot
2307+
* to initialize the TOAST snapshot; since we don't know which one to use,
2308+
* just use the oldest one. This is safe: at worst, we will get a "snapshot
2309+
* too old" error that might have been avoided otherwise.
2310+
*/
2311+
static void
2312+
init_toast_snapshot(Snapshot toast_snapshot)
2313+
{
2314+
Snapshot snapshot = GetOldestSnapshot();
2315+
2316+
if (snapshot == NULL)
2317+
elog(ERROR, "no known snapshots");
2318+
2319+
InitToastSnapshot(toast_snapshot, snapshot->lsn, snapshot->whenTaken);
2320+
}

src/backend/utils/time/snapmgr.c

+38
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,9 @@ typedef struct ActiveSnapshotElt
188188
/* Top of the stack of active snapshots */
189189
static ActiveSnapshotElt *ActiveSnapshot = NULL;
190190

191+
/* Bottom of the stack of active snapshots */
192+
static ActiveSnapshotElt *OldestActiveSnapshot = NULL;
193+
191194
/*
192195
* Currently registered Snapshots. Ordered in a heap by xmin, so that we can
193196
* quickly find the one with lowest xmin, to advance our MyPgXat->xmin.
@@ -393,6 +396,34 @@ GetLatestSnapshot(void)
393396
return SecondarySnapshot;
394397
}
395398

399+
/*
400+
* GetOldestSnapshot
401+
*
402+
* Get the oldest known snapshot, as judged by the LSN.
403+
*/
404+
Snapshot
405+
GetOldestSnapshot(void)
406+
{
407+
Snapshot OldestRegisteredSnapshot = NULL;
408+
XLogRecPtr RegisteredLSN = InvalidXLogRecPtr;
409+
XLogRecPtr ActiveLSN = InvalidXLogRecPtr;
410+
411+
if (!pairingheap_is_empty(&RegisteredSnapshots))
412+
{
413+
OldestRegisteredSnapshot = pairingheap_container(SnapshotData, ph_node,
414+
pairingheap_first(&RegisteredSnapshots));
415+
RegisteredLSN = OldestRegisteredSnapshot->lsn;
416+
}
417+
418+
if (OldestActiveSnapshot != NULL)
419+
ActiveLSN = OldestActiveSnapshot->as_snap->lsn;
420+
421+
if (XLogRecPtrIsInvalid(RegisteredLSN) || RegisteredLSN > ActiveLSN)
422+
return OldestActiveSnapshot->as_snap;
423+
424+
return OldestRegisteredSnapshot;
425+
}
426+
396427
/*
397428
* GetCatalogSnapshot
398429
* Get a snapshot that is sufficiently up-to-date for scan of the
@@ -674,6 +705,8 @@ PushActiveSnapshot(Snapshot snap)
674705
newactive->as_snap->active_count++;
675706

676707
ActiveSnapshot = newactive;
708+
if (OldestActiveSnapshot == NULL)
709+
OldestActiveSnapshot = ActiveSnapshot;
677710
}
678711

679712
/*
@@ -744,6 +777,8 @@ PopActiveSnapshot(void)
744777

745778
pfree(ActiveSnapshot);
746779
ActiveSnapshot = newstack;
780+
if (ActiveSnapshot == NULL)
781+
OldestActiveSnapshot = NULL;
747782

748783
SnapshotResetXmin();
749784
}
@@ -953,6 +988,8 @@ AtSubAbort_Snapshot(int level)
953988
pfree(ActiveSnapshot);
954989

955990
ActiveSnapshot = next;
991+
if (ActiveSnapshot == NULL)
992+
OldestActiveSnapshot = NULL;
956993
}
957994

958995
SnapshotResetXmin();
@@ -1037,6 +1074,7 @@ AtEOXact_Snapshot(bool isCommit)
10371074
* it'll go away with TopTransactionContext.
10381075
*/
10391076
ActiveSnapshot = NULL;
1077+
OldestActiveSnapshot = NULL;
10401078
pairingheap_reset(&RegisteredSnapshots);
10411079

10421080
CurrentSnapshot = NULL;

src/backend/utils/time/tqual.c

-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@
7878
/* Static variables representing various special snapshot semantics */
7979
SnapshotData SnapshotSelfData = {HeapTupleSatisfiesSelf};
8080
SnapshotData SnapshotAnyData = {HeapTupleSatisfiesAny};
81-
SnapshotData SnapshotToastData = {HeapTupleSatisfiesToast};
8281

8382
/* local functions */
8483
static bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);

src/include/storage/bufmgr.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,8 @@ TestForOldSnapshot(Snapshot snapshot, Relation relation, Page page)
279279

280280
if (old_snapshot_threshold >= 0
281281
&& (snapshot) != NULL
282-
&& (snapshot)->satisfies == HeapTupleSatisfiesMVCC
282+
&& ((snapshot)->satisfies == HeapTupleSatisfiesMVCC
283+
|| (snapshot)->satisfies == HeapTupleSatisfiesToast)
283284
&& !XLogRecPtrIsInvalid((snapshot)->lsn)
284285
&& PageGetLSN(page) > (snapshot)->lsn)
285286
TestForOldSnapshot_impl(snapshot, relation);

src/include/utils/snapmgr.h

+1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ extern TransactionId RecentGlobalDataXmin;
6464
extern Snapshot GetTransactionSnapshot(void);
6565
extern Snapshot GetLatestSnapshot(void);
6666
extern void SnapshotSetCommandId(CommandId curcid);
67+
extern Snapshot GetOldestSnapshot(void);
6768

6869
extern Snapshot GetCatalogSnapshot(Oid relid);
6970
extern Snapshot GetNonHistoricCatalogSnapshot(Oid relid);

src/include/utils/tqual.h

+22-10
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,16 @@
1616
#define TQUAL_H
1717

1818
#include "utils/snapshot.h"
19+
#include "access/xlogdefs.h"
1920

2021

2122
/* Static variables representing various special snapshot semantics */
2223
extern PGDLLIMPORT SnapshotData SnapshotSelfData;
2324
extern PGDLLIMPORT SnapshotData SnapshotAnyData;
24-
extern PGDLLIMPORT SnapshotData SnapshotToastData;
2525
extern PGDLLIMPORT SnapshotData CatalogSnapshotData;
2626

2727
#define SnapshotSelf (&SnapshotSelfData)
2828
#define SnapshotAny (&SnapshotAnyData)
29-
#define SnapshotToast (&SnapshotToastData)
30-
31-
/*
32-
* We don't provide a static SnapshotDirty variable because it would be
33-
* non-reentrant. Instead, users of that snapshot type should declare a
34-
* local variable of type SnapshotData, and initialize it with this macro.
35-
*/
36-
#define InitDirtySnapshot(snapshotdata) \
37-
((snapshotdata).satisfies = HeapTupleSatisfiesDirty)
3829

3930
/* This macro encodes the knowledge of which snapshots are MVCC-safe */
4031
#define IsMVCCSnapshot(snapshot) \
@@ -100,4 +91,25 @@ extern bool ResolveCminCmaxDuringDecoding(struct HTAB *tuplecid_data,
10091
HeapTuple htup,
10192
Buffer buffer,
10293
CommandId *cmin, CommandId *cmax);
94+
95+
/*
96+
* We don't provide a static SnapshotDirty variable because it would be
97+
* non-reentrant. Instead, users of that snapshot type should declare a
98+
* local variable of type SnapshotData, and initialize it with this macro.
99+
*/
100+
#define InitDirtySnapshot(snapshotdata) \
101+
((snapshotdata).satisfies = HeapTupleSatisfiesDirty)
102+
103+
/*
104+
* Similarly, some initialization is required for SnapshotToast. We need
105+
* to set lsn and whenTaken correctly to support snapshot_too_old.
106+
*/
107+
static inline void
108+
InitToastSnapshot(Snapshot snapshot, XLogRecPtr lsn, int64 whenTaken)
109+
{
110+
snapshot->satisfies = HeapTupleSatisfiesToast;
111+
snapshot->lsn = lsn;
112+
snapshot->whenTaken = whenTaken;
113+
}
114+
103115
#endif /* TQUAL_H */

0 commit comments

Comments
 (0)