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
111136 * InvalidTransactionId if no check is needed.
112137 */
113138static 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
868942HeapTupleSatisfiesMVCC (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 :
0 commit comments