3 * $Id: store.cc,v 1.486 1999/01/21 23:15:40 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
[] =
56 const char *storeStatusStr
[] =
62 const char *swapStatusStr
[] =
70 typedef struct lock_ctrl_t
{
77 * local function prototypes
79 static int storeCheckExpired(const StoreEntry
*);
80 static int storeEntryLocked(const StoreEntry
*);
81 static int storeEntryValidLength(const StoreEntry
*);
82 static void storeGetMemSpace(int);
83 static void storeHashDelete(StoreEntry
*);
84 static MemObject
*new_MemObject(const char *, const char *);
85 static void destroy_MemObject(StoreEntry
*);
86 static FREE destroy_StoreEntry
;
87 static void storePurgeMem(StoreEntry
*);
88 static int getKeyCounter(void);
89 static int storeKeepInMemory(const StoreEntry
*);
90 static OBJH storeCheckCachableStats
;
91 static EVH storeLateRelease
;
96 static dlink_list inmem_list
;
97 static int store_pages_max
= 0;
98 static int store_swap_high
= 0;
99 static int store_swap_low
= 0;
100 static int store_swap_mid
= 0;
101 static int store_maintain_rate
;
102 static Stack LateReleaseStack
;
105 new_MemObject(const char *url
, const char *log_url
)
107 MemObject
*mem
= memAllocate(MEM_MEMOBJECT
);
108 mem
->reply
= httpReplyCreate();
109 mem
->url
= xstrdup(url
);
110 mem
->log_url
= xstrdup(log_url
);
111 mem
->swapout
.fd
= -1;
114 /* XXX account log_url */
115 debug(20, 3) ("new_MemObject: returning %p\n", mem
);
120 new_StoreEntry(int mem_obj_flag
, const char *url
, const char *log_url
)
122 StoreEntry
*e
= NULL
;
123 e
= memAllocate(MEM_STOREENTRY
);
125 e
->mem_obj
= new_MemObject(url
, log_url
);
126 debug(20, 3) ("new_StoreEntry: returning %p\n", e
);
127 e
->expires
= e
->lastmod
= e
->lastref
= e
->timestamp
= -1;
132 destroy_MemObject(StoreEntry
* e
)
134 MemObject
*mem
= e
->mem_obj
;
135 const Ctx ctx
= ctx_enter(mem
->url
);
136 debug(20, 3) ("destroy_MemObject: destroying %p\n", mem
);
139 assert(mem
->swapout
.fd
== -1);
140 stmemFree(&mem
->data_hdr
);
142 /* XXX account log_url */
144 while (mem
->clients
!= NULL
)
145 storeUnregister(e
, mem
->clients
->callback_data
);
148 * There is no way to abort FD-less clients, so they might
149 * still have mem->clients set if mem->fd == -1
151 assert(mem
->fd
== -1 || mem
->clients
== NULL
);
152 httpReplyDestroy(mem
->reply
);
153 requestUnlink(mem
->request
);
155 ctx_exit(ctx
); /* must exit before we free mem->url */
157 safe_free(mem
->log_url
);
158 memFree(mem
, MEM_MEMOBJECT
);
162 destroy_StoreEntry(void *data
)
164 StoreEntry
*e
= data
;
165 debug(20, 3) ("destroy_StoreEntry: destroying %p\n", e
);
168 destroy_MemObject(e
);
170 assert(e
->key
== NULL
);
171 memFree(e
, MEM_STOREENTRY
);
174 /* ----- INTERFACE BETWEEN STORAGE MANAGER AND HASH TABLE FUNCTIONS --------- */
177 storeHashInsert(StoreEntry
* e
, const cache_key
* key
)
179 debug(20, 3) ("storeHashInsert: Inserting Entry %p key '%s'\n",
180 e
, storeKeyText(key
));
181 e
->key
= storeKeyDup(key
);
182 hash_join(store_table
, (hash_link
*) e
);
183 dlinkAdd(e
, &e
->lru
, &store_list
);
187 storeHashDelete(StoreEntry
* e
)
189 hash_remove_link(store_table
, (hash_link
*) e
);
190 dlinkDelete(&e
->lru
, &store_list
);
191 storeKeyFree(e
->key
);
195 /* -------------------------------------------------------------------------- */
198 /* get rid of memory copy of the object */
199 /* Only call this if storeCheckPurgeMem(e) returns 1 */
201 storePurgeMem(StoreEntry
* e
)
203 if (e
->mem_obj
== NULL
)
205 debug(20, 3) ("storePurgeMem: Freeing memory-copy of %s\n",
206 storeKeyText(e
->key
));
207 storeSetMemStatus(e
, NOT_IN_MEMORY
);
208 destroy_MemObject(e
);
209 if (e
->swap_status
!= SWAPOUT_DONE
)
214 storeLockObject(StoreEntry
* e
)
216 if (e
->lock_count
++ == 0) {
217 dlinkDelete(&e
->lru
, &store_list
);
218 dlinkAdd(e
, &e
->lru
, &store_list
);
220 debug(20, 3) ("storeLockObject: key '%s' count=%d\n",
221 storeKeyText(e
->key
), (int) e
->lock_count
);
222 e
->lastref
= squid_curtime
;
226 storeReleaseRequest(StoreEntry
* e
)
228 if (EBIT_TEST(e
->flags
, RELEASE_REQUEST
))
230 debug(20, 3) ("storeReleaseRequest: '%s'\n", storeKeyText(e
->key
));
231 EBIT_SET(e
->flags
, RELEASE_REQUEST
);
233 * Clear cachable flag here because we might get called before
234 * anyone else even looks at the cachability flag. Also, this
235 * prevents httpMakePublic from really setting a public key.
237 EBIT_CLR(e
->flags
, ENTRY_CACHABLE
);
238 storeSetPrivateKey(e
);
241 /* unlock object, return -1 if object get released after unlock
242 * otherwise lock_count */
244 storeUnlockObject(StoreEntry
* e
)
247 debug(20, 3) ("storeUnlockObject: key '%s' count=%d\n",
248 storeKeyText(e
->key
), e
->lock_count
);
250 return (int) e
->lock_count
;
251 if (e
->store_status
== STORE_PENDING
)
252 EBIT_SET(e
->flags
, RELEASE_REQUEST
);
253 assert(storePendingNClients(e
) == 0);
254 if (EBIT_TEST(e
->flags
, RELEASE_REQUEST
))
256 else if (storeKeepInMemory(e
)) {
257 storeSetMemStatus(e
, IN_MEMORY
);
258 requestUnlink(e
->mem_obj
->request
);
259 e
->mem_obj
->request
= NULL
;
262 if (EBIT_TEST(e
->flags
, KEY_PRIVATE
)) {
263 dlinkDelete(&e
->lru
, &store_list
);
264 dlinkAddTail(e
, &e
->lru
, &store_list
);
270 /* Lookup an object in the cache.
271 * return just a reference to object, don't start swapping in yet. */
273 storeGet(const cache_key
* key
)
275 debug(20, 3) ("storeGet: looking up %s\n", storeKeyText(key
));
276 return (StoreEntry
*) hash_lookup(store_table
, key
);
280 storeGetPublic(const char *uri
, const method_t method
)
282 StoreEntry
*e
= storeGet(storeKeyPublic(uri
, method
));
283 if (e
== NULL
&& squid_curtime
< 922500000)
284 e
= storeGet(storeKeyPublicOld(uri
, method
));
291 static int key_counter
= 0;
292 if (++key_counter
< 0)
298 storeSetPrivateKey(StoreEntry
* e
)
300 const cache_key
*newkey
;
301 MemObject
*mem
= e
->mem_obj
;
302 if (e
->key
&& EBIT_TEST(e
->flags
, KEY_PRIVATE
))
303 return; /* is already private */
305 if (e
->swap_file_number
> -1)
306 storeDirSwapLog(e
, SWAP_LOG_DEL
);
310 mem
->id
= getKeyCounter();
311 newkey
= storeKeyPrivate(mem
->url
, mem
->method
, mem
->id
);
313 newkey
= storeKeyPrivate("JUNK", METHOD_NONE
, getKeyCounter());
315 assert(hash_lookup(store_table
, newkey
) == NULL
);
316 EBIT_SET(e
->flags
, KEY_PRIVATE
);
317 storeHashInsert(e
, newkey
);
321 storeSetPublicKey(StoreEntry
* e
)
323 StoreEntry
*e2
= NULL
;
324 const cache_key
*newkey
;
325 MemObject
*mem
= e
->mem_obj
;
326 if (e
->key
&& !EBIT_TEST(e
->flags
, KEY_PRIVATE
))
327 return; /* is already public */
330 * We can't make RELEASE_REQUEST objects public. Depending on
331 * when RELEASE_REQUEST gets set, we might not be swapping out
332 * the object. If we're not swapping out, then subsequent
333 * store clients won't be able to access object data which has
334 * been freed from memory.
336 * If RELEASE_REQUEST is set, then ENTRY_CACHABLE should not
337 * be set, and storeSetPublicKey() should not be called.
339 assert(!EBIT_TEST(e
->flags
, RELEASE_REQUEST
));
340 newkey
= storeKeyPublic(mem
->url
, mem
->method
);
341 if ((e2
= (StoreEntry
*) hash_lookup(store_table
, newkey
))) {
342 debug(20, 3) ("storeSetPublicKey: Making old '%s' private.\n", mem
->url
);
343 storeSetPrivateKey(e2
);
345 newkey
= storeKeyPublic(mem
->url
, mem
->method
);
349 EBIT_CLR(e
->flags
, KEY_PRIVATE
);
350 storeHashInsert(e
, newkey
);
351 if (e
->swap_file_number
> -1)
352 storeDirSwapLog(e
, SWAP_LOG_ADD
);
356 storeCreateEntry(const char *url
, const char *log_url
, request_flags flags
, method_t method
)
358 StoreEntry
*e
= NULL
;
359 MemObject
*mem
= NULL
;
360 debug(20, 3) ("storeCreateEntry: '%s'\n", url
);
362 e
= new_StoreEntry(STORE_ENTRY_WITH_MEMOBJ
, url
, log_url
);
363 e
->lock_count
= 1; /* Note lock here w/o calling storeLock() */
365 mem
->method
= method
;
366 if (neighbors_do_private_keys
|| !flags
.hierarchical
)
367 storeSetPrivateKey(e
);
369 storeSetPublicKey(e
);
370 if (flags
.cachable
) {
371 EBIT_SET(e
->flags
, ENTRY_CACHABLE
);
372 EBIT_CLR(e
->flags
, RELEASE_REQUEST
);
374 EBIT_CLR(e
->flags
, ENTRY_CACHABLE
);
375 storeReleaseRequest(e
);
377 e
->store_status
= STORE_PENDING
;
378 storeSetMemStatus(e
, NOT_IN_MEMORY
);
379 e
->swap_status
= SWAPOUT_NONE
;
380 e
->swap_file_number
= -1;
382 e
->lastref
= squid_curtime
;
383 e
->timestamp
= 0; /* set in storeTimestampsSet() */
384 e
->ping_status
= PING_NONE
;
385 EBIT_SET(e
->flags
, ENTRY_VALIDATED
);
389 /* Mark object as expired */
391 storeExpireNow(StoreEntry
* e
)
393 debug(20, 3) ("storeExpireNow: '%s'\n", storeKeyText(e
->key
));
394 e
->expires
= squid_curtime
;
397 /* Append incoming data from a primary server to an entry. */
399 storeAppend(StoreEntry
* e
, const char *buf
, int len
)
401 MemObject
*mem
= e
->mem_obj
;
404 assert(e
->store_status
== STORE_PENDING
);
406 debug(20, 5) ("storeAppend: appending %d bytes for '%s'\n",
408 storeKeyText(e
->key
));
409 storeGetMemSpace(len
);
410 stmemAppend(&mem
->data_hdr
, buf
, len
);
411 mem
->inmem_hi
+= len
;
413 if (EBIT_TEST(e
->flags
, DELAY_SENDING
))
419 storeCheckSwapOut(e
);
421 storeUnlockObject(e
);
427 storeAppendPrintf(StoreEntry
* e
, const char *fmt
,...)
433 storeAppendPrintf(va_alist
)
437 StoreEntry
*e
= NULL
;
438 const char *fmt
= NULL
;
440 e
= va_arg(args
, StoreEntry
*);
441 fmt
= va_arg(args
, char *);
443 storeAppendVPrintf(e
, fmt
, args
);
447 /* used be storeAppendPrintf and Packer */
449 storeAppendVPrintf(StoreEntry
* e
, const char *fmt
, va_list vargs
)
451 LOCAL_ARRAY(char, buf
, 4096);
453 vsnprintf(buf
, 4096, fmt
, vargs
);
454 storeAppend(e
, buf
, strlen(buf
));
457 struct _store_check_cachable_hist
{
460 int not_entry_cachable
;
462 int wrong_content_length
;
466 int too_many_open_files
;
467 int too_many_open_fds
;
473 } store_check_cachable_hist
;
476 storeTooManyDiskFilesOpen(void)
478 if (Config
.max_open_disk_fds
== 0)
480 if (store_open_disk_fd
> Config
.max_open_disk_fds
)
486 storeCheckCachable(StoreEntry
* e
)
488 #if CACHE_ALL_METHODS
489 if (e
->mem_obj
->method
!= METHOD_GET
) {
490 debug(20, 2) ("storeCheckCachable: NO: non-GET method\n");
491 store_check_cachable_hist
.no
.non_get
++;
494 if (!EBIT_TEST(e
->flags
, ENTRY_CACHABLE
)) {
495 debug(20, 2) ("storeCheckCachable: NO: not cachable\n");
496 store_check_cachable_hist
.no
.not_entry_cachable
++;
497 } else if (EBIT_TEST(e
->flags
, RELEASE_REQUEST
)) {
498 debug(20, 2) ("storeCheckCachable: NO: release requested\n");
499 store_check_cachable_hist
.no
.release_request
++;
500 } else if (e
->store_status
== STORE_OK
&& EBIT_TEST(e
->flags
, ENTRY_BAD_LENGTH
)) {
501 debug(20, 2) ("storeCheckCachable: NO: wrong content-length\n");
502 store_check_cachable_hist
.no
.wrong_content_length
++;
503 } else if (EBIT_TEST(e
->flags
, ENTRY_NEGCACHED
)) {
504 debug(20, 3) ("storeCheckCachable: NO: negative cached\n");
505 store_check_cachable_hist
.no
.negative_cached
++;
506 return 0; /* avoid release call below */
507 } else if (e
->mem_obj
->inmem_hi
> Config
.Store
.maxObjectSize
) {
508 debug(20, 2) ("storeCheckCachable: NO: too big\n");
509 store_check_cachable_hist
.no
.too_big
++;
510 } else if (EBIT_TEST(e
->flags
, KEY_PRIVATE
)) {
511 debug(20, 3) ("storeCheckCachable: NO: private key\n");
512 store_check_cachable_hist
.no
.private_key
++;
513 } else if (e
->swap_status
!= SWAPOUT_NONE
) {
515 * here we checked the swap_status because the remaining
516 * cases are only relevant only if we haven't started swapping
517 * out the object yet.
520 } else if (storeTooManyDiskFilesOpen()) {
521 debug(20, 2) ("storeCheckCachable: NO: too many disk files open\n");
522 store_check_cachable_hist
.no
.too_many_open_files
++;
523 } else if (fdNFree() < RESERVED_FD
) {
524 debug(20, 2) ("storeCheckCachable: NO: too many FD's open\n");
525 store_check_cachable_hist
.no
.too_many_open_fds
++;
526 } else if (storeExpiredReferenceAge() < 300) {
527 debug(20, 2) ("storeCheckCachable: NO: LRU Age = %d\n",
528 storeExpiredReferenceAge());
529 store_check_cachable_hist
.no
.lru_age_too_low
++;
531 store_check_cachable_hist
.yes
.Default
++;
534 storeReleaseRequest(e
);
535 EBIT_CLR(e
->flags
, ENTRY_CACHABLE
);
540 storeCheckCachableStats(StoreEntry
* sentry
)
542 storeAppendPrintf(sentry
, "Category\t Count\n");
544 storeAppendPrintf(sentry
, "no.non_get\t%d\n",
545 store_check_cachable_hist
.no
.non_get
);
546 storeAppendPrintf(sentry
, "no.not_entry_cachable\t%d\n",
547 store_check_cachable_hist
.no
.not_entry_cachable
);
548 storeAppendPrintf(sentry
, "no.release_request\t%d\n",
549 store_check_cachable_hist
.no
.release_request
);
550 storeAppendPrintf(sentry
, "no.wrong_content_length\t%d\n",
551 store_check_cachable_hist
.no
.wrong_content_length
);
552 storeAppendPrintf(sentry
, "no.negative_cached\t%d\n",
553 store_check_cachable_hist
.no
.negative_cached
);
554 storeAppendPrintf(sentry
, "no.too_big\t%d\n",
555 store_check_cachable_hist
.no
.too_big
);
556 storeAppendPrintf(sentry
, "no.private_key\t%d\n",
557 store_check_cachable_hist
.no
.private_key
);
558 storeAppendPrintf(sentry
, "no.too_many_open_files\t%d\n",
559 store_check_cachable_hist
.no
.too_many_open_files
);
560 storeAppendPrintf(sentry
, "no.too_many_open_fds\t%d\n",
561 store_check_cachable_hist
.no
.too_many_open_fds
);
562 storeAppendPrintf(sentry
, "no.lru_age_too_low\t%d\n",
563 store_check_cachable_hist
.no
.lru_age_too_low
);
564 storeAppendPrintf(sentry
, "yes.default\t%d\n",
565 store_check_cachable_hist
.yes
.Default
);
568 /* Complete transfer into the local cache. */
570 storeComplete(StoreEntry
* e
)
572 debug(20, 3) ("storeComplete: '%s'\n", storeKeyText(e
->key
));
573 if (e
->store_status
!= STORE_PENDING
) {
575 * if we're not STORE_PENDING, then probably we got aborted
576 * and there should be NO clients on this entry
578 assert(EBIT_TEST(e
->flags
, ENTRY_ABORTED
));
579 assert(e
->mem_obj
->nclients
== 0);
582 e
->mem_obj
->object_sz
= e
->mem_obj
->inmem_hi
;
583 e
->store_status
= STORE_OK
;
584 assert(e
->mem_status
== NOT_IN_MEMORY
);
585 if (!storeEntryValidLength(e
)) {
586 EBIT_SET(e
->flags
, ENTRY_BAD_LENGTH
);
587 storeReleaseRequest(e
);
589 #if USE_CACHE_DIGESTS
590 if (e
->mem_obj
->request
)
591 e
->mem_obj
->request
->hier
.store_complete_stop
= current_time
;
594 storeCheckSwapOut(e
);
598 * Someone wants to abort this transfer. Set the reason in the
599 * request structure, call the server-side callback and mark the
600 * entry for releasing
603 storeAbort(StoreEntry
* e
)
605 MemObject
*mem
= e
->mem_obj
;
606 assert(e
->store_status
== STORE_PENDING
);
608 debug(20, 6) ("storeAbort: %s\n", storeKeyText(e
->key
));
609 storeLockObject(e
); /* lock while aborting */
610 storeNegativeCache(e
);
611 storeReleaseRequest(e
);
612 EBIT_SET(e
->flags
, ENTRY_ABORTED
);
613 storeSetMemStatus(e
, NOT_IN_MEMORY
);
614 /* No DISK swap for negative cached object */
615 e
->swap_status
= SWAPOUT_NONE
;
616 e
->store_status
= STORE_OK
;
618 * We assign an object length here. The only other place we assign
619 * the object length is in storeComplete()
621 mem
->object_sz
= mem
->inmem_hi
;
622 /* Notify the server side */
623 if (mem
->abort
.callback
) {
624 eventAdd("mem->abort.callback",
629 mem
->abort
.callback
= NULL
;
630 mem
->abort
.data
= NULL
;
632 /* Notify the client side */
634 /* Do we need to close the swapout file? */
635 /* Not if we never started swapping out */
636 /* But we may need to cancel an open/stat in progress if using ASYNC */
640 if (e
->swap_file_number
> -1) {
642 /* Need to cancel any pending ASYNC writes right now */
643 if (mem
->swapout
.fd
>= 0)
644 aioCancel(mem
->swapout
.fd
, NULL
);
646 /* we have to close the disk file if there is no write pending */
647 if (!storeSwapOutWriteQueued(mem
))
648 storeSwapOutFileClose(e
);
650 storeUnlockObject(e
); /* unlock */
653 /* Clear Memory storage to accommodate the given object len */
655 storeGetMemSpace(int size
)
657 StoreEntry
*e
= NULL
;
659 static time_t last_check
= 0;
663 dlink_node
*prev
= NULL
;
664 if (squid_curtime
== last_check
)
666 last_check
= squid_curtime
;
667 pages_needed
= (size
/ SM_PAGE_SIZE
) + 1;
668 if (memInUse(MEM_STMEM_BUF
) + pages_needed
< store_pages_max
)
670 if (store_rebuilding
)
672 debug(20, 2) ("storeGetMemSpace: Starting, need %d pages\n", pages_needed
);
673 head
= inmem_list
.head
;
674 for (m
= inmem_list
.tail
; m
; m
= prev
) {
679 if (storeEntryLocked(e
)) {
680 dlinkDelete(m
, &inmem_list
);
681 dlinkAdd(e
, m
, &inmem_list
);
686 if (memInUse(MEM_STMEM_BUF
) + pages_needed
< store_pages_max
)
689 debug(20, 3) ("storeGetMemSpace stats:\n");
690 debug(20, 3) (" %6d HOT objects\n", hot_obj_count
);
691 debug(20, 3) (" %6d were released\n", released
);
694 /* The maximum objects to scan for maintain storage space */
695 #define MAINTAIN_MAX_SCAN 1024
696 #define MAINTAIN_MAX_REMOVE 64
699 * This routine is to be called by main loop in main.c.
700 * It removes expired objects on only one bucket for each time called.
701 * returns the number of objects removed
703 * This should get called 1/s from main().
706 storeMaintainSwapSpace(void *datanotused
)
709 dlink_node
*prev
= NULL
;
710 StoreEntry
*e
= NULL
;
716 static time_t last_warn_time
= 0;
717 /* We can't delete objects while rebuilding swap */
718 if (store_rebuilding
) {
719 eventAdd("MaintainSwapSpace", storeMaintainSwapSpace
, NULL
, 1.0, 1);
721 } else if (store_swap_size
< store_swap_mid
) {
724 eventAdd("MaintainSwapSpace", storeMaintainSwapSpace
, NULL
, 1.0, 1);
725 } else if (store_swap_size
< store_swap_high
) {
728 eventAdd("MaintainSwapSpace", storeMaintainSwapSpace
, NULL
, 0.1, 1);
732 eventAdd("MaintainSwapSpace", storeMaintainSwapSpace
, NULL
, 0.0, 1);
734 debug(20, 3) ("storeMaintainSwapSpace\n");
735 for (m
= store_list
.tail
; m
; m
= prev
) {
739 if (storeEntryLocked(e
)) {
741 * If there is a locked entry at the tail of the LRU list,
742 * move it to the beginning to get it out of the way.
743 * Theoretically, we might have all locked objects at the
744 * tail, and then we'll never remove anything here and the
745 * LRU age will go to zero.
747 if (memInUse(MEM_STOREENTRY
) > max_scan
) {
748 dlinkDelete(&e
->lru
, &store_list
);
749 dlinkAdd(e
, &e
->lru
, &store_list
);
752 } else if (storeCheckExpired(e
)) {
756 if (expired
>= max_remove
)
758 if (scanned
>= max_scan
)
761 debug(20, 3) ("storeMaintainSwapSpace stats:\n");
762 debug(20, 3) (" %6d objects\n", memInUse(MEM_STOREENTRY
));
763 debug(20, 3) (" %6d were scanned\n", scanned
);
764 debug(20, 3) (" %6d were locked\n", locked
);
765 debug(20, 3) (" %6d were expired\n", expired
);
766 if (store_swap_size
< Config
.Swap
.maxSize
)
768 if (squid_curtime
- last_warn_time
< 10)
770 debug(20, 0) ("WARNING: Disk space over limit: %d KB > %d KB\n",
771 store_swap_size
, Config
.Swap
.maxSize
);
772 last_warn_time
= squid_curtime
;
776 /* release an object from a cache */
777 /* return number of objects released. */
779 storeRelease(StoreEntry
* e
)
781 debug(20, 3) ("storeRelease: Releasing: '%s'\n", storeKeyText(e
->key
));
782 /* If, for any reason we can't discard this object because of an
783 * outstanding request, mark it for pending release */
784 if (storeEntryLocked(e
)) {
786 debug(20, 3) ("storeRelease: Only setting RELEASE_REQUEST bit\n");
787 storeReleaseRequest(e
);
792 * Make sure all forgotten async ops are cancelled
796 if (store_rebuilding
) {
797 storeSetPrivateKey(e
);
799 storeSetMemStatus(e
, NOT_IN_MEMORY
);
800 destroy_MemObject(e
);
803 * Fake a call to storeLockObject(). When rebuilding is done,
804 * we'll just call storeUnlockObject() on these.
807 stackPush(&LateReleaseStack
, e
);
810 storeLog(STORE_LOG_RELEASE
, e
);
811 if (e
->swap_file_number
> -1) {
812 storeUnlinkFileno(e
->swap_file_number
);
813 storeDirMapBitReset(e
->swap_file_number
);
814 if (e
->swap_status
== SWAPOUT_DONE
)
815 if (EBIT_TEST(e
->flags
, ENTRY_VALIDATED
))
816 storeDirUpdateSwapSize(e
->swap_file_number
, e
->swap_file_sz
, -1);
817 if (!EBIT_TEST(e
->flags
, KEY_PRIVATE
))
818 storeDirSwapLog(e
, SWAP_LOG_DEL
);
820 storeSetMemStatus(e
, NOT_IN_MEMORY
);
821 destroy_StoreEntry(e
);
825 storeLateRelease(void *unused
)
830 if (store_rebuilding
) {
831 eventAdd("storeLateRelease", storeLateRelease
, NULL
, 1.0, 1);
834 for (i
= 0; i
< 10; i
++) {
835 e
= stackPop(&LateReleaseStack
);
838 debug(20, 1) ("storeLateRelease: released %d objects\n", n
);
841 storeUnlockObject(e
);
844 eventAdd("storeLateRelease", storeLateRelease
, NULL
, 0.0, 1);
847 /* return 1 if a store entry is locked */
849 storeEntryLocked(const StoreEntry
* e
)
853 if (e
->swap_status
== SWAPOUT_OPENING
)
855 if (e
->swap_status
== SWAPOUT_WRITING
)
857 if (e
->store_status
== STORE_PENDING
)
860 * SPECIAL, PUBLIC entries should be "locked"
862 if (EBIT_TEST(e
->flags
, ENTRY_SPECIAL
))
863 if (!EBIT_TEST(e
->flags
, KEY_PRIVATE
))
869 storeEntryValidLength(const StoreEntry
* e
)
872 const HttpReply
*reply
;
873 assert(e
->mem_obj
!= NULL
);
874 reply
= e
->mem_obj
->reply
;
875 debug(20, 3) ("storeEntryValidLength: Checking '%s'\n", storeKeyText(e
->key
));
876 debug(20, 5) ("storeEntryValidLength: object_len = %d\n",
878 debug(20, 5) ("storeEntryValidLength: hdr_sz = %d\n",
880 debug(20, 5) ("storeEntryValidLength: content_length = %d\n",
881 reply
->content_length
);
882 if (reply
->content_length
< 0) {
883 debug(20, 5) ("storeEntryValidLength: Unspecified content length: %s\n",
884 storeKeyText(e
->key
));
887 if (reply
->hdr_sz
== 0) {
888 debug(20, 5) ("storeEntryValidLength: Zero header size: %s\n",
889 storeKeyText(e
->key
));
892 if (e
->mem_obj
->method
== METHOD_HEAD
) {
893 debug(20, 5) ("storeEntryValidLength: HEAD request: %s\n",
894 storeKeyText(e
->key
));
897 if (reply
->sline
.status
== HTTP_NOT_MODIFIED
)
899 if (reply
->sline
.status
== HTTP_NO_CONTENT
)
901 diff
= reply
->hdr_sz
+ reply
->content_length
- objectLen(e
);
904 debug(20, 3) ("storeEntryValidLength: %d bytes too %s; '%s'\n",
905 diff
< 0 ? -diff
: diff
,
906 diff
< 0 ? "big" : "small",
907 storeKeyText(e
->key
));
912 storeInitHashValues(void)
915 /* Calculate size of hash table (maximum currently 64k buckets). */
916 i
= Config
.Swap
.maxSize
/ Config
.Store
.avgObjectSize
;
917 debug(20, 1) ("Swap maxSize %d KB, estimated %d objects\n",
918 Config
.Swap
.maxSize
, i
);
919 i
/= Config
.Store
.objectsPerBucket
;
920 debug(20, 1) ("Target number of buckets: %d\n", i
);
921 /* ideally the full scan period should be configurable, for the
922 * moment it remains at approximately 24 hours. */
923 store_hash_buckets
= storeKeyHashBuckets(i
);
924 store_maintain_rate
= 86400 / store_hash_buckets
;
925 assert(store_maintain_rate
> 0);
926 debug(20, 1) ("Using %d Store buckets, replacement runs every %d second%s\n",
929 store_maintain_rate
== 1 ? null_string
: "s");
930 debug(20, 1) ("Max Mem size: %d KB\n", Config
.memMaxSize
>> 10);
931 debug(20, 1) ("Max Swap size: %d KB\n", Config
.Swap
.maxSize
);
938 storeInitHashValues();
939 store_table
= hash_create(storeKeyHashCmp
,
940 store_hash_buckets
, storeKeyHashHash
);
943 if (storeVerifyCacheDirs() < 0) {
944 xstrncpy(tmp_error_buf
,
945 "\tFailed to verify one of the swap directories, Check cache.log\n"
946 "\tfor details. Run 'squid -z' to create swap directories\n"
947 "\tif needed, or if running Squid for the first time.",
949 fatal(tmp_error_buf
);
951 storeDirOpenSwapLogs();
952 store_list
.head
= store_list
.tail
= NULL
;
953 inmem_list
.head
= inmem_list
.tail
= NULL
;
954 stackInit(&LateReleaseStack
);
955 eventAdd("storeLateRelease", storeLateRelease
, NULL
, 1.0, 1);
957 cachemgrRegister("storedir",
958 "Store Directory Stats",
959 storeDirStats
, 0, 1);
960 cachemgrRegister("store_check_cachable_stats",
961 "storeCheckCachable() Stats",
962 storeCheckCachableStats
, 0, 1);
968 store_swap_high
= (long) (((float) Config
.Swap
.maxSize
*
969 (float) Config
.Swap
.highWaterMark
) / (float) 100);
970 store_swap_low
= (long) (((float) Config
.Swap
.maxSize
*
971 (float) Config
.Swap
.lowWaterMark
) / (float) 100);
972 store_swap_mid
= (store_swap_high
>> 1) + (store_swap_low
>> 1);
973 store_pages_max
= Config
.memMaxSize
/ SM_PAGE_SIZE
;
977 storeKeepInMemory(const StoreEntry
* e
)
979 MemObject
*mem
= e
->mem_obj
;
982 if (mem
->data_hdr
.head
== NULL
)
984 return mem
->inmem_lo
== 0;
988 storeCheckExpired(const StoreEntry
* e
)
990 if (storeEntryLocked(e
))
992 if (EBIT_TEST(e
->flags
, RELEASE_REQUEST
))
994 if (EBIT_TEST(e
->flags
, ENTRY_NEGCACHED
) && squid_curtime
>= e
->expires
)
996 if (squid_curtime
- e
->lastref
> storeExpiredReferenceAge())
1002 * storeExpiredReferenceAge
1004 * The LRU age is scaled exponentially between 1 minute and
1005 * Config.referenceAge , when store_swap_low < store_swap_size <
1006 * store_swap_high. This keeps store_swap_size within the low and high
1007 * water marks. If the cache is very busy then store_swap_size stays
1008 * closer to the low water mark, if it is not busy, then it will stay
1009 * near the high water mark. The LRU age value can be examined on the
1010 * cachemgr 'info' page.
1013 storeExpiredReferenceAge(void)
1018 x
= (double) (store_swap_high
- store_swap_size
) / (store_swap_high
- store_swap_low
);
1019 x
= x
< 0.0 ? 0.0 : x
> 1.0 ? 1.0 : x
;
1020 z
= pow((double) (Config
.referenceAge
/ 60), x
);
1021 age
= (time_t) (z
* 60.0);
1024 else if (age
> 31536000)
1030 storeNegativeCache(StoreEntry
* e
)
1032 e
->expires
= squid_curtime
+ Config
.negativeTtl
;
1033 EBIT_SET(e
->flags
, ENTRY_NEGCACHED
);
1037 storeFreeMemory(void)
1039 hashFreeItems(store_table
, destroy_StoreEntry
);
1040 hashFreeMemory(store_table
);
1042 #if USE_CACHE_DIGEST
1044 cacheDigestDestroy(store_digest
);
1046 store_digest
= NULL
;
1050 expiresMoreThan(time_t expires
, time_t when
)
1052 if (expires
< 0) /* No Expires given */
1054 return (expires
> (squid_curtime
+ when
));
1058 storeEntryValidToSend(StoreEntry
* e
)
1060 if (EBIT_TEST(e
->flags
, RELEASE_REQUEST
))
1062 if (EBIT_TEST(e
->flags
, ENTRY_NEGCACHED
))
1063 if (e
->expires
<= squid_curtime
)
1065 if (EBIT_TEST(e
->flags
, ENTRY_ABORTED
))
1071 storeTimestampsSet(StoreEntry
* entry
)
1073 time_t served_date
= -1;
1074 const HttpReply
*reply
= entry
->mem_obj
->reply
;
1075 served_date
= reply
->date
;
1076 if (served_date
< 0)
1077 served_date
= squid_curtime
;
1078 entry
->expires
= reply
->expires
;
1079 entry
->lastmod
= reply
->last_modified
;
1080 entry
->timestamp
= served_date
;
1084 storeRegisterAbort(StoreEntry
* e
, STABH
* cb
, void *data
)
1086 MemObject
*mem
= e
->mem_obj
;
1088 assert(mem
->abort
.callback
== NULL
);
1089 mem
->abort
.callback
= cb
;
1090 mem
->abort
.data
= data
;
1094 storeUnregisterAbort(StoreEntry
* e
)
1096 MemObject
*mem
= e
->mem_obj
;
1098 mem
->abort
.callback
= NULL
;
1102 storeMemObjectDump(MemObject
* mem
)
1104 debug(20, 1) ("MemObject->data.head: %p\n",
1105 mem
->data_hdr
.head
);
1106 debug(20, 1) ("MemObject->data.tail: %p\n",
1107 mem
->data_hdr
.tail
);
1108 debug(20, 1) ("MemObject->data.origin_offset: %d\n",
1109 mem
->data_hdr
.origin_offset
);
1110 debug(20, 1) ("MemObject->start_ping: %d.%06d\n",
1111 (int) mem
->start_ping
.tv_sec
,
1112 (int) mem
->start_ping
.tv_usec
);
1113 debug(20, 1) ("MemObject->inmem_hi: %d\n",
1114 (int) mem
->inmem_hi
);
1115 debug(20, 1) ("MemObject->inmem_lo: %d\n",
1116 (int) mem
->inmem_lo
);
1117 debug(20, 1) ("MemObject->clients: %p\n",
1119 debug(20, 1) ("MemObject->nclients: %d\n",
1121 debug(20, 1) ("MemObject->swapout.fd: %d\n",
1123 debug(20, 1) ("MemObject->reply: %p\n",
1125 debug(20, 1) ("MemObject->request: %p\n",
1127 debug(20, 1) ("MemObject->log_url: %p %s\n",
1129 checkNullString(mem
->log_url
));
1133 storeEntryDump(StoreEntry
* e
, int l
)
1135 debug(20, l
) ("StoreEntry->key: %s\n", storeKeyText(e
->key
));
1136 debug(20, l
) ("StoreEntry->next: %p\n", e
->next
);
1137 debug(20, l
) ("StoreEntry->mem_obj: %p\n", e
->mem_obj
);
1138 debug(20, l
) ("StoreEntry->timestamp: %d\n", (int) e
->timestamp
);
1139 debug(20, l
) ("StoreEntry->lastref: %d\n", (int) e
->lastref
);
1140 debug(20, l
) ("StoreEntry->expires: %d\n", (int) e
->expires
);
1141 debug(20, l
) ("StoreEntry->lastmod: %d\n", (int) e
->lastmod
);
1142 debug(20, l
) ("StoreEntry->swap_file_sz: %d\n", (int) e
->swap_file_sz
);
1143 debug(20, l
) ("StoreEntry->refcount: %d\n", e
->refcount
);
1144 debug(20, l
) ("StoreEntry->flags: %s\n", storeEntryFlags(e
));
1145 debug(20, l
) ("StoreEntry->swap_file_number: %d\n", (int) e
->swap_file_number
);
1146 debug(20, l
) ("StoreEntry->lock_count: %d\n", (int) e
->lock_count
);
1147 debug(20, l
) ("StoreEntry->mem_status: %d\n", (int) e
->mem_status
);
1148 debug(20, l
) ("StoreEntry->ping_status: %d\n", (int) e
->ping_status
);
1149 debug(20, l
) ("StoreEntry->store_status: %d\n", (int) e
->store_status
);
1150 debug(20, l
) ("StoreEntry->swap_status: %d\n", (int) e
->swap_status
);
1153 /* NOTE, this function assumes only two mem states */
1155 storeSetMemStatus(StoreEntry
* e
, int new_status
)
1157 MemObject
*mem
= e
->mem_obj
;
1158 if (new_status
== e
->mem_status
)
1160 assert(mem
!= NULL
);
1161 if (new_status
== IN_MEMORY
) {
1162 assert(mem
->inmem_lo
== 0);
1163 dlinkAdd(e
, &mem
->lru
, &inmem_list
);
1166 dlinkDelete(&mem
->lru
, &inmem_list
);
1169 e
->mem_status
= new_status
;
1173 storeUrl(const StoreEntry
* e
)
1176 return "[null_entry]";
1177 else if (e
->mem_obj
== NULL
)
1178 return "[null_mem_obj]";
1180 return e
->mem_obj
->url
;
1184 storeCreateMemObject(StoreEntry
* e
, const char *url
, const char *log_url
)
1188 e
->mem_obj
= new_MemObject(url
, log_url
);
1191 /* this just sets DELAY_SENDING */
1193 storeBuffer(StoreEntry
* e
)
1195 EBIT_SET(e
->flags
, DELAY_SENDING
);
1198 /* this just clears DELAY_SENDING and Invokes the handlers */
1200 storeBufferFlush(StoreEntry
* e
)
1202 EBIT_CLR(e
->flags
, DELAY_SENDING
);
1204 storeCheckSwapOut(e
);
1208 storeUnlinkFileno(int fileno
)
1210 debug(20, 5) ("storeUnlinkFileno: %08X\n", fileno
);
1212 safeunlink(storeSwapFullPath(fileno
, NULL
), 1);
1214 unlinkdUnlink(storeSwapFullPath(fileno
, NULL
));
1219 objectLen(const StoreEntry
* e
)
1221 assert(e
->mem_obj
!= NULL
);
1222 return e
->mem_obj
->object_sz
;
1226 contentLen(const StoreEntry
* e
)
1228 assert(e
->mem_obj
!= NULL
);
1229 assert(e
->mem_obj
->reply
!= NULL
);
1230 return e
->mem_obj
->object_sz
- e
->mem_obj
->reply
->hdr_sz
;
1234 storeEntryReply(StoreEntry
* e
)
1238 if (NULL
== e
->mem_obj
)
1240 return e
->mem_obj
->reply
;
1244 storeEntryReset(StoreEntry
* e
)
1246 MemObject
*mem
= e
->mem_obj
;
1247 debug(20, 3) ("storeEntryReset: %s\n", storeUrl(e
));
1248 assert(mem
->swapout
.fd
== -1);
1249 stmemFree(&mem
->data_hdr
);
1250 mem
->inmem_hi
= mem
->inmem_lo
= 0;
1251 httpReplyDestroy(mem
->reply
);
1252 mem
->reply
= httpReplyCreate();