]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/pconn.cc
4 * DEBUG: section 48 Persistent Connections
5 * AUTHOR: Duane Wessels
7 * SQUID Web Proxy Cache http://www.squid-cache.org/
8 * ----------------------------------------------------------
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.
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.
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.
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.
36 #include "CacheManager.h"
42 #define PCONN_FDS_SZ 8 /* pconn set size, increase for better memcache hit rate */
44 static MemAllocator
*pconn_fds_pool
= NULL
;
45 PconnModule
* PconnModule::instance
= NULL
;
46 CBDATA_CLASS_INIT(IdleConnList
);
48 /* ========== IdleConnList ============================================ */
50 IdleConnList::IdleConnList(const char *key
, PconnPool
*thePool
) : parent(thePool
)
52 hash
.key
= xstrdup(key
);
53 nfds_alloc
= PCONN_FDS_SZ
;
55 fds
= (int *)pconn_fds_pool
->alloc();
58 IdleConnList::~IdleConnList()
61 parent
->unlinkList(this);
63 if (nfds_alloc
== PCONN_FDS_SZ
)
64 pconn_fds_pool
->freeOne(fds
);
72 IdleConnList::findFDIndex (int fd
)
76 for (index
= nfds
- 1; index
>= 0; --index
) {
85 IdleConnList::removeFD(int fd
)
87 int index
= findFDIndex(fd
);
89 debugs(48, 2, "IdleConnList::removeFD: FD " << fd
<< " NOT FOUND!");
92 debugs(48, 3, "IdleConnList::removeFD: found FD " << fd
<< " at index " << index
);
94 for (; index
< nfds
- 1; index
++)
95 fds
[index
] = fds
[index
+ 1];
98 debugs(48, 3, "IdleConnList::removeFD: deleting " << hashKeyStr(&hash
));
105 IdleConnList::clearHandlers(int fd
)
107 comm_read_cancel(fd
, IdleConnList::read
, this);
108 commSetTimeout(fd
, -1, NULL
, NULL
);
112 IdleConnList::push(int fd
)
114 if (nfds
== nfds_alloc
) {
115 debugs(48, 3, "IdleConnList::push: growing FD array");
118 fds
= (int *)xmalloc(nfds_alloc
* sizeof(int));
119 xmemcpy(fds
, old
, nfds
* sizeof(int));
121 if (nfds
== PCONN_FDS_SZ
)
122 pconn_fds_pool
->freeOne(old
);
128 comm_read(fd
, fakeReadBuf
, sizeof(fakeReadBuf
), IdleConnList::read
, this);
129 commSetTimeout(fd
, Config
.Timeout
.pconn
, IdleConnList::timeout
, this);
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.
141 IdleConnList::findUseableFD()
145 for (int i
=nfds
-1; i
>=0; i
--) {
146 if (!comm_has_pending_read_callback(fds
[i
])) {
155 IdleConnList::read(int fd
, char *buf
, size_t len
, comm_err_t flag
, int xerrno
, void *data
)
157 debugs(48, 3, "IdleConnList::read: " << len
<< " bytes from FD " << fd
);
159 if (flag
== COMM_ERR_CLOSING
) {
160 /* Bail out early on COMM_ERR_CLOSING - close handlers will tidy up for us */
164 IdleConnList
*list
= (IdleConnList
*) data
;
165 if (list
->removeFD(fd
)) /* might delete list */
170 IdleConnList::timeout(int fd
, void *data
)
172 debugs(48, 3, "IdleConnList::timeout: FD " << fd
);
173 IdleConnList
*list
= (IdleConnList
*) data
;
174 if (list
->removeFD(fd
)) /* might delete list */
178 /* ========== PconnPool PRIVATE FUNCTIONS ============================================ */
181 PconnPool::key(const char *host
, u_short port
, const char *domain
, Ip::Address
&client_address
)
183 LOCAL_ARRAY(char, buf
, SQUIDHOSTNAMELEN
* 3 + 10);
184 char ntoabuf
[MAX_IPSTRLEN
];
186 if (domain
&& !client_address
.IsAnyAddr())
187 snprintf(buf
, SQUIDHOSTNAMELEN
* 3 + 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
* 3 + 10, "%s:%d/%s", host
, (int) port
, domain
);
190 else if ((!domain
) && !client_address
.IsAnyAddr())
191 snprintf(buf
, SQUIDHOSTNAMELEN
* 3 + 10, "%s:%d-%s", host
, (int) port
, client_address
.NtoA(ntoabuf
,MAX_IPSTRLEN
));
193 snprintf(buf
, SQUIDHOSTNAMELEN
* 3 + 10, "%s:%d", host
, (int) port
);
195 debugs(48,6,"PconnPool::key(" << (host
?host
:"(no host!)") << "," << port
<< "," << (domain
?domain
:"(no domain)") << "," << client_address
<< "is {" << buf
<< "}" );
200 PconnPool::dumpHist(StoreEntry
* e
)
204 "%s persistent connection counts:\n"
208 "\t---- ---------\n",
211 for (i
= 0; i
< PCONN_HIST_SZ
; i
++) {
215 storeAppendPrintf(e
, "\t%4d %9d\n", i
, hist
[i
]);
220 PconnPool::dumpHash(StoreEntry
*e
)
223 hash_link
*walker
= NULL
;
224 hash_table
*hid
= table
;
227 for (i
= 0, walker
= hid
->next
; walker
; walker
= hash_next(hid
)) {
228 storeAppendPrintf(e
, "\t item %5d: %s\n", i
++, (char *)(walker
->key
));
232 /* ========== PconnPool PUBLIC FUNCTIONS ============================================ */
234 PconnPool::PconnPool(const char *aDescr
) : table(NULL
), descr(aDescr
)
237 table
= hash_create((HASHCMP
*) strcmp
, 229, hash_string
);
239 for (i
= 0; i
< PCONN_HIST_SZ
; i
++)
242 PconnModule::GetInstance()->add(this);
245 PconnPool::~PconnPool()
248 hashFreeMemory(table
);
252 PconnPool::push(int fd
, const char *host
, u_short port
, const char *domain
, Ip::Address
&client_address
)
256 LOCAL_ARRAY(char, desc
, FD_DESC_SZ
);
259 debugs(48, 3, "PconnPool::push: Not many unused FDs");
262 } else if (shutting_down
) {
264 debugs(48, 3, "PconnPool::push: Squid is shutting down. Refusing to do anything");
268 aKey
= key(host
, port
, domain
, client_address
);
270 list
= (IdleConnList
*) hash_lookup(table
, aKey
);
273 list
= new IdleConnList(aKey
, this);
274 debugs(48, 3, "PconnPool::push: new IdleConnList for {" << hashKeyStr(&list
->hash
) << "}" );
275 hash_join(table
, &list
->hash
);
277 debugs(48, 3, "PconnPool::push: found IdleConnList for {" << hashKeyStr(&list
->hash
) << "}" );
282 assert(!comm_has_incomplete_write(fd
));
283 snprintf(desc
, FD_DESC_SZ
, "%s idle connection", host
);
285 debugs(48, 3, "PconnPool::push: pushed FD " << fd
<< " for " << aKey
);
289 * Return a pconn fd for host:port if available and retriable.
290 * Otherwise, return -1.
292 * We close available persistent connection if the caller transaction is not
293 * retriable to avoid having a growing number of open connections when many
294 * transactions create persistent connections but are not retriable.
297 PconnPool::pop(const char *host
, u_short port
, const char *domain
, Ip::Address
&client_address
, bool isRetriable
)
299 const char * aKey
= key(host
, port
, domain
, client_address
);
301 IdleConnList
*list
= (IdleConnList
*)hash_lookup(table
, aKey
);
303 debugs(48, 3, "PconnPool::pop: lookup for key {" << aKey
<< "} failed.");
306 debugs(48, 3, "PconnPool::pop: found " << hashKeyStr(&list
->hash
) << (isRetriable
?"(to use)":"(to kill)") );
309 int fd
= list
->findUseableFD(); // search from the end. skip pending reads.
312 list
->clearHandlers(fd
);
314 /* might delete list */
315 if (list
->removeFD(fd
) && !isRetriable
) {
325 PconnPool::unlinkList(IdleConnList
*list
) const
327 hash_remove_link(table
, &list
->hash
);
331 PconnPool::count(int uses
)
333 if (uses
>= PCONN_HIST_SZ
)
334 uses
= PCONN_HIST_SZ
- 1;
339 /* ========== PconnModule ============================================ */
342 * This simple class exists only for the cache manager
345 PconnModule::PconnModule() : pools(NULL
), poolCount(0)
347 pools
= (PconnPool
**) xcalloc(MAX_NUM_PCONN_POOLS
, sizeof(*pools
));
348 pconn_fds_pool
= memPoolCreate("pconn_fds", PCONN_FDS_SZ
* sizeof(int));
349 debugs(48, 0, "persistent connection module initialized");
350 registerWithCacheManager();
354 PconnModule::GetInstance()
356 if (instance
== NULL
)
357 instance
= new PconnModule
;
363 PconnModule::registerWithCacheManager(void)
365 CacheManager::GetInstance()->
366 registerAction("pconn",
367 "Persistent Connection Utilization Histograms",
376 assert(poolCount
< MAX_NUM_PCONN_POOLS
);
377 *(pools
+poolCount
) = aPool
;
382 PconnModule::dump(StoreEntry
*e
)
386 for (i
= 0; i
< poolCount
; i
++) {
387 storeAppendPrintf(e
, "\n Pool %d Stats\n", i
);
388 (*(pools
+i
))->dumpHist(e
);
389 storeAppendPrintf(e
, "\n Pool %d Hash Table\n",i
);
390 (*(pools
+i
))->dumpHash(e
);
395 PconnModule::DumpWrapper(StoreEntry
*e
)
397 PconnModule::GetInstance()->dump(e
);