summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Larkin-York <dan.larkin-york@mongodb.com>2022-08-29 22:32:52 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-08-29 23:12:36 +0000
commitbe2effed55930ecd2b9efc06057ab45dbb4163d0 (patch)
treea2da49851184afe549b7456ae9a6cd4eed2f50f9
parentccb0d93033900be8cead29d3cdd414d8a85f2b03 (diff)
downloadmongo-be2effed55930ecd2b9efc06057ab45dbb4163d0.tar.gz
SERVER-66699 Add statistics about bucket state management to serverStatus
-rw-r--r--jstests/noPassthrough/timeseries_server_status_state_management.js102
-rw-r--r--src/mongo/db/timeseries/bucket_catalog.cpp20
-rw-r--r--src/mongo/db/timeseries/bucket_catalog.h11
3 files changed, 133 insertions, 0 deletions
diff --git a/jstests/noPassthrough/timeseries_server_status_state_management.js b/jstests/noPassthrough/timeseries_server_status_state_management.js
new file mode 100644
index 00000000000..31f18ae52ba
--- /dev/null
+++ b/jstests/noPassthrough/timeseries_server_status_state_management.js
@@ -0,0 +1,102 @@
+/**
+ * Tests that BucketCatalog bucket state management statistics are reported correctly in server
+ * status.
+ *
+ * @tags: [
+ * # State management statistics added as part of scalability improvements project.
+ * featureFlagTimeseriesScalabilityImprovements,
+ * ]
+ */
+(function() {
+"use strict";
+
+const conn = MongoRunner.runMongod();
+
+const dbName = jsTestName();
+const db = conn.getDB(dbName);
+assert.commandWorked(db.dropDatabase());
+
+const coll = db.getCollection(jsTestName());
+
+const timeFieldName = "tt";
+const metaFieldName = "mm";
+
+const resetCollection = () => {
+ coll.drop();
+ assert.commandWorked(db.createCollection(
+ jsTestName(), {timeseries: {timeField: timeFieldName, metaField: metaFieldName}}));
+};
+
+const dropUnrelatedCollection = () => {
+ const unrelated = db.getCollection(jsTestName() + "_foo");
+ assert.commandWorked(db.createCollection(
+ unrelated.getName(), {timeseries: {timeField: timeFieldName, metaField: metaFieldName}}));
+ unrelated.drop();
+};
+
+const expected = {
+ bucketsManaged: 0,
+ currentEra: 0,
+ erasWithRemainingBuckets: 0,
+ trackedClearOperations: 0
+};
+const checkServerStatus = function() {
+ const actual =
+ assert.commandWorked(db.runCommand({serverStatus: 1})).bucketCatalog.stateManagement;
+ assert.eq(expected.bucketsManaged, actual.bucketsManaged);
+ assert.eq(expected.currentEra, actual.currentEra);
+ assert.eq(expected.erasWithRemainingBuckets, actual.erasWithRemainingBuckets);
+ assert.eq(expected.trackedClearOperations, actual.trackedClearOperations);
+};
+
+resetCollection();
+assert.commandWorked(coll.insert({[metaFieldName]: 1, [timeFieldName]: ISODate()}));
+expected.bucketsManaged++;
+expected.erasWithRemainingBuckets++;
+checkServerStatus();
+
+dropUnrelatedCollection();
+expected.currentEra++;
+expected.trackedClearOperations++;
+checkServerStatus();
+
+// Inserting into the existing bucket will update its era, which has no net effect on
+// erasWithRemainingBuckets, but the previously tracked clear will get cleaned up.
+assert.commandWorked(coll.insert({[metaFieldName]: 1, [timeFieldName]: ISODate()}));
+expected.trackedClearOperations--;
+checkServerStatus();
+
+assert.commandWorked(coll.insert({[metaFieldName]: 2, [timeFieldName]: ISODate()}));
+expected.bucketsManaged++;
+checkServerStatus();
+
+// Clearing the collection will not immediately remove the old bucket states.
+resetCollection();
+expected.currentEra++;
+expected.trackedClearOperations++;
+checkServerStatus();
+
+// Inserting more measurements will check and remove the old bucket for that meta, and open a new
+// one. The other meta value still has an old bucket.
+assert.commandWorked(coll.insert({[metaFieldName]: 1, [timeFieldName]: ISODate()}));
+expected.erasWithRemainingBuckets++;
+checkServerStatus();
+
+// If we clear an unrelated collection and add a third metadata value, we'll get a bucket in a third
+// era.
+dropUnrelatedCollection();
+assert.commandWorked(coll.insert({[metaFieldName]: 3, [timeFieldName]: ISODate()}));
+expected.bucketsManaged++;
+expected.currentEra++;
+expected.erasWithRemainingBuckets++;
+expected.trackedClearOperations++;
+checkServerStatus();
+
+// If we access the oldest bucket, we'll clear it and garbage collect a tracked clear.
+assert.commandWorked(coll.insert({[metaFieldName]: 2, [timeFieldName]: ISODate()}));
+expected.erasWithRemainingBuckets--;
+expected.trackedClearOperations--;
+checkServerStatus();
+
+MongoRunner.stopMongod(conn);
+}());
diff --git a/src/mongo/db/timeseries/bucket_catalog.cpp b/src/mongo/db/timeseries/bucket_catalog.cpp
index 10ce10848cf..e7df8f04471 100644
--- a/src/mongo/db/timeseries/bucket_catalog.cpp
+++ b/src/mongo/db/timeseries/bucket_catalog.cpp
@@ -27,6 +27,8 @@
* it in the license file.
*/
+#include "mongo/bson/bsonobj.h"
+#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/platform/basic.h"
#include "mongo/db/timeseries/bucket_catalog.h"
@@ -431,6 +433,17 @@ boost::optional<BucketCatalog::BucketState> BucketCatalog::BucketStateManager::s
return _setBucketStateHelper(catalogLock, id, target);
}
+void BucketCatalog::BucketStateManager::appendStats(BSONObjBuilder* base) const {
+ stdx::lock_guard catalogLock{*_mutex};
+
+ BSONObjBuilder builder{base->subobjStart("stateManagement")};
+
+ builder.appendNumber("bucketsManaged", static_cast<long long>(_bucketStates.size()));
+ builder.appendNumber("currentEra", static_cast<long long>(_era));
+ builder.appendNumber("erasWithRemainingBuckets", static_cast<long long>(_countMap.size()));
+ builder.appendNumber("trackedClearOperations", static_cast<long long>(_clearRegistry.size()));
+}
+
boost::optional<BucketCatalog::BucketState>
BucketCatalog::BucketStateManager::_setBucketStateHelper(WithLock catalogLock,
const OID& id,
@@ -1027,6 +1040,10 @@ void BucketCatalog::appendGlobalExecutionStats(BSONObjBuilder* builder) const {
_appendExecutionStatsToBuilder(&_globalExecutionStats, builder);
}
+void BucketCatalog::appendStateManagementStats(BSONObjBuilder* builder) const {
+ _bucketStateManager.appendStats(builder);
+}
+
BucketCatalog::BucketMetadata::BucketMetadata(BSONElement elem,
const StringData::ComparatorInterface* comparator)
: _metadataElement(elem), _comparator(comparator) {
@@ -1865,6 +1882,9 @@ public:
// Append the global execution stats for all namespaces.
bucketCatalog.appendGlobalExecutionStats(&builder);
+ // Append the global state management stats for all namespaces.
+ bucketCatalog.appendStateManagementStats(&builder);
+
return builder.obj();
}
} bucketCatalogServerStatus;
diff --git a/src/mongo/db/timeseries/bucket_catalog.h b/src/mongo/db/timeseries/bucket_catalog.h
index 921cc330a4d..a6cd1169a11 100644
--- a/src/mongo/db/timeseries/bucket_catalog.h
+++ b/src/mongo/db/timeseries/bucket_catalog.h
@@ -29,6 +29,7 @@
#pragma once
+#include "mongo/bson/bsonobjbuilder.h"
#include <boost/container/small_vector.hpp>
#include <boost/container/static_vector.hpp>
#include <queue>
@@ -377,6 +378,11 @@ public:
*/
void appendGlobalExecutionStats(BSONObjBuilder* builder) const;
+ /**
+ * Appends the global bucket state management stats for all namespaces to the builder.
+ */
+ void appendStateManagementStats(BSONObjBuilder* builder) const;
+
protected:
enum class BucketState {
// Bucket can be inserted into, and does not have an outstanding prepared commit
@@ -589,6 +595,11 @@ protected:
*/
boost::optional<BucketState> setBucketState(const OID& id, BucketState target);
+ /**
+ * Appends statistics for observability.
+ */
+ void appendStats(BSONObjBuilder* builder) const;
+
protected:
void _decrementEraCountHelper(uint64_t era);
void _incrementEraCountHelper(uint64_t era);