SERVER-117685 Create an RAII type to track action duration and report metrics in server status (#46846)

Co-authored-by: Ruby Chen <ruby.chen@mongodb.com>
GitOrigin-RevId: b043b27169fad80917105a98f10fd2ebbe734796
This commit is contained in:
Gregory Wlodarek 2026-01-29 09:53:17 +11:00 committed by MongoDB Bot
parent 3faba05337
commit 10d56640a6
7 changed files with 303 additions and 0 deletions

1
.github/CODEOWNERS vendored
View File

@ -1962,6 +1962,7 @@ WORKSPACE.bazel @10gen/devprod-build @svc-auto-approve-bot
/src/mongo/db/**/compact_structured* @10gen/server-catalog-and-routing-ddl @10gen/server-security @svc-auto-approve-bot
/src/mongo/db/**/partitioned* @10gen/server-programmability @svc-auto-approve-bot
/src/mongo/db/**/logical_session_cache_factory_mongod* @10gen/server-transactions @svc-auto-approve-bot
/src/mongo/db/**/action_duration_metrics* @10gen/server-storage-engine-integration @svc-auto-approve-bot
# The following patterns are parsed from ./src/mongo/db/admission/OWNERS.yml
/src/mongo/db/admission/**/* @10gen/server-workload-resilience @svc-auto-approve-bot

View File

@ -851,6 +851,7 @@ storage_engine_integration:
fully_marked: true
files:
- src/mongo/db/storage # excluding ./key_string, ./wiredtiger, ./devnull, some of ./kv
- src/mongo/db/action_duration_metrics*
- src/mongo/db/record_id*
- src/mongo/db/server_recovery*
- src/mongo/db/mongod_options_storage*

View File

@ -3545,3 +3545,26 @@ mongo_cc_library(
"//src/mongo/db/validate:validate_state",
],
)
mongo_cc_library(
name = "action_duration_metrics",
srcs = [
"action_duration_metrics.cpp",
],
deps = [
"//src/mongo/db/commands/server_status:server_status_core",
],
)
mongo_cc_unit_test(
name = "action_duration_metrics_test",
srcs = [
"action_duration_metrics_test.cpp",
],
tags = ["mongo_unittest_fourth_group"],
deps = [
"action_duration_metrics",
"//src/mongo/db:service_context_test_fixture",
"//src/mongo/unittest",
],
)

View File

@ -346,3 +346,6 @@ filters:
- "logical_session_cache_factory_mongod*":
approvers:
- 10gen/server-transactions
- "action_duration_metrics*":
approvers:
- 10gen/server-storage-engine-integration

View File

