This is an automated email from the ASF dual-hosted git repository.
masaori335 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new 494ff824dc Cleanup serving stale while origin server down (#13083)
494ff824dc is described below
commit 494ff824dcce3bcbfae1239f70e9bf8b26aa4459
Author: Masaori Koshiba <[email protected]>
AuthorDate: Tue Jun 9 08:15:51 2026 +0900
Cleanup serving stale while origin server down (#13083)
* Cleanup serving stale while origin server down
* Address comments from Copilot
* Clarify connection close
---
doc/admin-guide/files/records.yaml.en.rst | 3 +
src/proxy/http/HttpTransact.cc | 19 +--
tests/gold_tests/cache/proxy_serve_stale.test.py | 3 +
.../proxy_serve_stale_origin_down.replay.yaml | 161 +++++++++++++++++++++
4 files changed, 170 insertions(+), 16 deletions(-)
diff --git a/doc/admin-guide/files/records.yaml.en.rst
b/doc/admin-guide/files/records.yaml.en.rst
index a73fc70851..b362089c89 100644
--- a/doc/admin-guide/files/records.yaml.en.rst
+++ b/doc/admin-guide/files/records.yaml.en.rst
@@ -1954,6 +1954,9 @@ Origin Server Connect Attempts
:overridable:
Specifies how long (in seconds) |TS| remembers that an origin server was
unreachable.
+ During this window, if a stale cached response exists and its age is within
the cached response's ``max-age`` plus
+ :ts:cv:`proxy.config.http.cache.max_stale_age`, |TS| serves the stale
content directly
+ without attempting to contact the origin server.
.. ts:cv:: CONFIG proxy.config.http.uncacheable_requests_bypass_parent INT 1
:reloadable:
diff --git a/src/proxy/http/HttpTransact.cc b/src/proxy/http/HttpTransact.cc
index 27df97059e..1037611b07 100644
--- a/src/proxy/http/HttpTransact.cc
+++ b/src/proxy/http/HttpTransact.cc
@@ -2902,23 +2902,10 @@ HttpTransact::HandleCacheOpenReadHit(State *s)
find_server_and_update_current_info(s);
- // We do not want to try to revalidate documents if we think
- // the server is down due to the something report problem
- //
- // Note: we only want to skip origin servers because 1)
- // parent proxies have their own negative caching
- // scheme & 2) If we skip down parents, every page
- // we serve is potentially stale
- //
- if (s->current.request_to == ResolveInfo::ORIGIN_SERVER &&
is_server_negative_cached(s) && response_returnable == true &&
- is_stale_cache_response_returnable(s) == true) {
- server_up = false;
- update_current_info(&s->current, nullptr, ResolveInfo::UNDEFINED_LOOKUP,
true);
- TxnDbg(dbg_ctl_http_trans, "CacheOpenReadHit - server_down, returning
stale document");
- }
- // a parent lookup could come back as ParentResultType::FAIL if in
parent.config, go_direct == false and
+ // For origin servers (ResolveInfo::ORIGIN_SERVER): OSDNSLookup handles
serving stale when all origin servers are down.
+ // For parent proxies: a parent lookup could come back as
ParentResultType::FAIL if in parent.config, go_direct == false and
// there are no available parents (all down).
- else if (s->current.request_to == ResolveInfo::HOST_NONE &&
s->parent_result.result == ParentResultType::FAIL) {
+ if (s->current.request_to == ResolveInfo::HOST_NONE &&
s->parent_result.result == ParentResultType::FAIL) {
if (response_returnable == true && is_stale_cache_response_returnable(s)
== true) {
server_up = false;
update_current_info(&s->current, nullptr,
ResolveInfo::UNDEFINED_LOOKUP, true);
diff --git a/tests/gold_tests/cache/proxy_serve_stale.test.py
b/tests/gold_tests/cache/proxy_serve_stale.test.py
index 43c0bebd54..95e620a204 100644
--- a/tests/gold_tests/cache/proxy_serve_stale.test.py
+++ b/tests/gold_tests/cache/proxy_serve_stale.test.py
@@ -22,3 +22,6 @@ Test.ContinueOnFail = True
# Verify that stale content is served when the parent is down.
Test.ATSReplayTest(replay_file="replay/proxy_serve_stale.replay.yaml")
+
+# Verify that stale content is served when the origin server is down.
+Test.ATSReplayTest(replay_file="replay/proxy_serve_stale_origin_down.replay.yaml")
diff --git
a/tests/gold_tests/cache/replay/proxy_serve_stale_origin_down.replay.yaml
b/tests/gold_tests/cache/replay/proxy_serve_stale_origin_down.replay.yaml
new file mode 100644
index 0000000000..c1ca7e90ea
--- /dev/null
+++ b/tests/gold_tests/cache/replay/proxy_serve_stale_origin_down.replay.yaml
@@ -0,0 +1,161 @@
+# 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.
+
+meta:
+ version: "1.0"
+
+autest:
+ description: 'Verify that stale content is served when the origin server is
down'
+
+ dns:
+ name: 'dns-serve-stale-origin-down'
+ records:
+ backend.example.com: ["127.0.0.1"]
+
+
+ server:
+ name: 'server-serve-stale-origin-down'
+
+ client:
+ name: 'client-serve-stale-origin-down'
+
+ ats:
+ name: 'ts-serve-stale-origin-down'
+ process_config:
+ enable_cache: true
+
+ records_config:
+ proxy.config.diags.debug.enabled: 1
+ proxy.config.diags.debug.tags: 'cache|http|dns|hostdb'
+ proxy.config.http.cache.max_stale_age: 10
+ proxy.config.http.connect.down.policy: 4
+ proxy.config.http.connect_attempts_timeout: 1
+ proxy.config.http.connect_attempts_rr_retries: 0
+ proxy.config.http.connect_attempts_max_retries: 1
+ proxy.config.http.connect_attempts_max_retries_down_server: 0
+ proxy.config.http.down_server.cache_time: 5
+
+ remap_config:
+ - from: "http://example.com/"
+ to: "http://backend.example.com:{SERVER_HTTP_PORT}/"
+
+ plugin_config:
+ - 'xdebug.so --enable=x-cache,x-cache-key,via'
+
+sessions:
+- transactions:
+ # 1. Cache object
+ # cache-key: /path/a/
+ # cache: miss
+ # origin server: up
+ - client-request:
+ method: "GET"
+ version: "1.1"
+ url: /path/a/
+ headers:
+ fields:
+ - [ Host, example.com ]
+ - [ x-debug, "x-cache,x-cache-key,via" ]
+ - [ uuid, a-1 ]
+
+ server-response:
+ status: 200
+ headers:
+ fields:
+ - [ Content-Length, 16 ]
+ - [ Cache-Control, "public, max-age=1" ]
+ - [ Connection, close ]
+
+ proxy-response:
+ status: 200
+ headers:
+ fields:
+ - [ X-Cache, { value: miss, as: equal } ]
+
+ # 2. Make the origin server down
+ # cache-key: /path/b/
+ # cache: miss
+ # origin server: down
+ - client-request:
+ method: "GET"
+ version: "1.1"
+ url: /path/b/
+ headers:
+ fields:
+ - [ Host, example.com ]
+ - [ x-debug, "x-cache,x-cache-key,via" ]
+ - [ uuid, b-1 ]
+
+ server-response:
+ on_connect: reset
+
+ proxy-response:
+ status: 502
+ headers:
+ fields:
+ - [ X-Cache, { value: miss, as: equal } ]
+
+ # 3. Serve stale while proxy.config.http.down_server.cache_time
+ # cache-key: /path/a/
+ # cache: stale
+ # origin server: down (cached)
+ - client-request:
+ delay: 2s
+ method: "GET"
+ version: "1.1"
+ url: /path/a/
+ headers:
+ fields:
+ - [ Host, example.com ]
+ - [ x-debug, "x-cache,x-cache-key,via" ]
+ - [ uuid, a-2 ]
+
+ proxy-request:
+ expect: absent
+
+ server-response:
+ on_connect: reset
+
+ proxy-response:
+ status: 200
+ headers:
+ fields:
+ - [ X-Cache, { value: hit-stale, as: equal } ]
+
+ # 4. Revalidate stale contents
+ # cache-key: /path/a/
+ # cache: stale
+ # origin server: up
+ - client-request:
+ delay: 5s
+ method: "GET"
+ version: "1.1"
+ url: /path/a/
+ headers:
+ fields:
+ - [ Host, example.com ]
+ - [ x-debug, "x-cache,x-cache-key,via" ]
+ - [ uuid, a-99 ]
+
+ server-response:
+ status: 304
+
+ proxy-response:
+ status: 200
+ headers:
+ fields:
+ - [ X-Cache, { value: hit-stale, as: equal } ]
+