[ 
https://issues.apache.org/jira/browse/TS-4723?focusedWorklogId=26907&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-26907
 ]

ASF GitHub Bot logged work on TS-4723:
--------------------------------------

                Author: ASF GitHub Bot
            Created on: 23/Aug/16 13:19
            Start Date: 23/Aug/16 13:19
    Worklog Time Spent: 10m 
      Work Description: Github user SolidWallOfCode commented on a diff in the 
pull request:

    https://github.com/apache/trafficserver/pull/843#discussion_r75863130
  
    --- Diff: plugins/experimental/carp/carp.cc ---
    @@ -0,0 +1,713 @@
    +/** @file
    +
    +  A brief file description
    +
    +  @section license License
    +
    +  Licensed to the Apache Software Foundation (ASF) under one
    +  or more contributor license agreements.  See the NOTICE file
    +  distributed with this work for additional information
    +  regarding copyright ownership.  The ASF licenses this file
    +  to you under the Apache License, Version 2.0 (the
    +  "License"); you may not use this file except in compliance
    +  with the License.  You may obtain a copy of the License at
    +
    +      http://www.apache.org/licenses/LICENSE-2.0
    +
    +  Unless required by applicable law or agreed to in writing, software
    +  distributed under the License is distributed on an "AS IS" BASIS,
    +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +  See the License for the specific language governing permissions and
    +  limitations under the License.
    + */
    +
    +#include <errno.h>
    +
    +#include <string>
    +#include <sstream>
    +#include <stdlib.h> 
    +#include <stdio.h>
    +#include <sys/stat.h>
    +#include <sys/types.h>
    +#include <unistd.h>
    +
    +#include <memory.h>
    +
    +#include <ts/ts.h>
    +
    +#include "Common.h"
    +#include "CarpConfig.h"
    +#include "CarpConfigPool.h"
    +#include "CarpHashAlgorithm.h"
    +#include "UrlComponents.h"
    +
    +using namespace std;
    +
    +CarpConfigPool* g_CarpConfigPool = NULL;
    +int g_carpSelectedHostArgIndex = 0;
    +TSTextLogObject g_logObject = NULL;
    +
    +const char *logFileName = "carp";
    +
    
+//////////////////////////////////////////////////////////////////////////////
    
