blob: 2752bbb7dfd8a6c4e4fcab816bea5611e860ef63 [file] [log] [blame]
#pragma once
#include "managed_store_clock.hpp"
#include <boost/beast/http/verb.hpp>
#include <boost/url/url_view.hpp>
#include <nlohmann/json.hpp>
#include <map>
#include <string>
#include <string_view>
#include <vector>
// TODO:: probably need a new namespace? even though there is no good reason for
// it
// TODO:: probably will just rename to bmcweb
namespace managedStore
{
struct RdeRequestStats
{
nlohmann::json toJson() const
{
nlohmann::json obj;
obj["totalReqs"] = this->totalReqs;
obj["totalDbusErrors"] = this->totalDbusErrors;
obj["totalSkips"] = this->totalSkips;
obj["totalRateLimitToggles"] = this->totalRateLimitToggles;
obj["CurrentRdeDbusErrorCount"] = this->rdeDbusErrorCount;
obj["CurrentRdeRequestSkips"] = this->rdeRequestSkips;
return obj;
}
void clear()
{
this->totalReqs = 0;
this->totalDbusErrors = 0;
this->totalSkips = 0;
this->totalRateLimitToggles = 0;
}
/** Total number of Rde Requests */
int totalReqs = 0;
/** Total number if dbus error in Rde Requests */
int totalDbusErrors = 0;
/** Total number Rde Requests skipped because of Rate Limit */
int totalSkips = 0;
/** Total number times Rde Requests Rate Limiter enabled */
int totalRateLimitToggles = 0;
// This is used to enable the rate limit the number of requests that can
// be sent to the RDEd.
/** Current Dbus error count*/
int rdeDbusErrorCount = 0;
// This keeps track number of skipped RDEd requests before turning off
// rate limiting
/** Current RDE Requests skip count*/
int rdeRequestSkips = 0;
};
/** stats context captured by each request handler in the bmcweb */
class RequestStatsContext
{
public:
explicit RequestStatsContext(const std::string& queryURLKey,
const std::chrono::steady_clock::time_point& now =
managedStore::clockNow());
virtual ~RequestStatsContext();
RequestStatsContext(const RequestStatsContext&) = default;
RequestStatsContext& operator=(const RequestStatsContext&) = default;
RequestStatsContext(RequestStatsContext&&) = default;
RequestStatsContext& operator=(RequestStatsContext&&) = default;
nlohmann::json toJson() const;
std::string getQueryURLKey() const;
std::chrono::steady_clock::time_point getTimepointStart() const;
void setOK(bool ok);
bool ok() const;
constexpr static const char kSep = '|';
void updateRdeReqs();
void updateRdeDbusErrors();
void updateRdeSkips();
void updateRdeRateLimitToggles();
int getRdeReqs() const;
int getRdeDbusErrors() const;
int getRdeSkips() const;
int getRdeRateLimitToggles() const;
/** compose a key for the histogram bucket to agg the stats */
static std::string queryURLKeyFor(const boost::beast::http::verb& method,
const boost::urls::url_view& qURL);
protected:
std::string queryURLKey;
bool isOK = false;
std::chrono::steady_clock::time_point timepointStart;
/** Rde Request Stats */
RdeRequestStats rdeStats;
};
/** Stats collected by the RequestStatsStore */
struct RequestStats
{
nlohmann::json toJson() const
{
nlohmann::json obj;
obj["countOK"] = this->countOK;
obj["countError"] = this->countError;
obj["latencyHistogram"] = nlohmann::json::array();
for (const auto& histogramBucket : this->latencyHistogramMilliseconds)
{
nlohmann::json bucketJson;
bucketJson["milliseconds"] = histogramBucket.first;
bucketJson["count"] = histogramBucket.second;
obj["latencyHistogram"].push_back(bucketJson);
}
return obj;
}
/** num of ok (200)*/
int countOK = 0;
/** num of errors (none 200)*/
int countError = 0;
/** sparse histogra buckets */
std::map<int64_t, int64_t> latencyHistogramMilliseconds;
};
/** Static config for RequestStatsStore */
struct RequestStatsStoreConfig
{
nlohmann::json toJson() const;
bool enable = true;
/** config to enable rde rate limiting*/
bool enableRdeRateLimit = false;
/** Number of consecutive dbus error to trigger RDE Request Rate Limiting*/
int rdeDbusErrorThreshold = 10;
/** When rate limiting enabled, the number of RDE requests skipped
before disabling disabling rate limit */
int rdeMaxSkips = 30;
};
/** global store to track and managed aggregated stats */
class RequestStatsStore
{
public:
static void initialize(const RequestStatsStoreConfig& config);
/** singleton instance */
static RequestStatsStore& instance();
/** check if the store is enabled */
static bool isEnabled()
{
return instance().getConfig().enable;
}
/** check if the rde rate limiting is enabled */
static bool isRdeRateLimitEnabled()
{
return instance().getConfig().enableRdeRateLimit;
}
static int rdeErrorThreshold()
{
return instance().getConfig().rdeDbusErrorThreshold;
}
static int rdeMaxSkips()
{
return instance().getConfig().rdeMaxSkips;
}
/** log request stats */
void logRequestStats(std::chrono::steady_clock::time_point now,
const RequestStatsContext& requestStatsContext);
/** log Rde request stats */
void logRdeRequestStats(const RequestStatsContext& requestStatsContext);
/** reset all the stats */
void clear();
/** export JSON */
nlohmann::json toJson() const;
// Current RDE counters to enable/disable rate limiting
void updateCurrentRdeDbusErrors();
int getCurrentRdeDbusErrors() const;
void setCurrentRdeDbusErrors(int errCount);
void updateCurrentRdeRequestSkips();
int getCurrentRdeRequestSkips() const;
void setCurrentRdeRequestSkips(int skipCount);
// used only for testing:
explicit RequestStatsStore(const RequestStatsStoreConfig& config);
virtual ~RequestStatsStore();
RequestStatsStore(const RequestStatsStore&) = default;
RequestStatsStore& operator=(const RequestStatsStore&) = default;
RequestStatsStore(RequestStatsStore&&) = default;
RequestStatsStore& operator=(RequestStatsStore&&) = default;
/** read only stats */
const std::map<std::string, RequestStats>& queryStats() const
{
return this->requestURLStats;
}
/** read only config */
const RequestStatsStoreConfig& getConfig() const
{
return this->config;
}
protected:
/** store config */
RequestStatsStoreConfig config;
/** redfish URL -> RequestStats */
std::map<std::string, RequestStats> requestURLStats;
/** Rde Request Stats */
RdeRequestStats rdeStats;
};
} // namespace managedStore