26
26
#include "storage/smgr.h"
27
27
#include "utils/tqual.h"
28
28
29
+ /* Minimum tree height for application of fastpath optimization */
30
+ #define BTREE_FASTPATH_MIN_LEVEL 2
29
31
30
32
typedef struct
31
33
{
@@ -125,7 +127,7 @@ _bt_doinsert(Relation rel, IndexTuple itup,
125
127
/*
126
128
* It's very common to have an index on an auto-incremented or
127
129
* monotonically increasing value. In such cases, every insertion happens
128
- * towards the end of the index. We try to optimise that case by caching
130
+ * towards the end of the index. We try to optimize that case by caching
129
131
* the right-most leaf of the index. If our cached block is still the
130
132
* rightmost leaf, has enough free space to accommodate a new entry and
131
133
* the insertion key is strictly greater than the first key in this page,
@@ -176,13 +178,17 @@ _bt_doinsert(Relation rel, IndexTuple itup,
176
178
* the first key on the page.
177
179
*/
178
180
if (P_ISLEAF (lpageop ) && P_RIGHTMOST (lpageop ) &&
179
- !P_INCOMPLETE_SPLIT (lpageop ) &&
180
181
!P_IGNORE (lpageop ) &&
181
182
(PageGetFreeSpace (page ) > itemsz ) &&
182
183
PageGetMaxOffsetNumber (page ) >= P_FIRSTDATAKEY (lpageop ) &&
183
184
_bt_compare (rel , indnkeyatts , itup_scankey , page ,
184
185
P_FIRSTDATAKEY (lpageop )) > 0 )
185
186
{
187
+ /*
188
+ * The right-most block should never have incomplete split. But
189
+ * be paranoid and check for it anyway.
190
+ */
191
+ Assert (!P_INCOMPLETE_SPLIT (lpageop ));
186
192
fastpath = true;
187
193
}
188
194
else
@@ -868,6 +874,24 @@ _bt_insertonpg(Relation rel,
868
874
bool newitemonleft ;
869
875
Buffer rbuf ;
870
876
877
+ /*
878
+ * If we're here then a pagesplit is needed. We should never reach here
879
+ * if we're using the fastpath since we should have checked for all the
880
+ * required conditions, including the fact that this page has enough
881
+ * freespace. Note that this routine can in theory deal with the
882
+ * situation where a NULL stack pointer is passed (that's what would
883
+ * happen if the fastpath is taken), like it does during crash
884
+ * recovery. But that path is much slower, defeating the very purpose
885
+ * of the optimization. The following assertion should protect us from
886
+ * any future code changes that invalidate those assumptions.
887
+ *
888
+ * Note that whenever we fail to take the fastpath, we clear the
889
+ * cached block. Checking for a valid cached block at this point is
890
+ * enough to decide whether we're in a fastpath or not.
891
+ */
892
+ Assert (!(P_ISLEAF (lpageop ) &&
893
+ BlockNumberIsValid (RelationGetTargetBlock (rel ))));
894
+
871
895
/* Choose the split point */
872
896
firstright = _bt_findsplitloc (rel , page ,
873
897
newitemoff , itemsz ,
@@ -905,6 +929,7 @@ _bt_insertonpg(Relation rel,
905
929
BTMetaPageData * metad = NULL ;
906
930
OffsetNumber itup_off ;
907
931
BlockNumber itup_blkno ;
932
+ BlockNumber cachedBlock = InvalidBlockNumber ;
908
933
909
934
itup_off = newitemoff ;
910
935
itup_blkno = BufferGetBlockNumber (buf );
@@ -962,6 +987,15 @@ _bt_insertonpg(Relation rel,
962
987
MarkBufferDirty (cbuf );
963
988
}
964
989
990
+ /*
991
+ * Cache the block information if we just inserted into the rightmost
992
+ * leaf page of the index and it's not the root page. For very small
993
+ * index where root is also the leaf, there is no point trying for any
994
+ * optimization.
995
+ */
996
+ if (P_RIGHTMOST (lpageop ) && P_ISLEAF (lpageop ) && !P_ISROOT (lpageop ))
997
+ cachedBlock = BufferGetBlockNumber (buf );
998
+
965
999
/* XLOG stuff */
966
1000
if (RelationNeedsWAL (rel ))
967
1001
{
@@ -977,16 +1011,7 @@ _bt_insertonpg(Relation rel,
977
1011
XLogRegisterData ((char * ) & xlrec , SizeOfBtreeInsert );
978
1012
979
1013
if (P_ISLEAF (lpageop ))
980
- {
981
1014
xlinfo = XLOG_BTREE_INSERT_LEAF ;
982
-
983
- /*
984
- * Cache the block information if we just inserted into the
985
- * rightmost leaf page of the index.
986
- */
987
- if (P_RIGHTMOST (lpageop ))
988
- RelationSetTargetBlock (rel , BufferGetBlockNumber (buf ));
989
- }
990
1015
else
991
1016
{
992
1017
/*
@@ -1048,6 +1073,22 @@ _bt_insertonpg(Relation rel,
1048
1073
if (BufferIsValid (cbuf ))
1049
1074
_bt_relbuf (rel , cbuf );
1050
1075
_bt_relbuf (rel , buf );
1076
+
1077
+ /*
1078
+ * If we decided to cache the insertion target block, then set it now.
1079
+ * But before that, check for the height of the tree and don't go for
1080
+ * the optimization for small indexes. We defer that check to this
1081
+ * point to ensure that we don't call _bt_getrootheight while holding
1082
+ * lock on any other block.
1083
+ *
1084
+ * We do this after dropping locks on all buffers. So the information
1085
+ * about whether the insertion block is still the rightmost block or
1086
+ * not may have changed in between. But we will deal with that during
1087
+ * next insert operation. No special care is required while setting it.
1088
+ */
1089
+ if (BlockNumberIsValid (cachedBlock ) &&
1090
+ _bt_getrootheight (rel ) >= BTREE_FASTPATH_MIN_LEVEL )
1091
+ RelationSetTargetBlock (rel , cachedBlock );
1051
1092
}
1052
1093
}
1053
1094
0 commit comments