gistchoose-3.patch
text/x-patch
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
| File | + | − |
|---|---|---|
| src/backend/access/gist/gistutil.c | 0 | 0 |
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index 8b60774f50349222ee04fef13613e3592120973b..a127627334e80704edeed38d3522e4000a695abb 100644
*** a/src/backend/access/gist/gistutil.c
--- b/src/backend/access/gist/gistutil.c
*************** gistchoose(Relation r, Page p, IndexTupl
*** 379,384 ****
--- 379,385 ----
GISTENTRY entry,
identry[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
+ int keep_current_best;
Assert(!GistPageIsLeaf(p));
*************** gistchoose(Relation r, Page p, IndexTupl
*** 402,407 ****
--- 403,433 ----
best_penalty[0] = -1;
/*
+ * If we find a tuple that's exactly as good as the currently best one, we
+ * could use either one. When inserting a lot of tuples with the same or
+ * similar keys, it's preferable to descend down the same path when
+ * possible, as that's more cache-friendly. On the other hand, if all
+ * inserts land on the same leaf page after a split, we're never going to
+ * insert anything to the other half of the split, and will end up using
+ * only 50% of the available space. Distributing the inserts evenly would
+ * lead to better space usage, but that hurts cache-locality during
+ * insertion. To get the best of both worlds, when we find a tuple that's
+ * exactly as good as the previous best, choose randomly whether to stick
+ * to the old best, or use the new one. Once we decide to stick to the
+ * old best, we keep sticking to it for any subsequent equally good tuples
+ * we might find. This favors tuples with low offsets, but still allows
+ * some inserts to go to other equally-good subtrees.
+ *
+ * keep_current_best is -1 if we haven't yet had to make a random choice
+ * whether to keep the current best tuple. If we have done so, and
+ * decided to keep it, keep_current_best is 1; if we've decided to
+ * replace, keep_current_best is 0. (This state will be reset to -1 as
+ * soon as we've made the replacement, but sometimes we make the choice in
+ * advance of actually finding a replacement best tuple.)
+ */
+ keep_current_best = -1;
+
+ /*
* Loop over tuples on page.
*/
maxoff = PageGetMaxOffsetNumber(p);
*************** gistchoose(Relation r, Page p, IndexTupl
*** 446,451 ****
--- 472,480 ----
if (j < r->rd_att->natts - 1)
best_penalty[j + 1] = -1;
+
+ /* we have new best, so reset keep-it decision */
+ keep_current_best = -1;
}
else if (best_penalty[j] == usize)
{
*************** gistchoose(Relation r, Page p, IndexTupl
*** 468,479 ****
}
/*
! * If we find a tuple with zero penalty for all columns, there's no
! * need to examine remaining tuples; just break out of the loop and
! * return it.
*/
if (zero_penalty)
! break;
}
return result;
--- 497,537 ----
}
/*
! * If we looped past the last column, and did not update "result",
! * then this tuple is exactly as good as the prior best tuple.
! */
! if (j == r->rd_att->natts && result != i)
! {
! if (keep_current_best == -1)
! {
! /* we didn't make the random choice yet for this old best */
! keep_current_best = (random() <= (MAX_RANDOM_VALUE / 2)) ? 1 : 0;
! }
! if (keep_current_best == 0)
! {
! /* we choose to use the new tuple */
! result = i;
! /* choose again if there are even more exactly-as-good ones */
! keep_current_best = -1;
! }
! }
!
! /*
! * If we find a tuple with zero penalty for all columns, and we've
! * decided we don't want to search for another tuple with equal
! * penalty, there's no need to examine remaining tuples; just break
! * out of the loop and return it.
*/
if (zero_penalty)
! {
! if (keep_current_best == -1)
! {
! /* we didn't make the random choice yet for this old best */
! keep_current_best = (random() <= (MAX_RANDOM_VALUE / 2)) ? 1 : 0;
! }
! if (keep_current_best == 1)
! break;
! }
}
return result;