SERVER-114944: Add a templated atomic-based counter class. (#45058)

GitOrigin-RevId: 9b281196fc1e2b75a6884148a04a9d3177a9454d
This commit is contained in:
Mike Nugent 2025-12-10 17:20:09 -05:00 committed by MongoDB Bot
parent 5ddbc7c494
commit bdc1063064
3 changed files with 196 additions and 0 deletions

View File

@ -18,6 +18,14 @@ idl_generator(
src = "metrics_settings.idl",
)
mongo_cc_unit_test(
name = "metrics_counter_test",
srcs = [
"metrics_counter_test.cpp",
],
tags = ["mongo_unittest_third_group"],
)
mongo_cc_library(
name = "otel_metrics",
srcs = [

View File

@ -0,0 +1,101 @@
/**
* Copyright (C) 2025-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/platform/atomic.h"
#include "mongo/util/assert_util.h"
#include <string>
#include "src/mongo/util/modules.h"
namespace mongo::otel::metrics {
template <typename T>
class MONGO_MOD_PUBLIC Counter {
public:
// T must be nonnegative.
virtual void add(T value) = 0;
virtual T value() const = 0;
virtual const std::string& name() const = 0;
virtual const std::string& description() const = 0;
virtual const std::string& unit() const = 0;
};
// A lock free (non-decreasing) counter and metadata about it.
template <typename T>
class CounterImpl : public Counter<T> {
public:
CounterImpl(std::string name, std::string description, std::string unit);
void add(T value) override;
T value() const override {
return _value.load();
}
const std::string& name() const override {
return _name;
}
const std::string& description() const override {
return _description;
}
const std::string& unit() const override {
return _unit;
}
private:
std::string _name;
std::string _description;
std::string _unit;
Atomic<T> _value;
};
///////////////////////////////////////////////////////////////////////////////
// Implementation details
///////////////////////////////////////////////////////////////////////////////
template <typename T>
CounterImpl<T>::CounterImpl(std::string name, std::string description, std::string unit)
: _name(std::move(name)), _description(std::move(description)), _unit(std::move(unit)) {}
template <typename T>
void CounterImpl<T>::add(T value) {
massert(ErrorCodes::BadValue, "Counter increment must be nonnegative", value >= 0);
_value.fetchAndAddRelaxed(value);
}
} // namespace mongo::otel::metrics

View File

@ -0,0 +1,87 @@
/**
* Copyright (C) 2025-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/otel/metrics/metrics_counter.h"
#include "mongo/stdx/thread.h"
#include "mongo/unittest/unittest.h"
#include <vector>
namespace mongo::otel::metrics {
TEST(Int64CounterImplTest, Adds) {
CounterImpl<int64_t> counter("name", "description", "unit");
ASSERT_EQ(counter.value(), 0);
counter.add(1);
ASSERT_EQ(counter.value(), 1);
counter.add(10);
ASSERT_EQ(counter.value(), 11);
}
TEST(Int64CounterImplTest, AddsZero) {
CounterImpl<int64_t> counter("name", "description", "unit");
ASSERT_EQ(counter.value(), 0);
counter.add(0);
ASSERT_EQ(counter.value(), 0);
counter.add(10);
counter.add(0);
ASSERT_EQ(counter.value(), 10);
}
TEST(Int64CounterImplTest, ExceptionOnNegativeAdd) {
CounterImpl<int64_t> counter("name", "description", "unit");
counter.add(1);
counter.add(3);
ASSERT_THROWS_CODE(counter.add(-1), DBException, ErrorCodes::BadValue);
}
// Any issues with thread safety should be caught by tsan on this test.
TEST(Int64CounterImplTest, ConcurrentAdds) {
CounterImpl<int64_t> counter("name", "description", "unit");
constexpr int kNumThreads = 10;
constexpr int kIncrementsPerThread = 1000;
std::vector<stdx::thread> threads;
for (int i = 0; i < kNumThreads; ++i) {
threads.emplace_back([&counter]() {
for (int j = 0; j < kIncrementsPerThread; ++j) {
counter.add(1);
}
});
}
for (stdx::thread& thread : threads) {
thread.join();
}
ASSERT_EQ(counter.value(), kNumThreads * kIncrementsPerThread);
}
} // namespace mongo::otel::metrics