@ -0,0 +1,92 @@
/**
* Copyright (C) 2026-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* 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
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* 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 Server Side 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/db/action_duration_metrics.h"
#include "mongo/db/commands/server_status/server_status.h"
namespace mongo {
namespace {
const auto getActionDurationMetrics = ServiceContext::declareDecoration<ActionDurationMetrics>();
class ActionDurationServerStatus : public ServerStatusSection {
public:
using ServerStatusSection::ServerStatusSection;
bool includeByDefault() const final {
return true;
}
BSONObj generateSection(OperationContext* opCtx, const BSONElement& configElement) const final {
BSONObjBuilder b;
getActionDurationMetrics(opCtx->getServiceContext()).report(&b);
return b.obj();
}
};
auto& actionDurationServerStatus =
*ServerStatusSectionBuilder<ActionDurationServerStatus>("actionDuration")
.forShard()
.forRouter();
} // namespace
const ActionDurationMetrics& ActionDurationMetrics::getDecoration(ServiceContext* serviceContext) {
return getActionDurationMetrics(serviceContext);
}
void ActionDurationMetrics::record(const std::string& action, Milliseconds millis) {
stdx::lock_guard lk(_mutex);
auto& entry = _entries[action];
entry.last = millis;
entry.total += millis;
entry.count++;
}
void ActionDurationMetrics::report(BSONObjBuilder* builder) const {
stdx::lock_guard lk(_mutex);
for (const auto& [action, entry] : _entries) {
BSONObjBuilder sub(builder->subobjStart(action));
sub.append("lastDurationMillis", durationCount<Milliseconds>(entry.last));
sub.append("totalDurationMillis", durationCount<Milliseconds>(entry.total));
sub.append("count", entry.count);
}
}
ActionDurationTimer::ActionDurationTimer(OperationContext* opCtx, std::string action)
: _opCtx(opCtx), _action(std::move(action)), _start(opCtx->fastClockSource().now()) {}
ActionDurationTimer::~ActionDurationTimer() {
Milliseconds duration = _opCtx->fastClockSource().now() - _start;
getActionDurationMetrics(_opCtx->getServiceContext()).record(_action, duration);
}
} // namespace mongo

View File

@ -0,0 +1,91 @@
/**
* Copyright (C) 2026-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* 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
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* 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 Server Side 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.
*/
#pragma once
#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/db/service_context.h"
#include "mongo/stdx/mutex.h"
#include "mongo/util/time_support.h"
#include <cstdint>
#include <map>
#include <string>
namespace mongo {
/**
* Tracks per-action duration metrics.
*
* This is a ServiceContext decoration.
*/
class ActionDurationMetrics {
public:
struct Entry {
Milliseconds last{0};
Milliseconds total{0};
int64_t count{0};
};
static const ActionDurationMetrics& getDecoration(ServiceContext* serviceContext);
void record(const std::string& action, Milliseconds millis);
void report(BSONObjBuilder* builder) const;
private:
mutable stdx::mutex _mutex;
// Use an ordered map to prevent schema changes in FTDC.
std::map<std::string, Entry> _entries;
};
/**
* RAII helper that measures the duration of a named action and records it into the
* ActionDurationMetrics decoration.
*/
class ActionDurationTimer {
public:
ActionDurationTimer(const ActionDurationTimer&) = delete;
ActionDurationTimer& operator=(const ActionDurationTimer&) = delete;
ActionDurationTimer(ActionDurationTimer&&) = delete;
ActionDurationTimer& operator=(ActionDurationTimer&&) = delete;
// Caller is responsible for using the RAII within the OperationContext lifetime.
ActionDurationTimer(OperationContext* opCtx, std::string action);
~ActionDurationTimer();
private:
OperationContext* _opCtx;
std::string _action;
Date_t _start;
};
} // namespace mongo

View File

@ -0,0 +1,92 @@
/**
* Copyright (C) 2026-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* 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
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* 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 Server Side 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/db/action_duration_metrics.h"
#include "mongo/db/service_context_test_fixture.h"
#include "mongo/util/clock_source_mock.h"
namespace mongo {
namespace {
class ActionDurationMetricsTest : public ClockSourceMockServiceContextTest {
public:
void advanceTime(Milliseconds m) {
static_cast<ClockSourceMock*>(getServiceContext()->getFastClockSource())->advance(m);
}
void assertCounts(const ActionDurationMetrics& metrics,
const std::string& action,
Milliseconds lastDuration,
Milliseconds totalDuration,
int64_t count) {
BSONObjBuilder builder;
metrics.report(&builder);
BSONObj obj = builder.obj();
BSONObj actionObj = obj.getObjectField(action);
ASSERT_EQ(lastDuration.count(), actionObj.getField("lastDurationMillis").numberLong());
ASSERT_EQ(totalDuration.count(), actionObj.getField("totalDurationMillis").numberLong());
ASSERT_EQ(count, actionObj.getField("count").numberLong());
}
};
TEST_F(ActionDurationMetricsTest, ActionDurationTimer) {
auto opCtx = makeOperationContext();
const ActionDurationMetrics& metrics =
ActionDurationMetrics::getDecoration(opCtx->getServiceContext());
const std::string action = "TimerTest";
{
ActionDurationTimer actionTimer(opCtx.get(), action);
advanceTime(Milliseconds(10));
// Metrics are only advanced when the RAII type is destructed.
assertCounts(metrics, action, Milliseconds(0), Milliseconds(0), 0);
}
assertCounts(metrics, action, Milliseconds(10), Milliseconds(10), 1);
// Advancing time while there's no RAII type in scope has no effect.
advanceTime(Milliseconds(1000));
assertCounts(metrics, action, Milliseconds(10), Milliseconds(10), 1);
{
ActionDurationTimer actionTimer(opCtx.get(), action);
advanceTime(Milliseconds(25));
assertCounts(metrics, action, Milliseconds(10), Milliseconds(10), 1);
}
assertCounts(metrics, action, Milliseconds(25), Milliseconds(35), 2);
}
} // namespace
} // namespace mongo