1
#pragma once
2

            
3
#include <compare>
4
#include <cstddef>
5
#include <tuple>
6

            
7
#include "envoy/common/time.h"
8
#include "envoy/event/dispatcher.h"
9
#include "envoy/event/timer.h"
10

            
11
#include "source/common/common/logger.h"
12
#include "source/common/http/session_idle_list_interface.h"
13

            
14
#include "absl/container/btree_set.h"
15
#include "absl/container/node_hash_map.h"
16
#include "absl/time/time.h"
17

            
18
namespace Envoy {
19
namespace Http {
20

            
21
class TestSessionIdleList;
22

            
23
constexpr size_t kMaxSessionsToTerminateInOneRound = 5;
24
constexpr size_t kMaxSessionsToTerminateInOneRoundWhenSaturated = 50;
25

            
26
// This class manages a list of idle sessions.
27
class SessionIdleList : public SessionIdleListInterface, public Logger::Loggable<Logger::Id::http> {
28
public:
29
21
  explicit SessionIdleList(Event::Dispatcher& dispatcher) : dispatcher_(dispatcher) {};
30
21
  ~SessionIdleList() override = default;
31

            
32
  // Adds a session to the idle list.
33
  void AddSession(IdleSessionInterface& session) override;
34

            
35
  // Removes a session from the idle list.
36
  void RemoveSession(IdleSessionInterface& session) override;
37

            
38
  // Terminates idle sessions if they are eligible for termination. This is
39
  // called by the worker thread when the system is overloaded.
40
  void MaybeTerminateIdleSessions(bool is_saturated) override;
41

            
42
  // Sets the minimum time before a session can be terminated.
43
12
  void set_min_time_before_termination_allowed(absl::Duration min_time_before_termination_allowed) {
44
12
    min_time_before_termination_allowed_ = min_time_before_termination_allowed;
45
12
  };
46

            
47
13
  void set_max_sessions_to_terminate_in_one_round(int max_sessions_to_terminate_in_one_round) {
48
13
    max_sessions_to_terminate_in_one_round_ = max_sessions_to_terminate_in_one_round;
49
13
  }
50

            
51
  void set_max_sessions_to_terminate_in_one_round_when_saturated(
52
12
      int max_sessions_to_terminate_in_one_round_when_saturated) {
53
12
    max_sessions_to_terminate_in_one_round_when_saturated_ =
54
12
        max_sessions_to_terminate_in_one_round_when_saturated;
55
12
  }
56

            
57
  // Sets whether to ignore the minimum time before a session can be terminated.
58
1
  void set_ignore_min_time_before_termination_allowed(bool ignore) {
59
1
    ignore_min_time_before_termination_allowed_ = ignore;
60
1
  };
61

            
62
private:
63
  friend class TestSessionIdleList;
64

            
65
  class IdleSessions {
66
    struct SessionInfo {
67
      SessionInfo() = default;
68
      SessionInfo(IdleSessionInterface& session, MonotonicTime enqueue_time)
69
26
          : session(&session), enqueue_time(enqueue_time) {}
70
      IdleSessionInterface* session = nullptr;
71
      // The time at which this session was added.
72
      MonotonicTime enqueue_time;
73

            
74
      // Sort by enqueue time. Used by `IdleSessionSet` for session order.
75
63
      friend std::strong_ordering operator<=>(const SessionInfo& lhs, const SessionInfo& rhs) {
76
63
        return std::forward_as_tuple(lhs.enqueue_time, lhs.session) <=>
77
63
               std::forward_as_tuple(rhs.enqueue_time, rhs.session);
78
63
      }
79
    };
80

            
81
    using IdleSessionSet = absl::btree_set<SessionInfo>;
82
    using IdleSessionMap = absl::node_hash_map<IdleSessionInterface*, SessionInfo>;
83

            
84
  public:
85
21
    IdleSessions() = default;
86

            
87
    // This type is neither copyable nor movable.
88
    IdleSessions(const IdleSessions&) = delete;
89
    IdleSessions& operator=(const IdleSessions&) = delete;
90

            
91
12
    IdleSessionInterface& next_session_to_terminate() { return *set_.begin()->session; }
92

            
93
    void AddSessionToList(MonotonicTime enqueue_time, IdleSessionInterface& session);
94

            
95
    void RemoveSessionFromList(IdleSessionInterface& session);
96

            
97
    // Get the time at which the session was added to the idle list.
98
    MonotonicTime GetEnqueueTime(IdleSessionInterface& session) const;
99

            
100
    // Returns true if the session is in the map. For testing only.
101
15
    bool ContainsForTest(IdleSessionInterface& session) const { return map_.contains(&session); }
102

            
103
16
    size_t size() const { return set_.size(); }
104

            
105
  private:
106
    // Set of sessions, ordered by enqueue time.
107
    IdleSessionSet set_;
108

            
109
    // Alternate representation of the contents of the IdleSessionInterface list
110
    // to allow O(1) lookup which is required for efficient removal.
111
    IdleSessionMap map_;
112
  };
113

            
114
21
  const IdleSessions* idle_sessions() const { return &idle_sessions_; }
115

            
116
  // If this is > 0 then we do not terminate more than that many
117
  // sessions in a single attempt. This prevents us from doing too
118
  // much work in a single round. We want a small constant for this.
119
  size_t MaxSessionsToTerminateInOneRound(bool is_saturated) const;
120

            
121
  // Returns the minimum time before a session can be terminated.
122
  absl::Duration MinTimeBeforeTerminationAllowed() const;
123

            
124
  Event::Dispatcher& dispatcher_;
125
  // The sessions currently tracked.
126
  IdleSessions idle_sessions_;
127
  absl::Duration min_time_before_termination_allowed_ = absl::Minutes(1);
128
  bool ignore_min_time_before_termination_allowed_ = false;
129
  size_t max_sessions_to_terminate_in_one_round_ = kMaxSessionsToTerminateInOneRound;
130
  size_t max_sessions_to_terminate_in_one_round_when_saturated_ =
131
      kMaxSessionsToTerminateInOneRoundWhenSaturated;
132
};
133

            
134
} // namespace Http
135
} // namespace Envoy