]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/pconn.cc
2 * Copyright (C) 1996-2018 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 *aKey
, PconnPool
*thePool
) :
35 capacity_(PCONN_FDS_SZ
),
39 //Initialize hash_link members
43 theList_
= new Comm::ConnectionPointer
[capacity_
];
47 // TODO: re-attach to MemPools. WAS: theList = (?? *)pconn_fds_pool->alloc();
50 IdleConnList::~IdleConnList()
53 parent_
->unlinkList(this);
56 parent_
= NULL
; // prevent reentrant notifications and deletions
65 /** Search the list. Matches by FD socket number.
66 * Performed from the end of list where newest entries are.
68 * \retval <0 The connection is not listed
69 * \retval >=0 The connection array index
72 IdleConnList::findIndexOf(const Comm::ConnectionPointer
&conn
) const
74 for (int index
= size_
- 1; index
>= 0; --index
) {
75 if (conn
->fd
== theList_
[index
]->fd
) {
76 debugs(48, 3, HERE
<< "found " << conn
<< " at index " << index
);
81 debugs(48, 2, HERE
<< conn
<< " NOT FOUND!");
85 /** Remove the entry at specified index.
86 * May perform a shuffle of list entries to fill the gap.
87 * \retval false The index is not an in-use entry.
90 IdleConnList::removeAt(int index
)
92 if (index
< 0 || index
>= size_
)
95 // shuffle the remaining entries to fill the new gap.
96 for (; index
< size_
- 1; ++index
)
97 theList_
[index
] = theList_
[index
+ 1];
98 theList_
[--size_
] = NULL
;
101 parent_
->noteConnectionRemoved();
103 debugs(48, 3, "deleting " << hashKeyStr(this));
111 // almost a duplicate of removeFD. But drops multiple entries.
113 IdleConnList::closeN(size_t n
)
116 debugs(48, 2, HERE
<< "Nothing to do.");
118 } else if (n
>= (size_t)size_
) {
119 debugs(48, 2, HERE
<< "Closing all entries.");
121 const Comm::ConnectionPointer conn
= theList_
[--size_
];
122 theList_
[size_
] = NULL
;
126 parent_
->noteConnectionRemoved();
128 } else { //if (n < size_)
129 debugs(48, 2, HERE
<< "Closing " << n
<< " of " << size_
<< " entries.");
132 // ensure the first N entries are closed
133 for (index
= 0; index
< n
; ++index
) {
134 const Comm::ConnectionPointer conn
= theList_
[index
];
135 theList_
[index
] = NULL
;
139 parent_
->noteConnectionRemoved();
141 // shuffle the list N down.
142 for (index
= 0; index
< (size_t)size_
- n
; ++index
) {
143 theList_
[index
] = theList_
[index
+ n
];
145 // ensure the last N entries are unset
146 while (index
< ((size_t)size_
)) {
147 theList_
[index
] = NULL
;
153 if (parent_
&& size_
== 0) {
154 debugs(48, 3, "deleting " << hashKeyStr(this));
160 IdleConnList::clearHandlers(const Comm::ConnectionPointer
&conn
)
162 debugs(48, 3, HERE
<< "removing close handler for " << conn
);
163 comm_read_cancel(conn
->fd
, IdleConnList::Read
, this);
164 commUnsetConnTimeout(conn
);
168 IdleConnList::push(const Comm::ConnectionPointer
&conn
)
170 if (size_
== capacity_
) {
171 debugs(48, 3, HERE
<< "growing idle Connection array");
173 const Comm::ConnectionPointer
*oldList
= theList_
;
174 theList_
= new Comm::ConnectionPointer
[capacity_
];
175 for (int index
= 0; index
< size_
; ++index
)
176 theList_
[index
] = oldList
[index
];
182 parent_
->noteConnectionAdded();
184 theList_
[size_
] = conn
;
186 AsyncCall::Pointer readCall
= commCbCall(5,4, "IdleConnList::Read",
187 CommIoCbPtrFun(IdleConnList::Read
, this));
188 comm_read(conn
, fakeReadBuf_
, sizeof(fakeReadBuf_
), readCall
);
189 AsyncCall::Pointer timeoutCall
= commCbCall(5,4, "IdleConnList::Timeout",
190 CommTimeoutCbPtrFun(IdleConnList::Timeout
, this));
191 commSetConnTimeout(conn
, conn
->timeLeft(Config
.Timeout
.serverIdlePconn
), timeoutCall
);
194 /// Determine whether an entry in the idle list is available for use.
195 /// Returns false if the entry is unset, closed or closing.
197 IdleConnList::isAvailable(int i
) const
199 const Comm::ConnectionPointer
&conn
= theList_
[i
];
201 // connection already closed. useless.
202 if (!Comm::IsConnOpen(conn
))
205 // our connection early-read/close handler is scheduled to run already. unsafe
206 if (!COMMIO_FD_READCB(conn
->fd
)->active())
212 Comm::ConnectionPointer
215 for (int i
=size_
-1; i
>=0; --i
) {
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
)
225 // finally, a match. pop and return it.
226 Comm::ConnectionPointer result
= theList_
[i
];
227 clearHandlers(result
);
228 /* may delete this */
233 return Comm::ConnectionPointer();
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.
244 Comm::ConnectionPointer
245 IdleConnList::findUseable(const Comm::ConnectionPointer
&aKey
)
249 // small optimization: do the constant bool tests only once.
250 const bool keyCheckAddr
= !aKey
->local
.isAnyAddr();
251 const bool keyCheckPort
= aKey
->local
.port() > 0;
253 for (int i
=size_
-1; i
>=0; --i
) {
258 // local end port is required, but dont match.
259 if (keyCheckPort
&& aKey
->local
.port() != theList_
[i
]->local
.port())
262 // local address is required, but does not match.
263 if (keyCheckAddr
&& aKey
->local
.matchIPAddr(theList_
[i
]->local
) != 0)
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
)
271 // finally, a match. pop and return it.
272 Comm::ConnectionPointer result
= theList_
[i
];
273 clearHandlers(result
);
274 /* may delete this */
279 return Comm::ConnectionPointer();
282 /* might delete list */
284 IdleConnList::findAndClose(const Comm::ConnectionPointer
&conn
)
286 const int index
= findIndexOf(conn
);
289 parent_
->notifyManager("idle conn closure");
291 /* might delete this */
298 IdleConnList::Read(const Comm::ConnectionPointer
&conn
, char *, size_t len
, Comm::Flag flag
, int, void *data
)
300 debugs(48, 3, HERE
<< len
<< " bytes from " << conn
);
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 */
308 IdleConnList
*list
= (IdleConnList
*) data
;
309 /* may delete list/data */
310 list
->findAndClose(conn
);
314 IdleConnList::Timeout(const CommTimeoutCbParams
&io
)
316 debugs(48, 3, HERE
<< io
.conn
);
317 IdleConnList
*list
= static_cast<IdleConnList
*>(io
.data
);
318 /* may delete list/data */
319 list
->findAndClose(io
.conn
);
323 IdleConnList::endingShutdown()
328 /* ========== PconnPool PRIVATE FUNCTIONS ============================================ */
331 PconnPool::key(const Comm::ConnectionPointer
&destLink
, const char *domain
)
333 LOCAL_ARRAY(char, buf
, SQUIDHOSTNAMELEN
* 3 + 10);
335 destLink
->remote
.toUrl(buf
, SQUIDHOSTNAMELEN
* 3 + 10);
337 const int used
= strlen(buf
);
338 snprintf(buf
+used
, SQUIDHOSTNAMELEN
* 3 + 10-used
, "/%s", domain
);
341 debugs(48,6,"PconnPool::key(" << destLink
<< ", " << (domain
?domain
:"[no domain]") << ") is {" << buf
<< "}" );
346 PconnPool::dumpHist(StoreEntry
* e
) const
349 "%s persistent connection counts:\n"
351 "\t Requests\t Connection Count\n"
352 "\t --------\t ----------------\n",
355 for (int i
= 0; i
< PCONN_HIST_SZ
; ++i
) {
359 storeAppendPrintf(e
, "\t%d\t%d\n", i
, hist
[i
]);
364 PconnPool::dumpHash(StoreEntry
*e
) const
366 hash_table
*hid
= table
;
370 for (hash_link
*walker
= hash_next(hid
); walker
; walker
= hash_next(hid
)) {
371 storeAppendPrintf(e
, "\t item %d:\t%s\n", i
, (char *)(walker
->key
));
376 /* ========== PconnPool PUBLIC FUNCTIONS ============================================ */
378 PconnPool::PconnPool(const char *aDescr
, const CbcPointer
<PeerPoolMgr
> &aMgr
):
379 table(NULL
), descr(aDescr
),
384 table
= hash_create((HASHCMP
*) strcmp
, 229, hash_string
);
386 for (i
= 0; i
< PCONN_HIST_SZ
; ++i
)
389 PconnModule::GetInstance()->add(this);
393 DeleteIdleConnList(void *hashItem
)
395 delete static_cast<IdleConnList
*>(hashItem
);
398 PconnPool::~PconnPool()
400 PconnModule::GetInstance()->remove(this);
401 hashFreeItems(table
, &DeleteIdleConnList
);
402 hashFreeMemory(table
);
407 PconnPool::push(const Comm::ConnectionPointer
&conn
, const char *domain
)
410 debugs(48, 3, HERE
<< "Not many unused FDs");
413 } else if (shutting_down
) {
415 debugs(48, 3, HERE
<< "Squid is shutting down. Refusing to do anything");
418 // TODO: also close used pconns if we exceed peer max-conn limit
420 const char *aKey
= key(conn
, domain
);
421 IdleConnList
*list
= (IdleConnList
*) hash_lookup(table
, aKey
);
424 list
= new IdleConnList(aKey
, this);
425 debugs(48, 3, "new IdleConnList for {" << hashKeyStr(list
) << "}" );
426 hash_join(table
, list
);
428 debugs(48, 3, "found IdleConnList for {" << hashKeyStr(list
) << "}" );
432 assert(!comm_has_incomplete_write(conn
->fd
));
434 LOCAL_ARRAY(char, desc
, FD_DESC_SZ
);
435 snprintf(desc
, FD_DESC_SZ
, "Idle server: %s", aKey
);
436 fd_note(conn
->fd
, desc
);
437 debugs(48, 3, HERE
<< "pushed " << conn
<< " for " << aKey
);
439 // successful push notifications resume multi-connection opening sequence
440 notifyManager("push");
443 Comm::ConnectionPointer
444 PconnPool::pop(const Comm::ConnectionPointer
&dest
, const char *domain
, bool keepOpen
)
447 const char * aKey
= key(dest
, domain
);
449 IdleConnList
*list
= (IdleConnList
*)hash_lookup(table
, aKey
);
451 debugs(48, 3, HERE
<< "lookup for key {" << aKey
<< "} failed.");
452 // failure notifications resume standby conn creation after fdUsageHigh
453 notifyManager("pop failure");
454 return Comm::ConnectionPointer();
456 debugs(48, 3, "found " << hashKeyStr(list
) <<
457 (keepOpen
? " to use" : " to kill"));
460 /* may delete list */
461 Comm::ConnectionPointer popped
= list
->findUseable(dest
);
462 if (!keepOpen
&& Comm::IsConnOpen(popped
))
465 // successful pop notifications replenish standby connections pool
466 notifyManager("pop");
471 PconnPool::notifyManager(const char *reason
)
474 PeerPoolMgr::Checkpoint(mgr
, reason
);
478 PconnPool::closeN(int n
)
480 hash_table
*hid
= table
;
483 // close N connections, one per list, to treat all lists "fairly"
484 for (int i
= 0; i
< n
&& count(); ++i
) {
486 hash_link
*current
= hash_next(hid
);
489 current
= hash_next(hid
);
490 Must(current
); // must have one because the count() was positive
493 // may delete current
494 static_cast<IdleConnList
*>(current
)->closeN(1);
499 PconnPool::unlinkList(IdleConnList
*list
)
501 theCount
-= list
->count();
502 assert(theCount
>= 0);
503 hash_remove_link(table
, list
);
507 PconnPool::noteUses(int uses
)
509 if (uses
>= PCONN_HIST_SZ
)
510 uses
= PCONN_HIST_SZ
- 1;
515 /* ========== PconnModule ============================================ */
518 * This simple class exists only for the cache manager
521 PconnModule::PconnModule(): pools()
523 registerWithCacheManager();
527 PconnModule::GetInstance()
529 if (instance
== NULL
)
530 instance
= new PconnModule
;
536 PconnModule::registerWithCacheManager(void)
538 Mgr::RegisterAction("pconn",
539 "Persistent Connection Utilization Histograms",
544 PconnModule::add(PconnPool
*aPool
)
550 PconnModule::remove(PconnPool
*aPool
)
556 PconnModule::dump(StoreEntry
*e
)
558 typedef Pools::const_iterator PCI
;
559 int i
= 0; // TODO: Why number pools if they all have names?
560 for (PCI p
= pools
.begin(); p
!= pools
.end(); ++p
, ++i
) {
561 // TODO: Let each pool dump itself the way it wants to.
562 storeAppendPrintf(e
, "\n Pool %d Stats\n", i
);
564 storeAppendPrintf(e
, "\n Pool %d Hash Table\n",i
);
570 PconnModule::DumpWrapper(StoreEntry
*e
)
572 PconnModule::GetInstance()->dump(e
);