crashes. Improve related code.
Shared memory caching code was not checking whether the response is generally
cachable and, hence, tried to store responses that Squid already refused to
cache (e.g., release-requested entries). The shared memory cache should also
check that the response is contiguous because the disk store may not check
that (e.g., when disk caching id disabled).
The problem was exacerbated by the broken entry dump code accompanying the
FATAL message. The Splay tree iterator is evidently not iterating a portion of
the tree. I added a visitor pattern code to work around that, but did not try
to fix the Splay iterator iterator itself because, in part, the whole iterator
design felt inappropriate (storing and flattening the tree before iterating
it?) for its intended purpose. I could not check quickly enough where the
broken iterator is used besides mem_hdr::debugDump() so more bugs like this
are possible.
Optimized StoreEntry::checkCachable() a little and removed wrong TODO comment:
Disk-only mayStartSwapOut() should not accumulate "general" cachability checks
which are used by RAM caches as well.
Added more mem_hdr debugging and polished method descriptions.
Fixed NullStoreEntry::mayStartSwapout() spelling/case. It should override
StoreEntry::mayStartSwapOut().
SplayNode<V> * insert(Value data, SPLAYCMP * compare);
template <class FindValue> SplayNode<V> * splay(const FindValue &data, int( * compare)(FindValue const &a, Value const &b)) const;
+
+ /// recursively visit left nodes, this node, and then right nodes
+ template <class Visitor> void visit(Visitor &v) const;
};
typedef SplayNode<void *> splayNode;
const_iterator end() const;
+ /// recursively visit all nodes, in left-to-right order
+ template <class Visitor> void visit(Visitor &v) const;
+
size_t elements;
};
return top;
}
+template <class V>
+template <class Visitor>
+void
+SplayNode<V>::visit(Visitor &visitor) const {
+ if (left)
+ left->visit(visitor);
+ visitor(data);
+ if (right)
+ right->visit(visitor);
+}
+
+template <class V>
+template <class Visitor>
+void
+Splay<V>::visit(Visitor &visitor) const {
+ if (head)
+ head->visit(visitor);
+}
+
template <class V>
template <class FindValue>
typename Splay<V>::Value const *
return const_iterator(NULL);
}
+// XXX: This does not seem to iterate the whole thing in some cases.
template <class V>
class SplayConstIterator
{
/// whether we should cache the entry
bool
-MemStore::shouldCache(const StoreEntry &e) const
+MemStore::shouldCache(StoreEntry &e) const
{
if (e.mem_status == IN_MEMORY) {
debugs(20, 5, "already loaded from mem-cache: " << e);
return false; // will not cache due to cachable entry size limits
}
+ if (!e.mem_obj->isContiguous()) {
+ debugs(20, 5, "not contiguous");
+ return false;
+ }
+
if (!map) {
debugs(20, 5, HERE << "No map to mem-cache " << e);
return false;
static int64_t EntryLimit();
protected:
- bool shouldCache(const StoreEntry &e) const;
+ bool shouldCache(StoreEntry &e) const;
bool startCaching(StoreEntry &e);
void copyToShm(StoreEntry &e);
bool swappingOut() const { return swap_status == SWAPOUT_WRITING; }
void swapOutFileClose(int how);
const char *url() const;
+ /// generally cachable (checks agnostic to disk/RAM-specific requirements)
+ /// common part of mayStartSwapOut() and memoryCachable()
+ /// TODO: Make private so only those two methods can call this.
int checkCachable();
int checkNegativeHit() const;
int locked() const;
int validToSend() const;
- bool memoryCachable() const; ///< may be cached in memory
+ bool memoryCachable(); ///< checkCachable() and can be cached in memory
/// if needed, initialize mem_obj member w/o URI-related information
MemObject *makeMemObject();
store_client_t storeClientType() const {return STORE_MEM_CLIENT;}
char const *getSerialisedMetaData();
- bool mayStartSwapout() {return false;}
+ virtual bool mayStartSwapOut() { return false; }
void trimMemory(const bool preserveSwappable) {}
private:
void createOneStore(Store &aStore);
StoreEntry *find(const cache_key *key);
- bool keepForLocalMemoryCache(const StoreEntry &e) const;
+ bool keepForLocalMemoryCache(StoreEntry &e) const;
bool anchorCollapsed(StoreEntry &collapsed, bool &inSync);
bool anchorCollapsedOnDisk(StoreEntry &collapsed, bool &inSync);
return false;
}
+ debugs(19, 8, this << " removing " << aNode);
nodes.remove (aNode, NodeCompare);
delete aNode;
return true;
int64_t
mem_hdr::freeDataUpto(int64_t target_offset)
{
+ debugs(19, 8, this << " up to " << target_offset);
/* keep the last one to avoid change to other part of code */
SplayNode<mem_node*> const * theStart;
debugs (19, 0, "mem_hdr::debugDump: lowest offset: " << lowestOffset() << " highest offset + 1: " << endOffset() << ".");
std::ostringstream result;
PointerPrinter<mem_node *> foo(result, " - ");
- for_each (getNodes().begin(), getNodes().end(), foo);
+ getNodes().visit(foo);
debugs (19, 0, "mem_hdr::debugDump: Current available data is: " << result.str() << ".");
}
return 0;
}
-// TODO: remove checks already performed by swapoutPossible()
// TODO: move "too many open..." checks outside -- we are called too early/late
int
StoreEntry::checkCachable()
{
+ // XXX: This method is used for both memory and disk caches, but some
+ // checks are specific to disk caches. Move them to mayStartSwapOut().
+
+ // XXX: This method may be called several times, sometimes with different
+ // outcomes, making store_check_cachable_hist counters misleading.
+
+ // check this first to optimize handling of repeated calls for uncachables
+ if (EBIT_TEST(flags, RELEASE_REQUEST)) {
+ debugs(20, 2, "StoreEntry::checkCachable: NO: not cachable");
+ ++store_check_cachable_hist.no.not_entry_cachable; // TODO: rename?
+ return 0; // avoid rerequesting release below
+ }
+
#if CACHE_ALL_METHODS
if (mem_obj->method != Http::METHOD_GET) {
if (store_status == STORE_OK && EBIT_TEST(flags, ENTRY_BAD_LENGTH)) {
debugs(20, 2, "StoreEntry::checkCachable: NO: wrong content-length");
++store_check_cachable_hist.no.wrong_content_length;
- } else if (EBIT_TEST(flags, RELEASE_REQUEST)) {
- debugs(20, 2, "StoreEntry::checkCachable: NO: not cachable");
- ++store_check_cachable_hist.no.not_entry_cachable; // TODO: rename?
} else if (EBIT_TEST(flags, ENTRY_NEGCACHED)) {
debugs(20, 3, "StoreEntry::checkCachable: NO: negative cached");
++store_check_cachable_hist.no.negative_cached;
}
bool
-StoreEntry::memoryCachable() const
+StoreEntry::memoryCachable()
{
+ if (!checkCachable())
+ return 0;
+
if (mem_obj == NULL)
return 0;
// move this into [non-shared] memory cache class when we have one
/// whether e should be kept in local RAM for possible future caching
bool
-StoreController::keepForLocalMemoryCache(const StoreEntry &e) const
+StoreController::keepForLocalMemoryCache(StoreEntry &e) const
{
if (!e.memoryCachable())
return false;
int StoreEntry::checkNegativeHit() const STUB_RETVAL(0)
int StoreEntry::locked() const STUB_RETVAL(0)
int StoreEntry::validToSend() const STUB_RETVAL(0)
-bool StoreEntry::memoryCachable() const STUB_RETVAL(false)
+bool StoreEntry::memoryCachable() STUB_RETVAL(false)
MemObject *StoreEntry::makeMemObject() STUB_RETVAL(NULL)
void StoreEntry::createMemObject(const char *, const char *, const HttpRequestMethod &aMethod) STUB
void StoreEntry::dump(int debug_lvl) const STUB