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:
parent
3faba05337
commit
10d56640a6
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@ -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
|
||||
|
||||
@ -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*
|
||||
|
||||
@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
@ -346,3 +346,6 @@ filters:
|
||||
- "logical_session_cache_factory_mongod*":
|
||||
approvers:
|
||||
- 10gen/server-transactions
|
||||
- "action_duration_metrics*":
|
||||
approvers:
|
||||
- 10gen/server-storage-engine-integration
|
||||
|
||||
92
src/mongo/db/action_duration_metrics.cpp
Normal file
92
src/mongo/db/action_duration_metrics.cpp
Normal 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
|
||||
91
src/mongo/db/action_duration_metrics.h
Normal file
91
src/mongo/db/action_duration_metrics.h
Normal 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
|
||||
92
src/mongo/db/action_duration_metrics_test.cpp
Normal file
92
src/mongo/db/action_duration_metrics_test.cpp
Normal 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
|
||||
Loading…
Reference in New Issue
Block a user