+//////////////////////////////////////////////////////////////////////////////
    +/*
    + check for our carp routed header, dump status if requested
    + */
    +static int
    +processCarpRoutedHeader(TSHttpTxn txnp, TSMBuffer bufp, TSMLoc hdr_loc)
    +{
    +  string value;
    +  if (getHeader(bufp, hdr_loc, CARP_ROUTED_HEADER, value)) { // if found 
header
    +    if (value.compare("1") == 0) { // is loop prevention value
    +      TSDebug(DEBUG_TAG_HOOK, "Found %s header with loop prevention value, 
not forwarding again", CARP_ROUTED_HEADER.c_str());
    +      return 0;
    +    } else if (value.compare("dump") == 0) { // is dump status request
    +      TSDebug(DEBUG_TAG_HOOK, "Found %s header with dump request", 
CARP_ROUTED_HEADER.c_str());
    +      string status;
    +      g_CarpConfigPool->getGlobalHashAlgo()->dump(status);
    +      TSHttpTxnSetHttpRetStatus(txnp, TS_HTTP_STATUS_MULTI_STATUS);
    +      TSHttpTxnErrorBodySet(txnp, TSstrdup(status.c_str()), 
status.length(), NULL);
    +      return -1;
    +    }
    +    TSDebug(DEBUG_TAG_HOOK, "Found %s header with unknown value of %s, 
ignoring", CARP_ROUTED_HEADER.c_str(), value.c_str());
    +    removeHeader(bufp, hdr_loc, CARP_ROUTED_HEADER);
    +  }
    +  return 1; // all OK
    +}
    +
    +static bool
    +checkListForSelf(std::vector<HashNode *> list)
    +{
    +  for (size_t k = 0; k < list.size(); k++) {
    +    if (list[k]->isSelf) return true;
    +  }
    +  return false;
    +}
    +
    +/**
    + bIsPOSTRemap = false --- Hash request and forward to peer
    + bIsPOSTRemap = true --- hash request, extract OS sockaddr, insert 
forwarding header, forward
    + */
    +static int
    +handleRequestProcessing(TSCont contp, TSEvent event, void *edata, bool 
bIsPOSTRemap)
    +{
    +  TSHttpTxn txnp = (TSHttpTxn) edata;
    +  TSMBuffer bufp;
    +  TSMLoc hdr_loc;
    +  TSMLoc url_loc;
    +
    +  // get the client request so we can get URL and add header
    +  if (TSHttpTxnClientReqGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
    +    TSError("carp couldn't get request headers");
    +    return -1;
    +  }
    +
    +  int method_len;
    +  const char *method = TSHttpHdrMethodGet(bufp, hdr_loc, &method_len);
    +  if (NULL == method) {
    +    TSError("carp couldn't get http method");
    +    TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
    +    return -1;
    +  }
    +  if (((method_len == TS_HTTP_LEN_DELETE) && (strncasecmp(method, 
TS_HTTP_METHOD_DELETE, TS_HTTP_LEN_DELETE) == 0)) ||
    +      ((method_len == TS_HTTP_LEN_PURGE) && (strncasecmp(method, 
TS_HTTP_METHOD_PURGE, TS_HTTP_LEN_PURGE) == 0))) {
    +    TSDebug(DEBUG_TAG_HOOK, "Request method is '%s' so not routing 
request", string(method,method_len).c_str());
    +    TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
    +    return 0;
    +  }
    +
    +  if (TSHttpHdrUrlGet(bufp, hdr_loc, &url_loc) != TS_SUCCESS) {
    +    TSError("carp couldn't get url");
    +    TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
    +    return -1;
    +  }
    +  // if has carp loop prevention header, do not remap
    +  int iTemp = processCarpRoutedHeader(txnp, bufp, hdr_loc);
    +  if(iTemp <= 0) { // if dump or do not remap
    +    // check origin client request's scheme for premap mode
    +    if (!bIsPOSTRemap) {
    +      string oriScheme;
    +      if (!getHeader(bufp, hdr_loc, CARP_PREMAP_SCHEME, oriScheme)) {
    +        TSDebug(DEBUG_TAG_HOOK, "couldn't get '%s' header", 
CARP_PREMAP_SCHEME.c_str());
    +      } else {
    +        bool isHttps = (oriScheme == TS_URL_SCHEME_HTTPS);
    +        
    +        if (isHttps) {
    +          TSUrlSchemeSet(bufp, url_loc, TS_URL_SCHEME_HTTPS, 
TS_URL_LEN_HTTPS);
    +        } else {
    +          TSUrlSchemeSet(bufp, url_loc, TS_URL_SCHEME_HTTP, 
TS_URL_LEN_HTTP);  
    +        }   
    +        
    +   removeHeader(bufp, hdr_loc, CARP_STATUS_HEADER);
    +        removeHeader(bufp, hdr_loc, CARP_ROUTED_HEADER);
    +        removeHeader(bufp, hdr_loc, CARP_PREMAP_SCHEME.c_str());
    +        TSDebug(DEBUG_TAG_HOOK, "Set client request's scheme to %s through 
%s header", 
    +            isHttps?"https":"http", CARP_PREMAP_SCHEME.c_str());
    +      }
    +    } else {
    +      removeHeader(bufp, hdr_loc, CARP_ROUTED_HEADER);
    +    }
    +    TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
    +    return iTemp;
    +  }
    +
    +  UrlComponents reqUrl;
    +  reqUrl.populate(bufp, url_loc);
    +  // the url ONLY used to determine the cache owner
    +  string sUrl;
    +
    +  if (!bIsPOSTRemap) { // if pre-remap, then host not in URL so get from 
header
    +    string sHost;
    +    if (!getHeader(bufp, hdr_loc, TS_MIME_FIELD_HOST, sHost)) {
    +      TSDebug(DEBUG_TAG_HOOK, "Could not find host header, ignoring it");
    +    }
    +    reqUrl.setHost(sHost);
    +
    +    //[YTSATS-836] heuristically ignore the scheme and port when calculate 
cache owner
    +    UrlComponents normalizedUrl = reqUrl;
    +    normalizedUrl.setScheme(CARP_SCHEME_FOR_HASH);
    +    normalizedUrl.setPort(CARP_PORT_FOR_HASH);
    +    normalizedUrl.construct(sUrl);
    +  } else {
    +    reqUrl.construct(sUrl);
    +  }
    +
    +
    +  if (g_CarpConfigPool->getGlobalConfig()->hasWhiteList()) {
    +    string sCarpable;
    +    if (!getHeader(bufp, hdr_loc, CARPABLE_HEADER, sCarpable)) { // if no 
carpable header check whitelist
    +      if 
(!g_CarpConfigPool->getGlobalConfig()->isWhiteListed(reqUrl.getHost())) { // if 
white list exists, then host must be present
    +        TSDebug(DEBUG_TAG_HOOK, "Host '%s' is not whitelisted, not going 
through carp", reqUrl.getHost().c_str());
    +        TSHandleMLocRelease(bufp, hdr_loc, url_loc);
    +        TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
    +        return 0;
    +      } else {
    +        TSDebug(DEBUG_TAG_HOOK, "Found host (%s) whitelisted, 
routing...",reqUrl.getHost().c_str());
    +      }
    +    } else { // found carpable header, make sure it's 1
    +      if (sCarpable.compare("1") != 0) { // carpable header present but 
not 0, be strict and do not forward request
    +        TSDebug(DEBUG_TAG_HOOK, "Carpable (%s) present but value not 
acceptable (%s)", CARPABLE_HEADER.c_str(), sCarpable.c_str());
    +        TSHandleMLocRelease(bufp, hdr_loc, url_loc);
    +        TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
    +        return 0;
    +      } else {
    +        TSDebug(DEBUG_TAG_HOOK, "Found Carpable header, routing...");
    +      }
    +    }
    +  } else { // no whitelist so blacklist could be used
    +    if 
(g_CarpConfigPool->getGlobalConfig()->isBlackListed(reqUrl.getHost())) { // if 
host black listed, do not carp
    +      TSDebug(DEBUG_TAG_HOOK, "Host '%s' is blacklisted, not going through 
carp", reqUrl.getHost().c_str());
    +      TSHandleMLocRelease(bufp, hdr_loc, url_loc);
    +      TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
    +      return 0;
    +    }
    +  }
    +  
    +  TSDebug(DEBUG_TAG_HOOK, "URL to hash with=%s", sUrl.c_str());
    +
    +  // get nodeList and select node to forward to
    +  std::vector<HashNode *> nodeList = 
g_CarpConfigPool->getGlobalHashAlgo()->getRemapProxyList(sUrl);
    +  bool bAddHeaderResult=false;
    +  bool bAddForwardedResult = false;
    +  HashNode* node = NULL;
    +  bool bIsOwner = false;
    +
    +  if (nodeList.size() == 0) { // no hosts available to forward to
    +    TSDebug(DEBUG_TAG_HOOK, "no hosts available to forward to, will handle 
locally");
    +    goto done;
    +  } else {
    +    node = nodeList[0];
    +  }
    +
    +  bIsOwner = checkListForSelf(nodeList);
    +  for (size_t k = 0; k < nodeList.size(); k++) {
    +    TSDebug(DEBUG_TAG_HOOK, "nodeList host %d name is %s", 
static_cast<int>(k), nodeList[k]->name.c_str());
    +  }
    +
    +  //handle forwarding
    +  TSDebug(DEBUG_TAG_HOOK, "forwarding to '%s' (isSelf=%d)", 
node->name.c_str(), node->isSelf);
    +  if (!node->isSelf) { // carp does not forward if we choose ourself
    +    node->carpForward();
    +    TSDebug(DEBUG_TAG_HOOK, "carp forwarded to %s.", node->name.c_str());
    +    // insert carp loop prevention header
    +    bAddHeaderResult = addHeader(bufp, hdr_loc, CARP_ROUTED_HEADER, 
string("1"));
    +    if (!bAddHeaderResult) {
    +      TSError("Carp, error inserting '%s' header", 
CARP_ROUTED_HEADER.c_str());
    +    }
    +    bAddForwardedResult = addHeader(bufp, hdr_loc, CARP_STATUS_HEADER, 
string(CARP_FORWARDED));
    +    if (!bAddForwardedResult) {
    +      TSError("Carp, error inserting '%s' header", 
CARP_STATUS_HEADER.c_str());
    +    }
    +
    +    if (bIsPOSTRemap) { // if post remap, get remapped/OS Server Addr and 
add as header
    +      const struct sockaddr* sa = TSHttpTxnServerAddrGet(txnp);
    +      //        const struct sockaddr* sa = TSHttpTxnNextHopAddrGet(txnp);
    +      if (sa) { // sanity check
    +        struct sockaddr_storage ss;
    +        memcpy(static_cast<void *> (&ss), sa, sizeof (sockaddr_storage));
    +        if ((reinterpret_cast<sockaddr_in *> (&ss))->sin_port == 0) { // 
set port from client request URL
    +          (reinterpret_cast<sockaddr_in *> (&ss))->sin_port = 
htons(reqUrl.getPort());
    +        }
    +
    +        string sTemp;
    +        getStringFromSockaddr(reinterpret_cast<const sockaddr *> (&ss), 
sTemp);
    +        TSDebug(DEBUG_TAG_HOOK, "Inserting forward header with 
sockaddr:%s", sTemp.c_str());
    +        string sSockaddr;
    +        sSockaddr.reserve(32 + sizeof (sockaddr_storage) * 2);
    +        for (unsigned int i = 0; i<sizeof (sockaddr_storage); i++) {
    +          char val[8];
    +          sprintf(val, "%02X", reinterpret_cast<const unsigned char *> 
(&ss)[i]);
    +          sSockaddr += val;
    +        }
    +        sSockaddr += "/" + reqUrl.getScheme();
    +        // insert carp forwarding header
    +        bool bAddFwdHeaderResult = addHeader(bufp, hdr_loc, 
CARP_FORWARD_HEADER, sSockaddr);
    +        if (!bAddFwdHeaderResult) {
    +          TSError("Carp, error inserting '%s' header", 
CARP_FORWARD_HEADER.c_str());
    +        }
    +      }
    +    } else { // for premap mode
    +      string sScheme = reqUrl.getScheme();
    +
    +      if (!addHeader(bufp, hdr_loc, CARP_PREMAP_SCHEME, sScheme)) { 
    +        TSError("Carp, error inserting '%s' header in premap mode", 
CARP_PREMAP_SCHEME.c_str());  
    +      } else {
    +        TSDebug(DEBUG_TAG_HOOK, "Insert client request scheme %s in premap 
mode", sScheme.c_str());
    +      }
    +    }
    +    // set origin server/destination
    +    //      TSHttpTxnConfigIntSet(txnp, 
TS_CONFIG_HTTP_SHARE_SERVER_SESSIONS, 0); // disable conn sharing due to bug 
when used with TSHttpTxnServerAddrSet
    +    if (TSHttpTxnServerAddrSet(txnp, reinterpret_cast<const struct 
sockaddr *> (&node->forwardAddr)) != TS_SUCCESS) {
    +      TSDebug(DEBUG_TAG_HOOK, "Error calling TSHttpTxnServerAddrSet");
    +    } else {
    +      // set scheme appropriately based on destination
    +      TSDebug(DEBUG_TAG_HOOK, "Setting scheme to '%s'", 
node->getSchemeString());
    +      TSUrlSchemeSet(bufp, url_loc, node->getSchemeString(), -1);
    +      if (!bIsPOSTRemap) { // since we are forwarding, do not remap 
request and do not cache result
    +        TSSkipRemappingSet(txnp, true);
    +      }
    +      TSHttpTxnArgSet(txnp, g_carpSelectedHostArgIndex, static_cast<void 
*> (node));
    +      if (!bIsOwner) {
    +        TSHttpTxnServerRespNoStoreSet(txnp, 1); // do not cache the 
response
    +      } else {
    +        TSHttpTxnServerRespNoStoreSet(txnp, 0); // we are replicate owner, 
cache response
    +      }
    +    }
    +  } else {
    +    node->carpNoForward();
    +    TSDebug(DEBUG_TAG_HOOK, "carp forwarded to self.");
    +  }
    +
    +done :
    +  // done w/buffers, release them
    +  TSHandleMLocRelease(bufp, hdr_loc, url_loc);
    +  TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
    +  return 0;
    +}
    +
    +/**
    + * Convert ASCII hex digit to value of hex digit
    + * @param ch
    + * @return 
    + */
    +static unsigned char
    +getValueOfHex(unsigned char ch)
    +{
    +  ch -= '0';
    +  if(ch > 9) ch -= 7; // 'A' - ':' = 7
    --- End diff --
    
    Really? Use a magic constant then explain it, instead of just letting the 
compiler do the constant folding? Also, this doesn't work for lower case.


Issue Time Tracking
-------------------

    Worklog Id:     (was: 26907)
    Time Spent: 5h 10m  (was: 5h)

> ATS CARP Plugin
> ---------------
>
>                 Key: TS-4723
>                 URL: https://issues.apache.org/jira/browse/TS-4723
>             Project: Traffic Server
>          Issue Type: New Feature
>          Components: Plugins
>            Reporter: Eric Schwartz
>            Assignee: Eric Schwartz
>             Fix For: 7.0.0
>
>          Time Spent: 5h 10m
>  Remaining Estimate: 0h
>
> Open sourcing this plugin we use internally within Yahoo in place of 
> hierarchical caching.
> CARP is a plugin that allows you to group a bunch of ATS hosts into a cluster 
> and share cache space across the entire group. This is done with consistent 
> hashing on the object URL to generate an "owner" node in the cluster. 
> Requests to any other node in the cluster will be forwarded on to the 
> corresponding owner. More info in the README.
> Difference from internal version of note:
> I've ripped out some code we weren't entirely sure we could open source 
> because of a hash function. If it turns out that we can open source this, 
> I'll do so. The CarpHashAlgorithm class is meant to be extensible, so any 
> consistent hash function can replace it. The function included here is pretty 
> straightforward but not what we use in production, so just wanted to use that 
> caveat.
> One last caveat:
> You'll see some code and documentation in here for object replication. This 
> is something I added recently to CARP that allows you to specify an object be 
> replicated a certain number of times in the cluster. This is useful if you 
> have a network partition or if you're performing some sort of update. When an 
> object's primary owner is unreachable, a node in the cluster can go to the 
> secondary owner if it's available rather than having to fall all the way back 
> to origin. While I've done some initial testing on this with my own cluster 
> of hosts, it's not been tested in production so use at your own risk for now. 
> I'll be sure to keep the open source community informed on the progress of 
> our tests with this feature.



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)

Reply via email to