You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by jr...@apache.org on 2019/05/16 14:53:50 UTC

[trafficserver] branch 8.0.x updated: add a reason tag to traffic_control host subcommand.

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

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


The following commit(s) were added to refs/heads/8.0.x by this push:
     new ed0cd73  add a reason tag to traffic_control host subcommand.
ed0cd73 is described below

commit ed0cd7311624847529b6d1e54e58759975ebc505
Author: John Rushford <jr...@apache.org>
AuthorDate: Thu Jun 21 17:21:58 2018 +0000

    add a reason tag to traffic_control host subcommand.
    
    (cherry picked from commit b3dbff10a34bdb809914e3671922bffc508befd9)
    (cherry picked from commit 0b8eb39b86103f9a6bc586a0c3dca60328455670)
    
    Fixes a segfault that may occur when the debug and a lookup fails
    to find an entry in the hoststatus hash table.
    
    (cherry picked from commit 05dd0fdfb223d21b9a54236ba5fb7ab2a0e04050)
    
    Fix a regression in the traffic_ctl host status subcommand.  Host
    statuses were not being found as the reason tag was left off.
    
    (cherry picked from commit 58734d3c435fce12849193674c53c475ddb6f3a0)
    
    Corrects the wording of the usage messages for traffic_ctl host subcommand.
    
    (cherry picked from commit 3e6def494e41ed45d68e2f890ec82999b3ff9a3b)
    
    TCL: Rmove TCL dependency from HostStatus
    
    (cherry picked from commit 168275b726adc8bff5834e47c44a1ee460a13ee8)
    
    Use optionally provided hash string for hashing
    
    (cherry picked from commit f22b06f3f7c2aba5b14d8e633b2aefc7a4a14ef7)
    
    Fix a self detection issue where parents are not marked down when
    proxy.config.http.parent_proxy.self_detect is set to 2 because of
    multiple calls to creatHostStat() which was marking parents back up.
    Also added a new HostStatus Reason Code SELF_DETECT used when self
    detection marks a parent down.
    
    (cherry picked from commit b09ac093c7ec27f7a408b3ec2036592521d8da89)
    
    Change HostStatus to use only one stat per host.  The host stat
    is now a string value containing all time and reason data so that
    it may be restored from persistent store when ATS is restarted.
    
    (cherry picked from commit 8b9b1a75e007e0949de4813a6307651533eaa4cf)
    
     Conflicts:
    	doc/appendices/command-line/traffic_ctl.en.rst
    	iocore/cache/test/stub.cc
    	src/traffic_ctl/host.cc
    	src/traffic_server/HostStatus.cc
    
    (cherry picked from commit ff69f60a91485d3ffc58dafd2aebedca80c5526b)
    
    Load the Socks configuration after the host state persistence data is loaded.
    
    (cherry picked from commit 96c0c71fde39c4c64b0c4dacbd44920093c6a3f2)
---
 doc/admin-guide/files/parent.config.en.rst     |   8 +
 doc/appendices/command-line/traffic_ctl.en.rst |  48 ++-
 iocore/net/I_NetProcessor.h                    |   2 +
 iocore/net/P_UnixNetProcessor.h                |   1 +
 iocore/net/UnixNetProcessor.cc                 |  21 +-
 mgmt/LocalManager.cc                           |   4 +-
 mgmt/LocalManager.h                            |   2 +-
 mgmt/api/CoreAPI.cc                            |  11 +-
 mgmt/api/CoreAPI.h                             |   4 +-
 mgmt/api/CoreAPIRemote.cc                      |  12 +-
 mgmt/api/INKMgmtAPI.cc                         |   8 +-
 mgmt/api/NetworkMessage.cc                     |   4 +-
 mgmt/api/TSControlMain.cc                      |  13 +-
 mgmt/api/include/mgmtapi.h                     |   4 +-
 proxy/HostStatus.h                             | 157 ++++++++-
 proxy/ParentSelection.cc                       | 149 ++++++---
 proxy/ParentSelection.h                        |   1 +
 proxy/shared/UglyLogStubs.cc                   |   6 +
 src/traffic_ctl/Makefile.inc                   |   1 +
 src/traffic_ctl/host.cc                        |  65 +++-
 src/traffic_server/HostStatus.cc               | 430 ++++++++++++++++++++-----
 src/traffic_server/traffic_server.cc           |   2 +
 22 files changed, 766 insertions(+), 187 deletions(-)

diff --git a/doc/admin-guide/files/parent.config.en.rst b/doc/admin-guide/files/parent.config.en.rst
index 3a26e95..ca54825 100644
--- a/doc/admin-guide/files/parent.config.en.rst
+++ b/doc/admin-guide/files/parent.config.en.rst
@@ -141,6 +141,14 @@ The following list shows the possible actions and their allowed values.
 
         parent="p1.x.com:8080|2.0, 192.168.0.3:80|3.0, 192.168.0.4:80|5.0"
 
+    If ``round_robin`` is set to ``consistent_hash``, you may add a ``unique hash string``
+    following the ``weight`` for each parent.  The ``hash string`` must start with ``&``
+	  and is used to build both the primary and secondary rings using the ``hash string``
+    for each parent insted of the parents ``hostname`` or ``ip address``. This can be
+    useful so that two different hosts may be used to cache the same requests.  Example::
+
+        parent="p1.x.com:80|1.0&abcdef, p2.x.com:80|1.0&xyzl, p3.x.com:80|1.0&ldffg" round_robin=consistent_hash
+
 .. _parent-config-format-secondary-parent:
 
 ``secondary_parent``
diff --git a/doc/appendices/command-line/traffic_ctl.en.rst b/doc/appendices/command-line/traffic_ctl.en.rst
index 7bc4797..191376a 100644
--- a/doc/appendices/command-line/traffic_ctl.en.rst
+++ b/doc/appendices/command-line/traffic_ctl.en.rst
@@ -258,21 +258,59 @@ traffic_ctl host
     Get the current status of the hosts used in parent.config as a next hop in a multi-tiered cache heirarchy.  The value 0 or 1 is returned indicating that the host is marked as down '0' or marked as up '1'.  If a host is marked as down, it will not be used as the next hop parent, another host marked as up will be chosen.
 
 .. program:: traffic_ctl host
-.. option:: down --time seconds HOSTNAME [HOSTNAME ...]
+.. option:: down --time seconds --reason 'all|active|local|manual|self_detect' HOSTNAME [HOSTNAME ...]
 
     Marks the listed hosts as down so that they will not be chosen as a next hop parent.
-    If the --time option is included, the host is marked down for the specified number of
-    seconds after which the host will automatically be marked up.  0 seconds marks the host
-    down indefinately until marked up manually and is the default.
+    If the --time option is included, the host is marked down for the specified number of 
+    seconds after which the host will automatically be marked up.  0 seconds marks the host 
+    down indefinitely until marked up manually and is the default. A reason tag may be used
+    when marking a host down.  Valid values are 'manual', 'active', 'local', 'self_detect',
+    or 'all', 'manual' is used as the default.  The tags are used to indicate wether the host 
+    was marked down manually or by an 'active' or 'local' health check.  'self_detect' indicates
+    that a parent entry in parent.config was marked down because the entry refers to the
+    local host so, it is automatically marked down to prevent requests from looping. A host is
+    not marked up until all reason codes are cleared by marking up the host for the specified
+    reason code. The 'all' reason should be used very carefully as in the case of marking up,
+    it would clear the 'self_detect' reason and could result in trasaction failures due to
+    looping.  
+    
+    A stat is created for each host, with a the host fqdn and is prefixed with the string 
+    `proxy.process.host_status` with a string value.  The string value is a 
+    serialized representation of the Host status struct showing all current data ie, reasons, 
+    marked down times, and down time for each host.  The stats may be viewed using the 
+    `traffic_ctl metric` command or through the `stats_over_http` endpoint.
 
 .. program:: traffic_ctl host
-.. option:: up HOSTNAME [HOSTNAME ...]
+.. option:: up --reason 'all|active|local|manual|self_detect' HOSTNAME [HOSTNAME ...]
 
     Marks the listed hosts as up so that they will be available for use as a next hop parent.
+    By default, the 'manual' reason tag is used when marking up a host.  Use the --reason
+    tag to mark the host reason code as up using one of 'manual', 'active', 'local', 'all',
+    or 'self_detect'  Be careful with the 'all' reason as it clears all reason codes including
+    'self_detect' which is used to prevent a transaction from looping.
 
 Examples
 ========
 
+Mark down a host with `traffic_ctl` and view the associated host stats::
+=======
+
+$ traffic_ctl host down cdn-cache-02.foo.com --reason manual
+
+$ /opt/trafficserver/bin/traffic_ctl metric match host_status
+proxy.process.host_status.cdn-cache-01.foo.com HOST_STATUS_DOWN,ACTIVE:UP:0:0,LOCAL:UP:0:0,MANUAL:DOWN:1556896844:0,SELF_DETECT:UP:0:0
+proxy.process.host_status.cdn-cache-02.foo.com HOST_STATUS_UP,ACTIVE:UP:0:0,LOCAL:UP:0:0,MANUAL:UP:0:0,SELF_DETECT:UP:0:0
+proxy.process.host_status.cdn-cache-origin-01.foo.com HOST_STATUS_UP,ACTIVE:UP:0:0,LOCAL:UP:0:0,MANUAL:UP:0:0,SELF_DETECT:UP:0:0
+
+In the example above, 'cdn-cache-01.foo.com' is unavailable, `HOST_STATUS_DOWN` and was marked down 
+for the `manual` reason, `MANUAL:DOWN:1556896844:0`, at the time indicated by the UNIX time stamp 
+`1556896844`.  To make the host available, one would have to clear the `manual` reason using:: 
+`traffic_ctl host up cdn-cache-01.foo.com --reason manual`
+
+Configure Traffic Server to insert ``Via`` header in the response to
+the client::
+
+=======
 Configure Traffic Server to log in Squid format::
 
     $ traffic_ctl config set proxy.config.log.squid_log_enabled 1
