19 #include <glog/logging.h> 23 using std::chrono::milliseconds;
28 milliseconds timeout, Callback* callback)
30 eventBase_(eventBase),
31 drainIterator_(conns_.
end()),
32 idleIterator_(conns_.
end()),
35 idleConnEarlyDropThreshold_(timeout_ / 2) {
42 CHECK_NOTNULL(connection);
54 conns_.push_front(*connection);
62 scheduleTimeout(connection, timeout_);
65 if (drainHelper_.getShutdownState() >=
66 ShutdownState::NOTIFY_PENDING_SHUTDOWN &&
67 notifyPendingShutdown_) {
71 if (drainHelper_.getShutdownState() >= ShutdownState::CLOSE_WHEN_IDLE) {
78 eventBase_->runInLoop([connection,
this, cmDg, connDg] {
80 auto it = conns_.iterator_to(*connection);
81 DCHECK(it != conns_.end());
92 std::chrono::milliseconds timeout) {
93 if (timeout > std::chrono::milliseconds(0)) {
94 eventBase_->timer().scheduleTimeout(connection, timeout);
100 std::chrono::milliseconds timeout) {
101 eventBase_->timer().scheduleTimeout(callback, timeout);
112 auto it = conns_.iterator_to(*connection);
113 if (it == drainIterator_) {
116 if (it == idleIterator_) {
122 callback_->onConnectionRemoved(connection);
132 std::chrono::milliseconds idleGrace) {
133 VLOG(3) <<
this <<
" initiateGracefulShutdown with nconns=" << conns_.size();
135 VLOG(3) <<
"Ignoring redundant call to initiateGracefulShutdown";
138 drainHelper_.startDrainAll(idleGrace);
142 std::chrono::milliseconds idleGrace) {
144 VLOG(3) <<
"Ignoring partial drain with full drain in progress";
147 drainHelper_.startDrainPartial(pct, idleGrace);
151 double pct, std::chrono::milliseconds idleGrace) {
154 startDrain(idleGrace);
158 std::chrono::milliseconds idleGrace) {
165 startDrain(idleGrace);
169 std::chrono::milliseconds idleGrace) {
170 if (idleGrace.count() > 0) {
171 shutdownState_ = ShutdownState::NOTIFY_PENDING_SHUTDOWN;
172 scheduleTimeout(idleGrace);
173 VLOG(3) <<
"Scheduling idle grace period of " << idleGrace.count() <<
"ms";
175 manager_.notifyPendingShutdown_ =
false;
176 shutdownState_ = ShutdownState::CLOSE_WHEN_IDLE;
177 VLOG(3) <<
"proceeding directly to closing idle connections";
179 manager_.drainIterator_ = drainStartIterator();
186 size_t numCleared = 0;
189 auto it = manager_.drainIterator_;
191 CHECK(shutdownState_ == ShutdownState::NOTIFY_PENDING_SHUTDOWN ||
192 shutdownState_ == ShutdownState::CLOSE_WHEN_IDLE);
193 while (it != manager_.conns_.end() && (numKept + numCleared) < 64) {
195 if (shutdownState_ == ShutdownState::NOTIFY_PENDING_SHUTDOWN) {
210 if (shutdownState_ == ShutdownState::CLOSE_WHEN_IDLE) {
211 VLOG(2) <<
"Idle connections cleared: " << numCleared <<
212 ", busy conns kept: " << numKept;
214 VLOG(3) <<
this <<
" notified n=" << numKept;
216 manager_.drainIterator_ = it;
217 if (it != manager_.conns_.end()) {
218 manager_.eventBase_->runInLoop(
this);
220 if (shutdownState_ == ShutdownState::NOTIFY_PENDING_SHUTDOWN) {
221 VLOG(3) <<
this <<
" finished notify_pending_shutdown";
222 shutdownState_ = ShutdownState::NOTIFY_PENDING_SHUTDOWN_COMPLETE;
223 if (!isScheduled()) {
225 shutdownState_ = ShutdownState::CLOSE_WHEN_IDLE;
226 manager_.drainIterator_ = drainStartIterator();
227 manager_.eventBase_->runInLoop(
this);
230 shutdownState_ = ShutdownState::CLOSE_WHEN_IDLE_COMPLETE;
237 VLOG(2) <<
this <<
" idleGracefulTimeoutExpired";
238 if (shutdownState_ ==
239 ShutdownState::NOTIFY_PENDING_SHUTDOWN_COMPLETE) {
240 shutdownState_ = ShutdownState::CLOSE_WHEN_IDLE;
241 manager_.drainIterator_ = drainStartIterator();
244 VLOG(4) <<
this <<
" idleGracefulTimeoutExpired during " 245 "NOTIFY_PENDING_SHUTDOWN, ignoring";
250 drainHelper_.setShutdownState(ShutdownState::CLOSE_WHEN_IDLE_COMPLETE);
251 drainHelper_.cancelTimeout();
259 stopDrainingForShutdown();
262 VLOG_IF(4, conns_.empty()) <<
"no connections to drop";
263 VLOG_IF(2, !conns_.empty()) <<
"connections to drop: " << conns_.size();
266 while (!conns_.empty()) {
273 static const unsigned MAX_CONNS_TO_DUMP = 2;
274 if (++i <= MAX_CONNS_TO_DUMP) {
279 drainIterator_ = conns_.end();
280 idleIterator_ = conns_.end();
281 drainHelper_.cancelLoopCallback();
293 stopDrainingForShutdown();
295 const size_t N = conns_.size();
296 const size_t numToDrop = std::max<size_t>(0, std::min<size_t>(N, N * pct));
297 for (
size_t i = 0;
i < numToDrop && !conns_.empty();
i++) {
299 removeConnection(&conn);
306 auto it = conns_.iterator_to(conn);
307 if (it == idleIterator_) {
311 conns_.push_front(conn);
316 auto it = conns_.iterator_to(conn);
317 bool moveDrainIter =
false;
318 if (it == drainIterator_) {
320 moveDrainIter =
true;
323 conns_.push_back(conn);
324 if (idleIterator_ == conns_.end()) {
327 if (moveDrainIter && drainIterator_ == conns_.end()) {
334 VLOG(4) <<
"attempt to drop " << num <<
" idle connections";
335 if (idleConnEarlyDropThreshold_ >= timeout_) {
341 auto it = idleIterator_;
342 if (it == conns_.end()) {
345 auto idleTime = it->getIdleTime();
346 if (idleTime == std::chrono::milliseconds(0) ||
347 idleTime <= idleConnEarlyDropThreshold_) {
348 VLOG(4) <<
"conn's idletime: " << idleTime.count()
349 <<
", earlyDropThreshold: " << idleConnEarlyDropThreshold_.count()
350 <<
", attempt to drop " << count <<
"/" << num;
size_t dropIdleConnections(size_t num)
void idleGracefulTimeoutExpired()
void addConnection(ManagedConnection *connection, bool timeout=false)
void stopDrainingForShutdown()
ConnectionManager(folly::EventBase *eventBase, std::chrono::milliseconds timeout, Callback *callback=nullptr)
void dropConnections(double pct)
folly::SafeIntrusiveListHook listHook_
void startDrain(std::chrono::milliseconds idleGrace)
void fireNotifyPendingShutdown()
void removeConnection(ManagedConnection *connection)
void onActivated(ManagedConnection &conn) override
virtual void dropConnection()=0
void initiateGracefulShutdown(std::chrono::milliseconds idleGrace)
void startDrainAll(std::chrono::milliseconds idleGrace)
void dropAllConnections()
void onDeactivated(ManagedConnection &conn) override
void setConnectionManager(ConnectionManager *mgr)
void scheduleTimeout(ManagedConnection *const connection, std::chrono::milliseconds timeout)
auto end(TestAdlIterable &instance)
void fireCloseWhenIdle(bool force_to_close=false)
ConnectionManager * getConnectionManager()
void drainConnections(double pct, std::chrono::milliseconds idleGrace)
uint64_t getNumConnections() const override
virtual bool isBusy() const =0
folly::Function< void()> callback_
void startDrainPartial(double pct, std::chrono::milliseconds idleGrace)
virtual void dumpConnectionState(uint8_t loglevel)=0