3 * $Id: store.cc,v 1.510 1999/12/30 17:36:53 wessels Exp $
5 * DEBUG: section 20 Storage Manager
6 * AUTHOR: Harvest Derived
8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
9 * ----------------------------------------------------------
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
13 * National Laboratory for Applied Network Research and funded by the
14 * National Science Foundation. Squid is Copyrighted (C) 1998 by
15 * Duane Wessels and the University of California San Diego. Please
16 * see the COPYRIGHT file for full details. Squid incorporates
17 * software developed and/or copyrighted by other sources. Please see
18 * 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 #define REBUILD_TIMESTAMP_DELTA_MAX 2
40 #define STORE_IN_MEM_BUCKETS (229)
42 const char *memStatusStr
[] =
48 const char *pingStatusStr
[] =
55 const char *storeStatusStr
[] =
61 const char *swapStatusStr
[] =
68 typedef struct lock_ctrl_t
{
75 * local function prototypes
77 static int storeCheckExpired(const StoreEntry
*);
78 static int storeEntryLocked(const StoreEntry
*);
79 static int storeEntryValidLength(const StoreEntry
*);
80 static void storeGetMemSpace(int);
81 static void storeHashDelete(StoreEntry
*);
82 static MemObject
*new_MemObject(const char *, const char *);
83 static void destroy_MemObject(StoreEntry
*);
84 static FREE destroy_StoreEntry
;
85 static void storePurgeMem(StoreEntry
*);
86 static int getKeyCounter(void);
87 static int storeKeepInMemory(const StoreEntry
*);
88 static OBJH storeCheckCachableStats
;
89 static EVH storeLateRelease
;
91 static heap_key_func HeapKeyGen_StoreEntry_LFUDA
;
92 static heap_key_func HeapKeyGen_StoreEntry_GDSF
;
93 static heap_key_func HeapKeyGen_StoreEntry_LRU
;
101 * The heap equivalent of inmem_list, inmem_heap, is in globals.c so other
102 * modules can access it when updating object metadata (e.g., refcount)
105 static dlink_list inmem_list
;
107 static int store_pages_max
= 0;
108 static int store_swap_high
= 0;
109 static int store_swap_low
= 0;
110 static Stack LateReleaseStack
;
112 #if URL_CHECKSUM_DEBUG
114 url_checksum(const char *url
)
118 static unsigned char digest
[16];
120 MD5Update(&M
, (unsigned char *) url
, strlen(url
));
121 MD5Final(digest
, &M
);
122 xmemcpy(&ck
, digest
, sizeof(ck
));
128 new_MemObject(const char *url
, const char *log_url
)
130 MemObject
*mem
= memAllocate(MEM_MEMOBJECT
);
131 mem
->reply
= httpReplyCreate();
132 mem
->url
= xstrdup(url
);
133 #if URL_CHECKSUM_DEBUG
134 mem
->chksum
= url_checksum(mem
->url
);
136 mem
->log_url
= xstrdup(log_url
);
139 /* XXX account log_url */
140 debug(20, 3) ("new_MemObject: returning %p\n", mem
);
145 new_StoreEntry(int mem_obj_flag
, const char *url
, const char *log_url
)
147 StoreEntry
*e
= NULL
;
148 e
= memAllocate(MEM_STOREENTRY
);
150 e
->mem_obj
= new_MemObject(url
, log_url
);
151 debug(20, 3) ("new_StoreEntry: returning %p\n", e
);
152 e
->expires
= e
->lastmod
= e
->lastref
= e
->timestamp
= -1;
157 destroy_MemObject(StoreEntry
* e
)
159 MemObject
*mem
= e
->mem_obj
;
160 const Ctx ctx
= ctx_enter(mem
->url
);
161 debug(20, 3) ("destroy_MemObject: destroying %p\n", mem
);
162 #if URL_CHECKSUM_DEBUG
163 assert(mem
->chksum
== url_checksum(mem
->url
));
167 assert(mem
->swapout
.sio
== NULL
);
168 stmemFree(&mem
->data_hdr
);
171 * There is no way to abort FD-less clients, so they might
172 * still have mem->clients set if mem->fd == -1
174 assert(mem
->fd
== -1 || mem
->clients
== NULL
);
175 httpReplyDestroy(mem
->reply
);
176 requestUnlink(mem
->request
);
178 ctx_exit(ctx
); /* must exit before we free mem->url */
180 safe_free(mem
->log_url
); /* XXX account log_url */
181 memFree(mem
, MEM_MEMOBJECT
);
185 destroy_StoreEntry(void *data
)
187 StoreEntry
*e
= data
;
188 debug(20, 3) ("destroy_StoreEntry: destroying %p\n", e
);
191 destroy_MemObject(e
);
193 assert(e
->key
== NULL
);
194 memFree(e
, MEM_STOREENTRY
);
197 /* ----- INTERFACE BETWEEN STORAGE MANAGER AND HASH TABLE FUNCTIONS --------- */
200 storeHashInsert(StoreEntry
* e
, const cache_key
* key
)
202 debug(20, 3) ("storeHashInsert: Inserting Entry %p key '%s'\n",
203 e
, storeKeyText(key
));
204 e
->key
= storeKeyDup(key
);
205 hash_join(store_table
, (hash_link
*) e
);
207 if (EBIT_TEST(e
->flags
, ENTRY_SPECIAL
)) {
210 e
->node
= heap_insert(store_heap
, e
);
211 debug(20, 4) ("storeHashInsert: inserted node %p\n", e
->node
);
214 dlinkAdd(e
, &e
->lru
, &store_list
);
219 storeHashDelete(StoreEntry
* e
)
221 hash_remove_link(store_table
, (hash_link
*) e
);
224 debug(20, 4) ("storeHashDelete: deleting node %p\n", e
->node
);
225 heap_delete(store_heap
, e
->node
);
229 dlinkDelete(&e
->lru
, &store_list
);
231 storeKeyFree(e
->key
);
235 /* -------------------------------------------------------------------------- */
238 /* get rid of memory copy of the object */
239 /* Only call this if storeCheckPurgeMem(e) returns 1 */
241 storePurgeMem(StoreEntry
* e
)
243 if (e
->mem_obj
== NULL
)
245 debug(20, 3) ("storePurgeMem: Freeing memory-copy of %s\n",
246 storeKeyText(e
->key
));
247 storeSetMemStatus(e
, NOT_IN_MEMORY
);
248 destroy_MemObject(e
);
249 if (e
->swap_status
!= SWAPOUT_DONE
)
254 storeLockObject(StoreEntry
* e
)
256 if (e
->lock_count
++ == 0) {
259 * There is no reason to take any action here. Squid by
260 * default is moving locked objects to the end of the LRU
261 * list to keep them from getting bumped into by the
262 * replacement algorithm. We can't do that so we will just
263 * have to handle them.
265 debug(20, 4) ("storeLockObject: just locked node %p\n", e
->node
);
267 dlinkDelete(&e
->lru
, &store_list
);
268 dlinkAdd(e
, &e
->lru
, &store_list
);
271 debug(20, 3) ("storeLockObject: key '%s' count=%d\n",
272 storeKeyText(e
->key
), (int) e
->lock_count
);
273 e
->lastref
= squid_curtime
;
277 storeReleaseRequest(StoreEntry
* e
)
279 if (EBIT_TEST(e
->flags
, RELEASE_REQUEST
))
281 debug(20, 3) ("storeReleaseRequest: '%s'\n", storeKeyText(e
->key
));
282 EBIT_SET(e
->flags
, RELEASE_REQUEST
);
284 * Clear cachable flag here because we might get called before
285 * anyone else even looks at the cachability flag. Also, this
286 * prevents httpMakePublic from really setting a public key.
288 EBIT_CLR(e
->flags
, ENTRY_CACHABLE
);
289 storeSetPrivateKey(e
);
292 /* unlock object, return -1 if object get released after unlock
293 * otherwise lock_count */
295 storeUnlockObject(StoreEntry
* e
)
298 debug(20, 3) ("storeUnlockObject: key '%s' count=%d\n",
299 storeKeyText(e
->key
), e
->lock_count
);
301 return (int) e
->lock_count
;
302 if (e
->store_status
== STORE_PENDING
)
303 EBIT_SET(e
->flags
, RELEASE_REQUEST
);
304 assert(storePendingNClients(e
) == 0);
306 storeHeapPositionUpdate(e
);
308 if (EBIT_TEST(e
->flags
, RELEASE_REQUEST
))
310 else if (storeKeepInMemory(e
)) {
311 storeSetMemStatus(e
, IN_MEMORY
);
312 requestUnlink(e
->mem_obj
->request
);
313 e
->mem_obj
->request
= NULL
;
316 if (EBIT_TEST(e
->flags
, KEY_PRIVATE
)) {
319 * Squid/LRU is moving things around in the linked list in order
320 * to keep from bumping into them when purging from the LRU list.
322 debug(20, 4) ("storeUnlockObject: purged private node %p\n",
325 dlinkDelete(&e
->lru
, &store_list
);
326 dlinkAddTail(e
, &e
->lru
, &store_list
);
333 /* Lookup an object in the cache.
334 * return just a reference to object, don't start swapping in yet. */
336 storeGet(const cache_key
* key
)
338 debug(20, 3) ("storeGet: looking up %s\n", storeKeyText(key
));
339 return (StoreEntry
*) hash_lookup(store_table
, key
);
343 storeGetPublic(const char *uri
, const method_t method
)
345 return storeGet(storeKeyPublic(uri
, method
));
351 static int key_counter
= 0;
352 if (++key_counter
< 0)
358 storeSetPrivateKey(StoreEntry
* e
)
360 const cache_key
*newkey
;
361 MemObject
*mem
= e
->mem_obj
;
362 if (e
->key
&& EBIT_TEST(e
->flags
, KEY_PRIVATE
))
363 return; /* is already private */
365 if (e
->swap_file_number
> -1)
366 storeDirSwapLog(e
, SWAP_LOG_DEL
);
370 mem
->id
= getKeyCounter();
371 newkey
= storeKeyPrivate(mem
->url
, mem
->method
, mem
->id
);
373 newkey
= storeKeyPrivate("JUNK", METHOD_NONE
, getKeyCounter());
375 assert(hash_lookup(store_table
, newkey
) == NULL
);
376 EBIT_SET(e
->flags
, KEY_PRIVATE
);
377 storeHashInsert(e
, newkey
);
381 storeSetPublicKey(StoreEntry
* e
)
383 StoreEntry
*e2
= NULL
;
384 const cache_key
*newkey
;
385 MemObject
*mem
= e
->mem_obj
;
386 if (e
->key
&& !EBIT_TEST(e
->flags
, KEY_PRIVATE
))
387 return; /* is already public */
390 * We can't make RELEASE_REQUEST objects public. Depending on
391 * when RELEASE_REQUEST gets set, we might not be swapping out
392 * the object. If we're not swapping out, then subsequent
393 * store clients won't be able to access object data which has
394 * been freed from memory.
396 * If RELEASE_REQUEST is set, then ENTRY_CACHABLE should not
397 * be set, and storeSetPublicKey() should not be called.
400 if (EBIT_TEST(e
->flags
, RELEASE_REQUEST
))
401 debug(20, 1) ("assertion failed: RELEASE key %s, url %s\n",
404 assert(!EBIT_TEST(e
->flags
, RELEASE_REQUEST
));
405 newkey
= storeKeyPublic(mem
->url
, mem
->method
);
406 if ((e2
= (StoreEntry
*) hash_lookup(store_table
, newkey
))) {
407 debug(20, 3) ("storeSetPublicKey: Making old '%s' private.\n", mem
->url
);
408 storeSetPrivateKey(e2
);
410 newkey
= storeKeyPublic(mem
->url
, mem
->method
);
414 EBIT_CLR(e
->flags
, KEY_PRIVATE
);
415 storeHashInsert(e
, newkey
);
416 if (e
->swap_file_number
> -1)
417 storeDirSwapLog(e
, SWAP_LOG_ADD
);
421 storeCreateEntry(const char *url
, const char *log_url
, request_flags flags
, method_t method
)
423 StoreEntry
*e
= NULL
;
424 MemObject
*mem
= NULL
;
425 debug(20, 3) ("storeCreateEntry: '%s'\n", url
);
427 e
= new_StoreEntry(STORE_ENTRY_WITH_MEMOBJ
, url
, log_url
);
428 e
->lock_count
= 1; /* Note lock here w/o calling storeLock() */
430 mem
->method
= method
;
431 if (neighbors_do_private_keys
|| !flags
.hierarchical
)
432 storeSetPrivateKey(e
);
434 storeSetPublicKey(e
);
435 if (flags
.cachable
) {
436 EBIT_SET(e
->flags
, ENTRY_CACHABLE
);
437 EBIT_CLR(e
->flags
, RELEASE_REQUEST
);
439 EBIT_CLR(e
->flags
, ENTRY_CACHABLE
);
440 storeReleaseRequest(e
);
442 e
->store_status
= STORE_PENDING
;
443 storeSetMemStatus(e
, NOT_IN_MEMORY
);
444 e
->swap_status
= SWAPOUT_NONE
;
445 e
->swap_file_number
= -1;
447 e
->lastref
= squid_curtime
;
448 e
->timestamp
= 0; /* set in storeTimestampsSet() */
449 e
->ping_status
= PING_NONE
;
450 EBIT_SET(e
->flags
, ENTRY_VALIDATED
);
454 /* Mark object as expired */
456 storeExpireNow(StoreEntry
* e
)
458 debug(20, 3) ("storeExpireNow: '%s'\n", storeKeyText(e
->key
));
459 e
->expires
= squid_curtime
;
462 /* Append incoming data from a primary server to an entry. */
464 storeAppend(StoreEntry
* e
, const char *buf
, int len
)
466 MemObject
*mem
= e
->mem_obj
;
469 assert(e
->store_status
== STORE_PENDING
);
471 debug(20, 5) ("storeAppend: appending %d bytes for '%s'\n",
473 storeKeyText(e
->key
));
474 storeGetMemSpace(len
);
475 stmemAppend(&mem
->data_hdr
, buf
, len
);
476 mem
->inmem_hi
+= len
;
478 if (EBIT_TEST(e
->flags
, DELAY_SENDING
))
486 storeAppendPrintf(StoreEntry
* e
, const char *fmt
,...)
488 storeAppendPrintf(va_alist
)
497 StoreEntry
*e
= NULL
;
498 const char *fmt
= NULL
;
500 e
= va_arg(args
, StoreEntry
*);
501 fmt
= va_arg(args
, char *);
503 storeAppendVPrintf(e
, fmt
, args
);
507 /* used be storeAppendPrintf and Packer */
509 storeAppendVPrintf(StoreEntry
* e
, const char *fmt
, va_list vargs
)
511 LOCAL_ARRAY(char, buf
, 4096);
513 vsnprintf(buf
, 4096, fmt
, vargs
);
514 storeAppend(e
, buf
, strlen(buf
));
517 struct _store_check_cachable_hist
{
520 int not_entry_cachable
;
522 int wrong_content_length
;
526 int too_many_open_files
;
527 int too_many_open_fds
;
533 } store_check_cachable_hist
;
536 storeTooManyDiskFilesOpen(void)
538 if (Config
.max_open_disk_fds
== 0)
540 if (store_open_disk_fd
> Config
.max_open_disk_fds
)
546 storeCheckCachable(StoreEntry
* e
)
548 #if CACHE_ALL_METHODS
549 if (e
->mem_obj
->method
!= METHOD_GET
) {
550 debug(20, 2) ("storeCheckCachable: NO: non-GET method\n");
551 store_check_cachable_hist
.no
.non_get
++;
554 if (!EBIT_TEST(e
->flags
, ENTRY_CACHABLE
)) {
555 debug(20, 2) ("storeCheckCachable: NO: not cachable\n");
556 store_check_cachable_hist
.no
.not_entry_cachable
++;
557 } else if (EBIT_TEST(e
->flags
, RELEASE_REQUEST
)) {
558 debug(20, 2) ("storeCheckCachable: NO: release requested\n");
559 store_check_cachable_hist
.no
.release_request
++;
560 } else if (e
->store_status
== STORE_OK
&& EBIT_TEST(e
->flags
, ENTRY_BAD_LENGTH
)) {
561 debug(20, 2) ("storeCheckCachable: NO: wrong content-length\n");
562 store_check_cachable_hist
.no
.wrong_content_length
++;
563 } else if (EBIT_TEST(e
->flags
, ENTRY_NEGCACHED
)) {
564 debug(20, 3) ("storeCheckCachable: NO: negative cached\n");
565 store_check_cachable_hist
.no
.negative_cached
++;
566 return 0; /* avoid release call below */
567 } else if (e
->mem_obj
->inmem_hi
> Config
.Store
.maxObjectSize
) {
568 debug(20, 2) ("storeCheckCachable: NO: too big\n");
569 store_check_cachable_hist
.no
.too_big
++;
570 } else if (e
->mem_obj
->reply
->content_length
> (int) Config
.Store
.maxObjectSize
) {
571 debug(20, 2) ("storeCheckCachable: NO: too big\n");
572 store_check_cachable_hist
.no
.too_big
++;
573 } else if (EBIT_TEST(e
->flags
, KEY_PRIVATE
)) {
574 debug(20, 3) ("storeCheckCachable: NO: private key\n");
575 store_check_cachable_hist
.no
.private_key
++;
576 } else if (e
->swap_status
!= SWAPOUT_NONE
) {
578 * here we checked the swap_status because the remaining
579 * cases are only relevant only if we haven't started swapping
580 * out the object yet.
583 } else if (storeTooManyDiskFilesOpen()) {
584 debug(20, 2) ("storeCheckCachable: NO: too many disk files open\n");
585 store_check_cachable_hist
.no
.too_many_open_files
++;
586 } else if (fdNFree() < RESERVED_FD
) {
587 debug(20, 2) ("storeCheckCachable: NO: too many FD's open\n");
588 store_check_cachable_hist
.no
.too_many_open_fds
++;
591 * With the HEAP-based replacement policies a low reference
592 * age should not prevent cacheability of an object. We
593 * do not use LRU age at all.
596 } else if (storeExpiredReferenceAge() < 300) {
597 debug(20, 2) ("storeCheckCachable: NO: LRU Age = %d\n",
598 storeExpiredReferenceAge());
599 store_check_cachable_hist
.no
.lru_age_too_low
++;
602 store_check_cachable_hist
.yes
.Default
++;
605 storeReleaseRequest(e
);
606 EBIT_CLR(e
->flags
, ENTRY_CACHABLE
);
611 storeCheckCachableStats(StoreEntry
* sentry
)
613 storeAppendPrintf(sentry
, "Category\t Count\n");
615 storeAppendPrintf(sentry
, "no.non_get\t%d\n",
616 store_check_cachable_hist
.no
.non_get
);
617 storeAppendPrintf(sentry
, "no.not_entry_cachable\t%d\n",
618 store_check_cachable_hist
.no
.not_entry_cachable
);
619 storeAppendPrintf(sentry
, "no.release_request\t%d\n",
620 store_check_cachable_hist
.no
.release_request
);
621 storeAppendPrintf(sentry
, "no.wrong_content_length\t%d\n",
622 store_check_cachable_hist
.no
.wrong_content_length
);
623 storeAppendPrintf(sentry
, "no.negative_cached\t%d\n",
624 store_check_cachable_hist
.no
.negative_cached
);
625 storeAppendPrintf(sentry
, "no.too_big\t%d\n",
626 store_check_cachable_hist
.no
.too_big
);
627 storeAppendPrintf(sentry
, "no.private_key\t%d\n",
628 store_check_cachable_hist
.no
.private_key
);
629 storeAppendPrintf(sentry
, "no.too_many_open_files\t%d\n",
630 store_check_cachable_hist
.no
.too_many_open_files
);
631 storeAppendPrintf(sentry
, "no.too_many_open_fds\t%d\n",
632 store_check_cachable_hist
.no
.too_many_open_fds
);
633 storeAppendPrintf(sentry
, "no.lru_age_too_low\t%d\n",
634 store_check_cachable_hist
.no
.lru_age_too_low
);
635 storeAppendPrintf(sentry
, "yes.default\t%d\n",
636 store_check_cachable_hist
.yes
.Default
);
639 /* Complete transfer into the local cache. */
641 storeComplete(StoreEntry
* e
)
643 debug(20, 3) ("storeComplete: '%s'\n", storeKeyText(e
->key
));
644 if (e
->store_status
!= STORE_PENDING
) {
646 * if we're not STORE_PENDING, then probably we got aborted
647 * and there should be NO clients on this entry
649 assert(EBIT_TEST(e
->flags
, ENTRY_ABORTED
));
650 assert(e
->mem_obj
->nclients
== 0);
653 e
->mem_obj
->object_sz
= e
->mem_obj
->inmem_hi
;
654 e
->store_status
= STORE_OK
;
655 assert(e
->mem_status
== NOT_IN_MEMORY
);
656 if (!storeEntryValidLength(e
)) {
657 EBIT_SET(e
->flags
, ENTRY_BAD_LENGTH
);
658 storeReleaseRequest(e
);
660 #if USE_CACHE_DIGESTS
661 if (e
->mem_obj
->request
)
662 e
->mem_obj
->request
->hier
.store_complete_stop
= current_time
;
669 * Someone wants to abort this transfer. Set the reason in the
670 * request structure, call the server-side callback and mark the
671 * entry for releasing
674 storeAbort(StoreEntry
* e
)
676 MemObject
*mem
= e
->mem_obj
;
677 assert(e
->store_status
== STORE_PENDING
);
679 debug(20, 6) ("storeAbort: %s\n", storeKeyText(e
->key
));
680 storeLockObject(e
); /* lock while aborting */
681 storeNegativeCache(e
);
682 storeReleaseRequest(e
);
683 EBIT_SET(e
->flags
, ENTRY_ABORTED
);
684 storeSetMemStatus(e
, NOT_IN_MEMORY
);
685 e
->store_status
= STORE_OK
;
687 * We assign an object length here. The only other place we assign
688 * the object length is in storeComplete()
690 mem
->object_sz
= mem
->inmem_hi
;
691 /* Notify the server side */
692 if (mem
->abort
.callback
) {
693 eventAdd("mem->abort.callback",
698 mem
->abort
.callback
= NULL
;
699 mem
->abort
.data
= NULL
;
701 /* Notify the client side */
703 /* Do we need to close the swapout file? */
704 /* Not if we never started swapping out */
705 if (e
->swap_file_number
> -1) {
706 storeSwapOutFileClose(e
);
708 storeUnlockObject(e
); /* unlock */
711 /* Clear Memory storage to accommodate the given object len */
713 storeGetMemSpace(int size
)
715 StoreEntry
*e
= NULL
;
717 static time_t last_check
= 0;
720 #if !HEAP_REPLACEMENT
723 dlink_node
*prev
= NULL
;
726 heap_key min_age
= 0.0;
727 link_list
*locked_entries
= NULL
;
729 if (squid_curtime
== last_check
)
731 last_check
= squid_curtime
;
732 pages_needed
= (size
/ SM_PAGE_SIZE
) + 1;
733 if (memInUse(MEM_STMEM_BUF
) + pages_needed
< store_pages_max
)
735 if (store_dirs_rebuilding
)
737 debug(20, 2) ("storeGetMemSpace: Starting, need %d pages\n", pages_needed
);
739 while (heap_nodes(inmem_heap
) > 0) {
740 age
= heap_peepminkey(inmem_heap
);
741 e
= heap_extractmin(inmem_heap
);
742 e
->mem_obj
->node
= NULL
; /* no longer in the heap */
743 if (storeEntryLocked(e
)) {
745 debug(20, 5) ("storeGetMemSpace: locked key %s\n",
746 storeKeyText(e
->key
));
747 linklistPush(&locked_entries
, e
);
751 debug(20, 3) ("Released memory object with key %f size %d refs %d url %s\n",
752 age
, e
->swap_file_sz
, e
->refcount
, e
->mem_obj
->url
);
755 if (memInUse(MEM_STMEM_BUF
) + pages_needed
< store_pages_max
)
759 * Increase the heap age factor.
762 inmem_heap
->age
= min_age
;
764 * Reinsert all bumped locked entries back into heap...
766 while ((e
= linklistShift(&locked_entries
)))
767 e
->mem_obj
->node
= heap_insert(inmem_heap
, e
);
769 head
= inmem_list
.head
;
770 for (m
= inmem_list
.tail
; m
; m
= prev
) {
775 if (storeEntryLocked(e
)) {
777 dlinkDelete(m
, &inmem_list
);
778 dlinkAdd(e
, m
, &inmem_list
);
783 if (memInUse(MEM_STMEM_BUF
) + pages_needed
< store_pages_max
)
787 debug(20, 3) ("storeGetMemSpace: released %d/%d locked %d\n",
788 released
, hot_obj_count
, locked
);
789 debug(20, 3) ("storeGetMemSpace stats:\n");
790 debug(20, 3) (" %6d HOT objects\n", hot_obj_count
);
791 debug(20, 3) (" %6d were released\n", released
);
794 /* The maximum objects to scan for maintain storage space */
795 #define MAINTAIN_MAX_SCAN 1024
796 #define MAINTAIN_MAX_REMOVE 64
799 * This routine is to be called by main loop in main.c.
800 * It removes expired objects on only one bucket for each time called.
801 * returns the number of objects removed
803 * This should get called 1/s from main().
806 storeMaintainSwapSpace(void *datanotused
)
808 StoreEntry
*e
= NULL
;
815 static time_t last_warn_time
= 0;
816 #if !HEAP_REPLACEMENT
818 dlink_node
*prev
= NULL
;
821 heap_key min_age
= 0.0;
822 link_list
*locked_entries
= NULL
;
823 #if HEAP_REPLACEMENT_DEBUG
824 if (!verify_heap_property(store_heap
)) {
825 debug(20, 1) ("Heap property violated!\n");
829 /* We can't delete objects while rebuilding swap */
830 if (store_dirs_rebuilding
) {
831 eventAdd("MaintainSwapSpace", storeMaintainSwapSpace
, NULL
, 1.0, 1);
834 f
= (double) (store_swap_size
- store_swap_low
) / (store_swap_high
- store_swap_low
);
835 f
= f
< 0.0 ? 0.0 : f
> 1.0 ? 1.0 : f
;
836 max_scan
= (int) (f
* 400.0 + 100.0);
837 max_remove
= (int) (f
* 70.0 + 10.0);
838 eventAdd("MaintainSwapSpace", storeMaintainSwapSpace
, NULL
, 1.0 - f
, 1);
840 debug(20, 3) ("storeMaintainSwapSpace: f=%f, max_scan=%d, max_remove=%d\n",
841 f
, max_scan
, max_remove
);
843 while (heap_nodes(store_heap
) > 0) {
844 if (store_swap_size
< store_swap_low
)
846 if (expired
>= max_remove
)
848 if (scanned
>= max_scan
)
850 age
= heap_peepminkey(store_heap
);
851 e
= heap_extractmin(store_heap
);
852 e
->node
= NULL
; /* no longer in the heap */
854 if (storeEntryLocked(e
)) {
856 * Entry is in use ... put it in a linked list to ignore it.
858 if (!EBIT_TEST(e
->flags
, ENTRY_SPECIAL
)) {
860 * If this was a "SPECIAL" do not add it back into the heap.
861 * It will always be "SPECIAL" and therefore never removed.
863 debug(20, 4) ("storeMaintainSwapSpace: locked url %s\n",
864 (e
->mem_obj
&& e
->mem_obj
->url
) ? e
->mem_obj
->url
: storeKeyText(e
->key
));
865 linklistPush(&locked_entries
, e
);
869 } else if (storeCheckExpired(e
)) {
871 * Note: This will not check the reference age ifdef
872 * HEAP_REPLACEMENT, but it does some other useful
876 debug(20, 3) ("Released store object age %f size %d refs %d key %s\n",
877 age
, e
->swap_file_sz
, e
->refcount
, storeKeyText(e
->key
));
882 * Did not expire the object so we need to add it back
885 debug(20, 5) ("storeMaintainSwapSpace: non-expired %s\n",
886 storeKeyText(e
->key
));
887 linklistPush(&locked_entries
, e
);
890 if (store_swap_size
< store_swap_low
)
892 else if (expired
>= max_remove
)
894 else if (scanned
>= max_scan
)
898 * Bump the heap age factor.
901 store_heap
->age
= min_age
;
903 * Reinsert all bumped locked entries back into heap...
905 while ((e
= linklistShift(&locked_entries
)))
906 e
->node
= heap_insert(store_heap
, e
);
908 for (m
= store_list
.tail
; m
; m
= prev
) {
912 if (storeEntryLocked(e
)) {
914 * If there is a locked entry at the tail of the LRU list,
915 * move it to the beginning to get it out of the way.
916 * Theoretically, we might have all locked objects at the
917 * tail, and then we'll never remove anything here and the
918 * LRU age will go to zero.
920 if (memInUse(MEM_STOREENTRY
) > max_scan
) {
921 dlinkDelete(&e
->lru
, &store_list
);
922 dlinkAdd(e
, &e
->lru
, &store_list
);
925 } else if (storeCheckExpired(e
)) {
929 if (expired
>= max_remove
)
931 if (scanned
>= max_scan
)
935 debug(20, (expired
? 2 : 3)) ("storeMaintainSwapSpace: scanned %d/%d removed %d/%d locked %d f=%.03f\n",
936 scanned
, max_scan
, expired
, max_remove
, locked
, f
);
937 debug(20, 3) ("storeMaintainSwapSpace stats:\n");
938 debug(20, 3) (" %6d objects\n", memInUse(MEM_STOREENTRY
));
939 debug(20, 3) (" %6d were scanned\n", scanned
);
940 debug(20, 3) (" %6d were locked\n", locked
);
941 debug(20, 3) (" %6d were expired\n", expired
);
942 if (store_swap_size
< Config
.Swap
.maxSize
)
944 if (squid_curtime
- last_warn_time
< 10)
946 debug(20, 0) ("WARNING: Disk space over limit: %d KB > %d KB\n",
947 store_swap_size
, Config
.Swap
.maxSize
);
948 last_warn_time
= squid_curtime
;
952 /* release an object from a cache */
953 /* return number of objects released. */
955 storeRelease(StoreEntry
* e
)
957 debug(20, 3) ("storeRelease: Releasing: '%s'\n", storeKeyText(e
->key
));
958 /* If, for any reason we can't discard this object because of an
959 * outstanding request, mark it for pending release */
960 if (storeEntryLocked(e
)) {
962 debug(20, 3) ("storeRelease: Only setting RELEASE_REQUEST bit\n");
963 storeReleaseRequest(e
);
966 if (store_dirs_rebuilding
&& e
->swap_file_number
> -1) {
967 storeSetPrivateKey(e
);
969 storeSetMemStatus(e
, NOT_IN_MEMORY
);
970 destroy_MemObject(e
);
973 * Fake a call to storeLockObject(). When rebuilding is done,
974 * we'll just call storeUnlockObject() on these.
977 stackPush(&LateReleaseStack
, e
);
980 storeLog(STORE_LOG_RELEASE
, e
);
981 if (e
->swap_file_number
> -1) {
982 storeUnlink(e
->swap_file_number
);
983 storeDirMapBitReset(e
->swap_file_number
);
984 if (e
->swap_status
== SWAPOUT_DONE
)
985 if (EBIT_TEST(e
->flags
, ENTRY_VALIDATED
))
986 storeDirUpdateSwapSize(e
->swap_file_number
, e
->swap_file_sz
, -1);
987 if (!EBIT_TEST(e
->flags
, KEY_PRIVATE
))
988 storeDirSwapLog(e
, SWAP_LOG_DEL
);
990 storeSetMemStatus(e
, NOT_IN_MEMORY
);
991 destroy_StoreEntry(e
);
995 storeLateRelease(void *unused
)
1000 if (store_dirs_rebuilding
) {
1001 eventAdd("storeLateRelease", storeLateRelease
, NULL
, 1.0, 1);
1004 for (i
= 0; i
< 10; i
++) {
1005 e
= stackPop(&LateReleaseStack
);
1008 debug(20, 1) ("storeLateRelease: released %d objects\n", n
);
1011 storeUnlockObject(e
);
1014 eventAdd("storeLateRelease", storeLateRelease
, NULL
, 0.0, 1);
1017 /* return 1 if a store entry is locked */
1019 storeEntryLocked(const StoreEntry
* e
)
1023 if (e
->swap_status
== SWAPOUT_WRITING
)
1025 if (e
->store_status
== STORE_PENDING
)
1028 * SPECIAL, PUBLIC entries should be "locked"
1030 if (EBIT_TEST(e
->flags
, ENTRY_SPECIAL
))
1031 if (!EBIT_TEST(e
->flags
, KEY_PRIVATE
))
1037 storeEntryValidLength(const StoreEntry
* e
)
1040 const HttpReply
*reply
;
1041 assert(e
->mem_obj
!= NULL
);
1042 reply
= e
->mem_obj
->reply
;
1043 debug(20, 3) ("storeEntryValidLength: Checking '%s'\n", storeKeyText(e
->key
));
1044 debug(20, 5) ("storeEntryValidLength: object_len = %d\n",
1046 debug(20, 5) ("storeEntryValidLength: hdr_sz = %d\n",
1048 debug(20, 5) ("storeEntryValidLength: content_length = %d\n",
1049 reply
->content_length
);
1050 if (reply
->content_length
< 0) {
1051 debug(20, 5) ("storeEntryValidLength: Unspecified content length: %s\n",
1052 storeKeyText(e
->key
));
1055 if (reply
->hdr_sz
== 0) {
1056 debug(20, 5) ("storeEntryValidLength: Zero header size: %s\n",
1057 storeKeyText(e
->key
));
1060 if (e
->mem_obj
->method
== METHOD_HEAD
) {
1061 debug(20, 5) ("storeEntryValidLength: HEAD request: %s\n",
1062 storeKeyText(e
->key
));
1065 if (reply
->sline
.status
== HTTP_NOT_MODIFIED
)
1067 if (reply
->sline
.status
== HTTP_NO_CONTENT
)
1069 diff
= reply
->hdr_sz
+ reply
->content_length
- objectLen(e
);
1072 debug(20, 3) ("storeEntryValidLength: %d bytes too %s; '%s'\n",
1073 diff
< 0 ? -diff
: diff
,
1074 diff
< 0 ? "big" : "small",
1075 storeKeyText(e
->key
));
1080 storeInitHashValues(void)
1083 /* Calculate size of hash table (maximum currently 64k buckets). */
1084 i
= Config
.Swap
.maxSize
/ Config
.Store
.avgObjectSize
;
1085 debug(20, 1) ("Swap maxSize %d KB, estimated %d objects\n",
1086 Config
.Swap
.maxSize
, i
);
1087 i
/= Config
.Store
.objectsPerBucket
;
1088 debug(20, 1) ("Target number of buckets: %d\n", i
);
1089 /* ideally the full scan period should be configurable, for the
1090 * moment it remains at approximately 24 hours. */
1091 store_hash_buckets
= storeKeyHashBuckets(i
);
1092 debug(20, 1) ("Using %d Store buckets\n", store_hash_buckets
);
1093 debug(20, 1) ("Max Mem size: %d KB\n", Config
.memMaxSize
>> 10);
1094 debug(20, 1) ("Max Swap size: %d KB\n", Config
.Swap
.maxSize
);
1097 #if HEAP_REPLACEMENT
1098 #include "store_heap_replacement.c"
1105 storeInitHashValues();
1106 store_table
= hash_create(storeKeyHashCmp
,
1107 store_hash_buckets
, storeKeyHashHash
);
1110 #if HEAP_REPLACEMENT
1112 * Create new heaps with cache replacement policies attached to them.
1113 * The cache replacement policy is specified as either GDSF or LFUDA in
1114 * the squid.conf configuration file. Note that the replacement policy
1115 * applies only to the disk replacement algorithm. Memory replacement
1116 * always uses GDSF since we want to maximize object hit rate.
1118 inmem_heap
= new_heap(1000, HeapKeyGen_StoreEntry_GDSF
);
1119 if (Config
.replPolicy
) {
1120 if (tolower(Config
.replPolicy
[0]) == 'g') {
1121 debug(20, 1) ("Using GDSF disk replacement policy\n");
1122 store_heap
= new_heap(10000, HeapKeyGen_StoreEntry_GDSF
);
1123 } else if (tolower(Config
.replPolicy
[0]) == 'l') {
1124 if (tolower(Config
.replPolicy
[1]) == 'f') {
1125 debug(20, 1) ("Using LFUDA disk replacement policy\n");
1126 store_heap
= new_heap(10000, HeapKeyGen_StoreEntry_LFUDA
);
1127 } else if (tolower(Config
.replPolicy
[1]) == 'r') {
1128 debug(20, 1) ("Using LRU heap disk replacement policy\n");
1129 store_heap
= new_heap(10000, HeapKeyGen_StoreEntry_LRU
);
1132 debug(20, 1) ("Unrecognized replacement_policy; using GDSF\n");
1133 store_heap
= new_heap(10000, HeapKeyGen_StoreEntry_GDSF
);
1136 debug(20, 1) ("Using default disk replacement policy (GDSF)\n");
1137 store_heap
= new_heap(10000, HeapKeyGen_StoreEntry_GDSF
);
1140 store_list
.head
= store_list
.tail
= NULL
;
1141 inmem_list
.head
= inmem_list
.tail
= NULL
;
1143 stackInit(&LateReleaseStack
);
1144 eventAdd("storeLateRelease", storeLateRelease
, NULL
, 1.0, 1);
1146 storeRebuildStart();
1147 cachemgrRegister("storedir",
1148 "Store Directory Stats",
1149 storeDirStats
, 0, 1);
1150 cachemgrRegister("store_check_cachable_stats",
1151 "storeCheckCachable() Stats",
1152 storeCheckCachableStats
, 0, 1);
1156 storeConfigure(void)
1158 store_swap_high
= (long) (((float) Config
.Swap
.maxSize
*
1159 (float) Config
.Swap
.highWaterMark
) / (float) 100);
1160 store_swap_low
= (long) (((float) Config
.Swap
.maxSize
*
1161 (float) Config
.Swap
.lowWaterMark
) / (float) 100);
1162 store_pages_max
= Config
.memMaxSize
/ SM_PAGE_SIZE
;
1166 storeKeepInMemory(const StoreEntry
* e
)
1168 MemObject
*mem
= e
->mem_obj
;
1171 if (mem
->data_hdr
.head
== NULL
)
1173 return mem
->inmem_lo
== 0;
1177 storeCheckExpired(const StoreEntry
* e
)
1179 if (storeEntryLocked(e
))
1181 if (EBIT_TEST(e
->flags
, RELEASE_REQUEST
))
1183 if (EBIT_TEST(e
->flags
, ENTRY_NEGCACHED
) && squid_curtime
>= e
->expires
)
1185 #if HEAP_REPLACEMENT
1187 * With HEAP_REPLACEMENT we are not using the LRU reference age, the heap
1188 * controls the replacement of objects.
1192 if (squid_curtime
- e
->lastref
> storeExpiredReferenceAge())
1198 #if !HEAP_REPLACEMENT
1200 * storeExpiredReferenceAge
1202 * The LRU age is scaled exponentially between 1 minute and
1203 * Config.referenceAge , when store_swap_low < store_swap_size <
1204 * store_swap_high. This keeps store_swap_size within the low and high
1205 * water marks. If the cache is very busy then store_swap_size stays
1206 * closer to the low water mark, if it is not busy, then it will stay
1207 * near the high water mark. The LRU age value can be examined on the
1208 * cachemgr 'info' page.
1211 storeExpiredReferenceAge(void)
1216 x
= (double) (store_swap_high
- store_swap_size
) / (store_swap_high
- store_swap_low
);
1217 x
= x
< 0.0 ? 0.0 : x
> 1.0 ? 1.0 : x
;
1218 z
= pow((double) (Config
.referenceAge
/ 60), x
);
1219 age
= (time_t) (z
* 60.0);
1222 else if (age
> 31536000)
1229 storeNegativeCache(StoreEntry
* e
)
1231 e
->expires
= squid_curtime
+ Config
.negativeTtl
;
1232 EBIT_SET(e
->flags
, ENTRY_NEGCACHED
);
1236 storeFreeMemory(void)
1238 hashFreeItems(store_table
, destroy_StoreEntry
);
1239 hashFreeMemory(store_table
);
1241 #if USE_CACHE_DIGESTS
1243 cacheDigestDestroy(store_digest
);
1245 store_digest
= NULL
;
1249 expiresMoreThan(time_t expires
, time_t when
)
1251 if (expires
< 0) /* No Expires given */
1253 return (expires
> (squid_curtime
+ when
));
1257 storeEntryValidToSend(StoreEntry
* e
)
1259 if (EBIT_TEST(e
->flags
, RELEASE_REQUEST
))
1261 if (EBIT_TEST(e
->flags
, ENTRY_NEGCACHED
))
1262 if (e
->expires
<= squid_curtime
)
1264 if (EBIT_TEST(e
->flags
, ENTRY_ABORTED
))
1270 storeTimestampsSet(StoreEntry
* entry
)
1272 const HttpReply
*reply
= entry
->mem_obj
->reply
;
1273 time_t served_date
= reply
->date
;
1274 /* make sure that 0 <= served_date <= squid_curtime */
1275 if (served_date
< 0 || served_date
> squid_curtime
)
1276 served_date
= squid_curtime
;
1277 entry
->expires
= reply
->expires
;
1278 entry
->lastmod
= reply
->last_modified
;
1279 entry
->timestamp
= served_date
;
1283 storeRegisterAbort(StoreEntry
* e
, STABH
* cb
, void *data
)
1285 MemObject
*mem
= e
->mem_obj
;
1287 assert(mem
->abort
.callback
== NULL
);
1288 mem
->abort
.callback
= cb
;
1289 mem
->abort
.data
= data
;
1293 storeUnregisterAbort(StoreEntry
* e
)
1295 MemObject
*mem
= e
->mem_obj
;
1297 mem
->abort
.callback
= NULL
;
1301 storeMemObjectDump(MemObject
* mem
)
1303 debug(20, 1) ("MemObject->data.head: %p\n",
1304 mem
->data_hdr
.head
);
1305 debug(20, 1) ("MemObject->data.tail: %p\n",
1306 mem
->data_hdr
.tail
);
1307 debug(20, 1) ("MemObject->data.origin_offset: %d\n",
1308 mem
->data_hdr
.origin_offset
);
1309 debug(20, 1) ("MemObject->start_ping: %d.%06d\n",
1310 (int) mem
->start_ping
.tv_sec
,
1311 (int) mem
->start_ping
.tv_usec
);
1312 debug(20, 1) ("MemObject->inmem_hi: %d\n",
1313 (int) mem
->inmem_hi
);
1314 debug(20, 1) ("MemObject->inmem_lo: %d\n",
1315 (int) mem
->inmem_lo
);
1316 debug(20, 1) ("MemObject->clients: %p\n",
1318 debug(20, 1) ("MemObject->nclients: %d\n",
1320 debug(20, 1) ("MemObject->reply: %p\n",
1322 debug(20, 1) ("MemObject->request: %p\n",
1324 debug(20, 1) ("MemObject->log_url: %p %s\n",
1326 checkNullString(mem
->log_url
));
1330 storeEntryDump(const StoreEntry
* e
, int l
)
1332 debug(20, l
) ("StoreEntry->key: %s\n", storeKeyText(e
->key
));
1333 debug(20, l
) ("StoreEntry->next: %p\n", e
->next
);
1334 debug(20, l
) ("StoreEntry->mem_obj: %p\n", e
->mem_obj
);
1335 debug(20, l
) ("StoreEntry->timestamp: %d\n", (int) e
->timestamp
);
1336 debug(20, l
) ("StoreEntry->lastref: %d\n", (int) e
->lastref
);
1337 debug(20, l
) ("StoreEntry->expires: %d\n", (int) e
->expires
);
1338 debug(20, l
) ("StoreEntry->lastmod: %d\n", (int) e
->lastmod
);
1339 debug(20, l
) ("StoreEntry->swap_file_sz: %d\n", (int) e
->swap_file_sz
);
1340 debug(20, l
) ("StoreEntry->refcount: %d\n", e
->refcount
);
1341 debug(20, l
) ("StoreEntry->flags: %s\n", storeEntryFlags(e
));
1342 debug(20, l
) ("StoreEntry->swap_file_number: %d\n", (int) e
->swap_file_number
);
1343 debug(20, l
) ("StoreEntry->lock_count: %d\n", (int) e
->lock_count
);
1344 debug(20, l
) ("StoreEntry->mem_status: %d\n", (int) e
->mem_status
);
1345 debug(20, l
) ("StoreEntry->ping_status: %d\n", (int) e
->ping_status
);
1346 debug(20, l
) ("StoreEntry->store_status: %d\n", (int) e
->store_status
);
1347 debug(20, l
) ("StoreEntry->swap_status: %d\n", (int) e
->swap_status
);
1351 * NOTE, this function assumes only two mem states
1354 storeSetMemStatus(StoreEntry
* e
, int new_status
)
1356 MemObject
*mem
= e
->mem_obj
;
1357 if (new_status
== e
->mem_status
)
1359 assert(mem
!= NULL
);
1360 if (new_status
== IN_MEMORY
) {
1361 assert(mem
->inmem_lo
== 0);
1362 #if HEAP_REPLACEMENT
1363 if (mem
->node
== NULL
) {
1364 if (EBIT_TEST(e
->flags
, ENTRY_SPECIAL
)) {
1365 debug(20, 4) ("storeSetMemStatus: not inserting special %s\n",
1368 mem
->node
= heap_insert(inmem_heap
, e
);
1369 debug(20, 4) ("storeSetMemStatus: inserted mem node %p\n",
1374 dlinkAdd(e
, &mem
->lru
, &inmem_list
);
1378 #if HEAP_REPLACEMENT
1380 * It's being removed from the memory heap; is it already gone?
1383 heap_delete(inmem_heap
, mem
->node
);
1384 debug(20, 4) ("storeSetMemStatus: deleted mem node %p\n",
1389 dlinkDelete(&mem
->lru
, &inmem_list
);
1393 e
->mem_status
= new_status
;
1397 storeUrl(const StoreEntry
* e
)
1400 return "[null_entry]";
1401 else if (e
->mem_obj
== NULL
)
1402 return "[null_mem_obj]";
1404 return e
->mem_obj
->url
;
1408 storeCreateMemObject(StoreEntry
* e
, const char *url
, const char *log_url
)
1412 e
->mem_obj
= new_MemObject(url
, log_url
);
1415 /* this just sets DELAY_SENDING */
1417 storeBuffer(StoreEntry
* e
)
1419 EBIT_SET(e
->flags
, DELAY_SENDING
);
1422 /* this just clears DELAY_SENDING and Invokes the handlers */
1424 storeBufferFlush(StoreEntry
* e
)
1426 EBIT_CLR(e
->flags
, DELAY_SENDING
);
1432 objectLen(const StoreEntry
* e
)
1434 assert(e
->mem_obj
!= NULL
);
1435 return e
->mem_obj
->object_sz
;
1439 contentLen(const StoreEntry
* e
)
1441 assert(e
->mem_obj
!= NULL
);
1442 assert(e
->mem_obj
->reply
!= NULL
);
1443 return e
->mem_obj
->object_sz
- e
->mem_obj
->reply
->hdr_sz
;
1447 storeEntryReply(StoreEntry
* e
)
1451 if (NULL
== e
->mem_obj
)
1453 return e
->mem_obj
->reply
;
1457 storeEntryReset(StoreEntry
* e
)
1459 MemObject
*mem
= e
->mem_obj
;
1460 debug(20, 3) ("storeEntryReset: %s\n", storeUrl(e
));
1461 assert(mem
->swapout
.sio
== NULL
);
1462 stmemFree(&mem
->data_hdr
);
1463 mem
->inmem_hi
= mem
->inmem_lo
= 0;
1464 httpReplyDestroy(mem
->reply
);
1465 mem
->reply
= httpReplyCreate();
1466 e
->expires
= e
->lastmod
= e
->timestamp
= -1;
1469 #if HEAP_REPLACEMENT
1471 storeHeapPositionUpdate(StoreEntry
* e
)
1474 heap_update(store_heap
, e
->node
, e
);
1475 if (e
->mem_obj
&& e
->mem_obj
->node
)
1476 heap_update(inmem_heap
, e
->mem_obj
->node
, e
);