LCOV - code coverage report
Current view: top level - source/common/upstream - resource_manager_impl.h (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 24 67 35.8 %
Date: 2024-01-05 06:35:25 Functions: 11 20 55.0 %

          Line data    Source code
       1             : #pragma once
       2             : 
       3             : #include <atomic>
       4             : #include <cstdint>
       5             : #include <memory>
       6             : #include <string>
       7             : 
       8             : #include "envoy/common/resource.h"
       9             : #include "envoy/runtime/runtime.h"
      10             : #include "envoy/upstream/resource_manager.h"
      11             : #include "envoy/upstream/upstream.h"
      12             : 
      13             : #include "source/common/common/assert.h"
      14             : #include "source/common/common/basic_resource_impl.h"
      15             : 
      16             : namespace Envoy {
      17             : namespace Upstream {
      18             : 
      19             : struct ManagedResourceImpl : public BasicResourceLimitImpl {
      20             :   ManagedResourceImpl(uint64_t max, Runtime::Loader& runtime, const std::string& runtime_key,
      21             :                       Stats::Gauge& open_gauge, Stats::Gauge& remaining)
      22             :       : BasicResourceLimitImpl(max, runtime, runtime_key), open_gauge_(open_gauge),
      23       12675 :         remaining_(remaining) {
      24       12675 :     remaining_.set(max);
      25       12675 :   }
      26             : 
      27             :   // BasicResourceLimitImpl
      28         721 :   void inc() override {
      29         721 :     BasicResourceLimitImpl::inc();
      30         721 :     updateRemaining();
      31         721 :     open_gauge_.set(BasicResourceLimitImpl::canCreate() ? 0 : 1);
      32         721 :   }
      33        1067 :   void decBy(uint64_t amount) override {
      34        1067 :     BasicResourceLimitImpl::decBy(amount);
      35        1067 :     updateRemaining();
      36        1067 :     open_gauge_.set(BasicResourceLimitImpl::canCreate() ? 0 : 1);
      37        1067 :   }
      38             : 
      39             :   /**
      40             :    * We set the gauge instead of incrementing and decrementing because,
      41             :    * though atomics are used, it is possible for the current resource count
      42             :    * to be greater than the supplied max.
      43             :    */
      44        1788 :   void updateRemaining() {
      45             :     /**
      46             :      * We cannot use std::max here because max() and current_ are
      47             :      * unsigned and subtracting them may overflow.
      48             :      */
      49        1788 :     const uint64_t current_copy = current_;
      50        1788 :     remaining_.set(max() > current_copy ? max() - current_copy : 0);
      51        1788 :   }
      52             : 
      53             :   /**
      54             :    * A gauge to notify the live circuit breaker state. The gauge is set to 0
      55             :    * to notify that the circuit breaker is not yet triggered.
      56             :    */
      57             :   Stats::Gauge& open_gauge_;
      58             : 
      59             :   /**
      60             :    * The number of resources remaining before the circuit breaker opens.
      61             :    */
      62             :   Stats::Gauge& remaining_;
      63             : };
      64             : 
      65             : /**
      66             :  * Implementation of ResourceManager.
      67             :  * NOTE: This implementation makes some assumptions which favor simplicity over correctness.
      68             :  * 1) Primarily, it assumes that traffic will be mostly balanced over all the worker threads since
      69             :  *    no attempt is made to balance resources between them. It is possible that starvation can
      70             :  *    occur during high contention.
      71             :  * 2) Though atomics are used, it is possible for resources to temporarily go above the supplied
      72             :  *    maximums. This should not effect overall behavior.
      73             :  */
      74             : class ResourceManagerImpl : public ResourceManager {
      75             : public:
      76             :   ResourceManagerImpl(Runtime::Loader& runtime, const std::string& runtime_key,
      77             :                       uint64_t max_connections, uint64_t max_pending_requests,
      78             :                       uint64_t max_requests, uint64_t max_retries, uint64_t max_connection_pools,
      79             :                       uint64_t max_connections_per_host, ClusterCircuitBreakersStats cb_stats,
      80             :                       absl::optional<double> budget_percent,
      81             :                       absl::optional<uint32_t> min_retry_concurrency)
      82             :       : connections_(max_connections, runtime, runtime_key + "max_connections", cb_stats.cx_open_,
      83             :                      cb_stats.remaining_cx_),
      84             :         pending_requests_(max_pending_requests, runtime, runtime_key + "max_pending_requests",
      85             :                           cb_stats.rq_pending_open_, cb_stats.remaining_pending_),
      86             :         requests_(max_requests, runtime, runtime_key + "max_requests", cb_stats.rq_open_,
      87             :                   cb_stats.remaining_rq_),
      88             :         connection_pools_(max_connection_pools, runtime, runtime_key + "max_connection_pools",
      89             :                           cb_stats.cx_pool_open_, cb_stats.remaining_cx_pools_),
      90             :         max_connections_per_host_(max_connections_per_host),
      91             :         retries_(budget_percent, min_retry_concurrency, max_retries, runtime,
      92             :                  runtime_key + "retry_budget.", runtime_key + "max_retries",
      93             :                  cb_stats.rq_retry_open_, cb_stats.remaining_retries_, requests_,
      94        2535 :                  pending_requests_) {}
      95             : 
      96             :   // Upstream::ResourceManager
      97         519 :   ResourceLimit& connections() override { return connections_; }
      98         519 :   ResourceLimit& pendingRequests() override { return pending_requests_; }
      99         606 :   ResourceLimit& requests() override { return requests_; }
     100           0 :   ResourceLimit& retries() override { return retries_; }
     101         692 :   ResourceLimit& connectionPools() override { return connection_pools_; }
     102         173 :   uint64_t maxConnectionsPerHost() override { return max_connections_per_host_; }
     103             : 
     104             : private:
     105             :   class RetryBudgetImpl : public ResourceLimit {
     106             :   public:
     107             :     RetryBudgetImpl(absl::optional<double> budget_percent,
     108             :                     absl::optional<uint32_t> min_retry_concurrency, uint64_t max_retries,
     109             :                     Runtime::Loader& runtime, const std::string& retry_budget_runtime_key,
     110             :                     const std::string& max_retries_runtime_key, Stats::Gauge& open_gauge,
     111             :                     Stats::Gauge& remaining, const ResourceLimit& requests,
     112             :                     const ResourceLimit& pending_requests)
     113             :         : runtime_(runtime),
     114             :           max_retry_resource_(max_retries, runtime, max_retries_runtime_key, open_gauge, remaining),
     115             :           budget_percent_(budget_percent), min_retry_concurrency_(min_retry_concurrency),
     116             :           budget_percent_key_(retry_budget_runtime_key + "budget_percent"),
     117             :           min_retry_concurrency_key_(retry_budget_runtime_key + "min_retry_concurrency"),
     118        2535 :           requests_(requests), pending_requests_(pending_requests), remaining_(remaining) {}
     119             : 
     120             :     // Envoy::ResourceLimit
     121           0 :     bool canCreate() override {
     122           0 :       if (!useRetryBudget()) {
     123           0 :         return max_retry_resource_.canCreate();
     124           0 :       }
     125           0 :       clearRemainingGauge();
     126           0 :       return count() < max();
     127           0 :     }
     128           0 :     void inc() override {
     129           0 :       max_retry_resource_.inc();
     130           0 :       clearRemainingGauge();
     131           0 :     }
     132           0 :     void dec() override {
     133           0 :       max_retry_resource_.dec();
     134           0 :       clearRemainingGauge();
     135           0 :     }
     136           0 :     void decBy(uint64_t amount) override {
     137           0 :       max_retry_resource_.decBy(amount);
     138           0 :       clearRemainingGauge();
     139           0 :     }
     140           0 :     uint64_t max() override {
     141           0 :       if (!useRetryBudget()) {
     142           0 :         return max_retry_resource_.max();
     143           0 :       }
     144             : 
     145           0 :       const uint64_t current_active = requests_.count() + pending_requests_.count();
     146           0 :       const double budget_percent = runtime_.snapshot().getDouble(
     147           0 :           budget_percent_key_, budget_percent_ ? *budget_percent_ : 20.0);
     148           0 :       const uint32_t min_retry_concurrency = runtime_.snapshot().getInteger(
     149           0 :           min_retry_concurrency_key_, min_retry_concurrency_ ? *min_retry_concurrency_ : 3);
     150             : 
     151           0 :       clearRemainingGauge();
     152             : 
     153             :       // We enforce that the retry concurrency is never allowed to go below the
     154             :       // min_retry_concurrency, even if the configured percent of the current active requests
     155             :       // yields a value that is smaller.
     156           0 :       return std::max<uint64_t>(budget_percent / 100.0 * current_active, min_retry_concurrency);
     157           0 :     }
     158           0 :     uint64_t count() const override { return max_retry_resource_.count(); }
     159             : 
     160             :   private:
     161           0 :     bool useRetryBudget() const {
     162           0 :       return runtime_.snapshot().get(budget_percent_key_).has_value() ||
     163           0 :              runtime_.snapshot().get(min_retry_concurrency_key_).has_value() || budget_percent_ ||
     164           0 :              min_retry_concurrency_;
     165           0 :     }
     166             : 
     167             :     // If the retry budget is in use, the stats tracking remaining retries do not make sense since
     168             :     // they would dependent on other resources that can change without a call to this object.
     169             :     // Therefore, the gauge should just be reset to 0.
     170           0 :     void clearRemainingGauge() {
     171           0 :       if (useRetryBudget()) {
     172           0 :         remaining_.set(0);
     173           0 :       }
     174           0 :     }
     175             : 
     176             :     Runtime::Loader& runtime_;
     177             :     // The max_retry resource is nested within the budget to maintain state if the retry budget is
     178             :     // toggled.
     179             :     ManagedResourceImpl max_retry_resource_;
     180             :     const absl::optional<double> budget_percent_;
     181             :     const absl::optional<uint32_t> min_retry_concurrency_;
     182             :     const std::string budget_percent_key_;
     183             :     const std::string min_retry_concurrency_key_;
     184             :     const ResourceLimit& requests_;
     185             :     const ResourceLimit& pending_requests_;
     186             :     Stats::Gauge& remaining_;
     187             :   };
     188             : 
     189             :   ManagedResourceImpl connections_;
     190             :   ManagedResourceImpl pending_requests_;
     191             :   ManagedResourceImpl requests_;
     192             :   ManagedResourceImpl connection_pools_;
     193             :   uint64_t max_connections_per_host_;
     194             :   RetryBudgetImpl retries_;
     195             : };
     196             : 
     197             : using ResourceManagerImplPtr = std::unique_ptr<ResourceManagerImpl>;
     198             : 
     199             : } // namespace Upstream
     200             : } // namespace Envoy

Generated by: LCOV version 1.15