5 * DEBUG: section 47 Store Directory Routines
6 * AUTHOR: Duane Wessels
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
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.
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.
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.
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
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
38 #include "MemObject.h"
41 #include "SquidMath.h"
42 #include "SquidTime.h"
44 #include "swap_log_op.h"
47 #if HAVE_SYS_STATVFS_H
48 #include <sys/statvfs.h>
50 #endif /* HAVE_STATVFS */
51 /* statfs() needs <sys/param.h> and <sys/mount.h> on BSD systems */
53 #include <sys/param.h>
56 #include <sys/mount.h>
58 /* Windows and Linux use sys/vfs.h */
63 #include "StoreHashIndex.h"
65 static STDIRSELECT storeDirSelectSwapDirRoundRobin
;
66 static STDIRSELECT storeDirSelectSwapDirLeastLoad
;
69 * store_dirs_rebuilding is initialized to _1_ as a hack so that
70 * storeDirWriteCleanLogs() doesn't try to do anything unless _all_
71 * cache_dirs have been read. For example, without this hack, Squid
72 * will try to write clean log files if -kparse fails (becasue it
75 int StoreController::store_dirs_rebuilding
= 1;
77 StoreController::StoreController() : swapDir (new StoreHashIndex())
81 StoreController::~StoreController()
87 * This function pointer is set according to 'store_dir_select_algorithm'
90 STDIRSELECT
*storeDirSelectSwapDir
= storeDirSelectSwapDirLeastLoad
;
93 StoreController::init()
95 // XXX: add: if (UsingSmp())
96 memStore
= new MemStore
;
101 if (0 == strcasecmp(Config
.store_dir_select_algorithm
, "round-robin")) {
102 storeDirSelectSwapDir
= storeDirSelectSwapDirRoundRobin
;
103 debugs(47, 1, "Using Round Robin store dir selection");
105 storeDirSelectSwapDir
= storeDirSelectSwapDirLeastLoad
;
106 debugs(47, 1, "Using Least Load store dir selection");
111 StoreController::createOneStore(Store
&aStore
)
114 * On Windows, fork() is not available.
115 * The following is a workaround for create store directories sequentially
116 * when running on native Windows port.
118 #ifndef _SQUID_MSWIN_
127 #ifndef _SQUID_MSWIN_
135 StoreController::create()
139 #ifndef _SQUID_MSWIN_
147 pid
= wait3(&status
, WNOHANG
, NULL
);
150 pid
= waitpid(-1, &status
, 0);
153 } while (pid
> 0 || (pid
< 0 && errno
== EINTR
));
159 * Determine whether the given directory can handle this object
162 * Note: if the object size is -1, then the only swapdirs that
163 * will return true here are ones that have min and max unset,
164 * ie any-sized-object swapdirs. This is a good thing.
167 SwapDir::objectSizeIsAcceptable(int64_t objsize
) const
169 // If the swapdir has no range limits, then it definitely can
170 if (min_objsize
<= 0 && max_objsize
== -1)
174 * If the object size is -1 and the storedir has limits we
175 * can't store it there.
180 // Else, make sure that the object size will fit.
181 return min_objsize
<= objsize
&& max_objsize
> objsize
;
186 * This new selection scheme simply does round-robin on all SwapDirs.
187 * A SwapDir is skipped if it is over the max_size (100%) limit, or
191 storeDirSelectSwapDirRoundRobin(const StoreEntry
* e
)
196 RefCount
<SwapDir
> sd
;
198 // e->objectLen() is negative at this point when we are still STORE_PENDING
199 ssize_t objsize
= e
->mem_obj
->expectedReplySize();
201 objsize
+= e
->mem_obj
->swap_hdr_sz
;
203 for (i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
204 if (++dirn
>= Config
.cacheSwap
.n_configured
)
207 sd
= dynamic_cast<SwapDir
*>(INDEXSD(dirn
));
209 if (!sd
->canStore(*e
, objsize
, load
))
212 if (load
< 0 || load
> 1000) {
223 * Spread load across all of the store directories
225 * Note: We should modify this later on to prefer sticking objects
226 * in the *tightest fit* swapdir to conserve space, along with the
227 * actual swapdir usage. But for now, this hack will do while
228 * testing, so you should order your swapdirs in the config file
229 * from smallest maxobjsize to unlimited (-1) maxobjsize.
231 * We also have to choose nleast == nconf since we need to consider
232 * ALL swapdirs, regardless of state. Again, this is a hack while
233 * we sort out the real usefulness of this algorithm.
236 storeDirSelectSwapDirLeastLoad(const StoreEntry
* e
)
238 ssize_t most_free
= 0, cur_free
;
239 ssize_t least_objsize
= -1;
240 int least_load
= INT_MAX
;
244 RefCount
<SwapDir
> SD
;
246 // e->objectLen() is negative at this point when we are still STORE_PENDING
247 ssize_t objsize
= e
->mem_obj
->expectedReplySize();
250 objsize
+= e
->mem_obj
->swap_hdr_sz
;
252 for (i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
253 SD
= dynamic_cast<SwapDir
*>(INDEXSD(i
));
254 SD
->flags
.selected
= 0;
256 if (!SD
->canStore(*e
, objsize
, load
))
259 if (load
< 0 || load
> 1000)
262 if (load
> least_load
)
265 cur_free
= SD
->max_size
- SD
->cur_size
;
267 /* If the load is equal, then look in more details */
268 if (load
== least_load
) {
269 /* closest max_objsize fit */
271 if (least_objsize
!= -1)
272 if (SD
->max_objsize
> least_objsize
|| SD
->max_objsize
== -1)
276 if (cur_free
< most_free
)
281 least_objsize
= SD
->max_objsize
;
282 most_free
= cur_free
;
287 dynamic_cast<SwapDir
*>(INDEXSD(dirn
))->flags
.selected
= 1;
293 * An entry written to the swap log MUST have the following
295 * 1. It MUST be a public key. It does no good to log
296 * a public ADD, change the key, then log a private
297 * DEL. So we need to log a DEL before we change a
298 * key from public to private.
299 * 2. It MUST have a valid (> -1) swap_filen.
302 storeDirSwapLog(const StoreEntry
* e
, int op
)
305 assert(!EBIT_TEST(e
->flags
, KEY_PRIVATE
));
306 assert(e
->swap_filen
>= 0);
308 * icons and such; don't write them to the swap log
311 if (EBIT_TEST(e
->flags
, ENTRY_SPECIAL
))
314 assert(op
> SWAP_LOG_NOP
&& op
< SWAP_LOG_MAX
);
316 debugs(20, 3, "storeDirSwapLog: " <<
317 swap_log_op_str
[op
] << " " <<
318 e
->getMD5Text() << " " <<
319 e
->swap_dirn
<< " " <<
320 std::hex
<< std::uppercase
<< std::setfill('0') << std::setw(8) << e
->swap_filen
);
322 dynamic_cast<SwapDir
*>(INDEXSD(e
->swap_dirn
))->logEntry(*e
, op
);
326 StoreController::updateSize(int64_t size
, int sign
)
328 fatal("StoreController has no independent size\n");
332 SwapDir::updateSize(int64_t size
, int sign
)
334 int64_t blks
= (size
+ fs
.blksize
- 1) / fs
.blksize
;
335 int64_t k
= ((blks
* fs
.blksize
) >> 10) * sign
;
345 StoreController::stat(StoreEntry
&output
) const
347 storeAppendPrintf(&output
, "Store Directory Statistics:\n");
348 storeAppendPrintf(&output
, "Store Entries : %lu\n",
349 (unsigned long int)StoreEntry::inUseCount());
350 storeAppendPrintf(&output
, "Maximum Swap Size : %"PRIu64
" KB\n",
352 storeAppendPrintf(&output
, "Current Store Swap Size: %"PRIu64
" KB\n",
354 storeAppendPrintf(&output
, "Current Capacity : %"PRId64
"%% used, %"PRId64
"%% free\n",
355 Math::int64Percent(currentSize(), maxSize()),
356 Math::int64Percent((maxSize() - currentSize()), maxSize()));
359 memStore
->stat(output
);
361 /* now the swapDir */
362 swapDir
->stat(output
);
365 /* if needed, this could be taught to cache the result */
367 StoreController::maxSize() const
369 /* TODO: include memory cache ? */
370 return swapDir
->maxSize();
374 StoreController::minSize() const
376 /* TODO: include memory cache ? */
377 return swapDir
->minSize();
381 StoreController::currentSize() const
383 return swapDir
->currentSize();
387 StoreController::currentCount() const
389 return swapDir
->currentCount();
393 StoreController::maxObjectSize() const
395 return swapDir
->maxObjectSize();
401 if (cur_size
>= max_size
)
406 debugs(20, 1, "WARNING: Shrinking cache_dir #" << index
<< " to " << cur_size
<< " KB");
410 storeDirOpenSwapLogs(void)
412 for (int dirn
= 0; dirn
< Config
.cacheSwap
.n_configured
; ++dirn
)
413 dynamic_cast<SwapDir
*>(INDEXSD(dirn
))->openLog();
417 storeDirCloseSwapLogs(void)
419 for (int dirn
= 0; dirn
< Config
.cacheSwap
.n_configured
; ++dirn
)
420 dynamic_cast<SwapDir
*>(INDEXSD(dirn
))->closeLog();
424 * storeDirWriteCleanLogs
426 * Writes a "clean" swap log file from in-memory metadata.
427 * This is a rewrite of the original function to troll each
428 * StoreDir and write the logs, and flush at the end of
429 * the run. Thanks goes to Eric Stern, since this solution
430 * came out of his COSS code.
433 storeDirWriteCleanLogs(int reopen
)
435 const StoreEntry
*e
= NULL
;
438 struct timeval start
;
440 RefCount
<SwapDir
> sd
;
444 if (StoreController::store_dirs_rebuilding
) {
445 debugs(20, 1, "Not currently OK to rewrite swap log.");
446 debugs(20, 1, "storeDirWriteCleanLogs: Operation aborted.");
450 debugs(20, 1, "storeDirWriteCleanLogs: Starting...");
452 start
= current_time
;
454 for (dirn
= 0; dirn
< Config
.cacheSwap
.n_configured
; dirn
++) {
455 sd
= dynamic_cast<SwapDir
*>(INDEXSD(dirn
));
457 if (sd
->writeCleanStart() < 0) {
458 debugs(20, 1, "log.clean.start() failed for dir #" << sd
->index
);
464 * This may look inefficient as CPU wise it is more efficient to do this
465 * sequentially, but I/O wise the parallellism helps as it allows more
466 * hdd spindles to be active.
471 for (dirn
= 0; dirn
< Config
.cacheSwap
.n_configured
; dirn
++) {
472 sd
= dynamic_cast<SwapDir
*>(INDEXSD(dirn
));
474 if (NULL
== sd
->cleanLog
)
477 e
= sd
->cleanLog
->nextEntry();
487 sd
->cleanLog
->write(*e
);
489 if ((++n
& 0xFFFF) == 0) {
491 debugs(20, 1, " " << std::setw(7) << n
<<
492 " entries written so far.");
498 for (dirn
= 0; dirn
< Config
.cacheSwap
.n_configured
; dirn
++)
499 dynamic_cast<SwapDir
*>(INDEXSD(dirn
))->writeCleanDone();
502 storeDirOpenSwapLogs();
506 dt
= tvSubDsec(start
, current_time
);
508 debugs(20, 1, " Finished. Wrote " << n
<< " entries.");
509 debugs(20, 1, " Took "<< std::setw(3)<< std::setprecision(2) << dt
<<
510 " seconds ("<< std::setw(6) << ((double) n
/ (dt
> 0.0 ? dt
: 1.0)) << " entries/sec).");
517 StoreController::search(String
const url
, HttpRequest
*request
)
519 /* cheat, for now you can't search the memory hot cache */
520 return swapDir
->search(url
, request
);
524 StoreHashIndex::store(int const x
) const
530 StoreHashIndex::dir(const int i
) const
532 SwapDir
*sd
= dynamic_cast<SwapDir
*>(INDEXSD(i
));
538 StoreController::sync(void)
546 * handle callbacks all avaliable fs'es
549 StoreController::callback()
551 /* This will likely double count. Thats ok. */
552 PROF_start(storeDirCallback
);
554 /* mem cache callbacks ? */
555 int result
= swapDir
->callback();
557 PROF_stop(storeDirCallback
);
563 storeDirGetBlkSize(const char *path
, int *blksize
)
569 if (statvfs(path
, &sfs
)) {
570 debugs(50, 1, "" << path
<< ": " << xstrerror());
575 *blksize
= (int) sfs
.f_frsize
;
580 if (statfs(path
, &sfs
)) {
581 debugs(50, 1, "" << path
<< ": " << xstrerror());
586 *blksize
= (int) sfs
.f_bsize
;
589 * Sanity check; make sure we have a meaningful value.
598 #define fsbtoblk(num, fsbs, bs) \
599 (((fsbs) != 0 && (fsbs) < (bs)) ? \
600 (num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs)))
602 storeDirGetUFSStats(const char *path
, int *totl_kb
, int *free_kb
, int *totl_in
, int *free_in
)
608 if (statvfs(path
, &sfs
)) {
609 debugs(50, 1, "" << path
<< ": " << xstrerror());
613 *totl_kb
= (int) fsbtoblk(sfs
.f_blocks
, sfs
.f_frsize
, 1024);
614 *free_kb
= (int) fsbtoblk(sfs
.f_bfree
, sfs
.f_frsize
, 1024);
615 *totl_in
= (int) sfs
.f_files
;
616 *free_in
= (int) sfs
.f_ffree
;
621 if (statfs(path
, &sfs
)) {
622 debugs(50, 1, "" << path
<< ": " << xstrerror());
626 *totl_kb
= (int) fsbtoblk(sfs
.f_blocks
, sfs
.f_bsize
, 1024);
627 *free_kb
= (int) fsbtoblk(sfs
.f_bfree
, sfs
.f_bsize
, 1024);
628 *totl_in
= (int) sfs
.f_files
;
629 *free_in
= (int) sfs
.f_ffree
;
636 allocate_new_swapdir(SquidConfig::_cacheSwap
* swap
)
638 if (swap
->swapDirs
== NULL
) {
639 swap
->n_allocated
= 4;
640 swap
->swapDirs
= static_cast<StorePointer
*>(xcalloc(swap
->n_allocated
, sizeof(StorePointer
)));
643 if (swap
->n_allocated
== swap
->n_configured
) {
645 swap
->n_allocated
<<= 1;
646 tmp
= static_cast<StorePointer
*>(xcalloc(swap
->n_allocated
, sizeof(StorePointer
)));
647 memcpy(tmp
, swap
->swapDirs
, swap
->n_configured
* sizeof(SwapDir
*));
648 xfree(swap
->swapDirs
);
649 swap
->swapDirs
= tmp
;
654 free_cachedir(SquidConfig::_cacheSwap
* swap
)
657 /* DON'T FREE THESE FOR RECONFIGURE */
662 for (i
= 0; i
< swap
->n_configured
; i
++) {
663 /* TODO XXX this lets the swapdir free resources asynchronously
664 * swap->swapDirs[i]->deactivate();
665 * but there may be such a means already.
668 swap
->swapDirs
[i
] = NULL
;
671 safe_free(swap
->swapDirs
);
672 swap
->swapDirs
= NULL
;
673 swap
->n_allocated
= 0;
674 swap
->n_configured
= 0;
677 /* this should be a virtual method on StoreEntry,
678 * i.e. e->referenced()
679 * so that the entry can notify the creating Store
682 StoreController::reference(StoreEntry
&e
)
684 /* Notify the fs that we're referencing this object again */
686 if (e
.swap_dirn
> -1)
687 e
.store()->reference(e
);
689 // Notify the memory cache that we're referencing this object again
690 if (memStore
&& e
.mem_status
== IN_MEMORY
)
691 memStore
->reference(e
);
693 // TODO: move this code to a non-shared memory cache class when we have it
695 if (mem_policy
->Referenced
)
696 mem_policy
->Referenced(mem_policy
, &e
, &e
.mem_obj
->repl
);
701 StoreController::dereference(StoreEntry
& e
)
703 /* Notify the fs that we're not referencing this object any more */
705 if (e
.swap_filen
> -1)
706 e
.store()->dereference(e
);
708 // Notify the memory cache that we're not referencing this object any more
709 if (memStore
&& e
.mem_status
== IN_MEMORY
)
710 memStore
->dereference(e
);
712 // TODO: move this code to a non-shared memory cache class when we have it
714 if (mem_policy
->Dereferenced
)
715 mem_policy
->Dereferenced(mem_policy
, &e
, &e
.mem_obj
->repl
);
720 StoreController::get(const cache_key
*key
)
722 if (StoreEntry
*e
= swapDir
->get(key
)) {
723 // TODO: ignore and maybe handleIdleEntry() unlocked intransit entries
724 // because their backing store slot may be gone already.
725 debugs(20, 3, HERE
<< "got in-transit entry: " << *e
);
730 if (StoreEntry
*e
= memStore
->get(key
)) {
731 debugs(20, 3, HERE
<< "got mem-cached entry: " << *e
);
736 // TODO: this disk iteration is misplaced; move to StoreHashIndex
737 if (const int cacheDirs
= Config
.cacheSwap
.n_configured
) {
738 // ask each cache_dir until the entry is found; use static starting
739 // point to avoid asking the same subset of disks more often
740 // TODO: coordinate with put() to be able to guess the right disk often
742 for (int n
= 0; n
< cacheDirs
; ++n
) {
743 idx
= (idx
+ 1) % cacheDirs
;
744 SwapDir
*sd
= dynamic_cast<SwapDir
*>(INDEXSD(idx
));
748 if (StoreEntry
*e
= sd
->get(key
)) {
749 debugs(20, 3, HERE
<< "cache_dir " << idx
<<
750 " got cached entry: " << *e
);
756 debugs(20, 4, HERE
<< "none of " << Config
.cacheSwap
.n_configured
<<
757 " cache_dirs have " << storeKeyText(key
));
762 StoreController::get(String
const key
, STOREGETCLIENT aCallback
, void *aCallbackData
)
764 fatal("not implemented");
768 StoreController::handleIdleEntry(StoreEntry
&e
)
770 bool keepInLocalMemory
= false;
772 memStore
->considerKeeping(e
);
773 // leave keepInLocalMemory false; memStore maintains its own cache
775 keepInLocalMemory
= e
.memoryCachable() && // entry is in good shape and
776 // the local memory cache is not overflowing
777 (mem_node::InUseCount() <= store_pages_max
);
782 // XXX: Rock store specific: Since each SwapDir controls its index,
783 // unlocked entries should not stay in the global store_table.
785 debugs(20, 5, HERE
<< "destroying unlocked entry: " << &e
<< ' ' << e
);
786 destroyStoreEntry(static_cast<hash_link
*>(&e
));
790 // TODO: move this into [non-shared] memory cache class when we have one
791 if (keepInLocalMemory
) {
792 e
.setMemStatus(IN_MEMORY
);
793 e
.mem_obj
->unlinkRequest();
795 e
.purgeMem(); // may free e
799 StoreHashIndex::StoreHashIndex()
803 assert (store_table
== NULL
);
806 StoreHashIndex::~StoreHashIndex()
809 hashFreeItems(store_table
, destroyStoreEntry
);
810 hashFreeMemory(store_table
);
816 StoreHashIndex::callback()
825 for (int i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
826 if (ndir
>= Config
.cacheSwap
.n_configured
)
827 ndir
= ndir
% Config
.cacheSwap
.n_configured
;
829 int temp_result
= store(ndir
)->callback();
835 result
+= temp_result
;
838 fatal ("too much io\n");
848 StoreHashIndex::create()
850 for (int i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
856 /* Lookup an object in the cache.
857 * return just a reference to object, don't start swapping in yet. */
859 StoreHashIndex::get(const cache_key
*key
)
861 PROF_start(storeGet
);
862 debugs(20, 3, "storeGet: looking up " << storeKeyText(key
));
863 StoreEntry
*p
= static_cast<StoreEntry
*>(hash_lookup(store_table
, key
));
869 StoreHashIndex::get(String
const key
, STOREGETCLIENT aCallback
, void *aCallbackData
)
871 fatal("not implemented");
875 StoreHashIndex::init()
877 /* Calculate size of hash table (maximum currently 64k buckets). */
878 /* this is very bogus, its specific to the any Store maintaining an
879 * in-core index, not global */
880 size_t buckets
= (Store::Root().maxSize() + ( Config
.memMaxSize
>> 10)) / Config
.Store
.avgObjectSize
;
881 debugs(20, 1, "Swap maxSize " << Store::Root().maxSize() <<
882 " + " << ( Config
.memMaxSize
>> 10) << " KB, estimated " << buckets
<< " objects");
883 buckets
/= Config
.Store
.objectsPerBucket
;
884 debugs(20, 1, "Target number of buckets: " << buckets
);
885 /* ideally the full scan period should be configurable, for the
886 * moment it remains at approximately 24 hours. */
887 store_hash_buckets
= storeKeyHashBuckets(buckets
);
888 debugs(20, 1, "Using " << store_hash_buckets
<< " Store buckets");
889 debugs(20, 1, "Max Mem size: " << ( Config
.memMaxSize
>> 10) << " KB");
890 debugs(20, 1, "Max Swap size: " << Store::Root().maxSize() << " KB");
892 store_table
= hash_create(storeKeyHashCmp
,
893 store_hash_buckets
, storeKeyHashHash
);
895 for (int i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
896 /* this starts a search of the store dirs, loading their
897 * index. under the new Store api this should be
898 * driven by the StoreHashIndex, not by each store.
900 * That is, the HashIndex should perform a search of each dir it is
901 * indexing to do the hash insertions. The search is then able to
902 * decide 'from-memory', or 'from-clean-log' or 'from-dirty-log' or
905 * Step 1: make the store rebuilds use a search internally
906 * Step 2: change the search logic to use the four modes described
908 * Step 3: have the hash index walk the searches itself.
916 StoreHashIndex::maxSize() const
920 for (int i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
921 if (dir(i
).doReportStat())
922 result
+= store(i
)->maxSize();
929 StoreHashIndex::minSize() const
933 for (int i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
934 if (dir(i
).doReportStat())
935 result
+= store(i
)->minSize();
942 StoreHashIndex::currentSize() const
946 for (int i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
947 if (dir(i
).doReportStat())
948 result
+= store(i
)->currentSize();
955 StoreHashIndex::currentCount() const
959 for (int i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
960 if (dir(i
).doReportStat())
961 result
+= store(i
)->currentCount();
968 StoreHashIndex::maxObjectSize() const
972 for (int i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
973 if (dir(i
).active() && store(i
)->maxObjectSize() > result
)
974 result
= store(i
)->maxObjectSize();
981 StoreHashIndex::stat(StoreEntry
& output
) const
985 /* Now go through each store, calling its stat routine */
987 for (i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
988 storeAppendPrintf(&output
, "\n");
989 store(i
)->stat(output
);
994 StoreHashIndex::reference(StoreEntry
&)
998 StoreHashIndex::dereference(StoreEntry
&)
1002 StoreHashIndex::maintain()
1007 for (i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
1008 /* XXX FixMe: This should be done "in parallell" on the different
1009 * cache_dirs, not one at a time.
1011 /* call the maintain function .. */
1012 store(i
)->maintain();
1017 StoreHashIndex::updateSize(int64_t, int)
1021 StoreHashIndex::sync()
1023 for (int i
= 0; i
< Config
.cacheSwap
.n_configured
; ++i
)
1028 StoreHashIndex::search(String
const url
, HttpRequest
*)
1031 fatal ("Cannot search by url yet\n");
1033 return new StoreSearchHashIndex (this);
1036 CBDATA_CLASS_INIT(StoreSearchHashIndex
);
1038 StoreSearchHashIndex::StoreSearchHashIndex(RefCount
<StoreHashIndex
> aSwapDir
) : sd(aSwapDir
), _done (false), bucket (0)
1042 StoreSearchHashIndex::StoreSearchHashIndex(StoreSearchHashIndex const &);
1045 StoreSearchHashIndex::~StoreSearchHashIndex()
1049 StoreSearchHashIndex::next(void (aCallback
)(void *), void *aCallbackData
)
1052 aCallback (aCallbackData
);
1056 StoreSearchHashIndex::next()
1061 while (!isDone() && !entries
.size())
1064 return currentItem() != NULL
;
1068 StoreSearchHashIndex::error() const
1074 StoreSearchHashIndex::isDone() const
1076 return bucket
>= store_hash_buckets
|| _done
;
1080 StoreSearchHashIndex::currentItem()
1082 if (!entries
.size())
1085 return entries
.back();
1089 StoreSearchHashIndex::copyBucket()
1091 /* probably need to lock the store entries...
1092 * we copy them all to prevent races on the links. */
1093 debugs(47, 3, "StoreSearchHashIndex::copyBucket #" << bucket
);
1094 assert (!entries
.size());
1095 hash_link
*link_ptr
= NULL
;
1096 hash_link
*link_next
= NULL
;
1097 link_next
= hash_get_bucket(store_table
, bucket
);
1099 while (NULL
!= (link_ptr
= link_next
)) {
1100 link_next
= link_ptr
->next
;
1101 StoreEntry
*e
= (StoreEntry
*) link_ptr
;
1103 entries
.push_back(e
);
1107 debugs(47,3, "got entries: " << entries
.size());