summaryrefslogtreecommitdiff
path: root/src/third_party/wiredtiger/src/session/session_api.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/third_party/wiredtiger/src/session/session_api.c')
-rw-r--r--src/third_party/wiredtiger/src/session/session_api.c297
1 files changed, 249 insertions, 48 deletions
diff --git a/src/third_party/wiredtiger/src/session/session_api.c b/src/third_party/wiredtiger/src/session/session_api.c
index be3a5d93473..f58fa4319e6 100644
--- a/src/third_party/wiredtiger/src/session/session_api.c
+++ b/src/third_party/wiredtiger/src/session/session_api.c
@@ -50,6 +50,67 @@ __wt_session_reset_cursors(WT_SESSION_IMPL *session, bool free_buffers)
}
/*
+ * __wt_session_cursor_cache_sweep --
+ * Sweep the cursor cache.
+ */
+int
+__wt_session_cursor_cache_sweep(WT_SESSION_IMPL *session)
+{
+ WT_CURSOR *cursor, *cursor_tmp;
+ WT_CURSOR_LIST *cached_list;
+ WT_DECL_RET;
+ uint32_t position;
+ int i, t_ret, nbuckets, nexamined, nclosed;
+ bool productive;
+
+ if (!F_ISSET(session, WT_SESSION_CACHE_CURSORS))
+ return (0);
+
+ position = session->cursor_sweep_position;
+ productive = true;
+ nbuckets = nexamined = nclosed = 0;
+
+ /* Turn off caching so that cursor close doesn't try to cache. */
+ F_CLR(session, WT_SESSION_CACHE_CURSORS);
+ for (i = 0; i < WT_SESSION_CURSOR_SWEEP_MAX && productive; i++) {
+ ++nbuckets;
+ cached_list = &session->cursor_cache[position];
+ position = (position + 1) % WT_HASH_ARRAY_SIZE;
+ TAILQ_FOREACH_SAFE(cursor, cached_list, q, cursor_tmp) {
+ /*
+ * First check to see if the cursor could be reopened.
+ */
+ ++nexamined;
+ t_ret = cursor->reopen(cursor, true);
+ if (t_ret != 0) {
+ WT_TRET_NOTFOUND_OK(t_ret);
+ WT_TRET_NOTFOUND_OK(
+ cursor->reopen(cursor, false));
+ WT_TRET(cursor->close(cursor));
+ ++nclosed;
+ }
+ }
+
+ /*
+ * We continue sweeping as long as we have some good average
+ * productivity. At a minimum, we look at two buckets.
+ */
+ productive = (nclosed >= i);
+ }
+
+ session->cursor_sweep_position = position;
+ session->cursor_sweep_countdown = WT_SESSION_CURSOR_SWEEP_COUNTDOWN;
+ F_SET(session, WT_SESSION_CACHE_CURSORS);
+
+ WT_STAT_CONN_INCR(session, cursor_sweep);
+ WT_STAT_CONN_INCRV(session, cursor_sweep_buckets, nbuckets);
+ WT_STAT_CONN_INCRV(session, cursor_sweep_examined, nexamined);
+ WT_STAT_CONN_INCRV(session, cursor_sweep_closed, nclosed);
+
+ return (ret);
+}
+
+/*
* __wt_session_copy_values --
* Copy values into all positioned cursors, so that they don't keep
* transaction IDs pinned.
@@ -168,6 +229,55 @@ __session_clear(WT_SESSION_IMPL *session)
}
/*
+ * __session_close_cursors --
+ * Close all cursors in a list.
+ */
+static int
+__session_close_cursors(WT_SESSION_IMPL *session, WT_CURSOR_LIST *cursors)
+{
+ WT_CURSOR *cursor, *cursor_tmp;
+ WT_DECL_RET;
+
+ /* Close all open cursors. */
+ WT_TAILQ_SAFE_REMOVE_BEGIN(cursor, cursors, q, cursor_tmp) {
+ if (F_ISSET(cursor, WT_CURSTD_CACHED))
+ /*
+ * Put the cached cursor in an open state
+ * that allows it to be closed.
+ */
+ WT_TRET_NOTFOUND_OK(cursor->reopen(cursor, false));
+ else if (session->event_handler->handle_close != NULL &&
+ !WT_STREQ(cursor->internal_uri, WT_LAS_URI))
+ /*
+ * Notify the user that we are closing the cursor
+ * handle via the registered close callback.
+ */
+ WT_TRET(session->event_handler->handle_close(
+ session->event_handler, &session->iface, cursor));
+
+ WT_TRET(cursor->close(cursor));
+ } WT_TAILQ_SAFE_REMOVE_END
+
+ return (ret);
+}
+
+/*
+ * __session_close_cached_cursors --
+ * Fully close all cached cursors.
+ */
+static int
+__session_close_cached_cursors(WT_SESSION_IMPL *session)
+{
+ WT_DECL_RET;
+ int i;
+
+ for (i = 0; i < WT_HASH_ARRAY_SIZE; i++)
+ WT_TRET(__session_close_cursors(session,
+ &session->cursor_cache[i]));
+ return (ret);
+}
+
+/*
* __session_close --
* WT_SESSION->close method.
*/
@@ -175,16 +285,18 @@ static int
__session_close(WT_SESSION *wt_session, const char *config)
{
WT_CONNECTION_IMPL *conn;
- WT_CURSOR *cursor, *cursor_tmp;
WT_DECL_RET;
WT_SESSION_IMPL *session;
conn = (WT_CONNECTION_IMPL *)wt_session->connection;
session = (WT_SESSION_IMPL *)wt_session;
- SESSION_API_CALL(session, close, config, cfg);
+ SESSION_API_CALL_PREPARE_ALLOWED(session, close, config, cfg);
WT_UNUSED(cfg);
+ /* Close all open cursors while the cursor cache is disabled. */
+ F_CLR(session, WT_SESSION_CACHE_CURSORS);
+
/* Rollback any active transaction. */
if (F_ISSET(&session->txn, WT_TXN_RUNNING))
WT_TRET(__session_rollback_transaction(wt_session, NULL));
@@ -197,17 +309,8 @@ __session_close(WT_SESSION *wt_session, const char *config)
__wt_txn_release_snapshot(session);
/* Close all open cursors. */
- WT_TAILQ_SAFE_REMOVE_BEGIN(cursor, &session->cursors, q, cursor_tmp) {
- /*
- * Notify the user that we are closing the cursor handle
- * via the registered close callback.
- */
- if (session->event_handler->handle_close != NULL &&
- !WT_STREQ(cursor->internal_uri, WT_LAS_URI))
- WT_TRET(session->event_handler->handle_close(
- session->event_handler, wt_session, cursor));
- WT_TRET(cursor->close(cursor));
- } WT_TAILQ_SAFE_REMOVE_END
+ WT_TRET(__session_close_cursors(session, &session->cursors));
+ WT_TRET(__session_close_cached_cursors(session));
WT_ASSERT(session, session->ncursors == 0);
@@ -224,17 +327,14 @@ __session_close(WT_SESSION *wt_session, const char *config)
__wt_txn_destroy(session);
/*
- * Close the file where we tracked long operations. Do this before
- * releasing resources, as we do scratch buffer management when we flush
- * optrack buffers to disk
+ * Close the file where we tracked long operations. Do this before
+ * releasing resources, as we do scratch buffer management when we
+ * flush optrack buffers to disk.
*/
if (F_ISSET(conn, WT_CONN_OPTRACK)) {
if (session->optrackbuf_ptr > 0) {
- WT_IGNORE_RET((int)__wt_optrack_flush_buffer(session));
- WT_IGNORE_RET(__wt_close(session,
- &session->optrack_fh));
- /* Indicate that the file is closed */
- session->optrack_fh = NULL;
+ __wt_optrack_flush_buffer(session);
+ WT_TRET(__wt_close(session, &session->optrack_fh));
}
/* Free the operation tracking buffer */
@@ -291,7 +391,12 @@ __session_reconfigure(WT_SESSION *wt_session, const char *config)
WT_SESSION_IMPL *session;
session = (WT_SESSION_IMPL *)wt_session;
- SESSION_API_CALL(session, reconfigure, config, cfg);
+ /*
+ * Indicated as allowed in prepared state, even though not allowed,
+ * so that running transaction check below take precedence.
+ */
+ SESSION_API_CALL_PREPARE_ALLOWED(
+ session, reconfigure, config, cfg);
/*
* Note that this method only checks keys that are passed in by the
@@ -315,6 +420,17 @@ __session_reconfigure(WT_SESSION *wt_session, const char *config)
}
WT_ERR_NOTFOUND_OK(ret);
+ ret = __wt_config_getones(session, config, "cache_cursors", &cval);
+ if (ret == 0) {
+ if (cval.val)
+ F_SET(session, WT_SESSION_CACHE_CURSORS);
+ else {
+ F_CLR(session, WT_SESSION_CACHE_CURSORS);
+ WT_ERR(__session_close_cached_cursors(session));
+ }
+ }
+ WT_ERR_NOTFOUND_OK(ret);
+
err: API_END_RET_NOTFOUND_MAP(session, ret);
}
@@ -417,6 +533,16 @@ __session_open_cursor_int(WT_SESSION_IMPL *session, const char *uri,
if (*cursorp == NULL)
return (__wt_bad_object_type(session, uri));
+ if (owner != NULL) {
+ /*
+ * We support caching simple cursors that have no
+ * children. If this cursor is a child, we're not going
+ * to cache this child or its parent.
+ */
+ F_CLR(owner, WT_CURSTD_CACHEABLE);
+ F_CLR(*cursorp, WT_CURSTD_CACHEABLE);
+ }
+
/*
* When opening simple tables, the table code calls this function on the
* underlying data source, in which case the application's URI has been
@@ -439,6 +565,15 @@ int
__wt_open_cursor(WT_SESSION_IMPL *session,
const char *uri, WT_CURSOR *owner, const char *cfg[], WT_CURSOR **cursorp)
{
+ WT_DECL_RET;
+
+ if (F_ISSET(session, WT_SESSION_CACHE_CURSORS)) {
+ if ((ret = __wt_cursor_cache_get(
+ session, uri, owner, cfg, cursorp)) == 0)
+ return (0);
+ WT_RET_NOTFOUND_OK(ret);
+ }
+
return (__session_open_cursor_int(session, uri, owner, NULL, cfg,
cursorp));
}
@@ -461,6 +596,13 @@ __session_open_cursor(WT_SESSION *wt_session,
session = (WT_SESSION_IMPL *)wt_session;
SESSION_API_CALL(session, open_cursor, config, cfg);
+ if (to_dup == NULL && F_ISSET(session, WT_SESSION_CACHE_CURSORS)) {
+ if ((ret = __wt_cursor_cache_get(
+ session, uri, NULL, cfg, cursorp)) == 0)
+ return (0);
+ WT_RET_NOTFOUND_OK(ret);
+ }
+
statjoin = (to_dup != NULL && uri != NULL &&
WT_STREQ(uri, "statistics:join"));
if ((to_dup == NULL && uri == NULL) ||
@@ -730,7 +872,7 @@ __session_log_printf(WT_SESSION *wt_session, const char *fmt, ...)
va_list ap;
session = (WT_SESSION_IMPL *)wt_session;
- SESSION_API_CALL_NOCONF(session, log_printf);
+ SESSION_API_CALL_NOCONF_PREPARE_NOT_ALLOWED(session, log_printf);
va_start(ap, fmt);
ret = __wt_log_vprintf(session, fmt, ap);
@@ -884,6 +1026,8 @@ __session_reset(WT_SESSION *wt_session)
WT_TRET(__wt_session_reset_cursors(session, true));
+ WT_TRET(__wt_session_cursor_cache_sweep(session));
+
/* Release common session resources. */
WT_TRET(__wt_session_release_resources(session));
@@ -1231,22 +1375,23 @@ __wt_session_range_truncate(WT_SESSION_IMPL *session,
* what records currently appear in the object. For this reason, do a
* search-near, rather than a search. Additionally, we have to correct
* after calling search-near, to position the start/stop cursors on the
- * next record greater than/less than the original key.
+ * next record greater than/less than the original key. If we fail to
+ * find a key in a search-near, there are no keys in the table. If we
+ * fail to move forward or backward in a range, there are no keys in
+ * the range. In either of those cases, we're done.
*/
- if (start != NULL) {
- WT_ERR(start->search_near(start, &cmp));
- if (cmp < 0 && (ret = start->next(start)) != 0) {
+ if (start != NULL)
+ if ((ret = start->search_near(start, &cmp)) != 0 ||
+ (cmp < 0 && (ret = start->next(start)) != 0)) {
WT_ERR_NOTFOUND_OK(ret);
goto done;
}
- }
- if (stop != NULL) {
- WT_ERR(stop->search_near(stop, &cmp));
- if (cmp > 0 && (ret = stop->prev(stop)) != 0) {
+ if (stop != NULL)
+ if ((ret = stop->search_near(stop, &cmp)) != 0 ||
+ (cmp > 0 && (ret = stop->prev(stop)) != 0)) {
WT_ERR_NOTFOUND_OK(ret);
goto done;
}
- }
/*
* We always truncate in the forward direction because the underlying
@@ -1282,7 +1427,7 @@ err: /*
*/
if (local_start)
WT_TRET(start->close(start));
- else
+ else if (start != NULL)
WT_TRET(start->reset(start));
if (stop != NULL)
WT_TRET(stop->reset(stop));
@@ -1469,7 +1614,12 @@ __session_begin_transaction(WT_SESSION *wt_session, const char *config)
WT_SESSION_IMPL *session;
session = (WT_SESSION_IMPL *)wt_session;
- SESSION_API_CALL(session, begin_transaction, config, cfg);
+ /*
+ * Indicated as allowed in prepared state, even though not allowed,
+ * so that running transaction check below take precedence.
+ */
+ SESSION_API_CALL_PREPARE_ALLOWED(
+ session, begin_transaction, config, cfg);
WT_STAT_CONN_INCR(session, txn_begin);
WT_ERR(__wt_txn_context_check(session, false));
@@ -1491,7 +1641,8 @@ __session_commit_transaction(WT_SESSION *wt_session, const char *config)
WT_TXN *txn;
session = (WT_SESSION_IMPL *)wt_session;
- SESSION_API_CALL(session, commit_transaction, config, cfg);
+ SESSION_API_CALL_PREPARE_ALLOWED(
+ session, commit_transaction, config, cfg);
WT_STAT_CONN_INCR(session, txn_commit);
WT_ERR(__wt_txn_context_check(session, true));
@@ -1522,20 +1673,48 @@ __session_prepare_transaction(WT_SESSION *wt_session, const char *config)
{
WT_DECL_RET;
WT_SESSION_IMPL *session;
+ WT_TXN *txn;
session = (WT_SESSION_IMPL *)wt_session;
SESSION_API_CALL(session, prepare_transaction, config, cfg);
WT_ERR(__wt_txn_context_check(session, true));
- WT_TRET(__wt_txn_prepare(session, cfg));
-
/*
- * Below code to be corrected as part of prepare functionality
- * implementation, coded as below to avoid setting error to transaction.
+ * A failed transaction cannot be prepared, as it cannot guarantee
+ * a subsequent commit.
*/
+ txn = &session->txn;
+ if (F_ISSET(txn, WT_TXN_ERROR) && txn->mod_count != 0)
+ WT_ERR_MSG(session, EINVAL,
+ "failed transaction requires rollback%s%s",
+ txn->rollback_reason == NULL ? "" : ": ",
+ txn->rollback_reason == NULL ? "" : txn->rollback_reason);
+
+ WT_ERR(__wt_txn_prepare(session, cfg));
+
+err: API_END_RET(session, ret);
+
+}
+
+/*
+ * __session_prepare_transaction_readonly --
+ * WT_SESSION->prepare_transaction method; readonly version.
+ */
+static int
+__session_prepare_transaction_readonly(
+ WT_SESSION *wt_session, const char *config)
+{
+ WT_DECL_RET;
+ WT_SESSION_IMPL *session;
-err: API_END_RET_NO_TXN_ERROR(session, ret);
+ WT_UNUSED(config);
+
+ session = (WT_SESSION_IMPL *)wt_session;
+ SESSION_API_CALL_NOCONF(session, prepare_transaction);
+
+ ret = __wt_session_notsup(session);
+err: API_END_RET(session, ret);
}
/*
@@ -1549,7 +1728,8 @@ __session_rollback_transaction(WT_SESSION *wt_session, const char *config)
WT_SESSION_IMPL *session;
session = (WT_SESSION_IMPL *)wt_session;
- SESSION_API_CALL(session, rollback_transaction, config, cfg);
+ SESSION_API_CALL_PREPARE_ALLOWED(
+ session, rollback_transaction, config, cfg);
WT_STAT_CONN_INCR(session, txn_rollback);
WT_ERR(__wt_txn_context_check(session, true));
@@ -1595,7 +1775,7 @@ __session_transaction_pinned_range(WT_SESSION *wt_session, uint64_t *prange)
uint64_t pinned;
session = (WT_SESSION_IMPL *)wt_session;
- SESSION_API_CALL_NOCONF(session, pinned_range);
+ SESSION_API_CALL_NOCONF_PREPARE_NOT_ALLOWED(session, pinned_range);
txn_state = WT_SESSION_TXN_STATE(session);
@@ -1644,7 +1824,12 @@ __session_transaction_sync(WT_SESSION *wt_session, const char *config)
uint64_t time_start, time_stop;
session = (WT_SESSION_IMPL *)wt_session;
- SESSION_API_CALL(session, transaction_sync, config, cfg);
+ /*
+ * Indicated as allowed in prepared state, even though not allowed,
+ * so that running transaction check below take precedence.
+ */
+ SESSION_API_CALL_PREPARE_ALLOWED(
+ session, transaction_sync, config, cfg);
WT_STAT_CONN_INCR(session, txn_sync);
conn = S2C(session);
@@ -1738,7 +1923,12 @@ __session_checkpoint(WT_SESSION *wt_session, const char *config)
session = (WT_SESSION_IMPL *)wt_session;
WT_STAT_CONN_INCR(session, txn_checkpoint);
- SESSION_API_CALL(session, checkpoint, config, cfg);
+ /*
+ * Indicated as allowed in prepared state, even though not allowed,
+ * so that running transaction check below take precedence.
+ */
+ SESSION_API_CALL_PREPARE_ALLOWED(
+ session, checkpoint, config, cfg);
WT_ERR(__wt_inmem_unsupported_op(session, NULL));
@@ -1913,7 +2103,7 @@ __open_session(WT_CONNECTION_IMPL *conn,
__session_verify,
__session_begin_transaction,
__session_commit_transaction,
- __session_prepare_transaction,
+ __session_prepare_transaction_readonly,
__session_rollback_transaction,
__session_timestamp_transaction,
__session_checkpoint_readonly,
@@ -1974,16 +2164,27 @@ __open_session(WT_CONNECTION_IMPL *conn,
TAILQ_INIT(&session_ret->cursors);
TAILQ_INIT(&session_ret->dhandles);
+
/*
- * If we don't have one, allocate the dhandle hash array.
+ * If we don't have them, allocate the cursor and dhandle hash arrays.
* Allocate the table hash array as well.
*/
+ if (session_ret->cursor_cache == NULL)
+ WT_ERR(__wt_calloc_def(
+ session, WT_HASH_ARRAY_SIZE, &session_ret->cursor_cache));
if (session_ret->dhhash == NULL)
- WT_ERR(__wt_calloc(session, WT_HASH_ARRAY_SIZE,
- sizeof(struct __dhandles_hash), &session_ret->dhhash));
+ WT_ERR(__wt_calloc_def(
+ session, WT_HASH_ARRAY_SIZE, &session_ret->dhhash));
+
+ /* Initialize the dhandle hash array. */
for (i = 0; i < WT_HASH_ARRAY_SIZE; i++)
TAILQ_INIT(&session_ret->dhhash[i]);
+ /* Initialize the cursor cache hash buckets and sweep trigger. */
+ for (i = 0; i < WT_HASH_ARRAY_SIZE; i++)
+ TAILQ_INIT(&session_ret->cursor_cache[i]);
+ session_ret->cursor_sweep_countdown = WT_SESSION_CURSOR_SWEEP_COUNTDOWN;
+
/* Initialize transaction support: default to read-committed. */
session_ret->isolation = WT_ISO_READ_COMMITTED;
WT_ERR(__wt_txn_init(session, session_ret));