Skip to content

Commit e77bc80

Browse files
anarazelCommitfest Bot
authored and
Commitfest Bot
committed
heapam: Acquire right to set hint bits
This commit starts to use BufferPrepareToSetHintBits(), introduced in a previous commit, for the reasons explained in said commit. To amortize the cost of BufferPrepareToSetHintBits() for cases where hint bits are set at a high frequency, HeapTupleSatisfiesMVCCBatch() uses the new SetHintBitsExt() which defers BufferFinishSetHintBits() until all hint bits on a page have been set. It's likely worth introducing additional batch visibility routines, e.g. for vacuuming, but I did not find a regression with the state as of this commit. So that's left for later. Author: Reviewed-by: Discussion: https://postgr.es/m/ Backpatch:
1 parent 166af55 commit e77bc80

File tree

2 files changed

+97
-18
lines changed

2 files changed

+97
-18
lines changed

src/backend/access/heap/heapam_visibility.c

+96-18
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,35 @@
8080

8181

8282
/*
83-
* SetHintBits()
83+
* To be allowed to set hint bits SetHintBits() needs to call
84+
* BufferPrepareToSetHintBits(). However, that's not free, and some callsites
85+
* call SetHintBits() on numerous tuples in a row. For those it makes sense to
86+
* amortize the cost of BufferPrepareToSetHintBits(). Additionally it's
87+
* desirable to defer the cost of BufferPrepareToSetHintBits() until a hint
88+
* bit needs to actually be set. This enum serves as the necessary state space
89+
* passed to SetHintbitsExt().
90+
*/
91+
typedef enum SetHintBitsState
92+
{
93+
/* not yet checked if hint bits may be set */
94+
SHB_INITIAL,
95+
/* failed to get permission to set hint bits, don't check again */
96+
SHB_DISABLED,
97+
/* allowed to set hint bits */
98+
SHB_ENABLED,
99+
} SetHintBitsState;
100+
101+
/*
102+
* SetHintBitsExt()
84103
*
85104
* Set commit/abort hint bits on a tuple, if appropriate at this time.
86105
*
106+
* To be allowed to set a hint bit on a tuple, the page must not be undergoing
107+
* IO at this time (otherwise we e.g. could corrupt PG's page checksum or even
108+
* the filesystem's, as is known to happen with btrfs). The right to set a
109+
* hint bit is acquired on a page level with BufferPrepareToSetHintBits().
110+
* Only a single backend gets the right to set hint bits at a time.
111+
*
87112
* It is only safe to set a transaction-committed hint bit if we know the
88113
* transaction's commit record is guaranteed to be flushed to disk before the
89114
* buffer, or if the table is temporary or unlogged and will be obliterated by
@@ -111,9 +136,16 @@
111136
* InvalidTransactionId if no check is needed.
112137
*/
113138
static inline void
114-
SetHintBits(HeapTupleHeader tuple, Buffer buffer,
115-
uint16 infomask, TransactionId xid)
139+
SetHintBitsExt(HeapTupleHeader tuple, Buffer buffer,
140+
uint16 infomask, TransactionId xid, SetHintBitsState *state)
116141
{
142+
/*
143+
* In batched mode and we previously did not get permission to set hint
144+
* bits. Don't try again, in all likelihood IO is still going on.
145+
*/
146+
if (state && *state == SHB_DISABLED)
147+
return;
148+
117149
if (TransactionIdIsValid(xid))
118150
{
119151
/* NB: xid must be known committed here! */
@@ -127,8 +159,50 @@ SetHintBits(HeapTupleHeader tuple, Buffer buffer,
127159
}
128160
}
129161

162+
if (!state)
163+
{
164+
BufferSetHintBits16(buffer,
165+
&tuple->t_infomask,
166+
tuple->t_infomask | infomask);
167+
return;
168+
}
169+
170+
/*
171+
* If not batching or this is the first hint that we'd like to set on the
172+
* page, check if we are allowed to do so.
173+
*/
174+
if (*state == SHB_INITIAL)
175+
{
176+
if (BufferPrepareToSetHintBits(buffer))
177+
{
178+
*state = SHB_ENABLED;
179+
}
180+
else
181+
{
182+
/*
183+
* If we hold an exclusive lock nobody else should be able to
184+
* prevent us from setting hint bits. This is important as there
185+
* are a few hint bit sets that are important for correctness -
186+
* all those happen with the buffer exclusively locked.
187+
*/
188+
Assert(!BufferLockHeldByMe(buffer, BUFFER_LOCK_EXCLUSIVE));
189+
*state = SHB_DISABLED;
190+
return;
191+
}
192+
}
193+
130194
tuple->t_infomask |= infomask;
131-
MarkBufferDirtyHint(buffer, true);
195+
}
196+
197+
/*
198+
* Simple wrapper around SetHintBitExt(), use when operating on a single
199+
* tuple.
200+
*/
201+
static inline void
202+
SetHintBits(HeapTupleHeader tuple, Buffer buffer,
203+
uint16 infomask, TransactionId xid)
204+
{
205+
SetHintBitsExt(tuple, buffer, infomask, xid, NULL);
132206
}
133207

