Hi all,
  attached is a first revision of the patch for review. I've build-
and run- tested it.
Only issue not fully investigated is that when an user login is
specified in the request, carpSelectParent is not invoked at all. This
is however external from the carp code itself; only effect it has is
that the "login" carp key selector is actually useless.

On Mon, Jul 18, 2011 at 2:01 PM, Amos Jeffries <[email protected]> wrote:
> On 18/07/11 22:27, Kinkie wrote:
>>
>> Hi guys,
>>   I would like some input on how to best tweak CARP with a very small
>> change:
>> as it is now the canonical URL is used to define the hash for next parent.
>> It is very efficient - even too much.
>>
>> I'd like to add a variant to it so that only the host part of the URL
>> is used as initial key for the hashing.
>> This will decrease the effectiveness of the balancing, but make it
>> easier to cope with some websites which do not like it when requests
>> travel across different routes.
>>
>> There are three ways I can think of to address this:
>> - change the way CARP works - it's an one-liner
>> - new global setting
>> - new carp option (e.g. hcarp or host-carp)
>>
>> What do you think is the best way to implement this?
>
> I think a slightly more flexible way would be a carp-key=XX parameter. Where
> one can potentially set (scheme,host,port,path,params) tags. That would
> allow a bit more tuning of the mask.
>
> Amos
> --
> Please be using
>  Current Stable Squid 2.7.STABLE9 or 3.1.14
>  Beta testers wanted for 3.2.0.9
>



-- 
    /kinkie
=== modified file 'src/cache_cf.cc'
--- src/cache_cf.cc	2011-07-14 08:59:10 +0000
+++ src/cache_cf.cc	2011-07-30 18:08:42 +0000
@@ -2244,6 +2244,33 @@
                 fatalf("parse_peer: non-parent carp peer %s/%d\n", p->host, p->http_port);
 
             p->options.carp = 1;
