3 * $Id: store_dir.cc,v 1.150 2005/01/03 16:08:26 robertc Exp $
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"
42 #if HAVE_SYS_STATVFS_H
43 #include <sys/statvfs.h>
45 #endif /* HAVE_STATVFS */
46 /* statfs() needs <sys/param.h> and <sys/mount.h> on BSD systems */
48 #include <sys/param.h>
51 #include <sys/mount.h>
53 /* Windows and Linux use sys/vfs.h */
58 #include "StoreHashIndex.h"
60 static STDIRSELECT storeDirSelectSwapDirRoundRobin
;
61 static STDIRSELECT storeDirSelectSwapDirLeastLoad
;
63 StoreController::StoreController() : swapDir (new StoreHashIndex())
66 StoreController::~StoreController()
70 * This function pointer is set according to 'store_dir_select_algorithm'
73 STDIRSELECT
*storeDirSelectSwapDir
= storeDirSelectSwapDirLeastLoad
;
76 StoreController::init()
80 if (0 == strcasecmp(Config
.store_dir_select_algorithm
, "round-robin")) {
81 storeDirSelectSwapDir
= storeDirSelectSwapDirRoundRobin
;
82 debug(47, 1) ("Using Round Robin store dir selection\n");
84 storeDirSelectSwapDir
= storeDirSelectSwapDirLeastLoad
;
85 debug(47, 1) ("Using Least Load store dir selection\n");
90 StoreController::createOneStore(Store
&aStore
)
93 * On Windows, fork() is not available.
94 * The following is a workaround for create store directories sequentially
95 * when running on native Windows port.
106 #ifndef _SQUID_MSWIN_
114 StoreController::create()
118 #ifndef _SQUID_MSWIN_
126 pid
= wait3(&status
, WNOHANG
, NULL
);
129 pid
= waitpid(-1, &status
, 0);
132 } while (pid
> 0 || (pid
< 0 && errno
== EINTR
));
138 * Determine whether the given directory can handle this object
141 * Note: if the object size is -1, then the only swapdirs that
142 * will return true here are ones that have max_obj_size = -1,
143 * ie any-sized-object swapdirs. This is a good thing.
146 SwapDir::objectSizeIsAcceptable(ssize_t objsize
) const
149 * If the swapdir's max_obj_size is -1, then it definitely can
152 if (max_objsize
== -1)
156 * If the object size is -1, then if the storedir isn't -1 we
159 if ((objsize
== -1) && (max_objsize
!= -1))
163 * Else, make sure that the max object size is larger than objsize
165 return max_objsize
> objsize
;
170 * This new selection scheme simply does round-robin on all SwapDirs.
171 * A SwapDir is skipped if it is over the max_size (100%) limit, or
175 storeDirSelectSwapDirRoundRobin(const StoreEntry
* e
)
180 RefCount
<SwapDir
> sd
;
181 ssize_t objsize
= (ssize_t
) objectLen(e
);
183 for (i
= 0; i
<= Config
.cacheSwap
.n_configured
; i
++) {
184 if (++dirn
>= Config
.cacheSwap
.n_configured
)
187 sd
= dynamic_cast<SwapDir
*>(INDEXSD(dirn
));
189 if (sd
->flags
.read_only
)
192 if (sd
->cur_size
> sd
->max_size
)
195 if (!sd
->objectSizeIsAcceptable(objsize
))
198 /* check for error or overload condition */
199 load
= sd
->canStore(*e
);
201 if (load
< 0 || load
> 1000) {
212 * Spread load across all of the store directories
214 * Note: We should modify this later on to prefer sticking objects
215 * in the *tightest fit* swapdir to conserve space, along with the
216 * actual swapdir usage. But for now, this hack will do while
217 * testing, so you should order your swapdirs in the config file
218 * from smallest maxobjsize to unlimited (-1) maxobjsize.
220 * We also have to choose nleast == nconf since we need to consider
221 * ALL swapdirs, regardless of state. Again, this is a hack while
222 * we sort out the real usefulness of this algorithm.
225 storeDirSelectSwapDirLeastLoad(const StoreEntry
* e
)
228 ssize_t most_free
= 0, cur_free
;
229 ssize_t least_objsize
= -1;
230 int least_load
= INT_MAX
;
234 RefCount
<SwapDir
> SD
;
236 /* Calculate the object size */
237 objsize
= (ssize_t
) objectLen(e
);
240 objsize
+= e
->mem_obj
->swap_hdr_sz
;
242 for (i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
243 SD
= dynamic_cast<SwapDir
*>(INDEXSD(i
));
244 SD
->flags
.selected
= 0;
245 load
= SD
->canStore(*e
);
247 if (load
< 0 || load
> 1000) {
251 if (!SD
->objectSizeIsAcceptable(objsize
))
254 if (SD
->flags
.read_only
)
257 if (SD
->cur_size
> SD
->max_size
)
260 if (load
> least_load
)
263 cur_free
= SD
->max_size
- SD
->cur_size
;
265 /* If the load is equal, then look in more details */
266 if (load
== least_load
) {
267 /* closest max_objsize fit */
269 if (least_objsize
!= -1)
270 if (SD
->max_objsize
> least_objsize
|| SD
->max_objsize
== -1)
274 if (cur_free
< most_free
)
279 least_objsize
= SD
->max_objsize
;
280 most_free
= cur_free
;
285 dynamic_cast<SwapDir
*>(INDEXSD(dirn
))->flags
.selected
= 1;
291 * An entry written to the swap log MUST have the following
293 * 1. It MUST be a public key. It does no good to log
294 * a public ADD, change the key, then log a private
295 * DEL. So we need to log a DEL before we change a
296 * key from public to private.
297 * 2. It MUST have a valid (> -1) swap_filen.
300 storeDirSwapLog(const StoreEntry
* e
, int op
)
303 assert(!EBIT_TEST(e
->flags
, KEY_PRIVATE
));
304 assert(e
->swap_filen
>= 0);
306 * icons and such; don't write them to the swap log
309 if (EBIT_TEST(e
->flags
, ENTRY_SPECIAL
))
312 assert(op
> SWAP_LOG_NOP
&& op
< SWAP_LOG_MAX
);
314 debug(20, 3) ("storeDirSwapLog: %s %s %d %08X\n",
320 dynamic_cast<SwapDir
*>(INDEXSD(e
->swap_dirn
))->logEntry(*e
, op
);
324 StoreController::updateSize(size_t size
, int sign
)
326 fatal("StoreController has no independent size\n");
330 SwapDir::updateSize(size_t size
, int sign
)
332 int blks
= (size
+ fs
.blksize
- 1) / fs
.blksize
;
333 int k
= (blks
* fs
.blksize
>> 10) * sign
;
335 store_swap_size
+= k
;
344 StoreController::stat(StoreEntry
&output
) const
346 storeAppendPrintf(&output
, "Store Directory Statistics:\n");
347 storeAppendPrintf(&output
, "Store Entries : %lu\n",
348 (unsigned long int)StoreEntry::inUseCount());
349 storeAppendPrintf(&output
, "Maximum Swap Size : %8ld KB\n",
350 (long int) maxSize());
351 storeAppendPrintf(&output
, "Current Store Swap Size: %8lu KB\n",
353 storeAppendPrintf(&output
, "Current Capacity : %d%% used, %d%% free\n",
354 percent((int) store_swap_size
, (int) maxSize()),
355 percent((int) (maxSize() - store_swap_size
), (int) maxSize()));
356 /* FIXME Here we should output memory statistics */
358 /* now the swapDir */
359 swapDir
->stat(output
);
362 /* if needed, this could be taught to cache the result */
364 StoreController::maxSize() const
366 /* TODO: include memory cache ? */
367 return swapDir
->maxSize();
371 StoreController::minSize() const
373 /* TODO: include memory cache ? */
374 return swapDir
->minSize();
380 if (cur_size
>= max_size
)
385 debugs(20, 1, "WARNING: Shrinking cache_dir #" << index
<< " to " << cur_size
<< " KB");
389 storeDirOpenSwapLogs(void)
391 for (int dirn
= 0; dirn
< Config
.cacheSwap
.n_configured
; ++dirn
)
392 dynamic_cast<SwapDir
*>(INDEXSD(dirn
))->openLog();
396 storeDirCloseSwapLogs(void)
398 for (int dirn
= 0; dirn
< Config
.cacheSwap
.n_configured
; ++dirn
)
399 dynamic_cast<SwapDir
*>(INDEXSD(dirn
))->closeLog();
403 * storeDirWriteCleanLogs
405 * Writes a "clean" swap log file from in-memory metadata.
406 * This is a rewrite of the original function to troll each
407 * StoreDir and write the logs, and flush at the end of
408 * the run. Thanks goes to Eric Stern, since this solution
409 * came out of his COSS code.
412 storeDirWriteCleanLogs(int reopen
)
414 const StoreEntry
*e
= NULL
;
417 struct timeval start
;
419 RefCount
<SwapDir
> sd
;
423 if (store_dirs_rebuilding
) {
424 debug(20, 1) ("Not currently OK to rewrite swap log.\n");
425 debug(20, 1) ("storeDirWriteCleanLogs: Operation aborted.\n");
429 debug(20, 1) ("storeDirWriteCleanLogs: Starting...\n");
431 start
= current_time
;
433 for (dirn
= 0; dirn
< Config
.cacheSwap
.n_configured
; dirn
++) {
434 sd
= dynamic_cast<SwapDir
*>(INDEXSD(dirn
));
436 if (sd
->writeCleanStart() < 0) {
437 debug(20, 1) ("log.clean.start() failed for dir #%d\n", sd
->index
);
442 /* This writes all logs in parallel. It seems to me to be more efficient
443 * to write them sequentially. - RBC 20021214
448 for (dirn
= 0; dirn
< Config
.cacheSwap
.n_configured
; dirn
++) {
449 sd
= dynamic_cast<SwapDir
*>(INDEXSD(dirn
));
451 if (NULL
== sd
->cleanLog
)
454 e
= sd
->cleanLog
->nextEntry();
464 sd
->cleanLog
->write(*e
);
466 if ((++n
& 0xFFFF) == 0) {
468 debug(20, 1) (" %7d entries written so far.\n", n
);
474 for (dirn
= 0; dirn
< Config
.cacheSwap
.n_configured
; dirn
++)
475 dynamic_cast<SwapDir
*>(INDEXSD(dirn
))->writeCleanDone();
478 storeDirOpenSwapLogs();
482 dt
= tvSubDsec(start
, current_time
);
484 debug(20, 1) (" Finished. Wrote %d entries.\n", n
);
486 debug(20, 1) (" Took %3.1f seconds (%6.1f entries/sec).\n",
487 dt
, (double) n
/ (dt
> 0.0 ? dt
: 1.0));
493 StoreController::search(String
const url
, HttpRequest
*request
)
495 /* cheat, for now you can't search the memory hot cache */
496 return swapDir
->search(url
, request
);
500 StoreHashIndex::store(int const x
) const
506 StoreController::sync(void)
508 /* sync mem cache? */
513 * handle callbacks all avaliable fs'es
516 StoreController::callback()
518 /* This will likely double count. Thats ok. */
519 PROF_start(storeDirCallback
);
521 /* mem cache callbacks ? */
522 int result
= swapDir
->callback();
524 PROF_stop(storeDirCallback
);
530 storeDirGetBlkSize(const char *path
, int *blksize
)
536 if (statvfs(path
, &sfs
)) {
537 debug(50, 1) ("%s: %s\n", path
, xstrerror());
542 *blksize
= (int) sfs
.f_frsize
;
547 if (statfs(path
, &sfs
)) {
548 debug(50, 1) ("%s: %s\n", path
, xstrerror());
553 *blksize
= (int) sfs
.f_bsize
;
556 * Sanity check; make sure we have a meaningful value.
565 #define fsbtoblk(num, fsbs, bs) \
566 (((fsbs) != 0 && (fsbs) < (bs)) ? \
567 (num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs)))
569 storeDirGetUFSStats(const char *path
, int *totl_kb
, int *free_kb
, int *totl_in
, int *free_in
)
575 if (statvfs(path
, &sfs
)) {
576 debug(50, 1) ("%s: %s\n", path
, xstrerror());
580 *totl_kb
= (int) fsbtoblk(sfs
.f_blocks
, sfs
.f_frsize
, 1024);
581 *free_kb
= (int) fsbtoblk(sfs
.f_bfree
, sfs
.f_frsize
, 1024);
582 *totl_in
= (int) sfs
.f_files
;
583 *free_in
= (int) sfs
.f_ffree
;
588 if (statfs(path
, &sfs
)) {
589 debug(50, 1) ("%s: %s\n", path
, xstrerror());
593 *totl_kb
= (int) fsbtoblk(sfs
.f_blocks
, sfs
.f_bsize
, 1024);
594 *free_kb
= (int) fsbtoblk(sfs
.f_bfree
, sfs
.f_bsize
, 1024);
595 *totl_in
= (int) sfs
.f_files
;
596 *free_in
= (int) sfs
.f_ffree
;
603 allocate_new_swapdir(_SquidConfig::_cacheSwap
* swap
)
605 if (swap
->swapDirs
== NULL
) {
606 swap
->n_allocated
= 4;
607 swap
->swapDirs
= static_cast<StorePointer
*>(xcalloc(swap
->n_allocated
, sizeof(StorePointer
)));
610 if (swap
->n_allocated
== swap
->n_configured
) {
612 swap
->n_allocated
<<= 1;
613 tmp
= static_cast<StorePointer
*>(xcalloc(swap
->n_allocated
, sizeof(StorePointer
)));
614 xmemcpy(tmp
, swap
->swapDirs
, swap
->n_configured
* sizeof(SwapDir
*));
615 xfree(swap
->swapDirs
);
616 swap
->swapDirs
= tmp
;
621 free_cachedir(_SquidConfig::_cacheSwap
* swap
)
624 /* DON'T FREE THESE FOR RECONFIGURE */
629 for (i
= 0; i
< swap
->n_configured
; i
++) {
630 /* TODO XXX this lets the swapdir free resources asynchronously
631 * swap->swapDirs[i]->deactivate();
632 * but there may be such a means already.
635 swap
->swapDirs
[i
] = NULL
;
638 safe_free(swap
->swapDirs
);
639 swap
->swapDirs
= NULL
;
640 swap
->n_allocated
= 0;
641 swap
->n_configured
= 0;
644 /* this should be a virtual method on StoreEntry,
645 * i.e. e->referenced()
646 * so that the entry can notify the creating Store
649 StoreController::reference(StoreEntry
&e
)
651 /* Notify the fs that we're referencing this object again */
653 if (e
.swap_dirn
> -1)
654 e
.store()->reference(e
);
656 /* Notify the memory cache that we're referencing this object again */
658 if (mem_policy
->Referenced
)
659 mem_policy
->Referenced(mem_policy
, &e
, &e
.mem_obj
->repl
);
664 StoreController::dereference(StoreEntry
& e
)
666 /* Notify the fs that we're not referencing this object any more */
668 if (e
.swap_filen
> -1)
669 e
.store()->dereference(e
);
671 /* Notify the memory cache that we're not referencing this object any more */
673 if (mem_policy
->Dereferenced
)
674 mem_policy
->Dereferenced(mem_policy
, &e
, &e
.mem_obj
->repl
);
681 (const cache_key
*key
)
691 (String
const key
, STOREGETCLIENT callback
, void *cbdata
)
693 fatal("not implemented");
696 StoreHashIndex::StoreHashIndex()
698 assert (store_table
== NULL
);
701 StoreHashIndex::~StoreHashIndex()
704 hashFreeItems(store_table
, destroyStoreEntry
);
705 hashFreeMemory(store_table
);
711 StoreHashIndex::callback()
720 for (int i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
721 if (ndir
>= Config
.cacheSwap
.n_configured
)
722 ndir
= ndir
% Config
.cacheSwap
.n_configured
;
724 int temp_result
= store(ndir
)->callback();
730 result
+= temp_result
;
733 fatal ("too much io\n");
743 StoreHashIndex::create()
745 for (int i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++)
749 /* Lookup an object in the cache.
750 * return just a reference to object, don't start swapping in yet. */
754 (const cache_key
*key
)
756 PROF_start(storeGet
);
757 debug(20, 3) ("storeGet: looking up %s\n", storeKeyText(key
));
758 StoreEntry
*p
= static_cast<StoreEntry
*>(hash_lookup(store_table
, key
));
766 (String
const key
, STOREGETCLIENT callback
, void *cbdata
)
768 fatal("not implemented");
772 StoreHashIndex::init()
774 /* Calculate size of hash table (maximum currently 64k buckets). */
775 /* this is very bogus, its specific to the any Store maintaining an
776 * in-core index, not global */
777 size_t buckets
= Store::Root().maxSize() / Config
.Store
.avgObjectSize
;
778 debugs(20, 1, "Swap maxSize " << Store::Root().maxSize() <<
779 " KB, estimated " << buckets
<< " objects\n");
780 buckets
/= Config
.Store
.objectsPerBucket
;
781 debugs(20, 1, "Target number of buckets: " << buckets
);
782 /* ideally the full scan period should be configurable, for the
783 * moment it remains at approximately 24 hours. */
784 store_hash_buckets
= storeKeyHashBuckets(buckets
);
785 debugs(20, 1, "Using " << store_hash_buckets
<< " Store buckets");
786 debugs(20, 1, "Max Mem size: " << ( Config
.memMaxSize
>> 10) << " KB");
787 debugs(20, 1, "Max Swap size: " << Store::Root().maxSize() << " KB");
789 store_table
= hash_create(storeKeyHashCmp
,
790 store_hash_buckets
, storeKeyHashHash
);
792 for (int i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++)
793 /* this starts a search of the store dirs, loading their
794 * index. under the new Store api this should be
795 * driven by the StoreHashIndex, not by each store.
796 * Step 1: make the store rebuilds use a search internally
803 StoreHashIndex::maxSize() const
808 for (i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++)
809 result
+= store(i
)->maxSize();
815 StoreHashIndex::minSize() const
819 for (int i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++)
820 result
+= store(i
)->minSize();
826 StoreHashIndex::stat(StoreEntry
& output
) const
830 /* Now go through each store, calling its stat routine */
832 for (i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
833 storeAppendPrintf(&output
, "\n");
834 store(i
)->stat(output
);
839 StoreHashIndex::reference(StoreEntry
&)
843 StoreHashIndex::dereference(StoreEntry
&)
847 StoreHashIndex::maintain()
852 for (i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
853 /* XXX FixMe: This should be done "in parallell" on the different
854 * cache_dirs, not one at a time.
856 /* call the maintain function .. */
857 store(i
)->maintain();
862 StoreHashIndex::updateSize(unsigned int, int)
866 StoreHashIndex::sync()
868 for (int i
= 0; i
< Config
.cacheSwap
.n_configured
; ++i
)
873 StoreHashIndex::search(String
const url
, HttpRequest
*)
876 fatal ("Cannot search by url yet\n");
878 return new StoreSearchHashIndex (this);
881 CBDATA_CLASS_INIT(StoreSearchHashIndex
);
882 StoreSearchHashIndex::StoreSearchHashIndex(RefCount
<StoreHashIndex
> aSwapDir
) : sd(aSwapDir
), _done (false), bucket (0)
886 StoreSearchHashIndex::StoreSearchHashIndex(StoreSearchHashIndex const &);
889 StoreSearchHashIndex::~StoreSearchHashIndex()
893 StoreSearchHashIndex::next(void (callback
)(void *cbdata
), void *cbdata
)
900 StoreSearchHashIndex::next()
905 while (!isDone() && !entries
.size())
908 return currentItem() != NULL
;
912 StoreSearchHashIndex::error() const
918 StoreSearchHashIndex::isDone() const
920 return bucket
>= store_hash_buckets
|| _done
;
924 StoreSearchHashIndex::currentItem()
929 return entries
.back();
933 StoreSearchHashIndex::copyBucket()
935 /* probably need to lock the store entries...
936 * we copy them all to prevent races on the links. */
937 debugs(47, 3, "StoreSearchHashIndex::copyBucket #" << bucket
);
938 assert (!entries
.size());
939 hash_link
*link_ptr
= NULL
;
940 hash_link
*link_next
= NULL
;
941 link_next
= hash_get_bucket(store_table
, bucket
);
943 while (NULL
!= (link_ptr
= link_next
)) {
944 link_next
= link_ptr
->next
;
945 StoreEntry
*e
= (StoreEntry
*) link_ptr
;
947 entries
.push_back(e
);
951 debugs(47,3, "got entries: " << entries
.size());