]> git.ipfire.org Git - thirdparty/squid.git/blob - src/pconn.cc
Bug #1957 fix: Close a persistent ICAP connection if we have to open a
[thirdparty/squid.git] / src / pconn.cc
1
2 /*
3 * $Id: pconn.cc,v 1.51 2007/05/11 13:20:57 rousskov 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 assert(index >= 0);
90 debugs(48, 3, "IdleConnList::removeFD: found FD " << fd << " at index " << index);
91
92 for (; index < nfds - 1; index++)
93 fds[index] = fds[index + 1];
94
95 if (--nfds == 0) {
96 debugs(48, 3, "IdleConnList::removeFD: deleting " << hashKeyStr(&hash));
97 delete this;
98 }
99 }
100
101 void
102 IdleConnList::clearHandlers(int fd)
103 {
104 comm_read_cancel(fd, IdleConnList::read, this);
105 commSetTimeout(fd, -1, NULL, NULL);
106 }
107
108 void
109 IdleConnList::push(int fd)
110 {
111 if (nfds == nfds_alloc) {
112 debugs(48, 3, "IdleConnList::push: growing FD array");
113 nfds_alloc <<= 1;
114 int *old = fds;
115 fds = (int *)xmalloc(nfds_alloc * sizeof(int));
116 xmemcpy(fds, old, nfds * sizeof(int));
117
118 if (nfds == PCONN_FDS_SZ)
119 pconn_fds_pool->free(old);
120 else
121 xfree(old);
122 }
123
124 fds[nfds++] = fd;
125 comm_read(fd, fakeReadBuf, sizeof(fakeReadBuf), IdleConnList::read, this);
126 commSetTimeout(fd, Config.Timeout.pconn, IdleConnList::timeout, this);
127 }
128
129 /*
130 * XXX this routine isn't terribly efficient - if there's a pending
131 * read event (which signifies the fd will close in the next IO loop!)
132 * we ignore the FD and move onto the next one. This means, as an example,
133 * if we have a lot of FDs open to a very popular server and we get a bunch
134 * of requests JUST as they timeout (say, it shuts down) we'll be wasting
135 * quite a bit of CPU. Just keep it in mind.
136 */
137 int
138 IdleConnList::findUseableFD()
139 {
140 assert(nfds);
141
142 for (int i = 0; i< nfds; i++) {
143 if (!comm_has_pending_read_callback(fds[i])) {
144 return fds[i];
145 }
146 }
147
148 return -1;
149 }
150
151 void
152 IdleConnList::read(int fd, char *buf, size_t len, comm_err_t flag, int xerrno, void *data)
153 {
154 debugs(48, 3, "IdleConnList::read: " << len << " bytes from FD " << fd);
155
156 if (flag == COMM_ERR_CLOSING) {
157 /* Bail out early on COMM_ERR_CLOSING - close handlers will tidy up for us */
158 return;
159 }
160
161 IdleConnList *list = (IdleConnList *) data;
162 list->removeFD(fd); /* might delete list */
163 comm_close(fd);
164 }
165
166 void
167 IdleConnList::timeout(int fd, void *data)
168 {
169 debugs(48, 3, "IdleConnList::timeout: FD " << fd);
170 IdleConnList *list = (IdleConnList *) data;
171 list->removeFD(fd); /* might delete list */
172 comm_close(fd);
173 }
174
175 /* ========== PconnPool PRIVATE FUNCTIONS ============================================ */
176
177 const char *
178
179 PconnPool::key(const char *host, u_short port, const char *domain, struct IN_ADDR *client_address)
180 {
181 LOCAL_ARRAY(char, buf, SQUIDHOSTNAMELEN * 2 + 10);
182
183 if (domain && client_address)
184 snprintf(buf, SQUIDHOSTNAMELEN * 2 + 10, "%s:%d-%s/%s", host, (int) port, inet_ntoa(*client_address), domain);
185 else if (domain && (!client_address))
186 snprintf(buf, SQUIDHOSTNAMELEN * 2 + 10, "%s:%d/%s", host, (int) port, domain);
187 else if ((!domain) && client_address)
188 snprintf(buf, SQUIDHOSTNAMELEN * 2 + 10, "%s:%d-%s", host, (int) port, inet_ntoa(*client_address));
189 else
190 snprintf(buf, SQUIDHOSTNAMELEN * 2 + 10, "%s:%d", host, (int) port);
191
192 return buf;
193 }
194
195 void
196 PconnPool::dumpHist(StoreEntry * e)
197 {
198 int i;
199 storeAppendPrintf(e,
200 "%s persistent connection counts:\n"
201 "\n"
202 "\treq/\n"
203 "\tconn count\n"
204 "\t---- ---------\n",
205 descr);
206
207 for (i = 0; i < PCONN_HIST_SZ; i++) {
208 if (hist[i] == 0)
209 continue;
210
211 storeAppendPrintf(e, "\t%4d %9d\n", i, hist[i]);
212 }
213 }
214
215 /* ========== PconnPool PUBLIC FUNCTIONS ============================================ */
216
217 PconnPool::PconnPool(const char *aDescr) : table(NULL), descr(aDescr)
218 {
219 int i;
220 table = hash_create((HASHCMP *) strcmp, 229, hash_string);
221
222 for (i = 0; i < PCONN_HIST_SZ; i++)
223 hist[i] = 0;
224
225 PconnModule::GetInstance()->add
226 (this);
227 }
228
229 void
230
231 PconnPool::push(int fd, const char *host, u_short port, const char *domain, struct IN_ADDR *client_address)
232 {
233
234 IdleConnList *list;
235 const char *aKey;
236 LOCAL_ARRAY(char, desc, FD_DESC_SZ);
237
238 if (fdUsageHigh())
239 {
240 debugs(48, 3, "PconnPool::push: Not many unused FDs");
241 comm_close(fd);
242 return;
243 } else if (shutting_down)
244 {
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 {
255 list = new IdleConnList(aKey, this);
256 debugs(48, 3, "pconnNew: adding " << hashKeyStr(&list->hash));
257 hash_join(table, &list->hash);
258 }
259
260 list->push(fd);
261
262 assert(!comm_has_incomplete_write(fd));
263 snprintf(desc, FD_DESC_SZ, "%s idle connection", host);
264 fd_note(fd, desc);
265 debugs(48, 3, "PconnPool::push: pushed FD " << fd << " for " << aKey);
266 }
267
268 /*
269 * Return a pconn fd for host:port if available and retriable.
270 * Otherwise, return -1.
271 *
272 * We close available persistent connection if the caller transaction is not
273 * retriable to avoid having a growing number of open connections when many
274 * transactions create persistent connections but are not retriable.
275 */
276 int
277
278 PconnPool::pop(const char *host, u_short port, const char *domain, struct IN_ADDR *client_address, bool isRetriable)
279 {
280 IdleConnList *list;
281 const char * aKey = key(host, port, domain, client_address);
282 list = (IdleConnList *)hash_lookup(table, aKey);
283
284 if (list == NULL)
285 return -1;
286
287 int fd = list->findUseableFD();
288
289 if (fd >= 0)
290 {
291 list->clearHandlers(fd);
292 list->removeFD(fd); /* might delete list */
293
294 if (!isRetriable) {
295 comm_close(fd);
296 return -1;
297 }
298 }
299
300 return fd;
301 }
302
303 void
304 PconnPool::unlinkList(IdleConnList *list) const
305 {
306 hash_remove_link(table, &list->hash);
307 }
308
309 void
310 PconnPool::count(int uses)
311 {
312 if (uses >= PCONN_HIST_SZ)
313 uses = PCONN_HIST_SZ - 1;
314
315 hist[uses]++;
316 }
317
318 /* ========== PconnModule ============================================ */
319
320 /*
321 * This simple class exists only for the cache manager
322 */
323
324 PconnModule::PconnModule() : pools(NULL), poolCount(0)
325 {
326 pools = (PconnPool **) xcalloc(MAX_NUM_PCONN_POOLS, sizeof(*pools));
327 pconn_fds_pool = memPoolCreate("pconn_fds", PCONN_FDS_SZ * sizeof(int));
328 debugs(48, 0, "persistent connection module initialized");
329 }
330
331 PconnModule *
332 PconnModule::GetInstance()
333 {
334 if (instance == NULL)
335 instance = new PconnModule;
336
337 return instance;
338 }
339
340 void
341 PconnModule::registerWithCacheManager(CacheManager & manager)
342 {
343 manager.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 }