]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/store/Controller.cc
2 * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
9 /* DEBUG: section 20 Store Controller */
14 #include "profiler/Profiler.h"
15 #include "SquidConfig.h"
16 #include "SquidMath.h"
17 #include "store/Controller.h"
18 #include "store/Disks.h"
19 #include "store/LocalSearch.h"
21 #include "Transients.h"
28 * store_dirs_rebuilding is initialized to _1_ as a hack so that
29 * storeDirWriteCleanLogs() doesn't try to do anything unless _all_
30 * cache_dirs have been read. For example, without this hack, Squid
31 * will try to write clean log files if -kparse fails (becasue it
34 int Store::Controller::store_dirs_rebuilding
= 1;
36 Store::Controller::Controller() :
44 Store::Controller::~Controller()
51 hashFreeItems(store_table
, destroyStoreEntry
);
52 hashFreeMemory(store_table
);
53 store_table
= nullptr;
58 Store::Controller::init()
60 if (Config
.memShared
&& IamWorkerProcess()) {
61 memStore
= new MemStore
;
67 if (UsingSmp() && IamWorkerProcess() && Config
.onoff
.collapsed_forwarding
&&
69 transients
= new Transients
;
75 Store::Controller::create()
83 pid
= WaitForAnyPid(status
, WNOHANG
);
84 } while (pid
> 0 || (pid
< 0 && errno
== EINTR
));
89 Store::Controller::maintain()
91 static time_t last_warn_time
= 0;
93 PROF_start(storeMaintainSwapSpace
);
96 /* this should be emitted by the oversize dir, not globally */
98 if (Root().currentSize() > Store::Root().maxSize()) {
99 if (squid_curtime
- last_warn_time
> 10) {
100 debugs(20, DBG_CRITICAL
, "WARNING: Disk space over limit: "
101 << Store::Root().currentSize() / 1024.0 << " KB > "
102 << (Store::Root().maxSize() >> 10) << " KB");
103 last_warn_time
= squid_curtime
;
107 PROF_stop(storeMaintainSwapSpace
);
111 Store::Controller::getStats(StoreInfoStats
&stats
) const
114 memStore
->getStats(stats
);
116 // move this code to a non-shared memory cache class when we have it
117 stats
.mem
.shared
= false;
118 stats
.mem
.capacity
= Config
.memMaxSize
;
119 stats
.mem
.size
= mem_node::StoreMemSize();
120 stats
.mem
.count
= hot_obj_count
;
123 swapDir
->getStats(stats
);
125 // low-level info not specific to memory or disk cache
126 stats
.store_entry_count
= StoreEntry::inUseCount();
127 stats
.mem_object_count
= MemObject::inUseCount();
131 Store::Controller::stat(StoreEntry
&output
) const
133 storeAppendPrintf(&output
, "Store Directory Statistics:\n");
134 storeAppendPrintf(&output
, "Store Entries : %lu\n",
135 (unsigned long int)StoreEntry::inUseCount());
136 storeAppendPrintf(&output
, "Maximum Swap Size : %" PRIu64
" KB\n",
138 storeAppendPrintf(&output
, "Current Store Swap Size: %.2f KB\n",
139 currentSize() / 1024.0);
140 storeAppendPrintf(&output
, "Current Capacity : %.2f%% used, %.2f%% free\n",
141 Math::doublePercent(currentSize(), maxSize()),
142 Math::doublePercent((maxSize() - currentSize()), maxSize()));
145 memStore
->stat(output
);
147 /* now the swapDir */
148 swapDir
->stat(output
);
151 /* if needed, this could be taught to cache the result */
153 Store::Controller::maxSize() const
155 /* TODO: include memory cache ? */
156 return swapDir
->maxSize();
160 Store::Controller::minSize() const
162 /* TODO: include memory cache ? */
163 return swapDir
->minSize();
167 Store::Controller::currentSize() const
169 /* TODO: include memory cache ? */
170 return swapDir
->currentSize();
174 Store::Controller::currentCount() const
176 /* TODO: include memory cache ? */
177 return swapDir
->currentCount();
181 Store::Controller::maxObjectSize() const
183 /* TODO: include memory cache ? */
184 return swapDir
->maxObjectSize();
188 Store::Controller::updateLimits()
190 swapDir
->updateLimits();
192 store_swap_high
= (long) (((float) maxSize() *
193 (float) Config
.Swap
.highWaterMark
) / (float) 100);
194 store_swap_low
= (long) (((float) maxSize() *
195 (float) Config
.Swap
.lowWaterMark
) / (float) 100);
196 store_pages_max
= Config
.memMaxSize
/ sizeof(mem_node
);
198 // TODO: move this into a memory cache class when we have one
199 const int64_t memMax
= static_cast<int64_t>(min(Config
.Store
.maxInMemObjSize
, Config
.memMaxSize
));
200 const int64_t disksMax
= swapDir
? swapDir
->maxObjectSize() : 0;
201 store_maxobjsize
= std::max(disksMax
, memMax
);
205 Store::Controller::search()
207 // this is the only kind of search we currently support
208 return NewLocalSearch();
212 Store::Controller::sync(void)
220 * handle callbacks all avaliable fs'es
223 Store::Controller::callback()
225 /* This will likely double count. Thats ok. */
226 PROF_start(storeDirCallback
);
228 /* mem cache callbacks ? */
229 int result
= swapDir
->callback();
231 PROF_stop(storeDirCallback
);
237 Store::Controller::referenceBusy(StoreEntry
&e
)
239 // special entries do not belong to any specific Store, but are IN_MEMORY
240 if (EBIT_TEST(e
.flags
, ENTRY_SPECIAL
))
243 /* Notify the fs that we're referencing this object again */
245 if (e
.swap_dirn
> -1)
246 swapDir
->reference(e
);
248 // Notify the memory cache that we're referencing this object again
249 if (memStore
&& e
.mem_status
== IN_MEMORY
)
250 memStore
->reference(e
);
252 // TODO: move this code to a non-shared memory cache class when we have it
254 if (mem_policy
->Referenced
)
255 mem_policy
->Referenced(mem_policy
, &e
, &e
.mem_obj
->repl
);
260 Store::Controller::dereferenceIdle(StoreEntry
&e
, bool wantsLocalMemory
)
262 // special entries do not belong to any specific Store, but are IN_MEMORY
263 if (EBIT_TEST(e
.flags
, ENTRY_SPECIAL
))
266 bool keepInStoreTable
= false; // keep only if somebody needs it there
268 /* Notify the fs that we're not referencing this object any more */
270 if (e
.swap_filen
> -1)
271 keepInStoreTable
= swapDir
->dereference(e
) || keepInStoreTable
;
273 // Notify the memory cache that we're not referencing this object any more
274 if (memStore
&& e
.mem_status
== IN_MEMORY
)
275 keepInStoreTable
= memStore
->dereference(e
) || keepInStoreTable
;
277 // TODO: move this code to a non-shared memory cache class when we have it
279 if (mem_policy
->Dereferenced
)
280 mem_policy
->Dereferenced(mem_policy
, &e
, &e
.mem_obj
->repl
);
281 // non-shared memory cache relies on store_table
283 keepInStoreTable
= wantsLocalMemory
|| keepInStoreTable
;
286 return keepInStoreTable
;
290 Store::Controller::get(const cache_key
*key
)
292 if (StoreEntry
*e
= find(key
)) {
293 // this is not very precise: some get()s are not initiated by clients
301 /// Internal method to implements the guts of the Store::get() API:
302 /// returns an in-transit or cached object with a given key, if any.
304 Store::Controller::find(const cache_key
*key
)
306 debugs(20, 3, storeKeyText(key
));
308 if (StoreEntry
*e
= static_cast<StoreEntry
*>(hash_lookup(store_table
, key
))) {
309 // TODO: ignore and maybe handleIdleEntry() unlocked intransit entries
310 // because their backing store slot may be gone already.
311 debugs(20, 3, HERE
<< "got in-transit entry: " << *e
);
315 // Must search transients before caches because we must sync those we find.
317 if (StoreEntry
*e
= transients
->get(key
)) {
318 debugs(20, 3, "got shared in-transit entry: " << *e
);
320 const bool found
= anchorCollapsed(*e
, inSync
);
321 if (!found
|| inSync
)
323 assert(!e
->locked()); // ensure release will destroyStoreEntry()
324 e
->release(); // do not let others into the same trap
330 if (StoreEntry
*e
= memStore
->get(key
)) {
331 debugs(20, 3, HERE
<< "got mem-cached entry: " << *e
);
337 if (StoreEntry
*e
= swapDir
->get(key
)) {
338 debugs(20, 3, "got disk-cached entry: " << *e
);
343 debugs(20, 4, "cannot locate " << storeKeyText(key
));
348 Store::Controller::accumulateMore(StoreEntry
&entry
) const
350 return swapDir
? swapDir
->accumulateMore(entry
) : 0;
351 // The memory cache should not influence for-swapout accumulation decision.
355 Store::Controller::markForUnlink(StoreEntry
&e
)
357 if (transients
&& e
.mem_obj
&& e
.mem_obj
->xitTable
.index
>= 0)
358 transients
->markForUnlink(e
);
359 if (memStore
&& e
.mem_obj
&& e
.mem_obj
->memCache
.index
>= 0)
360 memStore
->markForUnlink(e
);
361 if (swapDir
&& e
.swap_filen
>= 0)
362 swapDir
->markForUnlink(e
);
366 Store::Controller::unlink(StoreEntry
&e
)
369 if (swapDir
&& e
.swap_filen
>= 0)
373 // move this into [non-shared] memory cache class when we have one
374 /// whether e should be kept in local RAM for possible future caching
376 Store::Controller::keepForLocalMemoryCache(StoreEntry
&e
) const
378 if (!e
.memoryCachable())
381 // does the current and expected size obey memory caching limits?
383 const int64_t loadedSize
= e
.mem_obj
->endOffset();
384 const int64_t expectedSize
= e
.mem_obj
->expectedReplySize(); // may be < 0
385 const int64_t ramSize
= max(loadedSize
, expectedSize
);
386 const int64_t ramLimit
= min(
387 static_cast<int64_t>(Config
.memMaxSize
),
388 static_cast<int64_t>(Config
.Store
.maxInMemObjSize
));
389 return ramSize
<= ramLimit
;
393 Store::Controller::memoryOut(StoreEntry
&e
, const bool preserveSwappable
)
395 bool keepInLocalMemory
= false;
397 memStore
->write(e
); // leave keepInLocalMemory false
399 keepInLocalMemory
= keepForLocalMemoryCache(e
);
401 debugs(20, 7, HERE
<< "keepInLocalMemory: " << keepInLocalMemory
);
403 if (!keepInLocalMemory
)
404 e
.trimMemory(preserveSwappable
);
408 Store::Controller::memoryUnlink(StoreEntry
&e
)
412 else // TODO: move into [non-shared] memory cache class when we have one
413 e
.destroyMemObject();
417 Store::Controller::memoryDisconnect(StoreEntry
&e
)
420 memStore
->disconnect(e
);
421 // else nothing to do for non-shared memory cache
425 Store::Controller::transientsAbandon(StoreEntry
&e
)
429 if (e
.mem_obj
->xitTable
.index
>= 0)
430 transients
->abandon(e
);
435 Store::Controller::transientsCompleteWriting(StoreEntry
&e
)
439 if (e
.mem_obj
->xitTable
.index
>= 0)
440 transients
->completeWriting(e
);
445 Store::Controller::transientReaders(const StoreEntry
&e
) const
447 return (transients
&& e
.mem_obj
&& e
.mem_obj
->xitTable
.index
>= 0) ?
448 transients
->readers(e
) : 0;
452 Store::Controller::transientsDisconnect(MemObject
&mem_obj
)
455 transients
->disconnect(mem_obj
);
459 Store::Controller::handleIdleEntry(StoreEntry
&e
)
461 bool keepInLocalMemory
= false;
463 if (EBIT_TEST(e
.flags
, ENTRY_SPECIAL
)) {
464 // Icons (and cache digests?) should stay in store_table until we
465 // have a dedicated storage for them (that would not purge them).
466 // They are not managed [well] by any specific Store handled below.
467 keepInLocalMemory
= true;
468 } else if (memStore
) {
469 // leave keepInLocalMemory false; memStore maintains its own cache
471 keepInLocalMemory
= keepForLocalMemoryCache(e
) && // in good shape and
472 // the local memory cache is not overflowing
473 (mem_node::InUseCount() <= store_pages_max
);
476 // An idle, unlocked entry that only belongs to a SwapDir which controls
477 // its own index, should not stay in the global store_table.
478 if (!dereferenceIdle(e
, keepInLocalMemory
)) {
479 debugs(20, 5, HERE
<< "destroying unlocked entry: " << &e
<< ' ' << e
);
480 destroyStoreEntry(static_cast<hash_link
*>(&e
));
484 debugs(20, 5, HERE
<< "keepInLocalMemory: " << keepInLocalMemory
);
486 // TODO: move this into [non-shared] memory cache class when we have one
487 if (keepInLocalMemory
) {
488 e
.setMemStatus(IN_MEMORY
);
489 e
.mem_obj
->unlinkRequest();
491 e
.purgeMem(); // may free e
496 Store::Controller::updateOnNotModified(StoreEntry
*old
, const StoreEntry
&newer
)
498 /* update the old entry object */
500 HttpReply
*oldReply
= const_cast<HttpReply
*>(old
->getReply());
503 const bool modified
= oldReply
->updateOnNotModified(newer
.getReply());
504 if (!old
->timestampsSet() && !modified
)
507 /* update stored image of the old entry */
509 if (memStore
&& old
->mem_status
== IN_MEMORY
&& !EBIT_TEST(old
->flags
, ENTRY_SPECIAL
))
510 memStore
->updateHeaders(old
);
512 if (old
->swap_dirn
> -1)
513 swapDir
->updateHeaders(old
);
517 Store::Controller::allowCollapsing(StoreEntry
*e
, const RequestFlags
&reqFlags
,
518 const HttpRequestMethod
&reqMethod
)
520 const KeyScope keyScope
= reqFlags
.refresh
? ksRevalidation
: ksDefault
;
521 e
->makePublic(keyScope
); // this is needed for both local and SMP collapsing
523 transients
->startWriting(e
, reqFlags
, reqMethod
);
524 debugs(20, 3, "may " << (transients
&& e
->mem_obj
->xitTable
.index
>= 0 ?
525 "SMP-" : "locally-") << "collapse " << *e
);
529 Store::Controller::syncCollapsed(const sfileno xitIndex
)
533 StoreEntry
*collapsed
= transients
->findCollapsed(xitIndex
);
534 if (!collapsed
) { // the entry is no longer locally active, ignore update
535 debugs(20, 7, "not SMP-syncing not-transient " << xitIndex
);
538 assert(collapsed
->mem_obj
);
539 assert(collapsed
->mem_obj
->smpCollapsed
);
541 debugs(20, 7, "syncing " << *collapsed
);
543 bool abandoned
= transients
->abandoned(*collapsed
);
546 if (memStore
&& collapsed
->mem_obj
->memCache
.io
== MemObject::ioDone
) {
549 debugs(20, 7, "fully mem-loaded " << *collapsed
);
550 } else if (memStore
&& collapsed
->mem_obj
->memCache
.index
>= 0) {
552 inSync
= memStore
->updateCollapsed(*collapsed
);
553 } else if (swapDir
&& collapsed
->swap_filen
>= 0) {
555 inSync
= swapDir
->updateCollapsed(*collapsed
);
557 found
= anchorCollapsed(*collapsed
, inSync
);
560 if (abandoned
&& collapsed
->store_status
== STORE_PENDING
) {
561 debugs(20, 3, "aborting abandoned but STORE_PENDING " << *collapsed
);
567 debugs(20, 5, "synced " << *collapsed
);
568 collapsed
->invokeHandlers();
569 } else if (found
) { // unrecoverable problem syncing this entry
570 debugs(20, 3, "aborting unsyncable " << *collapsed
);
572 } else { // the entry is still not in one of the caches
573 debugs(20, 7, "waiting " << *collapsed
);
577 /// Called for in-transit entries that are not yet anchored to a cache.
578 /// For cached entries, return true after synchronizing them with their cache
579 /// (making inSync true on success). For not-yet-cached entries, return false.
581 Store::Controller::anchorCollapsed(StoreEntry
&collapsed
, bool &inSync
)
583 // this method is designed to work with collapsed transients only
584 assert(collapsed
.mem_obj
);
585 assert(collapsed
.mem_obj
->xitTable
.index
>= 0);
586 assert(collapsed
.mem_obj
->smpCollapsed
);
588 debugs(20, 7, "anchoring " << collapsed
);
592 found
= memStore
->anchorCollapsed(collapsed
, inSync
);
593 if (!found
&& swapDir
)
594 found
= swapDir
->anchorCollapsed(collapsed
, inSync
);
598 debugs(20, 7, "anchored " << collapsed
);
600 debugs(20, 5, "failed to anchor " << collapsed
);
602 debugs(20, 7, "skipping not yet cached " << collapsed
);
609 Store::Controller::smpAware() const
611 return memStore
|| (swapDir
&& swapDir
->smpAware());
615 static RefCount
<Controller
> TheRoot
;
626 Store::Init(Controller
*root
)
628 TheRoot
= root
? root
: new Controller
;