void WiredTigerSessionCache::releaseSession(WiredTigerSession* session) { invariant(session); invariant(session->cursorsOut() == 0); boost::shared_lock<boost::shared_mutex> shutdownLock(_shutdownLock); if (_shuttingDown.loadRelaxed()) { // Leak the session in order to avoid race condition with clean shutdown, where the // storage engine is ripped from underneath transactions, which are not "active" // (i.e., do not have any locks), but are just about to delete the recovery unit. // See SERVER-16031 for more information. return; } // This checks that we are only caching idle sessions and not something which might hold // locks or otherwise prevent truncation. { WT_SESSION* ss = session->getSession(); uint64_t range; invariantWTOK(ss->transaction_pinned_range(ss, &range)); invariant(range == 0); } _sessionsOut.fetchAndSubtract(1); bool returnedToCache = false; invariant(session->_getEpoch() <= _epoch); // Only return sessions until we hit the maximum number of sessions we have ever seen demand // for concurrently. We also want to immediately delete any session that is from a // non-current epoch. if (session->_getEpoch() == _epoch && sessionsInCache.load() < _highWaterMark.load()) { returnedToCache = true; stdx::lock_guard<SpinLock> lock(_cacheLock); _sessions.push_back(session); } if (returnedToCache) { sessionsInCache.fetchAndAdd(1); } else { delete session; } if (_engine && _engine->haveDropsQueued()) { _engine->dropAllQueued(); } if (_engine && _engine->haveDropsQueued()) { _engine->dropAllQueued(); } }
void WiredTigerSessionCache::releaseSession(WiredTigerSession* session) { invariant(session); invariant(session->cursorsOut() == 0); const int shuttingDown = _shuttingDown.fetchAndAdd(1); ON_BLOCK_EXIT([this] { _shuttingDown.fetchAndSubtract(1); }); if (shuttingDown & kShuttingDownMask) { // Leak the session in order to avoid race condition with clean shutdown, where the // storage engine is ripped from underneath transactions, which are not "active" // (i.e., do not have any locks), but are just about to delete the recovery unit. // See SERVER-16031 for more information. return; } // This checks that we are only caching idle sessions and not something which might hold // locks or otherwise prevent truncation. { WT_SESSION* ss = session->getSession(); uint64_t range; invariantWTOK(ss->transaction_pinned_range(ss, &range)); invariant(range == 0); } // If the cursor epoch has moved on, close all cursors in the session. uint64_t cursorEpoch = _cursorEpoch.load(); if (session->_getCursorEpoch() != cursorEpoch) session->closeAllCursors(); bool returnedToCache = false; uint64_t currentEpoch = _epoch.load(); if (session->_getEpoch() == currentEpoch) { // check outside of lock to reduce contention stdx::lock_guard<stdx::mutex> lock(_cacheLock); if (session->_getEpoch() == _epoch.load()) { // recheck inside the lock for correctness returnedToCache = true; _sessions.push_back(session); } } else invariant(session->_getEpoch() < currentEpoch); if (!returnedToCache) delete session; if (_engine && _engine->haveDropsQueued()) _engine->dropSomeQueuedIdents(); }
void WiredTigerSessionCache::releaseSession(WiredTigerSession* session) { invariant(session); invariant(session->cursorsOut() == 0); const int shuttingDown = _shuttingDown.fetchAndAdd(1); ON_BLOCK_EXIT([this] { _shuttingDown.fetchAndSubtract(1); }); if (shuttingDown & kShuttingDownMask) { // There is a race condition with clean shutdown, where the storage engine is ripped from // underneath OperationContexts, which are not "active" (i.e., do not have any locks), but // are just about to delete the recovery unit. See SERVER-16031 for more information. Since // shutting down the WT_CONNECTION will close all WT_SESSIONS, we shouldn't also try to // directly close this session. session->_session = nullptr; // Prevents calling _session->close() in destructor. delete session; return; } { WT_SESSION* ss = session->getSession(); uint64_t range; // This checks that we are only caching idle sessions and not something which might hold // locks or otherwise prevent truncation. invariantWTOK(ss->transaction_pinned_range(ss, &range)); invariant(range == 0); // Release resources in the session we're about to cache. // If we are using hybrid caching, then close cursors now and let them // be cached at the WiredTiger level. if (kWiredTigerCursorCacheSize.load() < 0) { session->closeAllCursors(""); } invariantWTOK(ss->reset(ss)); } // If the cursor epoch has moved on, close all cursors in the session. uint64_t cursorEpoch = _cursorEpoch.load(); if (session->_getCursorEpoch() != cursorEpoch) session->closeCursorsForQueuedDrops(_engine); bool returnedToCache = false; uint64_t currentEpoch = _epoch.load(); bool dropQueuedIdentsAtSessionEnd = session->isDropQueuedIdentsAtSessionEndAllowed(); // Reset this session's flag for dropping queued idents to default, before returning it to // session cache. session->dropQueuedIdentsAtSessionEndAllowed(true); if (session->_getEpoch() == currentEpoch) { // check outside of lock to reduce contention stdx::lock_guard<stdx::mutex> lock(_cacheLock); if (session->_getEpoch() == _epoch.load()) { // recheck inside the lock for correctness returnedToCache = true; _sessions.push_back(session); } } else invariant(session->_getEpoch() < currentEpoch); if (!returnedToCache) delete session; if (dropQueuedIdentsAtSessionEnd && _engine && _engine->haveDropsQueued()) _engine->dropSomeQueuedIdents(); }
static void transaction_ops(WT_SESSION *session_arg) { WT_CONNECTION *conn; WT_CURSOR *cursor; WT_SESSION *session; session = session_arg; conn = session->connection; /*! [transaction commit/rollback] */ /* * Cursors may be opened before or after the transaction begins, and in * either case, subsequent operations are included in the transaction. * Opening cursors before the transaction begins allows applications to * cache cursors and use them for multiple operations. */ error_check(session->open_cursor( session, "table:mytable", NULL, NULL, &cursor)); error_check(session->begin_transaction(session, NULL)); cursor->set_key(cursor, "key"); cursor->set_value(cursor, "value"); switch (cursor->update(cursor)) { case 0: /* Update success */ error_check(session->commit_transaction(session, NULL)); /* * If commit_transaction succeeds, cursors remain positioned; if * commit_transaction fails, the transaction was rolled-back and * and all cursors are reset. */ break; case WT_ROLLBACK: /* Update conflict */ default: /* Other error */ error_check(session->rollback_transaction(session, NULL)); /* The rollback_transaction call resets all cursors. */ break; } /* * Cursors remain open and may be used for multiple transactions. */ /*! [transaction commit/rollback] */ error_check(cursor->close(cursor)); /*! [transaction isolation] */ /* A single transaction configured for snapshot isolation. */ error_check(session->open_cursor( session, "table:mytable", NULL, NULL, &cursor)); error_check(session->begin_transaction(session, "isolation=snapshot")); cursor->set_key(cursor, "some-key"); cursor->set_value(cursor, "some-value"); error_check(cursor->update(cursor)); error_check(session->commit_transaction(session, NULL)); /*! [transaction isolation] */ { /*! [transaction prepare] */ /* * Prepare a transaction which guarantees a subsequent commit will * succeed. Only commit and rollback are allowed on a transaction after * it has been prepared. */ error_check(session->open_cursor( session, "table:mytable", NULL, NULL, &cursor)); error_check(session->begin_transaction(session, NULL)); cursor->set_key(cursor, "key"); cursor->set_value(cursor, "value"); error_check(session->prepare_transaction( session, "prepare_timestamp=2a")); error_check(session->commit_transaction( session, "commit_timestamp=2b")); /*! [transaction prepare] */ } /*! [session isolation configuration] */ /* Open a session configured for read-uncommitted isolation. */ error_check(conn->open_session( conn, NULL, "isolation=read-uncommitted", &session)); /*! [session isolation configuration] */ /*! [session isolation re-configuration] */ /* Re-configure a session for snapshot isolation. */ error_check(session->reconfigure(session, "isolation=snapshot")); /*! [session isolation re-configuration] */ error_check(session->close(session, NULL)); session = session_arg; { /*! [transaction pinned range] */ /* Check the transaction ID range pinned by the session handle. */ uint64_t range; error_check(session->transaction_pinned_range(session, &range)); /*! [transaction pinned range] */ } error_check(session->begin_transaction(session, NULL)); { /*! [query timestamp] */ char timestamp_buf[2 * sizeof(uint64_t) + 1]; /*! [transaction timestamp] */ error_check( session->timestamp_transaction(session, "commit_timestamp=2a")); /*! [transaction timestamp] */ error_check(session->commit_transaction(session, NULL)); error_check(conn->query_timestamp( conn, timestamp_buf, "get=all_committed")); /*! [query timestamp] */ } /*! [set commit timestamp] */ error_check(conn->set_timestamp(conn, "commit_timestamp=2a")); /*! [set commit timestamp] */ /*! [set oldest timestamp] */ error_check(conn->set_timestamp(conn, "oldest_timestamp=2a")); /*! [set oldest timestamp] */ /*! [set stable timestamp] */ error_check(conn->set_timestamp(conn, "stable_timestamp=2a")); /*! [set stable timestamp] */ /*! [rollback to stable] */ error_check(conn->rollback_to_stable(conn, NULL)); /*! [rollback to stable] */ }