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"
38 #include "comm/Connection.h"
43 #define PCONN_FDS_SZ 8 /* pconn set size, increase for better memcache hit rate */
45 //TODO: re-attach to MemPools. WAS: static MemAllocator *pconn_fds_pool = NULL;
46 PconnModule
* PconnModule::instance
= NULL
;
47 CBDATA_CLASS_INIT(IdleConnList
);
49 /* ========== IdleConnList ============================================ */
51 IdleConnList::IdleConnList(const char *key
, PconnPool
*thePool
) :
52 nfds_alloc(PCONN_FDS_SZ
),
56 hash
.key
= xstrdup(key
);
57 theList
= new Comm::ConnectionPointer
[nfds_alloc
];
58 // TODO: re-attach to MemPools. WAS: fds = (int *)pconn_fds_pool->alloc();
61 IdleConnList::~IdleConnList()
63 parent
->unlinkList(this);
65 /* TODO: re-attach to MemPools.
66 if (nfds_alloc == PCONN_FDS_SZ)
67 pconn_fds_pool->freeOne(theList);
76 IdleConnList::findIndex(const Comm::ConnectionPointer
&conn
)
78 for (int index
= nfds
- 1; index
>= 0; --index
) {
79 if (conn
->fd
== theList
[index
]->fd
)
87 IdleConnList::remove(const Comm::ConnectionPointer
&conn
)
89 int index
= findIndex(conn
);
91 debugs(48, 2, HERE
<< conn
<< " NOT FOUND!");
94 debugs(48, 3, HERE
<< "found " << conn
<< " at index " << index
);
96 for (; index
< nfds
- 1; index
++)
97 theList
[index
] = theList
[index
+ 1];
100 debugs(48, 3, "IdleConnList::removeFD: deleting " << hashKeyStr(&hash
));
107 IdleConnList::clearHandlers(const Comm::ConnectionPointer
&conn
)
109 comm_read_cancel(conn
->fd
, IdleConnList::read
, this);
110 commSetTimeout(conn
->fd
, -1, NULL
, NULL
);
114 IdleConnList::push(const Comm::ConnectionPointer
&conn
)
116 if (nfds
== nfds_alloc
) {
117 debugs(48, 3, "IdleConnList::push: growing FD array");
119 const Comm::ConnectionPointer
*oldList
= theList
;
120 theList
= new Comm::ConnectionPointer
[nfds_alloc
];
121 for (int index
= 0; index
< nfds
; index
++)
122 theList
[index
] = oldList
[index
];
124 /* TODO: re-attach to MemPools.
125 if (nfds == PCONN_FDS_SZ)
126 pconn_fds_pool->freeOne(oldList);
132 theList
[nfds
++] = conn
;
133 comm_read(conn
, fakeReadBuf
, sizeof(fakeReadBuf
), IdleConnList::read
, this);
134 commSetTimeout(conn
->fd
, Config
.Timeout
.pconn
, IdleConnList::timeout
, this);
138 * XXX this routine isn't terribly efficient - if there's a pending
139 * read event (which signifies the fd will close in the next IO loop!)
140 * we ignore the FD and move onto the next one. This means, as an example,
141 * if we have a lot of FDs open to a very popular server and we get a bunch
142 * of requests JUST as they timeout (say, it shuts down) we'll be wasting
143 * quite a bit of CPU. Just keep it in mind.
145 Comm::ConnectionPointer
146 IdleConnList::findUseable(const Comm::ConnectionPointer
&key
)
150 for (int i
=nfds
-1; i
>=0; i
--) {
152 // callback pending indicates that remote end of the conn has just closed.
153 if (comm_has_pending_read_callback(theList
[i
]->fd
))
156 // local end port is required, but dont match.
157 if (key
->local
.GetPort() > 0 && key
->local
.GetPort() != theList
[i
]->local
.GetPort())
160 // local address is required, but does not match.
161 if (!key
->local
.IsAnyAddr() && key
->local
.matchIPAddr(theList
[i
]->local
) != 0)
172 IdleConnList::read(const Comm::ConnectionPointer
&conn
, char *buf
, size_t len
, comm_err_t flag
, int xerrno
, void *data
)
174 debugs(48, 3, HERE
<< len
<< " bytes from " << conn
);
176 if (flag
== COMM_ERR_CLOSING
) {
177 /* Bail out early on COMM_ERR_CLOSING - close handlers will tidy up for us */
181 IdleConnList
*list
= (IdleConnList
*) data
;
182 /* might delete list */
183 if (list
&& list
->remove(conn
)) {
184 Comm::ConnectionPointer nonConst
= conn
;
190 IdleConnList::timeout(int fd
, void *data
)
192 debugs(48, 3, "IdleConnList::timeout: FD " << fd
);
193 IdleConnList
*list
= (IdleConnList
*) data
;
194 Comm::ConnectionPointer temp
= new Comm::Connection
; // XXX: transition. make timeouts pass conn in
196 if (list
->remove(temp
)) {
199 temp
->fd
= -1; // XXX: transition. prevent temp erasure double-closing FD until timeout CB passess conn in.
202 /* ========== PconnPool PRIVATE FUNCTIONS ============================================ */
205 PconnPool::key(const Comm::ConnectionPointer
&destLink
, const char *domain
)
207 LOCAL_ARRAY(char, buf
, SQUIDHOSTNAMELEN
* 3 + 10);
209 destLink
->remote
.ToURL(buf
, SQUIDHOSTNAMELEN
* 3 + 10);
211 int used
= strlen(buf
);
212 snprintf(buf
+used
, SQUIDHOSTNAMELEN
* 3 + 10-used
, "/%s", domain
);
215 debugs(48,6,"PconnPool::key(" << destLink
<< ", " << (domain
?domain
:"[no domain]") << ") is {" << buf
<< "}" );
220 PconnPool::dumpHist(StoreEntry
* e
) const
223 "%s persistent connection counts:\n"
227 "\t---- ---------\n",
230 for (int i
= 0; i
< PCONN_HIST_SZ
; i
++) {
234 storeAppendPrintf(e
, "\t%4d %9d\n", i
, hist
[i
]);
239 PconnPool::dumpHash(StoreEntry
*e
) const
241 hash_table
*hid
= table
;
245 for (hash_link
*walker
= hid
->next
; walker
; walker
= hash_next(hid
)) {
246 storeAppendPrintf(e
, "\t item %5d: %s\n", i
++, (char *)(walker
->key
));
250 /* ========== PconnPool PUBLIC FUNCTIONS ============================================ */
252 PconnPool::PconnPool(const char *aDescr
) : table(NULL
), descr(aDescr
)
254 table
= hash_create((HASHCMP
*) strcmp
, 229, hash_string
);
256 for (int i
= 0; i
< PCONN_HIST_SZ
; i
++)
259 PconnModule::GetInstance()->add(this);
262 PconnPool::~PconnPool()
265 hashFreeMemory(table
);
269 PconnPool::push(const Comm::ConnectionPointer
&conn
, const char *domain
)
272 debugs(48, 3, "PconnPool::push: Not many unused FDs");
273 Comm::ConnectionPointer nonConst
= conn
;
276 } else if (shutting_down
) {
277 Comm::ConnectionPointer nonConst
= conn
;
279 debugs(48, 3, "PconnPool::push: Squid is shutting down. Refusing to do anything");
283 const char *aKey
= key(conn
, domain
);
284 IdleConnList
*list
= (IdleConnList
*) hash_lookup(table
, aKey
);
287 list
= new IdleConnList(aKey
, this);
288 debugs(48, 3, "PconnPool::push: new IdleConnList for {" << hashKeyStr(&list
->hash
) << "}" );
289 hash_join(table
, &list
->hash
);
291 debugs(48, 3, "PconnPool::push: found IdleConnList for {" << hashKeyStr(&list
->hash
) << "}" );
295 assert(!comm_has_incomplete_write(conn
->fd
));
297 LOCAL_ARRAY(char, desc
, FD_DESC_SZ
);
298 snprintf(desc
, FD_DESC_SZ
, "Idle: %s", aKey
);
299 fd_note(conn
->fd
, desc
);
300 debugs(48, 3, HERE
<< "pushed " << conn
<< " for " << aKey
);
304 PconnPool::pop(Comm::ConnectionPointer
&destLink
, const char *domain
, bool isRetriable
)
306 const char * aKey
= key(destLink
, domain
);
308 IdleConnList
*list
= (IdleConnList
*)hash_lookup(table
, aKey
);
310 debugs(48, 3, "PconnPool::pop: lookup for key {" << aKey
<< "} failed.");
313 debugs(48, 3, "PconnPool::pop: found " << hashKeyStr(&list
->hash
) << (isRetriable
?"(to use)":"(to kill)") );
316 Comm::ConnectionPointer temp
= list
->findUseable(destLink
);
318 if (Comm::IsConnOpen(temp
)) {
319 list
->clearHandlers(temp
);
321 /* might delete list */
322 if (list
->remove(temp
) && !isRetriable
)
332 PconnPool::unlinkList(IdleConnList
*list
) const
334 hash_remove_link(table
, &list
->hash
);
338 PconnPool::count(int uses
)
340 if (uses
>= PCONN_HIST_SZ
)
341 uses
= PCONN_HIST_SZ
- 1;
346 /* ========== PconnModule ============================================ */
349 * This simple class exists only for the cache manager
352 PconnModule::PconnModule() : pools(NULL
), poolCount(0)
354 pools
= (PconnPool
**) xcalloc(MAX_NUM_PCONN_POOLS
, sizeof(*pools
));
355 //TODO: re-link to MemPools. WAS: pconn_fds_pool = memPoolCreate("pconn_fds", PCONN_FDS_SZ * sizeof(int));
356 debugs(48, 0, "persistent connection module initialized");
357 registerWithCacheManager();
361 PconnModule::GetInstance()
363 if (instance
== NULL
)
364 instance
= new PconnModule
;
370 PconnModule::registerWithCacheManager(void)
372 CacheManager::GetInstance()->
373 registerAction("pconn",
374 "Persistent Connection Utilization Histograms",
380 PconnModule::add(PconnPool
*aPool
)
382 assert(poolCount
< MAX_NUM_PCONN_POOLS
);
383 *(pools
+poolCount
) = aPool
;
388 PconnModule::dump(StoreEntry
*e
)
390 for (int i
= 0; i
< poolCount
; i
++) {
391 storeAppendPrintf(e
, "\n Pool %d Stats\n", i
);
392 (*(pools
+i
))->dumpHist(e
);
393 storeAppendPrintf(e
, "\n Pool %d Hash Table\n",i
);
394 (*(pools
+i
))->dumpHash(e
);
399 PconnModule::DumpWrapper(StoreEntry
*e
)
401 PconnModule::GetInstance()->dump(e
);