diff --git a/iocore/net/I_NetProcessor.h b/iocore/net/I_NetProcessor.h
index 03934ec..c9b918a 100644
--- a/iocore/net/I_NetProcessor.h
+++ b/iocore/net/I_NetProcessor.h
@@ -207,6 +207,8 @@ public:
   */
   virtual void init() = 0;
 
+  virtual void init_socks() = 0;
+
   inkcoreapi virtual NetVConnection *allocate_vc(EThread *) = 0;
 
   /** Private constructor. */
diff --git a/iocore/net/P_UnixNetProcessor.h b/iocore/net/P_UnixNetProcessor.h
index b076c0a..d125b7f 100644
--- a/iocore/net/P_UnixNetProcessor.h
+++ b/iocore/net/P_UnixNetProcessor.h
@@ -43,6 +43,7 @@ public:
   NetVConnection *allocate_vc(EThread *t) override;
 
   void init() override;
+  void init_socks() override;
 
   Event *accept_thread_event;
 
diff --git a/iocore/net/UnixNetProcessor.cc b/iocore/net/UnixNetProcessor.cc
index 589c843..f796575 100644
--- a/iocore/net/UnixNetProcessor.cc
+++ b/iocore/net/UnixNetProcessor.cc
@@ -446,7 +446,18 @@ UnixNetProcessor::init()
   d.rec_int = 0;
   change_net_connections_throttle(nullptr, RECD_INT, d, nullptr);
 
-  // Socks
+  /*
+   * Stat pages
+   */
+  extern Action *register_ShowNet(Continuation * c, HTTPHdr * h);
+  if (etype == ET_NET) {
+    statPagesManager.register_http("net", register_ShowNet);
+  }
+}
+
+void
+UnixNetProcessor::init_socks()
+{
   if (!netProcessor.socks_conf_stuff) {
     socks_conf_stuff = new socks_conf_struct;
     loadSocksConfiguration(socks_conf_stuff);
@@ -459,14 +470,6 @@ UnixNetProcessor::init()
       socks_conf_stuff = netProcessor.socks_conf_stuff;
     }
   }
-
-  /*
-   * Stat pages
-   */
-  extern Action *register_ShowNet(Continuation * c, HTTPHdr * h);
-  if (etype == ET_NET) {
-    statPagesManager.register_http("net", register_ShowNet);
-  }
 }
 
 // Virtual function allows creation of an
diff --git a/mgmt/LocalManager.cc b/mgmt/LocalManager.cc
index e0e590d..2a3b0c3 100644
--- a/mgmt/LocalManager.cc
+++ b/mgmt/LocalManager.cc
@@ -136,9 +136,9 @@ LocalManager::hostStatusSetDown(const char *marshalled_req, int len)
 }
 
 void
-LocalManager::hostStatusSetUp(const char *marshalled_req)
+LocalManager::hostStatusSetUp(const char *marshalled_req, int len)
 {
-  signalEvent(MGMT_EVENT_HOST_STATUS_UP, marshalled_req);
+  signalEvent(MGMT_EVENT_HOST_STATUS_UP, marshalled_req, len);
   return;
 }
 
diff --git a/mgmt/LocalManager.h b/mgmt/LocalManager.h
index 4ff3591..4dc34db 100644
--- a/mgmt/LocalManager.h
+++ b/mgmt/LocalManager.h
@@ -96,7 +96,7 @@ public:
   void rollLogFiles();
   void clearStats(const char *name = nullptr);
   void hostStatusSetDown(const char *marshalled_req, int len);
-  void hostStatusSetUp(const char *marshalled_req);
+  void hostStatusSetUp(const char *marshalled_req, int len);
 
   bool processRunning();
 
diff --git a/mgmt/api/CoreAPI.cc b/mgmt/api/CoreAPI.cc
index 5ebaf7f..e479eb6 100644
--- a/mgmt/api/CoreAPI.cc
+++ b/mgmt/api/CoreAPI.cc
@@ -885,11 +885,12 @@ EventSignalCbUnregister(const char *event_name, TSEventSignalFunc func)
  *-------------------------------------------------------------------------
  * Sets the HOST status to Down
  *
- * 'marshalled_req' is marshalled here, (host_name and down_time).
+ * 'marshalled_req' is marshalled here, (host_name and down_time, na).
  * 'len' is the length of the 'req' marshaled data.
+ * 'na' unused.
  */
 TSMgmtError
-HostStatusSetDown(const char *marshalled_req, int len)
+HostStatusSetDown(const char *marshalled_req, int len, const char *na)
 {
   lmgmt->hostStatusSetDown(marshalled_req, len);
   return TS_ERR_OKAY;
@@ -901,11 +902,13 @@ HostStatusSetDown(const char *marshalled_req, int len)
  * Sets the HOST status to Up
  *
  * 'marshalled_req' is marshalled here, host_name.
+ * 'len' is the length of 'req'
+ * 'na' unused.
  */
 TSMgmtError
-HostStatusSetUp(const char *marshalled_req)
+HostStatusSetUp(const char *marshalled_req, int len, const char *na)
 {
-  lmgmt->hostStatusSetUp(marshalled_req);
+  lmgmt->hostStatusSetUp(marshalled_req, len);
   return TS_ERR_OKAY;
 }
 
diff --git a/mgmt/api/CoreAPI.h b/mgmt/api/CoreAPI.h
index c6e3b1c..aa4a444 100644
--- a/mgmt/api/CoreAPI.h
+++ b/mgmt/api/CoreAPI.h
@@ -82,6 +82,6 @@ TSMgmtError EventIsActive(const char *event_name, bool *is_current);
 TSMgmtError EventSignalCbRegister(const char *event_name, TSEventSignalFunc func, void *data);
 TSMgmtError EventSignalCbUnregister(const char *event_name, TSEventSignalFunc func);
 
-TSMgmtError HostStatusSetDown(const char *host_name, int down_time);
-TSMgmtError HostStatusSetUp(const char *host_name);
+TSMgmtError HostStatusSetDown(const char *host_name, int down_time, const char *reason);
+TSMgmtError HostStatusSetUp(const char *host_name, int down_time, const char *reason);
 TSMgmtError StatsReset(const char *name = nullptr);
diff --git a/mgmt/api/CoreAPIRemote.cc b/mgmt/api/CoreAPIRemote.cc
index 67a6651..8406d78 100644
--- a/mgmt/api/CoreAPIRemote.cc
+++ b/mgmt/api/CoreAPIRemote.cc
@@ -1033,26 +1033,28 @@ EventSignalCbUnregister(const char *event_name, TSEventSignalFunc func)
 }
 
 TSMgmtError
-HostStatusSetDown(const char *host_name, int down_time)
+HostStatusSetDown(const char *host_name, int down_time, const char *reason)
 {
-  fprintf(stderr, "%s:%s:%d - host_name: %s, down_time: %d\n", __FILE__, __func__, __LINE__, host_name, down_time);
   TSMgmtError ret         = TS_ERR_PARAMS;
   OpType op               = OpType::HOST_STATUS_DOWN;
   MgmtMarshallString name = const_cast<MgmtMarshallString>(host_name);
+  MgmtMarshallString re   = const_cast<MgmtMarshallString>(reason);
   MgmtMarshallInt dtime   = down_time;
 
-  ret = MGMTAPI_SEND_MESSAGE(main_socket_fd, op, &op, &name, &dtime);
+  ret = MGMTAPI_SEND_MESSAGE(main_socket_fd, op, &op, &name, &re, &dtime);
   return (ret == TS_ERR_OKAY) ? parse_generic_response(op, main_socket_fd) : ret;
 }
 
 TSMgmtError
-HostStatusSetUp(const char *host_name)
+HostStatusSetUp(const char *host_name, int down_time, const char *reason)
 {
   TSMgmtError ret         = TS_ERR_PARAMS;
   OpType op               = OpType::HOST_STATUS_UP;
   MgmtMarshallString name = const_cast<MgmtMarshallString>(host_name);
+  MgmtMarshallString re   = const_cast<MgmtMarshallString>(reason);
+  MgmtMarshallInt dtime   = down_time;
 
-  ret = MGMTAPI_SEND_MESSAGE(main_socket_fd, op, &op, &name);
+  ret = MGMTAPI_SEND_MESSAGE(main_socket_fd, op, &op, &name, &re, &dtime);
   return (ret == TS_ERR_OKAY) ? parse_generic_response(op, main_socket_fd) : ret;
 }
 
diff --git a/mgmt/api/INKMgmtAPI.cc b/mgmt/api/INKMgmtAPI.cc
index 9cb64ad..15d2caf 100644
--- a/mgmt/api/INKMgmtAPI.cc
+++ b/mgmt/api/INKMgmtAPI.cc
@@ -416,15 +416,15 @@ TSRecordEleDestroy(TSRecordEle *ele)
 
 /*--- host status operations ----------------------------------------------- */
 tsapi TSMgmtError
-TSHostStatusSetUp(const char *host_name)
+TSHostStatusSetUp(const char *host_name, int down_time, const char *reason)
 {
-  return HostStatusSetUp(host_name);
+  return HostStatusSetUp(host_name, down_time, reason);
 }
 
 tsapi TSMgmtError
-TSHostStatusSetDown(const char *host_name, int down_time)
+TSHostStatusSetDown(const char *host_name, int down_time, const char *reason)
 {
-  return HostStatusSetDown(host_name, down_time);
+  return HostStatusSetDown(host_name, down_time, reason);
 }
 
 /*--- statistics operations ----------------------------------------------- */
