-
/*
- * $Id: peer_select.cc,v 1.129 2003/02/21 22:50:10 robertc Exp $
+ * $Id$
*
* DEBUG: section 44 Peer Selection Algorithm
* AUTHOR: Duane Wessels
* 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.
*/
#include "squid.h"
+#include "event.h"
+#include "PeerSelectState.h"
#include "Store.h"
#include "ICP.h"
#include "HttpRequest.h"
#include "ACLChecklist.h"
-
-const char *hier_strings[] =
- {
- "NONE",
- "DIRECT",
- "SIBLING_HIT",
- "PARENT_HIT",
- "DEFAULT_PARENT",
- "SINGLE_PARENT",
- "FIRST_UP_PARENT",
- "FIRST_PARENT_MISS",
- "CLOSEST_PARENT_MISS",
- "CLOSEST_PARENT",
- "CLOSEST_DIRECT",
- "NO_DIRECT_FAIL",
- "SOURCE_FASTEST",
- "ROUNDROBIN_PARENT",
+#include "htcp.h"
+#include "forward.h"
+#include "SquidTime.h"
+#include "icmp/net_db.h"
+
+const char *hier_strings[] = {
+ "NONE",
+ "DIRECT",
+ "SIBLING_HIT",
+ "PARENT_HIT",
+ "DEFAULT_PARENT",
+ "SINGLE_PARENT",
+ "FIRST_UP_PARENT",
+ "FIRST_PARENT_MISS",
+ "CLOSEST_PARENT_MISS",
+ "CLOSEST_PARENT",
+ "CLOSEST_DIRECT",
+ "NO_DIRECT_FAIL",
+ "SOURCE_FASTEST",
+ "ROUNDROBIN_PARENT",
#if USE_CACHE_DIGESTS
- "CD_PARENT_HIT",
- "CD_SIBLING_HIT",
+ "CD_PARENT_HIT",
+ "CD_SIBLING_HIT",
#endif
-#if USE_CARP
- "CARP",
-#endif
- "ANY_PARENT",
- "INVALID CODE"
- };
-
-static struct
-{
+ "CARP",
+ "ANY_PARENT",
+ "USERHASH",
+ "SOURCEHASH",
+ "INVALID CODE"
+};
+
+static struct {
int timeouts;
-}
-
-PeerStats;
+} PeerStats;
-static const char *DirectStr[] =
- {
- "DIRECT_UNKNOWN",
- "DIRECT_NO",
- "DIRECT_MAYBE",
- "DIRECT_YES"
- };
+static const char *DirectStr[] = {
+ "DIRECT_UNKNOWN",
+ "DIRECT_NO",
+ "DIRECT_MAYBE",
+ "DIRECT_YES"
+};
static void peerSelectFoo(ps_state *);
static void peerPingTimeout(void *data);
static void peerGetSomeParent(ps_state *);
static void peerGetAllParents(ps_state *);
static void peerAddFwdServer(FwdServer **, peer *, hier_code);
+static void peerSelectPinned(ps_state * ps);
+
+CBDATA_CLASS_INIT(ps_state);
static void
peerSelectStateFree(ps_state * psstate)
{
if (psstate->acl_checklist) {
- debug(44, 1) ("calling aclChecklistFree() from peerSelectStateFree\n");
+ debugs(44, 1, "calling aclChecklistFree() from peerSelectStateFree");
delete (psstate->acl_checklist);
}
- requestUnlink(psstate->request);
- psstate->request = NULL;
+ HTTPMSGUNLOCK(psstate->request);
if (psstate->entry) {
assert(psstate->entry->ping_status != PING_WAITING);
- storeUnlockObject(psstate->entry);
+ psstate->entry->unlock();
psstate->entry = NULL;
}
}
static int
-peerSelectIcpPing(request_t * request, int direct, StoreEntry * entry)
+peerSelectIcpPing(HttpRequest * request, int direct, StoreEntry * entry)
{
int n;
assert(entry);
assert(entry->ping_status == PING_NONE);
assert(direct != DIRECT_YES);
- debug(44, 3) ("peerSelectIcpPing: %s\n", storeUrl(entry));
+ debugs(44, 3, "peerSelectIcpPing: " << entry->url() );
if (!request->flags.hierarchical && direct != DIRECT_NO)
return 0;
n = neighborsCount(request);
- debug(44, 3) ("peerSelectIcpPing: counted %d neighbors\n", n);
+ debugs(44, 3, "peerSelectIcpPing: counted " << n << " neighbors");
return n;
}
void
-peerSelect(request_t * request,
+peerSelect(HttpRequest * request,
StoreEntry * entry,
PSC * callback,
void *callback_data)
ps_state *psstate;
if (entry)
- debug(44, 3) ("peerSelect: %s\n", storeUrl(entry));
+ debugs(44, 3, "peerSelect: " << entry->url() );
else
- debug(44, 3) ("peerSelect: %s\n", RequestMethodStr[request->method]);
+ debugs(44, 3, "peerSelect: " << RequestMethodStr(request->method));
- psstate = cbdataAlloc(ps_state);
+ psstate = new ps_state;
- psstate->request = requestLink(request);
+ psstate->request = HTTPMSGLOCK(request);
psstate->entry = entry;
#endif
if (psstate->entry)
- storeLockObject(psstate->entry);
+ psstate->entry->lock();
peerSelectFoo(psstate);
}
{
ps_state *psstate = (ps_state *) data;
psstate->acl_checklist = NULL;
- debug(44, 3) ("peerCheckNeverDirectDone: %d\n", answer);
+ debugs(44, 3, "peerCheckNeverDirectDone: " << answer);
psstate->never_direct = answer ? 1 : -1;
peerSelectFoo(psstate);
}
{
ps_state *psstate = (ps_state *)data;
psstate->acl_checklist = NULL;
- debug(44, 3) ("peerCheckAlwaysDirectDone: %d\n", answer);
+ debugs(44, 3, "peerCheckAlwaysDirectDone: " << answer);
psstate->always_direct = answer ? 1 : -1;
peerSelectFoo(psstate);
}
void *cbdata;
if (entry) {
- debug(44, 3) ("peerSelectCallback: %s\n", storeUrl(entry));
+ debugs(44, 3, "peerSelectCallback: " << entry->url() );
if (entry->ping_status == PING_WAITING)
eventDelete(peerPingTimeout, psstate);
}
if (fs == NULL) {
- debug(44, 1) ("Failed to select source for '%s'\n", storeUrl(entry));
- debug(44, 1) (" always_direct = %d\n", psstate->always_direct);
- debug(44, 1) (" never_direct = %d\n", psstate->never_direct);
- debug(44, 1) (" timedout = %d\n", psstate->ping.timedout);
+ debugs(44, 1, "Failed to select source for '" << entry->url() << "'" );
+ debugs(44, 1, " always_direct = " << psstate->always_direct );
+ debugs(44, 1, " never_direct = " << psstate->never_direct );
+ debugs(44, 1, " timedout = " << psstate->ping.timedout );
}
psstate->ping.stop = current_time;
static int
peerCheckNetdbDirect(ps_state * psstate)
{
+#if USE_ICMP
peer *p;
int myrtt;
int myhops;
if (psstate->direct == DIRECT_NO)
return 0;
- myrtt = netdbHostRtt(psstate->request->host);
+ /* base lookup on RTT and Hops if ICMP NetDB is enabled. */
- debug(44, 3) ("peerCheckNetdbDirect: MY RTT = %d msec\n", myrtt);
+ myrtt = netdbHostRtt(psstate->request->GetHost());
- debug(44, 3) ("peerCheckNetdbDirect: minimum_direct_rtt = %d msec\n",
- Config.minDirectRtt);
+ debugs(44, 3, "peerCheckNetdbDirect: MY RTT = " << myrtt << " msec");
+ debugs(44, 3, "peerCheckNetdbDirect: minimum_direct_rtt = " << Config.minDirectRtt << " msec");
if (myrtt && myrtt <= Config.minDirectRtt)
return 1;
- myhops = netdbHostHops(psstate->request->host);
+ myhops = netdbHostHops(psstate->request->GetHost());
- debug(44, 3) ("peerCheckNetdbDirect: MY hops = %d\n", myhops);
-
- debug(44, 3) ("peerCheckNetdbDirect: minimum_direct_hops = %d\n",
- Config.minDirectHops);
+ debugs(44, 3, "peerCheckNetdbDirect: MY hops = " << myhops);
+ debugs(44, 3, "peerCheckNetdbDirect: minimum_direct_hops = " << Config.minDirectHops);
if (myhops && myhops <= Config.minDirectHops)
return 1;
- p = whichPeer(&psstate->closest_parent_miss);
+ p = whichPeer(psstate->closest_parent_miss);
if (p == NULL)
return 0;
- debug(44, 3) ("peerCheckNetdbDirect: closest_parent_miss RTT = %d msec\n",
- psstate->ping.p_rtt);
+ debugs(44, 3, "peerCheckNetdbDirect: closest_parent_miss RTT = " << psstate->ping.p_rtt << " msec");
if (myrtt && myrtt <= psstate->ping.p_rtt)
return 1;
+#endif /* USE_ICMP */
+
return 0;
}
peerSelectFoo(ps_state * ps)
{
StoreEntry *entry = ps->entry;
- request_t *request = ps->request;
- debug(44, 3) ("peerSelectFoo: '%s %s'\n",
- RequestMethodStr[request->method],
- request->host);
+ HttpRequest *request = ps->request;
+ debugs(44, 3, "peerSelectFoo: '" << RequestMethodStr(request->method) << " " << request->GetHost() << "'");
if (ps->direct == DIRECT_UNKNOWN) {
if (ps->always_direct == 0 && Config.accessList.AlwaysDirect) {
ps->direct = DIRECT_MAYBE;
}
- debug(44, 3) ("peerSelectFoo: direct = %s\n",
- DirectStr[ps->direct]);
+ debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct]);
}
+ if (!entry || entry->ping_status == PING_NONE)
+ peerSelectPinned(ps);
if (entry == NULL) {
(void) 0;
} else if (entry->ping_status == PING_NONE) {
peerSelectCallback(ps);
}
+/*
+ * peerSelectPinned
+ *
+ * Selects a pinned connection
+ */
+int peerAllowedToUse(const peer * p, HttpRequest * request);
+static void
+peerSelectPinned(ps_state * ps)
+{
+ HttpRequest *request = ps->request;
+ peer *peer;
+ if (!request->pinnedConnection())
+ return;
+ if (request->pinnedConnection()->validatePinnedConnection(request) != -1) {
+ peer = request->pinnedConnection()->pinnedPeer();
+ if (peer && peerAllowedToUse(peer, request)) {
+ peerAddFwdServer(&ps->servers, peer, PINNED);
+ if (ps->entry)
+ ps->entry->ping_status = PING_DONE; /* Skip ICP */
+ } else if (!peer && ps->direct != DIRECT_NO) {
+ peerAddFwdServer(&ps->servers, NULL, PINNED);
+ if (ps->entry)
+ ps->entry->ping_status = PING_DONE; /* Skip ICP */
+ }
+ }
+}
+
/*
* peerGetSomeNeighbor
- *
+ *
* Selects a neighbor (parent or sibling) based on one of the
* following methods:
* Cache Digests
* CARP
- * Netdb RTT estimates
+ * ICMP Netdb RTT estimates
* ICP/HTCP queries
*/
static void
peerGetSomeNeighbor(ps_state * ps)
{
StoreEntry *entry = ps->entry;
- request_t *request = ps->request;
+ HttpRequest *request = ps->request;
peer *p;
hier_code code = HIER_NONE;
assert(entry->ping_status == PING_NONE);
if ((p = netdbClosestParent(request))) {
code = CLOSEST_PARENT;
} else if (peerSelectIcpPing(request, ps->direct, entry)) {
- debug(44, 3) ("peerSelect: Doing ICP pings\n");
+ debugs(44, 3, "peerSelect: Doing ICP pings");
ps->ping.start = current_time;
ps->ping.n_sent = neighborsUdpPing(request,
entry,
&ps->ping.timeout);
if (ps->ping.n_sent == 0)
- debug(44, 0) ("WARNING: neighborsUdpPing returned 0\n");
+ debugs(44, 0, "WARNING: neighborsUdpPing returned 0");
+ debugs(44, 3, "peerSelect: " << ps->ping.n_replies_expected <<
+ " ICP replies expected, RTT " << ps->ping.timeout <<
+ " msec");
- debug(44, 3) ("peerSelect: %d ICP replies expected, RTT %d msec\n",
- ps->ping.n_replies_expected, ps->ping.timeout);
if (ps->ping.n_replies_expected > 0) {
entry->ping_status = PING_WAITING;
if (code != HIER_NONE) {
assert(p);
- debug(44, 3) ("peerSelect: %s/%s\n", hier_strings[code], p->host);
+ debugs(44, 3, "peerSelect: " << hier_strings[code] << "/" << p->host);
peerAddFwdServer(&ps->servers, p, code);
}
/*
* peerGetSomeNeighborReplies
- *
+ *
* Selects a neighbor (parent or sibling) based on ICP/HTCP replies.
*/
static void
peerGetSomeNeighborReplies(ps_state * ps)
{
- request_t *request = ps->request;
+ HttpRequest *request = ps->request;
peer *p = NULL;
hier_code code = HIER_NONE;
assert(ps->entry->ping_status == PING_WAITING);
if (peerCheckNetdbDirect(ps)) {
code = CLOSEST_DIRECT;
- debug(44, 3) ("peerSelect: %s/%s\n", hier_strings[code], request->host);
+ debugs(44, 3, "peerSelect: " << hier_strings[code] << "/" << request->GetHost());
peerAddFwdServer(&ps->servers, NULL, code);
return;
}
if ((p = ps->hit)) {
code = ps->hit_type == PEER_PARENT ? PARENT_HIT : SIBLING_HIT;
- } else
-#if ALLOW_SOURCE_PING
- if ((p = ps->secho)) {
- code = SOURCE_FASTEST;
- } else
-#endif
- if (ps->closest_parent_miss.sin_addr.s_addr != any_addr.s_addr) {
- p = whichPeer(&ps->closest_parent_miss);
- code = CLOSEST_PARENT_MISS;
- } else if (ps->first_parent_miss.sin_addr.s_addr != any_addr.s_addr) {
- p = whichPeer(&ps->first_parent_miss);
- code = FIRST_PARENT_MISS;
- }
-
+ } else {
+ if (!ps->closest_parent_miss.IsAnyAddr()) {
+ p = whichPeer(ps->closest_parent_miss);
+ code = CLOSEST_PARENT_MISS;
+ } else if (!ps->first_parent_miss.IsAnyAddr()) {
+ p = whichPeer(ps->first_parent_miss);
+ code = FIRST_PARENT_MISS;
+ }
+ }
if (p && code != HIER_NONE) {
- debug(44, 3) ("peerSelect: %s/%s\n", hier_strings[code], p->host);
+ debugs(44, 3, "peerSelect: " << hier_strings[code] << "/" << p->host);
peerAddFwdServer(&ps->servers, p, code);
}
}
/*
* peerGetSomeDirect
- *
+ *
* Simply adds a 'direct' entry to the FwdServers list if this
* request can be forwarded directly to the origin server
*/
if (ps->direct == DIRECT_NO)
return;
+ /* WAIS is not implemented natively */
if (ps->request->protocol == PROTO_WAIS)
- /* Its not really DIRECT, now is it? */
- peerAddFwdServer(&ps->servers, Config.Wais._peer, DIRECT);
- else
- peerAddFwdServer(&ps->servers, NULL, DIRECT);
+ return;
+
+ peerAddFwdServer(&ps->servers, NULL, HIER_DIRECT);
}
static void
peerGetSomeParent(ps_state * ps)
{
peer *p;
- request_t *request = ps->request;
+ HttpRequest *request = ps->request;
hier_code code = HIER_NONE;
- debug(44, 3) ("peerGetSomeParent: %s %s\n",
- RequestMethodStr[request->method],
- request->host);
+ debugs(44, 3, "peerGetSomeParent: " << RequestMethodStr(request->method) << " " << request->GetHost());
if (ps->direct == DIRECT_YES)
return;
if ((p = getDefaultParent(request))) {
code = DEFAULT_PARENT;
-#if USE_CARP
-
+ } else if ((p = peerUserHashSelectParent(request))) {
+ code = USERHASH_PARENT;
+ } else if ((p = peerSourceHashSelectParent(request))) {
+ code = SOURCEHASH_PARENT;
} else if ((p = carpSelectParent(request))) {
code = CARP;
-#endif
-
} else if ((p = getRoundRobinParent(request))) {
code = ROUNDROBIN_PARENT;
} else if ((p = getWeightedRoundRobinParent(request))) {
}
if (code != HIER_NONE) {
- debug(44, 3) ("peerSelect: %s/%s\n", hier_strings[code], p->host);
+ debugs(44, 3, "peerSelect: " << hier_strings[code] << "/" << p->host);
peerAddFwdServer(&ps->servers, p, code);
}
}
peerGetAllParents(ps_state * ps)
{
peer *p;
- request_t *request = ps->request;
+ HttpRequest *request = ps->request;
/* Add all alive parents */
for (p = Config.peers; p; p = p->next) {
if (!peerHTTPOkay(p, request))
continue;
- debug(15, 3) ("peerGetAllParents: adding alive parent %s\n", p->host);
+ debugs(15, 3, "peerGetAllParents: adding alive parent " << p->host);
peerAddFwdServer(&ps->servers, p, ANY_OLD_PARENT);
}
StoreEntry *entry = psstate->entry;
if (entry)
- debug(44, 3) ("peerPingTimeout: '%s'\n", storeUrl(entry));
+ debugs(44, 3, "peerPingTimeout: '" << entry->url() << "'" );
if (!cbdataReferenceValid(psstate->callback_data)) {
/* request aborted */
peerIcpParentMiss(peer * p, icp_common_t * header, ps_state * ps)
{
int rtt;
- int hops;
+#if USE_ICMP
if (Config.onoff.query_icmp) {
if (header->flags & ICP_FLAG_SRC_RTT) {
rtt = header->pad & 0xFFFF;
- hops = (header->pad >> 16) & 0xFFFF;
+ int hops = (header->pad >> 16) & 0xFFFF;
if (rtt > 0 && rtt < 0xFFFF)
netdbUpdatePeer(ps->request, p, rtt, hops);
}
}
}
+#endif /* USE_ICMP */
/* if closest-only is set, then don't allow FIRST_PARENT_MISS */
if (p->options.closest_only)
return;
/* set FIRST_MISS if there is no CLOSEST parent */
- if (ps->closest_parent_miss.sin_addr.s_addr != any_addr.s_addr)
+ if (!ps->closest_parent_miss.IsAnyAddr())
return;
rtt = (tvSubMsec(ps->ping.start, current_time) - p->basetime) / p->weight;
if (rtt < 1)
rtt = 1;
- if (ps->first_parent_miss.sin_addr.s_addr == any_addr.s_addr ||
- rtt < ps->ping.w_rtt) {
+ if (ps->first_parent_miss.IsAnyAddr() || rtt < ps->ping.w_rtt) {
ps->first_parent_miss = p->in_addr;
ps->ping.w_rtt = rtt;
}
{
ps_state *psstate = (ps_state *)data;
icp_opcode op = header->getOpCode();
- debug(44, 3) ("peerHandleIcpReply: %s %s\n",
- icp_opcode_str[op],
- storeUrl(psstate->entry));
+ debugs(44, 3, "peerHandleIcpReply: " << icp_opcode_str[op] << " " << psstate->entry->url() );
#if USE_CACHE_DIGESTS && 0
/* do cd lookup to count false misses */
return;
}
-#if ALLOW_SOURCE_PING
- else if (op == ICP_SECHO) {
- psstate->secho = p;
- peerSelectFoo(psstate);
- return;
- }
-
-#endif
if (psstate->ping.n_recv < psstate->ping.n_replies_expected)
return;
peerHandleHtcpReply(peer * p, peer_t type, htcpReplyData * htcp, void *data)
{
ps_state *psstate = (ps_state *)data;
- debug(44, 3) ("peerHandleIcpReply: %s %s\n",
- htcp->hit ? "HIT" : "MISS",
- storeUrl(psstate->entry));
+ debugs(44, 3, "peerHandleHtcpReply: " <<
+ (htcp->hit ? "HIT" : "MISS") << " " <<
+ psstate->entry->url() );
psstate->ping.n_recv++;
if (htcp->hit) {
peerHtcpParentMiss(peer * p, htcpReplyData * htcp, ps_state * ps)
{
int rtt;
- int hops;
+#if USE_ICMP
if (Config.onoff.query_icmp) {
if (htcp->cto.rtt > 0) {
rtt = (int) htcp->cto.rtt * 1000;
- hops = (int) htcp->cto.hops * 1000;
+ int hops = (int) htcp->cto.hops * 1000;
netdbUpdatePeer(ps->request, p, rtt, hops);
if (rtt && (ps->ping.p_rtt == 0 || rtt < ps->ping.p_rtt)) {
}
}
}
+#endif /* USE_ICMP */
/* if closest-only is set, then don't allow FIRST_PARENT_MISS */
if (p->options.closest_only)
return;
/* set FIRST_MISS if there is no CLOSEST parent */
- if (ps->closest_parent_miss.sin_addr.s_addr != any_addr.s_addr)
+ if (!ps->closest_parent_miss.IsAnyAddr())
return;
rtt = (tvSubMsec(ps->ping.start, current_time) - p->basetime) / p->weight;
if (rtt < 1)
rtt = 1;
- if (ps->first_parent_miss.sin_addr.s_addr == any_addr.s_addr ||
- rtt < ps->ping.w_rtt) {
+ if (ps->first_parent_miss.IsAnyAddr() || rtt < ps->ping.w_rtt) {
ps->first_parent_miss = p->in_addr;
ps->ping.w_rtt = rtt;
}
#endif
else
- debug(44, 1) ("peerHandlePingReply: unknown protocol_t %d\n", (int) proto);
+ debugs(44, 1, "peerHandlePingReply: unknown protocol_t " << proto);
}
static void
-peerAddFwdServer(FwdServer ** FS, peer * p, hier_code code)
+peerAddFwdServer(FwdServer ** FSVR, peer * p, hier_code code)
{
FwdServer *fs = (FwdServer *)memAllocate(MEM_FWD_SERVER);
- debug(44, 5) ("peerAddFwdServer: adding %s %s\n",
- p ? p->host : "DIRECT",
- hier_strings[code]);
+ debugs(44, 5, "peerAddFwdServer: adding " <<
+ (p ? p->host : "DIRECT") << " " <<
+ hier_strings[code] );
fs->_peer = cbdataReference(p);
fs->code = code;
- while (*FS)
- FS = &(*FS)->next;
+ while (*FSVR)
+ FSVR = &(*FSVR)->next;
+
+ *FSVR = fs;
+}
+
+void *
+ps_state::operator new(size_t)
+{
+ CBDATA_INIT_TYPE(ps_state);
+ return cbdataAlloc(ps_state);
+}
+
+ps_state::ps_state() : request (NULL),
+ entry (NULL),
+ always_direct (0),
+ never_direct (0),
+ direct (0),
+ callback (NULL),
+ callback_data (NULL),
+ servers (NULL),
+ first_parent_miss(),
+ closest_parent_miss(),
+ hit(NULL),
+ hit_type(PEER_NONE),
+ acl_checklist (NULL)
+{
+ ; // no local defaults.
+}
- *FS = fs;
+ping_data::ping_data() :
+ n_sent(0),
+ n_recv(0),
+ n_replies_expected(0),
+ timeout(0),
+ timedout(0),
+ w_rtt(0),
+ p_rtt(0)
+{
+ start.tv_sec = 0;
+ start.tv_usec = 0;
+ stop.tv_sec = 0;
+ stop.tv_usec = 0;
}