Skip to content

Commit 129a638

Browse files
authored
negative_caching_lifetime and ttl-in-cache interaction (#12665)
When both negative_caching_lifetime and cache.config ttl-in-cache are configured, ttl-in-cache was incorrectly overriding negative_caching_lifetime for negative responses (404, 403, 500, etc.). This caused negative responses to be cached for the ttl-in-cache duration instead of the intended negative_caching_lifetime. The fix modifies what_is_document_freshness() to skip the ttl-in-cache check for negatively cached responses.
1 parent 0565164 commit 129a638

File tree

3 files changed

+143
-9
lines changed

3 files changed

+143
-9
lines changed

src/proxy/http/HttpTransact.cc

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7284,18 +7284,27 @@ HttpTransact::what_is_document_freshness(State *s, HTTPHdr *client_request, HTTP
72847284
//////////////////////////////////////////////////////
72857285
// If config file has a ttl-in-cache field set, //
72867286
// it has priority over any other http headers and //
7287-
// other configuration parameters. //
7287+
// other configuration parameters. Negative caching //
7288+
// is different however since the user would //
7289+
// rather expect their explicitly configured //
7290+
// negative_caching_lifetime to be used instead of //
7291+
// ttl-in-cache. //
72887292
//////////////////////////////////////////////////////
72897293
if (s->cache_control.ttl_in_cache > 0) {
7290-
// what matters if ttl is set is not the age of the document
7291-
// but for how long it has been stored in the cache (resident time)
7292-
int resident_time = s->current.now - s->response_received_time;
7293-
7294-
TxnDbg(dbg_ctl_http_match, "ttl-in-cache = %d, resident time = %d", s->cache_control.ttl_in_cache, resident_time);
7295-
if (resident_time > s->cache_control.ttl_in_cache) {
7296-
return (Freshness_t::STALE);
7294+
auto status = static_cast<int>(cached_obj_response->status_get());
7295+
if (s->txn_conf->negative_caching_enabled && s->txn_conf->negative_caching_list.contains(status)) {
7296+
TxnDbg(dbg_ctl_http_match, "ttl-in-cache set, but skipping for negative cached response %d", status);
72977297
} else {
7298-
return (Freshness_t::FRESH);
7298+
// what matters if ttl is set is not the age of the document
7299+
// but for how long it has been stored in the cache (resident time)
7300+
int resident_time = s->current.now - s->response_received_time;
7301+
7302+
TxnDbg(dbg_ctl_http_match, "ttl-in-cache = %d, resident time = %d", s->cache_control.ttl_in_cache, resident_time);
7303+
if (resident_time > s->cache_control.ttl_in_cache) {
7304+
return (Freshness_t::STALE);
7305+
} else {
7306+
return (Freshness_t::FRESH);
7307+
}
72997308
}
73007309
}
73017310

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,32 @@
151151
tr.AddVerifierServerProcess("server-no-timeout", replay_file, http_ports=[server_port])
152152
tr.AddVerifierClientProcess("client-no-timeout", replay_file, http_ports=[ts.Variables.port])
153153
tr.StillRunningAfter = ts
154+
155+
#
156+
# Verify that negative_caching_lifetime is respected even when cache.config
157+
# has ttl-in-cache configured.
158+
#
159+
replay_file = "replay/negative-caching-ttl-in-cache.replay.yaml"
160+
tr = Test.AddTestRun("Verify negative_caching_lifetime and ttl-in-cache interaction.")
161+
dns = tr.MakeDNServer("dns", default="127.0.0.1")
162+
server = tr.AddVerifierServerProcess("server-ttl-in-cache", replay_file)
163+
server_port = server.Variables.http_port
164+
ts = tr.MakeATSProcess("ts-ttl-in-cache")
165+
ts.Disk.records_config.update(
166+
{
167+
'proxy.config.dns.nameservers': f"127.0.0.1:{dns.Variables.Port}",
168+
'proxy.config.dns.resolv_conf': 'NULL',
169+
'proxy.config.diags.debug.enabled': 1,
170+
'proxy.config.diags.debug.tags': 'http',
171+
'proxy.config.http.insert_age_in_response': 0,
172+
'proxy.config.http.negative_caching_enabled': 1,
173+
'proxy.config.http.negative_caching_lifetime': 2
174+
})
175+
ts.Disk.remap_config.AddLine(f'map / http://backend.example.com:{server_port}')
176+
# Configure cache.config with a long ttl-in-cache that should NOT override
177+
# negative_caching_lifetime for negative responses.
178+
ts.Disk.cache_config.AddLine('dest_domain=backend.example.com ttl-in-cache=30d')
179+
p = tr.AddVerifierClientProcess("client-ttl-in-cache", replay_file, http_ports=[ts.Variables.port])
180+
p.StartBefore(dns)
181+
p.StartBefore(server)
182+
p.StartBefore(ts)
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
#
18+
# Initial request that caches a 404 response.
19+
# This tests that negative_caching_lifetime is respected even when
20+
# ttl-in-cache is configured.
21+
#
22+
23+
meta:
24+
version: "1.0"
25+
26+
sessions:
27+
- transactions:
28+
29+
#
30+
# Test 1: Cache a 404 response.
31+
#
32+
- client-request:
33+
method: "GET"
34+
version: "1.1"
35+
scheme: "http"
36+
url: /path/404
37+
headers:
38+
fields:
39+
- [ Host, example.com ]
40+
- [ uuid, 100 ]
41+
42+
server-response:
43+
status: 404
44+
reason: "Not Found"
45+
headers:
46+
fields:
47+
- [ Content-Length, 8 ]
48+
49+
proxy-response:
50+
status: 404
51+
52+
# Repeat the request and verify that the 404 is served from the cache.
53+
- client-request:
54+
# Add a delay so ATS has time to finish any caching IO for the previous
55+
# transaction.
56+
delay: 100ms
57+
58+
method: "GET"
59+
version: "1.1"
60+
scheme: "http"
61+
url: /path/404
62+
headers:
63+
fields:
64+
- [ Host, example.com ]
65+
- [ uuid, 101 ]
66+
67+
68+
# The following should not reach the server since the 404 is cached.
69+
server-response:
70+
status: 200
71+
reason: OK
72+
73+
# Verify the cached 404 response is served.
74+
proxy-response:
75+
status: 404
76+
77+
78+
# Delay for 3 seconds to exceed negative_caching_lifetime and verify that the
79+
# 200 OK is served from the server rather than the cached 404.
80+
- client-request:
81+
delay: 3s
82+
method: "GET"
83+
version: "1.1"
84+
scheme: "http"
85+
url: /path/404
86+
headers:
87+
fields:
88+
- [ Host, example.com ]
89+
- [ uuid, 102 ]
90+
91+
server-response:
92+
status: 200
93+
reason: OK
94+
95+
proxy-response:
96+
status: 200

0 commit comments

Comments
 (0)