Skip to content

Commit db850f1

Browse files
authored
Add proxy.config.http.negative_revalidating_list (#12212)
* Add proxy.config.http.negative_revalidating_list * Address comments
1 parent 590fb85 commit db850f1

File tree

7 files changed

+294
-43
lines changed

7 files changed

+294
-43
lines changed

doc/admin-guide/files/records.yaml.en.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1928,6 +1928,12 @@ Negative Response Caching
19281928

19291929
This configuration defaults to 1,800 seconds (30 minutes).
19301930

1931+
.. ts:cv:: CONFIG proxy.config.http.negative_revalidating_list STRING 500 502 503 504
1932+
:reloadable:
1933+
1934+
The HTTP status codes for which the negative revalidating feature applies. Note that this is a
1935+
`STRING` configuration containing a space separated list of the desired HTTP status codes.
1936+
19311937
Proxy User Variables
19321938
====================
19331939

include/proxy/http/HttpConfig.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,9 @@ struct HttpConfigParams : public ConfigInfo {
802802
// bitset to hold the status codes that will BE cached with negative caching enabled
803803
HttpStatusBitset negative_caching_list;
804804

805+
// bitset to hold the status codes that will used by nagative revalidating enabled
806+
HttpStatusBitset negative_revalidating_list;
807+
805808
// All the overridable configurations goes into this class member, but they
806809
// are not copied over until needed ("lazy").
807810
OverridableHttpConfigParams oride;

src/proxy/http/HttpConfig.cc

Lines changed: 76 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -635,33 +635,51 @@ register_stat_callbacks()
635635
});
636636
}
637637

638+
/**
639+
Parse list of HTTP status code and return HttpStatusBitset
640+
- e.g. "204 305 403 404 414 500 501 502 503 504"
641+
*/
642+
static HttpStatusBitset
643+
parse_http_status_code_list(swoc::TextView status_list)
644+
{
645+
HttpStatusBitset set;
646+
647+
auto is_sep{[](char c) { return isspace(c) || ',' == c || ';' == c; }};
648+
649+
while (!status_list.ltrim_if(is_sep).empty()) {
650+
swoc::TextView span;
651+
swoc::TextView token{status_list.take_prefix_if(is_sep)};
652+
auto n = swoc::svtoi(token, &span);
653+
if (span.size() != token.size()) {
654+
Error("Invalid status code '%.*s': not a number", static_cast<int>(token.size()), token.data());
655+
} else if (n <= 0 || n >= HTTP_STATUS_NUMBER) {
656+
Error("Invalid status code '%.*s': out of range", static_cast<int>(token.size()), token.data());
657+
} else {
658+
set[n] = true;
659+
}
660+
}
661+
662+
return set;
663+
}
664+
638665
static bool
639666
set_negative_caching_list(const char *name, RecDataT dtype, RecData data, HttpConfigParams *c, bool update)
640667
{
641668
bool ret = false;
642669
HttpStatusBitset set;
670+
643671
// values from proxy.config.http.negative_caching_list
644672
if (0 == strcasecmp("proxy.config.http.negative_caching_list", name) && RECD_STRING == dtype && data.rec_string) {
645673
// parse the list of status codes
646-
swoc::TextView status_list(data.rec_string, strlen(data.rec_string));
647-
auto is_sep{[](char c) { return isspace(c) || ',' == c || ';' == c; }};
648-
while (!status_list.ltrim_if(is_sep).empty()) {
649-
swoc::TextView span, token{status_list.take_prefix_if(is_sep)};
650-
auto n = swoc::svtoi(token, &span);
651-
if (span.size() != token.size()) {
652-
Error("Invalid status code '%.*s' for negative caching: not a number", static_cast<int>(token.size()), token.data());
653-
} else if (n <= 0 || n >= HTTP_STATUS_NUMBER) {
654-
Error("Invalid status code '%.*s' for negative caching: out of range", static_cast<int>(token.size()), token.data());
655-
} else {
656-
set[n] = true;
657-
}
658-
}
674+
set = parse_http_status_code_list({data.rec_string, strlen(data.rec_string)});
659675
}
676+
660677
// set the return value
661678
if (set != c->negative_caching_list) {
662679
c->negative_caching_list = set;
663680
ret = ret || update;
664681
}
682+
665683
return ret;
666684
}
667685

