summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/SConscript10
-rw-r--r--src/mongo/db/catalog/capped_utils.cpp2
-rw-r--r--src/mongo/db/catalog/collection_impl.cpp14
-rw-r--r--src/mongo/db/catalog/index_create_impl.cpp2
-rw-r--r--src/mongo/db/commands/find_and_modify.cpp2
-rw-r--r--src/mongo/db/concurrency/d_concurrency_test.cpp7
-rw-r--r--src/mongo/db/concurrency/write_conflict_exception.h2
-rw-r--r--src/mongo/db/curop.cpp155
-rw-r--r--src/mongo/db/curop.h82
-rw-r--r--src/mongo/db/curop_metrics.cpp24
-rw-r--r--src/mongo/db/curop_test.cpp172
-rw-r--r--src/mongo/db/exec/update.cpp4
-rw-r--r--src/mongo/db/ops/write_ops_exec.cpp12
-rw-r--r--src/mongo/db/query/plan_executor.cpp2
-rw-r--r--src/mongo/db/query/plan_yield_policy.cpp2
-rw-r--r--src/mongo/db/storage/wiredtiger/wiredtiger_prepare_conflict.h2
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);