+        } else if (!strncasecmp(token, "carp-key=", strlen("carp-key="))) {
+            if (p->options.carp != 1)
+                fatalf("parse_peer: carp-key specified on non-carp peer %s/%d\n", p->host, p->http_port);
+            p->options.carp_key.set=1;
+            char *tmp=xstrdup(token+strlen("carp-key="));
+            char *key,*nextkey;
+            for (key = nextkey = tmp; key; key=nextkey) {
+            	nextkey=strchr(key,',');
+            	if (nextkey)
+            		*nextkey++='\0';
+            	if (!strcasecmp(key,"scheme")) {
+            		p->options.carp_key.scheme=1;
+            	} else if (!strcasecmp(key,"login")) {
+            		p->options.carp_key.login=1;
+            	} else if (!strcasecmp(key,"host")) {
+            		p->options.carp_key.host=1;
+            	} else if (!strcasecmp(key,"port")) {
+            		p->options.carp_key.port=1;
+            	} else if (!strcasecmp(key,"path")) {
+            		p->options.carp_key.path=1;
+            	} else if (!strcasecmp(key,"params")) {
+            		p->options.carp_key.params=1;
+            	} else {
+            		fatalf("invalid carp key '%s'",key);
+            	}
+            }
+            safe_free(tmp);
         } else if (!strcasecmp(token, "userhash")) {
 #if USE_AUTH
             if (p->type != PEER_PARENT)

=== modified file 'src/carp.cc'
--- src/carp.cc	2010-10-28 18:52:59 +0000
+++ src/carp.cc	2011-07-30 19:15:00 +0000
@@ -35,8 +35,10 @@
  */
 
 #include "squid.h"
+#include "HttpRequest.h"
 #include "mgr/Registration.h"
 #include "Store.h"
+#include "URLScheme.h"
 
 #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
 
@@ -164,35 +166,74 @@
 carpSelectParent(HttpRequest * request)
 {
     int k;
-    const char *c;
     peer *p = NULL;
     peer *tp;
     unsigned int user_hash = 0;
     unsigned int combined_hash;
     double score;
     double high_score = 0;
-    const char *key = NULL;
 
     if (n_carp_peers == 0)
         return NULL;
 
-    key = urlCanonical(request);
-
     /* calculate hash key */
-    debugs(39, 2, "carpSelectParent: Calculating hash for " << key);
-
-    for (c = key; *c != 0; c++)
-        user_hash += ROTATE_LEFT(user_hash, 19) + *c;
+    debugs(39, 2, "carpSelectParent: Calculating hash for " << urlCanonical(request));
 
     /* select peer */
     for (k = 0; k < n_carp_peers; k++) {
+    	String key;
         tp = carp_peers[k];
+    	if (tp->options.carp_key.set) {
+    		//this code follows urlCanonical's pattern.
+    		//   all cornercases default to urlCanonical
+    		if (request->protocol == AnyP::PROTO_URN) {
+    			key=urlCanonical(request);
+    		} else if (request->method.id()==METHOD_CONNECT) {
+    			key=urlCanonical(request);
+    		} else {
+    			if (tp->options.carp_key.scheme) {
+    				 // temporary, until bug 1961 URL handling is fixed.
+    				const URLScheme sch = request->protocol;
+    				key.append(sch.const_str());
+    				key.append("://");
+    			}
+    			if (tp->options.carp_key.login) {
+    				key.append(request->login);
+    				key.append('@');
+    			}
+    			if (tp->options.carp_key.host) {
+    				key.append(request->GetHost());
+    			}
+    			if (tp->options.carp_key.port) {
+    				static char portbuf[7];
+    				snprintf(portbuf,7,":%d", request->port);
+    				key.append(portbuf);
+    			}
+    			if (tp->options.carp_key.path) {
+    				String::size_type pos;
+    				if ((pos=request->urlpath.find('?'))!=String::npos)
+    					key.append(request->urlpath.substr(0,pos));
+    				else
+    					key.append(request->urlpath);
+    			}
+    			if (tp->options.carp_key.params) {
+    				String::size_type pos;
+    				if ((pos=request->urlpath.find('?'))!=String::npos)
+    					key.append(request->urlpath.substr(pos,request->urlpath.size()));
+    			}
+
+    		}
+    	} else {
+    		key=urlCanonical(request);
+    	}
+        for (const char *c = key.rawBuf(), *e=key.rawBuf()+key.size(); c < e; c++)
+            user_hash += ROTATE_LEFT(user_hash, 19) + *c;
         combined_hash = (user_hash ^ tp->carp.hash);
         combined_hash += combined_hash * 0x62531965;
         combined_hash = ROTATE_LEFT(combined_hash, 21);
         score = combined_hash * tp->carp.load_multiplier;
-        debugs(39, 3, "carpSelectParent: " << tp->name << " combined_hash " << combined_hash  <<
-               " score " << std::setprecision(0) << score);
+        debugs(39, 3, "carpSelectParent: key=" << key << " name=" << tp->name << " combined_hash=" << combined_hash  <<
+               " score=" << std::setprecision(0) << score);
 
         if ((score > high_score) && peerHTTPOkay(tp, request)) {
             p = tp;

=== modified file 'src/cf.data.pre'
--- src/cf.data.pre	2011-07-22 13:36:58 +0000
+++ src/cf.data.pre	2011-07-30 19:21:17 +0000
@@ -2178,6 +2178,14 @@
 			than the Squid default location.
 	
 	
+	==== CARP OPTIONS ====
+	
+	carp-key=key-specification
+			use a different key than the full URL to hash against the peer.
+			the key-specification is a comma-separated list of the keywords			
+			scheme, login, host, port, path, params
+			Order is not important.
+	
 	==== ACCELERATOR / REVERSE-PROXY OPTIONS ====
 	
 	originserver	Causes this parent to be contacted as an origin server.

=== modified file 'src/structs.h'
--- src/structs.h	2011-06-23 08:33:13 +0000
+++ src/structs.h	2011-07-30 18:08:45 +0000
@@ -855,6 +855,15 @@
 #endif
         unsigned int allow_miss:1;
         unsigned int carp:1;
+        struct {
+        	unsigned int set:1; //If false, whole url is to be used. Overrides others
+        	unsigned int scheme:1;
+        	unsigned int login:1;
+        	unsigned int host:1;
+        	unsigned int port:1;
+        	unsigned int path:1;
+        	unsigned int params:1;
+        } carp_key;
 #if USE_AUTH
         unsigned int userhash:1;
 #endif

Reply via email to