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