summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dist/api_data.py1
-rw-r--r--dist/flags.py1
-rw-r--r--lang/python/wiredtiger.i37
-rw-r--r--src/btree/bt_sync.c2
-rw-r--r--src/btree/rec_write.c20
-rw-r--r--src/config/config_def.c20
-rw-r--r--src/conn/conn_api.c1
-rw-r--r--src/include/api.h6
-rw-r--r--src/include/btree.i16
-rw-r--r--src/include/extern.h3
-rw-r--r--src/include/flags.h39
-rw-r--r--src/include/session.h1
-rw-r--r--src/include/txn.h3
-rw-r--r--src/include/txn.i5
-rw-r--r--src/include/wiredtiger.in8
-rw-r--r--src/lsm/lsm_cursor.c3
-rw-r--r--src/session/session_api.c31
-rw-r--r--src/txn/txn.c56
-rw-r--r--test/suite/test_bulk01.py2
-rw-r--r--test/suite/test_txn06.py62
-rw-r--r--test/suite/wttest.py5
21 files changed, 242 insertions, 80 deletions
diff --git a/dist/api_data.py b/dist/api_data.py
index 4cfa126f2fa..f0a9c9742e1 100644
--- a/dist/api_data.py
+++ b/dist/api_data.py
@@ -454,6 +454,7 @@ connection_runtime_config = [
'shared_cache',
'split',
'temporary',
+ 'transaction',
'verify',
'version',
'write']),
diff --git a/dist/flags.py b/dist/flags.py
index a7c25293ac9..9401db58b1d 100644
--- a/dist/flags.py
+++ b/dist/flags.py
@@ -73,6 +73,7 @@ flags = {
'VERB_SHARED_CACHE',
'VERB_SPLIT',
'VERB_TEMPORARY',
+ 'VERB_TRANSACTION',
'VERB_VERIFY',
'VERB_VERSION',
'VERB_WRITE',
diff --git a/lang/python/wiredtiger.i b/lang/python/wiredtiger.i
index be55845a7b2..5e88855276a 100644
--- a/lang/python/wiredtiger.i
+++ b/lang/python/wiredtiger.i
@@ -381,7 +381,6 @@ COMPARE_OK(__wt_cursor::compare)
COMPARE_OK(__wt_cursor::search_near)
/* Lastly, some methods need no (additional) error checking. */
-%exception __wt_connection::diagnostic_build;
%exception __wt_connection::get_home;
%exception __wt_connection::is_new;
%exception __wt_connection::search_near;
@@ -393,6 +392,8 @@ COMPARE_OK(__wt_cursor::search_near)
%exception __wt_cursor::_set_value_str;
%exception wiredtiger_strerror;
%exception wiredtiger_version;
+%exception diagnostic_build;
+%exception verbose_build;
/* WT_ASYNC_OP customization. */
/* First, replace the varargs get / set methods with Python equivalents. */
@@ -567,6 +568,12 @@ typedef int int_void;
if self.search() != 0:
raise KeyError
return self.get_value()
+
+ def __setitem__(self, key, value):
+ '''Python convenience for inserting'''
+ self.set_key(key)
+ self.set_key(value)
+ self.insert()
%}
};
@@ -801,16 +808,28 @@ typedef int int_void;
int _freecb() {
return (0);
}
-
- int diagnostic_build() {
-%#ifdef HAVE_DIAGNOSTIC
- return 1;
-%#else
- return 0;
-%#endif
- }
};
+%{
+int diagnostic_build() {
+#ifdef HAVE_DIAGNOSTIC
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+int verbose_build() {
+#ifdef HAVE_VERBOSE
+ return 1;
+#else
+ return 0;
+#endif
+}
+%}
+int diagnostic_build();
+int verbose_build();
+
/* Remove / rename parts of the C API that we don't want in Python. */
%immutable __wt_cursor::session;
%immutable __wt_cursor::uri;
diff --git a/src/btree/bt_sync.c b/src/btree/bt_sync.c
index af530cad35e..368905bd527 100644
--- a/src/btree/bt_sync.c
+++ b/src/btree/bt_sync.c
@@ -63,7 +63,7 @@ __sync_file(WT_SESSION_IMPL *session, int syncop)
page = walk->page;
if (__wt_page_is_modified(page)) {
if (txn->isolation == TXN_ISO_READ_COMMITTED)
- __wt_txn_refresh(session, 1, 0);
+ __wt_txn_refresh(session, 1);
leaf_bytes += page->memory_footprint;
++leaf_pages;
WT_ERR(__wt_rec_write(session, walk, NULL, 0));
diff --git a/src/btree/rec_write.c b/src/btree/rec_write.c
index c9a2b2335bd..85d393b7d60 100644
--- a/src/btree/rec_write.c
+++ b/src/btree/rec_write.c
@@ -4894,23 +4894,21 @@ err: __wt_scr_free(&tkey);
* be set before a subsequent checkpoint reads it, and because the
* current checkpoint is waiting on this reconciliation to complete,
* there's no risk of that happening).
+ *
+ * Otherwise, if no updates were skipped, we have a new maximum
+ * transaction written for the page (used to decide if a clean page can
+ * be evicted). The page only might be clean; if the write generation
+ * is unchanged since reconciliation started, clear it and update cache
+ * dirty statistics, if the write generation changed, then the page has
+ * been written since we started reconciliation, it cannot be
+ * discarded.
*/
if (r->leave_dirty) {
mod->first_dirty_txn = r->skipped_txn;
btree->modified = 1;
WT_FULL_BARRIER();
- }
-
- /*
- * If no updates were skipped, we have a new maximum transaction written
- * for the page (used to decide if a clean page can be evicted). The
- * page only might be clean; if the write generation is unchanged since
- * reconciliation started, clear it and update cache dirty statistics,
- * if the write generation changed, then the page has been written since
- * we started reconciliation, it cannot be discarded.
- */
- if (!r->leave_dirty) {
+ } else {
mod->rec_max_txn = r->max_txn;
if (WT_ATOMIC_CAS4(mod->write_gen, r->orig_write_gen, 0))
diff --git a/src/config/config_def.c b/src/config/config_def.c
index 7a8310a1512..0cd2d32df57 100644
--- a/src/config/config_def.c
+++ b/src/config/config_def.c
@@ -104,8 +104,8 @@ static const WT_CONFIG_CHECK confchk_connection_reconfigure[] = {
"choices=[\"api\",\"block\",\"checkpoint\",\"compact\",\"evict\""
",\"evictserver\",\"fileops\",\"log\",\"lsm\",\"metadata\","
"\"mutex\",\"overflow\",\"read\",\"reconcile\",\"recovery\","
- "\"salvage\",\"shared_cache\",\"split\",\"temporary\",\"verify\","
- "\"version\",\"write\"]",
+ "\"salvage\",\"shared_cache\",\"split\",\"temporary\","
+ "\"transaction\",\"verify\",\"version\",\"write\"]",
NULL },
{ NULL, NULL, NULL, NULL }
};
@@ -346,8 +346,8 @@ static const WT_CONFIG_CHECK confchk_wiredtiger_open[] = {
"choices=[\"api\",\"block\",\"checkpoint\",\"compact\",\"evict\""
",\"evictserver\",\"fileops\",\"log\",\"lsm\",\"metadata\","
"\"mutex\",\"overflow\",\"read\",\"reconcile\",\"recovery\","
- "\"salvage\",\"shared_cache\",\"split\",\"temporary\",\"verify\","
- "\"version\",\"write\"]",
+ "\"salvage\",\"shared_cache\",\"split\",\"temporary\","
+ "\"transaction\",\"verify\",\"version\",\"write\"]",
NULL },
{ NULL, NULL, NULL, NULL }
};
@@ -394,8 +394,8 @@ static const WT_CONFIG_CHECK confchk_wiredtiger_open_all[] = {
"choices=[\"api\",\"block\",\"checkpoint\",\"compact\",\"evict\""
",\"evictserver\",\"fileops\",\"log\",\"lsm\",\"metadata\","
"\"mutex\",\"overflow\",\"read\",\"reconcile\",\"recovery\","
- "\"salvage\",\"shared_cache\",\"split\",\"temporary\",\"verify\","
- "\"version\",\"write\"]",
+ "\"salvage\",\"shared_cache\",\"split\",\"temporary\","
+ "\"transaction\",\"verify\",\"version\",\"write\"]",
NULL },
{ "version", "string", NULL, NULL },
{ NULL, NULL, NULL, NULL }
@@ -439,8 +439,8 @@ static const WT_CONFIG_CHECK confchk_wiredtiger_open_basecfg[] = {
"choices=[\"api\",\"block\",\"checkpoint\",\"compact\",\"evict\""
",\"evictserver\",\"fileops\",\"log\",\"lsm\",\"metadata\","
"\"mutex\",\"overflow\",\"read\",\"reconcile\",\"recovery\","
- "\"salvage\",\"shared_cache\",\"split\",\"temporary\",\"verify\","
- "\"version\",\"write\"]",
+ "\"salvage\",\"shared_cache\",\"split\",\"temporary\","
+ "\"transaction\",\"verify\",\"version\",\"write\"]",
NULL },
{ "version", "string", NULL, NULL },
{ NULL, NULL, NULL, NULL }
@@ -484,8 +484,8 @@ static const WT_CONFIG_CHECK confchk_wiredtiger_open_usercfg[] = {
"choices=[\"api\",\"block\",\"checkpoint\",\"compact\",\"evict\""
",\"evictserver\",\"fileops\",\"log\",\"lsm\",\"metadata\","
"\"mutex\",\"overflow\",\"read\",\"reconcile\",\"recovery\","
- "\"salvage\",\"shared_cache\",\"split\",\"temporary\",\"verify\","
- "\"version\",\"write\"]",
+ "\"salvage\",\"shared_cache\",\"split\",\"temporary\","
+ "\"transaction\",\"verify\",\"version\",\"write\"]",
NULL },
{ NULL, NULL, NULL, NULL }
};
diff --git a/src/conn/conn_api.c b/src/conn/conn_api.c
index 2abe644bff2..c7562ab94c3 100644
--- a/src/conn/conn_api.c
+++ b/src/conn/conn_api.c
@@ -1202,6 +1202,7 @@ __wt_verbose_config(WT_SESSION_IMPL *session, const char *cfg[])
{ "shared_cache", WT_VERB_SHARED_CACHE },
{ "split", WT_VERB_SPLIT },
{ "temporary", WT_VERB_TEMPORARY },
+ { "transaction", WT_VERB_TRANSACTION },
{ "verify", WT_VERB_VERIFY },
{ "version", WT_VERB_VERSION },
{ "write", WT_VERB_WRITE },
diff --git a/src/include/api.h b/src/include/api.h
index d6e923e3560..3acb39acb19 100644
--- a/src/include/api.h
+++ b/src/include/api.h
@@ -11,7 +11,8 @@
const char *__oldname = (s)->name; \
(s)->cursor = (cur); \
(s)->dhandle = (dh); \
- (s)->name = #h "." #n;
+ (s)->name = #h "." #n; \
+ ++(s)->api_level
#define API_CALL_NOCONF(s, h, n, cur, dh) do { \
API_SESSION_INIT(s, h, n, cur, dh); \
@@ -31,7 +32,8 @@
#define API_END(s, ret) \
if ((s) != NULL) { \
(s)->dhandle = __olddh; \
- (s)->name = __oldname; \
+ if (--(s)->api_level > 0) \
+ (s)->name = __oldname; \
if (F_ISSET(&(s)->txn, TXN_RUNNING) && \
(ret) != 0 && \
(ret) != WT_NOTFOUND && \
diff --git a/src/include/btree.i b/src/include/btree.i
index d74e0c86f54..57df16dce5f 100644
--- a/src/include/btree.i
+++ b/src/include/btree.i
@@ -952,11 +952,23 @@ __wt_page_release(WT_SESSION_IMPL *session, WT_REF *ref, uint32_t flags)
return (0);
page = ref->page;
- /* Attempt to evict pages with the special "oldest" read generation. */
+ /*
+ * Attempt to evict pages with the special "oldest" read generation.
+ *
+ * This is set for pages that grow larger than the configured
+ * memory_page_max setting, and when we are attempting to scan without
+ * trashing the cache.
+ *
+ * Skip this if eviction is disabled for this operation or this tree,
+ * or if there is no chance of eviction succeeding for dirty pages due
+ * to a checkpoint or because we've already tried writing this page and
+ * it contains an update that isn't stable.
+ */
if (LF_ISSET(WT_READ_NO_EVICT) ||
page->read_gen != WT_READGEN_OLDEST ||
F_ISSET(btree, WT_BTREE_NO_EVICTION) ||
- (btree->checkpointing && __wt_page_is_modified(page)))
+ (__wt_page_is_modified(page) && (btree->checkpointing ||
+ !__wt_txn_visible_all(session, page->modify->first_dirty_txn))))
return (__wt_hazard_clear(session, page));
/*
diff --git a/src/include/extern.h b/src/include/extern.h
index 56029386ad7..dba872ba7d0 100644
--- a/src/include/extern.h
+++ b/src/include/extern.h
@@ -527,6 +527,7 @@ extern int __wt_str_name_check(WT_SESSION_IMPL *session, const char *str);
extern int __wt_name_check(WT_SESSION_IMPL *session, const char *str, size_t len);
extern int __wt_schema_worker(WT_SESSION_IMPL *session, const char *uri, int (*file_func)(WT_SESSION_IMPL *, const char *[]), int (*name_func)(WT_SESSION_IMPL *, const char *, int *), const char *cfg[], uint32_t open_flags);
extern int __wt_session_reset_cursors(WT_SESSION_IMPL *session);
+extern int __wt_session_copy_values(WT_SESSION_IMPL *session);
extern int __wt_open_cursor(WT_SESSION_IMPL *session, const char *uri, WT_CURSOR *owner, const char *cfg[], WT_CURSOR **cursorp);
extern int __wt_session_create_strip(WT_SESSION *wt_session, const char *v1, const char *v2, const char **value_ret);
extern int __wt_open_internal_session(WT_CONNECTION_IMPL *conn, const char *name, int uses_dhandles, int open_metadata, WT_SESSION_IMPL **sessionp);
@@ -615,7 +616,7 @@ extern void __wt_stat_refresh_connection_stats(void *stats_arg);
extern int __wt_txnid_cmp(const void *v1, const void *v2);
extern void __wt_txn_release_snapshot(WT_SESSION_IMPL *session);
extern void __wt_txn_update_oldest(WT_SESSION_IMPL *session);
-extern void __wt_txn_refresh(WT_SESSION_IMPL *session, int get_snapshot, int pin_reads);
+extern void __wt_txn_refresh(WT_SESSION_IMPL *session, int get_snapshot);
extern int __wt_txn_begin(WT_SESSION_IMPL *session, const char *cfg[]);
extern void __wt_txn_release(WT_SESSION_IMPL *session);
extern int __wt_txn_commit(WT_SESSION_IMPL *session, const char *cfg[]);
diff --git a/src/include/flags.h b/src/include/flags.h
index 1c58a1ea8e5..75dd80930d8 100644
--- a/src/include/flags.h
+++ b/src/include/flags.h
@@ -57,25 +57,26 @@
#define WT_TXN_LOG_CKPT_PREPARE 0x00000004
#define WT_TXN_LOG_CKPT_START 0x00000002
#define WT_TXN_LOG_CKPT_STOP 0x00000001
-#define WT_VERB_API 0x00200000
-#define WT_VERB_BLOCK 0x00100000
-#define WT_VERB_CHECKPOINT 0x00080000
-#define WT_VERB_COMPACT 0x00040000
-#define WT_VERB_EVICT 0x00020000
-#define WT_VERB_EVICTSERVER 0x00010000
-#define WT_VERB_FILEOPS 0x00008000
-#define WT_VERB_LOG 0x00004000
-#define WT_VERB_LSM 0x00002000
-#define WT_VERB_METADATA 0x00001000
-#define WT_VERB_MUTEX 0x00000800
-#define WT_VERB_OVERFLOW 0x00000400
-#define WT_VERB_READ 0x00000200
-#define WT_VERB_RECONCILE 0x00000100
-#define WT_VERB_RECOVERY 0x00000080
-#define WT_VERB_SALVAGE 0x00000040
-#define WT_VERB_SHARED_CACHE 0x00000020
-#define WT_VERB_SPLIT 0x00000010
-#define WT_VERB_TEMPORARY 0x00000008
+#define WT_VERB_API 0x00400000
+#define WT_VERB_BLOCK 0x00200000
+#define WT_VERB_CHECKPOINT 0x00100000
+#define WT_VERB_COMPACT 0x00080000
+#define WT_VERB_EVICT 0x00040000
+#define WT_VERB_EVICTSERVER 0x00020000
+#define WT_VERB_FILEOPS 0x00010000
+#define WT_VERB_LOG 0x00008000
+#define WT_VERB_LSM 0x00004000
+#define WT_VERB_METADATA 0x00002000
+#define WT_VERB_MUTEX 0x00001000
+#define WT_VERB_OVERFLOW 0x00000800
+#define WT_VERB_READ 0x00000400
+#define WT_VERB_RECONCILE 0x00000200
+#define WT_VERB_RECOVERY 0x00000100
+#define WT_VERB_SALVAGE 0x00000080
+#define WT_VERB_SHARED_CACHE 0x00000040
+#define WT_VERB_SPLIT 0x00000020
+#define WT_VERB_TEMPORARY 0x00000010
+#define WT_VERB_TRANSACTION 0x00000008
#define WT_VERB_VERIFY 0x00000004
#define WT_VERB_VERSION 0x00000002
#define WT_VERB_WRITE 0x00000001
diff --git a/src/include/session.h b/src/include/session.h
index eace12844e9..b9267b0ce5a 100644
--- a/src/include/session.h
+++ b/src/include/session.h
@@ -49,6 +49,7 @@ struct __wt_session_impl {
const char *name; /* Name */
uint32_t id; /* UID, offset in session array */
+ uint32_t api_level; /* Number of recursive calls */
WT_CONDVAR *cond; /* Condition variable */
diff --git a/src/include/txn.h b/src/include/txn.h
index 2d1f74ab00b..c28a9231750 100644
--- a/src/include/txn.h
+++ b/src/include/txn.h
@@ -40,6 +40,9 @@ struct __wt_txn_global {
*/
volatile uint64_t oldest_id;
+ /* The oldest session found in the last scan. */
+ uint32_t oldest_session;
+
/* Count of scanning threads, or -1 for exclusive access. */
volatile int32_t scan_count;
diff --git a/src/include/txn.i b/src/include/txn.i
index 9a8d64034c0..9e17d746885 100644
--- a/src/include/txn.i
+++ b/src/include/txn.i
@@ -316,8 +316,9 @@ __wt_txn_read_first(WT_SESSION_IMPL *session)
#endif
if (txn->isolation == TXN_ISO_READ_COMMITTED ||
- (!F_ISSET(txn, TXN_RUNNING) && txn->isolation == TXN_ISO_SNAPSHOT))
- __wt_txn_refresh(session, 1, 0);
+ (txn->isolation == TXN_ISO_SNAPSHOT &&
+ !F_ISSET(txn, TXN_HAS_SNAPSHOT)))
+ __wt_txn_refresh(session, 1);
}
/*
diff --git a/src/include/wiredtiger.in b/src/include/wiredtiger.in
index d42d327ba0b..c3a94e83f71 100644
--- a/src/include/wiredtiger.in
+++ b/src/include/wiredtiger.in
@@ -1569,8 +1569,8 @@ struct __wt_connection {
* "evictserver"\, \c "fileops"\, \c "log"\, \c "lsm"\, \c "metadata"\,
* \c "mutex"\, \c "overflow"\, \c "read"\, \c "reconcile"\, \c
* "recovery"\, \c "salvage"\, \c "shared_cache"\, \c "split"\, \c
- * "temporary"\, \c "verify"\, \c "version"\, \c "write"; default
- * empty.}
+ * "temporary"\, \c "transaction"\, \c "verify"\, \c "version"\, \c
+ * "write"; default empty.}
* @configend
* @errors
*/
@@ -1955,8 +1955,8 @@ struct __wt_connection {
* "checkpoint"\, \c "compact"\, \c "evict"\, \c "evictserver"\, \c "fileops"\,
* \c "log"\, \c "lsm"\, \c "metadata"\, \c "mutex"\, \c "overflow"\, \c
* "read"\, \c "reconcile"\, \c "recovery"\, \c "salvage"\, \c "shared_cache"\,
- * \c "split"\, \c "temporary"\, \c "verify"\, \c "version"\, \c "write";
- * default empty.}
+ * \c "split"\, \c "temporary"\, \c "transaction"\, \c "verify"\, \c "version"\,
+ * \c "write"; default empty.}
* @configend
* Additionally, if files named \c WiredTiger.config or \c WiredTiger.basecfg
* appear in the WiredTiger home directory, they are read for configuration
diff --git a/src/lsm/lsm_cursor.c b/src/lsm/lsm_cursor.c
index 5202f013989..bb3fb57a374 100644
--- a/src/lsm/lsm_cursor.c
+++ b/src/lsm/lsm_cursor.c
@@ -177,6 +177,9 @@ __clsm_enter(WT_CURSOR_LSM *clsm, int reset, int update)
*/
WT_RET(__wt_txn_autocommit_check(session));
+ if (session->txn.isolation == TXN_ISO_SNAPSHOT)
+ __wt_txn_read_first(session);
+
/*
* Figure out how many updates are required for
* snapshot isolation.
diff --git a/src/session/session_api.c b/src/session/session_api.c
index 3845ca86b66..092bcad3f73 100644
--- a/src/session/session_api.c
+++ b/src/session/session_api.c
@@ -30,6 +30,28 @@ __wt_session_reset_cursors(WT_SESSION_IMPL *session)
}
/*
+ * __wt_session_copy_values --
+ * Copy values into all positioned cursors, so that they don't keep
+ * transaction IDs pinned.
+ */
+int
+__wt_session_copy_values(WT_SESSION_IMPL *session)
+{
+ WT_CURSOR *cursor;
+ WT_DECL_RET;
+
+ TAILQ_FOREACH(cursor, &session->cursors, q)
+ if (F_ISSET(cursor, WT_CURSTD_VALUE_INT)) {
+ F_CLR(cursor, WT_CURSTD_VALUE_INT);
+ WT_RET(__wt_buf_set(session, &cursor->value,
+ cursor->value.data, cursor->value.size));
+ F_SET(cursor, WT_CURSTD_VALUE_EXT);
+ }
+
+ return (ret);
+}
+
+/*
* __session_clear --
* Clear a session structure.
*/
@@ -75,6 +97,13 @@ __session_close(WT_SESSION *wt_session, const char *config)
if (F_ISSET(&session->txn, TXN_RUNNING))
WT_TRET(__session_rollback_transaction(wt_session, NULL));
+ /*
+ * Also release any pinned transaction ID from a non-transactional
+ * operation.
+ */
+ if (conn->txn_global.states != NULL)
+ __wt_txn_release_snapshot(session);
+
/* Close all open cursors. */
while ((cursor = TAILQ_FIRST(&session->cursors)) != NULL) {
/*
@@ -969,6 +998,8 @@ __wt_open_session(WT_CONNECTION_IMPL *conn,
WT_ERR(
__session_reconfigure((WT_SESSION *)session_ret, config));
+ session_ret->name = NULL;
+
/*
* Publish: make the entry visible to server threads. There must be a
* barrier for two reasons, to ensure structure fields are set before
diff --git a/src/txn/txn.c b/src/txn/txn.c
index 6339b8d60de..d05343501d1 100644
--- a/src/txn/txn.c
+++ b/src/txn/txn.c
@@ -83,7 +83,7 @@ __wt_txn_update_oldest(WT_SESSION_IMPL *session)
* state wasn't refreshed after the last transaction committed. Push
* past the last committed transaction.
*/
- __wt_txn_refresh(session, 0, 0);
+ __wt_txn_refresh(session, 0);
}
/*
@@ -91,7 +91,7 @@ __wt_txn_update_oldest(WT_SESSION_IMPL *session)
* Allocate a transaction ID and/or a snapshot.
*/
void
-__wt_txn_refresh(WT_SESSION_IMPL *session, int get_snapshot, int pin_reads)
+__wt_txn_refresh(WT_SESSION_IMPL *session, int get_snapshot)
{
WT_CONNECTION_IMPL *conn;
WT_TXN *txn;
@@ -99,7 +99,7 @@ __wt_txn_refresh(WT_SESSION_IMPL *session, int get_snapshot, int pin_reads)
WT_TXN_STATE *s, *txn_state;
uint64_t current_id, id, oldest_id;
uint64_t prev_oldest_id, snap_min;
- uint32_t i, n, session_cnt;
+ uint32_t i, n, oldest_session, session_cnt;
int32_t count;
conn = S2C(session);
@@ -136,6 +136,7 @@ __wt_txn_refresh(WT_SESSION_IMPL *session, int get_snapshot, int pin_reads)
/* The oldest ID cannot change until the scan count goes to zero. */
prev_oldest_id = txn_global->oldest_id;
current_id = oldest_id = snap_min = txn_global->current;
+ oldest_session = 0;
/* Walk the array of concurrent transactions. */
WT_ORDERED_READ(session_cnt, conn->session_cnt);
@@ -160,10 +161,10 @@ __wt_txn_refresh(WT_SESSION_IMPL *session, int get_snapshot, int pin_reads)
}
/*
- * Ignore the session's own snap_min if we are in the process
- * of updating it.
+ * Ignore the session's own snap_min: we are about to update
+ * it.
*/
- if (get_snapshot && !pin_reads && s == txn_state)
+ if (get_snapshot && s == txn_state)
continue;
/*
@@ -175,8 +176,10 @@ __wt_txn_refresh(WT_SESSION_IMPL *session, int get_snapshot, int pin_reads)
* more details.
*/
if ((id = s->snap_min) != WT_TXN_NONE &&
- TXNID_LT(id, oldest_id))
+ TXNID_LT(id, oldest_id)) {
oldest_id = id;
+ oldest_session = i;
+ }
}
if (TXNID_LT(snap_min, oldest_id))
@@ -186,12 +189,9 @@ __wt_txn_refresh(WT_SESSION_IMPL *session, int get_snapshot, int pin_reads)
/*
* If we got a new snapshot, update the published snap_min for this
- * session. Skip this if we are updating a snapshot because there are
- * positioned cursors during commit: the cursors may be pointing to
- * data older than the snapshot we've calculated.
+ * session.
*/
- if (get_snapshot && !pin_reads &&
- (session->ncursors == 0 || txn_state->snap_min == WT_TXN_NONE)) {
+ if (get_snapshot) {
WT_ASSERT(session, TXNID_LE(prev_oldest_id, snap_min));
WT_ASSERT(session, prev_oldest_id == txn_global->oldest_id);
txn_state->snap_min = snap_min;
@@ -226,6 +226,18 @@ __wt_txn_refresh(WT_SESSION_IMPL *session, int get_snapshot, int pin_reads)
txn_global->oldest_id = oldest_id;
txn_global->scan_count = 0;
} else {
+ if (WT_VERBOSE_ISSET(session, WT_VERB_TRANSACTION) &&
+ current_id - oldest_id > 10000 &&
+ txn_global->oldest_session != oldest_session) {
+ (void)__wt_verbose(session, WT_VERB_TRANSACTION,
+ "old snapshot %" PRIu64
+ " pinned in session %d [%s]"
+ " with snap_min %" PRIu64 "\n",
+ oldest_id, oldest_session,
+ conn->sessions[oldest_session].name,
+ conn->sessions[oldest_session].txn.snap_min);
+ txn_global->oldest_session = oldest_session;
+ }
WT_ASSERT(session, txn_global->scan_count > 0);
(void)WT_ATOMIC_SUB4(txn_global->scan_count, 1);
}
@@ -267,8 +279,10 @@ __wt_txn_begin(WT_SESSION_IMPL *session, const char *cfg[])
txn->txn_logsync = 0;
F_SET(txn, TXN_RUNNING);
- if (txn->isolation == TXN_ISO_SNAPSHOT)
- __wt_txn_refresh(session, 1, session->ncursors > 0);
+ if (txn->isolation == TXN_ISO_SNAPSHOT) {
+ WT_RET(__wt_session_copy_values(session));
+ __wt_txn_refresh(session, 1);
+ }
return (0);
}
@@ -370,8 +384,10 @@ __wt_txn_commit(WT_SESSION_IMPL *session, const char *cfg[])
*/
if (session->ncursors > 0 &&
F_ISSET(txn, TXN_HAS_ID) &&
- txn->isolation != TXN_ISO_READ_UNCOMMITTED)
- __wt_txn_refresh(session, 1, 1);
+ txn->isolation != TXN_ISO_READ_UNCOMMITTED) {
+ WT_RET(__wt_session_copy_values(session));
+ __wt_txn_refresh(session, 1);
+ }
__wt_txn_release(session);
return (0);
@@ -445,6 +461,14 @@ __wt_txn_init(WT_SESSION_IMPL *session)
WT_RET(__wt_calloc_def(session,
S2C(session)->session_size, &txn->snapshot));
+#ifdef HAVE_DIAGNOSTIC
+ if (S2C(session)->txn_global.states != NULL) {
+ WT_TXN_STATE *txn_state;
+ txn_state = &S2C(session)->txn_global.states[session->id];
+ WT_ASSERT(session, txn_state->snap_min == WT_TXN_NONE);
+ }
+#endif
+
/*
* Take care to clean these out in case we are reusing the transaction
* for eviction.
diff --git a/test/suite/test_bulk01.py b/test/suite/test_bulk01.py
index 5866eff57d0..c99cedae1e6 100644
--- a/test/suite/test_bulk01.py
+++ b/test/suite/test_bulk01.py
@@ -100,7 +100,7 @@ class test_bulk_load_row_order(wttest.WiredTigerTestCase):
cursor.set_value(value_populate(cursor, 1))
cursor.insert()
- if not self.conn.diagnostic_build():
+ if not wiredtiger.diagnostic_build():
self.skipTest('requires a diagnostic build')
# Close explicitly, there's going to be a fallure.
diff --git a/test/suite/test_txn06.py b/test/suite/test_txn06.py
new file mode 100644
index 00000000000..0d3bf79c499
--- /dev/null
+++ b/test/suite/test_txn06.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+#
+# Public Domain 2008-2014 WiredTiger, Inc.
+#
+# This is free and unencumbered software released into the public domain.
+#
+# Anyone is free to copy, modify, publish, use, compile, sell, or
+# distribute this software, either in source code form or as a compiled
+# binary, for any purpose, commercial or non-commercial, and by any
+# means.
+#
+# In jurisdictions that recognize copyright laws, the author or authors
+# of this software dedicate any and all copyright interest in the
+# software to the public domain. We make this dedication for the benefit
+# of the public at large and to the detriment of our heirs and
+# successors. We intend this dedication to be an overt act of
+# relinquishment in perpetuity of all present and future rights to this
+# software under copyright law.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+#
+# test_txn06.py
+# Transactions: test long-running snapshots
+
+from suite_subprocess import suite_subprocess
+from wtscenario import multiply_scenarios, number_scenarios
+from helper import simple_populate
+import wiredtiger, wttest
+
+class test_txn06(wttest.WiredTigerTestCase, suite_subprocess):
+ conn_config = 'verbose=[transaction]'
+ tablename = 'test_txn06'
+ uri = 'table:' + tablename
+ source_uri = 'table:' + tablename + "_src"
+ nrows = 100000
+
+ def setUpConnectionOpen(self, *args):
+ if not wiredtiger.verbose_build():
+ self.skipTest('requires a verbose build')
+ super(wttest.WiredTigerTestCase, setUpConnectionOpen)(self, *args)
+
+ def test_long_running(self):
+ # Populate a table
+ simple_populate(self, self.source_uri, 'key_format=S', self.nrows)
+
+ # Now scan the table and copy the rows into a new table
+ c_src = self.session.create(self.uri, "key_format=S")
+ c_src = self.session.open_cursor(self.source_uri)
+ c = self.session.open_cursor(self.uri)
+ for k, v in c_src:
+ c.set_key(k)
+ c.set_value(v)
+ c.insert()
+
+if __name__ == '__main__':
+ wttest.run()
diff --git a/test/suite/wttest.py b/test/suite/wttest.py
index df2971ed009..25bd71945f6 100644
--- a/test/suite/wttest.py
+++ b/test/suite/wttest.py
@@ -137,6 +137,7 @@ class CapturedFd(object):
class WiredTigerTestCase(unittest.TestCase):
_globalSetup = False
_printOnceSeen = {}
+ conn_config = ''
@staticmethod
def globalSetup(preserveFiles = False, useTimestamp = False,
@@ -191,8 +192,8 @@ class WiredTigerTestCase(unittest.TestCase):
# Can be overridden
def setUpConnectionOpen(self, dir):
- conn = wiredtiger.wiredtiger_open(dir, 'create,error_prefix="' +
- self.shortid() + ': ' + '"')
+ conn = wiredtiger.wiredtiger_open(dir,
+ 'create,error_prefix="%s",%s' % (self.shortid(), self.conn_config))
self.pr(`conn`)
return conn