Export etl metrics to prometheus (#1256)

Fixes #1248.
This commit is contained in:
Sergey Kuznetsov
2024-03-14 11:37:31 +00:00
committed by GitHub
parent 010538d6fe
commit e83dfcbcc3
28 changed files with 708 additions and 51 deletions

View File

@@ -18,6 +18,7 @@
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 1,
"links": [],
"liveNow": false,
"panels": [
@@ -85,9 +86,11 @@
"fields": "",
"values": false
},
"textMode": "auto"
"showPercentChange": false,
"textMode": "auto",
"wideLayout": true
},
"pluginVersion": "10.2.0",
"pluginVersion": "10.4.0",
"targets": [
{
"datasource": {
@@ -105,6 +108,372 @@
"title": "Service state",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [
{
"options": {
"0": {
"color": "blue",
"index": 0,
"text": "No"
},
"1": {
"color": "green",
"index": 1,
"text": "Yes"
}
},
"type": "value"
}
],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 3,
"x": 3,
"y": 0
},
"id": 14,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"showPercentChange": false,
"textMode": "auto",
"wideLayout": true
},
"pluginVersion": "10.4.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"editorMode": "code",
"expr": "read_only",
"instant": false,
"legendFormat": "__auto",
"range": true,
"refId": "A"
}
],
"title": "Read only",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [
{
"options": {
"0": {
"color": "blue",
"index": 0,
"text": "No"
},
"1": {
"color": "green",
"index": 1,
"text": "Yes"
}
},
"type": "value"
}
],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 3,
"x": 6,
"y": 0
},
"id": 15,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"showPercentChange": false,
"textMode": "auto",
"wideLayout": true
},
"pluginVersion": "10.4.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"editorMode": "code",
"expr": "etl_writing",
"instant": false,
"legendFormat": "__auto",
"range": true,
"refId": "A"
}
],
"title": "Writing data to DB",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [
{
"options": {
"0": {
"color": "green",
"index": 0,
"text": "No"
},
"1": {
"color": "red",
"index": 1,
"text": "Yes"
}
},
"type": "value"
}
],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 3,
"x": 9,
"y": 0
},
"id": 16,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"showPercentChange": false,
"textMode": "auto",
"wideLayout": true
},
"pluginVersion": "10.4.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"editorMode": "code",
"expr": "etl_amendment_blocked",
"instant": false,
"legendFormat": "__auto",
"range": true,
"refId": "A"
}
],
"title": "Amendment blocked",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "s"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 13,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"editorMode": "code",
"exemplar": false,
"expr": "timestamp(etl_last_publish_seconds) - etl_last_publish_seconds",
"format": "time_series",
"instant": false,
"legendFormat": "ledger age",
"range": true,
"refId": "A"
}
],
"title": "Ledger Age",
"transformations": [
{
"id": "filterByValue",
"options": {
"filters": [
{
"config": {
"id": "lower",
"options": {
"value": 31500000
}
},
"fieldName": "ledger age"
}
],
"match": "all",
"type": "include"
}
}
],
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
@@ -166,9 +535,9 @@
},
"gridPos": {
"h": 8,
"w": 9,
"x": 3,
"y": 0
"w": 12,
"x": 0,
"y": 8
},
"id": 2,
"options": {
@@ -263,7 +632,7 @@
"h": 8,
"w": 12,
"x": 12,
"y": 0
"y": 8
},
"id": 9,
"options": {
@@ -358,7 +727,7 @@
"h": 8,
"w": 12,
"x": 0,
"y": 8
"y": 16
},
"id": 11,
"options": {
@@ -453,7 +822,7 @@
"h": 8,
"w": 12,
"x": 12,
"y": 8
"y": 16
},
"id": 6,
"options": {
@@ -550,7 +919,7 @@
"h": 8,
"w": 12,
"x": 0,
"y": 16
"y": 24
},
"id": 10,
"options": {
@@ -645,7 +1014,7 @@
"h": 8,
"w": 12,
"x": 12,
"y": 16
"y": 24
},
"id": 8,
"options": {
@@ -740,7 +1109,7 @@
"h": 8,
"w": 12,
"x": 0,
"y": 24
"y": 32
},
"id": 4,
"options": {
@@ -839,7 +1208,7 @@
"h": 8,
"w": 12,
"x": 12,
"y": 24
"y": 32
},
"id": 12,
"options": {
@@ -973,7 +1342,7 @@
"h": 8,
"w": 12,
"x": 0,
"y": 32
"y": 40
},
"id": 5,
"options": {
@@ -1081,7 +1450,7 @@
"h": 8,
"w": 12,
"x": 12,
"y": 32
"y": 40
},
"id": 3,
"options": {
@@ -1186,9 +1555,9 @@
},
"gridPos": {
"h": 8,
"w": 10,
"w": 12,
"x": 0,
"y": 40
"y": 48
},
"id": 7,
"options": {
@@ -1222,7 +1591,7 @@
}
],
"refresh": "5s",
"schemaVersion": 38,
"schemaVersion": 39,
"tags": [],
"templating": {
"list": []
@@ -1235,6 +1604,6 @@
"timezone": "",
"title": "Clio",
"uid": "aeaae84e-c194-47b2-ad65-86e45eebb815",
"version": 3,
"version": 1,
"weekStart": ""
}

