diff options
author | Michael Cahill <michael.cahill@mongodb.com> | 2016-08-02 16:42:36 +1000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-08-02 16:42:36 +1000 |
commit | 41eb2dcaac1ff25654d1503f5e29714576ff8d81 (patch) | |
tree | bef258cc1a9dd133ad750ae2b0de9860e0cdbbff | |
parent | 8f02a158b584534fc698cf93cefae85727db902b (diff) | |
download | mongo-41eb2dcaac1ff25654d1503f5e29714576ff8d81.tar.gz |
WT-2804 Don't read values in a tree without a snapshot. (#2924)
* Improve two recent assertions, one from WT-2798 relating to writing metadata updates to disk that are part of a running transaction, and another from WT-2802 that checks that we don't try to copy values from a cursor without a transaction pinned. The latter doesn't apply to cursors on checkpoints (including chunk cursors in an LSM tree).
* Copy cursor values before rollback in autocommit.
If an autocommit operation such as WT_CURSOR::update touches multiple trees (e.g., multiple column groups in a table, or index updates, or multiple chunks in an LSM tree), then some cursors may have consumed the application's key/value pair when the operation has to roll back. Take a copy of any such values before attempting to retry the operation.
-rw-r--r-- | src/include/api.h | 2 | ||||
-rw-r--r-- | src/reconcile/rec_write.c | 6 | ||||
-rw-r--r-- | src/session/session_api.c | 14 |
3 files changed, 15 insertions, 7 deletions
diff --git a/src/include/api.h b/src/include/api.h index 50b2eab83b8..0a4593178dc 100644 --- a/src/include/api.h +++ b/src/include/api.h @@ -66,6 +66,8 @@ else if (ret == 0 && !F_ISSET(&(s)->txn, WT_TXN_ERROR)) \ ret = __wt_txn_commit((s), NULL); \ else { \ + if (retry) \ + WT_TRET(__wt_session_copy_values(s)); \ WT_TRET(__wt_txn_rollback((s), NULL)); \ if ((ret == 0 || ret == WT_ROLLBACK) && \ (retry)) { \ diff --git a/src/reconcile/rec_write.c b/src/reconcile/rec_write.c index 0a14c027da7..b96b34594b0 100644 --- a/src/reconcile/rec_write.c +++ b/src/reconcile/rec_write.c @@ -1152,15 +1152,17 @@ __rec_txn_read(WT_SESSION_IMPL *session, WT_RECONCILE *r, if (!skipped && (F_ISSET(btree, WT_BTREE_LOOKASIDE) || __wt_txn_visible_all(session, max_txn))) { +#ifdef HAVE_DIAGNOSTIC /* * The checkpoint transaction is special. Make sure we never * write (metadata) updates from a checkpoint in a concurrent * session. */ - WT_ASSERT(session, *updp == NULL || - (txnid = (*updp)->txnid) == WT_TXN_NONE || + txnid = *updp == NULL ? WT_TXN_NONE : (*updp)->txnid; + WT_ASSERT(session, txnid == WT_TXN_NONE || txnid != S2C(session)->txn_global.checkpoint_txnid || WT_SESSION_IS_CHECKPOINT(session)); +#endif return (0); } diff --git a/src/session/session_api.c b/src/session/session_api.c index dd5acdbbf56..752fafffda5 100644 --- a/src/session/session_api.c +++ b/src/session/session_api.c @@ -66,11 +66,15 @@ __wt_session_copy_values(WT_SESSION_IMPL *session) TAILQ_FOREACH(cursor, &session->cursors, q) if (F_ISSET(cursor, WT_CURSTD_VALUE_INT)) { -#if 0 /* !!! LSM updates can violate this assertion. */ - /* We have to do this with a transaction ID pinned. */ - WT_ASSERT(session, - WT_SESSION_TXN_STATE(session)->snap_min != - WT_TXN_NONE); +#ifdef HAVE_DIAGNOSTIC + /* + * We have to do this with a transaction ID pinned + * unless the cursor is reading from a checkpoint. + */ + WT_TXN_STATE *txn_state = WT_SESSION_TXN_STATE(session); + WT_ASSERT(session, txn_state->snap_min != WT_TXN_NONE || + (WT_PREFIX_MATCH(cursor->uri, "file:") && + F_ISSET((WT_CURSOR_BTREE *)cursor, WT_CBT_NO_TXN))); #endif F_CLR(cursor, WT_CURSTD_VALUE_INT); |