@@ -685,6 +703,47 @@ load_negative_caching_var(RecRecord const *r, void *cookie)
685703
set_negative_caching_list(r->name, r->data_type, r->data, c, false);
686704
}
687705

706+
static bool
707+
set_negative_revalidating_list(const char *name, RecDataT dtype, RecData data, HttpConfigParams *c, bool update)
708+
{
709+
bool ret = false;
710+
HttpStatusBitset set;
711+
712+
// values from proxy.config.http.negative_revalidating_list
713+
if (0 == strcasecmp("proxy.config.http.negative_revalidating_list", name) && RECD_STRING == dtype && data.rec_string) {
714+
// parse the list of status codes
715+
set = parse_http_status_code_list({data.rec_string, strlen(data.rec_string)});
716+
}
717+
718+
// set the return value
719+
if (set != c->negative_revalidating_list) {
720+
c->negative_revalidating_list = set;
721+
ret = ret || update;
722+
}
723+
724+
return ret;
725+
}
726+
727+
// Method of getting the status code bitset
728+
static int
729+
negative_revalidating_list_cb(const char *name, RecDataT dtype, RecData data, void *cookie)
730+
{
731+
HttpConfigParams *c = static_cast<HttpConfigParams *>(cookie);
732+
// Signal an update if valid value arrived.
733+
if (set_negative_revalidating_list(name, dtype, data, c, true)) {
734+
http_config_cb(name, dtype, data, cookie);
735+
}
736+
return REC_ERR_OKAY;
737+
}
738+
739+
// Method of loading the negative caching config bitset
740+
void
741+
load_negative_revalidating_var(RecRecord const *r, void *cookie)
742+
{
743+
HttpConfigParams *c = static_cast<HttpConfigParams *>(cookie);
744+
set_negative_revalidating_list(r->name, r->data_type, r->data, c, false);
745+
}
746+
688747
/** Template for creating conversions and initialization for @c std::chrono based configuration variables.
689748
*
690749
* @tparam V The exact type of the configuration variable.
@@ -1020,6 +1079,8 @@ HttpConfig::startup()
10201079
HttpEstablishStaticConfigLongLong(c.oride.negative_revalidating_lifetime, "proxy.config.http.negative_revalidating_lifetime");
10211080
RecRegisterConfigUpdateCb("proxy.config.http.negative_caching_list", &negative_caching_list_cb, &c);
10221081
RecLookupRecord("proxy.config.http.negative_caching_list", &load_negative_caching_var, &c, true);
1082+
RecRegisterConfigUpdateCb("proxy.config.http.negative_revalidating_list", &negative_revalidating_list_cb, &c);
1083+
RecLookupRecord("proxy.config.http.negative_revalidating_list", &load_negative_revalidating_var, &c, true);
10231084

10241085
// Buffer size and watermark
10251086
HttpEstablishStaticConfigLongLong(c.oride.default_buffer_size_index, "proxy.config.http.default_buffer_size");
@@ -1339,7 +1400,8 @@ HttpConfig::reconfigure()
13391400
params->oride.ssl_client_sni_policy = ats_strdup(m_master.oride.ssl_client_sni_policy);
13401401
params->oride.ssl_client_alpn_protocols = ats_strdup(m_master.oride.ssl_client_alpn_protocols);
13411402

1342-
params->negative_caching_list = m_master.negative_caching_list;
1403+
params->negative_caching_list = m_master.negative_caching_list;
1404+
params->negative_revalidating_list = m_master.negative_revalidating_list;
13431405

13441406
params->oride.host_res_data = m_master.oride.host_res_data;
13451407
params->oride.host_res_data.conf_value = ats_strdup(m_master.oride.host_res_data.conf_value);

src/proxy/http/HttpTransact.cc

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4319,15 +4319,12 @@ HttpTransact::handle_cache_operation_on_forward_server_response(State *s)
43194319
SET_VIA_STRING(VIA_SERVER_RESULT, VIA_SERVER_SERVED);
43204320
SET_VIA_STRING(VIA_PROXY_RESULT, VIA_PROXY_SERVED);
43214321

4322-
/* if we receive a 500, 502, 503 or 504 while revalidating
4322+
/* By default, if we receive a 500, 502, 503 or 504 while revalidating
43234323
a document, treat the response as a 304 and in effect revalidate the document for
43244324
negative_revalidating_lifetime. (negative revalidating)
43254325
*/
4326-
4327-
if ((server_response_code == HTTP_STATUS_INTERNAL_SERVER_ERROR || server_response_code == HTTP_STATUS_GATEWAY_TIMEOUT ||
4328-
server_response_code == HTTP_STATUS_BAD_GATEWAY || server_response_code == HTTP_STATUS_SERVICE_UNAVAILABLE) &&
4329-
s->cache_info.action == CACHE_DO_UPDATE && s->txn_conf->negative_revalidating_enabled &&
4330-
is_stale_cache_response_returnable(s)) {
4326+
if (s->txn_conf->negative_revalidating_enabled && s->http_config_param->negative_revalidating_list[server_response_code] &&
4327+
s->cache_info.action == CACHE_DO_UPDATE && is_stale_cache_response_returnable(s)) {
43314328
HTTPStatus cached_response_code = s->cache_info.object_read->response_get()->status_get();
43324329
if (!(cached_response_code == HTTP_STATUS_INTERNAL_SERVER_ERROR || cached_response_code == HTTP_STATUS_GATEWAY_TIMEOUT ||
43334330
cached_response_code == HTTP_STATUS_BAD_GATEWAY || cached_response_code == HTTP_STATUS_SERVICE_UNAVAILABLE)) {

src/records/RecordsConfig.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,8 @@ static const RecordElement RecordsConfig[] =
488488
,
489489
{RECT_CONFIG, "proxy.config.http.negative_caching_list", RECD_STRING, "204 305 403 404 414 500 501 502 503 504", RECU_DYNAMIC, RR_NULL, RECC_NULL, nullptr, RECA_NULL}
490490
,
491+
{RECT_CONFIG, "proxy.config.http.negative_revalidating_list", RECD_STRING, "500 502 503 504", RECU_DYNAMIC, RR_NULL, RECC_NULL, nullptr, RECA_NULL}
492+
,
491493

492494
// #########################
493495
// # proxy users variables #

tests/gold_tests/cache/negative-revalidating.test.py

Lines changed: 54 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,47 +21,78 @@
2121
Test the negative revalidating feature.
2222
'''
2323

24+
25+
class NegativeRevalidatingTest:
26+
_test_num: int = 0
27+
28+
def __init__(self, name: str, records_config: dict, replay_file: str):
29+
self._tr = Test.AddTestRun(name)
30+
self._replay_file = replay_file
31+
32+
self.__setupOriginServer()
33+
self.__setupTS(records_config)
34+
self.__setupClient()
35+
36+
NegativeRevalidatingTest._test_num += 1
37+
38+
def __setupClient(self):
39+
self._tr.AddVerifierClientProcess(
40+
f"client-{NegativeRevalidatingTest._test_num}", self._replay_file, http_ports=[self._ts.Variables.port])
41+
42+
def __setupOriginServer(self):
43+
self._server = self._tr.AddVerifierServerProcess(f"server-{NegativeRevalidatingTest._test_num}", self._replay_file)
44+
45+
def __setupTS(self, records_config):
46+
self._ts = Test.MakeATSProcess(f"ts-{NegativeRevalidatingTest._test_num}")
47+
self._ts.Disk.records_config.update(records_config)
48+
self._ts.Disk.remap_config.AddLine('map / http://127.0.0.1:{0}'.format(self._server.Variables.http_port))
49+
50+
def run(self):
51+
self._tr.Processes.Default.StartBefore(self._ts)
52+
self._tr.StillRunningAfter = self._ts
53+
54+
2455
#
2556
# Verify disabled negative_revalidating behavior.
2657
#
27-
ts = Test.MakeATSProcess("ts-negative-revalidating-disabled")
28-
ts.Disk.records_config.update(
29-
{
58+
NegativeRevalidatingTest(
59+
"Verify negative revalidating disabled", {
3060
'proxy.config.diags.debug.enabled': 1,
3161
'proxy.config.diags.debug.tags': 'http|cache',
3262
'proxy.config.http.insert_age_in_response': 0,
63+
'proxy.config.http.insert_response_via_str': 2,
3364
'proxy.config.http.negative_revalidating_enabled': 0,
3465
'proxy.config.http.cache.max_stale_age': 6
35-
})
36-
tr = Test.AddTestRun("Verify disabled negative revalidating behavior.")
37-
replay_file = "replay/negative-revalidating-disabled.replay.yaml"
38-
server = tr.AddVerifierServerProcess("server1", replay_file)
39-
server_port = server.Variables.http_port
40-
tr.AddVerifierClientProcess("client1", replay_file, http_ports=[ts.Variables.port])
41-
ts.Disk.remap_config.AddLine('map / http://127.0.0.1:{0}'.format(server_port))
42-
tr.Processes.Default.StartBefore(ts)
43-
tr.StillRunningAfter = ts
66+
}, "replay/negative-revalidating-disabled.replay.yaml").run()
4467

4568
#
4669
# Verify enabled negative_revalidating behavior.
4770
#
48-
ts = Test.MakeATSProcess("ts-negative-revalidating-enabled")
49-
ts.Disk.records_config.update(
71+
NegativeRevalidatingTest(
72+
"Verify negative revalidating enabled",
5073
{
5174
'proxy.config.diags.debug.enabled': 1,
5275
'proxy.config.diags.debug.tags': 'http|cache',
5376
'proxy.config.http.insert_age_in_response': 0,
77+
'proxy.config.http.insert_response_via_str': 2,
5478

5579
# Negative revalidating is on by default. Verify this by leaving out the
5680
# following line and expect negative_revalidating to be enabled.
5781
# 'proxy.config.http.negative_revalidating_enabled': 1,
5882
'proxy.config.http.cache.max_stale_age': 6
59-
})
60-
tr = Test.AddTestRun("Verify negative revalidating behavior.")
61-
replay_file = "replay/negative-revalidating-enabled.replay.yaml"
62-
server = tr.AddVerifierServerProcess("server2", replay_file)
63-
server_port = server.Variables.http_port
64-
tr.AddVerifierClientProcess("client2", replay_file, http_ports=[ts.Variables.port])
65-
ts.Disk.remap_config.AddLine('map / http://127.0.0.1:{0}'.format(server_port))
66-
tr.Processes.Default.StartBefore(ts)
67-
tr.StillRunningAfter = ts
83+
},
84+
"replay/negative-revalidating-enabled.replay.yaml").run()
85+
86+
#
87+
# Verify negative_revalidating list behavior.
88+
#
89+
NegativeRevalidatingTest(
90+
"Verify negative_revalidating_list behavior", {
91+
'proxy.config.diags.debug.enabled': 1,
92+
'proxy.config.diags.debug.tags': 'http|cache',
93+
'proxy.config.http.insert_age_in_response': 0,
94+
'proxy.config.http.insert_response_via_str': 2,
95+
'proxy.config.http.cache.max_stale_age': 6,
96+
'proxy.config.http.negative_revalidating_enabled': 1,
97+
'proxy.config.http.negative_revalidating_list': "403 404"
98+
}, "replay/negative-revalidating-list.replay.yaml").run()

0 commit comments

Comments
 (0)