-
Notifications
You must be signed in to change notification settings - Fork 845
Make negative caching configurable with error HTTP status codes #3953
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
proxy/http/HttpConfig.h
Outdated
| 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; |
There was a problem hiding this comment.
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;.
There was a problem hiding this comment.
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]) { ...}`.
There was a problem hiding this comment.
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.
proxy/http/HttpTransact.cc
Outdated
| 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)) { |
There was a problem hiding this comment.
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]) {
|
Where is the configuration parsing? This needs to be able to be set from |
9b8d100 to
1f46202
Compare
d5e6cfb to
0dd7bd9
Compare
|
Updated. @SolidWallOfCode |
proxy/http/HttpConfig.cc
Outdated
| { | ||
| char *negative_status = nullptr; | ||
| HttpEstablishStaticConfigStringAlloc(negative_status, config_var.data()); | ||
| std::string buf; |
There was a problem hiding this comment.
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;
}
}
fd5453a to
f1fd8b8
Compare
proxy/http/HttpConfig.cc
Outdated
| 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)); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
proxy/http/HttpConfig.cc
Outdated
| ret = true; | ||
| } | ||
| } | ||
| if (update == true) { |
There was a problem hiding this comment.
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.
proxy/http/HttpConfig.cc
Outdated
| 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; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
configs/records.config.default.in
Outdated
| ############################################################################## | ||
| 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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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".
proxy/http/HttpConfig.cc
Outdated
| 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)}; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea.
There was a problem hiding this comment.
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.
proxy/http/HttpTransact.cc
Outdated
| case HTTP_STATUS_SERVICE_UNAVAILABLE: | ||
| case HTTP_STATUS_GATEWAY_TIMEOUT: | ||
| int status = s->hdr_info.server_response.status_get(); | ||
| auto params = HttpConfig::acquire(); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
|
Good to go. @SolidWallOfCode |
|
How much testing have you done with this? |
|
I did some basic "print out" tests of using both default value and configured values in |
|
Worth trying the test from #3211? (needs a remap like |
|
An excellent idea. Make it so, Xavier. |
|
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. |
|
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. |
|
The cherry pick conflict was minor, so I fixed it myself. |
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.