From 9487bae9c1514d66499a7015e44648dd9f231d73 Mon Sep 17 00:00:00 2001 From: Alex Rousskov Date: Mon, 11 Apr 2011 18:33:41 -0600 Subject: [PATCH] Added initial shared memory cache implementation (MemStore) and integrated it. Like Rock Store, shared memory cache keeps its own compact index of cached entries using extended Ipc::StoreMap class (MemStoreMap). Like Rock Store, the cache also struggles to keep its Root.get() results out of the store_table except during transit. There are several XXXs and TODOs that still need to be addressed for a more polished implementation. Eventually, the non-shared/local memory cache should also be implemented using a MemStore-like class, I think. This will allow to clearly isolate local from shared memory cache code. --- src/Makefile.am | 18 +-- src/MemObject.cc | 9 ++ src/MemObject.h | 3 + src/MemStore.cc | 278 +++++++++++++++++++++++++++++++++++++++++++++ src/MemStore.h | 52 +++++++++ src/MemStoreMap.cc | 47 ++++++++ src/MemStoreMap.h | 38 +++++++ src/Store.h | 10 +- src/SwapDir.h | 12 +- src/main.cc | 8 ++ src/store.cc | 78 ++++++++----- src/store_dir.cc | 71 +++++++++++- 12 files changed, 579 insertions(+), 45 deletions(-) create mode 100644 src/MemStore.cc create mode 100644 src/MemStore.h create mode 100644 src/MemStoreMap.cc create mode 100644 src/MemStoreMap.h diff --git a/src/Makefile.am b/src/Makefile.am index 89d76e77a4..7d8090fcfa 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -463,8 +463,8 @@ squid_SOURCES = \ Server.h \ structs.h \ swap_log_op.h \ - SwapDir.cc \ - SwapDir.h \ + SwapDir.cc MemStore.cc MemStoreMap.cc \ + SwapDir.h MemStore.h MemStoreMap.h \ time.cc \ tools.cc \ tunnel.cc \ @@ -1268,7 +1268,7 @@ tests_testCacheManager_SOURCES = \ $(TEST_CALL_SOURCES) \ tools.cc \ tunnel.cc \ - SwapDir.cc \ + SwapDir.cc MemStore.cc MemStoreMap.cc \ url.cc \ URLScheme.cc \ urn.cc \ @@ -1469,7 +1469,7 @@ tests_testEvent_SOURCES = \ $(TEST_CALL_SOURCES) \ tools.cc \ tunnel.cc \ - SwapDir.cc \ + SwapDir.cc MemStore.cc MemStoreMap.cc \ url.cc \ URLScheme.cc \ urn.cc \ @@ -1629,7 +1629,7 @@ tests_testEventLoop_SOURCES = \ $(TEST_CALL_SOURCES) \ tools.cc \ tunnel.cc \ - SwapDir.cc \ + SwapDir.cc MemStore.cc MemStoreMap.cc \ url.cc \ URLScheme.cc \ urn.cc \ @@ -1778,7 +1778,7 @@ tests_test_http_range_SOURCES = \ StoreMetaVary.cc \ StoreSwapLogData.cc \ String.cc \ - SwapDir.cc \ + SwapDir.cc MemStore.cc MemStoreMap.cc \ $(TEST_CALL_SOURCES) \ time.cc \ tools.cc \ @@ -1944,7 +1944,7 @@ tests_testHttpRequest_SOURCES = \ $(TEST_CALL_SOURCES) \ tools.cc \ tunnel.cc \ - SwapDir.cc \ + SwapDir.cc MemStore.cc MemStoreMap.cc \ url.cc \ URLScheme.cc \ urn.cc \ @@ -1997,7 +1997,7 @@ STORE_TEST_SOURCES=\ store_key_md5.cc \ Parsing.cc \ ConfigOption.cc \ - SwapDir.cc \ + SwapDir.cc MemStore.cc MemStoreMap.cc \ tests/stub_acl.cc tests/stub_cache_cf.cc \ tests/stub_helper.cc cbdata.cc String.cc \ tests/stub_comm.cc \ @@ -2391,7 +2391,7 @@ tests_testURL_SOURCES = \ $(TEST_CALL_SOURCES) \ tools.cc \ tunnel.cc \ - SwapDir.cc \ + SwapDir.cc MemStore.cc MemStoreMap.cc \ urn.cc \ wccp2.cc \ whois.cc \ diff --git a/src/MemObject.cc b/src/MemObject.cc index 884b77162e..d53834c71b 100644 --- a/src/MemObject.cc +++ b/src/MemObject.cc @@ -71,6 +71,15 @@ MemObject::inUseCount() return Pool().inUseCount(); } +void +MemObject::resetUrls(char const *aUrl, char const *aLog_url) +{ + safe_free(url); + safe_free(log_url); /* XXX account log_url */ + log_url = xstrdup(aLog_url); + url = xstrdup(aUrl); +} + MemObject::MemObject(char const *aUrl, char const *aLog_url) { debugs(20, 3, HERE << "new MemObject " << this); diff --git a/src/MemObject.h b/src/MemObject.h index a5051067b5..513e73668b 100644 --- a/src/MemObject.h +++ b/src/MemObject.h @@ -58,6 +58,9 @@ public: MemObject(char const *, char const *); ~MemObject(); + /// replaces construction-time URLs with correct ones; see hidden_mem_obj + void resetUrls(char const *aUrl, char const *aLog_url); + void write(StoreIOBuffer, STMCB *, void *); void unlinkRequest(); HttpReply const *getReply() const; diff --git a/src/MemStore.cc b/src/MemStore.cc new file mode 100644 index 0000000000..a82e1917a6 --- /dev/null +++ b/src/MemStore.cc @@ -0,0 +1,278 @@ +/* + * $Id$ + * + * DEBUG: section 20 Memory Cache + * + */ + +#include "config.h" +#include "ipc/mem/Page.h" +#include "ipc/mem/Pages.h" +#include "MemObject.h" +#include "MemStore.h" +#include "HttpReply.h" + + +// XXX: support storage using more than one page per entry + + +MemStore::MemStore(): map(NULL) +{ +} + +MemStore::~MemStore() +{ + delete map; +} + +void +MemStore::init() +{ + if (!map && Config.memMaxSize && (!UsingSmp() || IamWorkerProcess())) { + // TODO: warn if we cannot support the configured maximum entry size + const int64_t entrySize = Ipc::Mem::PageSize(); // for now + const int64_t entryCount = Config.memMaxSize / entrySize; + // TODO: warn if we cannot cache at least one item (misconfiguration) + if (entryCount > 0) + map = new MemStoreMap("cache_mem", entryCount); + } +} + +void +MemStore::stat(StoreEntry &output) const +{ + storeAppendPrintf(&output, "Memory Cache"); + // TODO: implement +} + +void +MemStore::maintain() +{ +} + +uint64_t +MemStore::minSize() const +{ + return 0; // XXX: irrelevant, but Store parent forces us to implement this +} + +uint64_t +MemStore::maxSize() const +{ + return 0; // XXX: make configurable +} + +void +MemStore::updateSize(int64_t eSize, int sign) +{ + // XXX: irrelevant, but Store parent forces us to implement this + fatal("MemStore::updateSize should not be called"); +} + +void +MemStore::reference(StoreEntry &) +{ +} + +void +MemStore::dereference(StoreEntry &) +{ +} + +int +MemStore::callback() +{ + return 0; +} + +StoreSearch * +MemStore::search(String const, HttpRequest *) +{ + fatal("not implemented"); + return NULL; +} + +StoreEntry * +MemStore::get(const cache_key *key) +{ + if (!map) + return NULL; + + // XXX: replace sfileno with a bigger word (sfileno is only for cache_dirs) + sfileno index; + const Ipc::StoreMapSlot *const slot = map->openForReading(key, index); + if (!slot) + return NULL; + + const Ipc::StoreMapSlot::Basics &basics = slot->basics; + const MemStoreMap::Extras &extras = map->extras(index); + + // create a brand new store entry and initialize it with stored info + StoreEntry *e = new StoreEntry(); + e->lock_count = 0; + + e->swap_file_sz = basics.swap_file_sz; + e->lastref = basics.lastref; + e->timestamp = basics.timestamp; + e->expires = basics.expires; + e->lastmod = basics.lastmod; + e->refcount = basics.refcount; + e->flags = basics.flags; + + e->store_status = STORE_OK; + e->mem_status = IN_MEMORY; // setMemStatus(IN_MEMORY) requires mem_obj + //e->swap_status = set in StoreEntry constructor to SWAPOUT_NONE; + e->ping_status = PING_NONE; + + EBIT_SET(e->flags, ENTRY_CACHABLE); + EBIT_CLR(e->flags, RELEASE_REQUEST); + EBIT_CLR(e->flags, KEY_PRIVATE); + EBIT_SET(e->flags, ENTRY_VALIDATED); + + const bool copied = copyFromShm(*e, extras); + + // we copied everything we could to local memory; no more need to lock + map->closeForReading(index); + + if (copied) { + e->hashInsert(key); + return e; + } + + debugs(20, 3, HERE << "mem-loading failed; freeing " << index); + map->free(index); // do not let others into the same trap + return NULL; +} + +void +MemStore::get(String const key, STOREGETCLIENT aCallback, void *aCallbackData) +{ + // XXX: not needed but Store parent forces us to implement this + fatal("MemStore::get(key,callback,data) should not be called"); +} + +bool +MemStore::copyFromShm(StoreEntry &e, const MemStoreMap::Extras &extras) +{ + const Ipc::Mem::PageId &page = extras.page; + + StoreIOBuffer sourceBuf(extras.storedSize, 0, + static_cast(PagePointer(page))); + + // XXX: We do not know the URLs yet, only the key, but we need to parse and + // store the response for the Root().get() callers to be happy because they + // expect IN_MEMORY entries to already have the response headers and body. + // At least one caller calls createMemObject() if there is not one, so + // we hide the true object until that happens (to avoid leaking TBD URLs). + e.createMemObject("TBD", "TBD"); + + // emulate the usual Store code but w/o inapplicable checks and callbacks: + + // from store_client::readBody(): + HttpReply *rep = (HttpReply *)e.getReply(); + const ssize_t end = headersEnd(sourceBuf.data, sourceBuf.length); + if (!rep->parseCharBuf(sourceBuf.data, end)) { + debugs(20, DBG_IMPORTANT, "Could not parse mem-cached headers: " << e); + return false; + } + // local memory stores both headers and body + e.mem_obj->object_sz = sourceBuf.length; // from StoreEntry::complete() + + storeGetMemSpace(sourceBuf.length); // from StoreEntry::write() + + assert(e.mem_obj->data_hdr.write(sourceBuf)); // from MemObject::write() + const int64_t written = e.mem_obj->endOffset(); + assert(written == sourceBuf.length); // StoreEntry::write never fails? + // would be nice to call validLength() here, but it needs e.key + + debugs(20, 7, HERE << "mem-loaded all " << written << " bytes of " << e << + " from " << page); + + e.hideMemObject(); + + return true; +} + +void +MemStore::considerKeeping(StoreEntry &e) +{ + if (!e.memoryCachable()) { + debugs(20, 7, HERE << "Not memory cachable: " << e); + return; // cannot keep due to entry state or properties + } + + assert(e.mem_obj); + if (!willFit(e.mem_obj->endOffset())) { + debugs(20, 5, HERE << "No mem-cache space for " << e); + return; // failed to free enough space + } + + keep(e); // may still fail +} + +bool +MemStore::willFit(int64_t need) +{ + // TODO: obey configured maximum entry size (with page-based rounding) + return need <= Ipc::Mem::PageSize(); +} + +/// allocates map slot and calls copyToShm to store the entry in shared memory +void +MemStore::keep(StoreEntry &e) +{ + if (!map) { + debugs(20, 5, HERE << "No map to mem-cache " << e); + return; + } + + sfileno index = 0; + Ipc::StoreMapSlot *slot = map->openForWriting(reinterpret_cast(e.key), index); + if (!slot) { + debugs(20, 5, HERE << "No room in mem-cache map to index " << e); + return; + } + + MemStoreMap::Extras &extras = map->extras(index); + if (copyToShm(e, extras)) { + slot->set(e); + map->closeForWriting(index, false); + } else { + map->abortIo(index); + } +} + +/// uses mem_hdr::copy() to copy local data to shared memory +bool +MemStore::copyToShm(StoreEntry &e, MemStoreMap::Extras &extras) +{ + Ipc::Mem::PageId page; + if (!Ipc::Mem::GetPage(page)) { + debugs(20, 5, HERE << "No mem-cache page for " << e); + return false; // GetPage is responsible for any cleanup on failures + } + + const int64_t bufSize = Ipc::Mem::PageSize(); + const int64_t eSize = e.mem_obj->endOffset(); + + StoreIOBuffer sharedSpace(bufSize, 0, + static_cast(PagePointer(page))); + + // check that we kept everything or purge incomplete/sparse cached entry + const ssize_t copied = e.mem_obj->data_hdr.copy(sharedSpace); + if (eSize != copied) { + debugs(20, 2, HERE << "Failed to mem-cache " << e << ": " << + eSize << "!=" << copied); + // cleanup + PutPage(page); + return false; + } + + debugs(20, 7, HERE << "mem-cached all " << eSize << " bytes of " << e << + " in " << page); + + // remember storage location and size + extras.page = page; + extras.storedSize = copied; + return true; +} diff --git a/src/MemStore.h b/src/MemStore.h new file mode 100644 index 0000000000..bb4320bba6 --- /dev/null +++ b/src/MemStore.h @@ -0,0 +1,52 @@ +/* + * $Id$ + */ + +#ifndef SQUID_MEMSTORE_H +#define SQUID_MEMSTORE_H + +#include "Store.h" +#include "MemStoreMap.h" + +/// Stores HTTP entities in RAM. Current implementation uses shared memory. +/// Unlike a disk store (SwapDir), operations are synchronous (and fast). +class MemStore: public Store { +public: + MemStore(); + virtual ~MemStore(); + + /// cache the entry or forget about it until the next considerKeeping call + void considerKeeping(StoreEntry &e); + + /* Store API */ + virtual int callback(); + virtual StoreEntry * get(const cache_key *); + virtual void get(String const key , STOREGETCLIENT callback, void *cbdata); + virtual void init(); + virtual uint64_t maxSize() const; + virtual uint64_t minSize() const; + virtual void stat(StoreEntry &) const; + virtual StoreSearch *search(String const url, HttpRequest *); + virtual void reference(StoreEntry &); + virtual void dereference(StoreEntry &); + virtual void maintain(); + virtual void updateSize(int64_t size, int sign); + +protected: + bool willFit(int64_t needed); + void keep(StoreEntry &e); + + bool copyToShm(StoreEntry &e, MemStoreMap::Extras &extras); + bool copyFromShm(StoreEntry &e, const MemStoreMap::Extras &extras); + +private: + MemStoreMap *map; ///< index of mem-cached entries +}; + +// Why use Store as a base? MemStore and SwapDir are both "caches". + +// Why not just use a SwapDir API? That would not help much because Store has +// to check/update memory cache separately from the disk cache. And same API +// would hurt because we can support synchronous get/put, unlike the disks. + +#endif /* SQUID_MEMSTORE_H */ diff --git a/src/MemStoreMap.cc b/src/MemStoreMap.cc new file mode 100644 index 0000000000..30e65bb6d4 --- /dev/null +++ b/src/MemStoreMap.cc @@ -0,0 +1,47 @@ +/* + * $Id$ + * + * DEBUG: section 20 Memory Cache + */ + +#include "config.h" + +#include "Store.h" +#include "MemStoreMap.h" + +MemStoreMap::MemStoreMap(const char *const aPath, const int limit): + Ipc::StoreMap(aPath, limit, Shared::MemSize(limit)) +{ + assert(shm.mem()); + shared = new (shm.reserve(Shared::MemSize(limit))) Shared; +} + +MemStoreMap::MemStoreMap(const char *const aPath): + Ipc::StoreMap(aPath) +{ + const int limit = entryLimit(); + assert(shm.mem()); + shared = reinterpret_cast(shm.reserve(Shared::MemSize(limit))); +} + +MemStoreMap::Extras & +MemStoreMap::extras(const sfileno fileno) +{ + assert(0 <= fileno && fileno < entryLimit()); + assert(shared); + return shared->extras[fileno]; +} + +const MemStoreMap::Extras & +MemStoreMap::extras(const sfileno fileno) const +{ + assert(0 <= fileno && fileno < entryLimit()); + assert(shared); + return shared->extras[fileno]; +} + +size_t +MemStoreMap::Shared::MemSize(int limit) +{ + return sizeof(Shared) + limit*sizeof(Extras); +} diff --git a/src/MemStoreMap.h b/src/MemStoreMap.h new file mode 100644 index 0000000000..e9379cdb36 --- /dev/null +++ b/src/MemStoreMap.h @@ -0,0 +1,38 @@ +#ifndef SQUID_MEMSTOREMAP_H +#define SQUID_MEMSTOREMAP_H + +#include "ipc/StoreMap.h" +#include "ipc/mem/Page.h" + +/// map of MemStore-cached entries +class MemStoreMap: public Ipc::StoreMap +{ +public: + // StoreEntry restoration info not already stored by Ipc::StoreMap + class Extras { + public: + Ipc::Mem::PageId page; ///< shared memory page with the entry content + int64_t storedSize; ///< total size of the stored entry content + }; + +public: + MemStoreMap(const char *const aPath, const int limit); ///< create a new shared StoreMap + MemStoreMap(const char *const aPath); ///< open an existing shared StoreMap + + /// write access to the extras; call openForWriting() first! + Extras &extras(const sfileno fileno); + /// read-only access to the extras; call openForReading() first! + const Extras &extras(const sfileno fileno) const; + +private: + /// data shared by all MemStoreMaps with the same path + class Shared { + public: + static size_t MemSize(int limit); + Extras extras[]; ///< extras storage + }; + + Shared *shared; ///< pointer to shared memory +}; + +#endif /* SQUID_MEMSTOREMAP_H */ diff --git a/src/Store.h b/src/Store.h index be5874e679..2b2cc54d16 100644 --- a/src/Store.h +++ b/src/Store.h @@ -110,6 +110,7 @@ public: void cacheNegatively(); /** \todo argh, why both? */ void invokeHandlers(); void purgeMem(); + void cacheInMemory(); ///< start or continue storing in memory cache void swapOut(); bool swapOutAble() const; void swapOutFileClose(int how); @@ -118,8 +119,9 @@ public: int checkNegativeHit() const; int locked() const; int validToSend() const; - int keepInMemory() const; + bool memoryCachable() const; ///< may be cached in memory void createMemObject(const char *, const char *); + void hideMemObject(); ///< no mem_obj for callers until createMemObject void dump(int debug_lvl) const; void hashDelete(); void hashInsert(const cache_key *); @@ -144,6 +146,7 @@ public: virtual RefCount store() const; MemObject *mem_obj; + MemObject *hidden_mem_obj; ///< mem_obj created before URLs were known RemovalPolicyNode repl; /* START OF ON-DISK STORE_META_STD TLV field */ time_t timestamp; @@ -319,6 +322,11 @@ public: virtual void maintain() = 0; /* perform regular maintenance should be private and self registered ... */ + // XXX: This method belongs to Store::Root/StoreController, but it is here + // because test cases use non-StoreController derivatives as Root + /// called when the entry is no longer needed by any transaction + virtual void handleIdleEntry(StoreEntry &e) {} + /* These should really be private */ virtual void updateSize(int64_t size, int sign) = 0; diff --git a/src/SwapDir.h b/src/SwapDir.h index aedefe4772..2c11895724 100644 --- a/src/SwapDir.h +++ b/src/SwapDir.h @@ -37,14 +37,14 @@ /* forward decls */ class RemovalPolicy; +class MemStore; /* Store dir configuration routines */ /* SwapDir *sd, char *path ( + char *opt later when the strtok mess is gone) */ class ConfigOption; -/* New class that replaces the static SwapDir methods as part of the Store overhaul */ - +/// hides memory/disk cache distinction from callers class StoreController : public Store { @@ -58,6 +58,9 @@ public: virtual void get(String const, STOREGETCLIENT, void * cbdata); + /* Store parent API */ + virtual void handleIdleEntry(StoreEntry &e); + virtual void init(); virtual void maintain(); /* perform regular maintenance should be private and self registered ... */ @@ -84,7 +87,8 @@ public: private: void createOneStore(Store &aStore); - StorePointer swapDir; + StorePointer swapDir; ///< summary view of all disk caches + MemStore *memStore; ///< memory cache }; /* migrating from the Config based list of swapdirs */ @@ -108,7 +112,7 @@ SQUIDCEXTERN void storeDirLRUAdd(StoreEntry *); SQUIDCEXTERN int storeDirGetBlkSize(const char *path, int *blksize); SQUIDCEXTERN int storeDirGetUFSStats(const char *, int *, int *, int *, int *); - +/// manages a single cache_dir class SwapDir : public Store { diff --git a/src/main.cc b/src/main.cc index 1aaebde76c..de345a1790 100644 --- a/src/main.cc +++ b/src/main.cc @@ -64,6 +64,7 @@ #include "StoreFileSystem.h" #include "DiskIO/DiskIOModule.h" #include "ipc/Kids.h" +#include "ipc/mem/Pages.h" #include "ipc/Coordinator.h" #include "ipc/Strand.h" #include "ip/tools.h" @@ -1411,6 +1412,13 @@ SquidMain(int argc, char **argv) /* NOTREACHED */ } + // XXX: this logic should probably be moved into Ipc::Mem::Init() + if (IamMasterProcess()) + Ipc::Mem::Init(); + else + if (UsingSmp() && IamWorkerProcess()) + Ipc::Mem::Attach(); + if (!opt_no_daemon && Config.workers > 0) watch_child(argv); diff --git a/src/store.cc b/src/store.cc index 65c7953355..0ac43b8a34 100644 --- a/src/store.cc +++ b/src/store.cc @@ -314,6 +314,13 @@ StoreEntry::storeClientType() const * offset 0 in the memory object is the HTTP headers. */ + if (/* XXX: restore: UsingSmp() && */ mem_status == IN_MEMORY) { + // clients of an object cached in shared memory are memory clients + return STORE_MEM_CLIENT; + } + + assert(mem_obj); + if (mem_obj->inmem_lo) return STORE_DISK_CLIENT; @@ -365,6 +372,7 @@ StoreEntry::storeClientType() const } StoreEntry::StoreEntry(): + hidden_mem_obj(NULL), swap_file_sz(0) { debugs(20, 3, HERE << "new StoreEntry " << this); @@ -378,6 +386,7 @@ StoreEntry::StoreEntry(): } StoreEntry::StoreEntry(const char *aUrl, const char *aLogUrl): + hidden_mem_obj(NULL), swap_file_sz(0) { debugs(20, 3, HERE << "new StoreEntry " << this); @@ -396,6 +405,7 @@ StoreEntry::~StoreEntry() SwapDir &sd = dynamic_cast(*store()); sd.disconnect(*this); } + delete hidden_mem_obj; } void @@ -406,6 +416,18 @@ StoreEntry::destroyMemObject() MemObject *mem = mem_obj; mem_obj = NULL; delete mem; + delete hidden_mem_obj; + hidden_mem_obj = NULL; +} + +void +StoreEntry::hideMemObject() +{ + debugs(20, 3, HERE << "hiding " << mem_obj); + assert(mem_obj); + assert(!hidden_mem_obj); + hidden_mem_obj = mem_obj; + mem_obj = NULL; } void @@ -532,29 +554,10 @@ StoreEntry::unlock() return 0; } - // XXX: Rock store specific: since each SwapDir controls the index, - // unlocked entries should not stay in the global store_table - if (fileno >= 0) { - Store::Root().dereference(*this); - debugs(20, 5, HERE << "destroying unlocked entry: " << this << ' ' << *this); - setMemStatus(NOT_IN_MEMORY); - destroyStoreEntry(static_cast(this)); - return 0; - } - else if (keepInMemory()) { - Store::Root().dereference(*this); - setMemStatus(IN_MEMORY); - mem_obj->unlinkRequest(); - } else { - Store::Root().dereference(*this); - - if (EBIT_TEST(flags, KEY_PRIVATE)) - debugs(20, 1, "WARNING: " << __FILE__ << ":" << __LINE__ << ": found KEY_PRIVATE"); - - /* StoreEntry::purgeMem may free e */ - purgeMem(); - } + if (EBIT_TEST(flags, KEY_PRIVATE)) + debugs(20, 1, "WARNING: " << __FILE__ << ":" << __LINE__ << ": found KEY_PRIVATE"); + Store::Root().handleIdleEntry(*this); // may delete us return 0; } @@ -722,6 +725,8 @@ StoreEntry::setPublicKey() } } + // TODO: storeGetPublic() calls below may create unlocked entries. + // We should add/use storeHas() API or lock/unlock those entries. if (mem_obj->vary_headers && !storeGetPublic(mem_obj->url, mem_obj->method)) { /* Create "vary" base object */ String vary; @@ -1428,8 +1433,8 @@ storeConfigure(void) store_pages_max = Config.memMaxSize / sizeof(mem_node); } -int -StoreEntry::keepInMemory() const +bool +StoreEntry::memoryCachable() const { if (mem_obj == NULL) return 0; @@ -1443,8 +1448,9 @@ StoreEntry::keepInMemory() const if (!Config.onoff.memory_cache_first && swap_status == SWAPOUT_DONE && refcount == 1) return 0; - // already kept more than allowed - if (mem_node::InUseCount() > store_pages_max) + const int64_t expectedSize = mem_obj->expectedReplySize(); + // objects of unknown size are not allowed into the memory cache, for now + if (expectedSize < 0 || expectedSize > Config.Store.maxInMemObjSize) return 0; return 1; @@ -1612,6 +1618,16 @@ StoreEntry::setMemStatus(mem_status_t new_status) if (new_status == mem_status) return; + // XXX: restore: if (UsingSmp()) + { + assert(new_status != IN_MEMORY); // we do not call this otherwise + // This method was designed to update replacement policy, not to + // actually purge something from the memory cache (TODO: rename?). + // Shared memory cache does not have a policy that needs updates. + mem_status = new_status; + return; + } + assert(mem_obj != NULL); if (new_status == IN_MEMORY) { @@ -1624,7 +1640,7 @@ StoreEntry::setMemStatus(mem_status_t new_status) debugs(20, 4, "StoreEntry::setMemStatus: inserted mem node " << mem_obj->url << " key: " << getMD5Text()); } - hot_obj_count++; + hot_obj_count++; // TODO: maintain for the shared hot cache as well } else { if (EBIT_TEST(flags, ENTRY_SPECIAL)) { debugs(20, 4, "StoreEntry::setMemStatus: special entry " << mem_obj->url); @@ -1656,6 +1672,14 @@ StoreEntry::createMemObject(const char *aUrl, const char *aLogUrl) if (mem_obj) return; + if (hidden_mem_obj) { + debugs(20, 3, HERE << "restoring " << hidden_mem_obj); + mem_obj = hidden_mem_obj; + hidden_mem_obj = NULL; + mem_obj->resetUrls(aUrl, aLogUrl); + return; + } + mem_obj = new MemObject(aUrl, aLogUrl); } diff --git a/src/store_dir.cc b/src/store_dir.cc index 67873f0239..3f83a93834 100644 --- a/src/store_dir.cc +++ b/src/store_dir.cc @@ -36,6 +36,8 @@ #include "squid.h" #include "Store.h" #include "MemObject.h" +#include "MemStore.h" +#include "mem_node.h" #include "SquidMath.h" #include "SquidTime.h" #include "SwapDir.h" @@ -73,10 +75,13 @@ static STDIRSELECT storeDirSelectSwapDirLeastLoad; int StoreController::store_dirs_rebuilding = 1; StoreController::StoreController() : swapDir (new StoreHashIndex()) + , memStore(NULL) {} StoreController::~StoreController() -{} +{ + delete memStore; +} /* * This function pointer is set according to 'store_dir_select_algorithm' @@ -87,6 +92,10 @@ STDIRSELECT *storeDirSelectSwapDir = storeDirSelectSwapDirLeastLoad; void StoreController::init() { + // XXX: add: if (UsingSmp()) + memStore = new MemStore; + memStore->init(); + swapDir->init(); if (0 == strcasecmp(Config.store_dir_select_algorithm, "round-robin")) { @@ -336,6 +345,9 @@ SwapDir::updateSize(int64_t size, int sign) void StoreController::stat(StoreEntry &output) const { + if (memStore) + memStore->stat(output); + storeAppendPrintf(&output, "Store Directory Statistics:\n"); storeAppendPrintf(&output, "Store Entries : %lu\n", (unsigned long int)StoreEntry::inUseCount()); @@ -509,7 +521,8 @@ StoreHashIndex::dir(const int i) const void StoreController::sync(void) { - /* sync mem cache? */ + if (memStore) + memStore->sync(); swapDir->sync(); } @@ -657,7 +670,11 @@ StoreController::reference(StoreEntry &e) if (e.swap_dirn > -1) e.store()->reference(e); - /* Notify the memory cache that we're referencing this object again */ + // Notify the memory cache that we're referencing this object again + if (memStore && e.mem_status == IN_MEMORY) + memStore->reference(e); + + // TODO: move this code to a non-shared memory cache class when we have it if (e.mem_obj) { if (mem_policy->Referenced) mem_policy->Referenced(mem_policy, &e, &e.mem_obj->repl); @@ -672,7 +689,11 @@ StoreController::dereference(StoreEntry & e) if (e.swap_filen > -1) e.store()->dereference(e); - /* Notify the memory cache that we're not referencing this object any more */ + // Notify the memory cache that we're not referencing this object any more + if (memStore && e.mem_status == IN_MEMORY) + memStore->dereference(e); + + // TODO: move this code to a non-shared memory cache class when we have it if (e.mem_obj) { if (mem_policy->Dereferenced) mem_policy->Dereferenced(mem_policy, &e, &e.mem_obj->repl); @@ -683,10 +704,20 @@ StoreEntry * StoreController::get(const cache_key *key) { if (StoreEntry *e = swapDir->get(key)) { + // TODO: ignore and maybe handleIdleEntry() unlocked intransit entries + // because their backing store slot may be gone already. debugs(20, 3, HERE << "got in-transit entry: " << *e); return e; } + if (memStore) { + if (StoreEntry *e = memStore->get(key)) { + debugs(20, 3, HERE << "got mem-cached entry: " << *e); + return e; + } + } + + // TODO: this disk iteration is misplaced; move to StoreHashIndex if (const int cacheDirs = Config.cacheSwap.n_configured) { // ask each cache_dir until the entry is found; use static starting // point to avoid asking the same subset of disks more often @@ -717,6 +748,38 @@ StoreController::get(String const key, STOREGETCLIENT aCallback, void *aCallback fatal("not implemented"); } +void +StoreController::handleIdleEntry(StoreEntry &e) +{ + bool keepInLocalMemory = false; + if (memStore) { + memStore->considerKeeping(e); + // leave keepInLocalMemory false; memStore maintains its own cache + } else { + keepInLocalMemory = e.memoryCachable() && // entry is in good shape and + // the local memory cache is not overflowing + (mem_node::InUseCount() <= store_pages_max); + } + + dereference(e); + + // XXX: Rock store specific: Since each SwapDir controls its index, + // unlocked entries should not stay in the global store_table. + if (fileno >= 0) { + debugs(20, 5, HERE << "destroying unlocked entry: " << &e << ' ' << e); + destroyStoreEntry(static_cast(&e)); + return; + } + + // TODO: move this into [non-shared] memory cache class when we have one + if (keepInLocalMemory) { + e.setMemStatus(IN_MEMORY); + e.mem_obj->unlinkRequest(); + } else { + e.purgeMem(); // may free e + } +} + StoreHashIndex::StoreHashIndex() { if (store_table) -- 2.47.2