diff --git a/mgmt/api/NetworkMessage.cc b/mgmt/api/NetworkMessage.cc
index 9e5f35f..de2e7fa 100644
--- a/mgmt/api/NetworkMessage.cc
+++ b/mgmt/api/NetworkMessage.cc
@@ -61,8 +61,8 @@ static const struct NetCmdOperation requests[] = {
   /* SERVER_BACKTRACE           */ {2, {MGMT_MARSHALL_INT, MGMT_MARSHALL_INT}},
   /* RECORD_DESCRIBE_CONFIG     */ {3, {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_INT}},
   /* LIFECYCLE_MESSAGE          */ {3, {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_DATA}},
-  /* HOST_STATUS_HOST_UP        */ {2, {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING}},
-  /* HOST_STATUS_HOST_DOWN      */ {3, {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_INT}},
+  /* HOST_STATUS_HOST_UP        */ {4, {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_STRING, MGMT_MARSHALL_INT}},
+  /* HOST_STATUS_HOST_DOWN      */ {4, {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_STRING, MGMT_MARSHALL_INT}},
 };
 
 // Responses always begin with a TSMgmtError code, followed by additional fields.
diff --git a/mgmt/api/TSControlMain.cc b/mgmt/api/TSControlMain.cc
index e709415..b7626bc 100644
--- a/mgmt/api/TSControlMain.cc
+++ b/mgmt/api/TSControlMain.cc
@@ -808,12 +808,14 @@ static TSMgmtError
 handle_host_status_up(int fd, void *req, size_t reqlen)
 {
   OpType optype;
-  MgmtMarshallString name = nullptr;
+  MgmtMarshallString name   = nullptr;
+  MgmtMarshallString reason = nullptr;
   MgmtMarshallInt err;
+  MgmtMarshallInt down_time;
 
-  err = recv_mgmt_request(req, reqlen, OpType::HOST_STATUS_UP, &optype, &name);
+  err = recv_mgmt_request(req, reqlen, OpType::HOST_STATUS_UP, &optype, &name, &reason, &down_time);
   if (err == TS_ERR_OKAY) {
-    err = HostStatusSetUp(name);
+    lmgmt->signalEvent(MGMT_EVENT_HOST_STATUS_UP, static_cast<char *>(req), reqlen);
   }
 
   ats_free(name);
@@ -830,11 +832,12 @@ static TSMgmtError
 handle_host_status_down(int fd, void *req, size_t reqlen)
 {
   OpType optype;
-  MgmtMarshallString name = nullptr;
+  MgmtMarshallString name   = nullptr;
+  MgmtMarshallString reason = nullptr;
   MgmtMarshallInt err;
   MgmtMarshallInt down_time;
 
-  err = recv_mgmt_request(req, reqlen, OpType::HOST_STATUS_DOWN, &optype, &name, &down_time);
+  err = recv_mgmt_request(req, reqlen, OpType::HOST_STATUS_DOWN, &optype, &name, &reason, &down_time);
   if (err == TS_ERR_OKAY) {
     lmgmt->signalEvent(MGMT_EVENT_HOST_STATUS_DOWN, static_cast<char *>(req), reqlen);
   }
diff --git a/mgmt/api/include/mgmtapi.h b/mgmt/api/include/mgmtapi.h
index 8fa22bc..588592b 100644
--- a/mgmt/api/include/mgmtapi.h
+++ b/mgmt/api/include/mgmtapi.h
@@ -428,8 +428,8 @@ tsapi TSMgmtError TSReadFromUrl(char *url, char **header, int *headerSize, char
  * NOTE: header and headerSize can be NULL
  */
 tsapi TSMgmtError TSReadFromUrlEx(const char *url, char **header, int *headerSize, char **body, int *bodySize, int timeout);
-tsapi TSMgmtError TSHostStatusSetUp(const char *host_name);
-tsapi TSMgmtError TSHostStatusSetDown(const char *host_name, int down_time);
+tsapi TSMgmtError TSHostStatusSetUp(const char *host_name, int down_time, const char *reason);
+tsapi TSMgmtError TSHostStatusSetDown(const char *host_name, int down_time, const char *reason);
 /*--- statistics operations -----------------------------------------------*/
 /* TSStatsReset: sets all the statistics variables to their default values
  * Outpue: TSErrr
diff --git a/proxy/HostStatus.h b/proxy/HostStatus.h
index 18f127e..65e9e76 100644
--- a/proxy/HostStatus.h
+++ b/proxy/HostStatus.h
@@ -23,30 +23,156 @@
 
 /*****************************************************************************
  *
- *  HostSelection.h - Interface to Host Selection System
+ *  HostStatus.h - Interface to Host Status System
  *
  *
  ****************************************************************************/
 
 #pragma once
 
-#include "ControlBase.h"
-#include "ControlMatcher.h"
+#include <time.h>
+#include <string>
+#include <sstream>
+#include "tscore/ink_rwlock.h"
 #include "records/P_RecProcess.h"
 
+#include <unordered_map>
+
+// host_status stats prefix.
+static const std::string stat_prefix = "proxy.process.host_status.";
+
 enum HostStatus_t {
   HOST_STATUS_INIT,
   HOST_STATUS_DOWN,
   HOST_STATUS_UP,
 };
 
-struct HostStatRec_t {
-  HostStatus_t status;
-  time_t marked_down;     // the time that this host was marked down.
-  unsigned int down_time; // number of seconds that the host should be down, 0 is indefinately
+static const constexpr char *HostStatusNames[3] = {"HOST_STATUS_INIT", "HOST_STATUS_DOWN", "HOST_STATUS_UP"};
+static const constexpr char *ReasonStatus[2]    = {"UP", "DOWN"};
+
+struct Reason {
+  static constexpr const unsigned int ACTIVE      = 0x1;
+  static constexpr const unsigned int LOCAL       = 0x2;
+  static constexpr const unsigned int MANUAL      = 0x4;
+  static constexpr const unsigned int SELF_DETECT = 0x8;
+  static constexpr const unsigned int ALL         = 0xf;
+
+  static constexpr const char *ACTIVE_REASON      = "active";
+  static constexpr const char *LOCAL_REASON       = "local";
+  static constexpr const char *MANUAL_REASON      = "manual";
+  static constexpr const char *SELF_DETECT_REASON = "self_detect";
+  static constexpr const char *ALL_REASON         = "all";
+
+  static constexpr const char *reasons[5] = {ACTIVE_REASON, LOCAL_REASON, MANUAL_REASON, SELF_DETECT_REASON, ALL_REASON};
+
+  static bool
+  validReason(const char *reason)
+  {
+    for (const char *i : reasons) {
+      if (strcmp(i, reason) == 0) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  static unsigned int
+  getReason(const char *reason_str)
+  {
+    if (strcmp(reason_str, ACTIVE_REASON) == 0) {
+      return ACTIVE;
+    } else if (strcmp(reason_str, LOCAL_REASON) == 0) {
+      return LOCAL;
+    } else if (strcmp(reason_str, MANUAL_REASON) == 0) {
+      return MANUAL;
+    } else if (strcmp(reason_str, SELF_DETECT_REASON) == 0) {
+      return SELF_DETECT;
+    } else if (strcmp(reason_str, ALL_REASON) == 0) {
+      return ALL;
+    }
+    // default is MANUAL
+    return MANUAL;
+  }
 };
 
-const std::string stat_prefix = "host_status.";
+// host status POD
+struct HostStatRec {
+  HostStatus_t status;
+  unsigned int reasons;
+  // time the host was marked down for a given reason.
+  time_t active_marked_down;
+  time_t local_marked_down;
+  time_t manual_marked_down;
+  time_t self_detect_marked_down;
+  // number of seconds that the host should be marked down for a given reason.
+  unsigned int active_down_time;
+  unsigned int local_down_time;
+  unsigned int manual_down_time;
+  unsigned int self_detect_down_time;
+
+  HostStatRec();
+  HostStatRec(std::string str);
+  HostStatRec(const HostStatRec &src)
+  {
+    status                  = src.status;
+    reasons                 = src.reasons;
+    active_marked_down      = src.active_marked_down;
+    active_down_time        = src.active_down_time;
+    local_marked_down       = src.local_marked_down;
+    local_down_time         = src.local_down_time;
+    manual_marked_down      = src.manual_marked_down;
+    manual_down_time        = src.manual_down_time;
+    self_detect_marked_down = src.self_detect_marked_down;
+    self_detect_down_time   = src.self_detect_down_time;
+  }
+  ~HostStatRec() {}
+
+  // serialize this HostStatusRec
+  std::stringstream &
+  operator<<(std::stringstream &os)
+  {
+    unsigned int r = getReasonState(Reason::ACTIVE);
+    os << HostStatusNames[status];
+    os << ",ACTIVE:" << ReasonStatus[r] << ":" << active_marked_down << ":" << active_down_time;
+    r = getReasonState(Reason::LOCAL);
+    os << ",LOCAL:" << ReasonStatus[r] << ":" << local_marked_down << ":" << local_down_time;
+    r = getReasonState(Reason::MANUAL);
+    os << ",MANUAL:" << ReasonStatus[r] << ":" << manual_marked_down << ":" << manual_down_time;
+    r = getReasonState(Reason::SELF_DETECT);
+    os << ",SELF_DETECT:" << ReasonStatus[r] << ":" << self_detect_marked_down << ":" << self_detect_down_time;
+
+    return os;
+  }
+
+  // serialize a HostStatRec
+  friend std::stringstream &
+  operator<<(std::stringstream &os, HostStatRec &hs)
+  {
+    unsigned int r = hs.getReasonState(Reason::ACTIVE);
+    os << HostStatusNames[hs.status];
+    os << ",ACTIVE:" << ReasonStatus[r] << ":" << hs.active_marked_down << ":" << hs.active_down_time;
+    r = hs.getReasonState(Reason::LOCAL);
+    os << ",LOCAL:" << ReasonStatus[r] << ":" << hs.local_marked_down << ":" << hs.local_down_time;
+    r = hs.getReasonState(Reason::MANUAL);
+    os << ",MANUAL:" << ReasonStatus[r] << ":" << hs.manual_marked_down << ":" << hs.manual_down_time;
+    r = hs.getReasonState(Reason::SELF_DETECT);
+    os << ",SELF_DETECT:" << ReasonStatus[r] << ":" << hs.self_detect_marked_down << ":" << hs.self_detect_down_time;
+
+    return os;
+  }
+
+  inline unsigned int
+  getReasonState(unsigned int reason)
+  {
+    unsigned int r = 0;
+    if (reasons == 0) {
+      r = 0;
+    } else if (reasons & reason) {
+      r = 1;
+    }
+    return r;
+  }
+};
 
 /**
  * Singleton placeholder for next hop status.
@@ -60,19 +186,20 @@ struct HostStatus {
     static HostStatus instance;
     return instance;
   }
-  void setHostStatus(const char *name, const HostStatus_t status, const unsigned int down_time);
+  void setHostStatus(const char *name, const HostStatus_t status, const unsigned int down_time, const unsigned int reason);
   HostStatus_t getHostStatus(const char *name);
-  void createHostStat(const char *name);
+  void createHostStat(const char *name, const char *data = nullptr);
+  void loadHostStatusFromStats();
+  void loadRecord(std::string &name, HostStatRec &h);
+  int getHostStat(std::string &stat_name, char *buf, unsigned int buf_len);
 
 private:
-  int next_stat_id = 1;
   HostStatus();
   HostStatus(const HostStatus &obj) = delete;
   HostStatus &operator=(HostStatus const &) = delete;
-  int getHostStatId(const char *name);
 
-  InkHashTable *hosts_statuses;  // next hop status, key is hostname or ip string, data is bool (available).
-  InkHashTable *hosts_stats_ids; // next hop stat ids, key is hostname or ip string, data is int stat id.
+  // next hop status, key is hostname or ip string, data is HostStatRec
+  std::unordered_map<std::string, HostStatRec *> hosts_statuses;
+
   ink_rwlock host_status_rwlock;
-  ink_rwlock host_statids_rwlock;
 };
diff --git a/proxy/ParentSelection.cc b/proxy/ParentSelection.cc
index 496146d..cbf83fc 100644
--- a/proxy/ParentSelection.cc
+++ b/proxy/ParentSelection.cc
@@ -369,8 +369,7 @@ ParentRecord::PreProcessParents(const char *val, const int line_num, char *buf,
           continue;
         } else {
           Debug("parent_select", "token: %s, matches this machine.  Marking down self from parent list at line %d", fqdn, line_num);
-          hs.createHostStat(fqdn);
-          hs.setHostStatus(fqdn, HostStatus_t::HOST_STATUS_DOWN, 0);
+          hs.setHostStatus(fqdn, HostStatus_t::HOST_STATUS_DOWN, 0, Reason::SELF_DETECT);
         }
       }
     } else {
@@ -382,8 +381,7 @@ ParentRecord::PreProcessParents(const char *val, const int line_num, char *buf,
         } else {
           Debug("parent_select", "token: %s, matches this machine.  Marking down self from parent list at line %d", token,
                 line_num);
-          hs.createHostStat(token);
-          hs.setHostStatus(token, HostStatus_t::HOST_STATUS_DOWN, 0);
+          hs.setHostStatus(token, HostStatus_t::HOST_STATUS_DOWN, 0, Reason::SELF_DETECT);
         }
       }
     }
@@ -414,7 +412,7 @@ ParentRecord::ProcessParents(char *val, bool isPrimary)
   int numTok          = 0;
   const char *current = nullptr;
   int port            = 0;
-  char *tmp = nullptr, *tmp2 = nullptr;
+  char *tmp = nullptr, *tmp2 = nullptr, *tmp3 = nullptr;
   const char *errPtr = nullptr;
   float weight       = 1.0;
 
@@ -467,23 +465,27 @@ ParentRecord::ProcessParents(char *val, bool isPrimary)
       }
     }
 
+    tmp3 = (char *)strchr(current, '&');
+
     // Make sure that is no garbage beyond the parent
-    //   port or weight
-    char *scan;
-    if (tmp2) {
-      scan = tmp2 + 1;
-    } else {
-      scan = tmp + 1;
-    }
-    for (; *scan != '\0' && (ParseRules::is_digit(*scan) || *scan == '.'); scan++) {
-      ;
-    }
-    for (; *scan != '\0' && ParseRules::is_wslfcr(*scan); scan++) {
-      ;
-    }
-    if (*scan != '\0') {
-      errPtr = "Garbage trailing entry or invalid separator";
-      goto MERROR;
+    //  port or weight
+    if (!tmp3) {
+      char *scan;
+      if (tmp2) {
+        scan = tmp2 + 1;
+      } else {
+        scan = tmp + 1;
+      }
+      for (; *scan != '\0' && (ParseRules::is_digit(*scan) || *scan == '.'); scan++) {
+        ;
+      }
+      for (; *scan != '\0' && ParseRules::is_wslfcr(*scan); scan++) {
+        ;
+      }
+      if (*scan != '\0') {
+        errPtr = "Garbage trailing entry or invalid separator";
+        goto MERROR;
+      }
     }
     // Check to make sure that the string will fit in the
     //  pRecord
@@ -506,7 +508,13 @@ ParentRecord::ProcessParents(char *val, bool isPrimary)
       this->parents[i].name                    = this->parents[i].hostname;
       this->parents[i].available               = true;
       this->parents[i].weight                  = weight;
-      hs.createHostStat(this->parents[i].hostname);
+      if (tmp3) {
+        memcpy(this->parents[i].hash_string, tmp3 + 1, strlen(tmp3));
+        this->parents[i].name = this->parents[i].hash_string;
+      }
+      if (hs.getHostStatus(this->parents[i].hostname) == HostStatus_t::HOST_STATUS_INIT) {
+        hs.setHostStatus(this->parents[i].hostname, HOST_STATUS_UP, 0, Reason::MANUAL);
+      }
     } else {
       memcpy(this->secondary_parents[i].hostname, current, tmp - current);
       this->secondary_parents[i].hostname[tmp - current] = '\0';
@@ -518,8 +526,15 @@ ParentRecord::ProcessParents(char *val, bool isPrimary)
       this->secondary_parents[i].name                    = this->secondary_parents[i].hostname;
       this->secondary_parents[i].available               = true;
       this->secondary_parents[i].weight                  = weight;
-      hs.createHostStat(this->secondary_parents[i].hostname);
+      if (tmp3) {
+        memcpy(this->secondary_parents[i].hash_string, tmp3 + 1, strlen(tmp3));
+        this->secondary_parents[i].name = this->secondary_parents[i].hash_string;
+      }
+      if (hs.getHostStatus(this->secondary_parents[i].hostname) == HostStatus_t::HOST_STATUS_INIT) {
+        hs.setHostStatus(this->secondary_parents[i].hostname, HOST_STATUS_UP, 0, Reason::MANUAL);
+      }
     }
+    tmp3 = nullptr;
   }
 
   if (isPrimary) {
@@ -804,7 +819,7 @@ ParentRecord::Print()
 {
   printf("\t\t");
   for (int i = 0; i < num_parents; i++) {
-    printf(" %s:%d ", parents[i].hostname, parents[i].port);
+    printf(" %s:%d|%f&%s ", parents[i].hostname, parents[i].port, parents[i].weight, parents[i].name);
   }
   printf(" direct=%s\n", (go_direct == true) ? "true" : "false");
   printf(" parent_is_proxy=%s\n", (parent_is_proxy == true) ? "true" : "false");
@@ -1429,7 +1444,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */,
   // Test 184
   // mark fuzzy down with HostStatus API.
   HostStatus &_st = HostStatus::instance();
-  _st.setHostStatus("fuzzy", HOST_STATUS_DOWN, 0);
+  _st.setHostStatus("fuzzy", HOST_STATUS_DOWN, 0, Reason::MANUAL);
 
   ST(184);
   REINIT;
@@ -1440,7 +1455,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */,
 
   // Test 185
   // mark fluffy down and expect furry to be chosen
-  _st.setHostStatus("fluffy", HOST_STATUS_DOWN, 0);
+  _st.setHostStatus("fluffy", HOST_STATUS_DOWN, 0, Reason::MANUAL);
 
   ST(185);
   REINIT;
@@ -1451,9 +1466,9 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */,
 
   // Test 186
   // mark furry and frisky down, fuzzy up and expect fuzzy to be chosen
-  _st.setHostStatus("furry", HOST_STATUS_DOWN, 0);
-  _st.setHostStatus("frisky", HOST_STATUS_DOWN, 0);
-  _st.setHostStatus("fuzzy", HOST_STATUS_UP, 0);
+  _st.setHostStatus("furry", HOST_STATUS_DOWN, 0, Reason::MANUAL);
+  _st.setHostStatus("frisky", HOST_STATUS_DOWN, 0, Reason::MANUAL);
+  _st.setHostStatus("fuzzy", HOST_STATUS_UP, 0, Reason::MANUAL);
 
   ST(186);
   REINIT;
@@ -1471,10 +1486,10 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */,
   REBUILD;
 
   // mark all up.
-  _st.setHostStatus("furry", HOST_STATUS_UP, 0);
-  _st.setHostStatus("fluffy", HOST_STATUS_UP, 0);
-  _st.setHostStatus("frisky", HOST_STATUS_UP, 0);
-  _st.setHostStatus("fuzzy", HOST_STATUS_UP, 0);
+  _st.setHostStatus("furry", HOST_STATUS_UP, 0, Reason::MANUAL);
+  _st.setHostStatus("fluffy", HOST_STATUS_UP, 0, Reason::MANUAL);
+  _st.setHostStatus("frisky", HOST_STATUS_UP, 0, Reason::MANUAL);
+  _st.setHostStatus("fuzzy", HOST_STATUS_UP, 0, Reason::MANUAL);
 
   REINIT;
   br(request, "i.am.rabbit.net");
@@ -1484,7 +1499,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */,
 
   // Test 188
   // mark fuzzy down and expect fluffy.
-  _st.setHostStatus("fuzzy", HOST_STATUS_DOWN, 0);
+  _st.setHostStatus("fuzzy", HOST_STATUS_DOWN, 0, Reason::MANUAL);
 
   ST(188);
   REINIT;
@@ -1495,7 +1510,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */,
 
   // Test 189
   // mark fuzzy back up and expect fuzzy.
-  _st.setHostStatus("fuzzy", HOST_STATUS_UP, 0);
+  _st.setHostStatus("fuzzy", HOST_STATUS_UP, 0, Reason::MANUAL);
 
   ST(189);
   REINIT;
@@ -1511,7 +1526,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */,
   // because the host status is set to down.
   params->markParentDown(result, fail_threshold, retry_time);
   // set host status down
-  _st.setHostStatus("fuzzy", HOST_STATUS_DOWN, 0);
+  _st.setHostStatus("fuzzy", HOST_STATUS_DOWN, 0, Reason::MANUAL);
   // sleep long enough so that fuzzy is retryable
   sleep(params->policy.ParentRetryTime + 1);
   ST(190);
@@ -1522,7 +1537,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */,
 
   // now set the host staus on fuzzy to up and it should now
   // be retried.
-  _st.setHostStatus("fuzzy", HOST_STATUS_UP, 0);
+  _st.setHostStatus("fuzzy", HOST_STATUS_UP, 0, Reason::MANUAL);
   ST(191);
   REINIT;
   br(request, "i.am.rabbit.net");
@@ -1535,10 +1550,10 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */,
   T("dest_domain=rabbit.net parent=fuzzy:80,fluffy:80,furry:80,frisky:80 round_robin=false go_direct=true\n");
   REBUILD;
   // mark all up.
-  _st.setHostStatus("fuzzy", HOST_STATUS_UP, 0);
-  _st.setHostStatus("fluffy", HOST_STATUS_UP, 0);
-  _st.setHostStatus("furry", HOST_STATUS_UP, 0);
-  _st.setHostStatus("frisky", HOST_STATUS_UP, 0);
+  _st.setHostStatus("fuzzy", HOST_STATUS_UP, 0, Reason::MANUAL);
+  _st.setHostStatus("fluffy", HOST_STATUS_UP, 0, Reason::MANUAL);
+  _st.setHostStatus("furry", HOST_STATUS_UP, 0, Reason::MANUAL);
+  _st.setHostStatus("frisky", HOST_STATUS_UP, 0, Reason::MANUAL);
   // fuzzy should be chosen.
   sleep(1);
   REINIT;
@@ -1553,7 +1568,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */,
   sleep(params->policy.ParentRetryTime + 1);
   // since the host status is down even though fuzzy is
   // retryable, fluffy should be chosen
-  _st.setHostStatus("fuzzy", HOST_STATUS_DOWN, 0);
+  _st.setHostStatus("fuzzy", HOST_STATUS_DOWN, 0, Reason::MANUAL);
   REINIT;
   br(request, "i.am.rabbit.net");
   FP;
@@ -1563,7 +1578,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */,
   // set the host status for fuzzy  back up and since its
   // retryable fuzzy should be chosen
   ST(194);
-  _st.setHostStatus("fuzzy", HOST_STATUS_UP, 0);
+  _st.setHostStatus("fuzzy", HOST_STATUS_UP, 0, Reason::MANUAL);
   REINIT;
   br(request, "i.am.rabbit.net");
   FP;
@@ -1684,7 +1699,7 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */,
   T("dest_domain=rabbit.net parent=fuzzy:80|1.0;fluffy:80|1.0 secondary_parent=furry:80|1.0;frisky:80|1.0 "
     "round_robin=consistent_hash go_direct=false secondary_mode=3\n");
   REBUILD;
-  _st.setHostStatus("fuzzy", HOST_STATUS_DOWN, 0);
+  _st.setHostStatus("fuzzy", HOST_STATUS_DOWN, 0, Reason::MANUAL);
   REINIT;
   br(request, "i.am.rabbit.net");
   FP;
@@ -1720,6 +1735,52 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */,
   sleep(1);
   RE(verify(result, PARENT_FAIL, nullptr, 80), 208);
 
+  // Tests 209 through 211 test that host selection is based upon the hash_string
+
+  // Test 209
+  // fuzzy { curly larry, moe } fluffy
+  tbl[0] = '\0';
+  ST(209);
+  T("dest_domain=stooges.net parent=curly:80|1.0&myhash;joe:80|1.0&hishash;larry:80|1.0&ourhash "
+    "round_robin=consistent_hash go_direct=false\n");
+  REBUILD;
+  REINIT;
+  br(request, "i.am.stooges.net");
+  FP;
+  RE(verify(result, PARENT_SPECIFIED, "larry", 80), 209);
+
+  // Test 210
+  // fuzzy { curly larry, moe } fluffy
+  tbl[0] = '\0';
+  ST(210);
+  T("dest_domain=stooges.net parent=curly:80|1.0&ourhash;joe:80|1.0&hishash;larry:80|1.0&myhash "
+    "round_robin=consistent_hash go_direct=false\n");
+  REBUILD;
+  REINIT;
+  br(request, "i.am.stooges.net");
+  FP;
+  RE(verify(result, PARENT_SPECIFIED, "curly", 80), 210);
+
+  // Test 211
+  // fuzzy { curly larry, moe } fluffy
+  tbl[0] = '\0';
+  ST(211);
+  T("dest_domain=stooges.net parent=curly:80|1.0&ourhash;joe:80|1.0&hishash;larry:80|1.0&myhash "
+    "secondary_parent=carol:80|1.0&ourhash;betty:80|1.0&hishash;donna:80|1.0&myhash "
+    "round_robin=consistent_hash go_direct=false\n");
+  REBUILD;
+  REINIT;
+  _st.setHostStatus("curly", HOST_STATUS_DOWN, 0, Reason::MANUAL);
+  br(request, "i.am.stooges.net");
+  FP;
+  RE(verify(result, PARENT_SPECIFIED, "carol", 80), 211);
+
+  // cleanup, allow changes to be persisted to records.snap
+  // so that subsequent test runs do not fail unexpectedly.
+  _st.setHostStatus("curly", HOST_STATUS_UP, 0, Reason::MANUAL);
+  _st.setHostStatus("fuzzy", HOST_STATUS_UP, 0, Reason::MANUAL);
+  sleep(2);
+
   delete request;
   delete result;
   delete params;
diff --git a/proxy/ParentSelection.h b/proxy/ParentSelection.h
index ee3dcab..c16684f 100644
--- a/proxy/ParentSelection.h
+++ b/proxy/ParentSelection.h
@@ -103,6 +103,7 @@ struct pRecord : ATSConsistentHashNode {
   const char *scheme; // for which parent matches (if any)
   int idx;
   float weight;
+  char hash_string[MAXDNAME + 1];
 };
 
 typedef ControlMatcher<ParentRecord, ParentResult> P_table;
diff --git a/proxy/shared/UglyLogStubs.cc b/proxy/shared/UglyLogStubs.cc
index ce4d97b..c54f560 100644
--- a/proxy/shared/UglyLogStubs.cc
+++ b/proxy/shared/UglyLogStubs.cc
@@ -104,6 +104,12 @@ UnixNetProcessor::init()
   ink_release_assert(false);
 }
 
+void
+UnixNetProcessor::init_socks()
+{
+  ink_release_assert(false);
+}
+
 // TODO: The following was necessary only for Solaris, should examine more.
 NetVCOptions const Connection::DEFAULT_OPTIONS;
 NetProcessor::AcceptOptions const NetProcessor::DEFAULT_ACCEPT_OPTIONS;
diff --git a/src/traffic_ctl/Makefile.inc b/src/traffic_ctl/Makefile.inc
index 15cc1ec..1efd85e 100644
--- a/src/traffic_ctl/Makefile.inc
+++ b/src/traffic_ctl/Makefile.inc
@@ -26,6 +26,7 @@ traffic_ctl_traffic_ctl_CPPFLAGS = \
     -I$(abs_top_srcdir)/lib \
     -I$(abs_top_srcdir)/mgmt \
     -I$(abs_top_srcdir)/mgmt/api/include \
+    -I$(abs_top_srcdir)/proxy \
     $(TS_INCLUDES)
 
 traffic_ctl_traffic_ctl_SOURCES = \
diff --git a/src/traffic_ctl/host.cc b/src/traffic_ctl/host.cc
index 2607804..244557d 100644
--- a/src/traffic_ctl/host.cc
+++ b/src/traffic_ctl/host.cc
@@ -20,9 +20,11 @@
   See the License for the specific language governing permissions and
   limitations under the License.
  */
+
 #include "traffic_ctl.h"
-#include "records/P_RecUtils.h"
-const std::string stat_prefix = "host_status.";
+#include "HostStatus.h"
+#include <records/P_RecUtils.h>
+
 static int
 status_get(unsigned argc, const char **argv)
 {
@@ -53,40 +55,89 @@ static int
 status_down(unsigned argc, const char **argv)
 {
   int down_time     = 0;
+  char *reason      = nullptr;
   const char *usage = "host down HOST [OPTIONS]";
 
   const ArgumentDescription opts[] = {
-    {"time", 'I', "number of seconds that a host is marked down", "I", &down_time, nullptr, nullptr}};
+    {"time", 'I', "number of seconds that a host is marked down", "I", &down_time, nullptr, nullptr},
+    // memory is allocated for 'reason', if this option is used
+    {"reason", '-', "reason for marking the host down, one of 'manual|active|local'", "S*", &reason, nullptr, nullptr},
+  };
 
   if (!CtrlProcessArguments(argc, argv, opts, countof(opts)) || n_file_arguments < 1) {
     return CtrlCommandUsage(usage, opts, countof(opts));
   }
 
+  for (unsigned i = 0; i < n_file_arguments; i++) {
+    if (strncmp(file_arguments[i], "--", 2) == 0) {
+      fprintf(stderr, "Invalid syntax, Usage: %s\n", usage);
+      return CTRL_EX_ERROR;
+    }
+  }
+
+  // if reason is not set, set it to manual (default)
+  if (reason == nullptr) {
+    reason = ats_strdup(Reason::MANUAL_REASON);
+  }
+
+  if (!Reason::validReason(reason)) {
+    fprintf(stderr, "\nInvalid reason: '%s'\n\n", reason);
+    return CtrlCommandUsage(usage, opts, countof(opts));
+  }
+
   TSMgmtError error = TS_ERR_OKAY;
   for (unsigned i = 0; i < n_file_arguments; ++i) {
-    error = TSHostStatusSetDown(file_arguments[i], down_time);
+    error = TSHostStatusSetDown(file_arguments[i], down_time, reason);
     if (error != TS_ERR_OKAY) {
       CtrlMgmtError(error, "failed to set %s", file_arguments[i]);
       return CTRL_EX_ERROR;
     }
   }
+  ats_free(reason);
 
   return CTRL_EX_OK;
 }
 static int
 status_up(unsigned argc, const char **argv)
 {
-  if (!CtrlProcessArguments(argc, argv, nullptr, 0) || n_file_arguments < 1) {
-    return CtrlCommandUsage("host up METRIC value", nullptr, 0);
+  char *reason      = nullptr;
+  const char *usage = "host up HOST [OPTIONS]";
+
+  const ArgumentDescription opts[] = {
+    // memory is allocated for 'reason', if this option is used
+    {"reason", '-', "reason for marking the host up, one of 'manual|active|local'", "S*", &reason, nullptr, nullptr},
+  };
+
+  if (!CtrlProcessArguments(argc, argv, opts, countof(opts)) || n_file_arguments < 1) {
+    return CtrlCommandUsage(usage, nullptr, 0);
+  }
+
+  for (unsigned i = 0; i < n_file_arguments; i++) {
+    if (strncmp(file_arguments[i], "--", 2) == 0) {
+      fprintf(stderr, "Invalid syntax, Usage: %s\n", usage);
+      return CTRL_EX_ERROR;
+    }
+  }
+
+  // if reason is not set, set it to manual (default)
+  if (reason == nullptr) {
+    reason = ats_strdup(Reason::MANUAL_REASON);
   }
+
+  if (!Reason::validReason(reason)) {
+    fprintf(stderr, "\nInvalid reason: '%s'\n\n", reason);
+    return CtrlCommandUsage(usage, opts, countof(opts));
+  }
+
   TSMgmtError error;
   for (unsigned i = 0; i < n_file_arguments; ++i) {
-    error = TSHostStatusSetUp(file_arguments[i]);
+    error = TSHostStatusSetUp(file_arguments[i], 0, reason);
     if (error != TS_ERR_OKAY) {
       CtrlMgmtError(error, "failed to set %s", file_arguments[i]);
       return CTRL_EX_ERROR;
     }
   }
+  ats_free(reason);
 
   return CTRL_EX_OK;
 }
diff --git a/src/traffic_server/HostStatus.cc b/src/traffic_server/HostStatus.cc
index e210ee5..44a646b 100644
--- a/src/traffic_server/HostStatus.cc
+++ b/src/traffic_server/HostStatus.cc
@@ -25,13 +25,40 @@
 
 static RecRawStatBlock *host_status_rsb = nullptr;
 
+inline void
+getStatName(std::string &stat_name, const char *name)
+{
+  stat_name = stat_prefix + name;
+}
+
 static void *
 mgmt_host_status_up_callback(void *x, char *data, int len)
 {
+  MgmtInt op;
+  MgmtMarshallString name;
+  MgmtMarshallInt down_time;
+  MgmtMarshallString reason_str;
+  std::string stat_name;
+  char buf[1024]                         = {0};
+  static const MgmtMarshallType fields[] = {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_STRING, MGMT_MARSHALL_INT};
+  Debug("host_statuses", "%s:%s:%d - data: %s, len: %d\n", __FILE__, __func__, __LINE__, data, len);
+
+  if (mgmt_message_parse(data, len, fields, countof(fields), &op, &name, &reason_str, &down_time) == -1) {
+    Error("Plugin message - RPC parsing error - message discarded.");
+  }
+  Debug("host_statuses", "op: %ld, name: %s, down_time: %d, reason_str: %s", static_cast<long>(op), name,
+        static_cast<int>(down_time), reason_str);
+
+  unsigned int reason = Reason::getReason(reason_str);
+
+  getStatName(stat_name, name);
   if (data != nullptr) {
     Debug("host_statuses", "marking up server %s", data);
     HostStatus &hs = HostStatus::instance();
-    hs.setHostStatus(data, HostStatus_t::HOST_STATUS_UP, 0);
+    if (hs.getHostStat(stat_name, buf, 1024) == REC_ERR_FAIL) {
+      hs.createHostStat(name);
+    }
+    hs.setHostStatus(name, HostStatus_t::HOST_STATUS_UP, down_time, reason);
   }
   return nullptr;
 }
@@ -42,87 +69,292 @@ mgmt_host_status_down_callback(void *x, char *data, int len)
   MgmtInt op;
   MgmtMarshallString name;
   MgmtMarshallInt down_time;
-  static const MgmtMarshallType fields[] = {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_INT};
+  MgmtMarshallString reason_str;
+  std::string stat_name;
+  char buf[1024]                         = {0};
+  static const MgmtMarshallType fields[] = {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_STRING, MGMT_MARSHALL_INT};
   Debug("host_statuses", "%s:%s:%d - data: %s, len: %d\n", __FILE__, __func__, __LINE__, data, len);
 
-  if (mgmt_message_parse(data, len, fields, countof(fields), &op, &name, &down_time) == -1) {
+  if (mgmt_message_parse(data, len, fields, countof(fields), &op, &name, &reason_str, &down_time) == -1) {
     Error("Plugin message - RPC parsing error - message discarded.");
   }
-  Debug("host_statuses", "op: %ld, name: %s, down_time: %d", static_cast<long>(op), name, static_cast<int>(down_time));
+  Debug("host_statuses", "op: %ld, name: %s, down_time: %d, reason_str: %s", static_cast<long>(op), name,
+        static_cast<int>(down_time), reason_str);
+
+  unsigned int reason = Reason::getReason(reason_str);
 
   if (data != nullptr) {
     Debug("host_statuses", "marking down server %s", name);
     HostStatus &hs = HostStatus::instance();
-    hs.setHostStatus(name, HostStatus_t::HOST_STATUS_DOWN, down_time);
+    if (hs.getHostStat(stat_name, buf, 1024) == REC_ERR_FAIL) {
+      hs.createHostStat(name);
+    }
+    hs.setHostStatus(name, HostStatus_t::HOST_STATUS_DOWN, down_time, reason);
   }
   return nullptr;
 }
 
+HostStatRec::HostStatRec()
+  : status(HOST_STATUS_UP),
+    reasons(0),
+    active_marked_down(0),
+    local_marked_down(0),
+    manual_marked_down(0),
+    self_detect_marked_down(0),
+    active_down_time(0),
+    local_down_time(0),
+    manual_down_time(0),
+    self_detect_down_time(0){};
+
+HostStatRec::HostStatRec(std::string str)
+{
+  std::vector<std::string> v1;
+  std::stringstream ss1(str);
+
+  reasons = 0;
+
+  // parse the csv strings from the stat record value string.
+  while (ss1.good()) {
+    char b1[64];
+    ss1.getline(b1, 64, ',');
+    v1.push_back(b1);
+  }
+
+  // v1 contains 5 strings.
+  ink_assert(v1.size() == 5);
+
+  // set the status and reasons fields.
+  for (unsigned int i = 0; i < v1.size(); i++) {
+    if (i == 0) { // set the status field
+      if (v1.at(i).compare("HOST_STATUS_UP") == 0) {
+        status = HOST_STATUS_UP;
+      } else if (v1.at(i).compare("HOST_STATUS_DOWN") == 0) {
+        status = HOST_STATUS_DOWN;
+      }
+    } else { // parse and set remaining reason fields.
+      std::vector<std::string> v2;
+      v2.clear();
+      std::stringstream ss2(v1.at(i));
+      while (ss2.good()) {
+        char b2[64];
+        ss2.getline(b2, 64, ':');
+        v2.push_back(b2);
+      }
+      // v2 contains 4 strings.
+      ink_assert(v2.size() == 4);
+
+      if (v2.at(0).compare("ACTIVE") == 0) {
+        if (v2.at(1).compare("DOWN") == 0) {
+          reasons |= Reason::ACTIVE;
+        } else if (reasons & Reason::ACTIVE) {
+          reasons ^= Reason::ACTIVE;
+        }
+        active_marked_down = atoi(v2.at(2).c_str());
+        active_down_time   = atoi(v2.at(3).c_str());
+      }
+      if (v2.at(0).compare("LOCAL") == 0) {
+        if (v2.at(1).compare("DOWN") == 0) {
+          reasons |= Reason::LOCAL;
+        } else if (reasons & Reason::LOCAL) {
+          reasons ^= Reason::LOCAL;
+        }
+        local_marked_down = atoi(v2.at(2).c_str());
+        local_down_time   = atoi(v2.at(3).c_str());
+      }
+      if (v2.at(0).compare("MANUAL") == 0) {
+        if (v2.at(1).compare("DOWN") == 0) {
+          reasons |= Reason::MANUAL;
+        } else if (reasons & Reason::MANUAL) {
+          reasons ^= Reason::MANUAL;
+        }
+        manual_marked_down = atoi(v2.at(2).c_str());
+        manual_down_time   = atoi(v2.at(3).c_str());
+      }
+      if (v2.at(0).compare("SELF_DETECT") == 0) {
+        if (v2.at(1).compare("DOWN") == 0) {
+          reasons |= Reason::SELF_DETECT;
+        } else if (reasons & Reason::SELF_DETECT) {
+          reasons ^= Reason::SELF_DETECT;
+        }
+        self_detect_marked_down = atoi(v2.at(2).c_str());
+        self_detect_down_time   = atoi(v2.at(3).c_str());
+      }
+    }
+  }
+}
+
+static void
+handle_record_read(const RecRecord *rec, void *edata)
+{
+  HostStatus &hs = HostStatus::instance();
+  std::string hostname;
+
+  if (rec) {
+    Debug("host_statuses", "name: %s", rec->name);
+
+    // parse the hostname from the stat name
+    char *s = const_cast<char *>(rec->name);
+    // 1st move the pointer past the stat prefix.
+    s += stat_prefix.length();
+    hostname = s;
+    hs.createHostStat(hostname.c_str(), rec->data.rec_string);
+    HostStatRec h(rec->data.rec_string);
+    hs.loadRecord(hostname, h);
+  }
+}
+
 HostStatus::HostStatus()
 {
-  hosts_statuses  = ink_hash_table_create(InkHashTableKeyType_String);
-  hosts_stats_ids = ink_hash_table_create(InkHashTableKeyType_String);
   ink_rwlock_init(&host_status_rwlock);
-  ink_rwlock_init(&host_statids_rwlock);
-  Debug("host_statuses", "registering ostas");
-  pmgmt->registerMgmtCallback(MGMT_EVENT_HOST_STATUS_UP, mgmt_host_status_up_callback, nullptr);
-  pmgmt->registerMgmtCallback(MGMT_EVENT_HOST_STATUS_DOWN, mgmt_host_status_down_callback, nullptr);
+  pmgmt->registerMgmtCallback(MGMT_EVENT_HOST_STATUS_UP, &mgmt_host_status_up_callback);
+  pmgmt->registerMgmtCallback(MGMT_EVENT_HOST_STATUS_DOWN, &mgmt_host_status_down_callback);
   host_status_rsb = RecAllocateRawStatBlock((int)TS_MAX_API_STATS);
 }
 
 HostStatus::~HostStatus()
 {
-  // release host_statues hash table.
-  InkHashTableIteratorState ht_iter;
-  InkHashTableEntry *ht_entry = nullptr;
-  ht_entry                    = ink_hash_table_iterator_first(hosts_statuses, &ht_iter);
-
-  while (ht_entry != nullptr) {
-    HostStatRec_t *value = static_cast<HostStatRec_t *>(ink_hash_table_entry_value(hosts_statuses, ht_entry));
-    ats_free(value);
-    ht_entry = ink_hash_table_iterator_next(hosts_statuses, &ht_iter);
+  for (auto &&it : hosts_statuses) {
+    ats_free(it.second);
   }
-  ink_hash_table_destroy(hosts_statuses);
-
-  // release host_stats_ids hash and the read and writer locks.
-  ink_hash_table_destroy(hosts_stats_ids);
+  // release the read and writer locks.
   ink_rwlock_destroy(&host_status_rwlock);
-  ink_rwlock_destroy(&host_statids_rwlock);
 }
 
 void
-HostStatus::setHostStatus(const char *name, HostStatus_t status, const unsigned int down_time)
+HostStatus::loadHostStatusFromStats()
 {
-  int stat_id = getHostStatId(name);
-  if (stat_id != -1) {
-    if (status == HostStatus_t::HOST_STATUS_UP) {
-      Debug("host_statuses", "set stat for :  name: %s, status: %d", name, status);
-      RecSetRawStatCount(host_status_rsb, stat_id, 1);
-      RecSetRawStatSum(host_status_rsb, stat_id, 1);
+  if (RecLookupMatchingRecords(RECT_PROCESS, stat_prefix.c_str(), handle_record_read, nullptr) != REC_ERR_OKAY) {
+    Error("[HostStatus] - While loading HostStatus stats, there was an Error reading HostStatus stats.");
+  }
+}
+
+void
+HostStatus::loadRecord(std::string &name, HostStatRec &h)
+{
+  HostStatRec *host_stat = nullptr;
+  Debug("host_statuses", "loading host status record for %s", name.c_str());
+  ink_rwlock_wrlock(&host_status_rwlock);
+  {
+    if (auto it = hosts_statuses.find(name.c_str()); it != hosts_statuses.end()) {
+      host_stat = it->second;
     } else {
-      RecSetRawStatCount(host_status_rsb, stat_id, 0);
-      RecSetRawStatSum(host_status_rsb, stat_id, 0);
-      Debug("host_statuses", "clear stat for :  name: %s, status: %d", name, status);
+      host_stat  = static_cast<HostStatRec *>(ats_malloc(sizeof(HostStatRec)));
+      *host_stat = h;
+      hosts_statuses.emplace(name, host_stat);
     }
   }
-  Debug("host_statuses", "name: %s, status: %d", name, status);
+  ink_rwlock_unlock(&host_status_rwlock);
+
+  *host_stat = h;
+}
+
+void
+HostStatus::setHostStatus(const char *name, HostStatus_t status, const unsigned int down_time, const unsigned int reason)
+{
+  std::string stat_name;
+  char buf[1024] = {0};
+
+  getStatName(stat_name, name);
+
+  if (getHostStat(stat_name, buf, 1024) == REC_ERR_FAIL) {
+    createHostStat(name);
+  }
+
+  int result = getHostStat(stat_name, buf, 1024);
+
   // update / insert status.
   // using the hash table pointer to store the HostStatus_t value.
-  HostStatRec_t *host_stat = nullptr;
+  HostStatRec *host_stat = nullptr;
   ink_rwlock_wrlock(&host_status_rwlock);
-  if (ink_hash_table_lookup(hosts_statuses, name, reinterpret_cast<InkHashTableValue *>(&host_stat)) == 0) {
-    host_stat = static_cast<HostStatRec_t *>(ats_malloc(sizeof(HostStatRec_t)));
-    ink_hash_table_insert(hosts_statuses, name, reinterpret_cast<InkHashTableValue *>(host_stat));
-  }
-  host_stat->status    = status;
-  host_stat->down_time = down_time;
-  if (status == HostStatus_t::HOST_STATUS_DOWN) {
-    host_stat->marked_down = time(0);
-  } else {
-    host_stat->marked_down = 0;
+  {
+    if (auto it = hosts_statuses.find(name); it != hosts_statuses.end()) {
+      host_stat = it->second;
+    } else {
+      host_stat = static_cast<HostStatRec *>(ats_malloc(sizeof(HostStatRec)));
+      bzero(host_stat, sizeof(HostStatRec));
+      hosts_statuses.emplace(name, host_stat);
+    }
+    if (reason & Reason::ACTIVE) {
+      Debug("host_statuses", "for host %s set status: %s, Reason:ACTIVE", name, HostStatusNames[status]);
+      if (status == HostStatus_t::HOST_STATUS_DOWN) {
+        host_stat->active_marked_down = time(0);
+        host_stat->active_down_time   = down_time;
+        host_stat->reasons |= Reason::ACTIVE;
+      } else {
+        host_stat->active_marked_down = 0;
+        host_stat->active_down_time   = 0;
+        if (host_stat->reasons & Reason::ACTIVE) {
+          host_stat->reasons ^= Reason::ACTIVE;
+        }
+      }
+    }
+    if (reason & Reason::LOCAL) {
+      Debug("host_statuses", "for host %s set status: %s, Reason:LOCAL", name, HostStatusNames[status]);
+      if (status == HostStatus_t::HOST_STATUS_DOWN) {
+        host_stat->local_marked_down = time(0);
+        host_stat->local_down_time   = down_time;
+        host_stat->reasons |= Reason::LOCAL;
+      } else {
+        host_stat->local_marked_down = 0;
+        host_stat->local_down_time   = 0;
+        if (host_stat->reasons & Reason::LOCAL) {
+          host_stat->reasons ^= Reason::LOCAL;
+        }
+      }
+    }
+    if (reason & Reason::MANUAL) {
+      Debug("host_statuses", "for host %s set status: %s, Reason:MANUAL", name, HostStatusNames[status]);
+      if (status == HostStatus_t::HOST_STATUS_DOWN) {
+        host_stat->manual_marked_down = time(0);
+        host_stat->manual_down_time   = down_time;
+        host_stat->reasons |= Reason::MANUAL;
+      } else {
+        host_stat->manual_marked_down = 0;
+        host_stat->manual_down_time   = 0;
+        if (host_stat->reasons & Reason::MANUAL) {
+          host_stat->reasons ^= Reason::MANUAL;
+        }
+      }
+    }
+    if (reason & Reason::SELF_DETECT) {
+      Debug("host_statuses", "for host %s set status: %s, Reason:SELF_DETECT", name, HostStatusNames[status]);
+      if (status == HostStatus_t::HOST_STATUS_DOWN) {
+        host_stat->self_detect_marked_down = time(0);
+        host_stat->self_detect_down_time   = down_time;
+        host_stat->reasons |= Reason::SELF_DETECT;
+      } else {
+        host_stat->self_detect_marked_down = 0;
+        host_stat->self_detect_down_time   = 0;
+        if (host_stat->reasons & Reason::SELF_DETECT) {
+          host_stat->reasons ^= Reason::SELF_DETECT;
+        }
+      }
+    }
+    if (status == HostStatus_t::HOST_STATUS_UP) {
+      if (host_stat->reasons == 0) {
+        host_stat->status = HostStatus_t::HOST_STATUS_UP;
+      }
+      Debug("host_statuses", "reasons: %d, status: %s", host_stat->reasons, HostStatusNames[host_stat->status]);
+    } else {
+      host_stat->status = status;
+      Debug("host_statuses", "reasons: %d, status: %s", host_stat->reasons, HostStatusNames[host_stat->status]);
+    }
   }
   ink_rwlock_unlock(&host_status_rwlock);
 
+  // update the stats
+  if (result == REC_ERR_OKAY) {
+    std::stringstream status_rec;
+    status_rec << *host_stat;
+    RecSetRecordString(stat_name.c_str(), const_cast<char *>(status_rec.str().c_str()), REC_SOURCE_EXPLICIT, true, false);
+    if (status == HostStatus_t::HOST_STATUS_UP) {
+      Debug("host_statuses", "set status up for name: %s, status: %d, stat_name: %s", name, status, stat_name.c_str());
+    } else {
+      Debug("host_statuses", "set status down for name: %s, status: %d, stat_name: %s", name, status, stat_name.c_str());
+    }
+  }
+  Debug("host_statuses", "name: %s, status: %d", name, status);
+
   // log it.
   if (status == HostStatus_t::HOST_STATUS_DOWN) {
     Note("Host %s has been marked down, down_time: %d - %s.", name, down_time, down_time == 0 ? "indefinitely." : "seconds.");
@@ -134,55 +366,93 @@ HostStatus::setHostStatus(const char *name, HostStatus_t status, const unsigned
 HostStatus_t
 HostStatus::getHostStatus(const char *name)
 {
-  HostStatRec_t *_status;
-  int lookup = 0;
-  time_t now = time(0);
+  HostStatRec *_status = 0;
+  time_t now           = time(0);
+  bool lookup          = false;
 
   // the hash table value pointer has the HostStatus_t value.
   ink_rwlock_rdlock(&host_status_rwlock);
-  lookup = ink_hash_table_lookup(hosts_statuses, name, reinterpret_cast<void **>(&_status));
+  {
+    auto it = hosts_statuses.find(name);
+    lookup  = it != hosts_statuses.end();
+    if (lookup) {
+      _status = it->second;
+    }
+  }
   ink_rwlock_unlock(&host_status_rwlock);
-  Debug("host_statuses", "name: %s, status: %d", name, static_cast<int>(_status->status));
 
   // if the host was marked down and it's down_time has elapsed, mark it up.
-  if (lookup == 1 && _status->status == HostStatus_t::HOST_STATUS_DOWN && _status->down_time > 0) {
-    if ((_status->down_time + _status->marked_down) < now) {
-      Debug("host_statuses", "name: %s, now: %ld, down_time: %d, marked_down: %ld", name, now, _status->down_time,
-            _status->marked_down);
-      setHostStatus(name, HostStatus_t::HOST_STATUS_UP, 0);
+  if (lookup && _status->status == HostStatus_t::HOST_STATUS_DOWN) {
+    unsigned int reasons = _status->reasons;
+    if ((_status->reasons & Reason::ACTIVE) && _status->active_down_time > 0) {
+      if ((_status->active_down_time + _status->active_marked_down) < now) {
+        Debug("host_statuses", "name: %s, now: %ld, down_time: %d, marked_down: %ld, reason: %s", name, now,
+              _status->active_down_time, _status->active_marked_down, Reason::ACTIVE_REASON);
+        setHostStatus(name, HostStatus_t::HOST_STATUS_UP, 0, Reason::ACTIVE);
+        reasons ^= Reason::ACTIVE;
+      }
+    }
+    if ((_status->reasons & Reason::LOCAL) && _status->local_down_time > 0) {
+      if ((_status->local_down_time + _status->local_marked_down) < now) {
+        Debug("host_statuses", "name: %s, now: %ld, down_time: %d, marked_down: %ld, reason: %s", name, now,
+              _status->local_down_time, _status->local_marked_down, Reason::LOCAL_REASON);
+        setHostStatus(name, HostStatus_t::HOST_STATUS_UP, 0, Reason::LOCAL);
+        reasons ^= Reason::LOCAL;
+      }
+    }
+    if ((_status->reasons & Reason::MANUAL) && _status->manual_down_time > 0) {
+      if ((_status->manual_down_time + _status->manual_marked_down) < now) {
+        Debug("host_statuses", "name: %s, now: %ld, down_time: %d, marked_down: %ld, reason: %s", name, now,
+              _status->manual_down_time, _status->manual_marked_down, Reason::MANUAL_REASON);
+        setHostStatus(name, HostStatus_t::HOST_STATUS_UP, 0, Reason::MANUAL);
+        reasons ^= Reason::MANUAL;
+      }
+    }
+    if ((_status->reasons & Reason::SELF_DETECT) && _status->self_detect_down_time > 0) {
+      if ((_status->self_detect_down_time + _status->self_detect_marked_down) < now) {
+        Debug("host_statuses", "name: %s, now: %ld, down_time: %d, marked_down: %ld, reason: %s", name, now,
+              _status->self_detect_down_time, _status->self_detect_marked_down, Reason::SELF_DETECT_REASON);
+        setHostStatus(name, HostStatus_t::HOST_STATUS_UP, 0, Reason::SELF_DETECT);
+        reasons ^= Reason::SELF_DETECT;
+      }
+    }
+    if (reasons == 0) {
       return HostStatus_t::HOST_STATUS_UP;
+    } else {
+      return HostStatus_t::HOST_STATUS_DOWN;
     }
   }
-  return lookup == 1 ? static_cast<HostStatus_t>(_status->status) : HostStatus_t::HOST_STATUS_INIT;
+  // didn't find this host in host status db, create the record
+  if (!lookup) {
+    createHostStat(name);
+  }
+
+  return lookup ? static_cast<HostStatus_t>(_status->status) : HostStatus_t::HOST_STATUS_UP;
 }
 
 void
-HostStatus::createHostStat(const char *name)
+HostStatus::createHostStat(const char *name, const char *data)
 {
-  InkHashTableEntry *entry;
-  entry = ink_hash_table_lookup_entry(hosts_stats_ids, name);
-  if (entry == nullptr) {
-    RecRegisterRawStat(host_status_rsb, RECT_PROCESS, (stat_prefix + name).c_str(), RECD_INT, RECP_NON_PERSISTENT,
-                       (int)next_stat_id, RecRawStatSyncSum);
-    Debug("host_statuses", "name: %s, id: %d", name, next_stat_id);
-    ink_rwlock_wrlock(&host_statids_rwlock);
-    ink_hash_table_insert(hosts_stats_ids, name, reinterpret_cast<void *>(next_stat_id));
-    ink_rwlock_unlock(&host_statids_rwlock);
-    setHostStatus(name, HostStatus_t::HOST_STATUS_UP, 0);
-    next_stat_id++;
+  char buf[1024] = {0};
+  HostStatRec r;
+
+  std::string stat_name;
+  std::stringstream status_rec;
+  if (data != nullptr) {
+    HostStatRec h(data);
+    r = h;
+  }
+  status_rec << r;
+  getStatName(stat_name, name);
+
+  if (getHostStat(stat_name, buf, 1024) == REC_ERR_FAIL) {
+    RecRegisterStatString(RECT_PROCESS, stat_name.c_str(), const_cast<char *>(status_rec.str().c_str()), RECP_PERSISTENT);
+    Debug("host_statuses", "stat name: %s, data: %s", stat_name.c_str(), status_rec.str().c_str());
   }
 }
 
 int
-HostStatus::getHostStatId(const char *name)
+HostStatus::getHostStat(std::string &stat_name, char *buf, unsigned int buf_len)
 {
-  int lookup   = 0;
-  intptr_t _id = -1;
-
-  ink_rwlock_rdlock(&host_statids_rwlock);
-  lookup = ink_hash_table_lookup(hosts_stats_ids, name, reinterpret_cast<void **>(&_id));
-  ink_rwlock_unlock(&host_statids_rwlock);
-  Debug("host_statuses", "name: %s, id: %d", name, static_cast<int>(_id));
-
-  return lookup == 1 ? static_cast<int>(_id) : -1;
+  return RecGetRecordString(stat_name.c_str(), buf, buf_len, true);
 }
diff --git a/src/traffic_server/traffic_server.cc b/src/traffic_server/traffic_server.cc
index 26fb015..1d183b9 100644
--- a/src/traffic_server/traffic_server.cc
+++ b/src/traffic_server/traffic_server.cc
@@ -1861,6 +1861,8 @@ main(int /* argc ATS_UNUSED */, const char **argv)
     RecProcessStart();
     initCacheControl();
     IpAllow::startup();
+    HostStatus::instance().loadHostStatusFromStats();
+    netProcessor.init_socks();
     ParentConfig::startup();
     HostStatus::instance();
 #ifdef SPLIT_DNS