80
80
81
81
82
82
/*
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()
84
103
*
85
104
* Set commit/abort hint bits on a tuple, if appropriate at this time.
86
105
*
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
+ *
87
112
* It is only safe to set a transaction-committed hint bit if we know the
88
113
* transaction's commit record is guaranteed to be flushed to disk before the
89
114
* buffer, or if the table is temporary or unlogged and will be obliterated by
111
136
* InvalidTransactionId if no check is needed.
112
137
*/
113
138
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 )
116
141
{
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
+
117
149
if (TransactionIdIsValid (xid ))
118
150
{
119
151
/* NB: xid must be known committed here! */
@@ -127,8 +159,50 @@ SetHintBits(HeapTupleHeader tuple, Buffer buffer,
127
159
}
128
160
}
129
161
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
+
130
194
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 );
132
206
}
133
207
134
208
/*
@@ -864,9 +938,9 @@ HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
864
938
* inserting/deleting transaction was still running --- which was more cycles
865
939
* and more contention on ProcArrayLock.
866
940
*/
867
- static bool
941
+ static inline bool
868
942
HeapTupleSatisfiesMVCC (HeapTuple htup , Snapshot snapshot ,
869
- Buffer buffer )
943
+ Buffer buffer , SetHintBitsState * state )
870
944
{
871
945
HeapTupleHeader tuple = htup -> t_data ;
872
946
@@ -921,8 +995,8 @@ HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
921
995
if (!TransactionIdIsCurrentTransactionId (HeapTupleHeaderGetRawXmax (tuple )))
922
996
{
923
997
/* deleting subtransaction must have aborted */
924
- SetHintBits (tuple , buffer , HEAP_XMAX_INVALID ,
925
- InvalidTransactionId );
998
+ SetHintBitsExt (tuple , buffer , HEAP_XMAX_INVALID ,
999
+ InvalidTransactionId , state );
926
1000
return true;
927
1001
}
928
1002
@@ -934,13 +1008,13 @@ HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
934
1008
else if (XidInMVCCSnapshot (HeapTupleHeaderGetRawXmin (tuple ), snapshot ))
935
1009
return false;
936
1010
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 );
939
1013
else
940
1014
{
941
1015
/* 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 );
944
1018
return false;
945
1019
}
946
1020
}
@@ -1003,14 +1077,14 @@ HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
1003
1077
if (!TransactionIdDidCommit (HeapTupleHeaderGetRawXmax (tuple )))
1004
1078
{
1005
1079
/* 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 );
1008
1082
return true;
1009
1083
}
1010
1084
1011
1085
/* xmax transaction committed */
1012
- SetHintBits (tuple , buffer , HEAP_XMAX_COMMITTED ,
1013
- HeapTupleHeaderGetRawXmax (tuple ));
1086
+ SetHintBitsExt (tuple , buffer , HEAP_XMAX_COMMITTED ,
1087
+ HeapTupleHeaderGetRawXmax (tuple ), state );
1014
1088
}
1015
1089
else
1016
1090
{
@@ -1606,6 +1680,7 @@ HeapTupleSatisfiesMVCCBatch(Snapshot snapshot, Buffer buffer,
1606
1680
OffsetNumber * vistuples_dense )
1607
1681
{
1608
1682
int nvis = 0 ;
1683
+ SetHintBitsState state = SHB_INITIAL ;
1609
1684
1610
1685
Assert (IsMVCCSnapshot (snapshot ));
1611
1686
@@ -1618,7 +1693,7 @@ HeapTupleSatisfiesMVCCBatch(Snapshot snapshot, Buffer buffer,
1618
1693
HeapTuple tup = & tuples [i ];
1619
1694
#endif
1620
1695
1621
- valid = HeapTupleSatisfiesMVCC (tup , snapshot , buffer );
1696
+ valid = HeapTupleSatisfiesMVCC (tup , snapshot , buffer , & state );
1622
1697
#ifdef BATCHMVCC_FEWER_ARGS
1623
1698
batchmvcc -> visible [i ] = valid ;
1624
1699
#else
@@ -1631,6 +1706,9 @@ HeapTupleSatisfiesMVCCBatch(Snapshot snapshot, Buffer buffer,
1631
1706
}
1632
1707
}
1633
1708
1709
+ if (state == SHB_ENABLED )
1710
+ BufferFinishSetHintBits (buffer , true, true);
1711
+
1634
1712
return nvis ;
1635
1713
}
1636
1714
@@ -1650,7 +1728,7 @@ HeapTupleSatisfiesVisibility(HeapTuple htup, Snapshot snapshot, Buffer buffer)
1650
1728
switch (snapshot -> snapshot_type )
1651
1729
{
1652
1730
case SNAPSHOT_MVCC :
1653
- return HeapTupleSatisfiesMVCC (htup , snapshot , buffer );
1731
+ return HeapTupleSatisfiesMVCC (htup , snapshot , buffer , NULL );
1654
1732
case SNAPSHOT_SELF :
1655
1733
return HeapTupleSatisfiesSelf (htup , snapshot , buffer );
1656
1734
case SNAPSHOT_ANY :
0 commit comments