diff options
-rw-r--r-- | dist/api_data.py | 1 | ||||
-rw-r--r-- | dist/flags.py | 1 | ||||
-rw-r--r-- | lang/python/wiredtiger.i | 37 | ||||
-rw-r--r-- | src/btree/bt_sync.c | 2 | ||||
-rw-r--r-- | src/btree/rec_write.c | 20 | ||||
-rw-r--r-- | src/config/config_def.c | 20 | ||||
-rw-r--r-- | src/conn/conn_api.c | 1 | ||||
-rw-r--r-- | src/include/api.h | 6 | ||||
-rw-r--r-- | src/include/btree.i | 16 | ||||
-rw-r--r-- | src/include/extern.h | 3 | ||||
-rw-r--r-- | src/include/flags.h | 39 | ||||
-rw-r--r-- | src/include/session.h | 1 | ||||
-rw-r--r-- | src/include/txn.h | 3 | ||||
-rw-r--r-- | src/include/txn.i | 5 | ||||
-rw-r--r-- | src/include/wiredtiger.in | 8 | ||||
-rw-r--r-- | src/lsm/lsm_cursor.c | 3 | ||||
-rw-r--r-- | src/session/session_api.c | 31 | ||||
-rw-r--r-- | src/txn/txn.c | 56 | ||||
-rw-r--r-- | test/suite/test_bulk01.py | 2 | ||||
-rw-r--r-- | test/suite/test_txn06.py | 62 | ||||
-rw-r--r-- | test/suite/wttest.py | 5 |
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 |