]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/pconn.cc
2 * DEBUG: section 48 Persistent Connections
3 * AUTHOR: Duane Wessels
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
35 #include "comm/Connection.h"
39 #include "mgr/Registration.h"
41 #include "SquidConfig.h"
44 #define PCONN_FDS_SZ 8 /* pconn set size, increase for better memcache hit rate */
46 //TODO: re-attach to MemPools. WAS: static MemAllocator *pconn_fds_pool = NULL;
47 PconnModule
* PconnModule::instance
= NULL
;
48 CBDATA_CLASS_INIT(IdleConnList
);
50 /* ========== IdleConnList ============================================ */
52 IdleConnList::IdleConnList(const char *key
, PconnPool
*thePool
) :
53 capacity_(PCONN_FDS_SZ
),
57 hash
.key
= xstrdup(key
);
58 theList_
= new Comm::ConnectionPointer
[capacity_
];
59 // TODO: re-attach to MemPools. WAS: theList = (?? *)pconn_fds_pool->alloc();
62 IdleConnList::~IdleConnList()
65 parent_
->unlinkList(this);
72 /** Search the list. Matches by FD socket number.
73 * Performed from the end of list where newest entries are.
75 * \retval <0 The connection is not listed
76 * \retval >=0 The connection array index
79 IdleConnList::findIndexOf(const Comm::ConnectionPointer
&conn
) const
81 for (int index
= size_
- 1; index
>= 0; --index
) {
82 if (conn
->fd
== theList_
[index
]->fd
) {
83 debugs(48, 3, HERE
<< "found " << conn
<< " at index " << index
);
88 debugs(48, 2, HERE
<< conn
<< " NOT FOUND!");
92 /** Remove the entry at specified index.
93 * May perform a shuffle of list entries to fill the gap.
94 * \retval false The index is not an in-use entry.
97 IdleConnList::removeAt(int index
)
99 if (index
< 0 || index
>= size_
)
102 // shuffle the remaining entries to fill the new gap.
103 for (; index
< size_
- 1; ++index
)
104 theList_
[index
] = theList_
[index
+ 1];
105 theList_
[--size_
] = NULL
;
108 parent_
->noteConnectionRemoved();
110 debugs(48, 3, HERE
<< "deleting " << hashKeyStr(&hash
));
118 // almost a duplicate of removeFD. But drops multiple entries.
120 IdleConnList::closeN(size_t n
)
123 debugs(48, 2, HERE
<< "Nothing to do.");
125 } else if (n
>= (size_t)size_
) {
126 debugs(48, 2, HERE
<< "Closing all entries.");
128 const Comm::ConnectionPointer conn
= theList_
[--size_
];
129 theList_
[size_
] = NULL
;
133 parent_
->noteConnectionRemoved();
135 } else { //if (n < size_)
136 debugs(48, 2, HERE
<< "Closing " << n
<< " of " << size_
<< " entries.");
139 // ensure the first N entries are closed
140 for (index
= 0; index
< n
; ++index
) {
141 const Comm::ConnectionPointer conn
= theList_
[index
];
142 theList_
[index
] = NULL
;
146 parent_
->noteConnectionRemoved();
148 // shuffle the list N down.
149 for (index
= 0; index
< (size_t)size_
- n
; ++index
) {
150 theList_
[index
] = theList_
[index
+ n
];
152 // ensure the last N entries are unset
153 while (index
< ((size_t)size_
)) {
154 theList_
[index
] = NULL
;
160 if (parent_
&& size_
== 0) {
161 debugs(48, 3, HERE
<< "deleting " << hashKeyStr(&hash
));
167 IdleConnList::clearHandlers(const Comm::ConnectionPointer
&conn
)
169 debugs(48, 3, HERE
<< "removing close handler for " << conn
);
170 comm_read_cancel(conn
->fd
, IdleConnList::Read
, this);
171 commUnsetConnTimeout(conn
);
175 IdleConnList::push(const Comm::ConnectionPointer
&conn
)
177 if (size_
== capacity_
) {
178 debugs(48, 3, HERE
<< "growing idle Connection array");
180 const Comm::ConnectionPointer
*oldList
= theList_
;
181 theList_
= new Comm::ConnectionPointer
[capacity_
];
182 for (int index
= 0; index
< size_
; ++index
)
183 theList_
[index
] = oldList
[index
];
189 parent_
->noteConnectionAdded();
191 theList_
[size_
] = conn
;
193 AsyncCall::Pointer readCall
= commCbCall(5,4, "IdleConnList::Read",
194 CommIoCbPtrFun(IdleConnList::Read
, this));
195 comm_read(conn
, fakeReadBuf_
, sizeof(fakeReadBuf_
), readCall
);
196 AsyncCall::Pointer timeoutCall
= commCbCall(5,4, "IdleConnList::Timeout",
197 CommTimeoutCbPtrFun(IdleConnList::Timeout
, this));
198 commSetConnTimeout(conn
, Config
.Timeout
.serverIdlePconn
, timeoutCall
);
201 /// Determine whether an entry in the idle list is available for use.
202 /// Returns false if the entry is unset, closed or closing.
204 IdleConnList::isAvailable(int i
) const
206 const Comm::ConnectionPointer
&conn
= theList_
[i
];
208 // connection already closed. useless.
209 if (!Comm::IsConnOpen(conn
))
212 // our connection early-read/close handler is scheduled to run already. unsafe
213 if (!COMMIO_FD_READCB(conn
->fd
)->active())
219 Comm::ConnectionPointer
222 for (int i
=size_
-1; i
>=0; --i
) {
227 // our connection timeout handler is scheduled to run already. unsafe for now.
228 // TODO: cancel the pending timeout callback and allow re-use of the conn.
229 if (fd_table
[theList_
[i
]->fd
].timeoutHandler
== NULL
)
232 // finally, a match. pop and return it.
233 Comm::ConnectionPointer result
= theList_
[i
];
234 /* may delete this */
236 clearHandlers(result
);
240 return Comm::ConnectionPointer();
244 * XXX this routine isn't terribly efficient - if there's a pending
245 * read event (which signifies the fd will close in the next IO loop!)
246 * we ignore the FD and move onto the next one. This means, as an example,
247 * if we have a lot of FDs open to a very popular server and we get a bunch
248 * of requests JUST as they timeout (say, it shuts down) we'll be wasting
249 * quite a bit of CPU. Just keep it in mind.
251 Comm::ConnectionPointer
252 IdleConnList::findUseable(const Comm::ConnectionPointer
&key
)
256 // small optimization: do the constant bool tests only once.
257 const bool keyCheckAddr
= !key
->local
.isAnyAddr();
258 const bool keyCheckPort
= key
->local
.port() > 0;
260 for (int i
=size_
-1; i
>=0; --i
) {
265 // local end port is required, but dont match.
266 if (keyCheckPort
&& key
->local
.port() != theList_
[i
]->local
.port())
269 // local address is required, but does not match.
270 if (keyCheckAddr
&& key
->local
.matchIPAddr(theList_
[i
]->local
) != 0)
273 // our connection timeout handler is scheduled to run already. unsafe for now.
274 // TODO: cancel the pending timeout callback and allow re-use of the conn.
275 if (fd_table
[theList_
[i
]->fd
].timeoutHandler
== NULL
)
278 // finally, a match. pop and return it.
279 Comm::ConnectionPointer result
= theList_
[i
];
280 /* may delete this */
282 clearHandlers(result
);
286 return Comm::ConnectionPointer();
289 /* might delete list */
291 IdleConnList::findAndClose(const Comm::ConnectionPointer
&conn
)
293 const int index
= findIndexOf(conn
);
295 /* might delete this */
303 IdleConnList::Read(const Comm::ConnectionPointer
&conn
, char *buf
, size_t len
, comm_err_t flag
, int xerrno
, void *data
)
305 debugs(48, 3, HERE
<< len
<< " bytes from " << conn
);
307 if (flag
== COMM_ERR_CLOSING
) {
308 debugs(48, 3, HERE
<< "COMM_ERR_CLOSING from " << conn
);
309 /* Bail out on COMM_ERR_CLOSING - may happen when shutdown aborts our idle FD */
313 IdleConnList
*list
= (IdleConnList
*) data
;
314 /* may delete list/data */
315 list
->findAndClose(conn
);
319 IdleConnList::Timeout(const CommTimeoutCbParams
&io
)
321 debugs(48, 3, HERE
<< io
.conn
);
322 IdleConnList
*list
= static_cast<IdleConnList
*>(io
.data
);
323 /* may delete list/data */
324 list
->findAndClose(io
.conn
);
327 /* ========== PconnPool PRIVATE FUNCTIONS ============================================ */
330 PconnPool::key(const Comm::ConnectionPointer
&destLink
, const char *domain
)
332 LOCAL_ARRAY(char, buf
, SQUIDHOSTNAMELEN
* 3 + 10);
334 destLink
->remote
.toUrl(buf
, SQUIDHOSTNAMELEN
* 3 + 10);
336 const int used
= strlen(buf
);
337 snprintf(buf
+used
, SQUIDHOSTNAMELEN
* 3 + 10-used
, "/%s", domain
);
340 debugs(48,6,"PconnPool::key(" << destLink
<< ", " << (domain
?domain
:"[no domain]") << ") is {" << buf
<< "}" );
345 PconnPool::dumpHist(StoreEntry
* e
) const
348 "%s persistent connection counts:\n"
352 "\t---- ---------\n",
355 for (int i
= 0; i
< PCONN_HIST_SZ
; ++i
) {
359 storeAppendPrintf(e
, "\t%4d %9d\n", i
, hist
[i
]);
364 PconnPool::dumpHash(StoreEntry
*e
) const
366 hash_table
*hid
= table
;
370 for (hash_link
*walker
= hid
->next
; walker
; walker
= hash_next(hid
)) {
371 storeAppendPrintf(e
, "\t item %5d: %s\n", i
, (char *)(walker
->key
));
376 /* ========== PconnPool PUBLIC FUNCTIONS ============================================ */
378 PconnPool::PconnPool(const char *aDescr
) : table(NULL
), descr(aDescr
),
382 table
= hash_create((HASHCMP
*) strcmp
, 229, hash_string
);
384 for (i
= 0; i
< PCONN_HIST_SZ
; ++i
)
387 PconnModule::GetInstance()->add(this);
390 PconnPool::~PconnPool()
393 hashFreeMemory(table
);
397 PconnPool::push(const Comm::ConnectionPointer
&conn
, const char *domain
)
400 debugs(48, 3, HERE
<< "Not many unused FDs");
403 } else if (shutting_down
) {
405 debugs(48, 3, HERE
<< "Squid is shutting down. Refusing to do anything");
409 const char *aKey
= key(conn
, domain
);
410 IdleConnList
*list
= (IdleConnList
*) hash_lookup(table
, aKey
);
413 list
= new IdleConnList(aKey
, this);
414 debugs(48, 3, HERE
<< "new IdleConnList for {" << hashKeyStr(&list
->hash
) << "}" );
415 hash_join(table
, &list
->hash
);
417 debugs(48, 3, HERE
<< "found IdleConnList for {" << hashKeyStr(&list
->hash
) << "}" );
421 assert(!comm_has_incomplete_write(conn
->fd
));
423 LOCAL_ARRAY(char, desc
, FD_DESC_SZ
);
424 snprintf(desc
, FD_DESC_SZ
, "Idle server: %s", aKey
);
425 fd_note(conn
->fd
, desc
);
426 debugs(48, 3, HERE
<< "pushed " << conn
<< " for " << aKey
);
429 Comm::ConnectionPointer
430 PconnPool::pop(const Comm::ConnectionPointer
&destLink
, const char *domain
, bool isRetriable
)
432 const char * aKey
= key(destLink
, domain
);
434 IdleConnList
*list
= (IdleConnList
*)hash_lookup(table
, aKey
);
436 debugs(48, 3, HERE
<< "lookup for key {" << aKey
<< "} failed.");
437 return Comm::ConnectionPointer();
439 debugs(48, 3, HERE
<< "found " << hashKeyStr(&list
->hash
) << (isRetriable
?"(to use)":"(to kill)") );
442 /* may delete list */
443 Comm::ConnectionPointer temp
= list
->findUseable(destLink
);
444 if (!isRetriable
&& Comm::IsConnOpen(temp
))
451 PconnPool::closeN(int n
, const Comm::ConnectionPointer
&destLink
, const char *domain
)
453 // TODO: optimize: we can probably do hash_lookup just once
454 for (int i
= 0; i
< n
; ++i
)
455 pop(destLink
, domain
, false); // may fail!
459 PconnPool::unlinkList(IdleConnList
*list
)
461 theCount
-= list
->count();
462 assert(theCount
>= 0);
463 hash_remove_link(table
, &list
->hash
);
467 PconnPool::noteUses(int uses
)
469 if (uses
>= PCONN_HIST_SZ
)
470 uses
= PCONN_HIST_SZ
- 1;
475 /* ========== PconnModule ============================================ */
478 * This simple class exists only for the cache manager
481 PconnModule::PconnModule() : pools(NULL
), poolCount(0)
483 pools
= (PconnPool
**) xcalloc(MAX_NUM_PCONN_POOLS
, sizeof(*pools
));
484 //TODO: re-link to MemPools. WAS: pconn_fds_pool = memPoolCreate("pconn_fds", PCONN_FDS_SZ * sizeof(int));
485 debugs(48, DBG_CRITICAL
, "persistent connection module initialized");
486 registerWithCacheManager();
490 PconnModule::GetInstance()
492 if (instance
== NULL
)
493 instance
= new PconnModule
;
499 PconnModule::registerWithCacheManager(void)
501 Mgr::RegisterAction("pconn",
502 "Persistent Connection Utilization Histograms",
507 PconnModule::add(PconnPool
*aPool
)
509 assert(poolCount
< MAX_NUM_PCONN_POOLS
);
510 *(pools
+poolCount
) = aPool
;
515 PconnModule::dump(StoreEntry
*e
)
519 for (i
= 0; i
< poolCount
; ++i
) {
520 storeAppendPrintf(e
, "\n Pool %d Stats\n", i
);
521 (*(pools
+i
))->dumpHist(e
);
522 storeAppendPrintf(e
, "\n Pool %d Hash Table\n",i
);
523 (*(pools
+i
))->dumpHash(e
);
528 PconnModule::DumpWrapper(StoreEntry
*e
)
530 PconnModule::GetInstance()->dump(e
);