summaryrefslogtreecommitdiff
path: root/src/btree
diff options
context:
space:
mode:
Diffstat (limited to 'src/btree')
-rw-r--r--src/btree/bt_curnext.c15
-rw-r--r--src/btree/bt_curprev.c19
-rw-r--r--src/btree/bt_cursor.c697
-rw-r--r--src/btree/bt_debug.c35
-rw-r--r--src/btree/bt_delete.c7
-rw-r--r--src/btree/bt_discard.c42
-rw-r--r--src/btree/bt_handle.c177
-rw-r--r--src/btree/bt_io.c9
-rw-r--r--src/btree/bt_ovfl.c8
-rw-r--r--src/btree/bt_page.c28
-rw-r--r--src/btree/bt_random.c432
-rw-r--r--src/btree/bt_read.c11
-rw-r--r--src/btree/bt_rebalance.c17
-rw-r--r--src/btree/bt_ret.c173
-rw-r--r--src/btree/bt_slvg.c43
-rw-r--r--src/btree/bt_split.c284
-rw-r--r--src/btree/bt_stat.c5
-rw-r--r--src/btree/bt_sync.c10
-rw-r--r--src/btree/bt_vrfy.c46
-rw-r--r--src/btree/bt_vrfy_dsk.c8
-rw-r--r--src/btree/bt_walk.c15
-rw-r--r--src/btree/col_modify.c5
-rw-r--r--src/btree/col_srch.c7
-rw-r--r--src/btree/row_key.c12
-rw-r--r--src/btree/row_modify.c9
-rw-r--r--src/btree/row_srch.c230
26 files changed, 1455 insertions, 889 deletions
diff --git a/src/btree/bt_curnext.c b/src/btree/bt_curnext.c
index 4d3976f9647..21e575ffca9 100644
--- a/src/btree/bt_curnext.c
+++ b/src/btree/bt_curnext.c
@@ -338,7 +338,7 @@ new_insert: if ((ins = cbt->ins) != NULL) {
}
/* Check for the end of the page. */
- if (cbt->row_iteration_slot >= page->pg_row_entries * 2 + 1)
+ if (cbt->row_iteration_slot >= page->entries * 2 + 1)
return (WT_NOTFOUND);
++cbt->row_iteration_slot;
@@ -356,7 +356,7 @@ new_insert: if ((ins = cbt->ins) != NULL) {
cbt->ins = NULL;
cbt->slot = cbt->row_iteration_slot / 2 - 1;
- rip = &page->pg_row_d[cbt->slot];
+ rip = &page->pg_row[cbt->slot];
upd = __wt_txn_read(session, WT_ROW_UPDATE(page, rip));
if (upd != NULL && WT_UPDATE_DELETED_ISSET(upd)) {
if (__wt_txn_visible_all(session, upd->txnid))
@@ -579,20 +579,20 @@ __wt_btcur_iterate_setup(WT_CURSOR_BTREE *cbt)
int
__wt_btcur_next(WT_CURSOR_BTREE *cbt, bool truncating)
{
+ WT_CURSOR *cursor;
WT_DECL_RET;
WT_PAGE *page;
WT_SESSION_IMPL *session;
uint32_t flags;
bool newpage;
+ cursor = &cbt->iface;
session = (WT_SESSION_IMPL *)cbt->iface.session;
WT_STAT_CONN_INCR(session, cursor_next);
WT_STAT_DATA_INCR(session, cursor_next);
- flags = WT_READ_SKIP_INTL; /* Tree walk flags. */
- if (truncating)
- LF_SET(WT_READ_TRUNCATE);
+ F_CLR(cursor, WT_CURSTD_KEY_SET | WT_CURSTD_VALUE_SET);
WT_RET(__cursor_func_init(cbt, false));
@@ -608,6 +608,9 @@ __wt_btcur_next(WT_CURSOR_BTREE *cbt, bool truncating)
* found. Then, move to the next page, until we reach the end of the
* file.
*/
+ flags = WT_READ_SKIP_INTL; /* tree walk flags */
+ if (truncating)
+ LF_SET(WT_READ_TRUNCATE);
for (newpage = false;; newpage = true) {
page = cbt->ref == NULL ? NULL : cbt->ref->page;
@@ -676,6 +679,8 @@ __wt_btcur_next(WT_CURSOR_BTREE *cbt, bool truncating)
if (ret == 0)
WT_ERR(__wt_cursor_key_order_check(session, cbt, true));
#endif
+ if (ret == 0)
+ F_SET(cursor, WT_CURSTD_KEY_INT | WT_CURSTD_VALUE_INT);
err: if (ret != 0)
WT_TRET(__cursor_reset(cbt));
diff --git a/src/btree/bt_curprev.c b/src/btree/bt_curprev.c
index 2dd443ffac1..bf4bdad6529 100644
--- a/src/btree/bt_curprev.c
+++ b/src/btree/bt_curprev.c
@@ -458,13 +458,13 @@ __cursor_row_prev(WT_CURSOR_BTREE *cbt, bool newpage)
if (!F_ISSET_ATOMIC(page, WT_PAGE_BUILD_KEYS))
WT_RET(__wt_row_leaf_keys(session, page));
- if (page->pg_row_entries == 0)
+ if (page->entries == 0)
cbt->ins_head = WT_ROW_INSERT_SMALLEST(page);
else
cbt->ins_head =
- WT_ROW_INSERT_SLOT(page, page->pg_row_entries - 1);
+ WT_ROW_INSERT_SLOT(page, page->entries - 1);
cbt->ins = WT_SKIP_LAST(cbt->ins_head);
- cbt->row_iteration_slot = page->pg_row_entries * 2 + 1;
+ cbt->row_iteration_slot = page->entries * 2 + 1;
cbt->rip_saved = NULL;
goto new_insert;
}
@@ -515,7 +515,7 @@ new_insert: if ((ins = cbt->ins) != NULL) {
cbt->ins = NULL;
cbt->slot = cbt->row_iteration_slot / 2 - 1;
- rip = &page->pg_row_d[cbt->slot];
+ rip = &page->pg_row[cbt->slot];
upd = __wt_txn_read(session, WT_ROW_UPDATE(page, rip));
if (upd != NULL && WT_UPDATE_DELETED_ISSET(upd)) {
if (__wt_txn_visible_all(session, upd->txnid))
@@ -535,20 +535,20 @@ new_insert: if ((ins = cbt->ins) != NULL) {
int
__wt_btcur_prev(WT_CURSOR_BTREE *cbt, bool truncating)
{
+ WT_CURSOR *cursor;
WT_DECL_RET;
WT_PAGE *page;
WT_SESSION_IMPL *session;
uint32_t flags;
bool newpage;
+ cursor = &cbt->iface;
session = (WT_SESSION_IMPL *)cbt->iface.session;
WT_STAT_CONN_INCR(session, cursor_prev);
WT_STAT_DATA_INCR(session, cursor_prev);
- flags = WT_READ_PREV | WT_READ_SKIP_INTL; /* Tree walk flags. */
- if (truncating)
- LF_SET(WT_READ_TRUNCATE);
+ F_CLR(cursor, WT_CURSTD_KEY_SET | WT_CURSTD_VALUE_SET);
WT_RET(__cursor_func_init(cbt, false));
@@ -564,6 +564,9 @@ __wt_btcur_prev(WT_CURSOR_BTREE *cbt, bool truncating)
* found. Then, move to the previous page, until we reach the start
* of the file.
*/
+ flags = WT_READ_PREV | WT_READ_SKIP_INTL; /* tree walk flags */
+ if (truncating)
+ LF_SET(WT_READ_TRUNCATE);
for (newpage = false;; newpage = true) {
page = cbt->ref == NULL ? NULL : cbt->ref->page;
@@ -631,6 +634,8 @@ __wt_btcur_prev(WT_CURSOR_BTREE *cbt, bool truncating)
if (ret == 0)
WT_ERR(__wt_cursor_key_order_check(session, cbt, false));
#endif
+ if (ret == 0)
+ F_SET(cursor, WT_CURSTD_KEY_INT | WT_CURSTD_VALUE_INT);
err: if (ret != 0)
WT_TRET(__cursor_reset(cbt));
diff --git a/src/btree/bt_cursor.c b/src/btree/bt_cursor.c
index 650289f2cd8..944e276fc01 100644
--- a/src/btree/bt_cursor.c
+++ b/src/btree/bt_cursor.c
@@ -9,6 +9,84 @@
#include "wt_internal.h"
/*
+ * When returning an error, we need to restore the cursor to a valid state, the
+ * upper-level cursor code is likely to retry. This structure and the associated
+ * functions are used save and restore the cursor state.
+ */
+typedef struct {
+ WT_ITEM key;
+ WT_ITEM value;
+ uint64_t recno;
+ uint32_t flags;
+} WT_CURFILE_STATE;
+
+/*
+ * __cursor_state_save --
+ * Save the cursor's external state.
+ */
+static inline void
+__cursor_state_save(WT_CURSOR *cursor, WT_CURFILE_STATE *state)
+{
+ WT_ITEM_SET(state->key, cursor->key);
+ WT_ITEM_SET(state->value, cursor->value);
+ state->recno = cursor->recno;
+ state->flags = cursor->flags;
+}
+
+/*
+ * __cursor_state_restore --
+ * Restore the cursor's external state.
+ */
+static inline void
+__cursor_state_restore(WT_CURSOR *cursor, WT_CURFILE_STATE *state)
+{
+ if (F_ISSET(state, WT_CURSTD_KEY_EXT))
+ WT_ITEM_SET(cursor->key, state->key);
+ if (F_ISSET(state, WT_CURSTD_VALUE_EXT))
+ WT_ITEM_SET(cursor->value, state->value);
+ cursor->recno = state->recno;
+ F_CLR(cursor, WT_CURSTD_KEY_INT | WT_CURSTD_VALUE_INT);
+ F_SET(cursor, F_MASK(state, WT_CURSTD_KEY_EXT | WT_CURSTD_VALUE_EXT));
+
+}
+
+/*
+ * __cursor_page_pinned --
+ * Return if we have a page pinned and it's not been flagged for forced
+ * eviction (the forced eviction test is so we periodically release pages
+ * grown too large).
+ */
+static inline bool
+__cursor_page_pinned(WT_CURSOR_BTREE *cbt)
+{
+ return (F_ISSET(cbt, WT_CBT_ACTIVE) &&
+ cbt->ref->page->read_gen != WT_READGEN_OLDEST);
+}
+
+/*
+ * __cursor_copy_int_key --
+ * If we're pointing into the tree, save the key into local memory.
+ */
+static inline int
+__cursor_copy_int_key(WT_CURSOR *cursor)
+{
+ /*
+ * We're about to discard the cursor's position and the cursor layer
+ * might retry the operation. We discard pinned pages on error, which
+ * will invalidate pinned keys. Clear WT_CURSTD_KEY_INT in all cases,
+ * the underlying page is gone whether we can allocate memory or not.
+ */
+ if (F_ISSET(cursor, WT_CURSTD_KEY_INT)) {
+ F_CLR(cursor, WT_CURSTD_KEY_INT);
+ if (!WT_DATA_IN_ITEM(&cursor->key))
+ WT_RET(__wt_buf_set((WT_SESSION_IMPL *)cursor->session,
+ &cursor->key, cursor->key.data, cursor->key.size));
+ F_SET(cursor, WT_CURSTD_KEY_EXT);
+ }
+ return (0);
+}
+
+/*
* __cursor_size_chk --
* Return if an inserted item is too large.
*/
@@ -55,6 +133,34 @@ __cursor_size_chk(WT_SESSION_IMPL *session, WT_ITEM *kv)
}
/*
+ * __cursor_disable_bulk --
+ * Disable bulk loads into a tree.
+ */
+static inline void
+__cursor_disable_bulk(WT_SESSION_IMPL *session, WT_BTREE *btree)
+{
+ /*
+ * Once a tree (other than the LSM primary) is no longer empty, eviction
+ * should pay attention to it, and it's no longer possible to bulk-load
+ * into it.
+ */
+ if (!btree->original)
+ return;
+ if (btree->lsm_primary) {
+ btree->original = 0; /* Make the next test faster. */
+ return;
+ }
+
+ /*
+ * We use a compare-and-swap here to avoid races among the first inserts
+ * into a tree. Eviction is disabled when an empty tree is opened, and
+ * it must only be enabled once.
+ */
+ if (__wt_atomic_cas8(&btree->original, 1, 0))
+ __wt_evict_file_exclusive_off(session);
+}
+
+/*
* __cursor_fix_implicit --
* Return if search went past the end of the tree.
*/
@@ -76,11 +182,11 @@ __cursor_fix_implicit(WT_BTREE *btree, WT_CURSOR_BTREE *cbt)
}
/*
- * __cursor_valid --
+ * __wt_cursor_valid --
* Return if the cursor references an valid key/value pair.
*/
-static inline bool
-__cursor_valid(WT_CURSOR_BTREE *cbt, WT_UPDATE **updp)
+bool
+__wt_cursor_valid(WT_CURSOR_BTREE *cbt, WT_UPDATE **updp)
{
WT_BTREE *btree;
WT_CELL *cell;
@@ -163,7 +269,7 @@ __cursor_valid(WT_CURSOR_BTREE *cbt, WT_UPDATE **updp)
* column-store pages don't have slots, but map one-to-one to
* keys, check for retrieval past the end of the page.
*/
- if (cbt->recno >= cbt->ref->ref_recno + page->pg_fix_entries)
+ if (cbt->recno >= cbt->ref->ref_recno + page->entries)
return (false);
/*
@@ -173,9 +279,9 @@ __cursor_valid(WT_CURSOR_BTREE *cbt, WT_UPDATE **updp)
break;
case BTREE_COL_VAR:
/* The search function doesn't check for empty pages. */
- if (page->pg_var_entries == 0)
+ if (page->entries == 0)
return (false);
- WT_ASSERT(session, cbt->slot < page->pg_var_entries);
+ WT_ASSERT(session, cbt->slot < page->entries);
/*
* Column-store updates are stored as "insert" objects. If
@@ -191,16 +297,16 @@ __cursor_valid(WT_CURSOR_BTREE *cbt, WT_UPDATE **updp)
* backing store; check the cell for a record already deleted
* when read.
*/
- cip = &page->pg_var_d[cbt->slot];
+ cip = &page->pg_var[cbt->slot];
if ((cell = WT_COL_PTR(page, cip)) == NULL ||
__wt_cell_type(cell) == WT_CELL_DEL)
return (false);
break;
case BTREE_ROW:
/* The search function doesn't check for empty pages. */
- if (page->pg_row_entries == 0)
+ if (page->entries == 0)
return (false);
- WT_ASSERT(session, cbt->slot < page->pg_row_entries);
+ WT_ASSERT(session, cbt->slot < page->entries);
/*
* See above: for row-store, no insert object can have the same
@@ -285,13 +391,17 @@ __cursor_row_modify(
int
__wt_btcur_reset(WT_CURSOR_BTREE *cbt)
{
+ WT_CURSOR *cursor;
WT_SESSION_IMPL *session;
+ cursor = &cbt->iface;
session = (WT_SESSION_IMPL *)cbt->iface.session;
WT_STAT_CONN_INCR(session, cursor_reset);
WT_STAT_DATA_INCR(session, cursor_reset);
+ F_CLR(cursor, WT_CURSTD_KEY_SET | WT_CURSTD_VALUE_SET);
+
return (__cursor_reset(cbt));
}
@@ -303,6 +413,7 @@ int
__wt_btcur_search(WT_CURSOR_BTREE *cbt)
{
WT_BTREE *btree;
+ WT_CURFILE_STATE state;
WT_CURSOR *cursor;
WT_DECL_RET;
WT_SESSION_IMPL *session;
@@ -317,20 +428,28 @@ __wt_btcur_search(WT_CURSOR_BTREE *cbt)
WT_STAT_CONN_INCR(session, cursor_search);
WT_STAT_DATA_INCR(session, cursor_search);
+ __cursor_state_save(cursor, &state);
+
+ /*
+ * The pinned page goes away if we do a search, make sure there's a
+ * local copy of any key, then re-save the cursor state.
+ */
+ WT_ERR(__cursor_copy_int_key(cursor));
+ __cursor_state_save(cursor, &state);
+
/*
* If we have a page pinned, search it; if we don't have a page pinned,
* or the search of the pinned page doesn't find an exact match, search
* from the root.
*/
valid = false;
- if (F_ISSET(cbt, WT_CBT_ACTIVE) &&
- cbt->ref->page->read_gen != WT_READGEN_OLDEST) {
+ if (__cursor_page_pinned(cbt)) {
__wt_txn_cursor_op(session);
WT_ERR(btree->type == BTREE_ROW ?
__cursor_row_search(session, cbt, cbt->ref, false) :
__cursor_col_search(session, cbt, cbt->ref));
- valid = cbt->compare == 0 && __cursor_valid(cbt, &upd);
+ valid = cbt->compare == 0 && __wt_cursor_valid(cbt, &upd);
}
if (!valid) {
WT_ERR(__cursor_func_init(cbt, true));
@@ -338,7 +457,7 @@ __wt_btcur_search(WT_CURSOR_BTREE *cbt)
WT_ERR(btree->type == BTREE_ROW ?
__cursor_row_search(session, cbt, NULL, false) :
__cursor_col_search(session, cbt, NULL));
- valid = cbt->compare == 0 && __cursor_valid(cbt, &upd);
+ valid = cbt->compare == 0 && __wt_cursor_valid(cbt, &upd);
}
if (valid)
@@ -352,6 +471,8 @@ __wt_btcur_search(WT_CURSOR_BTREE *cbt)
cbt->v = 0;
cursor->value.data = &cbt->v;
cursor->value.size = 1;
+ F_CLR(cursor, WT_CURSTD_KEY_SET | WT_CURSTD_VALUE_SET);
+ F_SET(cursor, WT_CURSTD_KEY_INT | WT_CURSTD_VALUE_INT);
} else
ret = WT_NOTFOUND;
@@ -360,8 +481,10 @@ __wt_btcur_search(WT_CURSOR_BTREE *cbt)
WT_ERR(__wt_cursor_key_order_init(session, cbt));
#endif
-err: if (ret != 0)
+err: if (ret != 0) {
WT_TRET(__cursor_reset(cbt));
+ __cursor_state_restore(cursor, &state);
+ }
return (ret);
}
@@ -373,6 +496,7 @@ int
__wt_btcur_search_near(WT_CURSOR_BTREE *cbt, int *exactp)
{
WT_BTREE *btree;
+ WT_CURFILE_STATE state;
WT_CURSOR *cursor;
WT_DECL_RET;
WT_SESSION_IMPL *session;
@@ -389,6 +513,15 @@ __wt_btcur_search_near(WT_CURSOR_BTREE *cbt, int *exactp)
WT_STAT_CONN_INCR(session, cursor_search_near);
WT_STAT_DATA_INCR(session, cursor_search_near);
+ __cursor_state_save(cursor, &state);
+
+ /*
+ * The pinned page goes away if we do a search, make sure there's a
+ * local copy of any key, then re-save the cursor state.
+ */
+ WT_ERR(__cursor_copy_int_key(cursor));
+ __cursor_state_save(cursor, &state);
+
/*
* If we have a row-store page pinned, search it; if we don't have a
* page pinned, or the search of the pinned page doesn't find an exact
@@ -402,9 +535,7 @@ __wt_btcur_search_near(WT_CURSOR_BTREE *cbt, int *exactp)
* existing record.
*/
valid = false;
- if (btree->type == BTREE_ROW &&
- F_ISSET(cbt, WT_CBT_ACTIVE) &&
- cbt->ref->page->read_gen != WT_READGEN_OLDEST) {
+ if (btree->type == BTREE_ROW && __cursor_page_pinned(cbt)) {
__wt_txn_cursor_op(session);
WT_ERR(__cursor_row_search(session, cbt, cbt->ref, true));
@@ -418,16 +549,15 @@ __wt_btcur_search_near(WT_CURSOR_BTREE *cbt, int *exactp)
* might be legitimately positioned after the last page slot).
* Ignore those cases, it makes things too complicated.
*/
- if (cbt->slot != 0 &&
- cbt->slot != cbt->ref->page->pg_row_entries - 1)
- valid = __cursor_valid(cbt, &upd);
+ if (cbt->slot != 0 && cbt->slot != cbt->ref->page->entries - 1)
+ valid = __wt_cursor_valid(cbt, &upd);
}
if (!valid) {
WT_ERR(__cursor_func_init(cbt, true));
WT_ERR(btree->type == BTREE_ROW ?
__cursor_row_search(session, cbt, NULL, true) :
__cursor_col_search(session, cbt, NULL));
- valid = __cursor_valid(cbt, &upd);
+ valid = __wt_cursor_valid(cbt, &upd);
}
/*
@@ -456,6 +586,8 @@ __wt_btcur_search_near(WT_CURSOR_BTREE *cbt, int *exactp)
cursor->value.data = &cbt->v;
cursor->value.size = 1;
exact = 0;
+ F_CLR(cursor, WT_CURSTD_KEY_SET | WT_CURSTD_VALUE_SET);
+ F_SET(cursor, WT_CURSTD_KEY_INT | WT_CURSTD_VALUE_INT);
} else if ((ret = __wt_btcur_next(cbt, false)) != WT_NOTFOUND)
exact = 1;
else {
@@ -463,22 +595,25 @@ __wt_btcur_search_near(WT_CURSOR_BTREE *cbt, int *exactp)
WT_ERR(btree->type == BTREE_ROW ?
__cursor_row_search(session, cbt, NULL, true) :
__cursor_col_search(session, cbt, NULL));
- if (__cursor_valid(cbt, &upd)) {
+ if (__wt_cursor_valid(cbt, &upd)) {
exact = cbt->compare;
ret = __wt_kv_return(session, cbt, upd);
} else if ((ret = __wt_btcur_prev(cbt, false)) != WT_NOTFOUND)
exact = -1;
}
+err: if (ret == 0 && exactp != NULL)
+ *exactp = exact;
+
#ifdef HAVE_DIAGNOSTIC
if (ret == 0)
- WT_ERR(__wt_cursor_key_order_init(session, cbt));
+ WT_TRET(__wt_cursor_key_order_init(session, cbt));
#endif
-err: if (ret != 0)
+ if (ret != 0) {
WT_TRET(__cursor_reset(cbt));
- if (exactp != NULL && (ret == 0 || ret == WT_NOTFOUND))
- *exactp = exact;
+ __cursor_state_restore(cursor, &state);
+ }
return (ret);
}
@@ -490,9 +625,11 @@ int
__wt_btcur_insert(WT_CURSOR_BTREE *cbt)
{
WT_BTREE *btree;
+ WT_CURFILE_STATE state;
WT_CURSOR *cursor;
WT_DECL_RET;
WT_SESSION_IMPL *session;
+ bool append_key;
btree = cbt->btree;
cursor = &cbt->iface;
@@ -503,30 +640,86 @@ __wt_btcur_insert(WT_CURSOR_BTREE *cbt)
WT_STAT_DATA_INCRV(session,
cursor_insert_bytes, cursor->key.size + cursor->value.size);
+ __cursor_state_save(cursor, &state);
+
if (btree->type == BTREE_ROW)
WT_RET(__cursor_size_chk(session, &cursor->key));
WT_RET(__cursor_size_chk(session, &cursor->value));
+ /* It's no longer possible to bulk-load into the tree. */
+ __cursor_disable_bulk(session, btree);
+
+ /*
+ * Insert a new record if WT_CURSTD_APPEND configured, (ignoring any
+ * application set record number). Although append can't be configured
+ * for a row-store, this code would break if it were, and that's owned
+ * by the upper cursor layer, be cautious.
+ */
+ append_key =
+ F_ISSET(cursor, WT_CURSTD_APPEND) && btree->type != BTREE_ROW;
+
/*
- * The tree is no longer empty: eviction should pay attention to it,
- * and it's no longer possible to bulk-load into it.
+ * If inserting with overwrite configured, and positioned to an on-page
+ * key, the update doesn't require another search. The cursor won't be
+ * positioned on a page with an external key set, but be sure. Cursors
+ * configured for append aren't included, regardless of whether or not
+ * they meet all other criteria.
*/
- if (btree->bulk_load_ok) {
- btree->bulk_load_ok = false;
- __wt_btree_evictable(session, true);
+ if (__cursor_page_pinned(cbt) &&
+ F_ISSET_ALL(cursor, WT_CURSTD_KEY_INT | WT_CURSTD_OVERWRITE) &&
+ !append_key) {
+ WT_ERR(__wt_txn_autocommit_check(session));
+ /*
+ * The cursor position may not be exact (the cursor's comparison
+ * value not equal to zero). Correct to an exact match so we can
+ * update whatever we're pointing at.
+ */
+ cbt->compare = 0;
+ ret = btree->type == BTREE_ROW ?
+ __cursor_row_modify(session, cbt, false) :
+ __cursor_col_modify(session, cbt, false);
+ if (ret == 0)
+ goto done;
+
+ /*
+ * The pinned page goes away if we fail for any reason, make
+ * sure there's a local copy of any key. (Restart could still
+ * use the pinned page, but that's an unlikely path.) Re-save
+ * the cursor state: we may retry but eventually fail.
+ */
+ WT_TRET(__cursor_copy_int_key(cursor));
+ __cursor_state_save(cursor, &state);
+ goto err;
}
-retry: WT_RET(__cursor_func_init(cbt, true));
+ /*
+ * The pinned page goes away if we do a search, make sure there's a
+ * local copy of any key. Re-save the cursor state: we may retry but
+ * eventually fail.
+ */
+ WT_ERR(__cursor_copy_int_key(cursor));
+ __cursor_state_save(cursor, &state);
- switch (btree->type) {
- case BTREE_COL_FIX:
- case BTREE_COL_VAR:
+retry: WT_ERR(__cursor_func_init(cbt, true));
+
+ if (btree->type == BTREE_ROW) {
+ WT_ERR(__cursor_row_search(session, cbt, NULL, true));
+ /*
+ * If not overwriting, fail if the key exists, else insert the
+ * key/value pair.
+ */
+ if (!F_ISSET(cursor, WT_CURSTD_OVERWRITE) &&
+ cbt->compare == 0 && __wt_cursor_valid(cbt, NULL))
+ WT_ERR(WT_DUPLICATE_KEY);
+
+ ret = __cursor_row_modify(session, cbt, false);
+ } else {
/*
- * If WT_CURSTD_APPEND is set, insert a new record (ignoring
- * the application's record number). The real record number
- * is assigned by the serialized append operation.
+ * Optionally insert a new record (ignoring the application's
+ * record number). The real record number is allocated by the
+ * serialized append operation.
*/
- if (F_ISSET(cursor, WT_CURSTD_APPEND))
+ if (append_key)
cbt->iface.recno = WT_RECNO_OOB;
WT_ERR(__cursor_col_search(session, cbt, NULL));
@@ -538,26 +731,14 @@ retry: WT_RET(__cursor_func_init(cbt, true));
* Fail in that case, the record exists.
*/
if (!F_ISSET(cursor, WT_CURSTD_OVERWRITE) &&
- ((cbt->compare == 0 && __cursor_valid(cbt, NULL)) ||
+ ((cbt->compare == 0 && __wt_cursor_valid(cbt, NULL)) ||
(cbt->compare != 0 && __cursor_fix_implicit(btree, cbt))))
WT_ERR(WT_DUPLICATE_KEY);
WT_ERR(__cursor_col_modify(session, cbt, false));
- if (F_ISSET(cursor, WT_CURSTD_APPEND))
- cbt->iface.recno = cbt->recno;
- break;
- case BTREE_ROW:
- WT_ERR(__cursor_row_search(session, cbt, NULL, true));
- /*
- * If not overwriting, fail if the key exists, else insert the
- * key/value pair.
- */
- if (!F_ISSET(cursor, WT_CURSTD_OVERWRITE) &&
- cbt->compare == 0 && __cursor_valid(cbt, NULL))
- WT_ERR(WT_DUPLICATE_KEY);
- ret = __cursor_row_modify(session, cbt, false);
- break;
+ if (append_key)
+ cbt->iface.recno = cbt->recno;
}
err: if (ret == WT_RESTART) {
@@ -565,11 +746,17 @@ err: if (ret == WT_RESTART) {
WT_STAT_DATA_INCR(session, cursor_restart);
goto retry;
}
- /* Insert doesn't maintain a position across calls, clear resources. */
- if (ret == 0)
- WT_TRET(__curfile_leave(cbt));
+
+done: /* Insert doesn't maintain a position across calls, clear resources. */
+ if (ret == 0) {
+ F_CLR(cursor, WT_CURSTD_KEY_SET | WT_CURSTD_VALUE_SET);
+ if (append_key)
+ F_SET(cursor, WT_CURSTD_KEY_INT);
+ }
+ WT_TRET(__cursor_reset(cbt));
if (ret != 0)
- WT_TRET(__cursor_reset(cbt));
+ __cursor_state_restore(cursor, &state);
+
return (ret);
}
@@ -605,16 +792,15 @@ __curfile_update_check(WT_CURSOR_BTREE *cbt)
}
/*
- * __wt_btcur_update_check --
+ * __wt_btcur_insert_check --
* Check whether an update would conflict.
*
- * This can be used to replace WT_CURSOR::insert or WT_CURSOR::update, so
- * they only check for conflicts without updating the tree. It is used to
- * maintain snapshot isolation for transactions that span multiple chunks
- * in an LSM tree.
+ * This can replace WT_CURSOR::insert, so it only checks for conflicts without
+ * updating the tree. It is used to maintain snapshot isolation for transactions
+ * that span multiple chunks in an LSM tree.
*/
int
-__wt_btcur_update_check(WT_CURSOR_BTREE *cbt)
+__wt_btcur_insert_check(WT_CURSOR_BTREE *cbt)
{
WT_BTREE *btree;
WT_CURSOR *cursor;
@@ -625,31 +811,35 @@ __wt_btcur_update_check(WT_CURSOR_BTREE *cbt)
btree = cbt->btree;
session = (WT_SESSION_IMPL *)cursor->session;
-retry: WT_RET(__cursor_func_init(cbt, true));
+ /*
+ * The pinned page goes away if we do a search, make sure there's a
+ * local copy of any key. Unlike most of the btree cursor routines,
+ * we don't have to save/restore the cursor key state, none of the
+ * work done here changes the key state.
+ */
+ WT_ERR(__cursor_copy_int_key(cursor));
- switch (btree->type) {
- case BTREE_ROW:
+retry: WT_ERR(__cursor_func_init(cbt, true));
+
+ if (btree->type == BTREE_ROW) {
WT_ERR(__cursor_row_search(session, cbt, NULL, true));
- /*
- * Just check for conflicts.
- */
+ /* Just check for conflicts. */
ret = __curfile_update_check(cbt);
- break;
- case BTREE_COL_FIX:
- case BTREE_COL_VAR:
+ } else
WT_ERR(__wt_illegal_value(session, NULL));
- break;
- }
err: if (ret == WT_RESTART) {
WT_STAT_CONN_INCR(session, cursor_restart);
WT_STAT_DATA_INCR(session, cursor_restart);
goto retry;
}
- WT_TRET(__curfile_leave(cbt));
- if (ret != 0)
- WT_TRET(__cursor_reset(cbt));
+
+ /* Insert doesn't maintain a position across calls, clear resources. */
+ if (ret == 0)
+ F_CLR(cursor, WT_CURSTD_KEY_SET | WT_CURSTD_VALUE_SET);
+ WT_TRET(__cursor_reset(cbt));
+
return (ret);
}
@@ -661,9 +851,11 @@ int
__wt_btcur_remove(WT_CURSOR_BTREE *cbt)
{
WT_BTREE *btree;
+ WT_CURFILE_STATE state;
WT_CURSOR *cursor;
WT_DECL_RET;
WT_SESSION_IMPL *session;
+ bool positioned;
btree = cbt->btree;
cursor = &cbt->iface;
@@ -673,22 +865,80 @@ __wt_btcur_remove(WT_CURSOR_BTREE *cbt)
WT_STAT_DATA_INCR(session, cursor_remove);
WT_STAT_DATA_INCRV(session, cursor_remove_bytes, cursor->key.size);
-retry: WT_RET(__cursor_func_init(cbt, true));
+ __cursor_state_save(cursor, &state);
- switch (btree->type) {
- case BTREE_COL_FIX:
- case BTREE_COL_VAR:
+ /*
+ * WT_CURSOR.remove has a unique semantic, the cursor stays positioned
+ * if it starts positioned, otherwise clear the cursor on completion.
+ */
+ positioned = F_ISSET(cursor, WT_CURSTD_KEY_INT);
+
+ /*
+ * If remove positioned to an on-page key, the remove doesn't require
+ * another search. We don't care about the "overwrite" configuration
+ * because regardless of the overwrite setting, any existing record is
+ * removed, and the record must exist with a positioned cursor. The
+ * cursor won't be positioned on a page with an external key set, but
+ * be sure.
+ */
+ if (__cursor_page_pinned(cbt) && F_ISSET(cursor, WT_CURSTD_KEY_INT)) {
+ WT_ERR(__wt_txn_autocommit_check(session));
+
+ /*
+ * The cursor position may not be exact (the cursor's comparison
+ * value not equal to zero). Correct to an exact match so we can
+ * remove whatever we're pointing at.
+ */
+ cbt->compare = 0;
+ ret = btree->type == BTREE_ROW ?
+ __cursor_row_modify(session, cbt, true) :
+ __cursor_col_modify(session, cbt, true);
+ if (ret == 0)
+ goto done;
+
+ /*
+ * The pinned page goes away if we fail for any reason, make
+ * sure there's a local copy of any key. (Restart could still
+ * use the pinned page, but that's an unlikely path.) Re-save
+ * the cursor state: we may retry but eventually fail.
+ */
+ WT_TRET(__cursor_copy_int_key(cursor));
+ __cursor_state_save(cursor, &state);
+ goto err;
+ }
+
+ /*
+ * The pinned page goes away if we do a search, make sure there's a
+ * local copy of any key. Re-save the cursor state: we may retry but
+ * eventually fail.
+ */
+ WT_ERR(__cursor_copy_int_key(cursor));
+ __cursor_state_save(cursor, &state);
+
+retry: WT_ERR(__cursor_func_init(cbt, true));
+
+ if (btree->type == BTREE_ROW) {
+ WT_ERR(__cursor_row_search(session, cbt, NULL, false));
+
+ /* Check whether an update would conflict. */
+ WT_ERR(__curfile_update_check(cbt));
+
+ if (cbt->compare != 0 || !__wt_cursor_valid(cbt, NULL))
+ WT_ERR(WT_NOTFOUND);
+
+ ret = __cursor_row_modify(session, cbt, true);
+ } else {
WT_ERR(__cursor_col_search(session, cbt, NULL));
/*
* If we find a matching record, check whether an update would
* conflict. Do this before checking if the update is visible
- * in __cursor_valid, or we can miss conflict.
+ * in __wt_cursor_valid, or we can miss conflict.
*/
WT_ERR(__curfile_update_check(cbt));
/* Remove the record if it exists. */
- if (cbt->compare != 0 || !__cursor_valid(cbt, NULL)) {
+ if (cbt->compare != 0 || !__wt_cursor_valid(cbt, NULL)) {
if (!__cursor_fix_implicit(btree, cbt))
WT_ERR(WT_NOTFOUND);
/*
@@ -704,19 +954,6 @@ retry: WT_RET(__cursor_func_init(cbt, true));
cbt->recno = cursor->recno;
} else
ret = __cursor_col_modify(session, cbt, true);
- break;
- case BTREE_ROW:
- /* Remove the record if it exists. */
- WT_ERR(__cursor_row_search(session, cbt, NULL, false));
-
- /* Check whether an update would conflict. */
- WT_ERR(__curfile_update_check(cbt));
-
- if (cbt->compare != 0 || !__cursor_valid(cbt, NULL))
- WT_ERR(WT_NOTFOUND);
-
- ret = __cursor_row_modify(session, cbt, true);
- break;
}
err: if (ret == WT_RESTART) {
@@ -724,15 +961,27 @@ err: if (ret == WT_RESTART) {
WT_STAT_DATA_INCR(session, cursor_restart);
goto retry;
}
+
/*
- * If the cursor is configured to overwrite and the record is not
- * found, that is exactly what we want.
+ * If the cursor is configured to overwrite and the record is not found,
+ * that is exactly what we want, return success.
*/
if (F_ISSET(cursor, WT_CURSTD_OVERWRITE) && ret == WT_NOTFOUND)
ret = 0;
- if (ret != 0)
+done: /*
+ * If the cursor was positioned, it stays positioned, point the cursor
+ * at an internal copy of the key. Otherwise, there's no position or
+ * key/value.
+ */
+ if (ret == 0)
+ F_CLR(cursor, WT_CURSTD_KEY_SET | WT_CURSTD_VALUE_SET);
+ if (ret == 0 && positioned)
+ WT_TRET(__wt_key_return(session, cbt));
+ else
WT_TRET(__cursor_reset(cbt));
+ if (ret != 0)
+ __cursor_state_restore(cursor, &state);
return (ret);
}
@@ -745,6 +994,7 @@ int
__wt_btcur_update(WT_CURSOR_BTREE *cbt)
{
WT_BTREE *btree;
+ WT_CURFILE_STATE state;
WT_CURSOR *cursor;
WT_DECL_RET;
WT_SESSION_IMPL *session;
@@ -757,24 +1007,71 @@ __wt_btcur_update(WT_CURSOR_BTREE *cbt)
WT_STAT_DATA_INCR(session, cursor_update);
WT_STAT_DATA_INCRV(session, cursor_update_bytes, cursor->value.size);
+ __cursor_state_save(cursor, &state);
+
if (btree->type == BTREE_ROW)
WT_RET(__cursor_size_chk(session, &cursor->key));
WT_RET(__cursor_size_chk(session, &cursor->value));
+ /* It's no longer possible to bulk-load into the tree. */
+ __cursor_disable_bulk(session, btree);
+
/*
- * The tree is no longer empty: eviction should pay attention to it,
- * and it's no longer possible to bulk-load into it.
+ * If update positioned to an on-page key, the update doesn't require
+ * another search. We don't care about the "overwrite" configuration
+ * because regardless of the overwrite setting, any existing record is
+ * updated, and the record must exist with a positioned cursor. The
+ * cursor won't be positioned on a page with an external key set, but
+ * be sure.
*/
- if (btree->bulk_load_ok) {
- btree->bulk_load_ok = false;
- __wt_btree_evictable(session, true);
+ if (__cursor_page_pinned(cbt) && F_ISSET(cursor, WT_CURSTD_KEY_INT)) {
+ WT_ERR(__wt_txn_autocommit_check(session));
+ /*
+ * The cursor position may not be exact (the cursor's comparison
+ * value not equal to zero). Correct to an exact match so we can
+ * update whatever we're pointing at.
+ */
+ cbt->compare = 0;
+ ret = btree->type == BTREE_ROW ?
+ __cursor_row_modify(session, cbt, false) :
+ __cursor_col_modify(session, cbt, false);
+ if (ret == 0)
+ goto done;
+
+ /*
+ * The pinned page goes away if we fail for any reason, make
+ * sure there's a local copy of any key. (Restart could still
+ * use the pinned page, but that's an unlikely path.) Re-save
+ * the cursor state: we may retry but eventually fail.
+ */
+ WT_TRET(__cursor_copy_int_key(cursor));
+ __cursor_state_save(cursor, &state);
+ goto err;
}
-retry: WT_RET(__cursor_func_init(cbt, true));
+ /*
+ * The pinned page goes away if we do a search, make sure there's a
+ * local copy of any key. Re-save the cursor state: we may retry but
+ * eventually fail.
+ */
+ WT_ERR(__cursor_copy_int_key(cursor));
+ __cursor_state_save(cursor, &state);
- switch (btree->type) {
- case BTREE_COL_FIX:
- case BTREE_COL_VAR:
+retry: WT_ERR(__cursor_func_init(cbt, true));
+
+ if (btree->type == BTREE_ROW) {
+ WT_ERR(__cursor_row_search(session, cbt, NULL, true));
+ /*
+ * If not overwriting, check for conflicts and fail if the key
+ * does not exist.
+ */
+ if (!F_ISSET(cursor, WT_CURSTD_OVERWRITE)) {
+ WT_ERR(__curfile_update_check(cbt));
+ if (cbt->compare != 0 || !__wt_cursor_valid(cbt, NULL))
+ WT_ERR(WT_NOTFOUND);
+ }
+ ret = __cursor_row_modify(session, cbt, false);
+ } else {
WT_ERR(__cursor_col_search(session, cbt, NULL));
/*
@@ -787,25 +1084,12 @@ retry: WT_RET(__cursor_func_init(cbt, true));
*/
if (!F_ISSET(cursor, WT_CURSTD_OVERWRITE)) {
WT_ERR(__curfile_update_check(cbt));
- if ((cbt->compare != 0 || !__cursor_valid(cbt, NULL)) &&
+ if ((cbt->compare != 0 ||
+ !__wt_cursor_valid(cbt, NULL)) &&
!__cursor_fix_implicit(btree, cbt))
WT_ERR(WT_NOTFOUND);
}
ret = __cursor_col_modify(session, cbt, false);
- break;
- case BTREE_ROW:
- WT_ERR(__cursor_row_search(session, cbt, NULL, true));
- /*
- * If not overwriting, check for conflicts and fail if the key
- * does not exist.
- */
- if (!F_ISSET(cursor, WT_CURSTD_OVERWRITE)) {
- WT_ERR(__curfile_update_check(cbt));
- if (cbt->compare != 0 || !__cursor_valid(cbt, NULL))
- WT_ERR(WT_NOTFOUND);
- }
- ret = __cursor_row_modify(session, cbt, false);
- break;
}
err: if (ret == WT_RESTART) {
@@ -822,116 +1106,14 @@ err: if (ret == WT_RESTART) {
* To make this work, we add a field to the btree cursor to pass back a
* pointer to the modify function's allocated update structure.
*/
- if (ret == 0)
+done: if (ret == 0)
WT_TRET(__wt_kv_return(session, cbt, cbt->modify_update));
- if (ret != 0)
+ if (ret != 0) {
WT_TRET(__cursor_reset(cbt));
- return (ret);
-}
-
-/*
- * __wt_btcur_next_random --
- * Move to a random record in the tree. There are two algorithms, one
- * where we select a record at random from the whole tree on each
- * retrieval and one where we first select a record at random from the
- * whole tree, and then subsequently sample forward from that location.
- * The sampling approach allows us to select reasonably uniform random
- * points from unbalanced trees.
- */
-int
-__wt_btcur_next_random(WT_CURSOR_BTREE *cbt)
-{
- WT_BTREE *btree;
- WT_DECL_RET;
- WT_SESSION_IMPL *session;
- WT_UPDATE *upd;
- wt_off_t size;
- uint64_t skip;
-
- session = (WT_SESSION_IMPL *)cbt->iface.session;
- btree = cbt->btree;
-
- /*
- * Only supports row-store: applications can trivially select a random
- * value from a column-store, if there were any reason to do so.
- */
- if (btree->type != BTREE_ROW)
- WT_RET_MSG(session, ENOTSUP,
- "WT_CURSOR.next_random only supported by row-store tables");
-
- WT_STAT_CONN_INCR(session, cursor_next);
- WT_STAT_DATA_INCR(session, cursor_next);
-
- /*
- * If retrieving random values without sampling, or we don't have a
- * page reference, pick a roughly random leaf page in the tree.
- */
- if (cbt->ref == NULL || cbt->next_random_sample_size == 0) {
- /*
- * Skip past the sample size of the leaf pages in the tree
- * between each random key return to compensate for unbalanced
- * trees.
- *
- * Use the underlying file size divided by its block allocation
- * size as our guess of leaf pages in the file (this can be
- * entirely wrong, as it depends on how many pages are in this
- * particular checkpoint, how large the leaf and internal pages
- * really are, and other factors). Then, divide that value by
- * the configured sample size and increment the final result to
- * make sure tiny files don't leave us with a skip value of 0.
- *
- * !!!
- * Ideally, the number would be prime to avoid restart issues.
- */
- if (cbt->next_random_sample_size != 0) {
- WT_ERR(btree->bm->size(btree->bm, session, &size));
- cbt->next_random_leaf_skip = (uint64_t)
- ((size / btree->allocsize) /
- cbt->next_random_sample_size) + 1;
- }
-
- /*
- * Choose a leaf page from the tree.
- */
- WT_ERR(__cursor_func_init(cbt, true));
- WT_WITH_PAGE_INDEX(
- session, ret = __wt_row_random_descent(session, cbt));
- WT_ERR(ret);
- } else {
- /*
- * Read through the tree, skipping leaf pages. Be cautious about
- * the skip count: if the last leaf page skipped was also the
- * last leaf page in the tree, it may be set to zero on return
- * with the end-of-walk condition.
- *
- * Pages read for data sampling aren't "useful"; don't update
- * the read generation of pages already in memory, and if a page
- * is read, set its generation to a low value so it is evicted
- * quickly.
- */
- for (skip =
- cbt->next_random_leaf_skip; cbt->ref == NULL || skip > 0;)
- WT_ERR(__wt_tree_walk_skip(session, &cbt->ref, &skip,
- WT_READ_NO_GEN |
- WT_READ_SKIP_INTL | WT_READ_WONT_NEED));
+ __cursor_state_restore(cursor, &state);
}
- /*
- * Select a random entry from the leaf page. If it's not valid, move to
- * the next entry, if that doesn't work, move to the previous entry.
- */
- WT_ERR(__wt_row_random_leaf(session, cbt));
- if (__cursor_valid(cbt, &upd))
- WT_ERR(__wt_kv_return(session, cbt, upd));
- else {
- if ((ret = __wt_btcur_next(cbt, false)) == WT_NOTFOUND)
- ret = __wt_btcur_prev(cbt, false);
- WT_ERR(ret);
- }
- return (0);
-
-err: WT_TRET(__cursor_reset(cbt));
return (ret);
}
@@ -1060,9 +1242,12 @@ __cursor_truncate(WT_SESSION_IMPL *session,
WT_DECL_RET;
/*
- * First, call the standard cursor remove method to do a full search and
- * re-position the cursor because we don't have a saved copy of the
- * page's write generation information, which we need to remove records.
+ * First, call the cursor search method to re-position the cursor: we
+ * may not have a cursor position (if the higher-level truncate code
+ * switched the cursors to have an "external" cursor key, and because
+ * we don't save a copy of the page's write generation information,
+ * which we need to remove records.
+ *
* Once that's done, we can delete records without a full search, unless
* we encounter a restart error because the page was modified by some
* other thread of control; in that case, repeat the full search to
@@ -1075,20 +1260,19 @@ __cursor_truncate(WT_SESSION_IMPL *session,
* instantiated the end cursor, so we know that page is pinned in memory
* and we can proceed without concern.
*/
-retry: WT_RET(__wt_btcur_remove(start));
+retry: WT_RET(__wt_btcur_search(start));
+ WT_ASSERT(session,
+ F_MASK((WT_CURSOR *)start, WT_CURSTD_KEY_SET) == WT_CURSTD_KEY_INT);
+
+ for (;;) {
+ if ((ret = rmfunc(session, start, 1)) != 0)
+ break;
- /*
- * Reset ret each time through so that we don't loop forever in
- * the cursor equals case.
- */
- for (ret = 0;;) {
if (stop != NULL && __cursor_equals(start, stop))
break;
if ((ret = __wt_btcur_next(start, true)) != 0)
break;
- start->compare = 0; /* Exact match */
- if ((ret = rmfunc(session, start, 1)) != 0)
- break;
+ start->compare = 0; /* Exact match */
}
if (ret == WT_RESTART) {
@@ -1121,29 +1305,32 @@ __cursor_truncate_fix(WT_SESSION_IMPL *session,
* record 37, records 1-36 magically appear. Those records can't be
* deleted, which means we have to ignore already "deleted" records.
*
- * First, call the standard cursor remove method to do a full search and
- * re-position the cursor because we don't have a saved copy of the
- * page's write generation information, which we need to remove records.
+ * First, call the cursor search method to re-position the cursor: we
+ * may not have a cursor position (if the higher-level truncate code
+ * switched the cursors to have an "external" cursor key, and because
+ * we don't save a copy of the page's write generation information,
+ * which we need to remove records.
+ *
* Once that's done, we can delete records without a full search, unless
* we encounter a restart error because the page was modified by some
* other thread of control; in that case, repeat the full search to
* refresh the page's modification information.
*/
-retry: WT_RET(__wt_btcur_remove(start));
- /*
- * Reset ret each time through so that we don't loop forever in
- * the cursor equals case.
- */
- for (ret = 0;;) {
+retry: WT_RET(__wt_btcur_search(start));
+ WT_ASSERT(session,
+ F_MASK((WT_CURSOR *)start, WT_CURSTD_KEY_SET) == WT_CURSTD_KEY_INT);
+
+ for (;;) {
+ value = (const uint8_t *)start->iface.value.data;
+ if (*value != 0 &&
+ (ret = rmfunc(session, start, 1)) != 0)
+ break;
+
if (stop != NULL && __cursor_equals(start, stop))
break;
if ((ret = __wt_btcur_next(start, true)) != 0)
break;
start->compare = 0; /* Exact match */
- value = (const uint8_t *)start->iface.value.data;
- if (*value != 0 &&
- (ret = rmfunc(session, start, 1)) != 0)
- break;
}
if (ret == WT_RESTART) {
@@ -1263,7 +1450,7 @@ __wt_btcur_close(WT_CURSOR_BTREE *cbt, bool lowlevel)
* Skip the usual cursor tear-down in that case.
*/
if (!lowlevel)
- ret = __curfile_leave(cbt);
+ ret = __cursor_reset(cbt);
__wt_buf_free(session, &cbt->_row_key);
__wt_buf_free(session, &cbt->_tmp);
diff --git a/src/btree/bt_debug.c b/src/btree/bt_debug.c
index d507cc0e396..d3f02e29b90 100644
--- a/src/btree/bt_debug.c
+++ b/src/btree/bt_debug.c
@@ -34,7 +34,7 @@ static const /* Output separator */
static int __debug_cell(WT_DBG *, const WT_PAGE_HEADER *, WT_CELL_UNPACK *);
static int __debug_cell_data(
- WT_DBG *, WT_PAGE *, int type, const char *, WT_CELL_UNPACK *);
+ WT_DBG *, WT_PAGE *, int, const char *, WT_CELL_UNPACK *);
static int __debug_col_skip(WT_DBG *, WT_INSERT_HEAD *, const char *, bool);
static int __debug_config(WT_SESSION_IMPL *, WT_DBG *, const char *);
static int __debug_dsk_cell(WT_DBG *, const WT_PAGE_HEADER *);
@@ -64,7 +64,7 @@ __wt_debug_set_verbose(WT_SESSION_IMPL *session, const char *v)
const char *cfg[2] = { NULL, NULL };
char buf[256];
- snprintf(buf, sizeof(buf), "verbose=[%s]", v);
+ WT_RET(__wt_snprintf(buf, sizeof(buf), "verbose=[%s]", v));
cfg[0] = buf;
return (__wt_verbose_config(session, cfg));
}
@@ -87,6 +87,7 @@ __debug_hex_byte(WT_DBG *ds, uint8_t v)
static int
__dmsg_event(WT_DBG *ds, const char *fmt, ...)
{
+ WT_DECL_RET;
WT_ITEM *msg;
WT_SESSION_IMPL *session;
size_t len, space;
@@ -107,8 +108,9 @@ __dmsg_event(WT_DBG *ds, const char *fmt, ...)
p = (char *)msg->mem + msg->size;
space = msg->memsize - msg->size;
va_start(ap, fmt);
- len = (size_t)vsnprintf(p, space, fmt, ap);
+ ret = __wt_vsnprintf_len_set(p, space, &len, fmt, ap);
va_end(ap);
+ WT_RET(ret);
/* Check if there was enough space. */
if (len < space) {
@@ -447,13 +449,14 @@ __debug_tree_shape_info(WT_PAGE *page)
v = page->memory_footprint;
if (v >= WT_GIGABYTE)
- snprintf(buf, sizeof(buf),
+ (void)__wt_snprintf(buf, sizeof(buf),
"(%p %" PRIu64 "G)", (void *)page, v / WT_GIGABYTE);
else if (v >= WT_MEGABYTE)
- snprintf(buf, sizeof(buf),
+ (void)__wt_snprintf(buf, sizeof(buf),
"(%p %" PRIu64 "M)", (void *)page, v / WT_MEGABYTE);
else
- snprintf(buf, sizeof(buf), "(%p %" PRIu64 ")", (void *)page, v);
+ (void)__wt_snprintf(buf, sizeof(buf),
+ "(%p %" PRIu64 ")", (void *)page, v);
return (buf);
}
@@ -652,7 +655,7 @@ __debug_page_metadata(WT_DBG *ds, WT_REF *ref)
page = ref->page;
mod = page->modify;
- WT_RET(ds->f(ds, "%p", (void *)page));
+ WT_RET(ds->f(ds, "%p", (void *)ref));
switch (page->type) {
case WT_PAGE_COL_INT:
@@ -662,25 +665,28 @@ __debug_page_metadata(WT_DBG *ds, WT_REF *ref)
break;
case WT_PAGE_COL_FIX:
WT_RET(ds->f(ds, " recno %" PRIu64, ref->ref_recno));
- entries = page->pg_fix_entries;
+ entries = page->entries;
break;
case WT_PAGE_COL_VAR:
WT_RET(ds->f(ds, " recno %" PRIu64, ref->ref_recno));
- entries = page->pg_var_entries;
+ entries = page->entries;
break;
case WT_PAGE_ROW_INT:
WT_INTL_INDEX_GET(session, page, pindex);
entries = pindex->entries;
break;
case WT_PAGE_ROW_LEAF:
- entries = page->pg_row_entries;
+ entries = page->entries;
break;
WT_ILLEGAL_VALUE(session);
}
WT_RET(ds->f(ds, ": %s\n", __wt_page_type_string(page->type)));
- WT_RET(ds->f(ds,
- "\t" "disk %p, entries %" PRIu32, (void *)page->dsk, entries));
+ WT_RET(ds->f(ds, "\t" "disk %p", (void *)page->dsk));
+ if (page->dsk != NULL)
+ WT_RET(ds->f(
+ ds, ", dsk_mem_size %" PRIu32, page->dsk->mem_size));
+ WT_RET(ds->f(ds, ", entries %" PRIu32, entries));
WT_RET(ds->f(ds,
", %s", __wt_page_is_modified(page) ? "dirty" : "clean"));
WT_RET(ds->f(ds, ", %s", __wt_rwlock_islocked(
@@ -696,8 +702,6 @@ __debug_page_metadata(WT_DBG *ds, WT_REF *ref)
WT_RET(ds->f(ds, ", evict-lru"));
if (F_ISSET_ATOMIC(page, WT_PAGE_OVERFLOW_KEYS))
WT_RET(ds->f(ds, ", overflow-keys"));
- if (F_ISSET_ATOMIC(page, WT_PAGE_SPLIT_BLOCK))
- WT_RET(ds->f(ds, ", split-block"));
if (F_ISSET_ATOMIC(page, WT_PAGE_SPLIT_INSERT))
WT_RET(ds->f(ds, ", split-insert"));
if (F_ISSET_ATOMIC(page, WT_PAGE_UPDATE_IGNORE))
@@ -837,7 +841,8 @@ __debug_page_col_var(WT_DBG *ds, WT_REF *ref)
__wt_cell_unpack(cell, unpack);
rle = __wt_cell_rle(unpack);
}
- snprintf(tag, sizeof(tag), "%" PRIu64 " %" PRIu64, recno, rle);
+ WT_RET(__wt_snprintf(
+ tag, sizeof(tag), "%" PRIu64 " %" PRIu64, recno, rle));
WT_RET(
__debug_cell_data(ds, page, WT_PAGE_COL_VAR, tag, unpack));
diff --git a/src/btree/bt_delete.c b/src/btree/bt_delete.c
index 00e41475de9..b55ad291c5e 100644
--- a/src/btree/bt_delete.c
+++ b/src/btree/bt_delete.c
@@ -318,13 +318,12 @@ __wt_delete_page_instantiate(WT_SESSION_IMPL *session, WT_REF *ref)
* hard case is if a page splits: the update structures might be moved
* to different pages, and we still have to find them all for an abort.
*/
-
if (page_del != NULL)
WT_RET(__wt_calloc_def(
- session, page->pg_row_entries + 1, &page_del->update_list));
+ session, page->entries + 1, &page_del->update_list));
/* Allocate the per-page update array. */
- WT_ERR(__wt_calloc_def(session, page->pg_row_entries, &upd_array));
+ WT_ERR(__wt_calloc_def(session, page->entries, &upd_array));
page->modify->mod_row_update = upd_array;
/*
@@ -332,7 +331,7 @@ __wt_delete_page_instantiate(WT_SESSION_IMPL *session, WT_REF *ref)
* structures, fill in the per-page update array with references to
* deleted items.
*/
- for (i = 0, size = 0; i < page->pg_row_entries; ++i) {
+ for (i = 0, size = 0; i < page->entries; ++i) {
WT_ERR(__wt_calloc_one(session, &upd));
WT_UPDATE_DELETED_SET(upd);
diff --git a/src/btree/bt_discard.c b/src/btree/bt_discard.c
index c2733d6567b..bab7b8145d6 100644
--- a/src/btree/bt_discard.c
+++ b/src/btree/bt_discard.c
@@ -16,13 +16,14 @@ static void __free_skip_array(
WT_SESSION_IMPL *, WT_INSERT_HEAD **, uint32_t, bool);
static void __free_skip_list(WT_SESSION_IMPL *, WT_INSERT *, bool);
static void __free_update(WT_SESSION_IMPL *, WT_UPDATE **, uint32_t, bool);
+static void __page_out_int(WT_SESSION_IMPL *, WT_PAGE **, bool);
/*
- * __wt_ref_out --
+ * __wt_ref_out_int --
* Discard an in-memory page, freeing all memory associated with it.
*/
void
-__wt_ref_out(WT_SESSION_IMPL *session, WT_REF *ref)
+__wt_ref_out_int(WT_SESSION_IMPL *session, WT_REF *ref, bool rewrite)
{
/*
* A version of the page-out function that allows us to make additional
@@ -56,15 +57,25 @@ __wt_ref_out(WT_SESSION_IMPL *session, WT_REF *ref)
}
#endif
- __wt_page_out(session, &ref->page);
+ __page_out_int(session, &ref->page, rewrite);
}
/*
- * __wt_page_out --
+ * __wt_ref_out --
* Discard an in-memory page, freeing all memory associated with it.
*/
void
-__wt_page_out(WT_SESSION_IMPL *session, WT_PAGE **pagep)
+__wt_ref_out(WT_SESSION_IMPL *session, WT_REF *ref)
+{
+ __wt_ref_out_int(session, ref, false);
+}
+
+/*
+ * __page_out_int --
+ * Discard an in-memory page, freeing all memory associated with it.
+ */
+static void
+__page_out_int(WT_SESSION_IMPL *session, WT_PAGE **pagep, bool rewrite)
{
WT_PAGE *page;
WT_PAGE_HEADER *dsk;
@@ -103,7 +114,7 @@ __wt_page_out(WT_SESSION_IMPL *session, WT_PAGE **pagep)
}
/* Update the cache's information. */
- __wt_cache_page_evict(session, page);
+ __wt_cache_page_evict(session, page, rewrite);
dsk = (WT_PAGE_HEADER *)page->dsk;
if (F_ISSET_ATOMIC(page, WT_PAGE_DISK_ALLOC))
@@ -148,6 +159,16 @@ __wt_page_out(WT_SESSION_IMPL *session, WT_PAGE **pagep)
}
/*
+ * __wt_page_out --
+ * Discard an in-memory page, freeing all memory associated with it.
+ */
+void
+__wt_page_out(WT_SESSION_IMPL *session, WT_PAGE **pagep)
+{
+ __page_out_int(session, pagep, false);
+}
+
+/*
* __free_page_modify --
* Discard the page's associated modification structures.
*/
@@ -206,8 +227,7 @@ __free_page_modify(WT_SESSION_IMPL *session, WT_PAGE *page)
if (mod->mod_col_update != NULL)
__free_skip_array(session, mod->mod_col_update,
page->type ==
- WT_PAGE_COL_FIX ? 1 : page->pg_var_entries,
- update_ignore);
+ WT_PAGE_COL_FIX ? 1 : page->entries, update_ignore);
break;
case WT_PAGE_ROW_LEAF:
/*
@@ -219,12 +239,12 @@ __free_page_modify(WT_SESSION_IMPL *session, WT_PAGE *page)
*/
if (mod->mod_row_insert != NULL)
__free_skip_array(session, mod->mod_row_insert,
- page->pg_row_entries + 1, update_ignore);
+ page->entries + 1, update_ignore);
/* Free the update array. */
if (mod->mod_row_update != NULL)
__free_update(session, mod->mod_row_update,
- page->pg_row_entries, update_ignore);
+ page->entries, update_ignore);
break;
}
@@ -332,7 +352,7 @@ static void
__free_page_col_var(WT_SESSION_IMPL *session, WT_PAGE *page)
{
/* Free the RLE lookup array. */
- __wt_free(session, page->pg_var_repeats);
+ __wt_free(session, page->u.col_var.repeats);
}
/*
diff --git a/src/btree/bt_handle.c b/src/btree/bt_handle.c
index 47c7972dd57..d76720b19ae 100644
--- a/src/btree/bt_handle.c
+++ b/src/btree/bt_handle.c
@@ -15,6 +15,44 @@ static int __btree_preload(WT_SESSION_IMPL *);
static int __btree_tree_open_empty(WT_SESSION_IMPL *, bool);
/*
+ * __btree_clear --
+ * Clear a Btree, either on handle discard or re-open.
+ */
+static int
+__btree_clear(WT_SESSION_IMPL *session)
+{
+ WT_BTREE *btree;
+ WT_DECL_RET;
+
+ btree = S2BT(session);
+
+ /*
+ * If the tree hasn't gone through an open/close cycle, there's no
+ * cleanup to be done.
+ */
+ if (!F_ISSET(btree, WT_BTREE_CLOSED))
+ return (0);
+
+ /* Close the Huffman tree. */
+ __wt_btree_huffman_close(session);
+
+ /* Terminate any associated collator. */
+ if (btree->collator_owned && btree->collator->terminate != NULL)
+ WT_TRET(btree->collator->terminate(
+ btree->collator, &session->iface));
+
+ /* Destroy locks. */
+ __wt_rwlock_destroy(session, &btree->ovfl_lock);
+ __wt_spin_destroy(session, &btree->flush_lock);
+
+ /* Free allocated memory. */
+ __wt_free(session, btree->key_format);
+ __wt_free(session, btree->value_format);
+
+ return (ret);
+}
+
+/*
* __wt_btree_open --
* Open a Btree.
*/
@@ -28,12 +66,27 @@ __wt_btree_open(WT_SESSION_IMPL *session, const char *op_cfg[])
WT_DATA_HANDLE *dhandle;
WT_DECL_RET;
size_t root_addr_size;
+ uint32_t mask;
uint8_t root_addr[WT_BTREE_MAX_ADDR_COOKIE];
const char *filename;
bool creation, forced_salvage, readonly;
- dhandle = session->dhandle;
btree = S2BT(session);
+ dhandle = session->dhandle;
+
+ /*
+ * This may be a re-open of an underlying object and we have to clean
+ * up. We can't clear the operation flags, however, they're set by the
+ * connection handle software that called us.
+ */
+ WT_RET(__btree_clear(session));
+
+ mask = F_MASK(btree, WT_BTREE_SPECIAL_FLAGS);
+ memset(btree, 0, sizeof(*btree));
+ btree->flags = mask;
+
+ /* Set the data handle first, our called functions reasonably use it. */
+ btree->dhandle = dhandle;
/* Checkpoint files are readonly. */
readonly = dhandle->checkpoint != NULL ||
@@ -126,6 +179,20 @@ __wt_btree_open(WT_SESSION_IMPL *session, const char *op_cfg[])
}
}
+ /*
+ * Eviction ignores trees until the handle's open flag is set, configure
+ * eviction before that happens.
+ *
+ * Files that can still be bulk-loaded cannot be evicted.
+ * Permanently cache-resident files can never be evicted.
+ * Special operations don't enable eviction. (The underlying commands
+ * may turn on eviction, but it's their decision.)
+ */
+ if (btree->original ||
+ F_ISSET(btree, WT_BTREE_IN_MEMORY | WT_BTREE_REBALANCE |
+ WT_BTREE_SALVAGE | WT_BTREE_UPGRADE | WT_BTREE_VERIFY))
+ WT_ERR(__wt_evict_file_exclusive_on(session));
+
if (0) {
err: WT_TRET(__wt_btree_close(session));
}
@@ -147,7 +214,24 @@ __wt_btree_close(WT_SESSION_IMPL *session)
btree = S2BT(session);
+ /*
+ * The close process isn't the same as discarding the handle: we might
+ * re-open the handle, which isn't a big deal, but the backing blocks
+ * for the handle may not yet have been discarded from the cache, and
+ * eviction uses WT_BTREE structure elements. Free backing resources
+ * but leave the rest alone, and we'll discard the structure when we
+ * discard the data handle.
+ *
+ * Handles can be closed multiple times, ignore all but the first.
+ */
+ if (F_ISSET(btree, WT_BTREE_CLOSED))
+ return (0);
+ F_SET(btree, WT_BTREE_CLOSED);
+
+ /* Discard any underlying block manager resources. */
if ((bm = btree->bm) != NULL) {
+ btree->bm = NULL;
+
/* Unload the checkpoint, unless it's a special command. */
if (!F_ISSET(btree,
WT_BTREE_SALVAGE | WT_BTREE_UPGRADE | WT_BTREE_VERIFY))
@@ -155,33 +239,26 @@ __wt_btree_close(WT_SESSION_IMPL *session)
/* Close the underlying block manager reference. */
WT_TRET(bm->close(bm, session));
-
- btree->bm = NULL;
}
- /* Close the Huffman tree. */
- __wt_btree_huffman_close(session);
-
- /* Destroy locks. */
- __wt_rwlock_destroy(session, &btree->ovfl_lock);
- __wt_spin_destroy(session, &btree->flush_lock);
-
- /* Free allocated memory. */
- __wt_free(session, btree->key_format);
- __wt_free(session, btree->value_format);
+ return (ret);
+}
- if (btree->collator_owned) {
- if (btree->collator->terminate != NULL)
- WT_TRET(btree->collator->terminate(
- btree->collator, &session->iface));
- btree->collator_owned = 0;
- }
- btree->collator = NULL;
- btree->kencryptor = NULL;
+/*
+ * __wt_btree_discard --
+ * Discard a Btree.
+ */
+int
+__wt_btree_discard(WT_SESSION_IMPL *session)
+{
+ WT_BTREE *btree;
+ WT_DECL_RET;
- btree->bulk_load_ok = false;
+ ret = __btree_clear(session);
- F_CLR(btree, WT_BTREE_SPECIAL_FLAGS);
+ btree = S2BT(session);
+ __wt_overwrite_and_free(session, btree);
+ session->dhandle->handle = NULL;
return (ret);
}
@@ -267,9 +344,9 @@ __btree_conf(WT_SESSION_IMPL *session, WT_CKPT *ckpt)
WT_RET(__wt_config_gets(session, cfg, "cache_resident", &cval));
if (cval.val)
- F_SET(btree, WT_BTREE_IN_MEMORY | WT_BTREE_NO_EVICTION);
+ F_SET(btree, WT_BTREE_IN_MEMORY);
else
- F_CLR(btree, WT_BTREE_IN_MEMORY | WT_BTREE_NO_EVICTION);
+ F_CLR(btree, WT_BTREE_IN_MEMORY);
WT_RET(__wt_config_gets(session,
cfg, "ignore_in_memory_cache_size", &cval));
@@ -282,6 +359,14 @@ __btree_conf(WT_SESSION_IMPL *session, WT_CKPT *ckpt)
} else
F_CLR(btree, WT_BTREE_IGNORE_CACHE);
+ /*
+ * The metadata isn't blocked by in-memory cache limits because metadata
+ * "unroll" is performed by updates that are potentially blocked by the
+ * cache-full checks.
+ */
+ if (WT_IS_METADATA(btree->dhandle))
+ F_SET(btree, WT_BTREE_IGNORE_CACHE);
+
WT_RET(__wt_config_gets(session, cfg, "log.enabled", &cval));
if (cval.val)
F_CLR(btree, WT_BTREE_NO_LOGGING);
@@ -359,8 +444,7 @@ __btree_conf(WT_SESSION_IMPL *session, WT_CKPT *ckpt)
}
/* Initialize locks. */
- WT_RET(__wt_rwlock_alloc(
- session, &btree->ovfl_lock, "btree overflow lock"));
+ __wt_rwlock_init(session, &btree->ovfl_lock);
WT_RET(__wt_spin_init(session, &btree->flush_lock, "btree flush"));
btree->checkpointing = WT_CKPT_OFF; /* Not checkpointing */
@@ -483,13 +567,10 @@ __btree_tree_open_empty(WT_SESSION_IMPL *session, bool creation)
/*
* Newly created objects can be used for cursor inserts or for bulk
* loads; set a flag that's cleared when a row is inserted into the
- * tree. Objects being bulk-loaded cannot be evicted, we set it
- * globally, there's no point in searching empty trees for eviction.
+ * tree.
*/
- if (creation) {
- btree->bulk_load_ok = true;
- __wt_btree_evictable(session, false);
- }
+ if (creation)
+ btree->original = 1;
/*
* A note about empty trees: the initial tree is a single root page.
@@ -582,27 +663,6 @@ __wt_btree_new_leaf_page(WT_SESSION_IMPL *session, WT_PAGE **pagep)
}
/*
- * __wt_btree_evictable --
- * Setup or release a cache-resident tree.
- */
-void
-__wt_btree_evictable(WT_SESSION_IMPL *session, bool on)
-{
- WT_BTREE *btree;
-
- btree = S2BT(session);
-
- /* Permanently cache-resident files can never be evicted. */
- if (F_ISSET(btree, WT_BTREE_IN_MEMORY))
- return;
-
- if (on)
- F_CLR(btree, WT_BTREE_NO_EVICTION);
- else
- F_SET(btree, WT_BTREE_NO_EVICTION);
-}
-
-/*
* __btree_preload --
* Pre-load internal pages.
*/
@@ -728,9 +788,16 @@ __btree_page_sizes(WT_SESSION_IMPL *session)
* Get the split percentage (reconciliation splits pages into smaller
* than the maximum page size chunks so we don't split every time a
* new entry is added). Determine how large newly split pages will be.
+ * Set to the minimum, if the read value is less than that.
*/
WT_RET(__wt_config_gets(session, cfg, "split_pct", &cval));
- btree->split_pct = (int)cval.val;
+ if (cval.val < WT_BTREE_MIN_SPLIT_PCT) {
+ btree->split_pct = WT_BTREE_MIN_SPLIT_PCT;
+ WT_RET(__wt_msg(session,
+ "Re-setting split_pct for %s to the minimum allowed of "
+ "%d%%.", session->dhandle->name, WT_BTREE_MIN_SPLIT_PCT));
+ } else
+ btree->split_pct = (int)cval.val;
intl_split_size = __wt_split_page_size(btree, btree->maxintlpage);
leaf_split_size = __wt_split_page_size(btree, btree->maxleafpage);
diff --git a/src/btree/bt_io.c b/src/btree/bt_io.c
index a8645f79dbe..b5e4d52394a 100644
--- a/src/btree/bt_io.c
+++ b/src/btree/bt_io.c
@@ -183,7 +183,7 @@ __wt_bt_write(WT_SESSION_IMPL *session, WT_ITEM *buf,
size_t dst_len, len, result_len, size, src_len;
int compression_failed; /* Extension API, so not a bool. */
uint8_t *dst, *src;
- bool data_checksum, encrypted;
+ bool data_checksum, encrypted, timer;
btree = S2BT(session);
bm = btree->bm;
@@ -216,7 +216,7 @@ __wt_bt_write(WT_SESSION_IMPL *session, WT_ITEM *buf,
&result_len));
WT_ASSERT(session,
dsk->mem_size == result_len + WT_BLOCK_COMPRESS_SKIP);
- ctmp->size = (uint32_t)result_len + WT_BLOCK_COMPRESS_SKIP;
+ ctmp->size = result_len + WT_BLOCK_COMPRESS_SKIP;
ip = ctmp;
} else {
WT_ASSERT(session, dsk->mem_size == buf->size);
@@ -357,7 +357,8 @@ __wt_bt_write(WT_SESSION_IMPL *session, WT_ITEM *buf,
data_checksum = !compressed;
break;
}
- if (!F_ISSET(session, WT_SESSION_INTERNAL))
+ timer = !F_ISSET(session, WT_SESSION_INTERNAL);
+ if (timer)
__wt_epoch(session, &start);
/* Call the block manager to write the block. */
@@ -367,7 +368,7 @@ __wt_bt_write(WT_SESSION_IMPL *session, WT_ITEM *buf,
bm, session, ip, addr, addr_sizep, data_checksum, checkpoint_io));
/* Update some statistics now that the write is done */
- if (!F_ISSET(session, WT_SESSION_INTERNAL)) {
+ if (timer) {
__wt_epoch(session, &stop);
WT_STAT_CONN_INCR(session, cache_write_app_count);
WT_STAT_CONN_INCRV(session, cache_write_app_time,
diff --git a/src/btree/bt_ovfl.c b/src/btree/bt_ovfl.c
index 29ea561db3a..ae0da62af57 100644
--- a/src/btree/bt_ovfl.c
+++ b/src/btree/bt_ovfl.c
@@ -67,11 +67,11 @@ __wt_ovfl_read(WT_SESSION_IMPL *session,
* Acquire the overflow lock, and retest the on-page cell's value inside
* the lock.
*/
- __wt_readlock(session, S2BT(session)->ovfl_lock);
+ __wt_readlock(session, &S2BT(session)->ovfl_lock);
ret = __wt_cell_type_raw(unpack->cell) == WT_CELL_VALUE_OVFL_RM ?
__wt_ovfl_txnc_search(page, unpack->data, unpack->size, store) :
__ovfl_read(session, unpack->data, unpack->size, store);
- __wt_readunlock(session, S2BT(session)->ovfl_lock);
+ __wt_readunlock(session, &S2BT(session)->ovfl_lock);
return (ret);
}
@@ -249,7 +249,7 @@ __wt_ovfl_discard(WT_SESSION_IMPL *session, WT_CELL *cell)
* Acquire the overflow lock to avoid racing with a thread reading the
* backing overflow blocks.
*/
- __wt_writelock(session, btree->ovfl_lock);
+ __wt_writelock(session, &btree->ovfl_lock);
switch (unpack->raw) {
case WT_CELL_KEY_OVFL:
@@ -263,7 +263,7 @@ __wt_ovfl_discard(WT_SESSION_IMPL *session, WT_CELL *cell)
WT_ILLEGAL_VALUE(session);
}
- __wt_writeunlock(session, btree->ovfl_lock);
+ __wt_writeunlock(session, &btree->ovfl_lock);
/* Free the backing disk blocks. */
return (bm->free(bm, session, unpack->data, unpack->size));
diff --git a/src/btree/bt_page.c b/src/btree/bt_page.c
index 7bac7079fe8..f20f6398e37 100644
--- a/src/btree/bt_page.c
+++ b/src/btree/bt_page.c
@@ -67,7 +67,7 @@ __wt_page_alloc(WT_SESSION_IMPL *session,
switch (type) {
case WT_PAGE_COL_FIX:
- page->pg_fix_entries = alloc_entries;
+ page->entries = alloc_entries;
break;
case WT_PAGE_COL_INT:
case WT_PAGE_ROW_INT:
@@ -102,12 +102,12 @@ err: if ((pindex = WT_INTL_INDEX_GET_SAFE(page)) != NULL) {
}
break;
case WT_PAGE_COL_VAR:
- page->pg_var_d = (WT_COL *)((uint8_t *)page + sizeof(WT_PAGE));
- page->pg_var_entries = alloc_entries;
+ page->pg_var = (WT_COL *)((uint8_t *)page + sizeof(WT_PAGE));
+ page->entries = alloc_entries;
break;
case WT_PAGE_ROW_LEAF:
- page->pg_row_d = (WT_ROW *)((uint8_t *)page + sizeof(WT_PAGE));
- page->pg_row_entries = alloc_entries;
+ page->pg_row = (WT_ROW *)((uint8_t *)page + sizeof(WT_PAGE));
+ page->entries = alloc_entries;
break;
WT_ILLEGAL_VALUE(session);
}
@@ -333,9 +333,10 @@ __inmem_col_var(
WT_CELL *cell;
WT_CELL_UNPACK *unpack, _unpack;
const WT_PAGE_HEADER *dsk;
+ size_t size;
uint64_t rle;
- size_t bytes_allocated;
uint32_t i, indx, n, repeat_off;
+ void *p;
btree = S2BT(session);
dsk = page->dsk;
@@ -343,7 +344,6 @@ __inmem_col_var(
repeats = NULL;
repeat_off = 0;
unpack = &_unpack;
- bytes_allocated = 0;
/*
* Walk the page, building references: the page contains unsorted value
@@ -351,7 +351,7 @@ __inmem_col_var(
* (WT_CELL_VALUE_OVFL) or deleted items (WT_CELL_DEL).
*/
indx = 0;
- cip = page->pg_var_d;
+ cip = page->pg_var;
WT_CELL_FOREACH(btree, dsk, cell, unpack, i) {
__wt_cell_unpack(cell, unpack);
WT_COL_PTR_SET(cip, WT_PAGE_DISK_OFFSET(page, cell));
@@ -367,12 +367,14 @@ __inmem_col_var(
if (rle > 1) {
if (repeats == NULL) {
__inmem_col_var_repeats(session, page, &n);
- WT_RET(__wt_realloc_def(session,
- &bytes_allocated, n + 1, &repeats));
+ size = sizeof(WT_COL_VAR_REPEAT) +
+ (n + 1) * sizeof(WT_COL_RLE);
+ WT_RET(__wt_calloc(session, 1, size, &p));
+ *sizep += size;
- page->pg_var_repeats = repeats;
+ page->u.col_var.repeats = p;
page->pg_var_nrepeats = n;
- *sizep += bytes_allocated;
+ repeats = page->pg_var_repeats;
}
repeats[repeat_off].indx = indx;
repeats[repeat_off].recno = recno;
@@ -569,7 +571,7 @@ __inmem_row_leaf(WT_SESSION_IMPL *session, WT_PAGE *page)
unpack = &_unpack;
/* Walk the page, building indices. */
- rip = page->pg_row_d;
+ rip = page->pg_row;
WT_CELL_FOREACH(btree, dsk, cell, unpack, i) {
__wt_cell_unpack(cell, unpack);
switch (unpack->type) {
diff --git a/src/btree/bt_random.c b/src/btree/bt_random.c
new file mode 100644
index 00000000000..c5948ec4ab5
--- /dev/null
+++ b/src/btree/bt_random.c
@@ -0,0 +1,432 @@
+/*-
+ * Copyright (c) 2014-2016 MongoDB, Inc.
+ * Copyright (c) 2008-2014 WiredTiger, Inc.
+ * All rights reserved.
+ *
+ * See the file LICENSE for redistribution information.
+ */
+
+#include "wt_internal.h"
+
+/*
+ * __wt_row_random_leaf --
+ * Return a random key from a row-store leaf page.
+ */
+int
+__wt_row_random_leaf(WT_SESSION_IMPL *session, WT_CURSOR_BTREE *cbt)
+{
+ WT_INSERT *ins, **start, **stop;
+ WT_INSERT_HEAD *ins_head;
+ WT_PAGE *page;
+ uint64_t samples;
+ uint32_t choice, entries, i;
+ int level;
+
+ page = cbt->ref->page;
+ start = stop = NULL; /* [-Wconditional-uninitialized] */
+ entries = 0; /* [-Wconditional-uninitialized] */
+
+ __cursor_pos_clear(cbt);
+
+ /* If the page has disk-based entries, select from them. */
+ if (page->entries != 0) {
+ cbt->compare = 0;
+ cbt->slot = __wt_random(&session->rnd) % page->entries;
+
+ /*
+ * The real row-store search function builds the key, so we
+ * have to as well.
+ */
+ return (__wt_row_leaf_key(session,
+ page, page->pg_row + cbt->slot, cbt->tmp, false));
+ }
+
+ /*
+ * If the tree is new (and not empty), it might have a large insert
+ * list.
+ *
+ * Walk down the list until we find a level with at least 50 entries,
+ * that's where we'll start rolling random numbers. The value 50 is
+ * used to ignore levels with only a few entries, that is, levels which
+ * are potentially badly skewed.
+ */
+ F_SET(cbt, WT_CBT_SEARCH_SMALLEST);
+ if ((ins_head = WT_ROW_INSERT_SMALLEST(page)) == NULL)
+ return (WT_NOTFOUND);
+ for (level = WT_SKIP_MAXDEPTH - 1; level >= 0; --level) {
+ start = &ins_head->head[level];
+ for (entries = 0, stop = start;
+ *stop != NULL; stop = &(*stop)->next[level])
+ ++entries;
+
+ if (entries > 50)
+ break;
+ }
+
+ /*
+ * If it's a tiny list and we went all the way to level 0, correct the
+ * level; entries is correctly set.
+ */
+ if (level < 0)
+ level = 0;
+
+ /*
+ * Step down the skip list levels, selecting a random chunk of the name
+ * space at each level.
+ */
+ for (samples = entries; level > 0; samples += entries) {
+ /*
+ * There are (entries) or (entries + 1) chunks of the name space
+ * considered at each level. They are: between start and the 1st
+ * element, between the 1st and 2nd elements, and so on to the
+ * last chunk which is the name space after the stop element on
+ * the current level. This last chunk of name space may or may
+ * not be there: as we descend the levels of the skip list, this
+ * chunk may appear, depending if the next level down has
+ * entries logically after the stop point in the current level.
+ * We can't ignore those entries: because of the algorithm used
+ * to determine the depth of a skiplist, there may be a large
+ * number of entries "revealed" by descending a level.
+ *
+ * If the next level down has more items after the current stop
+ * point, there are (entries + 1) chunks to consider, else there
+ * are (entries) chunks.
+ */
+ if (*(stop - 1) == NULL)
+ choice = __wt_random(&session->rnd) % entries;
+ else
+ choice = __wt_random(&session->rnd) % (entries + 1);
+
+ if (choice == entries) {
+ /*
+ * We selected the name space after the stop element on
+ * this level. Set the start point to the current stop
+ * point, descend a level and move the stop element to
+ * the end of the list, that is, the end of the newly
+ * discovered name space, counting entries as we go.
+ */
+ start = stop;
+ --start;
+ --level;
+ for (entries = 0, stop = start;
+ *stop != NULL; stop = &(*stop)->next[level])
+ ++entries;
+ } else {
+ /*
+ * We selected another name space on the level. Move the
+ * start pointer the selected number of entries forward
+ * to the start of the selected chunk (if the selected
+ * number is 0, start won't move). Set the stop pointer
+ * to the next element in the list and drop both start
+ * and stop down a level.
+ */
+ for (i = 0; i < choice; ++i)
+ start = &(*start)->next[level];
+ stop = &(*start)->next[level];
+
+ --start;
+ --stop;
+ --level;
+
+ /* Count the entries in the selected name space. */
+ for (entries = 0,
+ ins = *start; ins != *stop; ins = ins->next[level])
+ ++entries;
+ }
+ }
+
+ /*
+ * When we reach the bottom level, entries will already be set. Select
+ * a random entry from the name space and return it.
+ *
+ * It should be impossible for the entries count to be 0 at this point,
+ * but check for it out of paranoia and to quiet static testing tools.
+ */
+ if (entries > 0)
+ entries = __wt_random(&session->rnd) % entries;
+ for (ins = *start; entries > 0; --entries)
+ ins = ins->next[0];
+
+ cbt->ins = ins;
+ cbt->ins_head = ins_head;
+ cbt->compare = 0;
+
+ /*
+ * Random lookups in newly created collections can be slow if a page
+ * consists of a large skiplist. Schedule the page for eviction if we
+ * encounter a large skiplist. This worthwhile because applications
+ * that take a sample often take many samples, so the overhead of
+ * traversing the skip list each time accumulates to real time.
+ */
+ if (samples > 5000)
+ __wt_page_evict_soon(session, cbt->ref);
+
+ return (0);
+}
+
+/*
+ * __wt_random_descent --
+ * Find a random page in a tree for either sampling or eviction.
+ */
+int
+__wt_random_descent(WT_SESSION_IMPL *session, WT_REF **refp, bool eviction)
+{
+ WT_BTREE *btree;
+ WT_DECL_RET;
+ WT_PAGE *page;
+ WT_PAGE_INDEX *pindex;
+ WT_REF *current, *descent;
+ uint32_t flags, i, entries, retry;
+
+ *refp = NULL;
+
+ btree = S2BT(session);
+ current = NULL;
+ retry = 100;
+
+ /* Eviction should not be tapped to do eviction. */
+ if (eviction)
+ flags = WT_READ_CACHE | WT_READ_NO_EVICT | WT_READ_NO_GEN |
+ WT_READ_NO_WAIT | WT_READ_NOTFOUND_OK | WT_READ_RESTART_OK;
+ else
+ flags = WT_READ_RESTART_OK;
+
+ if (0) {
+restart: /*
+ * Discard the currently held page and restart the search from
+ * the root.
+ */
+ WT_RET(__wt_page_release(session, current, flags));
+ }
+
+ /* Search the internal pages of the tree. */
+ current = &btree->root;
+ for (;;) {
+ page = current->page;
+ if (!WT_PAGE_IS_INTERNAL(page))
+ break;
+
+ WT_INTL_INDEX_GET(session, page, pindex);
+ entries = pindex->entries;
+
+ /* Eviction just wants any random child. */
+ if (eviction) {
+ descent = pindex->index[
+ __wt_random(&session->rnd) % entries];
+ goto descend;
+ }
+
+ /*
+ * There may be empty pages in the tree, and they're useless to
+ * us. If we don't find a non-empty page in "entries" random
+ * guesses, take the first non-empty page in the tree. If the
+ * search page contains nothing other than empty pages, restart
+ * from the root some number of times before giving up.
+ *
+ * Random sampling is looking for a key/value pair on a random
+ * leaf page, and so will accept any page that contains a valid
+ * key/value pair, so on-disk is fine, but deleted is not.
+ */
+ descent = NULL;
+ for (i = 0; i < entries; ++i) {
+ descent =
+ pindex->index[__wt_random(&session->rnd) % entries];
+ if (descent->state == WT_REF_MEM ||
+ descent->state == WT_REF_DISK)
+ break;
+ }
+ if (i == entries)
+ for (i = 0; i < entries; ++i) {
+ descent = pindex->index[i];
+ if (descent->state == WT_REF_MEM ||
+ descent->state == WT_REF_DISK)
+ break;
+ }
+ if (i == entries || descent == NULL) {
+ if (--retry > 0)
+ goto restart;
+
+ WT_RET(__wt_page_release(session, current, flags));
+ return (WT_NOTFOUND);
+ }
+
+ /*
+ * Swap the current page for the child page. If the page splits
+ * while we're retrieving it, restart the search at the root.
+ *
+ * On other error, simply return, the swap call ensures we're
+ * holding nothing on failure.
+ */
+descend: if ((ret =
+ __wt_page_swap(session, current, descent, flags)) == 0) {
+ current = descent;
+ continue;
+ }
+ if (eviction && (ret == WT_NOTFOUND || ret == WT_RESTART))
+ break;
+ if (ret == WT_RESTART)
+ goto restart;
+ return (ret);
+ }
+
+ /*
+ * There is no point starting with the root page: the walk will exit
+ * immediately. In that case we aren't holding a hazard pointer so
+ * there is nothing to release.
+ */
+ if (!eviction || !__wt_ref_is_root(current))
+ *refp = current;
+ return (0);
+}
+
+/*
+ * __wt_btcur_next_random --
+ * Move to a random record in the tree. There are two algorithms, one
+ * where we select a record at random from the whole tree on each
+ * retrieval and one where we first select a record at random from the
+ * whole tree, and then subsequently sample forward from that location.
+ * The sampling approach allows us to select reasonably uniform random
+ * points from unbalanced trees.
+ */
+int
+__wt_btcur_next_random(WT_CURSOR_BTREE *cbt)
+{
+ WT_BTREE *btree;
+ WT_CURSOR *cursor;
+ WT_DECL_RET;
+ WT_SESSION_IMPL *session;
+ WT_UPDATE *upd;
+ wt_off_t size;
+ uint64_t n, skip;
+
+ btree = cbt->btree;
+ cursor = &cbt->iface;
+ session = (WT_SESSION_IMPL *)cbt->iface.session;
+
+ /*
+ * Only supports row-store: applications can trivially select a random
+ * value from a column-store, if there were any reason to do so.
+ */
+ if (btree->type != BTREE_ROW)
+ WT_RET_MSG(session, ENOTSUP,
+ "WT_CURSOR.next_random only supported by row-store tables");
+
+ WT_STAT_CONN_INCR(session, cursor_next);
+ WT_STAT_DATA_INCR(session, cursor_next);
+
+ F_CLR(cursor, WT_CURSTD_KEY_SET | WT_CURSTD_VALUE_SET);
+
+#ifdef HAVE_DIAGNOSTIC
+ /*
+ * Under some conditions we end up using the underlying cursor.next to
+ * walk through the object. Since there are multiple calls, we can hit
+ * the cursor-order checks, turn them off.
+ */
+ __wt_cursor_key_order_reset(cbt);
+#endif
+ /*
+ * If we don't have a current position in the tree, or if retrieving
+ * random values without sampling, pick a roughly random leaf page in
+ * the tree and return an entry from it.
+ */
+ if (cbt->ref == NULL || cbt->next_random_sample_size == 0) {
+ WT_ERR(__cursor_func_init(cbt, true));
+ WT_WITH_PAGE_INDEX(session,
+ ret = __wt_random_descent(session, &cbt->ref, false));
+ if (ret == 0)
+ goto random_page_entry;
+
+ /*
+ * Random descent may return not-found: the tree might be empty
+ * or have so many deleted items we didn't find any valid pages.
+ * We can't return WT_NOTFOUND to the application unless a tree
+ * is really empty, fallback to skipping through tree pages.
+ */
+ WT_ERR_NOTFOUND_OK(ret);
+ }
+
+ /*
+ * Cursor through the tree, skipping past the sample size of the leaf
+ * pages in the tree between each random key return to compensate for
+ * unbalanced trees.
+ *
+ * If the random descent attempt failed, we don't have a configured
+ * sample size, use 100 for no particular reason.
+ */
+ if (cbt->next_random_sample_size == 0)
+ cbt->next_random_sample_size = 100;
+
+ /*
+ * If the random descent attempt failed, or it's our first skip attempt,
+ * we haven't yet set the pages to skip, do it now.
+ *
+ * Use the underlying file size divided by its block allocation size as
+ * our guess of leaf pages in the file (this can be entirely wrong, as
+ * it depends on how many pages are in this particular checkpoint, how
+ * large the leaf and internal pages really are, and other factors).
+ * Then, divide that value by the configured sample size and increment
+ * the final result to make sure tiny files don't leave us with a skip
+ * value of 0.
+ *
+ * !!!
+ * Ideally, the number would be prime to avoid restart issues.
+ */
+ if (cbt->next_random_leaf_skip == 0) {
+ WT_ERR(btree->bm->size(btree->bm, session, &size));
+ cbt->next_random_leaf_skip = (uint64_t)
+ ((size / btree->allocsize) /
+ cbt->next_random_sample_size) + 1;
+ }
+
+ /*
+ * Be paranoid about loop termination: first, if the last leaf page
+ * skipped was also the last leaf page in the tree, skip may be set to
+ * zero on return along with the NULL WT_REF end-of-walk condition.
+ * Second, if a tree has no valid pages at all (the condition after
+ * initial creation), we might make no progress at all, or finally, if
+ * a tree has only deleted pages, we'll make progress, but never get a
+ * useful WT_REF. And, of course, the tree can switch from one of these
+ * states to another without warning. Decrement skip regardless of what
+ * is happening in the search, guarantee we eventually quit.
+ *
+ * Pages read for data sampling aren't "useful"; don't update the read
+ * generation of pages already in memory, and if a page is read, set
+ * its generation to a low value so it is evicted quickly.
+ */
+ for (skip = cbt->next_random_leaf_skip; cbt->ref == NULL || skip > 0;) {
+ n = skip;
+ WT_ERR(__wt_tree_walk_skip(session, &cbt->ref, &skip,
+ WT_READ_NO_GEN | WT_READ_SKIP_INTL | WT_READ_WONT_NEED));
+ if (n == skip) {
+ if (skip == 0)
+ break;
+ --skip;
+ }
+ }
+
+ /*
+ * We can't return WT_NOTFOUND to the application unless a tree is
+ * really empty, fallback to a random entry from the first page in the
+ * tree that has anything at all.
+ */
+ if (cbt->ref == NULL)
+ WT_ERR(__wt_btcur_next(cbt, false));
+
+random_page_entry:
+ /*
+ * Select a random entry from the leaf page. If it's not valid, move to
+ * the next entry, if that doesn't work, move to the previous entry.
+ */
+ WT_ERR(__wt_row_random_leaf(session, cbt));
+ if (__wt_cursor_valid(cbt, &upd))
+ WT_ERR(__wt_kv_return(session, cbt, upd));
+ else {
+ if ((ret = __wt_btcur_next(cbt, false)) == WT_NOTFOUND)
+ ret = __wt_btcur_prev(cbt, false);
+ WT_ERR(ret);
+ }
+ return (0);
+
+err: WT_TRET(__cursor_reset(cbt));
+ return (ret);
+}
diff --git a/src/btree/bt_read.c b/src/btree/bt_read.c
index 39f9e1159cb..64874547b9c 100644
--- a/src/btree/bt_read.c
+++ b/src/btree/bt_read.c
@@ -369,6 +369,7 @@ __page_read(WT_SESSION_IMPL *session, WT_REF *ref)
size_t addr_size;
uint32_t previous_state;
const uint8_t *addr;
+ bool timer;
btree = S2BT(session);
page = NULL;
@@ -408,10 +409,11 @@ __page_read(WT_SESSION_IMPL *session, WT_REF *ref)
* There's an address, read or map the backing disk page and build an
* in-memory version of the page.
*/
- if (!F_ISSET(session, WT_SESSION_INTERNAL))
+ timer = !F_ISSET(session, WT_SESSION_INTERNAL);
+ if (timer)
__wt_epoch(session, &start);
WT_ERR(__wt_bt_read(session, &tmp, addr, addr_size));
- if (!F_ISSET(session, WT_SESSION_INTERNAL)) {
+ if (timer) {
__wt_epoch(session, &stop);
WT_STAT_CONN_INCR(session, cache_read_app_count);
WT_STAT_CONN_INCRV(session, cache_read_app_time,
@@ -590,8 +592,9 @@ __wt_page_in_func(WT_SESSION_IMPL *session, WT_REF *ref, uint32_t flags
*/
if (LF_ISSET(WT_READ_NO_EVICT) ||
F_ISSET(session, WT_SESSION_NO_EVICTION) ||
- (F_ISSET(btree, WT_BTREE_NO_EVICTION) &&
- !F_ISSET(btree, WT_BTREE_NO_RECONCILE)))
+ btree->lsm_primary ||
+ (btree->evict_disabled > 0 &&
+ !F_ISSET(btree, WT_BTREE_ALLOW_SPLITS)))
goto skip_evict;
/*
diff --git a/src/btree/bt_rebalance.c b/src/btree/bt_rebalance.c
index 29380459b94..68848c7c8f5 100644
--- a/src/btree/bt_rebalance.c
+++ b/src/btree/bt_rebalance.c
@@ -265,7 +265,7 @@ __rebalance_row_leaf_key(WT_SESSION_IMPL *session,
*/
WT_RET(__wt_bt_read(session, rs->tmp1, addr, addr_len));
WT_RET(__wt_page_inmem(session, NULL, rs->tmp1->data, 0, 0, &page));
- ret = __wt_row_leaf_key_copy(session, page, &page->pg_row_d[0], key);
+ ret = __wt_row_leaf_key_copy(session, page, &page->pg_row[0], key);
__wt_page_out(session, &page);
return (ret);
}
@@ -406,12 +406,10 @@ __wt_bt_rebalance(WT_SESSION_IMPL *session, const char *cfg[])
WT_BTREE *btree;
WT_DECL_RET;
WT_REBALANCE_STUFF *rs, _rstuff;
- bool evict_reset;
WT_UNUSED(cfg);
btree = S2BT(session);
- evict_reset = false;
/*
* If the tree has never been written to disk, we're done, rebalance
@@ -433,14 +431,6 @@ __wt_bt_rebalance(WT_SESSION_IMPL *session, const char *cfg[])
/* Set the internal page tree type. */
rs->type = btree->root.page->type;
- /*
- * Get exclusive access to the file. (Not required, the only page in the
- * cache is the root page, and that cannot be evicted; however, this way
- * eviction ignores the tree entirely.)
- */
- WT_ERR(__wt_evict_file_exclusive_on(session));
- evict_reset = true;
-
/* Recursively walk the tree. */
switch (rs->type) {
case WT_PAGE_ROW_INT:
@@ -471,10 +461,7 @@ __wt_bt_rebalance(WT_SESSION_IMPL *session, const char *cfg[])
btree->root.page = rs->root;
rs->root = NULL;
-err: if (evict_reset)
- __wt_evict_file_exclusive_off(session);
-
- /* Discard any leftover root page we created. */
+err: /* Discard any leftover root page we created. */
if (rs->root != NULL) {
__wt_page_modify_clear(session, rs->root);
__wt_page_out(session, &rs->root);
diff --git a/src/btree/bt_ret.c b/src/btree/bt_ret.c
index 8ef2db67e7b..f17fa1b85d1 100644
--- a/src/btree/bt_ret.c
+++ b/src/btree/bt_ret.c
@@ -9,65 +9,22 @@
#include "wt_internal.h"
/*
- * __wt_kv_return --
- * Return a page referenced key/value pair to the application.
+ * __key_return --
+ * Change the cursor to reference an internal return key.
*/
-int
-__wt_kv_return(WT_SESSION_IMPL *session, WT_CURSOR_BTREE *cbt, WT_UPDATE *upd)
+static inline int
+__key_return(WT_SESSION_IMPL *session, WT_CURSOR_BTREE *cbt)
{
- WT_BTREE *btree;
- WT_CELL *cell;
- WT_CELL_UNPACK unpack;
WT_CURSOR *cursor;
WT_ITEM *tmp;
WT_PAGE *page;
WT_ROW *rip;
- uint8_t v;
-
- btree = S2BT(session);
page = cbt->ref->page;
cursor = &cbt->iface;
- switch (page->type) {
- case WT_PAGE_COL_FIX:
- /*
- * The interface cursor's record has usually been set, but that
- * isn't universally true, specifically, cursor.search_near may
- * call here without first setting the interface cursor.
- */
- cursor->recno = cbt->recno;
-
- /* If the cursor references a WT_UPDATE item, return it. */
- if (upd != NULL) {
- cursor->value.data = WT_UPDATE_DATA(upd);
- cursor->value.size = upd->size;
- return (0);
- }
-
- /* Take the value from the original page. */
- v = __bit_getv_recno(cbt->ref, cursor->recno, btree->bitcnt);
- return (__wt_buf_set(session, &cursor->value, &v, 1));
- case WT_PAGE_COL_VAR:
- /*
- * The interface cursor's record has usually been set, but that
- * isn't universally true, specifically, cursor.search_near may
- * call here without first setting the interface cursor.
- */
- cursor->recno = cbt->recno;
-
- /* If the cursor references a WT_UPDATE item, return it. */
- if (upd != NULL) {
- cursor->value.data = WT_UPDATE_DATA(upd);
- cursor->value.size = upd->size;
- return (0);
- }
-
- /* Take the value from the original page cell. */
- cell = WT_COL_PTR(page, &page->pg_var_d[cbt->slot]);
- break;
- case WT_PAGE_ROW_LEAF:
- rip = &page->pg_row_d[cbt->slot];
+ if (page->type == WT_PAGE_ROW_LEAF) {
+ rip = &page->pg_row[cbt->slot];
/*
* If the cursor references a WT_INSERT item, take its key.
@@ -79,7 +36,10 @@ __wt_kv_return(WT_SESSION_IMPL *session, WT_CURSOR_BTREE *cbt, WT_UPDATE *upd)
if (cbt->ins != NULL) {
cursor->key.data = WT_INSERT_KEY(cbt->ins);
cursor->key.size = WT_INSERT_KEY_SIZE(cbt->ins);
- } else if (cbt->compare == 0) {
+ return (0);
+ }
+
+ if (cbt->compare == 0) {
/*
* If not in an insert list and there's an exact match,
* the row-store search function built the key we want
@@ -97,16 +57,51 @@ __wt_kv_return(WT_SESSION_IMPL *session, WT_CURSOR_BTREE *cbt, WT_UPDATE *upd)
cursor->key.data = cbt->row_key->data;
cursor->key.size = cbt->row_key->size;
- } else
- WT_RET(__wt_row_leaf_key(
- session, page, rip, &cursor->key, false));
-
- /* If the cursor references a WT_UPDATE item, return it. */
- if (upd != NULL) {
- cursor->value.data = WT_UPDATE_DATA(upd);
- cursor->value.size = upd->size;
return (0);
}
+ return (__wt_row_leaf_key(
+ session, page, rip, &cursor->key, false));
+ }
+
+ /*
+ * WT_PAGE_COL_FIX, WT_PAGE_COL_VAR:
+ * The interface cursor's record has usually been set, but that
+ * isn't universally true, specifically, cursor.search_near may call
+ * here without first setting the interface cursor.
+ */
+ cursor->recno = cbt->recno;
+ return (0);
+}
+
+/*
+ * __value_return --
+ * Change the cursor to reference an internal return value.
+ */
+static inline int
+__value_return(WT_SESSION_IMPL *session, WT_CURSOR_BTREE *cbt, WT_UPDATE *upd)
+{
+ WT_BTREE *btree;
+ WT_CELL *cell;
+ WT_CELL_UNPACK unpack;
+ WT_CURSOR *cursor;
+ WT_PAGE *page;
+ WT_ROW *rip;
+ uint8_t v;
+
+ btree = S2BT(session);
+
+ page = cbt->ref->page;
+ cursor = &cbt->iface;
+
+ /* If the cursor references a WT_UPDATE item, return it. */
+ if (upd != NULL) {
+ cursor->value.data = WT_UPDATE_DATA(upd);
+ cursor->value.size = upd->size;
+ return (0);
+ }
+
+ if (page->type == WT_PAGE_ROW_LEAF) {
+ rip = &page->pg_row[cbt->slot];
/* Simple values have their location encoded in the WT_ROW. */
if (__wt_row_leaf_value(page, rip, &cursor->value))
@@ -121,13 +116,65 @@ __wt_kv_return(WT_SESSION_IMPL *session, WT_CURSOR_BTREE *cbt, WT_UPDATE *upd)
cursor->value.size = 0;
return (0);
}
- break;
- WT_ILLEGAL_VALUE(session);
+ __wt_cell_unpack(cell, &unpack);
+ return (__wt_page_cell_data_ref(
+ session, page, &unpack, &cursor->value));
+
}
- /* The value is an on-page cell, unpack and expand it as necessary. */
- __wt_cell_unpack(cell, &unpack);
- WT_RET(__wt_page_cell_data_ref(session, page, &unpack, &cursor->value));
+ if (page->type == WT_PAGE_COL_VAR) {
+ /* Take the value from the original page cell. */
+ cell = WT_COL_PTR(page, &page->pg_var[cbt->slot]);
+ __wt_cell_unpack(cell, &unpack);
+ return (__wt_page_cell_data_ref(
+ session, page, &unpack, &cursor->value));
+ }
+
+ /* WT_PAGE_COL_FIX: Take the value from the original page. */
+ v = __bit_getv_recno(cbt->ref, cursor->recno, btree->bitcnt);
+ return (__wt_buf_set(session, &cursor->value, &v, 1));
+}
+
+/*
+ * __wt_key_return --
+ * Change the cursor to reference an internal return key.
+ */
+int
+__wt_key_return(WT_SESSION_IMPL *session, WT_CURSOR_BTREE *cbt)
+{
+ WT_CURSOR *cursor;
+
+ cursor = &cbt->iface;
+
+ /*
+ * We may already have an internal key, in which case the cursor may
+ * not be set up to get another copy (for example, when we rely on a
+ * search-function result).
+ */
+ F_CLR(cursor, WT_CURSTD_KEY_EXT);
+ if (!F_ISSET(cursor, WT_CURSTD_KEY_INT)) {
+ WT_RET(__key_return(session, cbt));
+ F_SET(cursor, WT_CURSTD_KEY_INT);
+ }
+ return (0);
+}
+
+/*
+ * __wt_kv_return --
+ * Return a page referenced key/value pair to the application.
+ */
+int
+__wt_kv_return(WT_SESSION_IMPL *session, WT_CURSOR_BTREE *cbt, WT_UPDATE *upd)
+{
+ WT_CURSOR *cursor;
+
+ cursor = &cbt->iface;
+
+ WT_RET(__wt_key_return(session, cbt));
+
+ F_CLR(cursor, WT_CURSTD_VALUE_EXT);
+ WT_RET(__value_return(session, cbt, upd));
+ F_SET(cursor, WT_CURSTD_VALUE_INT);
return (0);
}
diff --git a/src/btree/bt_slvg.c b/src/btree/bt_slvg.c
index fde4d4fb9de..165f932afb2 100644
--- a/src/btree/bt_slvg.c
+++ b/src/btree/bt_slvg.c
@@ -166,13 +166,11 @@ __wt_bt_salvage(WT_SESSION_IMPL *session, WT_CKPT *ckptbase, const char *cfg[])
WT_DECL_RET;
WT_STUFF *ss, stuff;
uint32_t i, leaf_cnt;
- bool evict_reset;
WT_UNUSED(cfg);
btree = S2BT(session);
bm = btree->bm;
- evict_reset = false;
WT_CLEAR(stuff);
ss = &stuff;
@@ -184,13 +182,6 @@ __wt_bt_salvage(WT_SESSION_IMPL *session, WT_CKPT *ckptbase, const char *cfg[])
WT_ERR(__wt_scr_alloc(session, 0, &ss->tmp2));
/*
- * Salvage handles its own page eviction; get exclusive access to the
- * file, have eviction ignore the tree entirely.
- */
- WT_ERR(__wt_evict_file_exclusive_on(session));
- evict_reset = true;
-
- /*
* Step 1:
* Inform the underlying block manager that we're salvaging the file.
*/
@@ -350,9 +341,6 @@ err: WT_TRET(bm->salvage_end(bm, session));
if (ss->root_ref.page != NULL)
__wt_ref_out(session, &ss->root_ref);
- if (evict_reset)
- __wt_evict_file_exclusive_off(session);
-
/* Discard the leaf and overflow page memory. */
WT_TRET(__slvg_cleanup(session, ss));
@@ -603,9 +591,9 @@ __slvg_trk_leaf(WT_SESSION_IMPL *session,
*/
WT_ERR(__wt_page_inmem(session, NULL, dsk, 0, 0, &page));
WT_ERR(__wt_row_leaf_key_copy(session,
- page, &page->pg_row_d[0], &trk->row_start));
- WT_ERR(__wt_row_leaf_key_copy(session, page,
- &page->pg_row_d[page->pg_row_entries - 1], &trk->row_stop));
+ page, &page->pg_row[0], &trk->row_start));
+ WT_ERR(__wt_row_leaf_key_copy(session,
+ page, &page->pg_row[page->entries - 1], &trk->row_stop));
__wt_verbose(session, WT_VERB_SALVAGE,
"%s start key %s",
@@ -1235,7 +1223,7 @@ __slvg_col_build_leaf(WT_SESSION_IMPL *session, WT_TRACK *trk, WT_REF *ref)
WT_PAGE *page;
WT_SALVAGE_COOKIE *cookie, _cookie;
uint64_t recno, skip, take;
- uint32_t *entriesp, save_entries;
+ uint32_t save_entries;
cookie = &_cookie;
WT_CLEAR(*cookie);
@@ -1244,11 +1232,8 @@ __slvg_col_build_leaf(WT_SESSION_IMPL *session, WT_TRACK *trk, WT_REF *ref)
WT_RET(__wt_page_in(session, ref, 0));
page = ref->page;
- entriesp = page->type == WT_PAGE_COL_VAR ?
- &page->pg_var_entries : &page->pg_fix_entries;
-
- save_col_var = page->pg_var_d;
- save_entries = *entriesp;
+ save_col_var = page->pg_var;
+ save_entries = page->entries;
/*
* Calculate the number of K/V entries we are going to skip, and
@@ -1303,8 +1288,8 @@ __slvg_col_build_leaf(WT_SESSION_IMPL *session, WT_TRACK *trk, WT_REF *ref)
WT_ERR(__wt_reconcile(session, ref, cookie, WT_VISIBILITY_ERR, NULL));
/* Reset the page. */
- page->pg_var_d = save_col_var;
- *entriesp = save_entries;
+ page->pg_var = save_col_var;
+ page->entries = save_entries;
ret = __wt_page_release(session, ref, 0);
if (ret == 0)
@@ -1973,14 +1958,14 @@ __slvg_row_build_leaf(
/* We should have selected some entries, but not the entire page. */
WT_ASSERT(session,
skip_start + skip_stop > 0 &&
- skip_start + skip_stop < page->pg_row_entries);
+ skip_start + skip_stop < page->entries);
/*
* Take a copy of this page's first key to define the start of
* its range. The key may require processing, otherwise, it's
* a copy from the page.
*/
- rip = page->pg_row_d + skip_start;
+ rip = page->pg_row + skip_start;
WT_ERR(__wt_row_leaf_key(session, page, rip, key, false));
WT_ERR(__wt_row_ikey_incr(
session, ref->home, 0, key->data, key->size, ref));
@@ -1988,14 +1973,14 @@ __slvg_row_build_leaf(
/* Set the referenced flag on overflow pages we're using. */
if (trk->trk_ovfl_cnt != 0)
WT_ERR(__slvg_row_ovfl(session,
- trk, page, skip_start, page->pg_row_entries - skip_stop));
+ trk, page, skip_start, page->entries - skip_stop));
/*
* Change the page to reflect the correct record count: there is no
* need to copy anything on the page itself, the entries value limits
* the number of page items.
*/
- page->pg_row_entries -= skip_stop;
+ page->entries -= skip_stop;
cookie->skip = skip_start;
/*
@@ -2014,7 +1999,7 @@ __slvg_row_build_leaf(
WT_ERR(__wt_reconcile(session, ref, cookie, WT_VISIBILITY_ERR, NULL));
/* Reset the page. */
- page->pg_row_entries += skip_stop;
+ page->entries += skip_stop;
/*
* Discard our hazard pointer and evict the page, updating the
@@ -2081,7 +2066,7 @@ __slvg_row_ovfl(WT_SESSION_IMPL *session,
* We're merging a row-store page, and we took some number of records,
* figure out which (if any) overflow records we used.
*/
- for (rip = page->pg_row_d + start; start < stop; ++start, ++rip) {
+ for (rip = page->pg_row + start; start < stop; ++start, ++rip) {
copy = WT_ROW_KEY_COPY(rip);
(void)__wt_row_leaf_key_info(
page, copy, NULL, &cell, NULL, NULL);
diff --git a/src/btree/bt_split.c b/src/btree/bt_split.c
index fe49f937719..49043c8bab4 100644
--- a/src/btree/bt_split.c
+++ b/src/btree/bt_split.c
@@ -10,8 +10,8 @@
#define WT_MEM_TRANSFER(from_decr, to_incr, len) do { \
size_t __len = (len); \
- from_decr += __len; \
- to_incr += __len; \
+ (from_decr) += __len; \
+ (to_incr) += __len; \
} while (0)
/*
@@ -54,6 +54,16 @@ __split_oldest_gen(WT_SESSION_IMPL *session)
}
/*
+ * __wt_split_obsolete --
+ * Check if it is safe to free / evict based on split generation.
+ */
+bool
+__wt_split_obsolete(WT_SESSION_IMPL *session, uint64_t split_gen)
+{
+ return (split_gen < __split_oldest_gen(session));
+}
+
+/*
* __split_stash_add --
* Add a new entry into the session's split stash list.
*/
@@ -109,7 +119,7 @@ __wt_split_stash_discard(WT_SESSION_IMPL *session)
++i, ++stash) {
if (stash->p == NULL)
continue;
- else if (stash->split_gen >= oldest)
+ if (stash->split_gen >= oldest)
break;
/*
* It's a bad thing if another thread is in this memory after
@@ -177,7 +187,7 @@ __split_safe_free(WT_SESSION_IMPL *session,
exclusive = true;
if (exclusive) {
- __wt_free(session, p);
+ __wt_overwrite_and_free_len(session, p, s);
return (0);
}
@@ -187,7 +197,7 @@ __split_safe_free(WT_SESSION_IMPL *session,
#ifdef HAVE_DIAGNOSTIC
/*
* __split_verify_intl_key_order --
- * Verify the key order on an internal page after a split, diagnostic only.
+ * Verify the key order on an internal page after a split.
*/
static void
__split_verify_intl_key_order(WT_SESSION_IMPL *session, WT_PAGE *page)
@@ -239,6 +249,46 @@ __split_verify_intl_key_order(WT_SESSION_IMPL *session, WT_PAGE *page)
break;
}
}
+
+/*
+ * __split_verify_root --
+ * Verify a root page involved in a split.
+ */
+static int
+__split_verify_root(WT_SESSION_IMPL *session, WT_PAGE *page)
+{
+ WT_DECL_RET;
+ WT_REF *ref;
+
+ /* The split is complete and live, verify all of the pages involved. */
+ __split_verify_intl_key_order(session, page);
+
+ WT_INTL_FOREACH_BEGIN(session, page, ref) {
+ /*
+ * An eviction thread might be attempting to evict the page
+ * (the WT_REF may be WT_REF_LOCKED), or it may be a disk based
+ * page (the WT_REF may be WT_REF_READING), or it may be in
+ * some other state. Acquire a hazard pointer for any
+ * in-memory pages so we know the state of the page.
+ *
+ * Ignore pages not in-memory (deleted, on-disk, being read),
+ * there's no in-memory structure to check.
+ */
+ if ((ret = __wt_page_in(session,
+ ref, WT_READ_CACHE | WT_READ_NO_EVICT)) == WT_NOTFOUND)
+ continue;
+ WT_ERR(ret);
+
+ __split_verify_intl_key_order(session, ref->page);
+
+ WT_ERR(__wt_page_release(session, ref, WT_READ_NO_EVICT));
+ } WT_INTL_FOREACH_END;
+
+ return (0);
+
+err: /* Something really bad just happened. */
+ WT_PANIC_RET(session, ret, "fatal error during page split");
+}
#endif
/*
@@ -390,12 +440,12 @@ __split_ref_move(WT_SESSION_IMPL *session, WT_PAGE *from_home,
}
/*
- * __split_ref_step1 --
+ * __split_ref_prepare --
* Prepare a set of WT_REFs for a move.
*/
static void
-__split_ref_step1(
- WT_SESSION_IMPL *session, WT_PAGE_INDEX *pindex, bool skip_first)
+__split_ref_prepare(WT_SESSION_IMPL *session,
+ WT_PAGE_INDEX *pindex, uint64_t split_gen, bool skip_first)
{
WT_PAGE *child;
WT_REF *child_ref, *ref;
@@ -418,30 +468,25 @@ __split_ref_step1(
child = ref->page;
/*
- * Block eviction and splits in newly created pages.
+ * Block eviction in newly created pages.
*
* Once the split is live, newly created internal pages might be
* evicted and their WT_REF structures freed. If that happened
* before all threads exit the index of the page that previously
* "owned" the WT_REF, a thread might see a freed WT_REF. To
- * ensure that doesn't happen, the newly created page's modify
- * structure has a field with a transaction ID that's checked
- * before any internal page is evicted. Unfortunately, we don't
- * know the correct value until we update the original page's
- * index (we need a transaction ID from after that update), but
- * the act of updating the original page's index is what allows
- * the eviction to happen.
+ * ensure that doesn't happen, the newly created page contains
+ * the current split generation and can't be evicted until
+ * all readers have left the old generation.
*
- * Split blocking was because historic versions of the split
- * code didn't update the WT_REF.home field until after the
- * split was live, so the WT_REF.home fields being updated could
- * split again before the update, there's a race between splits
- * as to which would update them first. The current code updates
- * the WT_REF.home fields before going live (in this function),
- * this shouldn't be an issue, but for now splits remain turned
- * off.
+ * Historic, we also blocked splits in newly created pages
+ * because we didn't update the WT_REF.home field until after
+ * the split was live, so the WT_REF.home fields being updated
+ * could split again before the update, there's a race between
+ * splits as to which would update them first. The current code
+ * updates the WT_REF.home fields before going live (in this
+ * function), this isn't an issue.
*/
- F_SET_ATOMIC(child, WT_PAGE_SPLIT_BLOCK);
+ child->pg_intl_split_gen = split_gen;
/*
* We use a page flag to prevent the child from splitting from
@@ -465,64 +510,6 @@ __split_ref_step1(
}
/*
- * __split_ref_step2 --
- * Allow the newly created children to be evicted or split.
- */
-static int
-__split_ref_step2(
- WT_SESSION_IMPL *session, WT_PAGE_INDEX *pindex, bool skip_first)
-{
- WT_DECL_RET;
- WT_PAGE *child;
- WT_REF *ref;
- uint32_t i;
-
- /*
- * The split has gone live, enable eviction and splits on the newly
- * created internal pages.
- */
- WT_WRITE_BARRIER();
-
- for (i = skip_first ? 1 : 0; i < pindex->entries; ++i) {
- ref = pindex->index[i];
-
- /*
- * We don't hold hazard pointers on created pages, they cannot
- * be evicted because the page-modify transaction value set as
- * they were created prevents eviction. (See above, we reset
- * that value as part of fixing up the page.) But, an eviction
- * thread might be attempting to evict the page (the WT_REF may
- * be WT_REF_LOCKED), or it may be a disk based page (the WT_REF
- * may be WT_REF_READING), or it may be in some other state.
- * Acquire a hazard pointer for any in-memory pages so we know
- * the state of the page. Ignore pages not in-memory (deleted,
- * on-disk, being read), there's no in-memory structure to fix.
- */
- if ((ret = __wt_page_in(session,
- ref, WT_READ_CACHE | WT_READ_NO_EVICT)) == WT_NOTFOUND)
- continue;
- WT_ERR(ret);
-
- child = ref->page;
-
- /* The child can now be evicted or split. */
- F_CLR_ATOMIC(child, WT_PAGE_SPLIT_BLOCK);
-
-#ifdef HAVE_DIAGNOSTIC
- WT_WITH_PAGE_INDEX(session,
- __split_verify_intl_key_order(session, child));
-#endif
-
- WT_ERR(__wt_hazard_clear(session, ref));
- }
-
- return (0);
-
-err: /* Something really bad just happened. */
- WT_PANIC_RET(session, ret, "fatal error resolving a split");
-}
-
-/*
* __split_root --
* Split the root page in-memory, deepening the tree.
*/
@@ -653,8 +640,12 @@ __split_root(WT_SESSION_IMPL *session, WT_PAGE *root)
/* Start making real changes to the tree, errors are fatal. */
complete = WT_ERR_PANIC;
- /* Prepare the WT_REFs for the move. */
- __split_ref_step1(session, alloc_index, false);
+ /*
+ * Prepare the WT_REFs for the move: this requires a stable split
+ * generation to block splits in newly created pages, so get one.
+ */
+ WT_ENTER_PAGE_INDEX(session);
+ __split_ref_prepare(session, alloc_index, session->split_gen, false);
/*
* Confirm the root page's index hasn't moved, then update it, which
@@ -662,20 +653,27 @@ __split_root(WT_SESSION_IMPL *session, WT_PAGE *root)
*/
WT_ASSERT(session, WT_INTL_INDEX_GET_SAFE(root) == pindex);
WT_INTL_INDEX_SET(root, alloc_index);
+ alloc_index = NULL;
+
+ WT_LEAVE_PAGE_INDEX(session);
+
+ /*
+ * Get a generation for this split, mark the root page. This must be
+ * after the new index is swapped into place in order to know that no
+ * readers are looking at the old index.
+ */
+ split_gen = __wt_atomic_addv64(&S2C(session)->split_gen, 1);
+ root->pg_intl_split_gen = split_gen;
#ifdef HAVE_DIAGNOSTIC
WT_WITH_PAGE_INDEX(session,
- __split_verify_intl_key_order(session, root));
+ ret = __split_verify_root(session, root));
+ WT_ERR(ret);
#endif
- /* Finalize the WT_REFs we moved. */
- WT_ERR(__split_ref_step2(session, alloc_index, false));
- /* The split is complete and correct, ignore benign errors. */
+ /* The split is complete and verified, ignore benign errors. */
complete = WT_ERR_IGNORE;
- /* We've installed the allocated page-index, ensure error handling. */
- alloc_index = NULL;
-
/*
* We can't free the previous root's index, there may be threads using
* it. Add to the session's discard list, to be freed once we know no
@@ -686,7 +684,6 @@ __split_root(WT_SESSION_IMPL *session, WT_PAGE *root)
* fails, we don't roll back that change, because threads may already
* be using the new index.
*/
- split_gen = __wt_atomic_addv64(&S2C(session)->split_gen, 1);
size = sizeof(WT_PAGE_INDEX) + pindex->entries * sizeof(WT_REF *);
WT_TRET(__split_safe_free(session, split_gen, false, pindex, size));
root_decr += size;
@@ -846,10 +843,13 @@ __split_parent(WT_SESSION_IMPL *session, WT_REF *ref, WT_REF **ref_new,
WT_INTL_INDEX_SET(parent, alloc_index);
alloc_index = NULL;
-#ifdef HAVE_DIAGNOSTIC
- WT_WITH_PAGE_INDEX(session,
- __split_verify_intl_key_order(session, parent));
-#endif
+ /*
+ * Get a generation for this split, mark the page. This must be after
+ * the new index is swapped into place in order to know that no readers
+ * are looking at the old index.
+ */
+ split_gen = __wt_atomic_addv64(&S2C(session)->split_gen, 1);
+ parent->pg_intl_split_gen = split_gen;
/*
* If discarding the page's original WT_REF field, reset it to split.
@@ -869,16 +869,25 @@ __split_parent(WT_SESSION_IMPL *session, WT_REF *ref, WT_REF **ref_new,
__wt_free(session, ref->page_del);
}
+ /*
+ * Set the discarded WT_REF state to split, ensuring we don't
+ * race with any discard of the WT_REF deleted fields.
+ */
WT_PUBLISH(ref->state, WT_REF_SPLIT);
+
+ /*
+ * Push out the change: not required for correctness, but stops
+ * threads spinning on incorrect page references.
+ */
+ WT_FULL_BARRIER();
}
- /*
- * Push out the changes: not required for correctness, but don't let
- * threads spin on incorrect page references longer than necessary.
- */
- WT_FULL_BARRIER();
+#ifdef HAVE_DIAGNOSTIC
+ WT_WITH_PAGE_INDEX(session,
+ __split_verify_intl_key_order(session, parent));
+#endif
- /* The split is complete and correct, ignore benign errors. */
+ /* The split is complete and verified, ignore benign errors. */
complete = WT_ERR_IGNORE;
/*
@@ -908,7 +917,6 @@ __split_parent(WT_SESSION_IMPL *session, WT_REF *ref, WT_REF **ref_new,
*
* Acquire a new split generation.
*/
- split_gen = __wt_atomic_addv64(&S2C(session)->split_gen, 1);
for (i = 0, deleted_refs = scr->mem; i < deleted_entries; ++i) {
next_ref = pindex->index[deleted_refs[i]];
WT_ASSERT(session, next_ref->state == WT_REF_SPLIT);
@@ -1160,16 +1168,34 @@ __split_internal(WT_SESSION_IMPL *session, WT_PAGE *parent, WT_PAGE *page)
/* Start making real changes to the tree, errors are fatal. */
complete = WT_ERR_PANIC;
- /* Prepare the WT_REFs for the move. */
- __split_ref_step1(session, alloc_index, true);
+ /*
+ * Prepare the WT_REFs for the move: this requires a stable split
+ * generation to block splits in newly created pages, so get one.
+ */
+ WT_ENTER_PAGE_INDEX(session);
+ __split_ref_prepare(session, alloc_index, session->split_gen, true);
/* Split into the parent. */
- WT_ERR(__split_parent(session, page_ref, alloc_index->index,
- alloc_index->entries, parent_incr, false, false));
+ if ((ret = __split_parent(session, page_ref, alloc_index->index,
+ alloc_index->entries, parent_incr, false, false)) == 0) {
+ /*
+ * Confirm the page's index hasn't moved, then update it, which
+ * makes the split visible to threads descending the tree.
+ */
+ WT_ASSERT(session, WT_INTL_INDEX_GET_SAFE(page) == pindex);
+ WT_INTL_INDEX_SET(page, replace_index);
+ }
- /* Confirm the page's index hasn't moved, then update it. */
- WT_ASSERT(session, WT_INTL_INDEX_GET_SAFE(page) == pindex);
- WT_INTL_INDEX_SET(page, replace_index);
+ WT_LEAVE_PAGE_INDEX(session);
+ WT_ERR(ret);
+
+ /*
+ * Get a generation for this split, mark the parent page. This must be
+ * after the new index is swapped into place in order to know that no
+ * readers are looking at the old index.
+ */
+ split_gen = __wt_atomic_addv64(&S2C(session)->split_gen, 1);
+ page->pg_intl_split_gen = split_gen;
#ifdef HAVE_DIAGNOSTIC
WT_WITH_PAGE_INDEX(session,
@@ -1178,19 +1204,10 @@ __split_internal(WT_SESSION_IMPL *session, WT_PAGE *parent, WT_PAGE *page)
__split_verify_intl_key_order(session, page));
#endif
- /* Finalize the WT_REFs we moved. */
- WT_ERR(__split_ref_step2(session, alloc_index, true));
-
- /* The split is complete and correct, ignore benign errors. */
+ /* The split is complete and verified, ignore benign errors. */
complete = WT_ERR_IGNORE;
/*
- * Push out the changes: not required for correctness, but no reason
- * to wait.
- */
- WT_FULL_BARRIER();
-
- /*
* We don't care about the page-index we allocated, all we needed was
* the array of WT_REF structures, which has now been split into the
* parent page.
@@ -1207,7 +1224,6 @@ __split_internal(WT_SESSION_IMPL *session, WT_PAGE *parent, WT_PAGE *page)
* back that change, because threads may already be using the new parent
* page.
*/
- split_gen = __wt_atomic_addv64(&S2C(session)->split_gen, 1);
size = sizeof(WT_PAGE_INDEX) + pindex->entries * sizeof(WT_REF *);
WT_TRET(__split_safe_free(session, split_gen, false, pindex, size));
page_decr += size;
@@ -1284,10 +1300,6 @@ __split_internal_lock(WT_SESSION_IMPL *session, WT_REF *ref, bool trylock,
for (;;) {
parent = ref->home;
- /* Skip pages that aren't ready to split. */
- if (F_ISSET_ATOMIC(parent, WT_PAGE_SPLIT_BLOCK))
- return (EBUSY);
-
if (trylock)
WT_RET(__wt_try_writelock(session, &parent->page_lock));
else
@@ -1770,9 +1782,9 @@ __split_insert(WT_SESSION_IMPL *session, WT_REF *ref)
/* Find the last item on the page. */
if (type == WT_PAGE_ROW_LEAF)
- ins_head = page->pg_row_entries == 0 ?
+ ins_head = page->entries == 0 ?
WT_ROW_INSERT_SMALLEST(page) :
- WT_ROW_INSERT_SLOT(page, page->pg_row_entries - 1);
+ WT_ROW_INSERT_SLOT(page, page->entries - 1);
else
ins_head = WT_COL_APPEND(page);
moved_ins = WT_SKIP_LAST(ins_head);
@@ -1822,7 +1834,7 @@ __split_insert(WT_SESSION_IMPL *session, WT_REF *ref)
key->size = WT_INSERT_KEY_SIZE(ins);
} else
WT_ERR(__wt_row_leaf_key(
- session, page, &page->pg_row_d[0], key, true));
+ session, page, &page->pg_row[0], key, true));
WT_ERR(__wt_row_ikey(session, 0, key->data, key->size, child));
parent_incr += sizeof(WT_IKEY) + key->size;
__wt_scr_free(session, &key);
@@ -2086,8 +2098,7 @@ __wt_split_insert(WT_SESSION_IMPL *session, WT_REF *ref)
WT_PAGE *parent;
bool hazard;
- __wt_verbose(
- session, WT_VERB_SPLIT, "%p: split-insert", (void *)ref->page);
+ __wt_verbose(session, WT_VERB_SPLIT, "%p: split-insert", (void *)ref);
WT_RET(__split_internal_lock(session, ref, true, &parent, &hazard));
if ((ret = __split_insert(session, ref)) != 0) {
@@ -2178,8 +2189,7 @@ __wt_split_multi(WT_SESSION_IMPL *session, WT_REF *ref, int closing)
WT_PAGE *parent;
bool hazard;
- __wt_verbose(
- session, WT_VERB_SPLIT, "%p: split-multi", (void *)ref->page);
+ __wt_verbose(session, WT_VERB_SPLIT, "%p: split-multi", (void *)ref);
WT_RET(__split_internal_lock(session, ref, false, &parent, &hazard));
if ((ret = __split_multi(session, ref, closing)) != 0 || closing) {
@@ -2207,8 +2217,7 @@ __wt_split_reverse(WT_SESSION_IMPL *session, WT_REF *ref)
WT_PAGE *parent;
bool hazard;
- __wt_verbose(
- session, WT_VERB_SPLIT, "%p: reverse-split", (void *)ref->page);
+ __wt_verbose(session, WT_VERB_SPLIT, "%p: reverse-split", (void *)ref);
WT_RET(__split_internal_lock(session, ref, false, &parent, &hazard));
ret = __split_parent(session, ref, NULL, 0, 0, false, true);
@@ -2229,8 +2238,7 @@ __wt_split_rewrite(WT_SESSION_IMPL *session, WT_REF *ref, WT_MULTI *multi)
page = ref->page;
- __wt_verbose(
- session, WT_VERB_SPLIT, "%p: split-rewrite", (void *)ref->page);
+ __wt_verbose(session, WT_VERB_SPLIT, "%p: split-rewrite", (void *)ref);
/*
* This isn't a split: a reconciliation failed because we couldn't write
@@ -2266,7 +2274,7 @@ __wt_split_rewrite(WT_SESSION_IMPL *session, WT_REF *ref, WT_MULTI *multi)
* reconciliation, do it now.
*/
__wt_page_modify_clear(session, page);
- __wt_ref_out(session, ref);
+ __wt_ref_out_int(session, ref, true);
/* Swap the new page into place. */
ref->page = new->page;
diff --git a/src/btree/bt_stat.c b/src/btree/bt_stat.c
index 06428b87f6e..0da0e0807bd 100644
--- a/src/btree/bt_stat.c
+++ b/src/btree/bt_stat.c
@@ -40,6 +40,8 @@ __wt_btree_stat_init(WT_SESSION_IMPL *session, WT_CURSOR_STAT *cst)
WT_STAT_SET(session, stats, btree_maxleafpage, btree->maxleafpage);
WT_STAT_SET(session, stats, btree_maxleafvalue, btree->maxleafvalue);
+ WT_STAT_SET(session, stats, cache_bytes_dirty,
+ __wt_btree_dirty_inuse(session));
WT_STAT_SET(session, stats, cache_bytes_inuse,
__wt_btree_bytes_inuse(session));
@@ -104,8 +106,7 @@ __stat_page(WT_SESSION_IMPL *session, WT_PAGE *page, WT_DSRC_STATS **stats)
switch (page->type) {
case WT_PAGE_COL_FIX:
WT_STAT_INCR(session, stats, btree_column_fix);
- WT_STAT_INCRV(
- session, stats, btree_entries, page->pg_fix_entries);
+ WT_STAT_INCRV(session, stats, btree_entries, page->entries);
break;
case WT_PAGE_COL_INT:
WT_STAT_INCR(session, stats, btree_column_internal);
diff --git a/src/btree/bt_sync.c b/src/btree/bt_sync.c
index 7bf15baa67f..ead6ccc4ac0 100644
--- a/src/btree/bt_sync.c
+++ b/src/btree/bt_sync.c
@@ -78,6 +78,7 @@ __sync_file(WT_SESSION_IMPL *session, WT_CACHE_OP syncop)
uint64_t internal_bytes, internal_pages, leaf_bytes, leaf_pages;
uint64_t oldest_id, saved_pinned_id;
uint32_t flags;
+ bool timer;
conn = S2C(session);
btree = S2BT(session);
@@ -88,7 +89,8 @@ __sync_file(WT_SESSION_IMPL *session, WT_CACHE_OP syncop)
internal_bytes = leaf_bytes = 0;
internal_pages = leaf_pages = 0;
- if (WT_VERBOSE_ISSET(session, WT_VERB_CHECKPOINT))
+ timer = WT_VERBOSE_ISSET(session, WT_VERB_CHECKPOINT);
+ if (timer)
__wt_epoch(session, &start);
switch (syncop) {
@@ -186,9 +188,9 @@ __sync_file(WT_SESSION_IMPL *session, WT_CACHE_OP syncop)
* to grow significantly larger than the configured maximum
* size.
*/
- F_SET(btree, WT_BTREE_NO_RECONCILE);
+ F_SET(btree, WT_BTREE_ALLOW_SPLITS);
ret = __wt_evict_file_exclusive_on(session);
- F_CLR(btree, WT_BTREE_NO_RECONCILE);
+ F_CLR(btree, WT_BTREE_ALLOW_SPLITS);
WT_ERR(ret);
__wt_evict_file_exclusive_off(session);
@@ -242,7 +244,7 @@ __sync_file(WT_SESSION_IMPL *session, WT_CACHE_OP syncop)
break;
}
- if (WT_VERBOSE_ISSET(session, WT_VERB_CHECKPOINT)) {
+ if (timer) {
__wt_epoch(session, &end);
__wt_verbose(session, WT_VERB_CHECKPOINT,
"__sync_file WT_SYNC_%s wrote: %" PRIu64
diff --git a/src/btree/bt_vrfy.c b/src/btree/bt_vrfy.c
index 340f9bb6f0e..7475811adc5 100644
--- a/src/btree/bt_vrfy.c
+++ b/src/btree/bt_vrfy.c
@@ -216,13 +216,11 @@ __wt_verify(WT_SESSION_IMPL *session, const char *cfg[])
ckpt->raw.data, ckpt->raw.size,
root_addr, &root_addr_size, true));
- /*
- * Ignore trees with no root page.
- * Verify, then discard the checkpoint from the cache.
- */
- if (root_addr_size != 0 &&
- (ret = __wt_btree_tree_open(
- session, root_addr, root_addr_size)) == 0) {
+ /* Skip trees with no root page. */
+ if (root_addr_size != 0) {
+ WT_ERR(__wt_btree_tree_open(
+ session, root_addr, root_addr_size));
+
if (WT_VRFY_DUMP(vs))
WT_ERR(__wt_msg(session, "Root: %s %s",
__wt_addr_string(session,
@@ -230,14 +228,38 @@ __wt_verify(WT_SESSION_IMPL *session, const char *cfg[])
__wt_page_type_string(
btree->root.page->type)));
+ __wt_evict_file_exclusive_off(session);
+
+ /* Verify the tree. */
WT_WITH_PAGE_INDEX(session,
ret = __verify_tree(session, &btree->root, vs));
+ /*
+ * We have an exclusive lock on the handle, but we're
+ * swapping root pages in-and-out of that handle, and
+ * there's a race with eviction entering the tree and
+ * seeing an invalid root page. Eviction must work on
+ * trees being verified (else we'd have to do our own
+ * eviction), lock eviction out whenever we're loading
+ * a new root page. This loops works because we are
+ * called with eviction locked out, so we release the
+ * lock at the top of the loop and re-acquire it here.
+ */
+ WT_TRET(__wt_evict_file_exclusive_on(session));
WT_TRET(__wt_cache_op(session, WT_SYNC_DISCARD));
}
/* Unload the checkpoint. */
WT_TRET(bm->checkpoint_unload(bm, session));
+
+ /*
+ * We've finished one checkpoint's verification (verification,
+ * then cache eviction and checkpoint unload): if any errors
+ * occurred, quit. Done this way because otherwise we'd need
+ * at least two more state variables on error, one to know if
+ * we need to discard the tree from the cache and one to know
+ * if we need to unload the checkpoint.
+ */
WT_ERR(ret);
/* Display the tree shape. */
@@ -252,7 +274,7 @@ err: /* Inform the underlying block manager we're done. */
/* Discard the list of checkpoints. */
if (ckptbase != NULL)
- __wt_meta_ckptlist_free(session, ckptbase);
+ __wt_meta_ckptlist_free(session, &ckptbase);
/* Free allocated memory. */
__wt_scr_free(session, &vs->max_key);
@@ -386,7 +408,7 @@ recno_chk: if (recno != vs->record_total + 1)
}
switch (page->type) {
case WT_PAGE_COL_FIX:
- vs->record_total += page->pg_fix_entries;
+ vs->record_total += page->entries;
break;
case WT_PAGE_COL_VAR:
recno = 0;
@@ -614,7 +636,7 @@ __verify_row_leaf_key_order(
* If a tree is empty (just created), it won't have keys; if there
* are no keys, we're done.
*/
- if (page->pg_row_entries == 0)
+ if (page->entries == 0)
return (0);
/*
@@ -624,7 +646,7 @@ __verify_row_leaf_key_order(
*/
if (vs->max_addr->size != 0) {
WT_RET(__wt_row_leaf_key_copy(
- session, page, page->pg_row_d, vs->tmp1));
+ session, page, page->pg_row, vs->tmp1));
/*
* Compare the key against the largest key we've seen so far.
@@ -653,7 +675,7 @@ __verify_row_leaf_key_order(
/* Update the largest key we've seen to the last key on this page. */
WT_RET(__wt_row_leaf_key_copy(session, page,
- page->pg_row_d + (page->pg_row_entries - 1), vs->max_key));
+ page->pg_row + (page->entries - 1), vs->max_key));
(void)__wt_page_addr_string(session, ref, vs->max_addr);
return (0);
diff --git a/src/btree/bt_vrfy_dsk.c b/src/btree/bt_vrfy_dsk.c
index 3a6fd8261ba..a4071c44aee 100644
--- a/src/btree/bt_vrfy_dsk.c
+++ b/src/btree/bt_vrfy_dsk.c
@@ -203,7 +203,8 @@ __verify_dsk_row(
WT_ITEM *last;
enum { FIRST, WAS_KEY, WAS_VALUE } last_cell_type;
void *huffman;
- uint32_t cell_num, cell_type, i, key_cnt, prefix;
+ size_t prefix;
+ uint32_t cell_num, cell_type, i, key_cnt;
uint8_t *end;
int cmp;
@@ -343,8 +344,9 @@ __verify_dsk_row(
if (cell_num > 1 && prefix > last->size)
WT_ERR_VRFY(session,
"key %" PRIu32 " on page at %s has a prefix "
- "compression count of %" PRIu32 ", larger than "
- "the length of the previous key, %" WT_SIZET_FMT,
+ "compression count of %" WT_SIZET_FMT
+ ", larger than the length of the previous key, %"
+ WT_SIZET_FMT,
cell_num, tag, prefix, last->size);
/*
diff --git a/src/btree/bt_walk.c b/src/btree/bt_walk.c
index 049700952ee..86484feb7c9 100644
--- a/src/btree/bt_walk.c
+++ b/src/btree/bt_walk.c
@@ -340,9 +340,7 @@ __tree_walk_internal(WT_SESSION_IMPL *session,
* Take a copy of any held page and clear the return value. Remember
* the hazard pointer we're currently holding.
*
- * We may be passed a pointer to btree->evict_page that we are clearing
- * here. We check when discarding pages that we're not discarding that
- * page, so this clear must be done before the page is released.
+ * Clear the returned value, it makes future error handling easier.
*/
couple = couple_orig = ref = *refp;
*refp = NULL;
@@ -350,16 +348,19 @@ __tree_walk_internal(WT_SESSION_IMPL *session,
/* If no page is active, begin a walk from the start/end of the tree. */
if (ref == NULL) {
restart: /*
- * We can reach here with a NULL or root reference; the release
+ * We can be here with a NULL or root WT_REF; the page release
* function handles them internally, don't complicate this code
* by calling them out.
*/
WT_ERR(__wt_page_release(session, couple, flags));
- couple = couple_orig = ref = &btree->root;
- if (ref->page == NULL)
- goto done;
+ /*
+ * We're not supposed to walk trees without root pages. As this
+ * has not always been the case, assert to debug that change.
+ */
+ WT_ASSERT(session, btree->root.page != NULL);
+ couple = couple_orig = ref = &btree->root;
initial_descent = true;
goto descend;
}
diff --git a/src/btree/col_modify.c b/src/btree/col_modify.c
index a7920da5267..9ccb9728189 100644
--- a/src/btree/col_modify.c
+++ b/src/btree/col_modify.c
@@ -115,9 +115,8 @@ __wt_col_modify(WT_SESSION_IMPL *session, WT_CURSOR_BTREE *cbt,
page, mod->mod_col_update, ins_headp, 1);
ins_headp = &mod->mod_col_update[0];
} else {
- WT_PAGE_ALLOC_AND_SWAP(session,
- page, mod->mod_col_update, ins_headp,
- page->pg_var_entries);
+ WT_PAGE_ALLOC_AND_SWAP(session, page,
+ mod->mod_col_update, ins_headp, page->entries);
ins_headp = &mod->mod_col_update[cbt->slot];
}
diff --git a/src/btree/col_srch.c b/src/btree/col_srch.c
index 64ee9e94f4c..c72d66f8796 100644
--- a/src/btree/col_srch.c
+++ b/src/btree/col_srch.c
@@ -240,8 +240,8 @@ leaf_only:
cbt->compare = 1;
return (0);
}
- if (recno >= current->ref_recno + page->pg_fix_entries) {
- cbt->recno = current->ref_recno + page->pg_fix_entries;
+ if (recno >= current->ref_recno + page->entries) {
+ cbt->recno = current->ref_recno + page->entries;
goto past_end;
} else {
cbt->recno = recno;
@@ -257,8 +257,7 @@ leaf_only:
}
if ((cip = __col_var_search(current, recno, NULL)) == NULL) {
cbt->recno = __col_var_last_recno(current);
- cbt->slot = page->pg_var_entries == 0 ?
- 0 : page->pg_var_entries - 1;
+ cbt->slot = page->entries == 0 ? 0 : page->entries - 1;
goto past_end;
} else {
cbt->recno = recno;
diff --git a/src/btree/row_key.c b/src/btree/row_key.c
index 99ee34a6c5d..032fdf7d897 100644
--- a/src/btree/row_key.c
+++ b/src/btree/row_key.c
@@ -26,7 +26,7 @@ __wt_row_leaf_keys(WT_SESSION_IMPL *session, WT_PAGE *page)
btree = S2BT(session);
- if (page->pg_row_entries == 0) { /* Just checking... */
+ if (page->entries == 0) { /* Just checking... */
F_SET_ATOMIC(page, WT_PAGE_BUILD_KEYS);
return (0);
}
@@ -51,15 +51,15 @@ __wt_row_leaf_keys(WT_SESSION_IMPL *session, WT_PAGE *page)
*/
WT_RET(__wt_scr_alloc(session, 0, &key));
WT_RET(__wt_scr_alloc(session,
- (uint32_t)__bitstr_size(page->pg_row_entries), &tmp));
+ (uint32_t)__bitstr_size(page->entries), &tmp));
memset(tmp->mem, 0, tmp->memsize);
if ((gap = btree->key_gap) == 0)
gap = 1;
- __inmem_row_leaf_slots(tmp->mem, 0, page->pg_row_entries, gap);
+ __inmem_row_leaf_slots(tmp->mem, 0, page->entries, gap);
/* Instantiate the keys. */
- for (rip = page->pg_row_d, i = 0; i < page->pg_row_entries; ++rip, ++i)
+ for (rip = page->pg_row, i = 0; i < page->entries; ++rip, ++i)
if (__bit_test(tmp->mem, i))
WT_ERR(__wt_row_leaf_key_work(
session, page, rip, key, true));
@@ -282,7 +282,7 @@ switch_and_jump: /* Switching to a forward roll. */
* the tracking cache.
*/
if (slot_offset == 0) {
- __wt_readlock(session, btree->ovfl_lock);
+ __wt_readlock(session, &btree->ovfl_lock);
copy = WT_ROW_KEY_COPY(rip);
if (!__wt_row_leaf_key_info(page, copy,
NULL, &cell, &keyb->data, &keyb->size)) {
@@ -290,7 +290,7 @@ switch_and_jump: /* Switching to a forward roll. */
ret = __wt_dsk_cell_data_ref(session,
WT_PAGE_ROW_LEAF, unpack, keyb);
}
- __wt_readunlock(session, btree->ovfl_lock);
+ __wt_readunlock(session, &btree->ovfl_lock);
WT_ERR(ret);
break;
}
diff --git a/src/btree/row_modify.c b/src/btree/row_modify.c
index a1c214e5b8b..b1a81ca3d9f 100644
--- a/src/btree/row_modify.c
+++ b/src/btree/row_modify.c
@@ -85,9 +85,8 @@ __wt_row_modify(WT_SESSION_IMPL *session, WT_CURSOR_BTREE *cbt,
if (cbt->compare == 0) {
if (cbt->ins == NULL) {
/* Allocate an update array as necessary. */
- WT_PAGE_ALLOC_AND_SWAP(session,
- page, mod->mod_row_update,
- upd_entry, page->pg_row_entries);
+ WT_PAGE_ALLOC_AND_SWAP(session, page,
+ mod->mod_row_update, upd_entry, page->entries);
/* Set the WT_UPDATE array reference. */
upd_entry = &mod->mod_row_update[cbt->slot];
@@ -147,10 +146,10 @@ __wt_row_modify(WT_SESSION_IMPL *session, WT_CURSOR_BTREE *cbt,
* slot. That's hard, so we set a flag.
*/
WT_PAGE_ALLOC_AND_SWAP(session, page,
- mod->mod_row_insert, ins_headp, page->pg_row_entries + 1);
+ mod->mod_row_insert, ins_headp, page->entries + 1);
ins_slot = F_ISSET(cbt, WT_CBT_SEARCH_SMALLEST) ?
- page->pg_row_entries: cbt->slot;
+ page->entries: cbt->slot;
ins_headp = &mod->mod_row_insert[ins_slot];
/* Allocate the WT_INSERT_HEAD structure as necessary. */
diff --git a/src/btree/row_srch.c b/src/btree/row_srch.c
index d4e82c458d4..9c3d467340e 100644
--- a/src/btree/row_srch.c
+++ b/src/btree/row_srch.c
@@ -486,14 +486,14 @@ leaf_only:
if (insert && descend_right) {
cbt->append_tree = 1;
- if (page->pg_row_entries == 0) {
- cbt->slot = WT_ROW_SLOT(page, page->pg_row_d);
+ if (page->entries == 0) {
+ cbt->slot = WT_ROW_SLOT(page, page->pg_row);
F_SET(cbt, WT_CBT_SEARCH_SMALLEST);
ins_head = WT_ROW_INSERT_SMALLEST(page);
} else {
cbt->slot = WT_ROW_SLOT(page,
- page->pg_row_d + (page->pg_row_entries - 1));
+ page->pg_row + (page->entries - 1));
ins_head = WT_ROW_INSERT_SLOT(page, cbt->slot);
}
@@ -511,11 +511,11 @@ leaf_only:
* doing the tests and error handling inside the loop costs about 5%.
*/
base = 0;
- limit = page->pg_row_entries;
+ limit = page->entries;
if (collator == NULL && srch_key->size <= WT_COMPARE_SHORT_MAXLEN)
for (; limit != 0; limit >>= 1) {
indx = base + (limit >> 1);
- rip = page->pg_row_d + indx;
+ rip = page->pg_row + indx;
WT_ERR(
__wt_row_leaf_key(session, page, rip, item, true));
@@ -529,7 +529,7 @@ leaf_only:
else if (collator == NULL)
for (; limit != 0; limit >>= 1) {
indx = base + (limit >> 1);
- rip = page->pg_row_d + indx;
+ rip = page->pg_row + indx;
WT_ERR(
__wt_row_leaf_key(session, page, rip, item, true));
@@ -547,7 +547,7 @@ leaf_only:
else
for (; limit != 0; limit >>= 1) {
indx = base + (limit >> 1);
- rip = page->pg_row_d + indx;
+ rip = page->pg_row + indx;
WT_ERR(
__wt_row_leaf_key(session, page, rip, item, true));
@@ -591,13 +591,13 @@ leaf_match: cbt->compare = 0;
*/
if (base == 0) {
cbt->compare = 1;
- cbt->slot = WT_ROW_SLOT(page, page->pg_row_d);
+ cbt->slot = WT_ROW_SLOT(page, page->pg_row);
F_SET(cbt, WT_CBT_SEARCH_SMALLEST);
ins_head = WT_ROW_INSERT_SMALLEST(page);
} else {
cbt->compare = -1;
- cbt->slot = WT_ROW_SLOT(page, page->pg_row_d + (base - 1));
+ cbt->slot = WT_ROW_SLOT(page, page->pg_row + (base - 1));
ins_head = WT_ROW_INSERT_SLOT(page, cbt->slot);
}
@@ -623,215 +623,3 @@ leaf_match: cbt->compare = 0;
err: WT_TRET(__wt_page_release(session, current, 0));
return (ret);
}
-
-/*
- * __wt_row_random_leaf --
- * Return a random key from a row-store leaf page.
- */
-int
-__wt_row_random_leaf(WT_SESSION_IMPL *session, WT_CURSOR_BTREE *cbt)
-{
- WT_INSERT *ins, **start, **stop;
- WT_INSERT_HEAD *ins_head;
- WT_PAGE *page;
- uint64_t samples;
- uint32_t choice, entries, i;
- int level;
-
- page = cbt->ref->page;
- start = stop = NULL; /* [-Wconditional-uninitialized] */
- entries = 0; /* [-Wconditional-uninitialized] */
-
- __cursor_pos_clear(cbt);
-
- /* If the page has disk-based entries, select from them. */
- if (page->pg_row_entries != 0) {
- cbt->compare = 0;
- cbt->slot = __wt_random(&session->rnd) % page->pg_row_entries;
-
- /*
- * The real row-store search function builds the key, so we
- * have to as well.
- */
- return (__wt_row_leaf_key(session,
- page, page->pg_row_d + cbt->slot, cbt->tmp, false));
- }
-
- /*
- * If the tree is new (and not empty), it might have a large insert
- * list.
- *
- * Walk down the list until we find a level with at least 50 entries,
- * that's where we'll start rolling random numbers. The value 50 is
- * used to ignore levels with only a few entries, that is, levels which
- * are potentially badly skewed.
- */
- F_SET(cbt, WT_CBT_SEARCH_SMALLEST);
- if ((ins_head = WT_ROW_INSERT_SMALLEST(page)) == NULL)
- return (WT_NOTFOUND);
- for (level = WT_SKIP_MAXDEPTH - 1; level >= 0; --level) {
- start = &ins_head->head[level];
- for (entries = 0, stop = start;
- *stop != NULL; stop = &(*stop)->next[level])
- ++entries;
-
- if (entries > 50)
- break;
- }
-
- /*
- * If it's a tiny list and we went all the way to level 0, correct the
- * level; entries is correctly set.
- */
- if (level < 0)
- level = 0;
-
- /*
- * Step down the skip list levels, selecting a random chunk of the name
- * space at each level.
- */
- for (samples = entries; level > 0; samples += entries) {
- /*
- * There are (entries) or (entries + 1) chunks of the name space
- * considered at each level. They are: between start and the 1st
- * element, between the 1st and 2nd elements, and so on to the
- * last chunk which is the name space after the stop element on
- * the current level. This last chunk of name space may or may
- * not be there: as we descend the levels of the skip list, this
- * chunk may appear, depending if the next level down has
- * entries logically after the stop point in the current level.
- * We can't ignore those entries: because of the algorithm used
- * to determine the depth of a skiplist, there may be a large
- * number of entries "revealed" by descending a level.
- *
- * If the next level down has more items after the current stop
- * point, there are (entries + 1) chunks to consider, else there
- * are (entries) chunks.
- */
- if (*(stop - 1) == NULL)
- choice = __wt_random(&session->rnd) % entries;
- else
- choice = __wt_random(&session->rnd) % (entries + 1);
-
- if (choice == entries) {
- /*
- * We selected the name space after the stop element on
- * this level. Set the start point to the current stop
- * point, descend a level and move the stop element to
- * the end of the list, that is, the end of the newly
- * discovered name space, counting entries as we go.
- */
- start = stop;
- --start;
- --level;
- for (entries = 0, stop = start;
- *stop != NULL; stop = &(*stop)->next[level])
- ++entries;
- } else {
- /*
- * We selected another name space on the level. Move the
- * start pointer the selected number of entries forward
- * to the start of the selected chunk (if the selected
- * number is 0, start won't move). Set the stop pointer
- * to the next element in the list and drop both start
- * and stop down a level.
- */
- for (i = 0; i < choice; ++i)
- start = &(*start)->next[level];
- stop = &(*start)->next[level];
-
- --start;
- --stop;
- --level;
-
- /* Count the entries in the selected name space. */
- for (entries = 0,
- ins = *start; ins != *stop; ins = ins->next[level])
- ++entries;
- }
- }
-
- /*
- * When we reach the bottom level, entries will already be set. Select
- * a random entry from the name space and return it.
- *
- * It should be impossible for the entries count to be 0 at this point,
- * but check for it out of paranoia and to quiet static testing tools.
- */
- if (entries > 0)
- entries = __wt_random(&session->rnd) % entries;
- for (ins = *start; entries > 0; --entries)
- ins = ins->next[0];
-
- cbt->ins = ins;
- cbt->ins_head = ins_head;
- cbt->compare = 0;
-
- /*
- * Random lookups in newly created collections can be slow if a page
- * consists of a large skiplist. Schedule the page for eviction if we
- * encounter a large skiplist. This worthwhile because applications
- * that take a sample often take many samples, so the overhead of
- * traversing the skip list each time accumulates to real time.
- */
- if (samples > 5000)
- __wt_page_evict_soon(session, cbt->ref);
-
- return (0);
-}
-
-/*
- * __wt_row_random_descent --
- * Find a random leaf page in a row-store tree.
- */
-int
-__wt_row_random_descent(WT_SESSION_IMPL *session, WT_CURSOR_BTREE *cbt)
-{
- WT_BTREE *btree;
- WT_DECL_RET;
- WT_PAGE *page;
- WT_PAGE_INDEX *pindex;
- WT_REF *current, *descent;
-
- btree = S2BT(session);
- current = NULL;
-
- if (0) {
-restart: /*
- * Discard the currently held page and restart the search from
- * the root.
- */
- WT_RET(__wt_page_release(session, current, 0));
- }
-
- /* Search the internal pages of the tree. */
- current = &btree->root;
- for (;;) {
- page = current->page;
- if (page->type != WT_PAGE_ROW_INT)
- break;
-
- WT_INTL_INDEX_GET(session, page, pindex);
- descent = pindex->index[
- __wt_random(&session->rnd) % pindex->entries];
-
- /*
- * Swap the current page for the child page. If the page splits
- * while we're retrieving it, restart the search at the root.
- *
- * On other error, simply return, the swap call ensures we're
- * holding nothing on failure.
- */
- if ((ret = __wt_page_swap(
- session, current, descent, WT_READ_RESTART_OK)) == 0) {
- current = descent;
- continue;
- }
- if (ret == WT_RESTART)
- goto restart;
- return (ret);
- }
-
- cbt->ref = current;
- return (0);
-}