diff options
Diffstat (limited to 'src/mongo/db')
-rw-r--r-- | src/mongo/db/SConscript | 10 | ||||
-rw-r--r-- | src/mongo/db/catalog/capped_utils.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/catalog/collection_impl.cpp | 14 | ||||
-rw-r--r-- | src/mongo/db/catalog/index_create_impl.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/commands/find_and_modify.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/concurrency/d_concurrency_test.cpp | 7 | ||||
-rw-r--r-- | src/mongo/db/concurrency/write_conflict_exception.h | 2 | ||||
-rw-r--r-- | src/mongo/db/curop.cpp | 155 | ||||
-rw-r--r-- | src/mongo/db/curop.h | 82 | ||||
-rw-r--r-- | src/mongo/db/curop_metrics.cpp | 24 | ||||
-rw-r--r-- | src/mongo/db/curop_test.cpp | 172 | ||||
-rw-r--r-- | src/mongo/db/exec/update.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/ops/write_ops_exec.cpp | 12 | ||||
-rw-r--r-- | src/mongo/db/query/plan_executor.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/query/plan_yield_policy.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/storage/wiredtiger/wiredtiger_prepare_conflict.h | 2 |
16 files changed, 389 insertions, 105 deletions
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index 7739779e9cc..527e2ce7af6 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -213,6 +213,16 @@ env.Library( ], ) +env.CppUnitTest( + target='curop_test', + source=[ + 'curop_test.cpp', + ], + LIBDEPS=[ + 'curop', + ], +) + env.Library( target='curop_failpoint_helpers', source=[ diff --git a/src/mongo/db/catalog/capped_utils.cpp b/src/mongo/db/catalog/capped_utils.cpp index d89fcfe4177..4801b92ca73 100644 --- a/src/mongo/db/catalog/capped_utils.cpp +++ b/src/mongo/db/catalog/capped_utils.cpp @@ -231,7 +231,7 @@ mongo::Status mongo::cloneCollectionAsCapped(OperationContext* opCtx, // Go to the next document retries = 0; } catch (const WriteConflictException&) { - CurOp::get(opCtx)->debug().writeConflicts++; + CurOp::get(opCtx)->debug().additiveMetrics.incrementWriteConflicts(1); retries++; // logAndBackoff expects this to be 1 on first call. WriteConflictException::logAndBackoff(retries, "cloneCollectionAsCapped", fromNss.ns()); diff --git a/src/mongo/db/catalog/collection_impl.cpp b/src/mongo/db/catalog/collection_impl.cpp index de2fe045acc..8ef6a71b469 100644 --- a/src/mongo/db/catalog/collection_impl.cpp +++ b/src/mongo/db/catalog/collection_impl.cpp @@ -504,7 +504,7 @@ Status CollectionImpl::_insertDocuments(OperationContext* opCtx, int64_t keysInserted; status = _indexCatalog.indexRecords(opCtx, bsonRecords, &keysInserted); if (opDebug) { - opDebug->keysInserted += keysInserted; + opDebug->additiveMetrics.incrementKeysInserted(keysInserted); } return status; @@ -569,7 +569,7 @@ void CollectionImpl::deleteDocument(OperationContext* opCtx, int64_t keysDeleted; _indexCatalog.unindexRecord(opCtx, doc.value(), loc, noWarn, &keysDeleted); if (opDebug) { - opDebug->keysDeleted += keysDeleted; + opDebug->additiveMetrics.incrementKeysDeleted(keysDeleted); } _recordStore->deleteRecord(opCtx, loc); @@ -686,8 +686,8 @@ RecordId CollectionImpl::updateDocument(OperationContext* opCtx, uassertStatusOK(iam->update( opCtx, *updateTickets.mutableMap()[descriptor], &keysInserted, &keysDeleted)); if (opDebug) { - opDebug->keysInserted += keysInserted; - opDebug->keysDeleted += keysDeleted; + opDebug->additiveMetrics.incrementKeysInserted(keysInserted); + opDebug->additiveMetrics.incrementKeysDeleted(keysDeleted); } } } @@ -747,9 +747,9 @@ StatusWith<RecordId> CollectionImpl::_updateDocumentWithMove(OperationContext* o moveCounter.increment(); if (opDebug) { - opDebug->nmoved++; - opDebug->keysInserted += keysInserted; - opDebug->keysDeleted += keysDeleted; + opDebug->additiveMetrics.incrementNmoved(1); + opDebug->additiveMetrics.incrementKeysInserted(keysInserted); + opDebug->additiveMetrics.incrementKeysDeleted(keysDeleted); } return newLocation; diff --git a/src/mongo/db/catalog/index_create_impl.cpp b/src/mongo/db/catalog/index_create_impl.cpp index 7f3c45a2f28..9f34942e4ee 100644 --- a/src/mongo/db/catalog/index_create_impl.cpp +++ b/src/mongo/db/catalog/index_create_impl.cpp @@ -424,7 +424,7 @@ Status MultiIndexBlockImpl::insertAllDocumentsInCollection(std::set<RecordId>* d n++; retries = 0; } catch (const WriteConflictException&) { - CurOp::get(_opCtx)->debug().writeConflicts++; + CurOp::get(_opCtx)->debug().additiveMetrics.incrementWriteConflicts(1); retries++; // logAndBackoff expects this to be 1 on first call. WriteConflictException::logAndBackoff( retries, "index creation", _collection->ns().ns()); diff --git a/src/mongo/db/commands/find_and_modify.cpp b/src/mongo/db/commands/find_and_modify.cpp index c097d9f1ea4..4c70636f928 100644 --- a/src/mongo/db/commands/find_and_modify.cpp +++ b/src/mongo/db/commands/find_and_modify.cpp @@ -417,7 +417,7 @@ public: opDebug->setPlanSummaryMetrics(summaryStats); // Fill out OpDebug with the number of deleted docs. - opDebug->ndeleted = getDeleteStats(exec.get())->docsDeleted; + opDebug->additiveMetrics.ndeleted = getDeleteStats(exec.get())->docsDeleted; if (curOp->shouldDBProfile()) { BSONObjBuilder execStatsBob; diff --git a/src/mongo/db/concurrency/d_concurrency_test.cpp b/src/mongo/db/concurrency/d_concurrency_test.cpp index 145f4f31846..d54f2896c62 100644 --- a/src/mongo/db/concurrency/d_concurrency_test.cpp +++ b/src/mongo/db/concurrency/d_concurrency_test.cpp @@ -30,6 +30,7 @@ #include "mongo/platform/basic.h" +#include <boost/optional/optional_io.hpp> #include <string> #include <vector> @@ -132,14 +133,14 @@ TEST_F(DConcurrencyTestFixture, WriteConflictRetryRetriesFunctionOnWriteConflict auto opCtx = makeOperationContext(); opCtx->swapLockState(stdx::make_unique<MMAPV1LockerImpl>()); auto&& opDebug = CurOp::get(opCtx.get())->debug(); - ASSERT_EQUALS(0LL, opDebug.writeConflicts); + ASSERT_EQUALS(boost::none, opDebug.additiveMetrics.writeConflicts); ASSERT_EQUALS(100, writeConflictRetry(opCtx.get(), "", "", [&opDebug] { - if (opDebug.writeConflicts == 0LL) { + if (!opDebug.additiveMetrics.writeConflicts) { throw WriteConflictException(); } return 100; })); - ASSERT_EQUALS(1LL, opDebug.writeConflicts); + ASSERT_EQUALS(1LL, *opDebug.additiveMetrics.writeConflicts); } TEST_F(DConcurrencyTestFixture, WriteConflictRetryPropagatesNonWriteConflictException) { diff --git a/src/mongo/db/concurrency/write_conflict_exception.h b/src/mongo/db/concurrency/write_conflict_exception.h index 7b7e1cc1e9a..193ebfb112e 100644 --- a/src/mongo/db/concurrency/write_conflict_exception.h +++ b/src/mongo/db/concurrency/write_conflict_exception.h @@ -90,7 +90,7 @@ auto writeConflictRetry(OperationContext* opCtx, StringData opStr, StringData ns try { return f(); } catch (WriteConflictException const&) { - ++CurOp::get(opCtx)->debug().writeConflicts; + CurOp::get(opCtx)->debug().additiveMetrics.incrementWriteConflicts(1); WriteConflictException::logAndBackoff(attempts, opStr, ns); ++attempts; opCtx->recoveryUnit()->abandonSnapshot(); diff --git a/src/mongo/db/curop.cpp b/src/mongo/db/curop.cpp index 28091bfd359..08fb567b729 100644 --- a/src/mongo/db/curop.cpp +++ b/src/mongo/db/curop.cpp @@ -496,6 +496,9 @@ StringData getProtoString(int op) { #define OPDEBUG_TOSTRING_HELP_BOOL(x) \ if (x) \ s << " " #x ":" << (x) +#define OPDEBUG_TOSTRING_HELP_OPTIONAL(x, y) \ + if (y) \ + s << " " x ":" << (*y) string OpDebug::report(Client* client, const CurOp& curop, @@ -553,38 +556,24 @@ string OpDebug::report(Client* client, OPDEBUG_TOSTRING_HELP(ntoskip); OPDEBUG_TOSTRING_HELP_BOOL(exhaust); - OPDEBUG_TOSTRING_HELP(keysExamined); - OPDEBUG_TOSTRING_HELP(docsExamined); + OPDEBUG_TOSTRING_HELP_OPTIONAL("keysExamined", additiveMetrics.keysExamined); + OPDEBUG_TOSTRING_HELP_OPTIONAL("docsExamined", additiveMetrics.docsExamined); OPDEBUG_TOSTRING_HELP_BOOL(hasSortStage); OPDEBUG_TOSTRING_HELP_BOOL(fromMultiPlanner); OPDEBUG_TOSTRING_HELP_BOOL(replanned); - OPDEBUG_TOSTRING_HELP(nMatched); - OPDEBUG_TOSTRING_HELP(nModified); - OPDEBUG_TOSTRING_HELP(ninserted); - OPDEBUG_TOSTRING_HELP(ndeleted); + OPDEBUG_TOSTRING_HELP_OPTIONAL("nMatched", additiveMetrics.nMatched); + OPDEBUG_TOSTRING_HELP_OPTIONAL("nModified", additiveMetrics.nModified); + OPDEBUG_TOSTRING_HELP_OPTIONAL("ninserted", additiveMetrics.ninserted); + OPDEBUG_TOSTRING_HELP_OPTIONAL("ndeleted", additiveMetrics.ndeleted); OPDEBUG_TOSTRING_HELP_BOOL(fastmodinsert); OPDEBUG_TOSTRING_HELP_BOOL(upsert); OPDEBUG_TOSTRING_HELP_BOOL(cursorExhausted); - if (nmoved > 0) { - s << " nmoved:" << nmoved; - } - - if (keysInserted > 0) { - s << " keysInserted:" << keysInserted; - } - - if (keysDeleted > 0) { - s << " keysDeleted:" << keysDeleted; - } - - if (prepareReadConflicts > 0) { - s << " prepareReadConflicts:" << prepareReadConflicts; - } - - if (writeConflicts > 0) { - s << " writeConflicts:" << writeConflicts; - } + OPDEBUG_TOSTRING_HELP_OPTIONAL("nmoved", additiveMetrics.nmoved); + OPDEBUG_TOSTRING_HELP_OPTIONAL("keysInserted", additiveMetrics.keysInserted); + OPDEBUG_TOSTRING_HELP_OPTIONAL("keysDeleted", additiveMetrics.keysDeleted); + OPDEBUG_TOSTRING_HELP_OPTIONAL("prepareReadConflicts", additiveMetrics.prepareReadConflicts); + OPDEBUG_TOSTRING_HELP_OPTIONAL("writeConflicts", additiveMetrics.writeConflicts); s << " numYields:" << curop.numYields(); OPDEBUG_TOSTRING_HELP(nreturned); @@ -623,6 +612,9 @@ string OpDebug::report(Client* client, #define OPDEBUG_APPEND_BOOL(x) \ if (x) \ b.appendBool(#x, (x)) +#define OPDEBUG_APPEND_OPTIONAL(x, y) \ + if (y) \ + b.appendNumber(x, (*y)) void OpDebug::append(const CurOp& curop, const SingleThreadedLockStats& lockStats, @@ -645,38 +637,24 @@ void OpDebug::append(const CurOp& curop, OPDEBUG_APPEND_NUMBER(cursorid); OPDEBUG_APPEND_BOOL(exhaust); - OPDEBUG_APPEND_NUMBER(keysExamined); - OPDEBUG_APPEND_NUMBER(docsExamined); + OPDEBUG_APPEND_OPTIONAL("keysExamined", additiveMetrics.keysExamined); + OPDEBUG_APPEND_OPTIONAL("docsExamined", additiveMetrics.docsExamined); OPDEBUG_APPEND_BOOL(hasSortStage); OPDEBUG_APPEND_BOOL(fromMultiPlanner); OPDEBUG_APPEND_BOOL(replanned); - OPDEBUG_APPEND_NUMBER(nMatched); - OPDEBUG_APPEND_NUMBER(nModified); - OPDEBUG_APPEND_NUMBER(ninserted); - OPDEBUG_APPEND_NUMBER(ndeleted); + OPDEBUG_APPEND_OPTIONAL("nMatched", additiveMetrics.nMatched); + OPDEBUG_APPEND_OPTIONAL("nModified", additiveMetrics.nModified); + OPDEBUG_APPEND_OPTIONAL("ninserted", additiveMetrics.ninserted); + OPDEBUG_APPEND_OPTIONAL("ndeleted", additiveMetrics.ndeleted); OPDEBUG_APPEND_BOOL(fastmodinsert); OPDEBUG_APPEND_BOOL(upsert); OPDEBUG_APPEND_BOOL(cursorExhausted); - if (nmoved > 0) { - b.appendNumber("nmoved", nmoved); - } - - if (keysInserted > 0) { - b.appendNumber("keysInserted", keysInserted); - } - - if (keysDeleted > 0) { - b.appendNumber("keysDeleted", keysDeleted); - } - - if (prepareReadConflicts > 0) { - b.appendNumber("prepareReadConflicts", prepareReadConflicts); - } - - if (writeConflicts > 0) { - b.appendNumber("writeConflicts", writeConflicts); - } + OPDEBUG_APPEND_OPTIONAL("nmoved", additiveMetrics.nmoved); + OPDEBUG_APPEND_OPTIONAL("keysInserted", additiveMetrics.keysInserted); + OPDEBUG_APPEND_OPTIONAL("keysDeleted", additiveMetrics.keysDeleted); + OPDEBUG_APPEND_OPTIONAL("prepareReadConflicts", additiveMetrics.prepareReadConflicts); + OPDEBUG_APPEND_OPTIONAL("writeConflicts", additiveMetrics.writeConflicts); b.appendNumber("numYield", curop.numYields()); OPDEBUG_APPEND_NUMBER(nreturned); @@ -711,11 +689,84 @@ void OpDebug::append(const CurOp& curop, } void OpDebug::setPlanSummaryMetrics(const PlanSummaryStats& planSummaryStats) { - keysExamined = planSummaryStats.totalKeysExamined; - docsExamined = planSummaryStats.totalDocsExamined; + additiveMetrics.keysExamined = planSummaryStats.totalKeysExamined; + additiveMetrics.docsExamined = planSummaryStats.totalDocsExamined; hasSortStage = planSummaryStats.hasSortStage; fromMultiPlanner = planSummaryStats.fromMultiPlanner; replanned = planSummaryStats.replanned; } +namespace { + +/** + * Adds two boost::optional long longs together. Returns boost::none if both 'lhs' and 'rhs' are + * uninitialized, or the sum of 'lhs' and 'rhs' if they are both initialized. Returns 'lhs' if only + * 'rhs' is uninitialized and vice-versa. + */ +boost::optional<long long> addOptionalLongs(const boost::optional<long long>& lhs, + const boost::optional<long long>& rhs) { + if (!rhs) { + return lhs; + } + return lhs ? (*lhs + *rhs) : rhs; +} +} // namespace + +void OpDebug::AdditiveMetrics::add(const AdditiveMetrics& otherMetrics) { + keysExamined = addOptionalLongs(keysExamined, otherMetrics.keysExamined); + docsExamined = addOptionalLongs(docsExamined, otherMetrics.docsExamined); + nMatched = addOptionalLongs(nMatched, otherMetrics.nMatched); + nModified = addOptionalLongs(nModified, otherMetrics.nModified); + ninserted = addOptionalLongs(ninserted, otherMetrics.ninserted); + ndeleted = addOptionalLongs(ndeleted, otherMetrics.ndeleted); + nmoved = addOptionalLongs(nmoved, otherMetrics.nmoved); + keysInserted = addOptionalLongs(keysInserted, otherMetrics.keysInserted); + keysDeleted = addOptionalLongs(keysDeleted, otherMetrics.keysDeleted); + prepareReadConflicts = + addOptionalLongs(prepareReadConflicts, otherMetrics.prepareReadConflicts); + writeConflicts = addOptionalLongs(writeConflicts, otherMetrics.writeConflicts); +} + +void OpDebug::AdditiveMetrics::incrementWriteConflicts(long long n) { + if (!writeConflicts) { + writeConflicts = 0; + } + *writeConflicts += n; +} + +void OpDebug::AdditiveMetrics::incrementKeysInserted(long long n) { + if (!keysInserted) { + keysInserted = 0; + } + *keysInserted += n; +} + +void OpDebug::AdditiveMetrics::incrementKeysDeleted(long long n) { + if (!keysDeleted) { + keysDeleted = 0; + } + *keysDeleted += n; +} + +void OpDebug::AdditiveMetrics::incrementNmoved(long long n) { + if (!nmoved) { + nmoved = 0; + } + *nmoved += n; +} + +void OpDebug::AdditiveMetrics::incrementNinserted(long long n) { + if (!ninserted) { + ninserted = 0; + } + *ninserted += n; +} + +void OpDebug::AdditiveMetrics::incrementPrepareReadConflicts(long long n) { + if (!prepareReadConflicts) { + prepareReadConflicts = 0; + } + *prepareReadConflicts += n; +} + } // namespace mongo diff --git a/src/mongo/db/curop.h b/src/mongo/db/curop.h index a0d40523c84..bca2dbcac30 100644 --- a/src/mongo/db/curop.h +++ b/src/mongo/db/curop.h @@ -50,6 +50,69 @@ struct PlanSummaryStats; /* lifespan is different than CurOp because of recursives with DBDirectClient */ class OpDebug { public: + /** + * Holds counters for execution statistics that are meaningful both for multi-statement + * transactions and for individual operations outside of a transaction. + */ + class AdditiveMetrics { + public: + /** + * Adds all the fields of another AdditiveMetrics object together with the fields of this + * AdditiveMetrics instance. + */ + void add(const AdditiveMetrics& otherMetrics); + + /** + * Increments writeConflicts by n. + */ + void incrementWriteConflicts(long long n); + + /** + * Increments keysInserted by n. + */ + void incrementKeysInserted(long long n); + + /** + * Increments keysDeleted by n. + */ + void incrementKeysDeleted(long long n); + + /** + * Increments nmoved by n. + */ + void incrementNmoved(long long n); + + /** + * Increments ninserted by n. + */ + void incrementNinserted(long long n); + + /** + * Increments prepareReadConflicts by n. + */ + void incrementPrepareReadConflicts(long long n); + + boost::optional<long long> keysExamined; + boost::optional<long long> docsExamined; + + // Number of records that match the query. + boost::optional<long long> nMatched; + // Number of records written (no no-ops). + boost::optional<long long> nModified; + boost::optional<long long> ninserted; + boost::optional<long long> ndeleted; + + // Updates resulted in a move (moves are expensive). + boost::optional<long long> nmoved; + // Number of index keys inserted. + boost::optional<long long> keysInserted; + // Number of index keys removed. + boost::optional<long long> keysDeleted; + // Number of read conflicts caused by a prepared transaction. + boost::optional<long long> prepareReadConflicts; + boost::optional<long long> writeConflicts; + }; + OpDebug() = default; std::string report(Client* client, @@ -88,10 +151,6 @@ public: long long ntoskip{-1}; bool exhaust{false}; - // debugging/profile info - long long keysExamined{-1}; - long long docsExamined{-1}; - bool hasSortStage{false}; // true if the query plan involves an in-memory sort // True if the plan came from the multi-planner (not from the plan cache and not a query with a @@ -101,23 +160,11 @@ public: // True if a replan was triggered during the execution of this operation. bool replanned{false}; - long long nMatched{-1}; // number of records that match the query - long long nModified{-1}; // number of records written (no no-ops) - long long ninserted{-1}; - long long ndeleted{-1}; bool fastmodinsert{false}; // upsert of an $operation. builds a default object bool upsert{false}; // true if the update actually did an insert bool cursorExhausted{ false}; // true if the cursor has been closed at end a find/getMore operation - // The following metrics are initialized with 0 rather than -1 in order to simplify use by the - // CRUD path. - long long nmoved{0}; // updates resulted in a move (moves are expensive) - long long keysInserted{0}; // Number of index keys inserted. - long long keysDeleted{0}; // Number of index keys removed. - long long prepareReadConflicts{0}; // Number of read conflicts caused by a prepared transaction - long long writeConflicts{0}; - BSONObj execStats; // Owned here. // Details of any error (whether from an exception or a command returning failure). @@ -130,6 +177,9 @@ public: // Shard targeting info. int nShards{-1}; + + // Stores additive metrics. + AdditiveMetrics additiveMetrics; }; /** diff --git a/src/mongo/db/curop_metrics.cpp b/src/mongo/db/curop_metrics.cpp index be2ed8e00de..ac96709053a 100644 --- a/src/mongo/db/curop_metrics.cpp +++ b/src/mongo/db/curop_metrics.cpp @@ -64,21 +64,21 @@ void recordCurOpMetrics(OperationContext* opCtx) { const OpDebug& debug = CurOp::get(opCtx)->debug(); if (debug.nreturned > 0) returnedCounter.increment(debug.nreturned); - if (debug.ninserted > 0) - insertedCounter.increment(debug.ninserted); - if (debug.nMatched > 0) - updatedCounter.increment(debug.nMatched); - if (debug.ndeleted > 0) - deletedCounter.increment(debug.ndeleted); - if (debug.keysExamined > 0) - scannedCounter.increment(debug.keysExamined); - if (debug.docsExamined > 0) - scannedObjectCounter.increment(debug.docsExamined); + if (debug.additiveMetrics.ninserted) + insertedCounter.increment(*debug.additiveMetrics.ninserted); + if (debug.additiveMetrics.nMatched) + updatedCounter.increment(*debug.additiveMetrics.nMatched); + if (debug.additiveMetrics.ndeleted) + deletedCounter.increment(*debug.additiveMetrics.ndeleted); + if (debug.additiveMetrics.keysExamined) + scannedCounter.increment(*debug.additiveMetrics.keysExamined); + if (debug.additiveMetrics.docsExamined) + scannedObjectCounter.increment(*debug.additiveMetrics.docsExamined); if (debug.hasSortStage) scanAndOrderCounter.increment(); - if (debug.writeConflicts) - writeConflictsCounter.increment(debug.writeConflicts); + if (debug.additiveMetrics.writeConflicts) + writeConflictsCounter.increment(*debug.additiveMetrics.writeConflicts); } } // namespace mongo diff --git a/src/mongo/db/curop_test.cpp b/src/mongo/db/curop_test.cpp new file mode 100644 index 00000000000..e0e281d336a --- /dev/null +++ b/src/mongo/db/curop_test.cpp @@ -0,0 +1,172 @@ +/** + * Copyright (C) 2018 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects + * for all of the code used other than as permitted herein. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you do not + * wish to do so, delete this exception statement from your version. If you + * delete this exception statement from all source files in the program, + * then also delete it in the license file. + */ + +#include "mongo/platform/basic.h" + +#include <boost/optional/optional_io.hpp> + +#include "mongo/db/curop.h" +#include "mongo/unittest/unittest.h" + +namespace mongo { +namespace { + +TEST(CurOpTest, AddingAdditiveMetricsObjectsTogetherShouldAddFieldsTogether) { + OpDebug::AdditiveMetrics currentAdditiveMetrics = OpDebug::AdditiveMetrics(); + OpDebug::AdditiveMetrics additiveMetricsToAdd = OpDebug::AdditiveMetrics(); + + // Initialize field values for both AdditiveMetrics objects. + currentAdditiveMetrics.keysExamined = 0; + additiveMetricsToAdd.keysExamined = 2; + currentAdditiveMetrics.docsExamined = 4; + additiveMetricsToAdd.docsExamined = 2; + currentAdditiveMetrics.nMatched = 5; + additiveMetricsToAdd.nMatched = 5; + currentAdditiveMetrics.nModified = 3; + additiveMetricsToAdd.nModified = 1; + currentAdditiveMetrics.ninserted = 4; + additiveMetricsToAdd.ninserted = 0; + currentAdditiveMetrics.ndeleted = 3; + additiveMetricsToAdd.ndeleted = 2; + currentAdditiveMetrics.nmoved = 0; + additiveMetricsToAdd.nmoved = 4; + currentAdditiveMetrics.keysInserted = 6; + additiveMetricsToAdd.keysInserted = 5; + currentAdditiveMetrics.keysDeleted = 4; + additiveMetricsToAdd.keysDeleted = 2; + currentAdditiveMetrics.prepareReadConflicts = 1; + additiveMetricsToAdd.prepareReadConflicts = 5; + currentAdditiveMetrics.writeConflicts = 7; + additiveMetricsToAdd.writeConflicts = 0; + + // Save the current AdditiveMetrics object before adding. + OpDebug::AdditiveMetrics additiveMetricsBeforeAdd = currentAdditiveMetrics; + currentAdditiveMetrics.add(additiveMetricsToAdd); + + // The following field values should have changed after adding. + ASSERT_EQ(*currentAdditiveMetrics.keysExamined, + *additiveMetricsBeforeAdd.keysExamined + *additiveMetricsToAdd.keysExamined); + ASSERT_EQ(*currentAdditiveMetrics.docsExamined, + *additiveMetricsBeforeAdd.docsExamined + *additiveMetricsToAdd.docsExamined); + ASSERT_EQ(*currentAdditiveMetrics.nMatched, + *additiveMetricsBeforeAdd.nMatched + *additiveMetricsToAdd.nMatched); + ASSERT_EQ(*currentAdditiveMetrics.nModified, + *additiveMetricsBeforeAdd.nModified + *additiveMetricsToAdd.nModified); + ASSERT_EQ(*currentAdditiveMetrics.ninserted, + *additiveMetricsBeforeAdd.ninserted + *additiveMetricsToAdd.ninserted); + ASSERT_EQ(*currentAdditiveMetrics.ndeleted, + *additiveMetricsBeforeAdd.ndeleted + *additiveMetricsToAdd.ndeleted); + ASSERT_EQ(*currentAdditiveMetrics.nmoved, + *additiveMetricsBeforeAdd.nmoved + *additiveMetricsToAdd.nmoved); + ASSERT_EQ(*currentAdditiveMetrics.keysInserted, + *additiveMetricsBeforeAdd.keysInserted + *additiveMetricsToAdd.keysInserted); + ASSERT_EQ(*currentAdditiveMetrics.keysDeleted, + *additiveMetricsBeforeAdd.keysDeleted + *additiveMetricsToAdd.keysDeleted); + ASSERT_EQ(*currentAdditiveMetrics.prepareReadConflicts, + *additiveMetricsBeforeAdd.prepareReadConflicts + + *additiveMetricsToAdd.prepareReadConflicts); + ASSERT_EQ(*currentAdditiveMetrics.writeConflicts, + *additiveMetricsBeforeAdd.writeConflicts + *additiveMetricsToAdd.writeConflicts); +} + +TEST(CurOpTest, AddingUninitializedAdditiveMetricsFieldsShouldBeTreatedAsZero) { + OpDebug::AdditiveMetrics currentAdditiveMetrics = OpDebug::AdditiveMetrics(); + OpDebug::AdditiveMetrics additiveMetricsToAdd = OpDebug::AdditiveMetrics(); + + // Initialize field values for both AdditiveMetrics objects. + additiveMetricsToAdd.keysExamined = 5; + currentAdditiveMetrics.docsExamined = 4; + currentAdditiveMetrics.nModified = 3; + additiveMetricsToAdd.ninserted = 0; + currentAdditiveMetrics.nmoved = 0; + additiveMetricsToAdd.nmoved = 4; + currentAdditiveMetrics.keysInserted = 6; + additiveMetricsToAdd.keysInserted = 5; + currentAdditiveMetrics.keysDeleted = 4; + additiveMetricsToAdd.keysDeleted = 2; + currentAdditiveMetrics.prepareReadConflicts = 1; + additiveMetricsToAdd.prepareReadConflicts = 5; + currentAdditiveMetrics.writeConflicts = 7; + additiveMetricsToAdd.writeConflicts = 0; + + // Save the current AdditiveMetrics object before adding. + OpDebug::AdditiveMetrics additiveMetricsBeforeAdd = currentAdditiveMetrics; + currentAdditiveMetrics.add(additiveMetricsToAdd); + + // The 'keysExamined' field for the current AdditiveMetrics object was not initialized, so it + // should be treated as zero. + ASSERT_EQ(*currentAdditiveMetrics.keysExamined, *additiveMetricsToAdd.keysExamined); + + // The 'docsExamined' field for the AdditiveMetrics object to add was not initialized, so it + // should be treated as zero. + ASSERT_EQ(*currentAdditiveMetrics.docsExamined, *additiveMetricsBeforeAdd.docsExamined); + + // The 'nMatched' field for both the current AdditiveMetrics object and the AdditiveMetrics + // object to add were not initialized, so nMatched should still be uninitialized after the add. + ASSERT_EQ(currentAdditiveMetrics.nMatched, boost::none); + + // The following field values should have changed after adding. + ASSERT_EQ(*currentAdditiveMetrics.nmoved, + *additiveMetricsBeforeAdd.nmoved + *additiveMetricsToAdd.nmoved); + ASSERT_EQ(*currentAdditiveMetrics.keysInserted, + *additiveMetricsBeforeAdd.keysInserted + *additiveMetricsToAdd.keysInserted); + ASSERT_EQ(*currentAdditiveMetrics.keysDeleted, + *additiveMetricsBeforeAdd.keysDeleted + *additiveMetricsToAdd.keysDeleted); + ASSERT_EQ(*currentAdditiveMetrics.prepareReadConflicts, + *additiveMetricsBeforeAdd.prepareReadConflicts + + *additiveMetricsToAdd.prepareReadConflicts); + ASSERT_EQ(*currentAdditiveMetrics.writeConflicts, + *additiveMetricsBeforeAdd.writeConflicts + *additiveMetricsToAdd.writeConflicts); +} + +TEST(CurOpTest, AdditiveMetricsFieldsShouldIncrementByN) { + OpDebug::AdditiveMetrics additiveMetrics = OpDebug::AdditiveMetrics(); + + // Initialize field values. + additiveMetrics.writeConflicts = 1; + additiveMetrics.keysInserted = 2; + additiveMetrics.prepareReadConflicts = 6; + + // Increment the fields. + additiveMetrics.incrementWriteConflicts(1); + additiveMetrics.incrementKeysInserted(5); + additiveMetrics.incrementKeysDeleted(0); + additiveMetrics.incrementNmoved(1); + additiveMetrics.incrementNinserted(3); + additiveMetrics.incrementPrepareReadConflicts(2); + + ASSERT_EQ(*additiveMetrics.writeConflicts, 2); + ASSERT_EQ(*additiveMetrics.keysInserted, 7); + ASSERT_EQ(*additiveMetrics.keysDeleted, 0); + ASSERT_EQ(*additiveMetrics.nmoved, 1); + ASSERT_EQ(*additiveMetrics.ninserted, 3); + ASSERT_EQ(*additiveMetrics.prepareReadConflicts, 8); +} + +} // namespace +} // namespace mongo diff --git a/src/mongo/db/exec/update.cpp b/src/mongo/db/exec/update.cpp index a044324197c..0673af4c73d 100644 --- a/src/mongo/db/exec/update.cpp +++ b/src/mongo/db/exec/update.cpp @@ -757,8 +757,8 @@ const UpdateStats* UpdateStage::getUpdateStats(const PlanExecutor* exec) { void UpdateStage::recordUpdateStatsInOpDebug(const UpdateStats* updateStats, OpDebug* opDebug) { invariant(opDebug); - opDebug->nMatched = updateStats->nMatched; - opDebug->nModified = updateStats->nModified; + opDebug->additiveMetrics.nMatched = updateStats->nMatched; + opDebug->additiveMetrics.nModified = updateStats->nModified; opDebug->upsert = updateStats->inserted; opDebug->fastmodinsert = updateStats->fastmodinsert; } diff --git a/src/mongo/db/ops/write_ops_exec.cpp b/src/mongo/db/ops/write_ops_exec.cpp index afea9ad96eb..9718457f0be 100644 --- a/src/mongo/db/ops/write_ops_exec.cpp +++ b/src/mongo/db/ops/write_ops_exec.cpp @@ -290,7 +290,7 @@ SingleWriteResult createIndex(OperationContext* opCtx, // Unlike normal inserts, it is not an error to "insert" a duplicate index. long long n = cmdResult["numIndexesAfter"].numberInt() - cmdResult["numIndexesBefore"].numberInt(); - CurOp::get(opCtx)->debug().ninserted += n; + CurOp::get(opCtx)->debug().additiveMetrics.incrementNinserted(n); SingleWriteResult result; result.setN(n); @@ -410,7 +410,7 @@ bool insertBatchAndHandleErrors(OperationContext* opCtx, result.setN(1); std::fill_n(std::back_inserter(out->results), batch.size(), std::move(result)); - curOp.debug().ninserted += batch.size(); + curOp.debug().additiveMetrics.incrementNinserted(batch.size()); return true; } } catch (const DBException&) { @@ -441,7 +441,7 @@ bool insertBatchAndHandleErrors(OperationContext* opCtx, SingleWriteResult result; result.setN(1); out->results.emplace_back(std::move(result)); - curOp.debug().ninserted++; + curOp.debug().additiveMetrics.incrementNinserted(1); } catch (...) { // Release the lock following any error if we are not in multi-statement // transaction. Among other things, this ensures that we don't sleep in the WCE @@ -505,7 +505,7 @@ WriteResult performInserts(OperationContext* opCtx, const write_ops::Insert& who curOp.setNS_inlock(wholeOp.getNamespace().ns()); curOp.setLogicalOp_inlock(LogicalOp::opInsert); curOp.ensureStarted(); - curOp.debug().ninserted = 0; + curOp.debug().additiveMetrics.ninserted = 0; } uassertStatusOK(userAllowedWriteNS(wholeOp.getNamespace())); @@ -762,7 +762,7 @@ static SingleWriteResult performSingleDeleteOp(OperationContext* opCtx, curOp.ensureStarted(); } - curOp.debug().ndeleted = 0; + curOp.debug().additiveMetrics.ndeleted = 0; DeleteRequest request(ns); request.setQuery(op.getQ()); @@ -802,7 +802,7 @@ static SingleWriteResult performSingleDeleteOp(OperationContext* opCtx, uassertStatusOK(exec->executePlan()); long long n = DeleteStage::getNumDeleted(*exec); - curOp.debug().ndeleted = n; + curOp.debug().additiveMetrics.ndeleted = n; PlanSummaryStats summary; Explain::getSummaryStats(*exec, &summary); diff --git a/src/mongo/db/query/plan_executor.cpp b/src/mongo/db/query/plan_executor.cpp index 2eefdbbcb79..d34f429fdab 100644 --- a/src/mongo/db/query/plan_executor.cpp +++ b/src/mongo/db/query/plan_executor.cpp @@ -605,7 +605,7 @@ PlanExecutor::ExecState PlanExecutor::getNextImpl(Snapshotted<BSONObj>* objOut, if (id == WorkingSet::INVALID_ID) { if (!_yieldPolicy->canAutoYield()) throw WriteConflictException(); - CurOp::get(_opCtx)->debug().writeConflicts++; + CurOp::get(_opCtx)->debug().additiveMetrics.incrementWriteConflicts(1); writeConflictsInARow++; WriteConflictException::logAndBackoff( writeConflictsInARow, "plan execution", _nss.ns()); diff --git a/src/mongo/db/query/plan_yield_policy.cpp b/src/mongo/db/query/plan_yield_policy.cpp index b24f1787de1..6d493d40e4d 100644 --- a/src/mongo/db/query/plan_yield_policy.cpp +++ b/src/mongo/db/query/plan_yield_policy.cpp @@ -164,7 +164,7 @@ Status PlanYieldPolicy::yield(stdx::function<void()> beforeYieldingFn, return _planYielding->restoreStateWithoutRetrying(); } catch (const WriteConflictException&) { - CurOp::get(opCtx)->debug().writeConflicts++; + CurOp::get(opCtx)->debug().additiveMetrics.incrementWriteConflicts(1); WriteConflictException::logAndBackoff( attempt, "plan execution restoreState", _planYielding->nss().ns()); // retry diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_prepare_conflict.h b/src/mongo/db/storage/wiredtiger/wiredtiger_prepare_conflict.h index 9bb45c928d8..8736d6db9b6 100644 --- a/src/mongo/db/storage/wiredtiger/wiredtiger_prepare_conflict.h +++ b/src/mongo/db/storage/wiredtiger/wiredtiger_prepare_conflict.h @@ -67,7 +67,7 @@ int wiredTigerPrepareConflictRetry(OperationContext* opCtx, F&& f) { if (ret != WT_PREPARE_CONFLICT) return ret; - ++CurOp::get(opCtx)->debug().prepareReadConflicts; + CurOp::get(opCtx)->debug().additiveMetrics.incrementPrepareReadConflicts(1); wiredTigerPrepareConflictLog(attempts); // Wait on the session cache to signal that a unit of work has been committed or aborted. recoveryUnit->getSessionCache()->waitUntilPreparedUnitOfWorkCommitsOrAborts(opCtx); |