134208
/*
@@ -864,9 +938,9 @@ HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
864938
* inserting/deleting transaction was still running --- which was more cycles
865939
* and more contention on ProcArrayLock.
866940
*/
867-
static bool
941+
static inline bool
868942
HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
869-
Buffer buffer)
943+
Buffer buffer, SetHintBitsState *state)
870944
{
871945
HeapTupleHeader tuple = htup->t_data;
872946

@@ -921,8 +995,8 @@ HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
921995
if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
922996
{
923997
/* deleting subtransaction must have aborted */
924-
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
925-
InvalidTransactionId);
998+
SetHintBitsExt(tuple, buffer, HEAP_XMAX_INVALID,
999+
InvalidTransactionId, state);
9261000
return true;
9271001
}
9281002

@@ -934,13 +1008,13 @@ HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
9341008
else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
9351009
return false;
9361010
else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
937-
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
938-
HeapTupleHeaderGetRawXmin(tuple));
1011+
SetHintBitsExt(tuple, buffer, HEAP_XMIN_COMMITTED,
1012+
HeapTupleHeaderGetRawXmin(tuple), state);
9391013
else
9401014
{
9411015
/* it must have aborted or crashed */
942-
SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
943-
InvalidTransactionId);
1016+
SetHintBitsExt(tuple, buffer, HEAP_XMIN_INVALID,
1017+
InvalidTransactionId, state);
9441018
return false;
9451019
}
9461020
}
@@ -1003,14 +1077,14 @@ HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
10031077
if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
10041078
{
10051079
/* it must have aborted or crashed */
1006-
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
1007-
InvalidTransactionId);
1080+
SetHintBitsExt(tuple, buffer, HEAP_XMAX_INVALID,
1081+
InvalidTransactionId, state);
10081082
return true;
10091083
}
10101084

10111085
/* xmax transaction committed */
1012-
SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
1013-
HeapTupleHeaderGetRawXmax(tuple));
1086+
SetHintBitsExt(tuple, buffer, HEAP_XMAX_COMMITTED,
1087+
HeapTupleHeaderGetRawXmax(tuple), state);
10141088
}
10151089
else
10161090
{
@@ -1606,6 +1680,7 @@ HeapTupleSatisfiesMVCCBatch(Snapshot snapshot, Buffer buffer,
16061680
OffsetNumber *vistuples_dense)
16071681
{
16081682
int nvis = 0;
1683+
SetHintBitsState state = SHB_INITIAL;
16091684

16101685
Assert(IsMVCCSnapshot(snapshot));
16111686

@@ -1618,7 +1693,7 @@ HeapTupleSatisfiesMVCCBatch(Snapshot snapshot, Buffer buffer,
16181693
HeapTuple tup = &tuples[i];
16191694
#endif
16201695

1621-
valid = HeapTupleSatisfiesMVCC(tup, snapshot, buffer);
1696+
valid = HeapTupleSatisfiesMVCC(tup, snapshot, buffer, &state);
16221697
#ifdef BATCHMVCC_FEWER_ARGS
16231698
batchmvcc->visible[i] = valid;
16241699
#else
@@ -1631,6 +1706,9 @@ HeapTupleSatisfiesMVCCBatch(Snapshot snapshot, Buffer buffer,
16311706
}
16321707
}
16331708

1709+
if (state == SHB_ENABLED)
1710+
BufferFinishSetHintBits(buffer, true, true);
1711+
16341712
return nvis;
16351713
}
16361714

@@ -1650,7 +1728,7 @@ HeapTupleSatisfiesVisibility(HeapTuple htup, Snapshot snapshot, Buffer buffer)
16501728
switch (snapshot->snapshot_type)
16511729
{
16521730
case SNAPSHOT_MVCC:
1653-
return HeapTupleSatisfiesMVCC(htup, snapshot, buffer);
1731+
return HeapTupleSatisfiesMVCC(htup, snapshot, buffer, NULL);
16541732
case SNAPSHOT_SELF:
16551733
return HeapTupleSatisfiesSelf(htup, snapshot, buffer);
16561734
case SNAPSHOT_ANY:

src/tools/pgindent/typedefs.list

+1
Original file line numberDiff line numberDiff line change
@@ -2679,6 +2679,7 @@ SetConstraintStateData
26792679
SetConstraintTriggerData
26802680
SetExprState
26812681
SetFunctionReturnMode
2682+
SetHintBitsState
26822683
SetOp
26832684
SetOpCmd
26842685
SetOpPath

0 commit comments

Comments
 (0)