| #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 |