]> git.ipfire.org Git - thirdparty/squid.git/blame - src/store_dir.cc
Added stream operator << for StoreEntry to ease debugging.
[thirdparty/squid.git] / src / store_dir.cc
CommitLineData
f1dc9b30 1
2/*
262a0e14 3 * $Id$
f1dc9b30 4 *
5 * DEBUG: section 47 Store Directory Routines
6 * AUTHOR: Duane Wessels
7 *
2b6662ba 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 9 * ----------------------------------------------------------
f1dc9b30 10 *
2b6662ba 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.
f1dc9b30 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.
26ac0430 24 *
f1dc9b30 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.
26ac0430 29 *
f1dc9b30 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
cbdec147 32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 33 *
f1dc9b30 34 */
35
596dddc1 36#include "squid.h"
e6ccf245 37#include "Store.h"
528b2c61 38#include "MemObject.h"
a98bcbee 39#include "SquidMath.h"
985c86bc 40#include "SquidTime.h"
d3b3ab85 41#include "SwapDir.h"
4b981814 42#include "swap_log_op.h"
85407535 43
c0db87f2 44#if HAVE_STATVFS
45#if HAVE_SYS_STATVFS_H
46#include <sys/statvfs.h>
47#endif
ec15e022 48#endif /* HAVE_STATVFS */
49/* statfs() needs <sys/param.h> and <sys/mount.h> on BSD systems */
50#if HAVE_SYS_PARAM_H
51#include <sys/param.h>
203526a1 52#endif
ec15e022 53#if HAVE_SYS_MOUNT_H
54#include <sys/mount.h>
55#endif
56/* Windows and Linux use sys/vfs.h */
6c86a065 57#if HAVE_SYS_VFS_H
58#include <sys/vfs.h>
59#endif
c0db87f2 60
c8f4eac4 61#include "StoreHashIndex.h"
62
65a53c8e 63static STDIRSELECT storeDirSelectSwapDirRoundRobin;
64static STDIRSELECT storeDirSelectSwapDirLeastLoad;
c8f4eac4 65
b07b21cc 66/*
67 * store_dirs_rebuilding is initialized to _1_ as a hack so that
68 * storeDirWriteCleanLogs() doesn't try to do anything unless _all_
69 * cache_dirs have been read. For example, without this hack, Squid
70 * will try to write clean log files if -kparse fails (becasue it
71 * calls fatal()).
72 */
73int StoreController::store_dirs_rebuilding = 1;
bef81ea5 74
c8f4eac4 75StoreController::StoreController() : swapDir (new StoreHashIndex())
76{}
77
78StoreController::~StoreController()
79{}
65a53c8e 80
81/*
82 * This function pointer is set according to 'store_dir_select_algorithm'
83 * in squid.conf.
84 */
85STDIRSELECT *storeDirSelectSwapDir = storeDirSelectSwapDirLeastLoad;
d141c677 86
9838d6c8 87void
c8f4eac4 88StoreController::init()
596dddc1 89{
c8f4eac4 90 swapDir->init();
62e76326 91
65a53c8e 92 if (0 == strcasecmp(Config.store_dir_select_algorithm, "round-robin")) {
62e76326 93 storeDirSelectSwapDir = storeDirSelectSwapDirRoundRobin;
bf8fe701 94 debugs(47, 1, "Using Round Robin store dir selection");
65a53c8e 95 } else {
62e76326 96 storeDirSelectSwapDir = storeDirSelectSwapDirLeastLoad;
bf8fe701 97 debugs(47, 1, "Using Least Load store dir selection");
65a53c8e 98 }
85407535 99}
100
101void
c8f4eac4 102StoreController::createOneStore(Store &aStore)
596dddc1 103{
62e76326 104 /*
154c7949 105 * On Windows, fork() is not available.
106 * The following is a workaround for create store directories sequentially
107 * when running on native Windows port.
108 */
099a1791 109#ifndef _SQUID_MSWIN_
62e76326 110
154c7949 111 if (fork())
62e76326 112 return;
113
099a1791 114#endif
62e76326 115
c8f4eac4 116 aStore.create();
62e76326 117
099a1791 118#ifndef _SQUID_MSWIN_
62e76326 119
154c7949 120 exit(0);
62e76326 121
099a1791 122#endif
154c7949 123}
124
125void
c8f4eac4 126StoreController::create()
154c7949 127{
c8f4eac4 128 swapDir->create();
62e76326 129
099a1791 130#ifndef _SQUID_MSWIN_
62e76326 131
8a1c8f2c 132 pid_t pid;
62e76326 133
b2c141d4 134 do {
62e76326 135 int status;
b2c141d4 136#ifdef _SQUID_NEXT_
62e76326 137
138 pid = wait3(&status, WNOHANG, NULL);
b2c141d4 139#else
62e76326 140
141 pid = waitpid(-1, &status, 0);
b2c141d4 142#endif
62e76326 143
b2c141d4 144 } while (pid > 0 || (pid < 0 && errno == EINTR));
62e76326 145
099a1791 146#endif
596dddc1 147}
148
a8a33c46 149/**
cd748f27 150 * Determine whether the given directory can handle this object
151 * size
152 *
153 * Note: if the object size is -1, then the only swapdirs that
a8a33c46 154 * will return true here are ones that have min and max unset,
cd748f27 155 * ie any-sized-object swapdirs. This is a good thing.
156 */
c8f4eac4 157bool
3e62bd58 158SwapDir::objectSizeIsAcceptable(int64_t objsize) const
cd748f27 159{
a8a33c46 160 // If the swapdir has no range limits, then it definitely can
b6662ffd 161 if (min_objsize <= 0 && max_objsize == -1)
c8f4eac4 162 return true;
d68f43a0 163
164 /*
a8a33c46
A
165 * If the object size is -1 and the storedir has limits we
166 * can't store it there.
d68f43a0 167 */
a8a33c46 168 if (objsize == -1)
c8f4eac4 169 return false;
d68f43a0 170
a8a33c46
A
171 // Else, make sure that the object size will fit.
172 return min_objsize <= objsize && max_objsize > objsize;
cd748f27 173}
174
175
d141c677 176/*
177 * This new selection scheme simply does round-robin on all SwapDirs.
8e8d4f30 178 * A SwapDir is skipped if it is over the max_size (100%) limit, or
179 * overloaded.
d141c677 180 */
181static int
8e8d4f30 182storeDirSelectSwapDirRoundRobin(const StoreEntry * e)
d141c677 183{
184 static int dirn = 0;
185 int i;
8e8d4f30 186 int load;
c8f4eac4 187 RefCount<SwapDir> sd;
62e76326 188
246e6cc1
AJ
189 ssize_t objsize = e->objectLen();
190 if (objsize != -1)
191 objsize += e->mem_obj->swap_hdr_sz;
192
d141c677 193 for (i = 0; i <= Config.cacheSwap.n_configured; i++) {
62e76326 194 if (++dirn >= Config.cacheSwap.n_configured)
195 dirn = 0;
196
c8f4eac4 197 sd = dynamic_cast<SwapDir *>(INDEXSD(dirn));
62e76326 198
199 if (sd->flags.read_only)
200 continue;
201
202 if (sd->cur_size > sd->max_size)
203 continue;
204
246e6cc1 205 if (!sd->objectSizeIsAcceptable(objsize))
62e76326 206 continue;
207
208 /* check for error or overload condition */
209 load = sd->canStore(*e);
210
211 if (load < 0 || load > 1000) {
212 continue;
213 }
214
215 return dirn;
d141c677 216 }
62e76326 217
8e8d4f30 218 return -1;
d141c677 219}
960a01e3 220
a2899918 221/*
cd748f27 222 * Spread load across all of the store directories
223 *
224 * Note: We should modify this later on to prefer sticking objects
225 * in the *tightest fit* swapdir to conserve space, along with the
26ac0430 226 * actual swapdir usage. But for now, this hack will do while
cd748f27 227 * testing, so you should order your swapdirs in the config file
228 * from smallest maxobjsize to unlimited (-1) maxobjsize.
229 *
230 * We also have to choose nleast == nconf since we need to consider
231 * ALL swapdirs, regardless of state. Again, this is a hack while
232 * we sort out the real usefulness of this algorithm.
a2899918 233 */
65a53c8e 234static int
235storeDirSelectSwapDirLeastLoad(const StoreEntry * e)
a2899918 236{
e8dbac8b 237 ssize_t objsize;
8e8d4f30 238 ssize_t most_free = 0, cur_free;
239 ssize_t least_objsize = -1;
240 int least_load = INT_MAX;
cd748f27 241 int load;
242 int dirn = -1;
243 int i;
c8f4eac4 244 RefCount<SwapDir> SD;
cd748f27 245
246 /* Calculate the object size */
707fdc47 247 objsize = e->objectLen();
62e76326 248
cd748f27 249 if (objsize != -1)
62e76326 250 objsize += e->mem_obj->swap_hdr_sz;
251
cd748f27 252 for (i = 0; i < Config.cacheSwap.n_configured; i++) {
c8f4eac4 253 SD = dynamic_cast<SwapDir *>(INDEXSD(i));
62e76326 254 SD->flags.selected = 0;
255 load = SD->canStore(*e);
256
257 if (load < 0 || load > 1000) {
258 continue;
259 }
260
c8f4eac4 261 if (!SD->objectSizeIsAcceptable(objsize))
62e76326 262 continue;
263
264 if (SD->flags.read_only)
265 continue;
266
267 if (SD->cur_size > SD->max_size)
268 continue;
269
270 if (load > least_load)
271 continue;
272
273 cur_free = SD->max_size - SD->cur_size;
274
275 /* If the load is equal, then look in more details */
276 if (load == least_load) {
277 /* closest max_objsize fit */
278
279 if (least_objsize != -1)
280 if (SD->max_objsize > least_objsize || SD->max_objsize == -1)
281 continue;
282
283 /* most free */
284 if (cur_free < most_free)
285 continue;
286 }
287
288 least_load = load;
289 least_objsize = SD->max_objsize;
290 most_free = cur_free;
291 dirn = i;
a2899918 292 }
62e76326 293
ade906c8 294 if (dirn >= 0)
c8f4eac4 295 dynamic_cast<SwapDir *>(INDEXSD(dirn))->flags.selected = 1;
62e76326 296
cd748f27 297 return dirn;
596dddc1 298}
299
b109de6b 300/*
301 * An entry written to the swap log MUST have the following
302 * properties.
303 * 1. It MUST be a public key. It does no good to log
304 * a public ADD, change the key, then log a private
305 * DEL. So we need to log a DEL before we change a
306 * key from public to private.
cd748f27 307 * 2. It MUST have a valid (> -1) swap_filen.
b109de6b 308 */
4683e377 309void
5830cdb3 310storeDirSwapLog(const StoreEntry * e, int op)
4683e377 311{
d3b3ab85 312 assert (e);
d46a87a8 313 assert(!EBIT_TEST(e->flags, KEY_PRIVATE));
cd748f27 314 assert(e->swap_filen >= 0);
6c57e268 315 /*
316 * icons and such; don't write them to the swap log
317 */
62e76326 318
d46a87a8 319 if (EBIT_TEST(e->flags, ENTRY_SPECIAL))
62e76326 320 return;
321
b109de6b 322 assert(op > SWAP_LOG_NOP && op < SWAP_LOG_MAX);
62e76326 323
26ac0430
AJ
324 debugs(20, 3, "storeDirSwapLog: " <<
325 swap_log_op_str[op] << " " <<
326 e->getMD5Text() << " " <<
327 e->swap_dirn << " " <<
bf8fe701 328 std::hex << std::uppercase << std::setfill('0') << std::setw(8) << e->swap_filen);
62e76326 329
c8f4eac4 330 dynamic_cast<SwapDir *>(INDEXSD(e->swap_dirn))->logEntry(*e, op);
331}
332
333void
47f6e231 334StoreController::updateSize(int64_t size, int sign)
c8f4eac4 335{
336 fatal("StoreController has no independent size\n");
5608850b 337}
76fefb77 338
339void
47f6e231 340SwapDir::updateSize(int64_t size, int sign)
76fefb77 341{
6da430a4
AJ
342 int64_t blks = (size + fs.blksize - 1) / fs.blksize;
343 int64_t k = ((blks * fs.blksize) >> 10) * sign;
c8f4eac4 344 cur_size += k;
76fefb77 345 store_swap_size += k;
62e76326 346
0faf70d0 347 if (sign > 0)
62e76326 348 n_disk_objects++;
0faf70d0 349 else if (sign < 0)
62e76326 350 n_disk_objects--;
76fefb77 351}
c932b107 352
353void
c8f4eac4 354StoreController::stat(StoreEntry &output) const
c932b107 355{
c8f4eac4 356 storeAppendPrintf(&output, "Store Directory Statistics:\n");
357 storeAppendPrintf(&output, "Store Entries : %lu\n",
62e76326 358 (unsigned long int)StoreEntry::inUseCount());
12e11a5c
AJ
359 storeAppendPrintf(&output, "Maximum Swap Size : %"PRIu64" KB\n",
360 maxSize());
c8f4eac4 361 storeAppendPrintf(&output, "Current Store Swap Size: %8lu KB\n",
62e76326 362 store_swap_size);
12e11a5c
AJ
363 storeAppendPrintf(&output, "Current Capacity : %"PRId64"%% used, %"PRId64"%% free\n",
364 Math::int64Percent(store_swap_size, maxSize()),
365 Math::int64Percent((maxSize() - store_swap_size), maxSize()));
8a3e7d9e 366 /* FIXME Here we should output memory statistics */
e3ef2b09 367
c8f4eac4 368 /* now the swapDir */
369 swapDir->stat(output);
5d406e78 370}
371
c8f4eac4 372/* if needed, this could be taught to cache the result */
12e11a5c 373uint64_t
c8f4eac4 374StoreController::maxSize() const
f4e3fa54 375{
c8f4eac4 376 /* TODO: include memory cache ? */
377 return swapDir->maxSize();
378}
62e76326 379
12e11a5c 380uint64_t
c8f4eac4 381StoreController::minSize() const
382{
383 /* TODO: include memory cache ? */
384 return swapDir->minSize();
f4e3fa54 385}
386
387void
c8f4eac4 388SwapDir::diskFull()
f4e3fa54 389{
c8f4eac4 390 if (cur_size >= max_size)
62e76326 391 return;
392
c8f4eac4 393 max_size = cur_size;
62e76326 394
c8f4eac4 395 debugs(20, 1, "WARNING: Shrinking cache_dir #" << index << " to " << cur_size << " KB");
f4e3fa54 396}
95dcd2b8 397
398void
399storeDirOpenSwapLogs(void)
400{
d3b3ab85 401 for (int dirn = 0; dirn < Config.cacheSwap.n_configured; ++dirn)
c8f4eac4 402 dynamic_cast<SwapDir *>(INDEXSD(dirn))->openLog();
95dcd2b8 403}
404
405void
406storeDirCloseSwapLogs(void)
407{
d3b3ab85 408 for (int dirn = 0; dirn < Config.cacheSwap.n_configured; ++dirn)
c8f4eac4 409 dynamic_cast<SwapDir *>(INDEXSD(dirn))->closeLog();
95dcd2b8 410}
411
b2c141d4 412/*
413 * storeDirWriteCleanLogs
26ac0430 414 *
b2c141d4 415 * Writes a "clean" swap log file from in-memory metadata.
cd748f27 416 * This is a rewrite of the original function to troll each
417 * StoreDir and write the logs, and flush at the end of
418 * the run. Thanks goes to Eric Stern, since this solution
419 * came out of his COSS code.
b2c141d4 420 */
b2c141d4 421int
422storeDirWriteCleanLogs(int reopen)
95dcd2b8 423{
6a566b9c 424 const StoreEntry *e = NULL;
b2c141d4 425 int n = 0;
62e76326 426
e812ecfc 427 struct timeval start;
428 double dt;
c8f4eac4 429 RefCount<SwapDir> sd;
b2c141d4 430 int dirn;
6a566b9c 431 int notdone = 1;
62e76326 432
bef81ea5 433 if (StoreController::store_dirs_rebuilding) {
bf8fe701 434 debugs(20, 1, "Not currently OK to rewrite swap log.");
435 debugs(20, 1, "storeDirWriteCleanLogs: Operation aborted.");
62e76326 436 return 0;
b2c141d4 437 }
62e76326 438
bf8fe701 439 debugs(20, 1, "storeDirWriteCleanLogs: Starting...");
e812ecfc 440 getCurrentTime();
441 start = current_time;
62e76326 442
b2c141d4 443 for (dirn = 0; dirn < Config.cacheSwap.n_configured; dirn++) {
c8f4eac4 444 sd = dynamic_cast<SwapDir *>(INDEXSD(dirn));
62e76326 445
446 if (sd->writeCleanStart() < 0) {
bf8fe701 447 debugs(20, 1, "log.clean.start() failed for dir #" << sd->index);
62e76326 448 continue;
449 }
6a566b9c 450 }
62e76326 451
e78ef51b 452 /*
453 * This may look inefficient as CPU wise it is more efficient to do this
454 * sequentially, but I/O wise the parallellism helps as it allows more
455 * hdd spindles to be active.
d3b3ab85 456 */
c1dd71ae 457 while (notdone) {
62e76326 458 notdone = 0;
459
460 for (dirn = 0; dirn < Config.cacheSwap.n_configured; dirn++) {
c8f4eac4 461 sd = dynamic_cast<SwapDir *>(INDEXSD(dirn));
62e76326 462
463 if (NULL == sd->cleanLog)
464 continue;
465
466 e = sd->cleanLog->nextEntry();
467
468 if (!e)
469 continue;
470
471 notdone = 1;
472
473 if (!sd->canLog(*e))
474 continue;
475
476 sd->cleanLog->write(*e);
477
478 if ((++n & 0xFFFF) == 0) {
479 getCurrentTime();
bf8fe701 480 debugs(20, 1, " " << std::setw(7) << n <<
481 " entries written so far.");
62e76326 482 }
483 }
6a566b9c 484 }
62e76326 485
6a566b9c 486 /* Flush */
d3b3ab85 487 for (dirn = 0; dirn < Config.cacheSwap.n_configured; dirn++)
c8f4eac4 488 dynamic_cast<SwapDir *>(INDEXSD(dirn))->writeCleanDone();
62e76326 489
b2c141d4 490 if (reopen)
62e76326 491 storeDirOpenSwapLogs();
492
e812ecfc 493 getCurrentTime();
62e76326 494
e812ecfc 495 dt = tvSubDsec(start, current_time);
62e76326 496
bf8fe701 497 debugs(20, 1, " Finished. Wrote " << n << " entries.");
498 debugs(20, 1, " Took "<< std::setw(3)<< std::setprecision(2) << dt <<
499 " seconds ("<< std::setw(6) << ((double) n / (dt > 0.0 ? dt : 1.0)) << " entries/sec).");
62e76326 500
62e76326 501
b2c141d4 502 return n;
95dcd2b8 503}
d141c677 504
c8f4eac4 505StoreSearch *
30abd221 506StoreController::search(String const url, HttpRequest *request)
c8f4eac4 507{
508 /* cheat, for now you can't search the memory hot cache */
509 return swapDir->search(url, request);
510}
511
512StorePointer
513StoreHashIndex::store(int const x) const
514{
515 return INDEXSD(x);
516}
517
cd748f27 518void
c8f4eac4 519StoreController::sync(void)
cd748f27 520{
c8f4eac4 521 /* sync mem cache? */
522 swapDir->sync();
cd748f27 523}
524
525/*
26ac0430 526 * handle callbacks all avaliable fs'es
cd748f27 527 */
c8f4eac4 528int
529StoreController::callback()
cd748f27 530{
1d5161bd 531 /* This will likely double count. Thats ok. */
532 PROF_start(storeDirCallback);
533
c8f4eac4 534 /* mem cache callbacks ? */
535 int result = swapDir->callback();
1d5161bd 536
537 PROF_stop(storeDirCallback);
c8f4eac4 538
539 return result;
d141c677 540}
90d42c28 541
542int
543storeDirGetBlkSize(const char *path, int *blksize)
544{
545#if HAVE_STATVFS
62e76326 546
90d42c28 547 struct statvfs sfs;
62e76326 548
90d42c28 549 if (statvfs(path, &sfs)) {
bf8fe701 550 debugs(50, 1, "" << path << ": " << xstrerror());
62e76326 551 *blksize = 2048;
552 return 1;
90d42c28 553 }
62e76326 554
6759a7aa 555 *blksize = (int) sfs.f_frsize;
90d42c28 556#else
62e76326 557
90d42c28 558 struct statfs sfs;
62e76326 559
90d42c28 560 if (statfs(path, &sfs)) {
bf8fe701 561 debugs(50, 1, "" << path << ": " << xstrerror());
62e76326 562 *blksize = 2048;
563 return 1;
90d42c28 564 }
62e76326 565
90d42c28 566 *blksize = (int) sfs.f_bsize;
6759a7aa 567#endif
4b3af09f 568 /*
569 * Sanity check; make sure we have a meaningful value.
570 */
62e76326 571
d5b72fe7 572 if (*blksize < 512)
62e76326 573 *blksize = 2048;
574
90d42c28 575 return 0;
576}
781d6656 577
578#define fsbtoblk(num, fsbs, bs) \
579 (((fsbs) != 0 && (fsbs) < (bs)) ? \
580 (num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs)))
581int
582storeDirGetUFSStats(const char *path, int *totl_kb, int *free_kb, int *totl_in, int *free_in)
583{
584#if HAVE_STATVFS
62e76326 585
781d6656 586 struct statvfs sfs;
62e76326 587
781d6656 588 if (statvfs(path, &sfs)) {
bf8fe701 589 debugs(50, 1, "" << path << ": " << xstrerror());
62e76326 590 return 1;
781d6656 591 }
62e76326 592
781d6656 593 *totl_kb = (int) fsbtoblk(sfs.f_blocks, sfs.f_frsize, 1024);
594 *free_kb = (int) fsbtoblk(sfs.f_bfree, sfs.f_frsize, 1024);
595 *totl_in = (int) sfs.f_files;
596 *free_in = (int) sfs.f_ffree;
597#else
62e76326 598
781d6656 599 struct statfs sfs;
62e76326 600
781d6656 601 if (statfs(path, &sfs)) {
bf8fe701 602 debugs(50, 1, "" << path << ": " << xstrerror());
62e76326 603 return 1;
781d6656 604 }
62e76326 605
781d6656 606 *totl_kb = (int) fsbtoblk(sfs.f_blocks, sfs.f_bsize, 1024);
607 *free_kb = (int) fsbtoblk(sfs.f_bfree, sfs.f_bsize, 1024);
608 *totl_in = (int) sfs.f_files;
609 *free_in = (int) sfs.f_ffree;
610#endif
62e76326 611
781d6656 612 return 0;
613}
c8f4eac4 614
615void
e1f7507e 616allocate_new_swapdir(SquidConfig::_cacheSwap * swap)
c8f4eac4 617{
618 if (swap->swapDirs == NULL) {
619 swap->n_allocated = 4;
620 swap->swapDirs = static_cast<StorePointer *>(xcalloc(swap->n_allocated, sizeof(StorePointer)));
621 }
622
623 if (swap->n_allocated == swap->n_configured) {
624 StorePointer *tmp;
625 swap->n_allocated <<= 1;
626 tmp = static_cast<StorePointer *>(xcalloc(swap->n_allocated, sizeof(StorePointer)));
41d00cd3 627 memcpy(tmp, swap->swapDirs, swap->n_configured * sizeof(SwapDir *));
c8f4eac4 628 xfree(swap->swapDirs);
629 swap->swapDirs = tmp;
630 }
631}
632
633void
e1f7507e 634free_cachedir(SquidConfig::_cacheSwap * swap)
c8f4eac4 635{
636 int i;
637 /* DON'T FREE THESE FOR RECONFIGURE */
638
639 if (reconfiguring)
640 return;
641
642 for (i = 0; i < swap->n_configured; i++) {
643 /* TODO XXX this lets the swapdir free resources asynchronously
644 * swap->swapDirs[i]->deactivate();
26ac0430 645 * but there may be such a means already.
c8f4eac4 646 * RBC 20041225
647 */
648 swap->swapDirs[i] = NULL;
649 }
650
651 safe_free(swap->swapDirs);
652 swap->swapDirs = NULL;
653 swap->n_allocated = 0;
654 swap->n_configured = 0;
655}
656
657/* this should be a virtual method on StoreEntry,
658 * i.e. e->referenced()
659 * so that the entry can notify the creating Store
660 */
661void
662StoreController::reference(StoreEntry &e)
663{
664 /* Notify the fs that we're referencing this object again */
665
666 if (e.swap_dirn > -1)
667 e.store()->reference(e);
668
669 /* Notify the memory cache that we're referencing this object again */
670 if (e.mem_obj) {
671 if (mem_policy->Referenced)
672 mem_policy->Referenced(mem_policy, &e, &e.mem_obj->repl);
673 }
674}
675
676void
677StoreController::dereference(StoreEntry & e)
678{
679 /* Notify the fs that we're not referencing this object any more */
680
681 if (e.swap_filen > -1)
682 e.store()->dereference(e);
683
684 /* Notify the memory cache that we're not referencing this object any more */
685 if (e.mem_obj) {
686 if (mem_policy->Dereferenced)
687 mem_policy->Dereferenced(mem_policy, &e, &e.mem_obj->repl);
688 }
689}
690
691StoreEntry *
6ca34f6f 692StoreController::get(const cache_key *key)
c8f4eac4 693{
694
6ca34f6f 695 return swapDir->get(key);
c8f4eac4 696}
697
698void
6ca34f6f 699StoreController::get(String const key, STOREGETCLIENT aCallback, void *aCallbackData)
c8f4eac4 700{
701 fatal("not implemented");
702}
703
704StoreHashIndex::StoreHashIndex()
705{
47f6e231 706 if (store_table)
26ac0430 707 abort();
c8f4eac4 708 assert (store_table == NULL);
709}
710
711StoreHashIndex::~StoreHashIndex()
712{
713 if (store_table) {
714 hashFreeItems(store_table, destroyStoreEntry);
715 hashFreeMemory(store_table);
716 store_table = NULL;
717 }
718}
719
720int
721StoreHashIndex::callback()
722{
723 int result = 0;
724 int j;
725 static int ndir = 0;
726
727 do {
728 j = 0;
729
730 for (int i = 0; i < Config.cacheSwap.n_configured; i++) {
731 if (ndir >= Config.cacheSwap.n_configured)
732 ndir = ndir % Config.cacheSwap.n_configured;
733
734 int temp_result = store(ndir)->callback();
735
736 ++ndir;
737
738 j += temp_result;
739
740 result += temp_result;
741
742 if (j > 100)
743 fatal ("too much io\n");
744 }
745 } while (j > 0);
746
747 ndir++;
748
749 return result;
750}
751
752void
753StoreHashIndex::create()
754{
755 for (int i = 0; i < Config.cacheSwap.n_configured; i++)
756 store(i)->create();
757}
758
759/* Lookup an object in the cache.
760 * return just a reference to object, don't start swapping in yet. */
761StoreEntry *
6ca34f6f 762StoreHashIndex::get(const cache_key *key)
c8f4eac4 763{
764 PROF_start(storeGet);
bf8fe701 765 debugs(20, 3, "storeGet: looking up " << storeKeyText(key));
c8f4eac4 766 StoreEntry *p = static_cast<StoreEntry *>(hash_lookup(store_table, key));
767 PROF_stop(storeGet);
768 return p;
769}
770
771void
6ca34f6f 772StoreHashIndex::get(String const key, STOREGETCLIENT aCallback, void *aCallbackData)
c8f4eac4 773{
774 fatal("not implemented");
775}
776
777void
778StoreHashIndex::init()
779{
780 /* Calculate size of hash table (maximum currently 64k buckets). */
781 /* this is very bogus, its specific to the any Store maintaining an
782 * in-core index, not global */
13a07022 783 size_t buckets = (Store::Root().maxSize() + ( Config.memMaxSize >> 10)) / Config.Store.avgObjectSize;
c8f4eac4 784 debugs(20, 1, "Swap maxSize " << Store::Root().maxSize() <<
13a07022 785 " + " << ( Config.memMaxSize >> 10) << " KB, estimated " << buckets << " objects");
c8f4eac4 786 buckets /= Config.Store.objectsPerBucket;
787 debugs(20, 1, "Target number of buckets: " << buckets);
788 /* ideally the full scan period should be configurable, for the
789 * moment it remains at approximately 24 hours. */
790 store_hash_buckets = storeKeyHashBuckets(buckets);
791 debugs(20, 1, "Using " << store_hash_buckets << " Store buckets");
792 debugs(20, 1, "Max Mem size: " << ( Config.memMaxSize >> 10) << " KB");
793 debugs(20, 1, "Max Swap size: " << Store::Root().maxSize() << " KB");
794
795 store_table = hash_create(storeKeyHashCmp,
796 store_hash_buckets, storeKeyHashHash);
797
13a07022 798 for (int i = 0; i < Config.cacheSwap.n_configured; i++) {
c8f4eac4 799 /* this starts a search of the store dirs, loading their
800 * index. under the new Store api this should be
801 * driven by the StoreHashIndex, not by each store.
bef81ea5 802 *
803 * That is, the HashIndex should perform a search of each dir it is
26ac0430 804 * indexing to do the hash insertions. The search is then able to
bef81ea5 805 * decide 'from-memory', or 'from-clean-log' or 'from-dirty-log' or
806 * 'from-no-log'.
807 *
c8f4eac4 808 * Step 1: make the store rebuilds use a search internally
bef81ea5 809 * Step 2: change the search logic to use the four modes described
810 * above
811 * Step 3: have the hash index walk the searches itself.
c8f4eac4 812 */
813 store(i)->init();
13a07022 814 }
c8f4eac4 815}
816
12e11a5c 817uint64_t
c8f4eac4 818StoreHashIndex::maxSize() const
819{
12e11a5c 820 uint64_t result = 0;
c8f4eac4 821
12e11a5c 822 for (int i = 0; i < Config.cacheSwap.n_configured; i++)
c8f4eac4 823 result += store(i)->maxSize();
824
825 return result;
826}
827
12e11a5c 828uint64_t
c8f4eac4 829StoreHashIndex::minSize() const
830{
12e11a5c 831 uint64_t result = 0;
c8f4eac4 832
833 for (int i = 0; i < Config.cacheSwap.n_configured; i++)
834 result += store(i)->minSize();
835
836 return result;
837}
838
839void
840StoreHashIndex::stat(StoreEntry & output) const
841{
842 int i;
843
844 /* Now go through each store, calling its stat routine */
845
846 for (i = 0; i < Config.cacheSwap.n_configured; i++) {
847 storeAppendPrintf(&output, "\n");
848 store(i)->stat(output);
849 }
850}
851
852void
853StoreHashIndex::reference(StoreEntry&)
854{}
855
856void
857StoreHashIndex::dereference(StoreEntry&)
858{}
859
860void
861StoreHashIndex::maintain()
862{
863 int i;
864 /* walk each fs */
865
866 for (i = 0; i < Config.cacheSwap.n_configured; i++) {
867 /* XXX FixMe: This should be done "in parallell" on the different
868 * cache_dirs, not one at a time.
869 */
870 /* call the maintain function .. */
871 store(i)->maintain();
872 }
873}
874
875void
47f6e231 876StoreHashIndex::updateSize(int64_t, int)
c8f4eac4 877{}
878
879void
880StoreHashIndex::sync()
881{
882 for (int i = 0; i < Config.cacheSwap.n_configured; ++i)
883 store(i)->sync();
884}
885
886StoreSearch *
30abd221 887StoreHashIndex::search(String const url, HttpRequest *)
c8f4eac4 888{
889 if (url.size())
890 fatal ("Cannot search by url yet\n");
891
892 return new StoreSearchHashIndex (this);
893}
894
895CBDATA_CLASS_INIT(StoreSearchHashIndex);
aa839030 896
c8f4eac4 897StoreSearchHashIndex::StoreSearchHashIndex(RefCount<StoreHashIndex> aSwapDir) : sd(aSwapDir), _done (false), bucket (0)
898{}
899
900/* do not link
901StoreSearchHashIndex::StoreSearchHashIndex(StoreSearchHashIndex const &);
902*/
903
904StoreSearchHashIndex::~StoreSearchHashIndex()
905{}
906
907void
70efcae0 908StoreSearchHashIndex::next(void (aCallback)(void *), void *aCallbackData)
c8f4eac4 909{
910 next();
70efcae0 911 aCallback (aCallbackData);
c8f4eac4 912}
913
914bool
915StoreSearchHashIndex::next()
916{
917 if (entries.size())
918 entries.pop_back();
919
920 while (!isDone() && !entries.size())
921 copyBucket();
922
923 return currentItem() != NULL;
924}
925
926bool
927StoreSearchHashIndex::error() const
928{
929 return false;
930}
931
932bool
933StoreSearchHashIndex::isDone() const
934{
935 return bucket >= store_hash_buckets || _done;
936}
937
938StoreEntry *
939StoreSearchHashIndex::currentItem()
940{
941 if (!entries.size())
942 return NULL;
943
944 return entries.back();
945}
946
947void
948StoreSearchHashIndex::copyBucket()
949{
950 /* probably need to lock the store entries...
951 * we copy them all to prevent races on the links. */
952 debugs(47, 3, "StoreSearchHashIndex::copyBucket #" << bucket);
953 assert (!entries.size());
954 hash_link *link_ptr = NULL;
955 hash_link *link_next = NULL;
956 link_next = hash_get_bucket(store_table, bucket);
957
958 while (NULL != (link_ptr = link_next)) {
959 link_next = link_ptr->next;
960 StoreEntry *e = (StoreEntry *) link_ptr;
961
962 entries.push_back(e);
963 }
964
965 bucket++;
966 debugs(47,3, "got entries: " << entries.size());
967}