]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/pconn.cc
2 * Copyright (C) 1996-2014 The Squid Software Foundation and contributors
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.
9 /* DEBUG: section 48 Persistent Connections */
12 #include "CachePeer.h"
14 #include "comm/Connection.h"
15 #include "comm/Read.h"
19 #include "mgr/Registration.h"
20 #include "neighbors.h"
22 #include "PeerPoolMgr.h"
23 #include "SquidConfig.h"
26 #define PCONN_FDS_SZ 8 /* pconn set size, increase for better memcache hit rate */
28 //TODO: re-attach to MemPools. WAS: static MemAllocator *pconn_fds_pool = NULL;
29 PconnModule
* PconnModule::instance
= NULL
;
30 CBDATA_CLASS_INIT(IdleConnList
);
32 /* ========== IdleConnList ============================================ */
34 IdleConnList::IdleConnList(const char *key
, PconnPool
*thePool
) :
35 capacity_(PCONN_FDS_SZ
),
39 hash
.key
= xstrdup(key
);
40 theList_
= new Comm::ConnectionPointer
[capacity_
];
41 // TODO: re-attach to MemPools. WAS: theList = (?? *)pconn_fds_pool->alloc();
44 IdleConnList::~IdleConnList()
47 parent_
->unlinkList(this);
50 parent_
= NULL
; // prevent reentrant notifications and deletions
59 /** Search the list. Matches by FD socket number.
60 * Performed from the end of list where newest entries are.
62 * \retval <0 The connection is not listed
63 * \retval >=0 The connection array index
66 IdleConnList::findIndexOf(const Comm::ConnectionPointer
&conn
) const
68 for (int index
= size_
- 1; index
>= 0; --index
) {
69 if (conn
->fd
== theList_
[index
]->fd
) {
70 debugs(48, 3, HERE
<< "found " << conn
<< " at index " << index
);
75 debugs(48, 2, HERE
<< conn
<< " NOT FOUND!");
79 /** Remove the entry at specified index.
80 * May perform a shuffle of list entries to fill the gap.
81 * \retval false The index is not an in-use entry.
84 IdleConnList::removeAt(int index
)
86 if (index
< 0 || index
>= size_
)
89 // shuffle the remaining entries to fill the new gap.
90 for (; index
< size_
- 1; ++index
)
91 theList_
[index
] = theList_
[index
+ 1];
92 theList_
[--size_
] = NULL
;
95 parent_
->noteConnectionRemoved();
97 debugs(48, 3, HERE
<< "deleting " << hashKeyStr(&hash
));
105 // almost a duplicate of removeFD. But drops multiple entries.
107 IdleConnList::closeN(size_t n
)
110 debugs(48, 2, HERE
<< "Nothing to do.");
112 } else if (n
>= (size_t)size_
) {
113 debugs(48, 2, HERE
<< "Closing all entries.");
115 const Comm::ConnectionPointer conn
= theList_
[--size_
];
116 theList_
[size_
] = NULL
;
120 parent_
->noteConnectionRemoved();
122 } else { //if (n < size_)
123 debugs(48, 2, HERE
<< "Closing " << n
<< " of " << size_
<< " entries.");
126 // ensure the first N entries are closed
127 for (index
= 0; index
< n
; ++index
) {
128 const Comm::ConnectionPointer conn
= theList_
[index
];
129 theList_
[index
] = NULL
;
133 parent_
->noteConnectionRemoved();
135 // shuffle the list N down.
136 for (index
= 0; index
< (size_t)size_
- n
; ++index
) {
137 theList_
[index
] = theList_
[index
+ n
];
139 // ensure the last N entries are unset
140 while (index
< ((size_t)size_
)) {
141 theList_
[index
] = NULL
;
147 if (parent_
&& size_
== 0) {
148 debugs(48, 3, HERE
<< "deleting " << hashKeyStr(&hash
));
154 IdleConnList::clearHandlers(const Comm::ConnectionPointer
&conn
)
156 debugs(48, 3, HERE
<< "removing close handler for " << conn
);
157 comm_read_cancel(conn
->fd
, IdleConnList::Read
, this);
158 commUnsetConnTimeout(conn
);
162 IdleConnList::push(const Comm::ConnectionPointer
&conn
)
164 if (size_
== capacity_
) {
165 debugs(48, 3, HERE
<< "growing idle Connection array");
167 const Comm::ConnectionPointer
*oldList
= theList_
;
168 theList_
= new Comm::ConnectionPointer
[capacity_
];
169 for (int index
= 0; index
< size_
; ++index
)
170 theList_
[index
] = oldList
[index
];
176 parent_
->noteConnectionAdded();
178 theList_
[size_
] = conn
;
180 AsyncCall::Pointer readCall
= commCbCall(5,4, "IdleConnList::Read",
181 CommIoCbPtrFun(IdleConnList::Read
, this));
182 comm_read(conn
, fakeReadBuf_
, sizeof(fakeReadBuf_
), readCall
);
183 AsyncCall::Pointer timeoutCall
= commCbCall(5,4, "IdleConnList::Timeout",
184 CommTimeoutCbPtrFun(IdleConnList::Timeout
, this));
185 commSetConnTimeout(conn
, conn
->timeLeft(Config
.Timeout
.serverIdlePconn
), timeoutCall
);
188 /// Determine whether an entry in the idle list is available for use.
189 /// Returns false if the entry is unset, closed or closing.
191 IdleConnList::isAvailable(int i
) const
193 const Comm::ConnectionPointer
&conn
= theList_
[i
];
195 // connection already closed. useless.
196 if (!Comm::IsConnOpen(conn
))
199 // our connection early-read/close handler is scheduled to run already. unsafe
200 if (!COMMIO_FD_READCB(conn
->fd
)->active())
206 Comm::ConnectionPointer
209 for (int i
=size_
-1; i
>=0; --i
) {
214 // our connection timeout handler is scheduled to run already. unsafe for now.
215 // TODO: cancel the pending timeout callback and allow re-use of the conn.
216 if (fd_table
[theList_
[i
]->fd
].timeoutHandler
== NULL
)
219 // finally, a match. pop and return it.
220 Comm::ConnectionPointer result
= theList_
[i
];
221 /* may delete this */
223 clearHandlers(result
);
227 return Comm::ConnectionPointer();
231 * XXX this routine isn't terribly efficient - if there's a pending
232 * read event (which signifies the fd will close in the next IO loop!)
233 * we ignore the FD and move onto the next one. This means, as an example,
234 * if we have a lot of FDs open to a very popular server and we get a bunch
235 * of requests JUST as they timeout (say, it shuts down) we'll be wasting
236 * quite a bit of CPU. Just keep it in mind.
238 Comm::ConnectionPointer
239 IdleConnList::findUseable(const Comm::ConnectionPointer
&key
)
243 // small optimization: do the constant bool tests only once.
244 const bool keyCheckAddr
= !key
->local
.isAnyAddr();
245 const bool keyCheckPort
= key
->local
.port() > 0;
247 for (int i
=size_
-1; i
>=0; --i
) {
252 // local end port is required, but dont match.
253 if (keyCheckPort
&& key
->local
.port() != theList_
[i
]->local
.port())
256 // local address is required, but does not match.
257 if (keyCheckAddr
&& key
->local
.matchIPAddr(theList_
[i
]->local
) != 0)
260 // our connection timeout handler is scheduled to run already. unsafe for now.
261 // TODO: cancel the pending timeout callback and allow re-use of the conn.
262 if (fd_table
[theList_
[i
]->fd
].timeoutHandler
== NULL
)
265 // finally, a match. pop and return it.
266 Comm::ConnectionPointer result
= theList_
[i
];
267 /* may delete this */
269 clearHandlers(result
);
273 return Comm::ConnectionPointer();
276 /* might delete list */
278 IdleConnList::findAndClose(const Comm::ConnectionPointer
&conn
)
280 const int index
= findIndexOf(conn
);
283 parent_
->notifyManager("idle conn closure");
284 /* might delete this */
292 IdleConnList::Read(const Comm::ConnectionPointer
&conn
, char *buf
, size_t len
, Comm::Flag flag
, int xerrno
, void *data
)
294 debugs(48, 3, HERE
<< len
<< " bytes from " << conn
);
296 if (flag
== Comm::ERR_CLOSING
) {
297 debugs(48, 3, HERE
<< "Comm::ERR_CLOSING from " << conn
);
298 /* Bail out on Comm::ERR_CLOSING - may happen when shutdown aborts our idle FD */
302 IdleConnList
*list
= (IdleConnList
*) data
;
303 /* may delete list/data */
304 list
->findAndClose(conn
);
308 IdleConnList::Timeout(const CommTimeoutCbParams
&io
)
310 debugs(48, 3, HERE
<< io
.conn
);
311 IdleConnList
*list
= static_cast<IdleConnList
*>(io
.data
);
312 /* may delete list/data */
313 list
->findAndClose(io
.conn
);
316 /* ========== PconnPool PRIVATE FUNCTIONS ============================================ */
319 PconnPool::key(const Comm::ConnectionPointer
&destLink
, const char *domain
)
321 LOCAL_ARRAY(char, buf
, SQUIDHOSTNAMELEN
* 3 + 10);
323 destLink
->remote
.toUrl(buf
, SQUIDHOSTNAMELEN
* 3 + 10);
325 const int used
= strlen(buf
);
326 snprintf(buf
+used
, SQUIDHOSTNAMELEN
* 3 + 10-used
, "/%s", domain
);
329 debugs(48,6,"PconnPool::key(" << destLink
<< ", " << (domain
?domain
:"[no domain]") << ") is {" << buf
<< "}" );
334 PconnPool::dumpHist(StoreEntry
* e
) const
337 "%s persistent connection counts:\n"
339 "\t Requests\t Connection Count\n"
340 "\t --------\t ----------------\n",
343 for (int i
= 0; i
< PCONN_HIST_SZ
; ++i
) {
347 storeAppendPrintf(e
, "\t%d\t%d\n", i
, hist
[i
]);
352 PconnPool::dumpHash(StoreEntry
*e
) const
354 hash_table
*hid
= table
;
358 for (hash_link
*walker
= hash_next(hid
); walker
; walker
= hash_next(hid
)) {
359 storeAppendPrintf(e
, "\t item %d:\t%s\n", i
, (char *)(walker
->key
));
364 /* ========== PconnPool PUBLIC FUNCTIONS ============================================ */
366 PconnPool::PconnPool(const char *aDescr
, const CbcPointer
<PeerPoolMgr
> &aMgr
):
367 table(NULL
), descr(aDescr
),
372 table
= hash_create((HASHCMP
*) strcmp
, 229, hash_string
);
374 for (i
= 0; i
< PCONN_HIST_SZ
; ++i
)
377 PconnModule::GetInstance()->add(this);
381 DeleteIdleConnList(void *hashItem
)
383 delete reinterpret_cast<IdleConnList
*>(hashItem
);
386 PconnPool::~PconnPool()
388 PconnModule::GetInstance()->remove(this);
389 hashFreeItems(table
, &DeleteIdleConnList
);
390 hashFreeMemory(table
);
395 PconnPool::push(const Comm::ConnectionPointer
&conn
, const char *domain
)
398 debugs(48, 3, HERE
<< "Not many unused FDs");
401 } else if (shutting_down
) {
403 debugs(48, 3, HERE
<< "Squid is shutting down. Refusing to do anything");
406 // TODO: also close used pconns if we exceed peer max-conn limit
408 const char *aKey
= key(conn
, domain
);
409 IdleConnList
*list
= (IdleConnList
*) hash_lookup(table
, aKey
);
412 list
= new IdleConnList(aKey
, this);
413 debugs(48, 3, HERE
<< "new IdleConnList for {" << hashKeyStr(&list
->hash
) << "}" );
414 hash_join(table
, &list
->hash
);
416 debugs(48, 3, HERE
<< "found IdleConnList for {" << hashKeyStr(&list
->hash
) << "}" );
420 assert(!comm_has_incomplete_write(conn
->fd
));
422 LOCAL_ARRAY(char, desc
, FD_DESC_SZ
);
423 snprintf(desc
, FD_DESC_SZ
, "Idle server: %s", aKey
);
424 fd_note(conn
->fd
, desc
);
425 debugs(48, 3, HERE
<< "pushed " << conn
<< " for " << aKey
);
427 // successful push notifications resume multi-connection opening sequence
428 notifyManager("push");
431 Comm::ConnectionPointer
432 PconnPool::pop(const Comm::ConnectionPointer
&dest
, const char *domain
, bool keepOpen
)
435 const char * aKey
= key(dest
, domain
);
437 IdleConnList
*list
= (IdleConnList
*)hash_lookup(table
, aKey
);
439 debugs(48, 3, HERE
<< "lookup for key {" << aKey
<< "} failed.");
440 // failure notifications resume standby conn creation after fdUsageHigh
441 notifyManager("pop failure");
442 return Comm::ConnectionPointer();
444 debugs(48, 3, HERE
<< "found " << hashKeyStr(&list
->hash
) <<
445 (keepOpen
? " to use" : " to kill"));
448 /* may delete list */
449 Comm::ConnectionPointer popped
= list
->findUseable(dest
);
450 if (!keepOpen
&& Comm::IsConnOpen(popped
))
453 // successful pop notifications replenish standby connections pool
454 notifyManager("pop");
459 PconnPool::notifyManager(const char *reason
)
462 PeerPoolMgr::Checkpoint(mgr
, reason
);
466 PconnPool::closeN(int n
)
468 hash_table
*hid
= table
;
471 // close N connections, one per list, to treat all lists "fairly"
472 for (int i
= 0; i
< n
&& count(); ++i
) {
474 hash_link
*current
= hash_next(hid
);
477 current
= hash_next(hid
);
478 Must(current
); // must have one because the count() was positive
481 // may delete current
482 reinterpret_cast<IdleConnList
*>(current
)->closeN(1);
487 PconnPool::unlinkList(IdleConnList
*list
)
489 theCount
-= list
->count();
490 assert(theCount
>= 0);
491 hash_remove_link(table
, &list
->hash
);
495 PconnPool::noteUses(int uses
)
497 if (uses
>= PCONN_HIST_SZ
)
498 uses
= PCONN_HIST_SZ
- 1;
503 /* ========== PconnModule ============================================ */
506 * This simple class exists only for the cache manager
509 PconnModule::PconnModule(): pools()
511 registerWithCacheManager();
515 PconnModule::GetInstance()
517 if (instance
== NULL
)
518 instance
= new PconnModule
;
524 PconnModule::registerWithCacheManager(void)
526 Mgr::RegisterAction("pconn",
527 "Persistent Connection Utilization Histograms",
532 PconnModule::add(PconnPool
*aPool
)
538 PconnModule::remove(PconnPool
*aPool
)
544 PconnModule::dump(StoreEntry
*e
)
546 typedef Pools::const_iterator PCI
;
547 int i
= 0; // TODO: Why number pools if they all have names?
548 for (PCI p
= pools
.begin(); p
!= pools
.end(); ++p
, ++i
) {
549 // TODO: Let each pool dump itself the way it wants to.
550 storeAppendPrintf(e
, "\n Pool %d Stats\n", i
);
552 storeAppendPrintf(e
, "\n Pool %d Hash Table\n",i
);
558 PconnModule::DumpWrapper(StoreEntry
*e
)
560 PconnModule::GetInstance()->dump(e
);