]>
Commit | Line | Data |
---|---|---|
51fdcbd5 | 1 | /* |
f70aedc4 | 2 | * Copyright (C) 1996-2021 The Squid Software Foundation and contributors |
e25c139f | 3 | * |
bbc27441 AJ |
4 | * Squid software is distributed under GPLv2+ license and includes |
5 | * contributions from numerous individuals and organizations. | |
6 | * Please see the COPYING and CONTRIBUTORS files for details. | |
51fdcbd5 | 7 | */ |
603a02fd | 8 | |
bbc27441 AJ |
9 | /* DEBUG: section 48 Persistent Connections */ |
10 | ||
582c2af2 | 11 | #include "squid.h" |
e8dca475 | 12 | #include "CachePeer.h" |
582c2af2 | 13 | #include "comm.h" |
e0d28505 | 14 | #include "comm/Connection.h" |
7e66d5e2 | 15 | #include "comm/Read.h" |
c4ad1349 | 16 | #include "fd.h" |
582c2af2 FC |
17 | #include "fde.h" |
18 | #include "globals.h" | |
a750e510 | 19 | #include "mgr/Registration.h" |
e8dca475 | 20 | #include "neighbors.h" |
781ce8ff | 21 | #include "pconn.h" |
e8dca475 | 22 | #include "PeerPoolMgr.h" |
4d5904f7 | 23 | #include "SquidConfig.h" |
582c2af2 | 24 | #include "Store.h" |
603a02fd | 25 | |
f53969cc | 26 | #define PCONN_FDS_SZ 8 /* pconn set size, increase for better memcache hit rate */ |
62e76326 | 27 | |
642a305c | 28 | //TODO: re-attach to MemPools. WAS: static MemAllocator *pconn_fds_pool = NULL; |
62ee09ca | 29 | PconnModule * PconnModule::instance = NULL; |
781ce8ff | 30 | CBDATA_CLASS_INIT(IdleConnList); |
fa80a8ef | 31 | |
781ce8ff | 32 | /* ========== IdleConnList ============================================ */ |
603a02fd | 33 | |
a27fcaed | 34 | IdleConnList::IdleConnList(const char *aKey, PconnPool *thePool) : |
f53969cc SM |
35 | capacity_(PCONN_FDS_SZ), |
36 | size_(0), | |
37 | parent_(thePool) | |
603a02fd | 38 | { |
a27fcaed CT |
39 | //Initialize hash_link members |
40 | key = xstrdup(aKey); | |
41 | next = NULL; | |
42 | ||
e3981652 | 43 | theList_ = new Comm::ConnectionPointer[capacity_]; |
a27fcaed | 44 | |
b856803f | 45 | registerRunner(); |
a27fcaed | 46 | |
e3981652 | 47 | // TODO: re-attach to MemPools. WAS: theList = (?? *)pconn_fds_pool->alloc(); |
603a02fd | 48 | } |
49 | ||
781ce8ff | 50 | IdleConnList::~IdleConnList() |
603a02fd | 51 | { |
9815f129 AJ |
52 | if (parent_) |
53 | parent_->unlinkList(this); | |
62e76326 | 54 | |
e8dca475 CT |
55 | if (size_) { |
56 | parent_ = NULL; // prevent reentrant notifications and deletions | |
57 | closeN(size_); | |
58 | } | |
59 | ||
e3981652 | 60 | delete[] theList_; |
62e76326 | 61 | |
a27fcaed | 62 | xfree(key); |
603a02fd | 63 | } |
64 | ||
80463bb4 AJ |
65 | /** Search the list. Matches by FD socket number. |
66 | * Performed from the end of list where newest entries are. | |
67 | * | |
68 | * \retval <0 The connection is not listed | |
69 | * \retval >=0 The connection array index | |
70 | */ | |
781ce8ff | 71 | int |
80463bb4 | 72 | IdleConnList::findIndexOf(const Comm::ConnectionPointer &conn) const |
603a02fd | 73 | { |
e3981652 AJ |
74 | for (int index = size_ - 1; index >= 0; --index) { |
75 | if (conn->fd == theList_[index]->fd) { | |
80463bb4 | 76 | debugs(48, 3, HERE << "found " << conn << " at index " << index); |
139d9221 | 77 | return index; |
80463bb4 | 78 | } |
603a02fd | 79 | } |
62e76326 | 80 | |
80463bb4 | 81 | debugs(48, 2, HERE << conn << " NOT FOUND!"); |
781ce8ff | 82 | return -1; |
c4b7a5a9 | 83 | } |
84 | ||
80463bb4 | 85 | /** Remove the entry at specified index. |
a8c28b85 | 86 | * May perform a shuffle of list entries to fill the gap. |
80463bb4 AJ |
87 | * \retval false The index is not an in-use entry. |
88 | */ | |
68720ca2 | 89 | bool |
80463bb4 | 90 | IdleConnList::removeAt(int index) |
c4b7a5a9 | 91 | { |
e3981652 | 92 | if (index < 0 || index >= size_) |
68720ca2 | 93 | return false; |
c4b7a5a9 | 94 | |
80463bb4 | 95 | // shuffle the remaining entries to fill the new gap. |
5db6bf73 | 96 | for (; index < size_ - 1; ++index) |
e3981652 | 97 | theList_[index] = theList_[index + 1]; |
4eb4fb17 | 98 | theList_[--size_] = NULL; |
62e76326 | 99 | |
9815f129 | 100 | if (parent_) { |
983983ce | 101 | parent_->noteConnectionRemoved(); |
4eb4fb17 | 102 | if (size_ == 0) { |
a27fcaed | 103 | debugs(48, 3, "deleting " << hashKeyStr(this)); |
4eb4fb17 CT |
104 | delete this; |
105 | } | |
8ea9f779 | 106 | } |
2dba5b8e | 107 | |
9815f129 | 108 | return true; |
f915be8d AJ |
109 | } |
110 | ||
111 | // almost a duplicate of removeFD. But drops multiple entries. | |
112 | void | |
113 | IdleConnList::closeN(size_t n) | |
114 | { | |
115 | if (n < 1) { | |
116 | debugs(48, 2, HERE << "Nothing to do."); | |
117 | return; | |
8dc322e4 | 118 | } else if (n >= (size_t)size_) { |
f915be8d | 119 | debugs(48, 2, HERE << "Closing all entries."); |
8dc322e4 | 120 | while (size_ > 0) { |
4eb4fb17 | 121 | const Comm::ConnectionPointer conn = theList_[--size_]; |
9815f129 AJ |
122 | theList_[size_] = NULL; |
123 | clearHandlers(conn); | |
124 | conn->close(); | |
125 | if (parent_) | |
126 | parent_->noteConnectionRemoved(); | |
f915be8d | 127 | } |
8dc322e4 | 128 | } else { //if (n < size_) |
9815f129 | 129 | debugs(48, 2, HERE << "Closing " << n << " of " << size_ << " entries."); |
f915be8d | 130 | |
8dc322e4 | 131 | size_t index; |
f915be8d | 132 | // ensure the first N entries are closed |
5db6bf73 | 133 | for (index = 0; index < n; ++index) { |
8dc322e4 CT |
134 | const Comm::ConnectionPointer conn = theList_[index]; |
135 | theList_[index] = NULL; | |
9815f129 AJ |
136 | clearHandlers(conn); |
137 | conn->close(); | |
138 | if (parent_) | |
139 | parent_->noteConnectionRemoved(); | |
f915be8d AJ |
140 | } |
141 | // shuffle the list N down. | |
5db6bf73 | 142 | for (index = 0; index < (size_t)size_ - n; ++index) { |
8dc322e4 | 143 | theList_[index] = theList_[index + n]; |
f915be8d AJ |
144 | } |
145 | // ensure the last N entries are unset | |
8dc322e4 | 146 | while (index < ((size_t)size_)) { |
f412b2d6 FC |
147 | theList_[index] = NULL; |
148 | ++index; | |
f915be8d | 149 | } |
8dc322e4 | 150 | size_ -= n; |
f915be8d AJ |
151 | } |
152 | ||
9815f129 | 153 | if (parent_ && size_ == 0) { |
a27fcaed | 154 | debugs(48, 3, "deleting " << hashKeyStr(this)); |
781ce8ff | 155 | delete this; |
156 | } | |
c4b7a5a9 | 157 | } |
158 | ||
781ce8ff | 159 | void |
642a305c | 160 | IdleConnList::clearHandlers(const Comm::ConnectionPointer &conn) |
781ce8ff | 161 | { |
5be728d4 | 162 | debugs(48, 3, HERE << "removing close handler for " << conn); |
80463bb4 | 163 | comm_read_cancel(conn->fd, IdleConnList::Read, this); |
8d77a37c | 164 | commUnsetConnTimeout(conn); |
781ce8ff | 165 | } |
62e76326 | 166 | |
781ce8ff | 167 | void |
642a305c | 168 | IdleConnList::push(const Comm::ConnectionPointer &conn) |
c4b7a5a9 | 169 | { |
e3981652 AJ |
170 | if (size_ == capacity_) { |
171 | debugs(48, 3, HERE << "growing idle Connection array"); | |
172 | capacity_ <<= 1; | |
173 | const Comm::ConnectionPointer *oldList = theList_; | |
174 | theList_ = new Comm::ConnectionPointer[capacity_]; | |
5db6bf73 | 175 | for (int index = 0; index < size_; ++index) |
e3981652 | 176 | theList_[index] = oldList[index]; |
781ce8ff | 177 | |
642a305c | 178 | delete[] oldList; |
781ce8ff | 179 | } |
62e76326 | 180 | |
983983ce AJ |
181 | if (parent_) |
182 | parent_->noteConnectionAdded(); | |
2dba5b8e | 183 | |
f412b2d6 FC |
184 | theList_[size_] = conn; |
185 | ++size_; | |
8d77a37c | 186 | AsyncCall::Pointer readCall = commCbCall(5,4, "IdleConnList::Read", |
dc49061a | 187 | CommIoCbPtrFun(IdleConnList::Read, this)); |
8d77a37c | 188 | comm_read(conn, fakeReadBuf_, sizeof(fakeReadBuf_), readCall); |
4eb4fb17 | 189 | AsyncCall::Pointer timeoutCall = commCbCall(5,4, "IdleConnList::Timeout", |
dc49061a | 190 | CommTimeoutCbPtrFun(IdleConnList::Timeout, this)); |
c5c06f02 | 191 | commSetConnTimeout(conn, conn->timeLeft(Config.Timeout.serverIdlePconn), timeoutCall); |
603a02fd | 192 | } |
193 | ||
a8c28b85 AJ |
194 | /// Determine whether an entry in the idle list is available for use. |
195 | /// Returns false if the entry is unset, closed or closing. | |
196 | bool | |
197 | IdleConnList::isAvailable(int i) const | |
198 | { | |
199 | const Comm::ConnectionPointer &conn = theList_[i]; | |
200 | ||
201 | // connection already closed. useless. | |
202 | if (!Comm::IsConnOpen(conn)) | |
203 | return false; | |
204 | ||
205 | // our connection early-read/close handler is scheduled to run already. unsafe | |
206 | if (!COMMIO_FD_READCB(conn->fd)->active()) | |
207 | return false; | |
208 | ||
209 | return true; | |
210 | } | |
211 | ||
9815f129 AJ |
212 | Comm::ConnectionPointer |
213 | IdleConnList::pop() | |
214 | { | |
5e263176 | 215 | for (int i=size_-1; i>=0; --i) { |
9815f129 | 216 | |
a8c28b85 AJ |
217 | if (!isAvailable(i)) |
218 | continue; | |
219 | ||
220 | // our connection timeout handler is scheduled to run already. unsafe for now. | |
221 | // TODO: cancel the pending timeout callback and allow re-use of the conn. | |
222 | if (fd_table[theList_[i]->fd].timeoutHandler == NULL) | |
9815f129 AJ |
223 | continue; |
224 | ||
225 | // finally, a match. pop and return it. | |
226 | Comm::ConnectionPointer result = theList_[i]; | |
f7605425 | 227 | clearHandlers(result); |
9815f129 AJ |
228 | /* may delete this */ |
229 | removeAt(i); | |
230 | return result; | |
231 | } | |
232 | ||
233 | return Comm::ConnectionPointer(); | |
603a02fd | 234 | } |
235 | ||
781ce8ff | 236 | /* |
237 | * XXX this routine isn't terribly efficient - if there's a pending | |
238 | * read event (which signifies the fd will close in the next IO loop!) | |
239 | * we ignore the FD and move onto the next one. This means, as an example, | |
240 | * if we have a lot of FDs open to a very popular server and we get a bunch | |
241 | * of requests JUST as they timeout (say, it shuts down) we'll be wasting | |
242 | * quite a bit of CPU. Just keep it in mind. | |
243 | */ | |
642a305c | 244 | Comm::ConnectionPointer |
a27fcaed | 245 | IdleConnList::findUseable(const Comm::ConnectionPointer &aKey) |
603a02fd | 246 | { |
e3981652 | 247 | assert(size_); |
62e76326 | 248 | |
e884bbde | 249 | // small optimization: do the constant bool tests only once. |
a27fcaed CT |
250 | const bool keyCheckAddr = !aKey->local.isAnyAddr(); |
251 | const bool keyCheckPort = aKey->local.port() > 0; | |
e884bbde | 252 | |
5e263176 | 253 | for (int i=size_-1; i>=0; --i) { |
139d9221 | 254 | |
a8c28b85 | 255 | if (!isAvailable(i)) |
139d9221 AJ |
256 | continue; |
257 | ||
61beade2 | 258 | // local end port is required, but do not match. |
a27fcaed | 259 | if (keyCheckPort && aKey->local.port() != theList_[i]->local.port()) |
139d9221 AJ |
260 | continue; |
261 | ||
262 | // local address is required, but does not match. | |
a27fcaed | 263 | if (keyCheckAddr && aKey->local.matchIPAddr(theList_[i]->local) != 0) |
139d9221 AJ |
264 | continue; |
265 | ||
a8c28b85 AJ |
266 | // our connection timeout handler is scheduled to run already. unsafe for now. |
267 | // TODO: cancel the pending timeout callback and allow re-use of the conn. | |
268 | if (fd_table[theList_[i]->fd].timeoutHandler == NULL) | |
269 | continue; | |
270 | ||
80463bb4 | 271 | // finally, a match. pop and return it. |
e3981652 | 272 | Comm::ConnectionPointer result = theList_[i]; |
f7605425 | 273 | clearHandlers(result); |
80463bb4 AJ |
274 | /* may delete this */ |
275 | removeAt(i); | |
276 | return result; | |
781ce8ff | 277 | } |
278 | ||
80463bb4 | 279 | return Comm::ConnectionPointer(); |
603a02fd | 280 | } |
281 | ||
a8c28b85 AJ |
282 | /* might delete list */ |
283 | void | |
284 | IdleConnList::findAndClose(const Comm::ConnectionPointer &conn) | |
285 | { | |
286 | const int index = findIndexOf(conn); | |
287 | if (index >= 0) { | |
e8dca475 CT |
288 | if (parent_) |
289 | parent_->notifyManager("idle conn closure"); | |
f7605425 | 290 | clearHandlers(conn); |
a8c28b85 AJ |
291 | /* might delete this */ |
292 | removeAt(index); | |
a8c28b85 AJ |
293 | conn->close(); |
294 | } | |
295 | } | |
296 | ||
781ce8ff | 297 | void |
ced8def3 | 298 | IdleConnList::Read(const Comm::ConnectionPointer &conn, char *, size_t len, Comm::Flag flag, int, void *data) |
603a02fd | 299 | { |
642a305c | 300 | debugs(48, 3, HERE << len << " bytes from " << conn); |
62e76326 | 301 | |
c8407295 AJ |
302 | if (flag == Comm::ERR_CLOSING) { |
303 | debugs(48, 3, HERE << "Comm::ERR_CLOSING from " << conn); | |
304 | /* Bail out on Comm::ERR_CLOSING - may happen when shutdown aborts our idle FD */ | |
62e76326 | 305 | return; |
c4b7a5a9 | 306 | } |
307 | ||
781ce8ff | 308 | IdleConnList *list = (IdleConnList *) data; |
a8c28b85 AJ |
309 | /* may delete list/data */ |
310 | list->findAndClose(conn); | |
603a02fd | 311 | } |
312 | ||
781ce8ff | 313 | void |
8d77a37c | 314 | IdleConnList::Timeout(const CommTimeoutCbParams &io) |
5ba18ba6 | 315 | { |
8d77a37c AJ |
316 | debugs(48, 3, HERE << io.conn); |
317 | IdleConnList *list = static_cast<IdleConnList *>(io.data); | |
a8c28b85 AJ |
318 | /* may delete list/data */ |
319 | list->findAndClose(io.conn); | |
781ce8ff | 320 | } |
62e76326 | 321 | |
a27fcaed CT |
322 | void |
323 | IdleConnList::endingShutdown() | |
324 | { | |
325 | closeN(size_); | |
326 | } | |
327 | ||
781ce8ff | 328 | /* ========== PconnPool PRIVATE FUNCTIONS ============================================ */ |
62e76326 | 329 | |
781ce8ff | 330 | const char * |
642a305c | 331 | PconnPool::key(const Comm::ConnectionPointer &destLink, const char *domain) |
781ce8ff | 332 | { |
06093389 | 333 | LOCAL_ARRAY(char, buf, SQUIDHOSTNAMELEN * 3 + 10); |
781ce8ff | 334 | |
4dd643d5 | 335 | destLink->remote.toUrl(buf, SQUIDHOSTNAMELEN * 3 + 10); |
55622953 CT |
336 | |
337 | // when connecting through a cache_peer, ignore the final destination | |
338 | if (destLink->getPeer()) | |
339 | domain = nullptr; | |
340 | ||
642a305c | 341 | if (domain) { |
80463bb4 | 342 | const int used = strlen(buf); |
642a305c AJ |
343 | snprintf(buf+used, SQUIDHOSTNAMELEN * 3 + 10-used, "/%s", domain); |
344 | } | |
345 | ||
346 | debugs(48,6,"PconnPool::key(" << destLink << ", " << (domain?domain:"[no domain]") << ") is {" << buf << "}" ); | |
781ce8ff | 347 | return buf; |
348 | } | |
62e76326 | 349 | |
781ce8ff | 350 | void |
642a305c | 351 | PconnPool::dumpHist(StoreEntry * e) const |
781ce8ff | 352 | { |
5ba18ba6 | 353 | storeAppendPrintf(e, |
781ce8ff | 354 | "%s persistent connection counts:\n" |
62e76326 | 355 | "\n" |
e87619bc AJ |
356 | "\t Requests\t Connection Count\n" |
357 | "\t --------\t ----------------\n", | |
781ce8ff | 358 | descr); |
62e76326 | 359 | |
5db6bf73 | 360 | for (int i = 0; i < PCONN_HIST_SZ; ++i) { |
781ce8ff | 361 | if (hist[i] == 0) |
62e76326 | 362 | continue; |
363 | ||
e87619bc | 364 | storeAppendPrintf(e, "\t%d\t%d\n", i, hist[i]); |
5ba18ba6 | 365 | } |
366 | } | |
603a02fd | 367 | |
06093389 | 368 | void |
642a305c | 369 | PconnPool::dumpHash(StoreEntry *e) const |
06093389 | 370 | { |
06093389 AJ |
371 | hash_table *hid = table; |
372 | hash_first(hid); | |
373 | ||
642a305c | 374 | int i = 0; |
e8dca475 | 375 | for (hash_link *walker = hash_next(hid); walker; walker = hash_next(hid)) { |
e87619bc | 376 | storeAppendPrintf(e, "\t item %d:\t%s\n", i, (char *)(walker->key)); |
5db6bf73 | 377 | ++i; |
06093389 AJ |
378 | } |
379 | } | |
380 | ||
781ce8ff | 381 | /* ========== PconnPool PUBLIC FUNCTIONS ============================================ */ |
603a02fd | 382 | |
e8dca475 | 383 | PconnPool::PconnPool(const char *aDescr, const CbcPointer<PeerPoolMgr> &aMgr): |
f53969cc SM |
384 | table(NULL), descr(aDescr), |
385 | mgr(aMgr), | |
386 | theCount(0) | |
603a02fd | 387 | { |
ed7f5615 | 388 | int i; |
30abd221 | 389 | table = hash_create((HASHCMP *) strcmp, 229, hash_string); |
62e76326 | 390 | |
5db6bf73 | 391 | for (i = 0; i < PCONN_HIST_SZ; ++i) |
781ce8ff | 392 | hist[i] = 0; |
62e76326 | 393 | |
7a56c509 AJ |
394 | PconnModule::GetInstance()->add(this); |
395 | } | |
396 | ||
e8dca475 CT |
397 | static void |
398 | DeleteIdleConnList(void *hashItem) | |
399 | { | |
af834148 | 400 | delete static_cast<IdleConnList*>(hashItem); |
e8dca475 CT |
401 | } |
402 | ||
7a56c509 AJ |
403 | PconnPool::~PconnPool() |
404 | { | |
e8dca475 CT |
405 | PconnModule::GetInstance()->remove(this); |
406 | hashFreeItems(table, &DeleteIdleConnList); | |
7a56c509 | 407 | hashFreeMemory(table); |
e8dca475 | 408 | descr = NULL; |
603a02fd | 409 | } |
410 | ||
411 | void | |
642a305c | 412 | PconnPool::push(const Comm::ConnectionPointer &conn, const char *domain) |
603a02fd | 413 | { |
9e008dda | 414 | if (fdUsageHigh()) { |
80463bb4 AJ |
415 | debugs(48, 3, HERE << "Not many unused FDs"); |
416 | conn->close(); | |
62e76326 | 417 | return; |
9e008dda | 418 | } else if (shutting_down) { |
80463bb4 AJ |
419 | conn->close(); |
420 | debugs(48, 3, HERE << "Squid is shutting down. Refusing to do anything"); | |
62e76326 | 421 | return; |
47c9d579 | 422 | } |
e8dca475 | 423 | // TODO: also close used pconns if we exceed peer max-conn limit |
62e76326 | 424 | |
642a305c AJ |
425 | const char *aKey = key(conn, domain); |
426 | IdleConnList *list = (IdleConnList *) hash_lookup(table, aKey); | |
62e76326 | 427 | |
9e008dda | 428 | if (list == NULL) { |
781ce8ff | 429 | list = new IdleConnList(aKey, this); |
a27fcaed CT |
430 | debugs(48, 3, "new IdleConnList for {" << hashKeyStr(list) << "}" ); |
431 | hash_join(table, list); | |
06093389 | 432 | } else { |
a27fcaed | 433 | debugs(48, 3, "found IdleConnList for {" << hashKeyStr(list) << "}" ); |
603a02fd | 434 | } |
62e76326 | 435 | |
642a305c AJ |
436 | list->push(conn); |
437 | assert(!comm_has_incomplete_write(conn->fd)); | |
781ce8ff | 438 | |
642a305c | 439 | LOCAL_ARRAY(char, desc, FD_DESC_SZ); |
97b32442 | 440 | snprintf(desc, FD_DESC_SZ, "Idle server: %s", aKey); |
642a305c AJ |
441 | fd_note(conn->fd, desc); |
442 | debugs(48, 3, HERE << "pushed " << conn << " for " << aKey); | |
e8dca475 CT |
443 | |
444 | // successful push notifications resume multi-connection opening sequence | |
445 | notifyManager("push"); | |
603a02fd | 446 | } |
447 | ||
80463bb4 | 448 | Comm::ConnectionPointer |
e8dca475 | 449 | PconnPool::pop(const Comm::ConnectionPointer &dest, const char *domain, bool keepOpen) |
603a02fd | 450 | { |
55622953 CT |
451 | // always call shared pool first because we need to close an idle |
452 | // connection there if we have to use a standby connection. | |
453 | if (const auto direct = popStored(dest, domain, keepOpen)) | |
454 | return direct; | |
455 | ||
456 | // either there was no pconn to pop or this is not a retriable xaction | |
457 | if (const auto peer = dest->getPeer()) { | |
458 | if (peer->standby.pool) | |
459 | return peer->standby.pool->popStored(dest, domain, true); | |
460 | } | |
e8dca475 | 461 | |
55622953 CT |
462 | return nullptr; |
463 | } | |
464 | ||
465 | /// implements pop() API while disregarding peer standby pools | |
466 | /// \returns an open connection or nil | |
467 | Comm::ConnectionPointer | |
468 | PconnPool::popStored(const Comm::ConnectionPointer &dest, const char *domain, const bool keepOpen) | |
469 | { | |
e8dca475 | 470 | const char * aKey = key(dest, domain); |
62e76326 | 471 | |
06093389 AJ |
472 | IdleConnList *list = (IdleConnList *)hash_lookup(table, aKey); |
473 | if (list == NULL) { | |
80463bb4 | 474 | debugs(48, 3, HERE << "lookup for key {" << aKey << "} failed."); |
e8dca475 | 475 | // failure notifications resume standby conn creation after fdUsageHigh |
55622953 | 476 | notifyManager("pop lookup failure"); |
80463bb4 | 477 | return Comm::ConnectionPointer(); |
af6a12ee | 478 | } else { |
a27fcaed | 479 | debugs(48, 3, "found " << hashKeyStr(list) << |
e8dca475 | 480 | (keepOpen ? " to use" : " to kill")); |
06093389 | 481 | } |
781ce8ff | 482 | |
55622953 CT |
483 | if (const auto popped = list->findUseable(dest)) { // may delete list |
484 | // successful pop notifications replenish standby connections pool | |
485 | notifyManager("pop"); | |
486 | ||
487 | if (keepOpen) | |
488 | return popped; | |
489 | ||
e8dca475 | 490 | popped->close(); |
55622953 CT |
491 | return Comm::ConnectionPointer(); |
492 | } | |
e8dca475 | 493 | |
55622953 CT |
494 | // failure notifications resume standby conn creation after fdUsageHigh |
495 | notifyManager("pop usability failure"); | |
496 | return Comm::ConnectionPointer(); | |
e8dca475 | 497 | } |
781ce8ff | 498 | |
e8dca475 CT |
499 | void |
500 | PconnPool::notifyManager(const char *reason) | |
501 | { | |
502 | if (mgr.valid()) | |
503 | PeerPoolMgr::Checkpoint(mgr, reason); | |
603a02fd | 504 | } |
ed7f5615 | 505 | |
506 | void | |
e8dca475 | 507 | PconnPool::closeN(int n) |
2dba5b8e | 508 | { |
e8dca475 CT |
509 | hash_table *hid = table; |
510 | hash_first(hid); | |
511 | ||
512 | // close N connections, one per list, to treat all lists "fairly" | |
513 | for (int i = 0; i < n && count(); ++i) { | |
514 | ||
515 | hash_link *current = hash_next(hid); | |
516 | if (!current) { | |
517 | hash_first(hid); | |
518 | current = hash_next(hid); | |
519 | Must(current); // must have one because the count() was positive | |
520 | } | |
521 | ||
522 | // may delete current | |
af834148 | 523 | static_cast<IdleConnList*>(current)->closeN(1); |
e8dca475 | 524 | } |
2dba5b8e CT |
525 | } |
526 | ||
527 | void | |
528 | PconnPool::unlinkList(IdleConnList *list) | |
ed7f5615 | 529 | { |
2dba5b8e CT |
530 | theCount -= list->count(); |
531 | assert(theCount >= 0); | |
a27fcaed | 532 | hash_remove_link(table, list); |
781ce8ff | 533 | } |
534 | ||
535 | void | |
2dba5b8e | 536 | PconnPool::noteUses(int uses) |
781ce8ff | 537 | { |
538 | if (uses >= PCONN_HIST_SZ) | |
539 | uses = PCONN_HIST_SZ - 1; | |
540 | ||
5db6bf73 | 541 | ++hist[uses]; |
781ce8ff | 542 | } |
543 | ||
544 | /* ========== PconnModule ============================================ */ | |
545 | ||
546 | /* | |
547 | * This simple class exists only for the cache manager | |
548 | */ | |
549 | ||
e8dca475 | 550 | PconnModule::PconnModule(): pools() |
781ce8ff | 551 | { |
6852be71 | 552 | registerWithCacheManager(); |
781ce8ff | 553 | } |
554 | ||
62ee09ca | 555 | PconnModule * |
556 | PconnModule::GetInstance() | |
557 | { | |
558 | if (instance == NULL) | |
559 | instance = new PconnModule; | |
560 | ||
561 | return instance; | |
562 | } | |
563 | ||
564 | void | |
15fab853 | 565 | PconnModule::registerWithCacheManager(void) |
62ee09ca | 566 | { |
8822ebee | 567 | Mgr::RegisterAction("pconn", |
d9fc6862 A |
568 | "Persistent Connection Utilization Histograms", |
569 | DumpWrapper, 0, 1); | |
62ee09ca | 570 | } |
571 | ||
781ce8ff | 572 | void |
642a305c | 573 | PconnModule::add(PconnPool *aPool) |
781ce8ff | 574 | { |
e8dca475 | 575 | pools.insert(aPool); |
781ce8ff | 576 | } |
577 | ||
578 | void | |
e8dca475 | 579 | PconnModule::remove(PconnPool *aPool) |
781ce8ff | 580 | { |
e8dca475 CT |
581 | pools.erase(aPool); |
582 | } | |
781ce8ff | 583 | |
e8dca475 CT |
584 | void |
585 | PconnModule::dump(StoreEntry *e) | |
586 | { | |
587 | typedef Pools::const_iterator PCI; | |
588 | int i = 0; // TODO: Why number pools if they all have names? | |
589 | for (PCI p = pools.begin(); p != pools.end(); ++p, ++i) { | |
590 | // TODO: Let each pool dump itself the way it wants to. | |
06093389 | 591 | storeAppendPrintf(e, "\n Pool %d Stats\n", i); |
e8dca475 | 592 | (*p)->dumpHist(e); |
06093389 | 593 | storeAppendPrintf(e, "\n Pool %d Hash Table\n",i); |
e8dca475 | 594 | (*p)->dumpHash(e); |
781ce8ff | 595 | } |
596 | } | |
597 | ||
62ee09ca | 598 | void |
599 | PconnModule::DumpWrapper(StoreEntry *e) | |
781ce8ff | 600 | { |
62ee09ca | 601 | PconnModule::GetInstance()->dump(e); |
ed7f5615 | 602 | } |
f53969cc | 603 |