-
/*
- * DEBUG: section 20 Storage Manager
- * AUTHOR: Harvest Derived
- *
- * SQUID Web Proxy Cache http://www.squid-cache.org/
- * ----------------------------------------------------------
- *
- * Squid is the result of efforts by numerous individuals from
- * the Internet community; see the CONTRIBUTORS file for full
- * details. Many organizations have provided support for Squid's
- * development; see the SPONSORS file for full details. Squid is
- * Copyrighted (C) 2001 by the Regents of the University of
- * California; see the COPYRIGHT file for full details. Squid
- * incorporates software developed and/or copyrighted by other
- * sources; see the CREDITS file for full details.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
*
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
*/
+/* DEBUG: section 20 Storage Manager */
+
#include "squid.h"
#include "CacheDigest.h"
#include "CacheManager.h"
#include "StatCounters.h"
#include "stmem.h"
#include "Store.h"
+#include "store/Controller.h"
+#include "store/Disk.h"
+#include "store/Disks.h"
#include "store_digest.h"
#include "store_key_md5.h"
-#include "store_key_md5.h"
#include "store_log.h"
#include "store_rebuild.h"
#include "StoreClient.h"
#include "StoreMeta.h"
#include "StrList.h"
#include "swap_log_op.h"
-#include "SwapDir.h"
#include "tools.h"
#if USE_DELAY_POOLS
#include "DelayPools.h"
#endif
+/** StoreEntry uses explicit new/delete operators, which set pool chunk size to 2MB
+ * XXX: convert to MEMPROXY_CLASS() API
+ */
+#include "mem/Pool.h"
+
#include <climits>
#include <stack>
static std::stack<StoreEntry*> LateReleaseStack;
MemAllocator *StoreEntry::pool = NULL;
-StorePointer Store::CurrentRoot = NULL;
-
-void
-Store::Root(Store * aRoot)
-{
- CurrentRoot = aRoot;
-}
-
-void
-Store::Root(StorePointer aRoot)
-{
- Root(aRoot.getRaw());
-}
-
void
Store::Stats(StoreEntry * output)
{
- assert (output);
+ assert(output);
Root().stat(*output);
}
-void
-Store::create()
-{}
-
-void
-Store::diskFull()
-{}
-
-void
-Store::sync()
-{}
-
-void
-Store::unlink (StoreEntry &anEntry)
-{
- fatal("Store::unlink on invalid Store\n");
-}
-
+// XXX: new/delete operators need to be replaced with MEMPROXY_CLASS
+// definitions but doing so exposes bug 4370, and maybe 4354 and 4355
void *
StoreEntry::operator new (size_t bytecount)
{
- assert (bytecount == sizeof (StoreEntry));
+ assert(bytecount == sizeof (StoreEntry));
if (!pool) {
pool = memPoolCreate ("StoreEntry", bytecount);
- pool->setChunkSize(2048 * 1024);
}
return pool->alloc();
}
void
-StoreEntry::makePublic()
+StoreEntry::makePublic(const KeyScope scope)
{
/* This object can be cached for a long time */
-
if (!EBIT_TEST(flags, RELEASE_REQUEST))
- setPublicKey();
+ setPublicKey(scope);
}
void
* ->deferRead (fd, buf, len, callback, DelayAwareRead, this)
*/
- if (amountToRead == 0) {
+ if (amountToRead <= 0) {
assert (mem_obj);
- /* read ahead limit */
- /* Perhaps these two calls should both live in MemObject */
-#if USE_DELAY_POOLS
- if (!mem_obj->readAheadPolicyCanRead()) {
-#endif
- mem_obj->delayRead(DeferredRead(DeferReader, this, CommRead(conn, buf, len, callback)));
- return;
-#if USE_DELAY_POOLS
- }
-
- /* delay id limit */
- mem_obj->mostBytesAllowed().delayRead(DeferredRead(DeferReader, this, CommRead(conn, buf, len, callback)));
+ mem_obj->delayRead(DeferredRead(DeferReader, this, CommRead(conn, buf, len, callback)));
return;
-
-#endif
-
}
if (fd_table[conn->fd].closing()) {
}
bool
-StoreEntry::checkDeferRead(int fd) const
+StoreEntry::checkDeferRead(int) const
{
return (bytesWanted(Range<size_t>(0,INT_MAX)) == 0);
}
void
-StoreEntry::setNoDelay (bool const newValue)
+StoreEntry::setNoDelay(bool const newValue)
{
if (mem_obj)
mem_obj->setNoDelay(newValue);
}
StoreEntry::StoreEntry() :
- mem_obj(NULL),
- timestamp(-1),
- lastref(-1),
- expires(-1),
- lastmod(-1),
- swap_file_sz(0),
- refcount(0),
- flags(0),
- swap_filen(-1),
- swap_dirn(-1),
- mem_status(NOT_IN_MEMORY),
- ping_status(PING_NONE),
- store_status(STORE_PENDING),
- swap_status(SWAPOUT_NONE),
- lock_count(0)
+ mem_obj(NULL),
+ timestamp(-1),
+ lastref(-1),
+ expires(-1),
+ lastModified_(-1),
+ swap_file_sz(0),
+ refcount(0),
+ flags(0),
+ swap_filen(-1),
+ swap_dirn(-1),
+ mem_status(NOT_IN_MEMORY),
+ ping_status(PING_NONE),
+ store_status(STORE_PENDING),
+ swap_status(SWAPOUT_NONE),
+ lock_count(0)
{
debugs(20, 5, "StoreEntry constructed, this=" << this);
}
return;
// Store::Root() is FATALly missing during shutdown
- if (e->swap_filen >= 0 && !shutting_down) {
- SwapDir &sd = dynamic_cast<SwapDir&>(*e->store());
- sd.disconnect(*e);
- }
+ if (e->swap_filen >= 0 && !shutting_down)
+ e->disk().disconnect(*e);
e->destroyMemObject();
StoreEntry::touch()
{
lastref = squid_curtime;
- Store::Root().reference(*this);
}
void
}
StoreEntry *
-storeGetPublicByRequestMethod(HttpRequest * req, const HttpRequestMethod& method)
+storeGetPublicByRequestMethod(HttpRequest * req, const HttpRequestMethod& method, const KeyScope keyScope)
{
- return Store::Root().get(storeKeyPublicByRequestMethod(req, method));
+ return Store::Root().get(storeKeyPublicByRequestMethod(req, method, keyScope));
}
StoreEntry *
-storeGetPublicByRequest(HttpRequest * req)
+storeGetPublicByRequest(HttpRequest * req, const KeyScope keyScope)
{
- StoreEntry *e = storeGetPublicByRequestMethod(req, req->method);
+ StoreEntry *e = storeGetPublicByRequestMethod(req, req->method, keyScope);
if (e == NULL && req->method == Http::METHOD_HEAD)
/* We can generate a HEAD reply from a cached GET object */
- e = storeGetPublicByRequestMethod(req, Http::METHOD_GET);
+ e = storeGetPublicByRequestMethod(req, Http::METHOD_GET, keyScope);
return e;
}
void
StoreEntry::setPrivateKey()
{
- const cache_key *newkey;
-
if (key && EBIT_TEST(flags, KEY_PRIVATE))
return; /* is already private */
hashDelete();
}
- if (mem_obj && mem_obj->hasUris()) {
+ if (mem_obj && mem_obj->hasUris())
mem_obj->id = getKeyCounter();
- newkey = storeKeyPrivate(mem_obj->storeId(), mem_obj->method, mem_obj->id);
- } else {
- newkey = storeKeyPrivate("JUNK", Http::METHOD_NONE, getKeyCounter());
- }
+ const cache_key *newkey = storeKeyPrivate();
assert(hash_lookup(store_table, newkey) == NULL);
EBIT_SET(flags, KEY_PRIVATE);
}
void
-StoreEntry::setPublicKey()
+StoreEntry::setPublicKey(const KeyScope scope)
{
- const cache_key *newkey;
-
if (key && !EBIT_TEST(flags, KEY_PRIVATE))
return; /* is already public */
assert(!EBIT_TEST(flags, RELEASE_REQUEST));
- if (mem_obj->request) {
- HttpRequest *request = mem_obj->request;
-
- if (!mem_obj->vary_headers) {
- /* First handle the case where the object no longer varies */
- safe_free(request->vary_headers);
- } else {
- if (request->vary_headers && strcmp(request->vary_headers, mem_obj->vary_headers) != 0) {
- /* Oops.. the variance has changed. Kill the base object
- * to record the new variance key
- */
- safe_free(request->vary_headers); /* free old "bad" variance key */
- if (StoreEntry *pe = storeGetPublic(mem_obj->storeId(), mem_obj->method))
- pe->release();
- }
-
- /* Make sure the request knows the variance status */
- if (!request->vary_headers) {
- const char *vary = httpMakeVaryMark(request, mem_obj->getReply());
-
- if (vary)
- request->vary_headers = xstrdup(vary);
- }
- }
-
- // 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->storeId(), mem_obj->method)) {
- /* Create "vary" base object */
- String vary;
- StoreEntry *pe = storeCreateEntry(mem_obj->storeId(), mem_obj->logUri(), request->flags, request->method);
- /* We are allowed to do this typecast */
- HttpReply *rep = new HttpReply;
- rep->setHeaders(Http::scOkay, "Internal marker object", "x-squid-internal/vary", -1, -1, squid_curtime + 100000);
- vary = mem_obj->getReply()->header.getList(HDR_VARY);
-
- if (vary.size()) {
- /* Again, we own this structure layout */
- rep->header.putStr(HDR_VARY, vary.termedBuf());
- vary.clean();
- }
-
-#if X_ACCELERATOR_VARY
- vary = mem_obj->getReply()->header.getList(HDR_X_ACCELERATOR_VARY);
-
- if (vary.size() > 0) {
- /* Again, we own this structure layout */
- rep->header.putStr(HDR_X_ACCELERATOR_VARY, vary.termedBuf());
- vary.clean();
- }
-
-#endif
- pe->replaceHttpReply(rep, false); // no write until key is public
-
- pe->timestampsSet();
-
- pe->makePublic();
+ adjustVary();
+ forcePublicKey(calcPublicKey(scope));
+}
- pe->startWriting(); // after makePublic()
+void
+StoreEntry::clearPublicKeyScope()
+{
+ if (!key || EBIT_TEST(flags, KEY_PRIVATE))
+ return; // probably the old public key was deleted or made private
- pe->complete();
+ // TODO: adjustVary() when collapsed revalidation supports that
- pe->unlock("StoreEntry::setPublicKey+Vary");
- }
+ const cache_key *newKey = calcPublicKey(ksDefault);
+ if (!storeKeyHashCmp(key, newKey))
+ return; // probably another collapsed revalidation beat us to this change
- newkey = storeKeyPublicByRequest(mem_obj->request);
- } else
- newkey = storeKeyPublic(mem_obj->storeId(), mem_obj->method);
+ forcePublicKey(newKey);
+}
+/// Unconditionally sets public key for this store entry.
+/// Releases the old entry with the same public key (if any).
+void
+StoreEntry::forcePublicKey(const cache_key *newkey)
+{
if (StoreEntry *e2 = (StoreEntry *)hash_lookup(store_table, newkey)) {
+ assert(e2 != this);
debugs(20, 3, "Making old " << *e2 << " private.");
e2->setPrivateKey();
e2->release();
-
- if (mem_obj->request)
- newkey = storeKeyPublicByRequest(mem_obj->request);
- else
- newkey = storeKeyPublic(mem_obj->storeId(), mem_obj->method);
}
if (key)
storeDirSwapLog(this, SWAP_LOG_ADD);
}
+/// Calculates correct public key for feeding forcePublicKey().
+/// Assumes adjustVary() has been called for this entry already.
+const cache_key *
+StoreEntry::calcPublicKey(const KeyScope keyScope)
+{
+ assert(mem_obj);
+ return mem_obj->request ? storeKeyPublicByRequest(mem_obj->request.getRaw(), keyScope) :
+ storeKeyPublic(mem_obj->storeId(), mem_obj->method, keyScope);
+}
+
+/// Updates mem_obj->request->vary_headers to reflect the current Vary.
+/// The vary_headers field is used to calculate the Vary marker key.
+/// Releases the old Vary marker with an outdated key (if any).
+void
+StoreEntry::adjustVary()
+{
+ assert(mem_obj);
+
+ if (!mem_obj->request)
+ return;
+
+ HttpRequestPointer request(mem_obj->request);
+
+ if (mem_obj->vary_headers.isEmpty()) {
+ /* First handle the case where the object no longer varies */
+ request->vary_headers.clear();
+ } else {
+ if (!request->vary_headers.isEmpty() && request->vary_headers.cmp(mem_obj->vary_headers) != 0) {
+ /* Oops.. the variance has changed. Kill the base object
+ * to record the new variance key
+ */
+ request->vary_headers.clear(); /* free old "bad" variance key */
+ if (StoreEntry *pe = storeGetPublic(mem_obj->storeId(), mem_obj->method))
+ pe->release();
+ }
+
+ /* Make sure the request knows the variance status */
+ if (request->vary_headers.isEmpty())
+ request->vary_headers = httpMakeVaryMark(request.getRaw(), mem_obj->getReply().getRaw());
+ }
+
+ // TODO: storeGetPublic() calls below may create unlocked entries.
+ // We should add/use storeHas() API or lock/unlock those entries.
+ if (!mem_obj->vary_headers.isEmpty() && !storeGetPublic(mem_obj->storeId(), mem_obj->method)) {
+ /* Create "vary" base object */
+ String vary;
+ StoreEntry *pe = storeCreateEntry(mem_obj->storeId(), mem_obj->logUri(), request->flags, request->method);
+ /* We are allowed to do this typecast */
+ HttpReply *rep = new HttpReply;
+ rep->setHeaders(Http::scOkay, "Internal marker object", "x-squid-internal/vary", -1, -1, squid_curtime + 100000);
+ vary = mem_obj->getReply()->header.getList(Http::HdrType::VARY);
+
+ if (vary.size()) {
+ /* Again, we own this structure layout */
+ rep->header.putStr(Http::HdrType::VARY, vary.termedBuf());
+ vary.clean();
+ }
+
+#if X_ACCELERATOR_VARY
+ vary = mem_obj->getReply()->header.getList(Http::HdrType::HDR_X_ACCELERATOR_VARY);
+
+ if (vary.size() > 0) {
+ /* Again, we own this structure layout */
+ rep->header.putStr(Http::HdrType::HDR_X_ACCELERATOR_VARY, vary.termedBuf());
+ vary.clean();
+ }
+
+#endif
+ pe->replaceHttpReply(rep, false); // no write until key is public
+
+ pe->timestampsSet();
+
+ pe->makePublic();
+
+ pe->startWriting(); // after makePublic()
+
+ pe->complete();
+
+ pe->unlock("StoreEntry::forcePublicKey+Vary");
+ }
+}
+
StoreEntry *
storeCreatePureEntry(const char *url, const char *log_url, const RequestFlags &flags, const HttpRequestMethod& method)
{
assert(store_status == STORE_PENDING);
// XXX: caller uses content offset, but we also store headers
- if (const HttpReply *reply = mem_obj->getReply())
+ if (const HttpReplyPointer reply = mem_obj->getReply())
writeBuffer.offset += reply->hdr_sz;
debugs(20, 5, "storeWrite: writing " << writeBuffer.length << " bytes for '" << getMD5Text() << "'");
write(tempBuffer);
}
+void
+StoreEntry::vappendf(const char *fmt, va_list vargs)
+{
+ LOCAL_ARRAY(char, buf, 4096);
+ *buf = 0;
+ int x;
+
+#ifdef VA_COPY
+ va_args ap;
+ /* Fix of bug 753r. The value of vargs is undefined
+ * after vsnprintf() returns. Make a copy of vargs
+ * incase we loop around and call vsnprintf() again.
+ */
+ VA_COPY(ap,vargs);
+ errno = 0;
+ if ((x = vsnprintf(buf, sizeof(buf), fmt, ap)) < 0) {
+ fatal(xstrerr(errno));
+ return;
+ }
+ va_end(ap);
+#else /* VA_COPY */
+ errno = 0;
+ if ((x = vsnprintf(buf, sizeof(buf), fmt, vargs)) < 0) {
+ fatal(xstrerr(errno));
+ return;
+ }
+#endif /*VA_COPY*/
+
+ if (x < static_cast<int>(sizeof(buf))) {
+ append(buf, x);
+ return;
+ }
+
+ // okay, do it the slow way.
+ char *buf2 = new char[x+1];
+ int y = vsnprintf(buf2, x+1, fmt, vargs);
+ assert(y >= 0 && y == x);
+ append(buf2, y);
+ delete[] buf2;
+}
+
+// deprecated. use StoreEntry::appendf() instead.
void
storeAppendPrintf(StoreEntry * e, const char *fmt,...)
{
va_list args;
va_start(args, fmt);
-
- storeAppendVPrintf(e, fmt, args);
+ e->vappendf(fmt, args);
va_end(args);
}
-/* used be storeAppendPrintf and Packer */
+// deprecated. use StoreEntry::appendf() instead.
void
storeAppendVPrintf(StoreEntry * e, const char *fmt, va_list vargs)
{
- LOCAL_ARRAY(char, buf, 4096);
- buf[0] = '\0';
- vsnprintf(buf, 4096, fmt, vargs);
- e->append(buf, strlen(buf));
+ e->vappendf(fmt, vargs);
}
struct _store_check_cachable_hist {
int private_key;
int too_many_open_files;
int too_many_open_fds;
+ int missing_parts;
} no;
struct {
return 0;
}
+bool
+StoreEntry::checkTooBig() const
+{
+ if (mem_obj->endOffset() > store_maxobjsize)
+ return true;
+
+ if (getReply()->content_length < 0)
+ return false;
+
+ return (getReply()->content_length > store_maxobjsize);
+}
+
// TODO: move "too many open..." checks outside -- we are called too early/late
bool
StoreEntry::checkCachable()
debugs(20, 3, "StoreEntry::checkCachable: NO: negative cached");
++store_check_cachable_hist.no.negative_cached;
return 0; /* avoid release call below */
- } else if ((getReply()->content_length > 0 &&
- getReply()->content_length > store_maxobjsize) ||
- mem_obj->endOffset() > store_maxobjsize) {
+ } else if (!mem_obj || !getReply()) {
+ // XXX: In bug 4131, we forgetHit() without mem_obj, so we need
+ // this segfault protection, but how can we get such a HIT?
+ debugs(20, 2, "StoreEntry::checkCachable: NO: missing parts: " << *this);
+ ++store_check_cachable_hist.no.missing_parts;
+ } else if (checkTooBig()) {
debugs(20, 2, "StoreEntry::checkCachable: NO: too big");
++store_check_cachable_hist.no.too_big;
} else if (checkTooSmall()) {
store_check_cachable_hist.no.wrong_content_length);
storeAppendPrintf(sentry, "no.negative_cached\t%d\n",
store_check_cachable_hist.no.negative_cached);
+ storeAppendPrintf(sentry, "no.missing_parts\t%d\n",
+ store_check_cachable_hist.no.missing_parts);
storeAppendPrintf(sentry, "no.too_big\t%d\n",
store_check_cachable_hist.no.too_big);
storeAppendPrintf(sentry, "no.too_small\t%d\n",
store_check_cachable_hist.yes.Default);
}
+void
+StoreEntry::lengthWentBad(const char *reason)
+{
+ debugs(20, 3, "because " << reason << ": " << *this);
+ EBIT_SET(flags, ENTRY_BAD_LENGTH);
+ releaseRequest();
+}
+
void
StoreEntry::complete()
{
assert(mem_status == NOT_IN_MEMORY);
- if (!validLength()) {
- EBIT_SET(flags, ENTRY_BAD_LENGTH);
- releaseRequest();
- }
+ if (!EBIT_TEST(flags, ENTRY_BAD_LENGTH) && !validLength())
+ lengthWentBad("!validLength() in complete()");
#if USE_CACHE_DIGESTS
if (mem_obj->request)
/*
* Someone wants to abort this transfer. Set the reason in the
- * request structure, call the server-side callback and mark the
+ * request structure, call the callback and mark the
* entry for releasing
*/
void
* it becomes active will self register
*/
void
-Store::Maintain(void *notused)
+Store::Maintain(void *)
{
Store::Root().maintain();
#define MAINTAIN_MAX_SCAN 1024
#define MAINTAIN_MAX_REMOVE 64
-/*
- * This routine is to be called by main loop in main.c.
- * It removes expired objects on only one bucket for each time called.
- *
- * This should get called 1/s from main().
- */
-void
-StoreController::maintain()
-{
- static time_t last_warn_time = 0;
-
- PROF_start(storeMaintainSwapSpace);
- swapDir->maintain();
-
- /* this should be emitted by the oversize dir, not globally */
-
- if (Store::Root().currentSize() > Store::Root().maxSize()) {
- if (squid_curtime - last_warn_time > 10) {
- debugs(20, DBG_CRITICAL, "WARNING: Disk space over limit: "
- << Store::Root().currentSize() / 1024.0 << " KB > "
- << (Store::Root().maxSize() >> 10) << " KB");
- last_warn_time = squid_curtime;
- }
- }
-
- PROF_stop(storeMaintainSwapSpace);
-}
-
/* release an object from a cache */
void
StoreEntry::release()
return;
}
- Store::Root().memoryUnlink(*this);
+ if (Store::Controller::store_dirs_rebuilding && swap_filen > -1) {
+ /* TODO: Teach disk stores to handle releases during rebuild instead. */
- if (StoreController::store_dirs_rebuilding && swap_filen > -1) {
- setPrivateKey();
+ Store::Root().memoryUnlink(*this);
- if (swap_filen > -1) {
- // lock the entry until rebuilding is done
- lock("storeLateRelease");
- setReleaseFlag();
- LateReleaseStack.push(this);
- } else {
- destroyStoreEntry(static_cast<hash_link *>(this));
- // "this" is no longer valid
- }
+ setPrivateKey();
- PROF_stop(storeRelease);
+ // lock the entry until rebuilding is done
+ lock("storeLateRelease");
+ setReleaseFlag();
+ LateReleaseStack.push(this);
return;
}
storeLog(STORE_LOG_RELEASE, this);
-
- if (swap_filen > -1) {
+ if (swap_filen > -1 && !EBIT_TEST(flags, KEY_PRIVATE)) {
// log before unlink() below clears swap_filen
- if (!EBIT_TEST(flags, KEY_PRIVATE))
- storeDirSwapLog(this, SWAP_LOG_DEL);
-
- unlink();
+ storeDirSwapLog(this, SWAP_LOG_DEL);
}
+ Store::Root().unlink(*this);
destroyStoreEntry(static_cast<hash_link *>(this));
PROF_stop(storeRelease);
}
static void
-storeLateRelease(void *unused)
+storeLateRelease(void *)
{
StoreEntry *e;
static int n = 0;
- if (StoreController::store_dirs_rebuilding) {
+ if (Store::Controller::store_dirs_rebuilding) {
eventAdd("storeLateRelease", storeLateRelease, NULL, 1.0, 1);
return;
}
storeRegisterWithCacheManager();
}
-/// computes maximum size of a cachable object
-/// larger objects are rejected by all (disk and memory) cache stores
-static int64_t
-storeCalcMaxObjSize()
-{
- int64_t ms = 0; // nothing can be cached without at least one store consent
-
- // global maximum is at least the disk store maximum
- for (int i = 0; i < Config.cacheSwap.n_configured; ++i) {
- assert (Config.cacheSwap.swapDirs[i].getRaw());
- const int64_t storeMax = dynamic_cast<SwapDir *>(Config.cacheSwap.swapDirs[i].getRaw())->maxObjectSize();
- if (ms < storeMax)
- ms = storeMax;
- }
-
- // global maximum is at least the memory store maximum
- // TODO: move this into a memory cache class when we have one
- const int64_t memMax = static_cast<int64_t>(min(Config.Store.maxInMemObjSize, Config.memMaxSize));
- if (ms < memMax)
- ms = memMax;
-
- return ms;
-}
-
void
storeConfigure(void)
{
- store_swap_high = (long) (((float) Store::Root().maxSize() *
- (float) Config.Swap.highWaterMark) / (float) 100);
- store_swap_low = (long) (((float) Store::Root().maxSize() *
- (float) Config.Swap.lowWaterMark) / (float) 100);
- store_pages_max = Config.memMaxSize / sizeof(mem_node);
-
- store_maxobjsize = storeCalcMaxObjSize();
+ Store::Root().updateLimits();
}
bool
void
storeFreeMemory(void)
{
- Store::Root(NULL);
+ Store::FreeMemory();
#if USE_CACHE_DIGESTS
-
- if (store_digest)
- cacheDigestDestroy(store_digest);
-
+ delete store_digest;
#endif
-
store_digest = NULL;
}
return 1;
}
-void
+bool
StoreEntry::timestampsSet()
{
const HttpReply *reply = getReply();
time_t served_date = reply->date;
- int age = reply->header.getInt(HDR_AGE);
+ int age = reply->header.getInt(Http::HdrType::AGE);
/* Compute the timestamp, mimicking RFC2616 section 13.2.3. */
/* make sure that 0 <= served_date <= squid_curtime */
served_date -= (squid_curtime - request_sent);
}
+ time_t exp = 0;
if (reply->expires > 0 && reply->date > -1)
- expires = served_date + (reply->expires - reply->date);
+ exp = served_date + (reply->expires - reply->date);
else
- expires = reply->expires;
+ exp = reply->expires;
+
+ if (timestamp == served_date && expires == exp) {
+ // if the reply lacks LMT, then we now know that our effective
+ // LMT (i.e., timestamp) will stay the same, otherwise, old and
+ // new modification times must match
+ if (reply->last_modified < 0 || reply->last_modified == lastModified())
+ return false; // nothing has changed
+ }
- lastmod = reply->last_modified;
+ expires = exp;
+
+ lastModified_ = reply->last_modified;
timestamp = served_date;
+
+ return true;
}
void
debugs(20, l, "StoreEntry->timestamp: " << timestamp);
debugs(20, l, "StoreEntry->lastref: " << lastref);
debugs(20, l, "StoreEntry->expires: " << expires);
- debugs(20, l, "StoreEntry->lastmod: " << lastmod);
+ debugs(20, l, "StoreEntry->lastModified_: " << lastModified_);
debugs(20, l, "StoreEntry->swap_file_sz: " << swap_file_sz);
debugs(20, l, "StoreEntry->refcount: " << refcount);
debugs(20, l, "StoreEntry->flags: " << storeEntryFlags(this));
const char *
StoreEntry::url() const
{
- if (this == NULL)
- return "[null_entry]";
- else if (mem_obj == NULL)
+ if (mem_obj == NULL)
return "[null_mem_obj]";
else
return mem_obj->storeId();
mem_obj->setUris(aUrl, aLogUrl, aMethod);
}
-/* this just sets DELAY_SENDING */
+/** disable sending content to the clients.
+ *
+ * This just sets DELAY_SENDING.
+ */
void
StoreEntry::buffer()
{
EBIT_SET(flags, DELAY_SENDING);
}
-/* this just clears DELAY_SENDING and Invokes the handlers */
+/** flush any buffered content.
+ *
+ * This just clears DELAY_SENDING and Invokes the handlers
+ * to begin sending anything that may be buffered.
+ */
void
StoreEntry::flush()
{
}
HttpReply const *
-StoreEntry::getReply () const
+StoreEntry::getReply() const
{
- if (NULL == mem_obj)
- return NULL;
-
- return mem_obj->getReply();
+ return (mem_obj ? mem_obj->getReply().getRaw() : nullptr);
}
void
StoreEntry::reset()
{
assert (mem_obj);
- debugs(20, 3, "StoreEntry::reset: " << url());
+ debugs(20, 3, url());
mem_obj->reset();
- HttpReply *rep = (HttpReply *) getReply(); // bypass const
- rep->reset();
- expires = lastmod = timestamp = -1;
+ expires = lastModified_ = timestamp = -1;
}
/*
return;
}
- mem_obj->replaceHttpReply(rep);
+ mem_obj->replaceReply(HttpReplyPointer(rep));
if (andStartWriting)
startWriting();
void
StoreEntry::startWriting()
{
- Packer p;
-
- /* TODO: when we store headers serparately remove the header portion */
+ /* TODO: when we store headers separately remove the header portion */
/* TODO: mark the length of the headers ? */
/* We ONLY want the headers */
- packerToStoreInit(&p, this);
assert (isEmpty());
assert(mem_obj);
const HttpReply *rep = getReply();
assert(rep);
- rep->packHeadersInto(&p);
+ buffer();
+ rep->packHeadersInto(this);
mem_obj->markEndOfReplyHeaders();
EBIT_CLR(flags, ENTRY_FWD_HDR_WAIT);
- rep->body.packInto(&p);
-
- packerClean(&p);
+ rep->body.packInto(this);
+ flush();
}
char const *
}
void
-StoreEntry::memOutDecision(const bool willCacheInRam)
+StoreEntry::memOutDecision(const bool)
{
transientsAbandonmentCheck();
}
}
bool
-StoreEntry::modifiedSince(HttpRequest * request) const
+StoreEntry::modifiedSince(const time_t ims, const int imslen) const
{
int object_length;
- time_t mod_time = lastmod;
-
- if (mod_time < 0)
- mod_time = timestamp;
+ const time_t mod_time = lastModified();
debugs(88, 3, "modifiedSince: '" << url() << "'");
if (object_length < 0)
object_length = contentLen();
- if (mod_time > request->ims) {
+ if (mod_time > ims) {
debugs(88, 3, "--> YES: entry newer than client");
return true;
- } else if (mod_time < request->ims) {
+ } else if (mod_time < ims) {
debugs(88, 3, "--> NO: entry older than client");
return false;
- } else if (request->imslen < 0) {
+ } else if (imslen < 0) {
debugs(88, 3, "--> NO: same LMT, no client length");
return false;
- } else if (request->imslen == object_length) {
+ } else if (imslen == object_length) {
debugs(88, 3, "--> NO: same LMT, same length");
return false;
} else {
StoreEntry::hasEtag(ETag &etag) const
{
if (const HttpReply *reply = getReply()) {
- etag = reply->header.getETag(HDR_ETAG);
+ etag = reply->header.getETag(Http::HdrType::ETAG);
if (etag.str)
return true;
}
bool
StoreEntry::hasIfMatchEtag(const HttpRequest &request) const
{
- const String reqETags = request.header.getList(HDR_IF_MATCH);
+ const String reqETags = request.header.getList(Http::HdrType::IF_MATCH);
return hasOneOfEtags(reqETags, false);
}
bool
StoreEntry::hasIfNoneMatchEtag(const HttpRequest &request) const
{
- const String reqETags = request.header.getList(HDR_IF_NONE_MATCH);
+ const String reqETags = request.header.getList(Http::HdrType::IF_NONE_MATCH);
// weak comparison is allowed only for HEAD or full-body GET requests
const bool allowWeakMatch = !request.flags.isRanged &&
(request.method == Http::METHOD_GET || request.method == Http::METHOD_HEAD);
bool
StoreEntry::hasOneOfEtags(const String &reqETags, const bool allowWeakMatch) const
{
- const ETag repETag = getReply()->header.getETag(HDR_ETAG);
+ const ETag repETag = getReply()->header.getETag(Http::HdrType::ETAG);
if (!repETag.str)
return strListIsMember(&reqETags, "*", ',');
return matched;
}
-SwapDir::Pointer
-StoreEntry::store() const
+Store::Disk &
+StoreEntry::disk() const
{
assert(0 <= swap_dirn && swap_dirn < Config.cacheSwap.n_configured);
- return INDEXSD(swap_dirn);
-}
-
-void
-StoreEntry::unlink()
-{
- store()->unlink(*this); // implies disconnect()
- swap_filen = -1;
- swap_dirn = -1;
- swap_status = SWAPOUT_NONE;
+ const RefCount<Store::Disk> &sd = INDEXSD(swap_dirn);
+ assert(sd);
+ return *sd;
}
/*
return true;
}
+const char *
+StoreEntry::describeTimestamps() const
+{
+ LOCAL_ARRAY(char, buf, 256);
+ snprintf(buf, 256, "LV:%-9d LU:%-9d LM:%-9d EX:%-9d",
+ static_cast<int>(timestamp),
+ static_cast<int>(lastref),
+ static_cast<int>(lastModified_),
+ static_cast<int>(expires));
+ return buf;
+}
+
std::ostream &operator <<(std::ostream &os, const StoreEntry &e)
{
os << "e:";
// print only set flags, using unique letters
if (e.flags) {
if (EBIT_TEST(e.flags, ENTRY_SPECIAL)) os << 'S';
- if (EBIT_TEST(e.flags, ENTRY_REVALIDATE)) os << 'R';
+ if (EBIT_TEST(e.flags, ENTRY_REVALIDATE_ALWAYS)) os << 'R';
if (EBIT_TEST(e.flags, DELAY_SENDING)) os << 'P';
if (EBIT_TEST(e.flags, RELEASE_REQUEST)) os << 'X';
if (EBIT_TEST(e.flags, REFRESH_REQUEST)) os << 'F';
+ if (EBIT_TEST(e.flags, ENTRY_REVALIDATE_STALE)) os << 'E';
if (EBIT_TEST(e.flags, ENTRY_DISPATCHED)) os << 'D';
if (EBIT_TEST(e.flags, KEY_PRIVATE)) os << 'I';
if (EBIT_TEST(e.flags, ENTRY_FWD_HDR_WAIT)) os << 'W';
{
return NULL;
}
+