Added a new CF tag to the Squid request status %Ss access log field.
This tag marks transactions that have waited for a CF initiator
transaction. This wait may happen in two cases (or their combination):
1. Classic collapsing: A client request gets collapsed on arrival
(e.g., TCP_CF_HIT or TCP_CF_MISS).
2. Collapsed revalidation: An internal revalidation request is collapsed
(e.g., TCP_CF_REFRESH_MODIFIED).
A CF tag approach is simple but the resulting access.log records cannot
distinguish some cases. For example, a pure collapsed revalidation
transaction (case 2) cannot be distinguished from these transactions:
* a collapsed client that got collapsed on revalidation (case 1+2);
* a collapsed client that initiated revalidation.
We may want to log more collapsing details in the future.
These changes do not affect CF initiating code.
In order to track collapsed transactions, a new CollapsingHistory class
was introduced. Since more and more non-logging code relies on ALE, this
history is kept in ALE. ClientHttpRequest uses its logType field instead
of the LogTags in ALE, so we also use logType for storing
ClientHttpRequest's CollapsedHistory. Eventually, ClientHttpRequest
should eliminate logType in favor of direct ALE use.
Also: ICP code fixing/refactoring:
* htcpSyncAle() and icpSyncAle() should not require the caller to supply
correct LogTags because callers like fillChecklist() do not have
access to that information (it is not stored in the transaction object
unlike the other pieces of info that these functions copy to ALE).
* Added icpUdpData::ale to preserve master transaction info when
messages are queued. Several icpUdpData improvements were triggered by
this change because ale is a (second!) non-POD member and icpUdpData
was mistreated as a POD. They include:
- Removed icpUdpData::start as unused.
- Removed icpUdpData::len as set but otherwise unused.
- Removed icpUdpData::logcode as essentially duplicating msg->opcode.
* Update ICP ALE, if any, as soon as the transaction tags become known
(instead of sometimes waiting for the ICP message to be logged). The
ICP message may be dropped and/or never be logged, but we should keep
ALE up to date because it is used in an increasingly many contexts.
Also found and marked an ICP memory leak. It is best to fix that in a
dedicated commit.
Also supplied URN code with ALE. Full-featured Client-based classes
already use ALE. We have not tested with URNs, but these changes may
improve logging of transactions that involve URN resolution.
Also fixed problematic StoreEntry::collapsingInitiator(). It could
return true if the entry had transients but had nothing to do with
collapsing. It also incorrectly assumed that a collapsed entry is always
marked with ENTRY_FWD_HDR_WAIT. That assumption is wrong because
Controller::allowCollapsing() does not set this flag for the entry.
We did not find a better way to track StoreEntry objects associated with
CF initiators than to add a new StoreEntry flag. Hitting an entry
flagged with ENTRY_REQUIRES_COLLAPSING requires collapsing the request.
Ip::Address caddr;
int64_t highOffset = 0;
int64_t objectSize = 0;
- LogTags code = LOG_TAG_NONE;
+ LogTags code;
struct timeval start_time; ///< The time the master transaction started
struct timeval trTime; ///< The response time
const char *rfc931 = nullptr;
--- /dev/null
+/*
+ * Copyright (C) 1996-2018 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.
+ */
+
+#ifndef SQUID_COLLAPSING_HISTORY_H
+#define SQUID_COLLAPSING_HISTORY_H
+
+/// collapsed forwarding history of a master transaction
+class CollapsingHistory
+{
+public:
+ /// whether at least one request was collapsed
+ bool collapsed() const { return revalidationCollapses || otherCollapses; }
+
+ /* These stats count collapsing decisions, regardless of their outcome. */
+
+ /// the total number of collapsed internal revalidation requests
+ int revalidationCollapses = 0;
+ /// the total number of all other (a.k.a. "classic") collapsed requests
+ int otherCollapses = 0;
+};
+
+#endif /* SQUID_COLLAPSING_HISTORY_H */
+
return;
case AnyP::PROTO_URN:
- urnStart(request, entry);
+ urnStart(request, entry, al);
return;
default:
ICPState(icp_common_t &aHeader, HttpRequest *aRequest);
virtual ~ICPState();
- /// whether the found entry warrants an ICP_HIT response
- bool foundHit(const StoreEntry &) const;
-
icp_common_t header;
HttpRequest *request;
int fd;
protected:
/* StoreClient API */
+ virtual LogTags *loggingTags() override;
virtual void fillChecklist(ACLFilledChecklist &) const override;
- mutable AccessLogEntryPointer al;
-};
-
-/// \ingroup ServerProtocolICPAPI
-struct icpUdpData {
-
- /// IP address for the remote end. Because we reply to packets from unknown non-peers.
- Ip::Address address;
-
- void *msg;
- size_t len;
- icpUdpData *next;
-#ifndef LESS_TIMING
-
- struct timeval start;
-#endif
- LogTags logcode;
+ /// either confirms and starts processing a cache hit or returns false
+ bool confirmAndPrepHit(const StoreEntry &);
- struct timeval queue_time;
+ mutable AccessLogEntryPointer al;
};
extern Comm::ConnectionPointer icpIncomingConn;
/// \ingroup ServerProtocolICPAPI
icp_opcode icpGetCommonOpcode();
-/// \ingroup ServerProtocolICPAPI
-int icpUdpSend(int, const Ip::Address &, icp_common_t *, const LogTags &, int, AccessLogEntryPointer);
-
-/// \ingroup ServerProtocolICPAPI
-LogTags icpLogFromICPCode(icp_opcode opcode);
-
/// \ingroup ServerProtocolICPAPI
void icpDenyAccess(Ip::Address &from, char *url, int reqnum, int fd);
/// \ingroup ServerProtocolICPAPI
PF icpHandleUdp;
-/// \ingroup ServerProtocolICPAPI
-PF icpUdpSendQueue;
-
/// \ingroup ServerProtocolICPAPI
void icpHandleIcpV3(int, Ip::Address &, char *, int);
*/
#include "squid.h"
+#include "Debug.h"
#include "LogTags.h"
// old deprecated tag strings
"TYPE_MAX"
};
+void
+LogTags::update(const LogTags_ot t)
+{
+ assert(t < LOG_TYPE_MAX);
+ debugs(83, 7, Str_[oldType] << " to " << Str_[t]);
+ oldType = t;
+}
+
/*
* This method is documented in http://wiki.squid-cache.org/SquidFaq/SquidLogs#Squid_result_codes
* Please keep the wiki up to date
int pos = 0;
// source tags
- if (oldType && oldType < LOG_TYPE_MAX)
- pos += snprintf(buf, sizeof(buf), "%s",Str_[oldType]);
+ const int protoLen = 3;
+ if (oldType && oldType < LOG_TYPE_MAX) {
+ assert(Str_[oldType][protoLen] == '_');
+ snprintf(buf, protoLen + 1, "%s", Str_[oldType]);
+ pos += protoLen;
+ }
else
pos += snprintf(buf, sizeof(buf), "NONE");
+ if (collapsingHistory.collapsed())
+ pos += snprintf(buf + pos, sizeof(buf) - pos, "_CF");
+
+ const char *tag = Str_[oldType] + protoLen;
+ pos += snprintf(buf + pos, sizeof(buf) - pos, "%s", tag);
+
if (err.ignored)
pos += snprintf(buf+pos,sizeof(buf)-pos, "_IGNORED");
#ifndef SQUID_SRC_LOGTAGS_H
#define SQUID_SRC_LOGTAGS_H
+#include "CollapsingHistory.h"
+
/** Squid transaction result code/tag set.
*
* These codes indicate how the request was received
class LogTags
{
public:
- LogTags(LogTags_ot t) : oldType(t) {assert(oldType < LOG_TYPE_MAX);}
- // XXX: this operator does not reset flags
- // TODO: either replace with a category-only setter or remove
- LogTags &operator =(const LogTags_ot &t) {assert(t < LOG_TYPE_MAX); oldType = t; return *this;}
+ LogTags() = default;
+ explicit LogTags(const LogTags_ot t) { update(t); }
+
+ void update(const LogTags_ot t);
/// compute the status access.log field
const char *c_str() const;
public: // XXX: only until client_db.cc stats are redesigned.
- // deprecated LogTag enum value
- LogTags_ot oldType;
+ /// a set of client protocol, cache use, and other transaction outcome tags
+ LogTags_ot oldType = LOG_TAG_NONE;
+ /// controls CF tag presence
+ CollapsingHistory collapsingHistory;
};
/// iterator for LogTags_ot enumeration
clientStreamForward.h \
CollapsedForwarding.cc \
CollapsedForwarding.h \
+ CollapsingHistory.h \
CommandLine.cc \
CommandLine.h \
CompletionDispatcher.cc \
HttpReply.cc \
int.h \
int.cc \
+ LogTags.cc \
MasterXaction.cc \
MasterXaction.h \
MemBuf.cc \
HttpReply.cc \
int.h \
int.cc \
+ LogTags.cc \
RequestFlags.h \
RequestFlags.cc \
Transients.cc \
HttpReply.cc \
int.h \
int.cc \
+ LogTags.cc \
MasterXaction.cc \
MasterXaction.h \
MemBuf.cc \
/// whether there is a corresponding locked shared memory table entry
bool hasMemStore() const { return mem_obj && mem_obj->memCache.index >= 0; }
- /// whether this is a collapsed forwarding-created public entry that still
- /// has not received its response headers; new requests may collapse on it
- bool collapsingInitiator() const;
+ /// whether this entry can feed collapsed requests and only them
+ bool hittingRequiresCollapsing() const { return EBIT_TEST(flags, ENTRY_REQUIRES_COLLAPSING); }
+
+ /// allow or forbid collapsed requests feeding
+ void setCollapsingRequirement(const bool required);
MemObject *mem_obj;
RemovalPolicyNode repl;
class StoreEntry;
class ACLFilledChecklist;
+class LogTags;
/// A StoreEntry::getPublic*() caller.
class StoreClient
/// An isNull() entry indicates a cache miss.
virtual void created(StoreEntry *) = 0;
+ /// \return LogTags (if the class logs transactions) or nil (otherwise)
+ virtual LogTags *loggingTags() = 0;
+
protected:
/// configure the ACL checklist with the current transaction state
virtual void fillChecklist(ACLFilledChecklist &) const = 0;
+ /// \returns whether the caller must collapse on the given entry
+ /// Before returning true, updates common collapsing-related stats.
+ /// See also: StoreEntry::hittingRequiresCollapsing().
+ bool startCollapsingOn(const StoreEntry &, const bool doingRevalidation);
+
// These methods only interpret Squid configuration. Their allowances are
// provisional -- other factors may prevent collapsed forwarding. The first
// two exist primarily to distinguish two major CF cases in callers code.
/// whether Squid configuration allows us to become a CF initiator
bool mayInitiateCollapsing() const { return onCollapsingPath(); }
- /// whether Squid configuration allows collapsing on the initiatorEntry
- bool mayCollapseOn(const StoreEntry &initiatorEntry) const;
/// whether Squid configuration allows collapsing for this transaction
bool onCollapsingPath() const;
};
e->mem_obj->xitTable.index = index;
e->mem_obj->xitTable.io = Store::ioReading;
anchor->exportInto(*e);
+ const bool collapsingRequired = EBIT_TEST(anchor->basics.flags, ENTRY_REQUIRES_COLLAPSING);
+ e->setCollapsingRequirement(collapsingRequired);
// keep read lock to receive updates from others
return e;
}
return NULL;
}
+void
+Transients::clearCollapsingRequirement(const StoreEntry &e)
+{
+ assert(map);
+ assert(e.hasTransients());
+ assert(isWriter(e));
+ const auto idx = e.mem_obj->xitTable.index;
+ auto &anchor = map->writeableEntry(idx);
+ if (EBIT_TEST(anchor.basics.flags, ENTRY_REQUIRES_COLLAPSING)) {
+ EBIT_CLR(anchor.basics.flags, ENTRY_REQUIRES_COLLAPSING);
+ CollapsedForwarding::Broadcast(e);
+ }
+}
+
void
Transients::monitorIo(StoreEntry *e, const cache_key *key, const Store::IoStatus direction)
{
}
void
-Transients::status(const StoreEntry &entry, bool &aborted, bool &waitingToBeFreed) const
+Transients::status(const StoreEntry &entry, Transients::EntryStatus &entryStatus) const
{
assert(map);
assert(entry.hasTransients());
const auto idx = entry.mem_obj->xitTable.index;
const auto &anchor = isWriter(entry) ?
map->writeableEntry(idx) : map->readableEntry(idx);
- aborted = anchor.writerHalted;
- waitingToBeFreed = anchor.waitingToBeFreed;
+ entryStatus.abortedByWriter = anchor.writerHalted;
+ entryStatus.waitingToBeFreed = anchor.waitingToBeFreed;
+ entryStatus.collapsed = EBIT_TEST(anchor.basics.flags, ENTRY_REQUIRES_COLLAPSING);
}
void
class Transients: public Store::Controlled, public Ipc::StoreMapCleaner
{
public:
+ /// shared entry metadata, used for synchronization
+ class EntryStatus
+ {
+ public:
+ bool abortedByWriter = false; ///< whether the entry was aborted
+ bool waitingToBeFreed = false; ///< whether the entry was marked for deletion
+ bool collapsed = false; ///< whether the entry allows collapsing
+ };
+
Transients();
virtual ~Transients();
/// return a local, previously collapsed entry
StoreEntry *findCollapsed(const sfileno xitIndex);
+ /// removes collapsing requirement (for future hits)
+ void clearCollapsingRequirement(const StoreEntry &e);
+
/// start listening for remote DELETE requests targeting either a complete
/// StoreEntry (ioReading) or a being-formed miss StoreEntry (ioWriting)
void monitorIo(StoreEntry*, const cache_key*, const Store::IoStatus);
/// called when the in-transit entry has been successfully cached
void completeWriting(const StoreEntry &e);
- /// copies current shared entry metadata into parameters
- /// \param aborted whether the entry was aborted
- /// \param waitingToBeFreed whether the entry was marked for deletion
- void status(const StoreEntry &e, bool &aborted, bool &waitingToBeFreed) const;
+ /// copies current shared entry metadata into entryStatus
+ void status(const StoreEntry &e, EntryStatus &entryStatus) const;
/// number of entry readers some time ago
int readers(const StoreEntry &e) const;
asState->request = HttpRequest::FromUrl(asres, mx);
assert(asState->request != NULL);
+ // XXX: Missing a hittingRequiresCollapsing() && startCollapsingOn() check.
if ((e = storeGetPublic(asres, Http::METHOD_GET)) == NULL) {
e = storeCreateEntry(asres, asres, RequestFlags(), Http::METHOD_GET);
asState->sc = storeClientListAdd(e, asState);
#include "SquidTime.h"
Adaptation::Icap::History::History():
- logType(LOG_TAG_NONE),
req_sz(0),
concurrencyLevel(0)
{
return;
}
- http->logType = LOG_TCP_REFRESH;
+ http->logType.update(LOG_TCP_REFRESH);
http->request->flags.refresh = true;
#if STORE_CLIENT_LIST_DEBUG
/* Prevent a race with the store client memory free routines
/* Prepare to make a new temporary request */
saveState();
+ // TODO: Consider also allowing regular (non-collapsed) revalidation hits.
// TODO: support collapsed revalidation for Vary-controlled entries
bool collapsingAllowed = Config.onoff.collapsed_forwarding &&
!Store::Root().smpAware() &&
StoreEntry *entry = nullptr;
if (collapsingAllowed) {
if (const auto e = storeGetPublicByRequest(http->request, ksRevalidation)) {
- if (e->collapsingInitiator() && mayCollapseOn(*e)) {
+ if (e->hittingRequiresCollapsing() && startCollapsingOn(*e, true)) {
entry = e;
entry->lock("clientReplyContext::processExpired#alreadyRevalidating");
} else {
debugs(88, 3, "CF slave hit private non-shareable " << *http->storeEntry() << ". MISS");
// restore context to meet processMiss() expectations
restoreState();
- http->logType = LOG_TCP_MISS;
+ http->logType.update(LOG_TCP_MISS);
processMiss();
return;
}
// request to origin was aborted
if (EBIT_TEST(http->storeEntry()->flags, ENTRY_ABORTED)) {
debugs(88, 3, "request to origin aborted '" << http->storeEntry()->url() << "', sending old entry to client");
- http->logType = LOG_TCP_REFRESH_FAIL_OLD;
+ http->logType.update(LOG_TCP_REFRESH_FAIL_OLD);
sendClientOldEntry();
}
// origin replied 304
if (status == Http::scNotModified) {
- http->logType = LOG_TCP_REFRESH_UNMODIFIED;
+ http->logType.update(LOG_TCP_REFRESH_UNMODIFIED);
http->request->flags.staleIfHit = false; // old_entry is no longer stale
// update headers on existing entry
old_rep->sline.status() << ") to client");
sendClientOldEntry();
} else {
- http->logType = LOG_TCP_REFRESH_MODIFIED;
+ http->logType.update(LOG_TCP_REFRESH_MODIFIED);
debugs(88, 3, "origin replied " << status <<
", replacing existing entry and forwarding to client");
// origin replied with an error
else if (http->request->flags.failOnValidationError) {
- http->logType = LOG_TCP_REFRESH_FAIL_ERR;
+ http->logType.update(LOG_TCP_REFRESH_FAIL_ERR);
debugs(88, 3, "origin replied with error " << status <<
", forwarding to client due to fail_on_validation_err");
sendClientUpstreamResponse();
} else {
// ignore and let client have old entry
- http->logType = LOG_TCP_REFRESH_FAIL_OLD;
+ http->logType.update(LOG_TCP_REFRESH_FAIL_OLD);
debugs(88, 3, "origin replied with error " <<
status << ", sending old entry (" << old_rep->sline.status() << ") to client");
sendClientOldEntry();
} else if (result.flags.error) {
/* swap in failure */
debugs(88, 3, "clientCacheHit: swapin failure for " << http->uri);
- http->logType = LOG_TCP_SWAPFAIL_MISS;
+ http->logType.update(LOG_TCP_SWAPFAIL_MISS);
removeClientStoreReference(&sc, http);
processMiss();
return;
// happen to regular hits because we are called asynchronously.
if (!e->mayStartHitting()) {
debugs(88, 3, "unsharable " << *e << ". MISS");
- http->logType = LOG_TCP_MISS;
+ http->logType.update(LOG_TCP_MISS);
processMiss();
return;
}
* object
*/
/* treat as a miss */
- http->logType = LOG_TCP_MISS;
+ http->logType.update(LOG_TCP_MISS);
processMiss();
return;
}
if (http->request->storeId().cmp(e->mem_obj->storeId()) != 0) {
debugs(33, DBG_IMPORTANT, "clientProcessHit: URL mismatch, '" << e->mem_obj->storeId() << "' != '" << http->request->storeId() << "'");
- http->logType = LOG_TCP_MISS; // we lack a more precise LOG_*_MISS code
+ http->logType.update(LOG_TCP_MISS); // we lack a more precise LOG_*_MISS code
processMiss();
return;
}
case VARY_CANCEL:
/* varyEvaluateMatch found a object loop. Process as miss */
debugs(88, DBG_IMPORTANT, "clientProcessHit: Vary object loop!");
- http->logType = LOG_TCP_MISS; // we lack a more precise LOG_*_MISS code
+ http->logType.update(LOG_TCP_MISS); // we lack a more precise LOG_*_MISS code
processMiss();
return;
}
if (e->checkNegativeHit() && !r->flags.noCacheHack()) {
debugs(88, 5, "negative-HIT");
- http->logType = LOG_TCP_NEGATIVE_HIT;
+ http->logType.update(LOG_TCP_NEGATIVE_HIT);
sendMoreData(result);
return;
} else if (blockedHit()) {
debugs(88, 5, "send_hit forces a MISS");
- http->logType = LOG_TCP_MISS;
+ http->logType.update(LOG_TCP_MISS);
processMiss();
return;
} else if (!http->flags.internal && refreshCheckHTTP(e, r)) {
* modification time.
* XXX: BUG 1890 objects without Date do not get one added.
*/
- http->logType = LOG_TCP_MISS;
+ http->logType.update(LOG_TCP_MISS);
processMiss();
} else if (r->flags.noCache) {
debugs(88, 3, "validate HIT object? NO. Client sent CC:no-cache. Do CLIENT_REFRESH_MISS");
* This did not match a refresh pattern that overrides no-cache
* we should honour the client no-cache header.
*/
- http->logType = LOG_TCP_CLIENT_REFRESH_MISS;
+ http->logType.update(LOG_TCP_CLIENT_REFRESH_MISS);
processMiss();
} else if (r->url.getScheme() == AnyP::PROTO_HTTP || r->url.getScheme() == AnyP::PROTO_HTTPS) {
debugs(88, 3, "validate HIT object? YES.");
* We don't know how to re-validate other protocols. Handle
* them as if the object has expired.
*/
- http->logType = LOG_TCP_MISS;
+ http->logType.update(LOG_TCP_MISS);
processMiss();
}
return;
#if USE_DELAY_POOLS
if (e->store_status != STORE_OK)
- http->logType = LOG_TCP_MISS;
+ http->logType.update(LOG_TCP_MISS);
else
#endif
if (e->mem_status == IN_MEMORY)
- http->logType = LOG_TCP_MEM_HIT;
+ http->logType.update(LOG_TCP_MEM_HIT);
else if (Config.onoff.offline)
- http->logType = LOG_TCP_OFFLINE_HIT;
+ http->logType.update(LOG_TCP_OFFLINE_HIT);
sendMoreData(result);
}
if (http->redirect.status) {
HttpReply *rep = new HttpReply;
- http->logType = LOG_TCP_REDIRECT;
+ http->logType.update(LOG_TCP_REDIRECT);
http->storeEntry()->releaseRequest();
rep->redirect(http->redirect.status, http->redirect.location);
http->storeEntry()->replaceHttpReply(rep);
if (e->getReply()->sline.status() != Http::scOkay) {
debugs(88, 4, "Reply code " << e->getReply()->sline.status() << " != 200");
- http->logType = LOG_TCP_MISS;
+ http->logType.update(LOG_TCP_MISS);
processMiss();
return true;
}
identifyFoundObject(newEntry);
}
+LogTags *
+clientReplyContext::loggingTags()
+{
+ // XXX: clientReplyContext code assumes that http cbdata is always valid.
+ // TODO: Either add cbdataReferenceValid(http) checks in all the relevant
+ // places, like this one, or remove cbdata protection of the http member.
+ return &http->logType;
+}
+
void
clientReplyContext::purgeFoundGet(StoreEntry *newEntry)
{
assert (entry && !entry->isNull());
if (EBIT_TEST(entry->flags, ENTRY_SPECIAL)) {
- http->logType = LOG_TCP_DENIED;
+ http->logType.update(LOG_TCP_DENIED);
Ip::Address tmp_noaddr;
tmp_noaddr.setNoAddr(); // TODO: make a global const
ErrorState *err = clientBuildError(ERR_ACCESS_DENIED, Http::scForbidden, NULL,
sc = storeClientListAdd(http->storeEntry(), this);
- http->logType = LOG_TCP_HIT;
+ http->logType.update(LOG_TCP_HIT);
reqofs = 0;
Config2.onoff.enable_purge);
if (!Config2.onoff.enable_purge) {
- http->logType = LOG_TCP_DENIED;
+ http->logType.update(LOG_TCP_DENIED);
Ip::Address tmp_noaddr;
tmp_noaddr.setNoAddr();
ErrorState *err = clientBuildError(ERR_ACCESS_DENIED, Http::scForbidden, NULL,
void
clientReplyContext::purgeDoMissPurge()
{
- http->logType = LOG_TCP_MISS;
+ http->logType.update(LOG_TCP_MISS);
lookingforstore = 3;
StoreEntry::getPublicByRequestMethod(this,http->request, Http::METHOD_GET);
}
if (NULL == http->storeEntry()) {
/** \li If no StoreEntry object is current assume this object isn't in the cache set MISS*/
debugs(85, 3, "StoreEntry is NULL - MISS");
- http->logType = LOG_TCP_MISS;
+ http->logType.update(LOG_TCP_MISS);
doGetMoreData();
return;
}
if (Config.onoff.offline) {
/** \li If we are running in offline mode set to HIT */
debugs(85, 3, "offline HIT " << *e);
- http->logType = LOG_TCP_HIT;
+ http->logType.update(LOG_TCP_HIT);
doGetMoreData();
return;
}
/** \li If redirection status is True force this to be a MISS */
debugs(85, 3, "REDIRECT status forced StoreEntry to NULL (no body on 3XX responses) " << *e);
forgetHit();
- http->logType = LOG_TCP_REDIRECT;
+ http->logType.update(LOG_TCP_REDIRECT);
doGetMoreData();
return;
}
if (!e->validToSend()) {
debugs(85, 3, "!storeEntryValidToSend MISS " << *e);
forgetHit();
- http->logType = LOG_TCP_MISS;
+ http->logType.update(LOG_TCP_MISS);
doGetMoreData();
return;
}
if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
/* \li Special entries are always hits, no matter what the client says */
debugs(85, 3, "ENTRY_SPECIAL HIT " << *e);
- http->logType = LOG_TCP_HIT;
+ http->logType.update(LOG_TCP_HIT);
doGetMoreData();
return;
}
if (r->flags.noCache) {
debugs(85, 3, "no-cache REFRESH MISS " << *e);
forgetHit();
- http->logType = LOG_TCP_CLIENT_REFRESH_MISS;
+ http->logType.update(LOG_TCP_CLIENT_REFRESH_MISS);
doGetMoreData();
return;
}
- if (e->collapsingInitiator() && !mayCollapseOn(*e)) {
+ if (e->hittingRequiresCollapsing() && !startCollapsingOn(*e, false)) {
debugs(85, 3, "prohibited CF MISS " << *e);
forgetHit();
- http->logType = LOG_TCP_MISS;
+ http->logType.update(LOG_TCP_MISS);
doGetMoreData();
return;
}
debugs(85, 3, "default HIT " << *e);
- http->logType = LOG_TCP_HIT;
+ http->logType.update(LOG_TCP_HIT);
doGetMoreData();
}
}
/* continue forwarding, not finished yet. */
- http->logType = LOG_TCP_MISS;
+ http->logType.update(LOG_TCP_MISS);
context->doGetMoreData();
} else
{
Ip::Address tmp_noaddr;
tmp_noaddr.setNoAddr(); // TODO: make a global const
- http->logType = LOG_TCP_DENIED_REPLY;
+ http->logType.update(LOG_TCP_DENIED_REPLY);
ErrorState *err = clientBuildError(ERR_TOO_BIG, Http::scForbidden, NULL,
http->getConn() != NULL ? http->getConn()->clientConnection->remote : tmp_noaddr,
http->request);
void
clientReplyContext::sendPreconditionFailedError()
{
- http->logType = LOG_TCP_HIT;
+ http->logType.update(LOG_TCP_HIT);
Ip::Address tmp_noaddr;
tmp_noaddr.setNoAddr();
ErrorState *const err =
// log as TCP_INM_HIT if code 304 generated for
// If-None-Match request
if (!http->request->flags.ims)
- http->logType = LOG_TCP_INM_HIT;
+ http->logType.update(LOG_TCP_INM_HIT);
else
- http->logType = LOG_TCP_IMS_HIT;
+ http->logType.update(LOG_TCP_IMS_HIT);
removeClientStoreReference(&sc, http);
createStoreEntry(http->request->method, RequestFlags());
e = http->storeEntry();
err_type page_id;
page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName, 1);
- http->logType = LOG_TCP_DENIED_REPLY;
+ http->logType.update(LOG_TCP_DENIED_REPLY);
if (page_id == ERR_NONE)
page_id = ERR_ACCESS_DENIED;
/* state variable - replace with class to handle storeentries at some point */
int lookingforstore;
+
+ /* StoreClient API */
virtual void created (StoreEntry *newEntry);
+ virtual LogTags *loggingTags();
ClientHttpRequest *http;
int headers_sz;
uri(NULL),
log_uri(NULL),
req_sz(0),
- logType(LOG_TAG_NONE),
calloutContext(NULL),
maxReplyBodySize_(0),
entry_(NULL),
*/
page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName, answer != ACCESS_AUTH_REQUIRED);
- http->logType = LOG_TCP_DENIED;
+ http->logType.update(LOG_TCP_DENIED);
if (auth_challenge) {
#if USE_AUTH
ClientHttpRequest::httpStart()
{
PROF_start(httpStart);
- logType = LOG_TAG_NONE;
+ // XXX: Re-initializes rather than updates. Should not be needed at all.
+ logType.update(LOG_TAG_NONE);
debugs(85, 4, logType.c_str() << " for '" << uri << "'");
/* no one should have touched this */
ENTRY_NEGCACHED,
ENTRY_VALIDATED,
ENTRY_BAD_LENGTH,
- ENTRY_ABORTED
+ ENTRY_ABORTED,
+ /// Whether the entry serves collapsed hits now.
+ /// Meaningful only for public entries.
+ ENTRY_REQUIRES_COLLAPSING
};
/*
/* StoreClient API */
void created(StoreEntry *);
+ virtual LogTags *loggingTags();
virtual void fillChecklist(ACLFilledChecklist &) const;
public:
size_t reqHdrsSz = 0; ///< size of the req_hdrs content
HttpRequest::Pointer request;
+ /// optimization: nil until needed
+ mutable AccessLogEntryPointer al;
+
private:
HttpRequest::Pointer checkHitRequest;
Ip::Address from;
htcpDataHeader *dhdr = nullptr;
- mutable AccessLogEntryPointer al;
};
class htcpDetail {
static void htcpHandleMsg(char *buf, int sz, Ip::Address &from);
-static void htcpLogHtcp(Ip::Address &, int, LogTags, const char *, AccessLogEntryPointer);
+static void htcpLogHtcp(Ip::Address &, const int, const LogTags_ot, const char *, AccessLogEntryPointer);
static void htcpHandleTst(htcpDataHeader *, char *buf, int sz, Ip::Address &from);
static void htcpRecv(int fd, void *data);
static void htcpHandleTstResponse(htcpDataHeader *, char *, int, Ip::Address &);
static void
-htcpSyncAle(AccessLogEntryPointer &al, const Ip::Address &caddr, int opcode, LogTags logcode, const char *url)
+htcpSyncAle(AccessLogEntryPointer &al, const Ip::Address &caddr, const int opcode, const char *url)
{
if (!al)
al = new AccessLogEntry();
al->cache.caddr = caddr;
al->htcp.opcode = htcpOpcodeStr[opcode];
- al->cache.code = logcode;
al->url = url;
// HTCP transactions do not wait
al->cache.start_time = current_time;
debugs(31, 3, "htcpCheckHit: NO; entry not valid to send" );
} else if (refreshCheckHTCP(e, checkHitRequest.getRaw())) {
debugs(31, 3, "htcpCheckHit: NO; cached response is stale");
- } else if (e->collapsingInitiator() && !mayCollapseOn(*e)) {
+ } else if (e->hittingRequiresCollapsing() && !startCollapsingOn(*e, false)) {
debugs(31, 3, "htcpCheckHit: NO; prohibited CF hit: " << *e);
} else {
debugs(31, 3, "htcpCheckHit: YES!?");
// e->abandon();
}
+LogTags *
+htcpSpecifier::loggingTags()
+{
+ // calling htcpSyncAle() here would not change cache.code
+ if (!al)
+ al = new AccessLogEntry();
+ return &al->cache.code;
+}
+
void
htcpSpecifier::fillChecklist(ACLFilledChecklist &checklist) const
{
checklist.setRequest(request.getRaw());
- htcpSyncAle(al, from, dhdr->opcode, LOG_TAG_NONE, uri);
+ htcpSyncAle(al, from, dhdr->opcode, uri);
checklist.al = al;
}
if (!s->request) {
debugs(31, 3, "htcpHandleTstRequest: failed to parse request");
- htcpLogHtcp(from, dhdr->opcode, LOG_UDP_INVALID, dash_str, nullptr);
+ htcpLogHtcp(from, dhdr->opcode, LOG_UDP_INVALID, dash_str, s->al);
return;
}
if (!htcpAccessAllowed(Config.accessList.htcp, s, from)) {
debugs(31, 3, "htcpHandleTstRequest: Access denied");
- htcpLogHtcp(from, dhdr->opcode, LOG_UDP_DENIED, s->uri, nullptr);
+ htcpLogHtcp(from, dhdr->opcode, LOG_UDP_DENIED, s->uri, s->al);
return;
}
debugs(31, 3, "htcpHandleClr: htcpUnpackSpecifier failed");
htcpLogHtcp(from, hdr->opcode, LOG_UDP_INVALID, dash_str, nullptr);
return;
+ } else {
+ s->setFrom(from);
+ s->setDataHeader(hdr);
}
if (!s->request) {
debugs(31, 3, "htcpHandleTstRequest: failed to parse request");
- htcpLogHtcp(from, hdr->opcode, LOG_UDP_INVALID, dash_str, nullptr);
+ htcpLogHtcp(from, hdr->opcode, LOG_UDP_INVALID, dash_str, s->al);
return;
}
if (!htcpAccessAllowed(Config.accessList.htcp_clr, s, from)) {
debugs(31, 3, "htcpHandleClr: Access denied");
- htcpLogHtcp(from, hdr->opcode, LOG_UDP_DENIED, s->uri, nullptr);
+ htcpLogHtcp(from, hdr->opcode, LOG_UDP_DENIED, s->uri, s->al);
return;
}
case 1:
htcpClrReply(hdr, 1, from); /* hit */
- htcpLogHtcp(from, hdr->opcode, LOG_UDP_HIT, s->uri, nullptr);
+ htcpLogHtcp(from, hdr->opcode, LOG_UDP_HIT, s->uri, s->al);
break;
case 0:
htcpClrReply(hdr, 0, from); /* miss */
- htcpLogHtcp(from, hdr->opcode, LOG_UDP_MISS, s->uri, nullptr);
+ htcpLogHtcp(from, hdr->opcode, LOG_UDP_MISS, s->uri, s->al);
break;
default:
}
static void
-htcpLogHtcp(Ip::Address &caddr, int opcode, LogTags logcode, const char *url, AccessLogEntryPointer al)
+htcpLogHtcp(Ip::Address &caddr, const int opcode, const LogTags_ot logcode, const char *url, AccessLogEntryPointer al)
{
- if (LOG_TAG_NONE == logcode.oldType)
- return;
if (!Config.onoff.log_udp)
return;
- htcpSyncAle(al, caddr, opcode, logcode, url);
+ htcpSyncAle(al, caddr, opcode, url);
+
+ assert(logcode != LOG_TAG_NONE);
+ al->cache.code.update(logcode);
+
accessLogLog(al, NULL);
}
#include <cerrno>
+/// a delayed icpUdpSend() call
+class DelayedUdpSend {
+public:
+ Ip::Address address; ///< remote peer (which may not be a cache_peer)
+ icp_common_t *msg = nullptr; ///< ICP message with network byte order fields
+ DelayedUdpSend *next = nullptr; ///< invasive FIFO queue of delayed ICP messages
+ AccessLogEntryPointer ale; ///< sender's master transaction summary
+ struct timeval queue_time = {0, 0}; ///< queuing timestamp
+};
+
static void icpIncomingConnectionOpened(const Comm::ConnectionPointer &conn, int errNo);
/// \ingroup ServerProtocolICPInternal2
-static void icpLogIcp(const Ip::Address &, const LogTags &, int, const char *, int, AccessLogEntryPointer);
+static void icpLogIcp(const Ip::Address &, const LogTags_ot, int, const char *, const int, AccessLogEntryPointer &);
/// \ingroup ServerProtocolICPInternal2
static void icpHandleIcpV2(int, Ip::Address &, char *, int);
/// \ingroup ServerProtocolICPInternal2
static void icpCount(void *, int, size_t, int);
+static LogTags_ot icpLogFromICPCode(icp_opcode);
+
+static int icpUdpSend(int fd, const Ip::Address &to, icp_common_t * msg, int delay, AccessLogEntryPointer al);
+
static void
-icpSyncAle(AccessLogEntryPointer &al, const Ip::Address &caddr, LogTags logcode, const char *url, int len, int delay)
+icpSyncAle(AccessLogEntryPointer &al, const Ip::Address &caddr, const char *url, int len, int delay)
{
if (!al)
al = new AccessLogEntry();
al->icp.opcode = ICP_QUERY;
al->cache.caddr = caddr;
- al->cache.code = logcode;
al->url = url;
// XXX: move to use icp.clientReply instead
al->http.clientReplySz.payloadData = len;
* IcpQueueHead is global so comm_incoming() knows whether or not
* to call icpUdpSendQueue.
*/
-static icpUdpData *IcpQueueHead = NULL;
+static DelayedUdpSend *IcpQueueHead = NULL;
/// \ingroup ServerProtocolICPInternal2
-static icpUdpData *IcpQueueTail = NULL;
+static DelayedUdpSend *IcpQueueTail = NULL;
/// \ingroup ServerProtocolICPInternal2
Comm::ConnectionPointer icpIncomingConn = NULL;
}
bool
-ICPState::foundHit(const StoreEntry &e) const
+ICPState::confirmAndPrepHit(const StoreEntry &e)
{
if (e.isNull())
return false;
if (!Config.onoff.icp_hit_stale && refreshCheckICP(&e, request))
return false;
- if (e.collapsingInitiator() && !mayCollapseOn(e))
+ if (e.hittingRequiresCollapsing() && !startCollapsingOn(e, false))
return false;
return true;
}
+LogTags *
+ICPState::loggingTags()
+{
+ // calling icpSyncAle(LOG_TAG_NONE) here would not change cache.code
+ if (!al)
+ al = new AccessLogEntry();
+ return &al->cache.code;
+}
+
void
ICPState::fillChecklist(ACLFilledChecklist &checklist) const
{
checklist.setRequest(request);
- icpSyncAle(al, from, LOG_TAG_NONE, url, 0, 0);
+ icpSyncAle(al, from, url, 0, 0);
checklist.al = al;
}
ICPState(aHeader, aRequest),rtt(0),src_rtt(0),flags(0) {}
~ICP2State();
- void created(StoreEntry * newEntry);
+ virtual void created(StoreEntry * newEntry) override;
int rtt;
int src_rtt;
debugs(12, 5, "icpHandleIcpV2: OPCODE " << icp_opcode_str[header.opcode]);
icp_opcode codeToSend;
- if (foundHit(*e)) {
+ if (confirmAndPrepHit(*e)) {
codeToSend = ICP_HIT;
} else {
#if USE_ICMP
/* End ICP2State */
-/// \ingroup ServerProtocolICPInternal2
+/// updates ALE (if any) and logs the transaction (if needed)
static void
-icpLogIcp(const Ip::Address &caddr, const LogTags &logcode, int len, const char *url, int delay, AccessLogEntry::Pointer al)
+icpLogIcp(const Ip::Address &caddr, const LogTags_ot logcode, const int len, const char *url, int delay, AccessLogEntry::Pointer &al)
{
- if (LOG_TAG_NONE == logcode.oldType)
- return;
+ assert(logcode != LOG_TAG_NONE);
- if (LOG_ICP_QUERY == logcode.oldType)
- return;
+ // Optimization: No premature (ALE creation in) icpSyncAle().
+ if (al) {
+ icpSyncAle(al, caddr, url, len, delay);
+ al->cache.code.update(logcode);
+ }
- clientdbUpdate(caddr, logcode, AnyP::PROTO_ICP, len);
+ if (logcode == LOG_ICP_QUERY)
+ return; // we never log queries
- if (!Config.onoff.log_udp)
+ if (!Config.onoff.log_udp) {
+ clientdbUpdate(caddr, al ? al->cache.code : LogTags(logcode), AnyP::PROTO_ICP, len);
return;
+ }
- icpSyncAle(al, caddr, logcode, url, len, delay);
+ if (!al) {
+ // The above attempt to optimize ALE creation has failed. We do need it.
+ icpSyncAle(al, caddr, url, len, delay);
+ al->cache.code.update(logcode);
+ }
+ clientdbUpdate(caddr, al->cache.code, AnyP::PROTO_ICP, len);
accessLogLog(al, NULL);
}
void
icpUdpSendQueue(int fd, void *)
{
- icpUdpData *q;
+ DelayedUdpSend *q;
while ((q = IcpQueueHead) != NULL) {
int delay = tvSubUsec(q->queue_time, current_time);
/* increment delay to prevent looping */
- const int x = icpUdpSend(fd, q->address, (icp_common_t *) q->msg, q->logcode, ++delay, nullptr);
+ const int x = icpUdpSend(fd, q->address, q->msg, ++delay, q->ale);
IcpQueueHead = q->next;
- xfree(q);
+ delete q;
if (x < 0)
break;
return (icp_common_t *)buf;
}
-int
+// TODO: Move retries to icpCreateAndSend(); the other caller does not retry.
+/// writes the given UDP msg to the socket; queues a retry on the first failure
+/// \returns a negative number on failures
+static int
icpUdpSend(int fd,
const Ip::Address &to,
icp_common_t * msg,
- const LogTags &logcode,
int delay,
AccessLogEntryPointer al)
{
- icpUdpData *queue;
int x;
int len;
len = (int) ntohs(msg->length);
if (x >= 0) {
/* successfully written */
+ const auto logcode = icpLogFromICPCode(static_cast<icp_opcode>(msg->opcode));
icpLogIcp(to, logcode, len, (char *) (msg + 1), delay, al);
icpCount(msg, SENT, (size_t) len, delay);
safe_free(msg);
} else if (0 == delay) {
/* send failed, but queue it */
- queue = (icpUdpData *) xcalloc(1, sizeof(icpUdpData));
+ const auto queue = new DelayedUdpSend();
queue->address = to;
queue->msg = msg;
- queue->len = (int) ntohs(msg->length);
queue->queue_time = current_time;
- queue->logcode = logcode;
+ queue->ale = al;
if (IcpQueueHead == NULL) {
IcpQueueHead = queue;
++statCounter.icp.replies_queued;
} else {
/* don't queue it */
+ // XXX: safe_free(msg)
++statCounter.icp.replies_dropped;
}
return ICP_ERR;
}
-LogTags
+static LogTags_ot
icpLogFromICPCode(icp_opcode opcode)
{
if (opcode == ICP_ERR)
if (opcode == ICP_MISS_NOFETCH)
return LOG_UDP_MISS_NOFETCH;
+ if (opcode == ICP_DECHO)
+ return LOG_ICP_QUERY;
+
+ if (opcode == ICP_QUERY)
+ return LOG_ICP_QUERY;
+
fatal("expected ICP opcode\n");
return LOG_UDP_INVALID;
void
icpCreateAndSend(icp_opcode opcode, int flags, char const *url, int reqnum, int pad, int fd, const Ip::Address &from, AccessLogEntry::Pointer al)
{
+ // update potentially shared ALE ASAP; the ICP query itself may be delayed
+ if (al)
+ al->cache.code.update(icpLogFromICPCode(opcode));
icp_common_t *reply = icp_common_t::CreateMessage(opcode, flags, url, reqnum, pad);
- icpUdpSend(fd, from, reply, icpLogFromICPCode(opcode), 0, al);
+ icpUdpSend(fd, from, reply, 0, al);
}
void
* count this DENIED query in the clientdb, even though
* we're not sending an ICP reply...
*/
- clientdbUpdate(from, LOG_UDP_DENIED, AnyP::PROTO_ICP, 0);
+ clientdbUpdate(from, LogTags(LOG_UDP_DENIED), AnyP::PROTO_ICP, 0);
} else {
icpCreateAndSend(ICP_DENIED, 0, url, reqnum, 0, fd, from, nullptr);
}
debugs(12, 5, "icpHandleIcpV3: OPCODE " << icp_opcode_str[header.opcode]);
icp_opcode codeToSend;
- if (foundHit(*e)) {
+ if (confirmAndPrepHit(*e)) {
codeToSend = ICP_HIT;
} else if (icpGetCommonOpcode() == ICP_ERR)
codeToSend = ICP_MISS;
into.lastModified(basics.lastmod);
into.swap_file_sz = basics.swap_file_sz;
into.refcount = basics.refcount;
+ const bool collapsingRequired = into.hittingRequiresCollapsing();
into.flags = basics.flags;
+ // There are possibly several flags we do not need to overwrite,
+ // and ENTRY_REQUIRES_COLLAPSING is one of them.
+ // TODO: check for other flags.
+ into.setCollapsingRequirement(collapsingRequired);
}
void
/* StoreClient API */
virtual void created(StoreEntry *);
+ virtual LogTags *loggingTags() { return nullptr; } // no access logging/ACLs
virtual void fillChecklist(ACLFilledChecklist &) const;
private:
void
MimeIcon::fillChecklist(ACLFilledChecklist &) const
{
- // Unreachable: We never call mayInitiateCollapsing() or mayCollapseOn().
+ // Unreachable: We never mayInitiateCollapsing() or startCollapsingOn().
assert(false);
}
int i;
int reqnum = 0;
int flags;
- icp_common_t *query;
int queries_sent = 0;
int peers_pinged = 0;
int parent_timeout = 0, parent_exprep = 0;
if (p->icp.port == echo_port) {
debugs(15, 4, "neighborsUdpPing: Looks like a dumb cache, send DECHO ping");
- query = icp_common_t::CreateMessage(ICP_DECHO, 0, url, reqnum, 0);
- icpUdpSend(icpOutgoingConn->fd, p->in_addr, query, LOG_ICP_QUERY, 0, nullptr);
+ // TODO: Get ALE from callback_data if possible.
+ icpCreateAndSend(ICP_DECHO, 0, url, reqnum, 0,
+ icpOutgoingConn->fd, p->in_addr, nullptr);
} else {
flags = 0;
if (p->icp.version == ICP_VERSION_2)
flags |= ICP_FLAG_SRC_RTT;
- query = icp_common_t::CreateMessage(ICP_QUERY, flags, url, reqnum, 0);
-
- icpUdpSend(icpOutgoingConn->fd, p->in_addr, query, LOG_ICP_QUERY, 0, nullptr);
+ // TODO: Get ALE from callback_data if possible.
+ icpCreateAndSend(ICP_QUERY, flags, url, reqnum, 0,
+ icpOutgoingConn->fd, p->in_addr, nullptr);
}
}
}
// APIs) to pass around a few basic data points like start_ping and ping!
CachePeer *p = (CachePeer *)data;
MemObject *mem;
- icp_common_t *query;
int reqnum;
// TODO: use class AnyP::Uri instead of constructing and re-parsing a string
LOCAL_ARRAY(char, url, MAX_URL);
mcastSetTtl(icpOutgoingConn->fd, p->mcast.ttl);
p->mcast.id = mem->id;
reqnum = icpSetCacheKey((const cache_key *)fake->key);
- query = icp_common_t::CreateMessage(ICP_QUERY, 0, url, reqnum, 0);
- icpUdpSend(icpOutgoingConn->fd, p->in_addr, query, LOG_ICP_QUERY, 0, nullptr);
+ icpCreateAndSend(ICP_QUERY, 0, url, reqnum, 0,
+ icpOutgoingConn->fd, p->in_addr, psstate->al);
fake->ping_status = PING_WAITING;
eventAdd("peerCountMcastPeersDone",
peerCountMcastPeersDone,
old_e = fetch->old_entry = storeGetPublicByRequest(req);
+ // XXX: Missing a hittingRequiresCollapsing() && startCollapsingOn() check.
if (old_e) {
debugs(72, 5, "found old " << *old_e);
++ refreshCounts[rcHTTP].total;
++ refreshCounts[rcHTTP].status[reason];
request->flags.staleIfHit = refreshIsStaleIfHit(reason);
+ // TODO: Treat collapsed responses as fresh but second-hand.
return (Config.onoff.offline || reason < 200) ? 0 : 1;
}
shareableWhenPrivate = false; // may already be false
if (EBIT_TEST(flags, RELEASE_REQUEST))
return;
-
setPrivateKey(shareable, true);
}
/* TODO: when we store headers separately remove the header portion */
/* TODO: mark the length of the headers ? */
/* We ONLY want the headers */
-
assert (isEmpty());
assert(mem_obj);
rep->body.packInto(this);
flush();
+
+ // The entry headers are written, new clients
+ // should not collapse anymore.
+ if (hittingRequiresCollapsing()) {
+ setCollapsingRequirement(false);
+ Store::Root().transientsClearCollapsingRequirement(*this);
+ }
}
char const *
return buf;
}
-bool
-StoreEntry::collapsingInitiator() const
+void
+StoreEntry::setCollapsingRequirement(const bool required)
{
- if (!publicKey())
- return false;
- return EBIT_TEST(flags, ENTRY_FWD_HDR_WAIT) ||
- (hasTransients() && !hasMemStore() && !hasDisk());
+ if (required)
+ EBIT_SET(flags, ENTRY_REQUIRES_COLLAPSING);
+ else
+ EBIT_CLR(flags, ENTRY_REQUIRES_COLLAPSING);
}
static std::ostream &
transients->disconnect(e);
}
+void
+Store::Controller::transientsClearCollapsingRequirement(StoreEntry &e)
+{
+ if (transients)
+ transients->clearCollapsingRequirement(e);
+}
+
void
Store::Controller::handleIdleEntry(StoreEntry &e)
{
const HttpRequestMethod &reqMethod)
{
const KeyScope keyScope = reqFlags.refresh ? ksRevalidation : ksDefault;
+ // set the flag now so that it gets copied into the Transients entry
+ e->setCollapsingRequirement(true);
if (e->makePublic(keyScope)) { // this is needed for both local and SMP collapsing
debugs(20, 3, "may " << (transients && e->hasTransients() ?
"SMP-" : "locally-") << "collapse " << *e);
return true;
}
+ // paranoid cleanup; the flag is meaningless for private entries
+ e->setCollapsingRequirement(false);
return false;
}
debugs(20, 7, "syncing " << *collapsed);
- bool abortedByWriter = false;
- bool waitingToBeFreed = false;
- transients->status(*collapsed, abortedByWriter, waitingToBeFreed);
+ Transients::EntryStatus entryStatus;
+ transients->status(*collapsed, entryStatus);
- if (waitingToBeFreed) {
+ if (!entryStatus.collapsed) {
+ debugs(20, 5, "removing collapsing requirement for " << *collapsed << " since remote writer probably got headers");
+ collapsed->setCollapsingRequirement(false);
+ }
+
+ if (entryStatus.waitingToBeFreed) {
debugs(20, 3, "will release " << *collapsed << " due to waitingToBeFreed");
collapsed->release(true); // may already be marked
}
assert(transients->isReader(*collapsed));
- if (abortedByWriter) {
+ if (entryStatus.abortedByWriter) {
debugs(20, 3, "aborting " << *collapsed << " because its writer has aborted");
collapsed->abort();
return;
}
+ if (entryStatus.collapsed && !collapsed->hittingRequiresCollapsing()) {
+ debugs(20, 3, "aborting " << *collapsed << " due to writer/reader collapsing state mismatch");
+ collapsed->abort();
+ return;
+ }
+
bool found = false;
bool inSync = false;
if (memStore && collapsed->mem_obj->memCache.io == MemObject::ioDone) {
found = anchorToCache(*collapsed, inSync);
}
- if (waitingToBeFreed && !found) {
+ if (entryStatus.waitingToBeFreed && !found) {
debugs(20, 3, "aborting unattached " << *collapsed <<
" because it was marked for deletion before we could attach it");
collapsed->abort();
/// disassociates the entry from the intransit table
void transientsDisconnect(StoreEntry &);
+ /// removes collapsing requirement (for future hits)
+ void transientsClearCollapsingRequirement(StoreEntry &e);
+
/// disassociates the entry from the memory cache, preserving cached data
void memoryDisconnect(StoreEntry &);
}
bool
-StoreClient::mayCollapseOn(const StoreEntry &e) const
+StoreClient::startCollapsingOn(const StoreEntry &e, const bool doingRevalidation)
{
- assert(e.collapsingInitiator()); // our result is not meaningful for regular hits
- return onCollapsingPath();
+ if (!e.hittingRequiresCollapsing())
+ return false; // collapsing is impossible due to the entry state
+
+ if (!onCollapsingPath())
+ return false; // collapsing is impossible due to Squid configuration
+
+ /* collapsing is possible; the caller must collapse */
+
+ if (const auto tags = loggingTags()) {
+ if (doingRevalidation)
+ tags->collapsingHistory.revalidationCollapses++;
+ else
+ tags->collapsingHistory.otherCollapses++;
+ }
+
+ debugs(85, 5, e << " doingRevalidation=" << doingRevalidation);
+ return true;
}
void
StoreClient::fillChecklist(ACLFilledChecklist &checklist) const
{
// TODO: Consider moving all CF-related methods into a new dedicated class.
- Must(!"mayCollapse() caller must override fillChecklist()");
+ Must(!"startCollapsingOn() caller must override fillChecklist()");
}
/* store_client */
icp_opcode icp_common_t::getOpCode() const STUB_RETVAL(ICP_INVALID)
ICPState::ICPState(icp_common_t &aHeader, HttpRequest *aRequest) STUB
ICPState::~ICPState() STUB
-bool ICPState::foundHit(const StoreEntry &) const STUB_RETVAL(false)
+bool ICPState::confirmAndPrepHit(const StoreEntry &) STUB_RETVAL(false)
+LogTags *ICPState::loggingTags() STUB_RETVAL(nullptr)
void ICPState::fillChecklist(ACLFilledChecklist&) const STUB
Comm::ConnectionPointer icpIncomingConn;
bool icpAccessAllowed(Ip::Address &from, HttpRequest * icp_request) STUB_RETVAL(false)
void icpCreateAndSend(icp_opcode, int flags, char const *url, int reqnum, int pad, int fd, const Ip::Address &from, AccessLogEntryPointer) STUB
icp_opcode icpGetCommonOpcode() STUB_RETVAL(ICP_INVALID)
-int icpUdpSend(int, const Ip::Address &, icp_common_t *, LogTags, int, AccessLogEntryPointer) STUB_RETVAL(0)
-LogTags icpLogFromICPCode(icp_opcode opcode) STUB_RETVAL(LOG_TAG_NONE)
void icpDenyAccess(Ip::Address &from, char *url, int reqnum, int fd) STUB
void icpHandleIcpV3(int, Ip::Address &, char *, int) STUB
void icpConnectionsOpen(void) STUB
void StoreEntry::release(const bool shareable) STUB
void StoreEntry::append(char const *, int) STUB
void StoreEntry::vappendf(const char *, va_list) STUB
+void StoreEntry::setCollapsingRequirement(const bool required) STUB
NullStoreEntry *NullStoreEntry::getInstance() STUB_RETVAL(NULL)
const char *NullStoreEntry::getMD5Text() const STUB_RETVAL(NULL)
server.len = 0;
if (logTag_ptr)
- *logTag_ptr = LOG_TCP_TUNNEL;
+ logTag_ptr->update(LOG_TCP_TUNNEL);
if (!clientExpectsConnectResponse()) {
// closing the connection is the best we can do here
assert(!tunnelState->waitingForConnectExchange());
*tunnelState->status_ptr = Http::scOkay;
if (tunnelState->logTag_ptr)
- *tunnelState->logTag_ptr = LOG_TCP_TUNNEL;
+ tunnelState->logTag_ptr->update(LOG_TCP_TUNNEL);
if (cbdataReferenceValid(tunnelState)) {
// Shovel any payload already pushed into reply buffer by the server response
/* DEBUG: section 52 URN Parsing */
#include "squid.h"
+#include "AccessLogEntry.h"
#include "acl/FilledChecklist.h"
#include "cbdata.h"
#include "errorpage.h"
CBDATA_CLASS(UrnState);
public:
+ explicit UrnState(const AccessLogEntry::Pointer &anAle): ale(anAle) {}
+
void created (StoreEntry *newEntry);
void start (HttpRequest *, StoreEntry *);
char *getHost(const SBuf &urlpath);
virtual ~UrnState();
- StoreEntry *entry;
- store_client *sc;
- StoreEntry *urlres_e;
+ StoreEntry *entry = nullptr;
+ store_client *sc = nullptr;
+ StoreEntry *urlres_e = nullptr;
HttpRequest::Pointer request;
HttpRequest::Pointer urlres_r;
struct {
- bool force_menu;
+ bool force_menu = false;
} flags;
- char reqbuf[URN_REQBUF_SZ];
- int reqofs;
+ char reqbuf[URN_REQBUF_SZ] = { '\0' };
+ int reqofs = 0;
private:
/* StoreClient API */
+ virtual LogTags *loggingTags() { return ale ? &ale->cache.code : nullptr; }
virtual void fillChecklist(ACLFilledChecklist &) const;
- char *urlres;
+ char *urlres = nullptr;
+ AccessLogEntry::Pointer ale; ///< master transaction summary
};
typedef struct {
UrnState::fillChecklist(ACLFilledChecklist &checklist) const
{
checklist.setRequest(request.getRaw());
+ checklist.al = ale;
}
void
UrnState::created(StoreEntry *e)
{
- if (e->isNull() || (e->collapsingInitiator() && !mayCollapseOn(*e))) {
+ if (e->isNull() || (e->hittingRequiresCollapsing() && !startCollapsingOn(*e, false))) {
urlres_e = storeCreateEntry(urlres, urlres, RequestFlags(), Http::METHOD_GET);
sc = storeClientListAdd(urlres_e, this);
- FwdState::fwdStart(Comm::ConnectionPointer(), urlres_e, urlres_r.getRaw());
+ FwdState::Start(Comm::ConnectionPointer(), urlres_e, urlres_r.getRaw(), ale);
// TODO: StoreClients must either store/lock or abandon found entries.
//if (!e->isNull())
// e->abandon();
}
void
-urnStart(HttpRequest * r, StoreEntry * e)
+urnStart(HttpRequest *r, StoreEntry *e, const AccessLogEntryPointer &ale)
{
- UrnState *anUrn = new UrnState();
+ const auto anUrn = new UrnState(ale);
anUrn->start (r, e);
}
#ifndef SQUID_URN_H_
#define SQUID_URN_H_
+class AccessLogEntry;
class HttpRequest;
class StoreEntry;
-void urnStart(HttpRequest *, StoreEntry *);
+template <class C> class RefCount;
+typedef RefCount<AccessLogEntry> AccessLogEntryPointer;
+
+void urnStart(HttpRequest *, StoreEntry *, const AccessLogEntryPointer &ale);
#endif /* SQUID_URN_H_ */