]> git.ipfire.org Git - thirdparty/squid.git/blob - src/pconn.cc
Merge from trunk
[thirdparty/squid.git] / src / pconn.cc
1
2 /*
3 * $Id: pconn.cc,v 1.55 2007/12/27 01:03:13 hno Exp $
4 *
5 * DEBUG: section 48 Persistent Connections
6 * AUTHOR: Duane Wessels
7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35
36 #include "squid.h"
37 #include "CacheManager.h"
38 #include "Store.h"
39 #include "comm.h"
40 #include "pconn.h"
41 #include "fde.h"
42
43 #define PCONN_FDS_SZ 8 /* pconn set size, increase for better memcache hit rate */
44
45 static MemAllocator *pconn_fds_pool = NULL;
46 PconnModule * PconnModule::instance = NULL;
47 CBDATA_CLASS_INIT(IdleConnList);
48
49 /* ========== IdleConnList ============================================ */
50
51 IdleConnList::IdleConnList(const char *key, PconnPool *thePool) : parent(thePool)
52 {
53 hash.key = xstrdup(key);
54 nfds_alloc = PCONN_FDS_SZ;
55 nfds = 0;
56 fds = (int *)pconn_fds_pool->alloc();
57 }
58
59 IdleConnList::~IdleConnList()
60 {
61
62 parent->unlinkList(this);
63
64 if (nfds_alloc == PCONN_FDS_SZ)
65 pconn_fds_pool->free(fds);
66 else
67 xfree(fds);
68
69 xfree(hash.key);
70 }
71
72 int
73 IdleConnList::findFDIndex (int fd)
74 {
75 int index;
76
77 for (index = nfds - 1; index >= 0; --index) {
78 if (fds[index] == fd)
79 return index;
80 }
81
82 return -1;
83 }
84
85 void
86 IdleConnList::removeFD(int fd)
87 {
88 int index = findFDIndex(fd);
89 if (index < 0) {
90 debugs(48, 0, "IdleConnList::removeFD: FD " << fd << " NOT FOUND!");
91 return;
92 }
93 debugs(48, 3, "IdleConnList::removeFD: found FD " << fd << " at index " << index);
94
95 for (; index < nfds - 1; index++)
96 fds[index] = fds[index + 1];
97
98 if (--nfds == 0) {
99 debugs(48, 3, "IdleConnList::removeFD: deleting " << hashKeyStr(&hash));
100 delete this;
101 }
102 }
103
104 void
105 IdleConnList::clearHandlers(int fd)
106 {
107 comm_read_cancel(fd, IdleConnList::read, this);
108 commSetTimeout(fd, -1, NULL, NULL);
109 }
110
111 void
112 IdleConnList::push(int fd)
113 {
114 if (nfds == nfds_alloc) {
115 debugs(48, 3, "IdleConnList::push: growing FD array");
116 nfds_alloc <<= 1;
117 int *old = fds;
118 fds = (int *)xmalloc(nfds_alloc * sizeof(int));
119 xmemcpy(fds, old, nfds * sizeof(int));
120
121 if (nfds == PCONN_FDS_SZ)
122 pconn_fds_pool->free(old);
123 else
124 xfree(old);
125 }
126
127 fds[nfds++] = fd;
128 comm_read(fd, fakeReadBuf, sizeof(fakeReadBuf), IdleConnList::read, this);
129 commSetTimeout(fd, Config.Timeout.pconn, IdleConnList::timeout, this);
130 }
131
132 /*
133 * XXX this routine isn't terribly efficient - if there's a pending
134 * read event (which signifies the fd will close in the next IO loop!)
135 * we ignore the FD and move onto the next one. This means, as an example,
136 * if we have a lot of FDs open to a very popular server and we get a bunch
137 * of requests JUST as they timeout (say, it shuts down) we'll be wasting
138 * quite a bit of CPU. Just keep it in mind.
139 */
140 int
141 IdleConnList::findUseableFD()
142 {
143 assert(nfds);
144
145 for (int i=nfds-1; i>=0; i--) {
146 if (!comm_has_pending_read_callback(fds[i])) {
147 return fds[i];
148 }
149 }
150
151 return -1;
152 }
153
154 void
155 IdleConnList::read(int fd, char *buf, size_t len, comm_err_t flag, int xerrno, void *data)
156 {
157 debugs(48, 3, "IdleConnList::read: " << len << " bytes from FD " << fd);
158
159 if (flag == COMM_ERR_CLOSING) {
160 /* Bail out early on COMM_ERR_CLOSING - close handlers will tidy up for us */
161 return;
162 }
163
164 IdleConnList *list = (IdleConnList *) data;
165 list->removeFD(fd); /* might delete list */
166 comm_close(fd);
167 }
168
169 void
170 IdleConnList::timeout(int fd, void *data)
171 {
172 debugs(48, 3, "IdleConnList::timeout: FD " << fd);
173 IdleConnList *list = (IdleConnList *) data;
174 list->removeFD(fd); /* might delete list */
175 comm_close(fd);
176 }
177
178 /* ========== PconnPool PRIVATE FUNCTIONS ============================================ */
179
180 const char *
181 PconnPool::key(const char *host, u_short port, const char *domain, IPAddress &client_address)
182 {
183 LOCAL_ARRAY(char, buf, SQUIDHOSTNAMELEN * 2 + 10);
184 char ntoabuf[MAX_IPSTRLEN];
185
186 if (domain && !client_address.IsAnyAddr())
187 snprintf(buf, SQUIDHOSTNAMELEN * 2 + 10, "%s:%d-%s/%s", host, (int) port, client_address.NtoA(ntoabuf,MAX_IPSTRLEN), domain);
188 else if (domain && client_address.IsAnyAddr())
189 snprintf(buf, SQUIDHOSTNAMELEN * 2 + 10, "%s:%d/%s", host, (int) port, domain);
190 else if ((!domain) && !client_address.IsAnyAddr())
191 snprintf(buf, SQUIDHOSTNAMELEN * 2 + 10, "%s:%d-%s", host, (int) port, client_address.NtoA(ntoabuf,MAX_IPSTRLEN));
192 else
193 snprintf(buf, SQUIDHOSTNAMELEN * 2 + 10, "%s:%d", host, (int) port);
194
195 return buf;
196 }
197
198 void
199 PconnPool::dumpHist(StoreEntry * e)
200 {
201 int i;
202 storeAppendPrintf(e,
203 "%s persistent connection counts:\n"
204 "\n"
205 "\treq/\n"
206 "\tconn count\n"
207 "\t---- ---------\n",
208 descr);
209
210 for (i = 0; i < PCONN_HIST_SZ; i++) {
211 if (hist[i] == 0)
212 continue;
213
214 storeAppendPrintf(e, "\t%4d %9d\n", i, hist[i]);
215 }
216 }
217
218 /* ========== PconnPool PUBLIC FUNCTIONS ============================================ */
219
220 PconnPool::PconnPool(const char *aDescr) : table(NULL), descr(aDescr)
221 {
222 int i;
223 table = hash_create((HASHCMP *) strcmp, 229, hash_string);
224
225 for (i = 0; i < PCONN_HIST_SZ; i++)
226 hist[i] = 0;
227
228 PconnModule::GetInstance()->add
229 (this);
230 }
231
232 void
233 PconnPool::push(int fd, const char *host, u_short port, const char *domain, IPAddress &client_address)
234 {
235
236 IdleConnList *list;
237 const char *aKey;
238 LOCAL_ARRAY(char, desc, FD_DESC_SZ);
239
240 if (fdUsageHigh()) {
241 debugs(48, 3, "PconnPool::push: Not many unused FDs");
242 comm_close(fd);
243 return;
244 } else if (shutting_down) {
245 comm_close(fd);
246 return;
247 }
248
249 aKey = key(host, port, domain, client_address);
250
251 list = (IdleConnList *) hash_lookup(table, aKey);
252
253 if (list == NULL) {
254 list = new IdleConnList(aKey, this);
255 debugs(48, 3, "pconnNew: adding " << hashKeyStr(&list->hash));
256 hash_join(table, &list->hash);
257 }
258
259 list->push(fd);
260
261 assert(!comm_has_incomplete_write(fd));
262 snprintf(desc, FD_DESC_SZ, "%s idle connection", host);
263 fd_note(fd, desc);
264 debugs(48, 3, "PconnPool::push: pushed FD " << fd << " for " << aKey);
265 }
266
267 /*
268 * Return a pconn fd for host:port if available and retriable.
269 * Otherwise, return -1.
270 *
271 * We close available persistent connection if the caller transaction is not
272 * retriable to avoid having a growing number of open connections when many
273 * transactions create persistent connections but are not retriable.
274 */
275 int
276
277 PconnPool::pop(const char *host, u_short port, const char *domain, IPAddress &client_address, bool isRetriable)
278 {
279 IdleConnList *list;
280 const char * aKey = key(host, port, domain, client_address);
281 list = (IdleConnList *)hash_lookup(table, aKey);
282
283 if (list == NULL)
284 return -1;
285
286 int fd = list->findUseableFD(); // search from the end. skip pending reads.
287
288 if (fd >= 0) {
289 list->clearHandlers(fd);
290 list->removeFD(fd); /* might delete list */
291
292 if (!isRetriable) {
293 comm_close(fd);
294 return -1;
295 }
296 }
297
298 return fd;
299 }
300
301 void
302 PconnPool::unlinkList(IdleConnList *list) const
303 {
304 hash_remove_link(table, &list->hash);
305 }
306
307 void
308 PconnPool::count(int uses)
309 {
310 if (uses >= PCONN_HIST_SZ)
311 uses = PCONN_HIST_SZ - 1;
312
313 hist[uses]++;
314 }
315
316 /* ========== PconnModule ============================================ */
317
318 /*
319 * This simple class exists only for the cache manager
320 */
321
322 PconnModule::PconnModule() : pools(NULL), poolCount(0)
323 {
324 pools = (PconnPool **) xcalloc(MAX_NUM_PCONN_POOLS, sizeof(*pools));
325 pconn_fds_pool = memPoolCreate("pconn_fds", PCONN_FDS_SZ * sizeof(int));
326 debugs(48, 0, "persistent connection module initialized");
327 registerWithCacheManager();
328 }
329
330 PconnModule *
331 PconnModule::GetInstance()
332 {
333 if (instance == NULL)
334 instance = new PconnModule;
335
336 return instance;
337 }
338
339 void
340 PconnModule::registerWithCacheManager(void)
341 {
342 CacheManager::GetInstance()->
343 registerAction("pconn",
344 "Persistent Connection Utilization Histograms",
345 DumpWrapper, 0, 1);
346 }
347
348 void
349
350 PconnModule::add
351 (PconnPool *aPool)
352 {
353 assert(poolCount < MAX_NUM_PCONN_POOLS);
354 *(pools+poolCount) = aPool;
355 poolCount++;
356 }
357
358 void
359 PconnModule::dump(StoreEntry *e)
360 {
361 int i;
362
363 for (i = 0; i < poolCount; i++) {
364 (*(pools+i))->dumpHist(e);
365 }
366 }
367
368 void
369 PconnModule::DumpWrapper(StoreEntry *e)
370 {
371 PconnModule::GetInstance()->dump(e);
372 }