View File

@@ -276,7 +276,7 @@ ETLService::ETLService(
{
startSequence_ = config.maybeValue<uint32_t>("start_sequence");
finishSequence_ = config.maybeValue<uint32_t>("finish_sequence");
state_.isReadOnly = config.valueOr("read_only", state_.isReadOnly);
state_.isReadOnly = config.valueOr("read_only", static_cast<bool>(state_.isReadOnly));
extractorThreads_ = config.valueOr<uint32_t>("extractor_threads", extractorThreads_);
txnThreshold_ = config.valueOr<size_t>("txn_threshold", txnThreshold_);
}

View File

@@ -212,8 +212,8 @@ public:
boost::json::object result;
result["etl_sources"] = loadBalancer_->toJson();
result["is_writer"] = state_.isWriting.load();
result["read_only"] = state_.isReadOnly;
result["is_writer"] = static_cast<int>(state_.isWriting);
result["read_only"] = static_cast<int>(state_.isReadOnly);
auto last = ledgerPublisher_.getLastPublish();
if (last.time_since_epoch().count() != 0)
result["last_publish_age_seconds"] = std::to_string(ledgerPublisher_.lastPublishAgeSeconds());

View File

@@ -19,6 +19,10 @@
#pragma once
#include "util/prometheus/Bool.hpp"
#include "util/prometheus/Label.hpp"
#include "util/prometheus/Prometheus.hpp"
#include <atomic>
namespace etl {
@@ -33,9 +37,19 @@ struct SystemState {
* In strict read-only mode, the process will never attempt to become the ETL writer, and will only publish ledgers
* as they are written to the database.
*/
bool isReadOnly = false;
util::prometheus::Bool isReadOnly = PrometheusService::boolMetric(
"read_only",
util::prometheus::Labels{},
"Whether the process is in strict read-only mode"
);
/** @brief Whether the process is writing to the database. */
util::prometheus::Bool isWriting = PrometheusService::boolMetric(
"etl_writing",
util::prometheus::Labels{},
"Whether the process is writing to the database"
);
std::atomic_bool isWriting = false; /**< @brief Whether the process is writing to the database. */
std::atomic_bool isStopping = false; /**< @brief Whether the software is stopping. */
std::atomic_bool writeConflict = false; /**< @brief Whether a write conflict was detected. */
@@ -46,7 +60,11 @@ struct SystemState {
* arrived from rippled and therefore can't extract the ledger diff. When this happens, Clio can't proceed with ETL
* and should log this error and only handle RPC requests.
*/
std::atomic_bool isAmendmentBlocked = false;
util::prometheus::Bool isAmendmentBlocked = PrometheusService::boolMetric(
"etl_amendment_blocked",
util::prometheus::Labels{},
"Whether clio detected an amendment block"
);
};
} // namespace etl

View File

@@ -25,6 +25,8 @@
#include "etl/SystemState.hpp"
#include "util/Assert.hpp"
#include "util/log/Logger.hpp"
#include "util/prometheus/Counter.hpp"
#include "util/prometheus/Prometheus.hpp"
#include <boost/asio/io_context.hpp>
#include <boost/asio/strand.hpp>
@@ -75,8 +77,11 @@ class LedgerPublisher {
std::chrono::time_point<ripple::NetClock> lastCloseTime_;
mutable std::shared_mutex closeTimeMtx_;
std::chrono::time_point<std::chrono::system_clock> lastPublish_;
mutable std::shared_mutex publishTimeMtx_;
std::reference_wrapper<util::prometheus::CounterInt> lastPublishSeconds_ = PrometheusService::counterInt(
"etl_last_publish_seconds",
{},
"Seconds since epoch of the last published ledger"
);
std::optional<uint32_t> lastPublishedSequence_;
mutable std::shared_mutex lastPublishedSeqMtx_;
@@ -232,8 +237,8 @@ public:
std::chrono::time_point<std::chrono::system_clock>
getLastPublish() const
{
std::shared_lock const lck(publishTimeMtx_);
return lastPublish_;
return std::chrono::time_point<std::chrono::system_clock>{std::chrono::seconds{lastPublishSeconds_.get().value()
}};
}
/**
@@ -273,8 +278,9 @@ private:
void
setLastPublishTime()
{
std::scoped_lock const lck(publishTimeMtx_);
lastPublish_ = std::chrono::system_clock::now();
using namespace std::chrono;
auto const nowSeconds = duration_cast<seconds>(system_clock::now().time_since_epoch()).count();
lastPublishSeconds_.get().set(nowSeconds);
}
void

View File

@@ -0,0 +1,89 @@
//------------------------------------------------------------------------------
/*
This file is part of clio: https://github.com/XRPLF/clio
Copyright (c) 2024, the clio developers.
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#pragma once
#include "util/Assert.hpp"
#include "util/prometheus/Gauge.hpp"
#include <cstdint>
#include <functional>
namespace util::prometheus {
template <typename T>
concept SomeBoolImpl = requires(T a) {
{
a.set(0)
} -> std::same_as<void>;
{
a.value()
} -> std::same_as<int64_t>;
};
/**
* @brief A wrapped to provide bool interface for a Prometheus metric
* @note Prometheus does not have a native bool type, so we use a counter with a value of 0 or 1
*/
template <SomeBoolImpl ImplType>
class AnyBool {
std::reference_wrapper<ImplType> impl_;
public:
/**
* @brief Construct a bool metric
*
* @param impl The implementation of the metric
*/
explicit AnyBool(ImplType& impl) : impl_(impl)
{
}
/**
* @brief Set the value of the bool metric
*
* @param value The value to set
* @return A reference to the metric
*/
AnyBool&
operator=(bool value)
{
impl_.get().set(value ? 1 : 0);
return *this;
}
/**
* @brief Get the value of the bool metric
*
* @return The value of the metric
*/
operator bool() const
{
auto const value = impl_.get().value();
ASSERT(value == 0 || value == 1, "Invalid value for bool: {}", value);
return value == 1;
}
};
/**
* @brief Alias for Prometheus bool metric with GaugeInt implementation
*/
using Bool = AnyBool<GaugeInt>;
} // namespace util::prometheus

View File

@@ -83,6 +83,18 @@ struct AnyCounter : MetricBase, impl::AnyCounterBase<NumberType> {
return *this;
}
/**
* @brief Set the value of the counter
*
* @param value The value to set the counter to
*/
void
set(ValueType value)
{
ASSERT(value >= this->value(), "Cannot decrease a counter {}", this->name());
this->pimpl_->set(value);
}
/**
* @brief Reset the counter to zero
*/

View File

@@ -21,6 +21,7 @@
#include "util/Assert.hpp"
#include "util/config/Config.hpp"
#include "util/prometheus/Bool.hpp"
#include "util/prometheus/Counter.hpp"
#include "util/prometheus/Gauge.hpp"
#include "util/prometheus/Histogram.hpp"
@@ -52,6 +53,13 @@ convertBaseTo(MetricBase& metricBase)
} // namespace
Bool
PrometheusImpl::boolMetric(std::string name, Labels labels, std::optional<std::string> description)
{
auto& metric = gaugeInt(std::move(name), std::move(labels), std::move(description));
return Bool{metric};
}
CounterInt&
PrometheusImpl::counterInt(std::string name, Labels labels, std::optional<std::string> description)
{
@@ -175,6 +183,12 @@ PrometheusService::init(util::Config const& config)
instance_ = std::make_unique<util::prometheus::PrometheusImpl>(enabled, compressReply);
}
util::prometheus::Bool
PrometheusService::boolMetric(std::string name, util::prometheus::Labels labels, std::optional<std::string> description)
{
return instance().boolMetric(std::move(name), std::move(labels), std::move(description));
}
util::prometheus::CounterInt&
PrometheusService::counterInt(std::string name, util::prometheus::Labels labels, std::optional<std::string> description)
{

View File

@@ -20,6 +20,7 @@
#pragma once
#include "util/config/Config.hpp"
#include "util/prometheus/Bool.hpp"
#include "util/prometheus/Counter.hpp"
#include "util/prometheus/Gauge.hpp"
#include "util/prometheus/Histogram.hpp"
@@ -54,6 +55,18 @@ public:
virtual ~PrometheusInterface() = default;
/**
* @brief Get a bool based metric. It will be created if it doesn't exist
* @note Prometheus does not have a native bool type, so we use a counter with a value of 0 or 1
*
* @param name The name of the metric
* @param labels The labels of the metric
* @param description The description of the metric
* @return The bool object
*/
virtual Bool
boolMetric(std::string name, Labels labels, std::optional<std::string> description = std::nullopt) = 0;
/**
* @brief Get an integer based counter metric. It will be created if it doesn't exist
*
@@ -176,6 +189,9 @@ class PrometheusImpl : public PrometheusInterface {
public:
using PrometheusInterface::PrometheusInterface;
Bool
boolMetric(std::string name, Labels labels, std::optional<std::string> description = std::nullopt) override;
CounterInt&
counterInt(std::string name, Labels labels, std::optional<std::string> description) override;
@@ -242,6 +258,22 @@ public:
*/
void static init(util::Config const& config = util::Config{});
/**
* @brief Get a bool based metric. It will be created if it doesn't exist
* @note Prometheus does not have a native bool type, so we use a counter with a value of 0 or 1
*
* @param name The name of the metric
* @param labels The labels of the metric
* @param description The description of the metric
* @return The bool object
*/
static util::prometheus::Bool
boolMetric(
std::string name,
util::prometheus::Labels labels,
std::optional<std::string> description = std::nullopt
);
/**
* @brief Get an integer based counter metric. It will be created if it doesn't exist
*

View File

@@ -97,6 +97,7 @@ target_sources(
util/BatchingTests.cpp
util/LedgerUtilsTests.cpp
# Prometheus support
util/prometheus/BoolTests.cpp
util/prometheus/CounterTests.cpp
util/prometheus/GaugeTests.cpp
util/prometheus/HistogramTests.cpp

View File

@@ -19,7 +19,6 @@
#include "util/TerminationHandler.hpp"
#include "util/TestGlobals.hpp"
#include "util/prometheus/Prometheus.hpp"
#include <gtest/gtest.h>
@@ -33,7 +32,6 @@ int
main(int argc, char* argv[])
{
util::setTerminationHandler();
PrometheusService::init();
testing::InitGoogleTest(&argc, argv);
TestGlobals::instance().parse(argc, argv);

View File

@@ -21,6 +21,7 @@
#include "etl/impl/AmendmentBlock.hpp"
#include "util/FakeAmendmentBlockAction.hpp"
#include "util/Fixtures.hpp"
#include "util/MockPrometheus.hpp"
#include <boost/asio/io_context.hpp>
#include <gtest/gtest.h>
@@ -32,8 +33,7 @@
using namespace testing;
using namespace etl;
class AmendmentBlockHandlerTest : public NoLoggerFixture {
protected:
struct AmendmentBlockHandlerTest : util::prometheus::WithPrometheus, NoLoggerFixture {
using AmendmentBlockHandlerType = impl::AmendmentBlockHandler<FakeAmendmentBlockAction>;
boost::asio::io_context ioc_;

View File

@@ -24,6 +24,7 @@
#include "etl/impl/FakeDiffProvider.hpp"
#include "util/Fixtures.hpp"
#include "util/MockCache.hpp"
#include "util/MockPrometheus.hpp"
#include "util/async/context/BasicExecutionContext.hpp"
#include "util/config/Config.hpp"
@@ -43,7 +44,7 @@ namespace {
constexpr auto SEQ = 30;
struct CacheLoaderTest : MockBackendTest {
struct CacheLoaderTest : util::prometheus::WithPrometheus, MockBackendTest {
void
SetUp() override
{

View File

@@ -21,6 +21,7 @@
#include "etl/impl/CursorProvider.hpp"
#include "etl/impl/FakeDiffProvider.hpp"
#include "util/Fixtures.hpp"
#include "util/MockPrometheus.hpp"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -36,7 +37,7 @@ namespace {
constexpr auto SEQ = 30;
struct CursorProviderTest : MockBackendTestNaggy {
struct CursorProviderTest : util::prometheus::WithPrometheus, MockBackendTestNaggy {
DiffProvider diffProvider;
};
struct ParametrizedCursorProviderTest : CursorProviderTest, WithParamInterface<std::size_t> {};

View File

@@ -24,6 +24,7 @@
#include "util/MockExtractionDataPipe.hpp"
#include "util/MockLedgerFetcher.hpp"
#include "util/MockNetworkValidatedLedgers.hpp"
#include "util/MockPrometheus.hpp"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -34,8 +35,7 @@
using namespace testing;
using namespace etl;
class ETLExtractorTest : public NoLoggerFixture {
protected:
struct ETLExtractorTest : util::prometheus::WithPrometheus, NoLoggerFixture {
using ExtractionDataPipeType = MockExtractionDataPipe;
using LedgerFetcherType = MockLedgerFetcher;
using ExtractorType = etl::impl::Extractor<ExtractionDataPipeType, MockNetworkValidatedLedgers, LedgerFetcherType>;
@@ -48,7 +48,6 @@ protected:
std::unique_ptr<ExtractorType> extractor_;
public:
void
SetUp() override
{

View File

@@ -20,6 +20,7 @@
#include "etl/impl/GrpcSource.hpp"
#include "util/Fixtures.hpp"
#include "util/MockBackend.hpp"
#include "util/MockPrometheus.hpp"
#include "util/MockXrpLedgerAPIService.hpp"
#include "util/TestObject.hpp"
#include "util/config/Config.hpp"
@@ -39,7 +40,9 @@
using namespace etl::impl;
struct GrpcSourceTests : NoLoggerFixture, unittests::util::WithMockXrpLedgerAPIService {
struct GrpcSourceTests : NoLoggerFixture,
util::prometheus::WithPrometheus,
unittests::util::WithMockXrpLedgerAPIService {
GrpcSourceTests()
: WithMockXrpLedgerAPIService("localhost:50051")
, mockBackend_(std::make_shared<testing::StrictMock<MockBackend>>(util::Config{}))

View File

@@ -23,6 +23,7 @@
#include "etl/impl/LedgerPublisher.hpp"
#include "util/Fixtures.hpp"
#include "util/MockCache.hpp"
#include "util/MockPrometheus.hpp"
#include "util/MockSubscriptionManager.hpp"
#include "util/TestObject.hpp"
#include "util/config/Config.hpp"
@@ -49,7 +50,10 @@ static auto constexpr LEDGERHASH = "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A
static auto constexpr SEQ = 30;
static auto constexpr AGE = 800;
class ETLLedgerPublisherTest : public MockBackendTest, public SyncAsioContextTest, public MockSubscriptionManagerTest {
struct ETLLedgerPublisherTest : util::prometheus::WithPrometheus,
MockBackendTest,
SyncAsioContextTest,
MockSubscriptionManagerTest {
void
SetUp() override
{

View File

@@ -25,6 +25,7 @@
#include "util/MockExtractionDataPipe.hpp"
#include "util/MockLedgerLoader.hpp"
#include "util/MockLedgerPublisher.hpp"
#include "util/MockPrometheus.hpp"
#include "util/StringUtils.hpp"
#include <gmock/gmock.h>
@@ -46,8 +47,7 @@ constexpr static auto RAW_HEADER =
"3E2232B33EF57CECAC2816E3122816E31A0A00F8377CD95DFA484CFAE282656A58"
"CE5AA29652EFFD80AC59CD91416E4E13DBBE";
class ETLTransformerTest : public MockBackendTest {
protected:
struct ETLTransformerTest : util::prometheus::WithPrometheus, MockBackendTest {
using DataType = FakeFetchResponse;
using ExtractionDataPipeType = MockExtractionDataPipe;
using LedgerLoaderType = MockLedgerLoader;
@@ -64,7 +64,6 @@ protected:
std::unique_ptr<TransformerType> transformer_;
public:
void
SetUp() override
{

View File

@@ -20,6 +20,7 @@
#pragma once
#include "util/Fixtures.hpp"
#include "util/MockPrometheus.hpp"
#include "util/MockWsBase.hpp"
#include "web/interface/ConnectionBase.hpp"
@@ -33,7 +34,7 @@
// Base class for feed tests, providing easy way to access the received feed
template <typename TestedFeed>
class FeedBaseTest : public SyncAsioContextTest, public MockBackendTest {
class FeedBaseTest : public util::prometheus::WithPrometheus, public SyncAsioContextTest, public MockBackendTest {
protected:
std::shared_ptr<web::ConnectionBase> sessionPtr;
std::shared_ptr<TestedFeed> testFeedPtr;

View File

@@ -21,6 +21,7 @@
#include "feed/FeedTestUtil.hpp"
#include "feed/SubscriptionManager.hpp"
#include "util/Fixtures.hpp"
#include "util/MockPrometheus.hpp"
#include "util/MockWsBase.hpp"
#include "util/TestObject.hpp"
#include "web/interface/ConnectionBase.hpp"
@@ -52,7 +53,9 @@ namespace json = boost::json;
using namespace feed;
using namespace feed::impl;
class SubscriptionManagerTest : public MockBackendTest, public SyncAsioContextTest {
class SubscriptionManagerTest : public util::prometheus::WithPrometheus,
public MockBackendTest,
public SyncAsioContextTest {
protected:
std::shared_ptr<SubscriptionManager> SubscriptionManagerPtr;
std::shared_ptr<web::ConnectionBase> session;

View File

@@ -23,6 +23,7 @@
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/Types.hpp"
#include "util/Fixtures.hpp"
#include "util/MockPrometheus.hpp"
#include "util/TestObject.hpp"
#include <boost/asio/impl/spawn.hpp>
@@ -57,7 +58,7 @@ constexpr static auto INDEX1 = "E6DBAFC99223B42257915A63DFC6B0C032D4070F9A574B25
constexpr static auto INDEX2 = "E6DBAFC99223B42257915A63DFC6B0C032D4070F9A574B255AD97466726FC322";
constexpr static auto TXNID = "E6DBAFC99223B42257915A63DFC6B0C032D4070F9A574B255AD97466726FC321";
class RPCHelpersTest : public MockBackendTest, public SyncAsioContextTest {
class RPCHelpersTest : public util::prometheus::WithPrometheus, public MockBackendTest, public SyncAsioContextTest {
void
SetUp() override
{

View File

@@ -25,7 +25,6 @@
#include "rpc/common/Types.hpp"
#include "rpc/handlers/Subscribe.hpp"
#include "util/Fixtures.hpp"
#include "util/MockPrometheus.hpp"
#include "util/MockWsBase.hpp"
#include "util/TestObject.hpp"
#include "web/interface/ConnectionBase.hpp"
@@ -62,7 +61,7 @@ constexpr static auto PAYS20XRPGETS10USDBOOKDIR = "7B1767D41DBCE79D9585CF9D0262A
constexpr static auto INDEX1 = "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC";
constexpr static auto INDEX2 = "E6DBAFC99223B42257915A63DFC6B0C032D4070F9A574B255AD97466726FC321";
class RPCSubscribeHandlerTest : public util::prometheus::WithPrometheus, public HandlerBaseTest {
class RPCSubscribeHandlerTest : public HandlerBaseTest {
protected:
void
SetUp() override

View File

@@ -24,6 +24,7 @@
#include "util/MockCounters.hpp"
#include "util/MockETLService.hpp"
#include "util/MockLoadBalancer.hpp"
#include "util/MockPrometheus.hpp"
#include "util/MockSubscriptionManager.hpp"
#include "util/log/Logger.hpp"
@@ -349,6 +350,7 @@ protected:
*/
template <template <typename> typename MockType = ::testing::NiceMock>
struct HandlerBaseTestBase : public MockBackendTestBase<MockType>,
public util::prometheus::WithPrometheus,
public SyncAsioContextTest,
public MockETLServiceTest {
protected:

View File

@@ -21,6 +21,7 @@
#include "util/Assert.hpp"
#include "util/config/Config.hpp"
#include "util/prometheus/Bool.hpp"
#include "util/prometheus/Counter.hpp"
#include "util/prometheus/Gauge.hpp"
#include "util/prometheus/Histogram.hpp"
@@ -81,6 +82,10 @@ using MockHistogramImplDouble = MockHistogramImpl<double>;
struct MockPrometheusImpl : PrometheusInterface {
MockPrometheusImpl() : PrometheusInterface(true, true)
{
EXPECT_CALL(*this, boolMetric)
.WillRepeatedly([this](std::string name, Labels labels, std::optional<std::string>) -> Bool {
return Bool{getMetric<GaugeInt>(std::move(name), std::move(labels))};
});
EXPECT_CALL(*this, counterInt)
.WillRepeatedly([this](std::string name, Labels labels, std::optional<std::string>) -> CounterInt& {
return getMetric<CounterInt>(std::move(name), std::move(labels));
@@ -109,6 +114,7 @@ struct MockPrometheusImpl : PrometheusInterface {
);
}
MOCK_METHOD(Bool, boolMetric, (std::string, Labels, std::optional<std::string>), (override));
MOCK_METHOD(CounterInt&, counterInt, (std::string, Labels, std::optional<std::string>), (override));
MOCK_METHOD(CounterDouble&, counterDouble, (std::string, Labels, std::optional<std::string>), (override));
MOCK_METHOD(GaugeInt&, gaugeInt, (std::string, Labels, std::optional<std::string>), (override));
@@ -252,7 +258,7 @@ struct WithPrometheus : virtual ::testing::Test {
~WithPrometheus() override
{
PrometheusService::init();
PrometheusService::replaceInstance(nullptr);
}
};

View File

@@ -0,0 +1,63 @@
//------------------------------------------------------------------------------
/*
This file is part of clio: https://github.com/XRPLF/clio
Copyright (c) 2024, the clio developers.
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include "util/prometheus/Bool.hpp"
#include "util/prometheus/Gauge.hpp"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <cstdint>
using namespace util::prometheus;
using testing::StrictMock;
struct BoolTests : public testing::Test {
struct MockImpl {
MOCK_METHOD(void, set, (int64_t), ());
MOCK_METHOD(int64_t, value, (), ());
};
StrictMock<MockImpl> impl_;
AnyBool<StrictMock<MockImpl>> bool_{impl_};
};
TEST_F(BoolTests, Set)
{
EXPECT_CALL(impl_, set(1));
bool_ = true;
EXPECT_CALL(impl_, set(0));
bool_ = false;
}
TEST_F(BoolTests, Get)
{
EXPECT_CALL(impl_, value()).WillOnce(testing::Return(1));
EXPECT_TRUE(bool_);
EXPECT_CALL(impl_, value()).WillOnce(testing::Return(0));
EXPECT_FALSE(bool_);
}
TEST_F(BoolTests, DefaultValues)
{
GaugeInt gauge{"test", ""};
Bool realBool{gauge};
EXPECT_FALSE(realBool);
}

View File

@@ -70,6 +70,13 @@ TEST_F(AnyCounterTests, operatorAdd)
counter += 42;
}
TEST_F(AnyCounterTests, set)
{
EXPECT_CALL(mockCounterImpl, value()).WillOnce(::testing::Return(4));
EXPECT_CALL(mockCounterImpl, set(42));
counter.set(42);
}
TEST_F(AnyCounterTests, reset)
{
EXPECT_CALL(mockCounterImpl, set(0));
@@ -82,6 +89,20 @@ TEST_F(AnyCounterTests, value)
EXPECT_EQ(counter.value(), 42);
}
struct AnyCounterDeathTest : AnyCounterTests {};
TEST_F(AnyCounterDeathTest, setLowerValue)
{
testing::Mock::AllowLeak(&mockCounterImpl);
EXPECT_DEATH(
{
EXPECT_CALL(mockCounterImpl, value()).WillOnce(::testing::Return(50));
counter.set(42);
},
".*"
);
}
struct CounterIntTests : ::testing::Test {
CounterInt counter{"test_counter", R"(label1="value1",label2="value2")"};
};

View File

@@ -128,10 +128,22 @@ TEST_F(GaugeIntTests, multithreadAddAndSubstract)
);
}
TEST_F(GaugeIntTests, DefaultValue)
{
GaugeInt realGauge{"some_gauge", ""};
EXPECT_EQ(realGauge.value(), 0);
}
struct GaugeDoubleTests : ::testing::Test {
GaugeDouble gauge{"test_Gauge", R"(label1="value1",label2="value2")"};
};
TEST_F(GaugeDoubleTests, DefaultValue)
{
GaugeDouble realGauge{"some_gauge", ""};
EXPECT_EQ(realGauge.value(), 0.);
}
TEST_F(GaugeDoubleTests, operatorAdd)
{
++gauge;

View File

@@ -19,6 +19,7 @@
#include "rpc/Errors.hpp"
#include "util/Fixtures.hpp"
#include "util/MockETLService.hpp"
#include "util/MockPrometheus.hpp"
#include "util/MockRPCEngine.hpp"
#include "util/Taggable.hpp"
#include "util/config/Config.hpp"
@@ -65,7 +66,9 @@ struct MockWsBase : public web::ConnectionBase {
}
};
class WebRPCServerHandlerTest : public MockBackendTest, public SyncAsioContextTest {
class WebRPCServerHandlerTest : public util::prometheus::WithPrometheus,
public MockBackendTest,
public SyncAsioContextTest {
protected:
void
SetUp() override