Skip to content

Conversation

@nqyy
Copy link
Contributor

@nqyy nqyy commented Jul 11, 2018

This PR is a resurrection of #2031.
This will resolve the issue: #3211.

A new configurable variable is added to records.config: proxy.config.http.negative_caching_list.
It enables users to self-define status code for negative caching.

MgmtByte server_session_sharing_pool = TS_SERVER_SESSION_SHARING_POOL_THREAD;

// Vector to hold the status codes that will BE cached with negative caching enabled
std::vector<std::bitset<10>> codeNegCache;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a std::vector? And why 10 as the base bitset size? Why not 32 or 64? But overall, the maximum possibly status code is 599, which makes std::bitset<600> a reasonable choice. Then you can do c.codeNegCache[HTTP_STATUS_NOT_FOUND] = true;.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P.S. "codeNegCache" is a bad name. "isNegativeStatus" might be better, for something like "if (isNegativeStatus[client.info->status]) { ...}`.

Copy link
Contributor Author

@nqyy nqyy Jul 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. I had some misunderstanding on the vector stuff.

case HTTP_STATUS_GATEWAY_TIMEOUT:
int status = s->hdr_info.server_response.status_get();
auto params = HttpConfig::acquire();
if (std::binary_search(params->codeNegCache.begin(), params->codeNegCache.end(), status, cmp_func)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, riffing on the previous comment, this becomes

if (params->isNegativeStatus[status]) {

@SolidWallOfCode
Copy link
Member

Where is the configuration parsing? This needs to be able to be set from records.config.

@nqyy nqyy force-pushed the NegativeCaching branch 2 times, most recently from 9b8d100 to 1f46202 Compare July 11, 2018 21:28
@nqyy nqyy changed the title Negative caching shouldn't happen on 400 response Make negative caching accept configured error status codes Jul 11, 2018
@nqyy nqyy force-pushed the NegativeCaching branch 4 times, most recently from d5e6cfb to 0dd7bd9 Compare July 12, 2018 21:50
@nqyy
Copy link
Contributor Author

nqyy commented Jul 12, 2018

Updated. @SolidWallOfCode

{
char *negative_status = nullptr;
HttpEstablishStaticConfigStringAlloc(negative_status, config_var.data());
std::string buf;
Copy link
Member

@SolidWallOfCode SolidWallOfCode Jul 13, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should use TextView here - it is as clean and avoids any memory allocation. Also, if the configuration is set, don't enable the defaults (because then it would be impossible to turn those off). Error checking and reporting is good too. In terms of hooking this up to the configuration file, take a look at the code in HttpConnectionCount.cc, around line 161.

  char *negative_status = nullptr;
  ts::TextView list(config_var);

  if (list.empty()) {
    // The responses with the following status code WILL BE cached by default with negative caching enabled.
    set[HTTP_STATUS_NO_CONTENT]            = true;
    set[HTTP_STATUS_USE_PROXY]             = true;
    set[HTTP_STATUS_FORBIDDEN]             = true;
    set[HTTP_STATUS_NOT_FOUND]             = true;
    set[HTTP_STATUS_METHOD_NOT_ALLOWED]    = true;
    set[HTTP_STATUS_REQUEST_URI_TOO_LONG]  = true;
    set[HTTP_STATUS_INTERNAL_SERVER_ERROR] = true;
    set[HTTP_STATUS_NOT_IMPLEMENTED]       = true;
    set[HTTP_STATUS_BAD_GATEWAY]           = true;
    set[HTTP_STATUS_SERVICE_UNAVAILABLE]   = true;
    set[HTTP_STATUS_GATEWAY_TIMEOUT]       = true;
  }

  auto is_sep { [](char c) { return isspace(c) || ',' == c || ';' == c; } };
  while (! list.ltrim_if(is_sep).empty()) {
    ts::TextView span, token { list.take_prefix_if(is_sep) };
    auto n = ts::svtoi(token, &span);
    if (span.size() != token.size()) {
      ink_fatal("Invalid status code '%.*s' for negative caching: not a number", static_cast<int>(token.size()), token.data());
    } else if (n <= 0 || n >= 600) {
      ink_fatal("Invalid status code '%.*s' for negative caching: out of range", static_cast<int>(token.size()), token.data());
    } else {
      set[n] = true;
    }
  }

if (0 == strcasecmp("proxy.config.http.negative_caching_list", name) && RECD_STRING == dtype) {
if (data.rec_string) {
// parse the list of status code
ts::TextView status_list(std::string(data.rec_string));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is a std::string used here? Why not status_list(data.rec_string, strlen(data.rec_string));? The use of std::string in this way already requires a null terminate string.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

ret = true;
}
}
if (update == true) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps return update? Or assign to zret instead of ret or update. There's no comment indicating what exactly the return value of this function means so it's hard to be sure.

set_negative_caching_list(r->name, r->data_type, r->data, c, false);

// The responses with the following status code WILL BE cached by default with negative caching enabled.
c->negative_caching_list[HTTP_STATUS_NO_CONTENT] = true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suppose I want to configure negative caching to not include HTTP_STATUS_FORBIDDEN. How do I do that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In previous logic, they are there by default. Maybe, I can put them in the records.config default value but not here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but in the previous logic they could not be configured at all. These should be used only if the incoming string is null or empty.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. I'll change this logic.

@nqyy nqyy force-pushed the NegativeCaching branch from 55ceefd to 541421b Compare July 16, 2018 16:31
@nqyy nqyy changed the title Make negative caching accept configured error status codes Make negative caching configurable with error HTTP status codes Jul 16, 2018
##############################################################################
CONFIG proxy.config.http.negative_caching_enabled INT 0
CONFIG proxy.config.http.negative_caching_lifetime INT 1800
CONFIG proxy.config.http.negative_caching_list STRING 204 305 403 404 405 414 500 501 502 503 504
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No - it's a policy goal to reduce the amount of things in this file. The defaults should be handled if needed in RecordsConfig.cc, as you've already done.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But users need to put 204 305 403 404 405 414 500 501 502 503 504 in here on their own, and modify it if they want.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, if you want, you can allow a special token like "default" to mean "add all the default status codes". Therefore to add 301, it would be "default 301".

ts::TextView status_list(data.rec_string, strlen(data.rec_string));
while (!status_list.empty()) {
auto is_sep{[](char c) { return isspace(c) || ',' == c || ';' == c; }};
ts::TextView span, token{status_list.ltrim_if(is_sep).take_prefix_if(is_sep)};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to either move is_sep and the ltrim_if up, or handle the possibility of an empty token if there are trailing separators. I'd favor the former - use while (!status_list.ltrim_if(is_sep).empty()) { and move the is_sep definition to just before the while.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right

}
}
}
// set the return value
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would probably be faster to do

if ((set ^ c->negative_caching_list).any()) {
  c->negative_caching_list = set;
  ret = ret || update;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed it the first time, but double checking there is an equality operator for bitset. I thought it was a bit odd to not have that.

case HTTP_STATUS_SERVICE_UNAVAILABLE:
case HTTP_STATUS_GATEWAY_TIMEOUT:
int status = s->hdr_info.server_response.status_get();
auto params = HttpConfig::acquire();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you want to use s->http_config_params here - that is the HTTP configuration parameters acquired when the HttpSM was created (and which HttpTransact is a member). You can see other examples of this in HttpTransact methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I see. I was following the way that the original PR was constructed.

}

static bool
set_negative_caching_list(const char *name, RecDataT dtype, RecData data, HttpConfigParams *c, bool update)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this called from anywhere else? Maybe it should be folded in to negative_caching_list_cb.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

negative_caching_list_cb is only called when reloading. set_negative_caching_list is called once in the first place.

@nqyy nqyy force-pushed the NegativeCaching branch from f051fcb to b6b911d Compare July 16, 2018 20:04
@nqyy nqyy force-pushed the NegativeCaching branch from 2cc7915 to 35f25ce Compare July 16, 2018 20:51
@nqyy
Copy link
Contributor Author

nqyy commented Jul 16, 2018

Good to go. @SolidWallOfCode

@SolidWallOfCode
Copy link
Member

How much testing have you done with this?

@nqyy
Copy link
Contributor Author

nqyy commented Jul 17, 2018

I did some basic "print out" tests of using both default value and configured values in records.config when running and reloading traffic server. @SolidWallOfCode

@SolidWallOfCode SolidWallOfCode merged commit 4c34cbc into apache:master Jul 17, 2018
@nqyy nqyy deleted the NegativeCaching branch July 17, 2018 18:40
@mlibbey
Copy link
Contributor

mlibbey commented Jul 19, 2018

Worth trying the test from #3211?

$ traffic_ctl config get proxy.config.http.negative_caching_lifetime
proxy.config.http.negative_caching_lifetime: 10
$ for status in `seq 400 499`; do 
echo "####status code=$status####"; 
for retry in `seq 3`; do 
  result=$(curl -sD- -o/dev/null -x 127.0.0.1:80 "http://httpbin.org/status/$status" | egrep "(Age|X-Cache)"); 
  if [[ $retry < 3 ]]; then 
    sleep 6; 
  else if [[ "$result" == *"Age: 0"* ]]; then 
      echo "$status code works"; 
    else 
      echo "$status is cached too long"; 
    fi;
  fi; 
  done; 
done

(needs a remap like

map http://httpbin.org http://httpbin.org

@SolidWallOfCode
Copy link
Member

An excellent idea. Make it so, Xavier.

@zwoop
Copy link
Contributor

zwoop commented Aug 13, 2018

This does not cherry-pick cleanly to 8.0.x, if you would like this feature in 8.0.x, please make a PR against this branch.

@bryancall
Copy link
Contributor

I also tried to cherry pick this change and it failed. There needs to be a new PR opened that is a backport to the 8.0.x tree. I am going to remove this PR from the 8.0.x project.

@bryancall bryancall modified the milestones: 9.0.0, 8.0.0 Aug 31, 2018
@bryancall
Copy link
Contributor

The cherry pick conflict was minor, so I fixed it myself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants