]> git.ipfire.org Git - thirdparty/squid.git/blame - src/pconn.cc
NoNewGlobals for MapLabel (#1746)
[thirdparty/squid.git] / src / pconn.cc
CommitLineData
51fdcbd5 1/*
b8ae064d 2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
e25c139f 3 *
bbc27441
AJ
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
51fdcbd5 7 */
603a02fd 8
bbc27441
AJ
9/* DEBUG: section 48 Persistent Connections */
10
582c2af2 11#include "squid.h"
e8dca475 12#include "CachePeer.h"
582c2af2 13#include "comm.h"
e0d28505 14#include "comm/Connection.h"
7e66d5e2 15#include "comm/Read.h"
c4ad1349 16#include "fd.h"
582c2af2
FC
17#include "fde.h"
18#include "globals.h"
a750e510 19#include "mgr/Registration.h"
e8dca475 20#include "neighbors.h"
781ce8ff 21#include "pconn.h"
e8dca475 22#include "PeerPoolMgr.h"
4d5904f7 23#include "SquidConfig.h"
582c2af2 24#include "Store.h"
603a02fd 25
f53969cc 26#define PCONN_FDS_SZ 8 /* pconn set size, increase for better memcache hit rate */
62e76326 27
a1b1756c 28//TODO: re-attach to MemPools. WAS: static Mem::Allocator *pconn_fds_pool = nullptr;
aee3523a 29PconnModule * PconnModule::instance = nullptr;
781ce8ff 30CBDATA_CLASS_INIT(IdleConnList);
fa80a8ef 31
781ce8ff 32/* ========== IdleConnList ============================================ */
603a02fd 33
a27fcaed 34IdleConnList::IdleConnList(const char *aKey, PconnPool *thePool) :
f53969cc
SM
35 capacity_(PCONN_FDS_SZ),
36 size_(0),
37 parent_(thePool)
603a02fd 38{
a27fcaed
CT
39 //Initialize hash_link members
40 key = xstrdup(aKey);
aee3523a 41 next = nullptr;
a27fcaed 42
e3981652 43 theList_ = new Comm::ConnectionPointer[capacity_];
a27fcaed 44
b856803f 45 registerRunner();
a27fcaed 46
e3981652 47// TODO: re-attach to MemPools. WAS: theList = (?? *)pconn_fds_pool->alloc();
603a02fd 48}
49
781ce8ff 50IdleConnList::~IdleConnList()
603a02fd 51{
9815f129
AJ
52 if (parent_)
53 parent_->unlinkList(this);
62e76326 54
e8dca475 55 if (size_) {
aee3523a 56 parent_ = nullptr; // prevent reentrant notifications and deletions
e8dca475
CT
57 closeN(size_);
58 }
59
e3981652 60 delete[] theList_;
62e76326 61
a27fcaed 62 xfree(key);
603a02fd 63}
64
80463bb4
AJ
65/** Search the list. Matches by FD socket number.
66 * Performed from the end of list where newest entries are.
67 *
68 * \retval <0 The connection is not listed
69 * \retval >=0 The connection array index
70 */
781ce8ff 71int
80463bb4 72IdleConnList::findIndexOf(const Comm::ConnectionPointer &conn) const
603a02fd 73{
e3981652
AJ
74 for (int index = size_ - 1; index >= 0; --index) {
75 if (conn->fd == theList_[index]->fd) {
bf95c10a 76 debugs(48, 3, "found " << conn << " at index " << index);
139d9221 77 return index;
80463bb4 78 }
603a02fd 79 }
62e76326 80
bf95c10a 81 debugs(48, 2, conn << " NOT FOUND!");
781ce8ff 82 return -1;
c4b7a5a9 83}
84
80463bb4 85/** Remove the entry at specified index.
a8c28b85 86 * May perform a shuffle of list entries to fill the gap.
80463bb4
AJ
87 * \retval false The index is not an in-use entry.
88 */
68720ca2 89bool
80463bb4 90IdleConnList::removeAt(int index)
c4b7a5a9 91{
e3981652 92 if (index < 0 || index >= size_)
68720ca2 93 return false;
c4b7a5a9 94
80463bb4 95 // shuffle the remaining entries to fill the new gap.
5db6bf73 96 for (; index < size_ - 1; ++index)
e3981652 97 theList_[index] = theList_[index + 1];
aee3523a 98 theList_[--size_] = nullptr;
62e76326 99
9815f129 100 if (parent_) {
983983ce 101 parent_->noteConnectionRemoved();
4eb4fb17 102 if (size_ == 0) {
a27fcaed 103 debugs(48, 3, "deleting " << hashKeyStr(this));
4eb4fb17
CT
104 delete this;
105 }
8ea9f779 106 }
2dba5b8e 107
9815f129 108 return true;
f915be8d
AJ
109}
110
111// almost a duplicate of removeFD. But drops multiple entries.
112void
113IdleConnList::closeN(size_t n)
114{
115 if (n < 1) {
bf95c10a 116 debugs(48, 2, "Nothing to do.");
f915be8d 117 return;
8dc322e4 118 } else if (n >= (size_t)size_) {
bf95c10a 119 debugs(48, 2, "Closing all entries.");
8dc322e4 120 while (size_ > 0) {
4eb4fb17 121 const Comm::ConnectionPointer conn = theList_[--size_];
aee3523a 122 theList_[size_] = nullptr;
9815f129
AJ
123 clearHandlers(conn);
124 conn->close();
125 if (parent_)
126 parent_->noteConnectionRemoved();
f915be8d 127 }
8dc322e4 128 } else { //if (n < size_)
bf95c10a 129 debugs(48, 2, "Closing " << n << " of " << size_ << " entries.");
f915be8d 130
8dc322e4 131 size_t index;
f915be8d 132 // ensure the first N entries are closed
5db6bf73 133 for (index = 0; index < n; ++index) {
8dc322e4 134 const Comm::ConnectionPointer conn = theList_[index];
aee3523a 135 theList_[index] = nullptr;
9815f129
AJ
136 clearHandlers(conn);
137 conn->close();
138 if (parent_)
139 parent_->noteConnectionRemoved();
f915be8d
AJ
140 }
141 // shuffle the list N down.
5db6bf73 142 for (index = 0; index < (size_t)size_ - n; ++index) {
8dc322e4 143 theList_[index] = theList_[index + n];
f915be8d
AJ
144 }
145 // ensure the last N entries are unset
8dc322e4 146 while (index < ((size_t)size_)) {
aee3523a 147 theList_[index] = nullptr;
f412b2d6 148 ++index;
f915be8d 149 }
8dc322e4 150 size_ -= n;
f915be8d
AJ
151 }
152
9815f129 153 if (parent_ && size_ == 0) {
a27fcaed 154 debugs(48, 3, "deleting " << hashKeyStr(this));
781ce8ff 155 delete this;
156 }
c4b7a5a9 157}
158
781ce8ff 159void
642a305c 160IdleConnList::clearHandlers(const Comm::ConnectionPointer &conn)
781ce8ff 161{
bf95c10a 162 debugs(48, 3, "removing close handler for " << conn);
80463bb4 163 comm_read_cancel(conn->fd, IdleConnList::Read, this);
8d77a37c 164 commUnsetConnTimeout(conn);
781ce8ff 165}
62e76326 166
781ce8ff 167void
642a305c 168IdleConnList::push(const Comm::ConnectionPointer &conn)
c4b7a5a9 169{
e3981652 170 if (size_ == capacity_) {
bf95c10a 171 debugs(48, 3, "growing idle Connection array");
e3981652
AJ
172 capacity_ <<= 1;
173 const Comm::ConnectionPointer *oldList = theList_;
174 theList_ = new Comm::ConnectionPointer[capacity_];
5db6bf73 175 for (int index = 0; index < size_; ++index)
e3981652 176 theList_[index] = oldList[index];
781ce8ff 177
642a305c 178 delete[] oldList;
781ce8ff 179 }
62e76326 180
983983ce
AJ
181 if (parent_)
182 parent_->noteConnectionAdded();
2dba5b8e 183
f412b2d6
FC
184 theList_[size_] = conn;
185 ++size_;
8d77a37c 186 AsyncCall::Pointer readCall = commCbCall(5,4, "IdleConnList::Read",
dc49061a 187 CommIoCbPtrFun(IdleConnList::Read, this));
8d77a37c 188 comm_read(conn, fakeReadBuf_, sizeof(fakeReadBuf_), readCall);
4eb4fb17 189 AsyncCall::Pointer timeoutCall = commCbCall(5,4, "IdleConnList::Timeout",
dc49061a 190 CommTimeoutCbPtrFun(IdleConnList::Timeout, this));
c5c06f02 191 commSetConnTimeout(conn, conn->timeLeft(Config.Timeout.serverIdlePconn), timeoutCall);
603a02fd 192}
193
a8c28b85
AJ
194/// Determine whether an entry in the idle list is available for use.
195/// Returns false if the entry is unset, closed or closing.
196bool
197IdleConnList::isAvailable(int i) const
198{
199 const Comm::ConnectionPointer &conn = theList_[i];
200
201 // connection already closed. useless.
202 if (!Comm::IsConnOpen(conn))
203 return false;
204
205 // our connection early-read/close handler is scheduled to run already. unsafe
206 if (!COMMIO_FD_READCB(conn->fd)->active())
207 return false;
208
209 return true;
210}
211
9815f129
AJ
212Comm::ConnectionPointer
213IdleConnList::pop()
214{
5e263176 215 for (int i=size_-1; i>=0; --i) {
9815f129 216
a8c28b85
AJ
217 if (!isAvailable(i))
218 continue;
219
220 // our connection timeout handler is scheduled to run already. unsafe for now.
221 // TODO: cancel the pending timeout callback and allow re-use of the conn.
aee3523a 222 if (fd_table[theList_[i]->fd].timeoutHandler == nullptr)
9815f129
AJ
223 continue;
224
225 // finally, a match. pop and return it.
226 Comm::ConnectionPointer result = theList_[i];
f7605425 227 clearHandlers(result);
9815f129
AJ
228 /* may delete this */
229 removeAt(i);
230 return result;
231 }
232
233 return Comm::ConnectionPointer();
603a02fd 234}
235
781ce8ff 236/*
237 * XXX this routine isn't terribly efficient - if there's a pending
238 * read event (which signifies the fd will close in the next IO loop!)
239 * we ignore the FD and move onto the next one. This means, as an example,
240 * if we have a lot of FDs open to a very popular server and we get a bunch
241 * of requests JUST as they timeout (say, it shuts down) we'll be wasting
242 * quite a bit of CPU. Just keep it in mind.
243 */
642a305c 244Comm::ConnectionPointer
a27fcaed 245IdleConnList::findUseable(const Comm::ConnectionPointer &aKey)
603a02fd 246{
e3981652 247 assert(size_);
62e76326 248
e884bbde 249 // small optimization: do the constant bool tests only once.
a27fcaed
CT
250 const bool keyCheckAddr = !aKey->local.isAnyAddr();
251 const bool keyCheckPort = aKey->local.port() > 0;
e884bbde 252
5e263176 253 for (int i=size_-1; i>=0; --i) {
139d9221 254
a8c28b85 255 if (!isAvailable(i))
139d9221
AJ
256 continue;
257
61beade2 258 // local end port is required, but do not match.
a27fcaed 259 if (keyCheckPort && aKey->local.port() != theList_[i]->local.port())
139d9221
AJ
260 continue;
261
262 // local address is required, but does not match.
a27fcaed 263 if (keyCheckAddr && aKey->local.matchIPAddr(theList_[i]->local) != 0)
139d9221
AJ
264 continue;
265
a8c28b85
AJ
266 // our connection timeout handler is scheduled to run already. unsafe for now.
267 // TODO: cancel the pending timeout callback and allow re-use of the conn.
aee3523a 268 if (fd_table[theList_[i]->fd].timeoutHandler == nullptr)
a8c28b85
AJ
269 continue;
270
80463bb4 271 // finally, a match. pop and return it.
e3981652 272 Comm::ConnectionPointer result = theList_[i];
f7605425 273 clearHandlers(result);
80463bb4
AJ
274 /* may delete this */
275 removeAt(i);
276 return result;
781ce8ff 277 }
278
80463bb4 279 return Comm::ConnectionPointer();
603a02fd 280}
281
a8c28b85
AJ
282/* might delete list */
283void
284IdleConnList::findAndClose(const Comm::ConnectionPointer &conn)
285{
286 const int index = findIndexOf(conn);
287 if (index >= 0) {
e8dca475
CT
288 if (parent_)
289 parent_->notifyManager("idle conn closure");
f7605425 290 clearHandlers(conn);
a8c28b85
AJ
291 /* might delete this */
292 removeAt(index);
a8c28b85
AJ
293 conn->close();
294 }
295}
296
781ce8ff 297void
ced8def3 298IdleConnList::Read(const Comm::ConnectionPointer &conn, char *, size_t len, Comm::Flag flag, int, void *data)
603a02fd 299{
bf95c10a 300 debugs(48, 3, len << " bytes from " << conn);
62e76326 301
c8407295 302 if (flag == Comm::ERR_CLOSING) {
bf95c10a 303 debugs(48, 3, "Comm::ERR_CLOSING from " << conn);
c8407295 304 /* Bail out on Comm::ERR_CLOSING - may happen when shutdown aborts our idle FD */
62e76326 305 return;
c4b7a5a9 306 }
307
781ce8ff 308 IdleConnList *list = (IdleConnList *) data;
a8c28b85
AJ
309 /* may delete list/data */
310 list->findAndClose(conn);
603a02fd 311}
312
781ce8ff 313void
8d77a37c 314IdleConnList::Timeout(const CommTimeoutCbParams &io)
5ba18ba6 315{
bf95c10a 316 debugs(48, 3, io.conn);
8d77a37c 317 IdleConnList *list = static_cast<IdleConnList *>(io.data);
a8c28b85
AJ
318 /* may delete list/data */
319 list->findAndClose(io.conn);
781ce8ff 320}
62e76326 321
a27fcaed
CT
322void
323IdleConnList::endingShutdown()
324{
325 closeN(size_);
326}
327
781ce8ff 328/* ========== PconnPool PRIVATE FUNCTIONS ============================================ */
62e76326 329
781ce8ff 330const char *
642a305c 331PconnPool::key(const Comm::ConnectionPointer &destLink, const char *domain)
781ce8ff 332{
06093389 333 LOCAL_ARRAY(char, buf, SQUIDHOSTNAMELEN * 3 + 10);
781ce8ff 334
4dd643d5 335 destLink->remote.toUrl(buf, SQUIDHOSTNAMELEN * 3 + 10);
55622953
CT
336
337 // when connecting through a cache_peer, ignore the final destination
338 if (destLink->getPeer())
339 domain = nullptr;
340
642a305c 341 if (domain) {
80463bb4 342 const int used = strlen(buf);
642a305c
AJ
343 snprintf(buf+used, SQUIDHOSTNAMELEN * 3 + 10-used, "/%s", domain);
344 }
345
346 debugs(48,6,"PconnPool::key(" << destLink << ", " << (domain?domain:"[no domain]") << ") is {" << buf << "}" );
781ce8ff 347 return buf;
348}
62e76326 349
781ce8ff 350void
642a305c 351PconnPool::dumpHist(StoreEntry * e) const
781ce8ff 352{
5ba18ba6 353 storeAppendPrintf(e,
781ce8ff 354 "%s persistent connection counts:\n"
62e76326 355 "\n"
e87619bc
AJ
356 "\t Requests\t Connection Count\n"
357 "\t --------\t ----------------\n",
781ce8ff 358 descr);
62e76326 359
5db6bf73 360 for (int i = 0; i < PCONN_HIST_SZ; ++i) {
781ce8ff 361 if (hist[i] == 0)
62e76326 362 continue;
363
e87619bc 364 storeAppendPrintf(e, "\t%d\t%d\n", i, hist[i]);
5ba18ba6 365 }
366}
603a02fd 367
06093389 368void
642a305c 369PconnPool::dumpHash(StoreEntry *e) const
06093389 370{
06093389
AJ
371 hash_table *hid = table;
372 hash_first(hid);
373
642a305c 374 int i = 0;
e8dca475 375 for (hash_link *walker = hash_next(hid); walker; walker = hash_next(hid)) {
e87619bc 376 storeAppendPrintf(e, "\t item %d:\t%s\n", i, (char *)(walker->key));
5db6bf73 377 ++i;
06093389
AJ
378 }
379}
380
781ce8ff 381/* ========== PconnPool PUBLIC FUNCTIONS ============================================ */
603a02fd 382
e8dca475 383PconnPool::PconnPool(const char *aDescr, const CbcPointer<PeerPoolMgr> &aMgr):
aee3523a 384 table(nullptr), descr(aDescr),
f53969cc
SM
385 mgr(aMgr),
386 theCount(0)
603a02fd 387{
ed7f5615 388 int i;
30abd221 389 table = hash_create((HASHCMP *) strcmp, 229, hash_string);
62e76326 390
5db6bf73 391 for (i = 0; i < PCONN_HIST_SZ; ++i)
781ce8ff 392 hist[i] = 0;
62e76326 393
7a56c509
AJ
394 PconnModule::GetInstance()->add(this);
395}
396
e8dca475
CT
397static void
398DeleteIdleConnList(void *hashItem)
399{
af834148 400 delete static_cast<IdleConnList*>(hashItem);
e8dca475
CT
401}
402
7a56c509
AJ
403PconnPool::~PconnPool()
404{
e8dca475
CT
405 PconnModule::GetInstance()->remove(this);
406 hashFreeItems(table, &DeleteIdleConnList);
7a56c509 407 hashFreeMemory(table);
aee3523a 408 descr = nullptr;
603a02fd 409}
410
411void
642a305c 412PconnPool::push(const Comm::ConnectionPointer &conn, const char *domain)
603a02fd 413{
9e008dda 414 if (fdUsageHigh()) {
bf95c10a 415 debugs(48, 3, "Not many unused FDs");
80463bb4 416 conn->close();
62e76326 417 return;
9e008dda 418 } else if (shutting_down) {
80463bb4 419 conn->close();
bf95c10a 420 debugs(48, 3, "Squid is shutting down. Refusing to do anything");
62e76326 421 return;
47c9d579 422 }
e8dca475 423 // TODO: also close used pconns if we exceed peer max-conn limit
62e76326 424
642a305c
AJ
425 const char *aKey = key(conn, domain);
426 IdleConnList *list = (IdleConnList *) hash_lookup(table, aKey);
62e76326 427
aee3523a 428 if (list == nullptr) {
781ce8ff 429 list = new IdleConnList(aKey, this);
a27fcaed
CT
430 debugs(48, 3, "new IdleConnList for {" << hashKeyStr(list) << "}" );
431 hash_join(table, list);
06093389 432 } else {
a27fcaed 433 debugs(48, 3, "found IdleConnList for {" << hashKeyStr(list) << "}" );
603a02fd 434 }
62e76326 435
642a305c
AJ
436 list->push(conn);
437 assert(!comm_has_incomplete_write(conn->fd));
781ce8ff 438
642a305c 439 LOCAL_ARRAY(char, desc, FD_DESC_SZ);
97b32442 440 snprintf(desc, FD_DESC_SZ, "Idle server: %s", aKey);
642a305c 441 fd_note(conn->fd, desc);
bf95c10a 442 debugs(48, 3, "pushed " << conn << " for " << aKey);
e8dca475
CT
443
444 // successful push notifications resume multi-connection opening sequence
445 notifyManager("push");
603a02fd 446}
447
80463bb4 448Comm::ConnectionPointer
e8dca475 449PconnPool::pop(const Comm::ConnectionPointer &dest, const char *domain, bool keepOpen)
603a02fd 450{
55622953
CT
451 // always call shared pool first because we need to close an idle
452 // connection there if we have to use a standby connection.
453 if (const auto direct = popStored(dest, domain, keepOpen))
454 return direct;
455
456 // either there was no pconn to pop or this is not a retriable xaction
457 if (const auto peer = dest->getPeer()) {
458 if (peer->standby.pool)
459 return peer->standby.pool->popStored(dest, domain, true);
460 }
e8dca475 461
55622953
CT
462 return nullptr;
463}
464
465/// implements pop() API while disregarding peer standby pools
466/// \returns an open connection or nil
467Comm::ConnectionPointer
468PconnPool::popStored(const Comm::ConnectionPointer &dest, const char *domain, const bool keepOpen)
469{
e8dca475 470 const char * aKey = key(dest, domain);
62e76326 471
06093389 472 IdleConnList *list = (IdleConnList *)hash_lookup(table, aKey);
aee3523a 473 if (list == nullptr) {
bf95c10a 474 debugs(48, 3, "lookup for key {" << aKey << "} failed.");
e8dca475 475 // failure notifications resume standby conn creation after fdUsageHigh
55622953 476 notifyManager("pop lookup failure");
80463bb4 477 return Comm::ConnectionPointer();
af6a12ee 478 } else {
a27fcaed 479 debugs(48, 3, "found " << hashKeyStr(list) <<
e8dca475 480 (keepOpen ? " to use" : " to kill"));
06093389 481 }
781ce8ff 482
55622953
CT
483 if (const auto popped = list->findUseable(dest)) { // may delete list
484 // successful pop notifications replenish standby connections pool
485 notifyManager("pop");
486
487 if (keepOpen)
488 return popped;
489
e8dca475 490 popped->close();
55622953
CT
491 return Comm::ConnectionPointer();
492 }
e8dca475 493
55622953
CT
494 // failure notifications resume standby conn creation after fdUsageHigh
495 notifyManager("pop usability failure");
496 return Comm::ConnectionPointer();
e8dca475 497}
781ce8ff 498
e8dca475
CT
499void
500PconnPool::notifyManager(const char *reason)
501{
502 if (mgr.valid())
503 PeerPoolMgr::Checkpoint(mgr, reason);
603a02fd 504}
ed7f5615 505
506void
e8dca475 507PconnPool::closeN(int n)
2dba5b8e 508{
e8dca475
CT
509 hash_table *hid = table;
510 hash_first(hid);
511
512 // close N connections, one per list, to treat all lists "fairly"
513 for (int i = 0; i < n && count(); ++i) {
514
515 hash_link *current = hash_next(hid);
516 if (!current) {
517 hash_first(hid);
518 current = hash_next(hid);
519 Must(current); // must have one because the count() was positive
520 }
521
522 // may delete current
af834148 523 static_cast<IdleConnList*>(current)->closeN(1);
e8dca475 524 }
2dba5b8e
CT
525}
526
527void
528PconnPool::unlinkList(IdleConnList *list)
ed7f5615 529{
2dba5b8e
CT
530 theCount -= list->count();
531 assert(theCount >= 0);
a27fcaed 532 hash_remove_link(table, list);
781ce8ff 533}
534
535void
2dba5b8e 536PconnPool::noteUses(int uses)
781ce8ff 537{
538 if (uses >= PCONN_HIST_SZ)
539 uses = PCONN_HIST_SZ - 1;
540
5db6bf73 541 ++hist[uses];
781ce8ff 542}
543
544/* ========== PconnModule ============================================ */
545
546/*
547 * This simple class exists only for the cache manager
548 */
549
e8dca475 550PconnModule::PconnModule(): pools()
781ce8ff 551{
6852be71 552 registerWithCacheManager();
781ce8ff 553}
554
62ee09ca 555PconnModule *
556PconnModule::GetInstance()
557{
aee3523a 558 if (instance == nullptr)
62ee09ca 559 instance = new PconnModule;
560
561 return instance;
562}
563
564void
15fab853 565PconnModule::registerWithCacheManager(void)
62ee09ca 566{
8822ebee 567 Mgr::RegisterAction("pconn",
d9fc6862
A
568 "Persistent Connection Utilization Histograms",
569 DumpWrapper, 0, 1);
62ee09ca 570}
571
781ce8ff 572void
642a305c 573PconnModule::add(PconnPool *aPool)
781ce8ff 574{
e8dca475 575 pools.insert(aPool);
781ce8ff 576}
577
578void
e8dca475 579PconnModule::remove(PconnPool *aPool)
781ce8ff 580{
e8dca475
CT
581 pools.erase(aPool);
582}
781ce8ff 583
e8dca475
CT
584void
585PconnModule::dump(StoreEntry *e)
586{
587 typedef Pools::const_iterator PCI;
588 int i = 0; // TODO: Why number pools if they all have names?
589 for (PCI p = pools.begin(); p != pools.end(); ++p, ++i) {
590 // TODO: Let each pool dump itself the way it wants to.
06093389 591 storeAppendPrintf(e, "\n Pool %d Stats\n", i);
e8dca475 592 (*p)->dumpHist(e);
06093389 593 storeAppendPrintf(e, "\n Pool %d Hash Table\n",i);
e8dca475 594 (*p)->dumpHash(e);
781ce8ff 595 }
596}
597
62ee09ca 598void
599PconnModule::DumpWrapper(StoreEntry *e)
781ce8ff 600{
62ee09ca 601 PconnModule::GetInstance()->dump(e);
ed7f5615 602}
f53969cc 603