]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/store/Controller.cc
50641ae86028fdaa74316cde3108e53a6694b8a3
2 * Copyright (C) 1996-2025 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 "SquidConfig.h"
15 #include "SquidMath.h"
16 #include "store/Controller.h"
17 #include "store/Disks.h"
18 #include "store/forward.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 (because it
34 int Store::Controller::store_dirs_rebuilding
= 1;
36 Store::Controller::Controller() :
38 sharedMemStore(nullptr),
45 /// this destructor is never called because Controller singleton is immortal
46 Store::Controller::~Controller()
48 // assert at runtime because we cannot `= delete` an overridden destructor
49 assert(!"Controller is never destroyed");
53 Store::Controller::init()
55 if (IamWorkerProcess()) {
56 if (MemStore::Enabled()) {
57 sharedMemStore
= new MemStore
;
58 sharedMemStore
->init();
59 } else if (Config
.memMaxSize
> 0) {
66 if (Transients::Enabled() && IamWorkerProcess()) {
67 transients
= new Transients
;
73 Store::Controller::create()
77 #if !(_SQUID_WINDOWS_ || _SQUID_MINGW_)
81 pid
= WaitForAnyPid(status
, WNOHANG
);
82 } while (pid
> 0 || (pid
< 0 && errno
== EINTR
));
87 Store::Controller::maintain()
89 static time_t last_warn_time
= 0;
93 /* this should be emitted by the oversize dir, not globally */
95 if (Root().currentSize() > Store::Root().maxSize()) {
96 if (squid_curtime
- last_warn_time
> 10) {
97 debugs(20, DBG_CRITICAL
, "WARNING: Disk space over limit: "
98 << Store::Root().currentSize() / 1024.0 << " KB > "
99 << (Store::Root().maxSize() >> 10) << " KB");
100 last_warn_time
= squid_curtime
;
106 Store::Controller::getStats(StoreInfoStats
&stats
) const
109 sharedMemStore
->getStats(stats
);
111 // move this code to a non-shared memory cache class when we have it
112 stats
.mem
.shared
= false;
113 stats
.mem
.capacity
= Config
.memMaxSize
;
114 stats
.mem
.size
= mem_node::StoreMemSize();
116 // XXX: also count internal/in-transit objects
117 stats
.mem
.count
= hot_obj_count
;
119 // XXX: count internal/in-transit objects instead
120 stats
.mem
.count
= hot_obj_count
;
124 disks
->getStats(stats
);
126 // low-level info not specific to memory or disk cache
127 stats
.store_entry_count
= StoreEntry::inUseCount();
128 stats
.mem_object_count
= MemObject::inUseCount();
132 Store::Controller::stat(StoreEntry
&output
) const
134 storeAppendPrintf(&output
, "Store Directory Statistics:\n");
135 storeAppendPrintf(&output
, "Store Entries : %lu\n",
136 (unsigned long int)StoreEntry::inUseCount());
137 storeAppendPrintf(&output
, "Maximum Swap Size : %" PRIu64
" KB\n",
139 storeAppendPrintf(&output
, "Current Store Swap Size: %.2f KB\n",
140 currentSize() / 1024.0);
141 storeAppendPrintf(&output
, "Current Capacity : %.2f%% used, %.2f%% free\n",
142 Math::doublePercent(currentSize(), maxSize()),
143 Math::doublePercent((maxSize() - currentSize()), maxSize()));
146 sharedMemStore
->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 disks
->maxSize();
160 Store::Controller::minSize() const
162 /* TODO: include memory cache ? */
163 return disks
->minSize();
167 Store::Controller::currentSize() const
169 /* TODO: include memory cache ? */
170 return disks
->currentSize();
174 Store::Controller::currentCount() const
176 /* TODO: include memory cache ? */
177 return disks
->currentCount();
181 Store::Controller::maxObjectSize() const
183 /* TODO: include memory cache ? */
184 return disks
->maxObjectSize();
188 Store::Controller::configure()
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
= disks
->maxObjectSize();
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)
215 sharedMemStore
->sync();
220 * handle callbacks all available fs'es
223 Store::Controller::callback()
225 /* mem cache callbacks ? */
226 return disks
->callback();
229 /// update reference counters of the recently touched entry
231 Store::Controller::referenceBusy(StoreEntry
&e
)
233 // special entries do not belong to any specific Store, but are IN_MEMORY
234 if (EBIT_TEST(e
.flags
, ENTRY_SPECIAL
))
237 /* Notify the fs that we're referencing this object again */
242 // Notify the memory cache that we're referencing this object again
243 if (sharedMemStore
&& e
.mem_status
== IN_MEMORY
)
244 sharedMemStore
->reference(e
);
246 // TODO: move this code to a non-shared memory cache class when we have it
248 if (mem_policy
->Referenced
)
249 mem_policy
->Referenced(mem_policy
, &e
, &e
.mem_obj
->repl
);
253 /// dereference()s an idle entry
254 /// \returns false if and only if the entry should be deleted
256 Store::Controller::dereferenceIdle(StoreEntry
&e
, bool wantsLocalMemory
)
258 // special entries do not belong to any specific Store, but are IN_MEMORY
259 if (EBIT_TEST(e
.flags
, ENTRY_SPECIAL
))
262 // idle private entries cannot be reused
263 if (EBIT_TEST(e
.flags
, KEY_PRIVATE
))
266 bool keepInStoreTable
= false; // keep only if somebody needs it there
268 // Notify the fs that we are not referencing this object any more. This
269 // should be done even if we overwrite keepInStoreTable afterwards.
272 keepInStoreTable
= disks
->dereference(e
) || keepInStoreTable
;
274 // Notify the memory cache that we're not referencing this object any more
275 if (sharedMemStore
&& e
.mem_status
== IN_MEMORY
)
276 keepInStoreTable
= sharedMemStore
->dereference(e
) || keepInStoreTable
;
278 // TODO: move this code to a non-shared memory cache class when we have it
280 if (mem_policy
->Dereferenced
)
281 mem_policy
->Dereferenced(mem_policy
, &e
, &e
.mem_obj
->repl
);
282 // non-shared memory cache relies on store_table
284 keepInStoreTable
= wantsLocalMemory
|| keepInStoreTable
;
287 if (e
.hittingRequiresCollapsing()) {
288 // If we were writing this now-locally-idle entry, then we did not
289 // finish and should now destroy an incomplete entry. Otherwise, do not
290 // leave this idle StoreEntry behind because handleIMSReply() lacks
291 // freshness checks when hitting a collapsed revalidation entry.
292 keepInStoreTable
= false; // may overrule fs decisions made above
295 return keepInStoreTable
;
299 Store::Controller::markedForDeletion(const cache_key
*key
) const
301 // assuming a public key, checking Transients should cover all cases.
302 return transients
&& transients
->markedForDeletion(key
);
306 Store::Controller::markedForDeletionAndAbandoned(const StoreEntry
&e
) const
308 // The opposite check order could miss a reader that has arrived after the
309 // !readers() and before the markedForDeletion() check.
310 return markedForDeletion(reinterpret_cast<const cache_key
*>(e
.key
)) &&
311 transients
&& !transients
->readers(e
);
315 Store::Controller::hasReadableDiskEntry(const StoreEntry
&e
) const
317 return disks
->hasReadableEntry(e
);
320 /// flags problematic entries before find() commits to finalizing/returning them
322 Store::Controller::checkFoundCandidate(const StoreEntry
&entry
) const
324 checkTransients(entry
);
326 // The "hittingRequiresCollapsing() has an active writer" checks below
327 // protect callers from getting stuck and/or from using a stale revalidation
328 // reply. However, these protections are not reliable because the writer may
329 // disappear at any time and/or without a trace. Collapsing adds risks...
330 if (entry
.hittingRequiresCollapsing()) {
331 if (entry
.hasTransients()) {
332 // Too late to check here because the writer may be gone by now, but
333 // Transients do check when they setCollapsingRequirement().
335 // a local writer must hold a lock on its writable entry
336 if (!(entry
.locked() && entry
.isAccepting()))
337 throw TextException("no local writer", Here());
343 Store::Controller::find(const cache_key
*key
)
345 if (const auto entry
= peek(key
)) {
348 allowSharing(*entry
, key
);
349 checkFoundCandidate(*entry
);
351 referenceBusy(*entry
);
353 } catch (const std::exception
&ex
) {
354 debugs(20, 2, "failed with " << *entry
<< ": " << ex
.what());
362 /// indexes and adds SMP-tracking for an ephemeral peek() result
364 Store::Controller::allowSharing(StoreEntry
&entry
, const cache_key
*key
)
366 // anchorToCache() below and many find() callers expect a registered entry
367 addReading(&entry
, key
);
369 if (entry
.hasTransients()) {
370 // store hadWriter before computing `found`; \see Transients::get()
371 const auto hadWriter
= transients
->hasWriter(entry
);
372 const auto found
= anchorToCache(entry
);
374 // !found should imply hittingRequiresCollapsing() regardless of writer presence
375 if (!entry
.hittingRequiresCollapsing()) {
376 debugs(20, DBG_IMPORTANT
, "ERROR: Squid BUG: missing ENTRY_REQUIRES_COLLAPSING for " << entry
);
377 throw TextException("transients entry missing ENTRY_REQUIRES_COLLAPSING", Here());
381 // prevent others from falling into the same trap
382 throw TextException("unattached transients entry missing writer", Here());
389 Store::Controller::findCallbackXXX(const cache_key
*key
)
391 // We could check for mem_obj presence (and more), moving and merging some
392 // of the duplicated neighborsUdpAck() and neighborsHtcpReply() code here,
393 // but that would mean polluting Store with HTCP/ICP code. Instead, we
394 // should encapsulate callback-related data in a protocol-neutral MemObject
395 // member or use an HTCP/ICP-specific index rather than store_table.
397 // cannot reuse peekAtLocal() because HTCP/ICP callbacks may use private keys
398 return static_cast<StoreEntry
*>(hash_lookup(store_table
, key
));
401 /// \returns either an existing local reusable StoreEntry object or nil
402 /// To treat remotely marked entries specially,
403 /// callers ought to check markedForDeletion() first!
405 Store::Controller::peekAtLocal(const cache_key
*key
)
407 if (StoreEntry
*e
= static_cast<StoreEntry
*>(hash_lookup(store_table
, key
))) {
408 // callers must only search for public entries
409 assert(!EBIT_TEST(e
->flags
, KEY_PRIVATE
));
410 assert(e
->publicKey());
413 // TODO: ignore and maybe handleIdleEntry() unlocked intransit entries
414 // because their backing store slot may be gone already.
421 Store::Controller::peek(const cache_key
*key
)
423 debugs(20, 3, storeKeyText(key
));
425 if (markedForDeletion(key
)) {
426 debugs(20, 3, "ignoring marked in-transit " << storeKeyText(key
));
430 if (StoreEntry
*e
= peekAtLocal(key
)) {
431 debugs(20, 3, "got local in-transit entry: " << *e
);
435 // Must search transients before caches because we must sync those we find.
437 if (StoreEntry
*e
= transients
->get(key
)) {
438 debugs(20, 3, "got shared in-transit entry: " << *e
);
443 if (sharedMemStore
) {
444 if (StoreEntry
*e
= sharedMemStore
->get(key
)) {
445 debugs(20, 3, "got mem-cached entry: " << *e
);
450 if (const auto e
= disks
->get(key
)) {
451 debugs(20, 3, "got disk-cached entry: " << *e
);
455 debugs(20, 4, "cannot locate " << storeKeyText(key
));
460 Store::Controller::transientsReader(const StoreEntry
&e
) const
462 return transients
&& e
.hasTransients() && transients
->isReader(e
);
466 Store::Controller::transientsWriter(const StoreEntry
&e
) const
468 return transients
&& e
.hasTransients() && transients
->isWriter(e
);
472 Store::Controller::accumulateMore(StoreEntry
&entry
) const
474 return disks
->accumulateMore(entry
);
475 // The memory cache should not influence for-swapout accumulation decision.
478 // Must be called from StoreEntry::release() or releaseRequest() because
479 // those methods currently manage local indexing of StoreEntry objects.
480 // TODO: Replace StoreEntry::release*() with Root().evictCached().
482 Store::Controller::evictCached(StoreEntry
&e
)
486 transients
->evictCached(e
);
487 memoryEvictCached(e
);
488 disks
->evictCached(e
);
492 Store::Controller::evictIfFound(const cache_key
*key
)
494 debugs(20, 7, storeKeyText(key
));
496 if (StoreEntry
*entry
= peekAtLocal(key
)) {
497 debugs(20, 5, "marking local in-transit " << *entry
);
498 entry
->release(true);
503 sharedMemStore
->evictIfFound(key
);
505 disks
->evictIfFound(key
);
508 transients
->evictIfFound(key
);
511 /// whether the memory cache is allowed to store that many additional pages
513 Store::Controller::memoryCacheHasSpaceFor(const int pagesRequired
) const
515 // XXX: We count mem_nodes but may free shared memory pages instead.
516 const auto fits
= mem_node::InUseCount() + pagesRequired
<= store_pages_max
;
517 debugs(20, 7, fits
<< ": " << mem_node::InUseCount() << '+' << pagesRequired
<< '?' << store_pages_max
);
522 Store::Controller::freeMemorySpace(const int bytesRequired
)
524 const auto pagesRequired
= (bytesRequired
+ SM_PAGE_SIZE
-1) / SM_PAGE_SIZE
;
526 if (memoryCacheHasSpaceFor(pagesRequired
))
529 // XXX: When store_pages_max is smaller than pagesRequired, we should not
530 // look for more space (but we do because we want to abandon idle entries?).
532 // limit our performance impact to one walk per second
533 static time_t lastWalk
= 0;
534 if (lastWalk
== squid_curtime
)
536 lastWalk
= squid_curtime
;
538 debugs(20, 2, "need " << pagesRequired
<< " pages");
540 // let abandon()/handleIdleEntry() know about the impeding memory shortage
541 memoryPagesDebt_
= pagesRequired
;
543 // XXX: SMP-unaware: Walkers should iterate memory cache, not store_table.
544 // XXX: Limit iterations by time, not arbitrary count.
545 const auto walker
= mem_policy
->PurgeInit(mem_policy
, 100000);
547 while (const auto entry
= walker
->Next(walker
)) {
548 // Abandoned memory cache entries are purged during memory shortage.
549 entry
->abandon(__func__
); // may delete entry
552 if (memoryCacheHasSpaceFor(pagesRequired
))
555 // TODO: Move to RemovalPolicyWalker::Done() that has more/better details.
556 debugs(20, 3, "removed " << removed
<< " out of " << hot_obj_count
<< " memory-cached entries");
557 walker
->Done(walker
);
558 memoryPagesDebt_
= 0;
561 // move this into [non-shared] memory cache class when we have one
562 /// whether e should be kept in local RAM for possible future caching
564 Store::Controller::keepForLocalMemoryCache(StoreEntry
&e
) const
566 if (!e
.memoryCachable())
569 // does the current and expected size obey memory caching limits?
571 const int64_t loadedSize
= e
.mem_obj
->endOffset();
572 const int64_t expectedSize
= e
.mem_obj
->expectedReplySize(); // may be < 0
573 const int64_t ramSize
= max(loadedSize
, expectedSize
);
574 const int64_t ramLimit
= min(
575 static_cast<int64_t>(Config
.memMaxSize
),
576 static_cast<int64_t>(Config
.Store
.maxInMemObjSize
));
577 return ramSize
<= ramLimit
;
581 Store::Controller::memoryOut(StoreEntry
&e
, const bool preserveSwappable
)
583 bool keepInLocalMemory
= false;
585 sharedMemStore
->write(e
); // leave keepInLocalMemory false
586 else if (localMemStore
)
587 keepInLocalMemory
= keepForLocalMemoryCache(e
);
589 debugs(20, 7, "keepInLocalMemory: " << keepInLocalMemory
);
591 if (!keepInLocalMemory
)
592 e
.trimMemory(preserveSwappable
);
595 /// removes the entry from the memory cache
596 /// XXX: Dangerous side effect: Unlocked entries lose their mem_obj.
598 Store::Controller::memoryEvictCached(StoreEntry
&e
)
600 // TODO: Untangle memory caching from mem_obj.
602 sharedMemStore
->evictCached(e
);
603 else // TODO: move into [non-shared] memory cache class when we have one
605 e
.destroyMemObject();
609 Store::Controller::memoryDisconnect(StoreEntry
&e
)
612 sharedMemStore
->disconnect(e
);
613 // else nothing to do for non-shared memory cache
617 Store::Controller::noteStoppedSharedWriting(StoreEntry
&e
)
619 if (transients
&& e
.hasTransients()) // paranoid: the caller should check
620 transients
->completeWriting(e
);
624 Store::Controller::transientReaders(const StoreEntry
&e
) const
626 return (transients
&& e
.hasTransients()) ?
627 transients
->readers(e
) : 0;
631 Store::Controller::transientsDisconnect(StoreEntry
&e
)
634 transients
->disconnect(e
);
638 Store::Controller::handleIdleEntry(StoreEntry
&e
)
640 bool keepInLocalMemory
= false;
642 if (EBIT_TEST(e
.flags
, ENTRY_SPECIAL
)) {
643 // Icons (and cache digests?) should stay in store_table until we
644 // have a dedicated storage for them (that would not purge them).
645 // They are not managed [well] by any specific Store handled below.
646 keepInLocalMemory
= true;
647 } else if (sharedMemStore
) {
648 // leave keepInLocalMemory false; sharedMemStore maintains its own cache
649 } else if (localMemStore
) {
650 keepInLocalMemory
= keepForLocalMemoryCache(e
) && // in good shape and
651 // the local memory cache is not overflowing
652 memoryCacheHasSpaceFor(memoryPagesDebt_
);
655 // An idle, unlocked entry that only belongs to a SwapDir which controls
656 // its own index, should not stay in the global store_table.
657 if (!dereferenceIdle(e
, keepInLocalMemory
)) {
658 debugs(20, 5, "destroying unlocked entry: " << &e
<< ' ' << e
);
659 destroyStoreEntry(static_cast<hash_link
*>(&e
));
663 debugs(20, 5, "keepInLocalMemory: " << keepInLocalMemory
);
665 // formerly known as "WARNING: found KEY_PRIVATE"
666 assert(!EBIT_TEST(e
.flags
, KEY_PRIVATE
));
668 // TODO: move this into [non-shared] memory cache class when we have one
669 if (keepInLocalMemory
) {
670 e
.setMemStatus(IN_MEMORY
);
671 e
.mem_obj
->unlinkRequest();
675 // We know the in-memory data will be gone. Get rid of the entire entry if
676 // it has nothing worth preserving on disk either.
677 if (!e
.swappedOut()) {
678 e
.release(); // deletes e
682 memoryEvictCached(e
); // may already be gone
683 // and keep the entry in store_table for its on-disk data
687 Store::Controller::updateOnNotModified(StoreEntry
*old
, StoreEntry
&e304
)
693 // updateOnNotModified() may be called many times for the same old entry.
694 // e304.mem_obj->appliedUpdates value distinguishes two cases:
695 // false: Independent store clients revalidating the same old StoreEntry.
696 // Each such update uses its own e304. The old StoreEntry
697 // accumulates such independent updates.
698 // true: Store clients feeding off the same 304 response. Each such update
699 // uses the same e304. For timestamps correctness and performance
700 // sake, it is best to detect and skip such repeated update calls.
701 if (e304
.mem_obj
->appliedUpdates
) {
702 debugs(20, 5, "ignored repeated update of " << *old
<< " with " << e304
);
705 e304
.mem_obj
->appliedUpdates
= true;
708 if (!old
->updateOnNotModified(e304
)) {
709 debugs(20, 5, "updated nothing in " << *old
<< " with " << e304
);
713 debugs(20, DBG_IMPORTANT
, "ERROR: Failed to update a cached response: " << CurrentException
);
717 if (sharedMemStore
&& old
->mem_status
== IN_MEMORY
&& !EBIT_TEST(old
->flags
, ENTRY_SPECIAL
))
718 sharedMemStore
->updateHeaders(old
);
720 if (old
->swap_dirn
> -1)
721 disks
->updateHeaders(old
);
727 Store::Controller::allowCollapsing(StoreEntry
*e
, const RequestFlags
&reqFlags
,
728 const HttpRequestMethod
&)
730 const KeyScope keyScope
= reqFlags
.refresh
? ksRevalidation
: ksDefault
;
731 // set the flag now so that it gets copied into the Transients entry
732 e
->setCollapsingRequirement(true);
733 if (e
->makePublic(keyScope
)) { // this is needed for both local and SMP collapsing
734 debugs(20, 3, "may " << (transients
&& e
->hasTransients() ?
735 "SMP-" : "locally-") << "collapse " << *e
);
736 assert(e
->hittingRequiresCollapsing());
739 // paranoid cleanup; the flag is meaningless for private entries
740 e
->setCollapsingRequirement(false);
745 Store::Controller::addReading(StoreEntry
*e
, const cache_key
*key
)
748 transients
->monitorIo(e
, key
, Store::ioReading
);
753 Store::Controller::addWriting(StoreEntry
*e
, const cache_key
*key
)
756 if (EBIT_TEST(e
->flags
, ENTRY_SPECIAL
))
757 return; // constant memory-resident entries do not need transients
760 transients
->monitorIo(e
, key
, Store::ioWriting
);
761 // else: non-SMP configurations do not need transients
765 Store::Controller::syncCollapsed(const sfileno xitIndex
)
769 StoreEntry
*collapsed
= transients
->findCollapsed(xitIndex
);
770 if (!collapsed
) { // the entry is no longer active, ignore update
771 debugs(20, 7, "not SMP-syncing not-transient " << xitIndex
);
775 if (!collapsed
->locked()) {
776 debugs(20, 3, "skipping (and may destroy) unlocked " << *collapsed
);
777 handleIdleEntry(*collapsed
);
781 assert(collapsed
->mem_obj
);
783 if (EBIT_TEST(collapsed
->flags
, ENTRY_ABORTED
)) {
784 debugs(20, 3, "skipping already aborted " << *collapsed
);
788 debugs(20, 7, "syncing " << *collapsed
);
790 Transients::EntryStatus entryStatus
;
791 transients
->status(*collapsed
, entryStatus
);
793 if (entryStatus
.waitingToBeFreed
) {
794 debugs(20, 3, "will release " << *collapsed
<< " due to waitingToBeFreed");
795 collapsed
->release(true); // may already be marked
798 if (transients
->isWriter(*collapsed
))
799 return; // readers can only change our waitingToBeFreed flag
801 assert(transients
->isReader(*collapsed
));
805 if (sharedMemStore
&& collapsed
->mem_obj
->memCache
.io
== Store::ioDone
) {
808 debugs(20, 7, "already handled by memory store: " << *collapsed
);
809 } else if (sharedMemStore
&& collapsed
->hasMemStore()) {
811 inSync
= sharedMemStore
->updateAnchored(*collapsed
);
812 // TODO: handle entries attached to both memory and disk
813 } else if (collapsed
->hasDisk()) {
815 inSync
= disks
->updateAnchored(*collapsed
);
818 found
= anchorToCache(*collapsed
);
821 // TODO: Write an exception handler for the entire method.
822 debugs(20, 3, "anchorToCache() failed for " << *collapsed
<< ": " << CurrentException
);
828 if (entryStatus
.waitingToBeFreed
&& !found
) {
829 debugs(20, 3, "aborting unattached " << *collapsed
<<
830 " because it was marked for deletion before we could attach it");
836 debugs(20, 5, "synced " << *collapsed
);
838 collapsed
->setCollapsingRequirement(false);
839 collapsed
->invokeHandlers();
843 if (found
) { // unrecoverable problem syncing this entry
844 debugs(20, 3, "aborting unsyncable " << *collapsed
);
849 if (!entryStatus
.hasWriter
) {
850 debugs(20, 3, "aborting abandoned-by-writer " << *collapsed
);
855 // the entry is still not in one of the caches
856 debugs(20, 7, "waiting " << *collapsed
);
857 collapsed
->setCollapsingRequirement(true);
860 /// If possible and has not been done, associates the entry with its store(s).
861 /// \returns false for not-yet-cached entries that we may attach later
862 /// \returns true for other entries after synchronizing them with their store
864 Store::Controller::anchorToCache(StoreEntry
&entry
)
866 assert(entry
.hasTransients());
867 assert(transientsReader(entry
));
869 // TODO: Attach entries to both memory and disk
871 // TODO: Reduce code duplication with syncCollapsed()
872 if (sharedMemStore
&& entry
.mem().memCache
.io
== Store::ioDone
) {
873 debugs(20, 5, "already handled by memory store: " << entry
);
875 } else if (sharedMemStore
&& entry
.hasMemStore()) {
876 debugs(20, 5, "already anchored to memory store: " << entry
);
878 } else if (entry
.hasDisk()) {
879 debugs(20, 5, "already anchored to disk: " << entry
);
883 debugs(20, 7, "anchoring " << entry
);
885 Transients::EntryStatus entryStatus
;
886 transients
->status(entry
, entryStatus
);
890 found
= sharedMemStore
->anchorToCache(entry
);
892 found
= disks
->anchorToCache(entry
);
895 debugs(20, 7, "anchored " << entry
);
896 entry
.setCollapsingRequirement(false);
900 if (entryStatus
.waitingToBeFreed
)
901 throw TextException("will never be able to anchor to an already marked entry", Here());
903 if (!entryStatus
.hasWriter
)
904 throw TextException("will never be able to anchor to an abandoned-by-writer entry", Here());
906 debugs(20, 7, "skipping not yet cached " << entry
);
907 entry
.setCollapsingRequirement(true);
912 Store::Controller::SmpAware()
914 return MemStore::Enabled() || Disks::SmpAware();
918 Store::Controller::checkTransients(const StoreEntry
&e
) const
920 if (EBIT_TEST(e
.flags
, ENTRY_SPECIAL
))
922 assert(!transients
|| e
.hasTransients());
928 static const auto root
= new Controller();