You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by cm...@apache.org on 2026/04/21 14:26:47 UTC

(trafficserver) 02/05: Add option 3 to connect down policy (#12928)

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 d02f084a8f3cb077c3efed2b60aa3822bbf763eb
Author: Vijay Mamidi <vi...@yahoo.com>
AuthorDate: Sat Mar 7 09:47:53 2026 -0600

    Add option 3 to connect down policy (#12928)
    
    * Add option 3 to connect down policy to count inactive connections as failures
    
    * Add option 3 to connect down policy to count inactive connections as failures
    
    * Add option 3 to connect down policy to count inactive connections as failures
    
    * Update src/proxy/http/HttpSM.cc
    
    Co-authored-by: Copilot <17...@users.noreply.github.com>
    
    * Add option 3 to connect down policy to count inactive connections as failures
    
    * Add option 3 to connect down policy to count inactive connections as failures
    
    * Add option 3 to connect down policy to count inactive connections as failures
    
    ---------
    
    Co-authored-by: vmamidi <vm...@apple.com>
    Co-authored-by: Copilot <17...@users.noreply.github.com>
    (cherry picked from commit e92356bd8def86e0f8d85bdb751a8f4aa0785b2e)
---
 doc/admin-guide/files/records.yaml.en.rst          |  10 +-
 src/proxy/http/HttpSM.cc                           |   8 +-
 .../connect_down_policy_3.test.py                  | 113 +++++++++++++++++++++
 .../replay/inactive_timeout.replay.yaml            |  38 +++++++
 4 files changed, 165 insertions(+), 4 deletions(-)

diff --git a/doc/admin-guide/files/records.yaml.en.rst b/doc/admin-guide/files/records.yaml.en.rst
index e989bc80e4..921ec44251 100644
--- a/doc/admin-guide/files/records.yaml.en.rst
+++ b/doc/admin-guide/files/records.yaml.en.rst
@@ -1833,9 +1833,13 @@ Origin Server Connect Attempts
 .. ts:cv:: CONFIG proxy.config.http.connect.down.policy INT 2
    :overridable:
 
-   Controls what origin server connection failures contribute to marking a server down. When set to 2, any connection failure during the TCP and TLS
-   handshakes will contribute to marking the server down. When set to 1, only TCP handshake failures will contribute to marking a server down.
-   When set to 0, no connection failures will be used towards marking a server down.
+   Controls what origin server connection failures contribute to marking a server down.
+   When set to ``2``, any connection failure during the TCP and TLS handshakes will
+   contribute to marking the server down. When set to ``1``, only TCP handshake failures
+   will contribute to marking a server down. When set to ``0``, no connection failures
+   will be used towards marking a server down. When set to ``3``, all failures covered
+   by ``2`` plus transaction inactive timeouts (server goes silent after connection is
+   established) will contribute to marking a server down.
 
 .. ts:cv:: CONFIG proxy.config.http.server_max_connections INT 0
    :reloadable:
diff --git a/src/proxy/http/HttpSM.cc b/src/proxy/http/HttpSM.cc
index 9dd5938cf1..38312c9bf6 100644
--- a/src/proxy/http/HttpSM.cc
+++ b/src/proxy/http/HttpSM.cc
@@ -4676,12 +4676,18 @@ HttpSM::track_connect_fail() const
   bool retval = false;
   if (t_state.current.server->had_connect_fail()) {
     // What does our policy say?
-    if (t_state.txn_conf->connect_down_policy == 2) { // Any connection error through TLS handshake
+    if (t_state.txn_conf->connect_down_policy == 2 ||
+        t_state.txn_conf->connect_down_policy == 3) { // Any connection error through TLS handshake
       retval = true;
     } else if (t_state.txn_conf->connect_down_policy == 1) { // Any connection error through TCP
       retval = t_state.current.server->connect_result != -ENET_SSL_CONNECT_FAILED;
     }
   }
+  // Policy 3 additionally marks the server down on transaction inactive timeout,
+  // even when had_connect_fail() is false (connect_result was cleared at CONNECTION_ALIVE).
+  if (!retval && t_state.txn_conf->connect_down_policy == 3) {
+    retval = (t_state.current.server->state == HttpTransact::INACTIVE_TIMEOUT);
+  }
   return retval;
 }
 
diff --git a/tests/gold_tests/connect_down_policy/connect_down_policy_3.test.py b/tests/gold_tests/connect_down_policy/connect_down_policy_3.test.py
new file mode 100644
index 0000000000..f4f5d9681a
--- /dev/null
+++ b/tests/gold_tests/connect_down_policy/connect_down_policy_3.test.py
@@ -0,0 +1,113 @@
+'''
+Verify proxy.config.http.connect.down.policy=3 marks the origin down on
+transaction inactive timeout (server goes silent after connection is established),
+and that policy=2 does not mark the origin down for the same scenario.
+'''
+#  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.
+
+import os
+
+Test.Summary = '''
+Verify connect.down.policy=3 marks the origin down on inactive timeout after
+connection is established, and policy=2 does not.
+'''
+
+REPLAY_FILE = "replay/inactive_timeout.replay.yaml"
+
+# Inactivity timeout in seconds. The replay server-response delay (10s) is
+# intentionally longer so ATS fires the timeout before the server replies.
+INACTIVITY_TIMEOUT = 3
+
+
+class ConnectDownPolicy3Test:
+    """
+    Test that policy=3 marks the origin server down when the server goes silent
+    and ATS fires an INACTIVE_TIMEOUT.
+
+    Sequence:
+      1. ATS connects to the origin and sends the request.
+      2. The origin delays its response beyond INACTIVITY_TIMEOUT.
+      3. ATS fires VC_EVENT_INACTIVITY_TIMEOUT, calling track_connect_fail().
+      4. Under policy=3 track_connect_fail() returns true → mark_host_failure().
+      5. With connect_attempts_rr_retries=0, increment_fail_count marks the host
+         down immediately (fcount >= 0), writing a "marking down" entry to error.log.
+    """
+
+    def __init__(self, policy, expect_mark_down):
+        self._policy = policy
+        self._expect_mark_down = expect_mark_down
+        self._name = f"policy{policy}"
+        self._server = Test.MakeVerifierServerProcess(f"server-{self._name}", REPLAY_FILE)
+        self._configure_trafficserver()
+
+    def _configure_trafficserver(self):
+        self._ts = Test.MakeATSProcess(f"ts-{self._name}", enable_cache=False)
+
+        self._ts.Disk.remap_config.AddLine(f"map / http://127.0.0.1:{self._server.Variables.http_port}/")
+
+        self._ts.Disk.records_config.update(
+            {
+                'proxy.config.diags.debug.enabled': 1,
+                'proxy.config.diags.debug.tags': 'hostdb|http',
+                # Use policy under test.
+                'proxy.config.http.connect.down.policy': self._policy,
+                # No connection retries — the single timeout failure is sufficient.
+                'proxy.config.http.connect_attempts_max_retries': 0,
+                # Set rr_retries=0 so it does not exceed max_retries (which would
+                # emit a Warning). With max_retries=0, increment_fail_count marks
+                # the host down on the first failure (fcount >= 0 is always true).
+                'proxy.config.http.connect_attempts_rr_retries': 0,
+                # Short server-side inactivity timeout so the test runs quickly.
+                'proxy.config.http.transaction_no_activity_timeout_out': INACTIVITY_TIMEOUT,
+                # Keep the host marked down long enough to verify.
+                'proxy.config.hostdb.fail.timeout': 60,
+            })
+
+    def _test_inactive_timeout(self):
+        tr = Test.AddTestRun(f"policy={self._policy}: inactive timeout triggers 504")
+        tr.Processes.Default.StartBefore(self._server)
+        tr.Processes.Default.StartBefore(self._ts)
+        tr.AddVerifierClientProcess(f"client-{self._name}", REPLAY_FILE, http_ports=[self._ts.Variables.port])
+
+    def _test_mark_down(self):
+        if self._expect_mark_down:
+            # Wait for error.log to appear then verify it contains the mark-down entry.
+            tr = Test.AddTestRun(f"policy={self._policy}: check error.log for mark-down")
+            tr.Processes.Default.Command = (
+                os.path.join(Test.Variables.AtsTestToolsDir, 'condwait') + ' 60 1 -f ' +
+                os.path.join(self._ts.Variables.LOGDIR, 'error.log'))
+            self._ts.Disk.error_log.Content = Testers.ContainsExpression(
+                "marking down", f"policy={self._policy}: origin should be marked down after inactive timeout")
+        else:
+            # Pre-create error.log so ExcludesExpression can read it even if ATS
+            # never writes to it (policy=2 should not mark the host down).
+            error_log_path = os.path.join(self._ts.Variables.LOGDIR, 'error.log')
+            tr = Test.AddTestRun(f"policy={self._policy}: verify no mark-down in error.log")
+            tr.Processes.Default.Command = f"touch {error_log_path}"
+            self._ts.Disk.error_log.Content = Testers.ExcludesExpression(
+                "marking down", f"policy={self._policy}: origin should NOT be marked down after inactive timeout")
+
+    def run(self):
+        self._test_inactive_timeout()
+        self._test_mark_down()
+
+
+# Policy 3: inactive timeout SHOULD mark the origin down.
+ConnectDownPolicy3Test(policy=3, expect_mark_down=True).run()
+
+# Policy 2: inactive timeout should NOT mark the origin down.
+ConnectDownPolicy3Test(policy=2, expect_mark_down=False).run()
diff --git a/tests/gold_tests/connect_down_policy/replay/inactive_timeout.replay.yaml b/tests/gold_tests/connect_down_policy/replay/inactive_timeout.replay.yaml
new file mode 100644
index 0000000000..75bfd8f93e
--- /dev/null
+++ b/tests/gold_tests/connect_down_policy/replay/inactive_timeout.replay.yaml
@@ -0,0 +1,38 @@
+#  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"
+
+sessions:
+- transactions:
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /inactive/timeout/test
+      headers:
+        fields:
+        - [ Host, example.com ]
+        - [ uuid, 1 ]
+
+    # Server accepts the connection but goes silent (delay longer than inactivity timeout).
+    server-response:
+      status: 200
+      delay: 10s
+
+    # ATS should time out waiting for the server response and return 504.
+    proxy-response:
+      status: 504