theBusyConns(0),
theAllWaiters(0),
connOverloadReported(false),
- theIdleConns("ICAP Service"),
+ theIdleConns("ICAP Service",NULL),
isSuspended(0), notifying(false),
updateScheduled(false),
wasAnnouncedUp(true), // do not announce an "up" service at startup
{
Ip::Address client_addr;
- int connection = theIdleConns.pop(cfg().host.termedBuf(), cfg().port, NULL, client_addr,
- retriableXact);
+ int connection = -1;
+
+ /* 2011-06-17: rousskov:
+ * There are two things that happen at the same time in pop(). Both are important.
+ * 1) Ensure that we can use a pconn for this transaction.
+ * 2) Ensure that the number of idle pconns does not grow without bounds.
+ *
+ * Both happen in the beginning of the transaction. Both are dictated by real-world problems.
+ * retriable means you can repeat the request if you suspect the first try failed due to a pconn race.
+ * HTTP and ICAP rules prohibit the use of pconns for non-retriable requests.
+ *
+ * If there are zero idle connections, (2) is irrelevant. (2) is only relevant when there are many
+ * idle connections and we should not open more connections without closing some idle ones,
+ * or instead of just opening a new connection and leaving idle connections as is.
+ * In other words, (2) tells us to close one FD for each new one we open due to retriable.
+ */
+ if (retriableXact)
+ connection = theIdleConns.findUseableFD();
+ else
+ theIdleConns.closeN(1);
reused = connection >= 0; // reused a persistent connection
debugs(93, 3, HERE << "pushing pconn" << comment);
commSetTimeout(fd, -1, NULL, NULL);
Ip::Address anyAddr;
- theIdleConns.push(fd, cfg().host.termedBuf(), cfg().port, NULL, anyAddr);
+ theIdleConns.push(fd);
} else {
debugs(93, 3, HERE << "closing pconn" << comment);
// comm_close will clear timeout
void Adaptation::Icap::ServiceRep::noteConnectionUse(int fd)
{
Must(fd >= 0);
- fd_table[fd].noteUse(&theIdleConns);
+ fd_table[fd].noteUse(NULL); // pconn re-use but not via PconnPool API
}
void Adaptation::Icap::ServiceRep::setMaxConnections()
if (excess && theIdleConns.count() > 0) {
const int n = min(excess, theIdleConns.count());
debugs(93,5, HERE << "closing " << n << " pconns to relief debt");
- Ip::Address anyAddr;
- theIdleConns.closeN(n, cfg().host.termedBuf(), cfg().port, NULL, anyAddr);
+ theIdleConns.closeN(n);
}
scheduleNotification();
IdleConnList::~IdleConnList()
{
-
- parent->unlinkList(this);
+ if (parent)
+ parent->unlinkList(this);
if (nfds_alloc == PCONN_FDS_SZ)
pconn_fds_pool->freeOne(fds);
if (parent)
parent->noteConnectionRemoved();
- if (--nfds == 0) {
+ if (parent && --nfds == 0) {
+ debugs(48, 3, "IdleConnList::removeFD: deleting " << hashKeyStr(&hash));
+ delete this;
+ }
+}
+
+// almost a duplicate of removeFD. But drops multiple entries.
+void
+IdleConnList::closeN(size_t n)
+{
+ if (n < 1) {
+ debugs(48, 2, HERE << "Nothing to do.");
+ return;
+ } else if (n < (size_t)count()) {
+ debugs(48, 2, HERE << "Closing all entries.");
+ while (nfds >= 0) {
+ int fd = fds[--nfds];
+ fds[nfds] = -1;
+ clearHandlers(fd);
+ comm_close(fd);
+ if (parent)
+ parent->noteConnectionRemoved();
+ }
+ } else {
+ debugs(48, 2, HERE << "Closing " << n << " of " << nfds << " entries.");
+
+ size_t index = 0;
+ // ensure the first N entries are closed
+ while (index < n) {
+ int fd = fds[--nfds];
+ fds[nfds] = -1;
+ clearHandlers(fd);
+ comm_close(fd);
+ if (parent)
+ parent->noteConnectionRemoved();
+ }
+ // shuffle the list N down.
+ for (;index < (size_t)nfds; index++) {
+ fds[index - n] = fds[index];
+ }
+ // ensure the last N entries are unset
+ while (index < ((size_t)nfds) + n) {
+ fds[index] = -1;
+ }
+ }
+
+ if (parent && nfds == 0) {
debugs(48, 3, "IdleConnList::removeFD: deleting " << hashKeyStr(&hash));
delete this;
}