This is an automated email from the ASF dual-hosted git repository.

cmcfarlen pushed a commit to branch 10.2.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git

commit 4f9720e84b02ba47d0a1a131c13e45719e7ef151
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
    
    (cherry picked from commit 494ff824dcce3bcbfae1239f70e9bf8b26aa4459)
---
 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 cfb11e82cb..be630e72bb 100644
--- a/doc/admin-guide/files/records.yaml.en.rst
+++ b/doc/admin-guide/files/records.yaml.en.rst
@@ -1959,6 +1959,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 fa2a9a7145..107a8dc823 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 } ]
+

Reply via email to