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

bnolsen 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 994e02e395 HttpSessionManager: implement global_locked session pool 
option (#10117)
994e02e395 is described below

commit 994e02e395df323db0a3aa2bf8b81dfb28d69c07
Author: Brian Olsen <[email protected]>
AuthorDate: Sat Sep 23 08:06:15 2023 -0600

    HttpSessionManager: implement global_locked session pool option (#10117)
    
    Use only when transaction loads starve the session pool to the
    point that the pool is mostly bypassed.
    
    Co-authored-by: Brian Olsen <[email protected]>
---
 doc/admin-guide/files/records.yaml.en.rst | 48 ++++++++++++--------
 iocore/eventsystem/I_Lock.h               | 74 ++++++++++++++++++++++++++++---
 proxy/http/HttpConfig.cc                  |  7 +--
 proxy/http/HttpProxyAPIEnums.h            |  5 ++-
 proxy/http/HttpSessionManager.cc          | 50 ++++++++++++++++++---
 5 files changed, 149 insertions(+), 35 deletions(-)

diff --git a/doc/admin-guide/files/records.yaml.en.rst 
b/doc/admin-guide/files/records.yaml.en.rst
index 633eb2ef8f..f87157ac52 100644
--- a/doc/admin-guide/files/records.yaml.en.rst
+++ b/doc/admin-guide/files/records.yaml.en.rst
@@ -1070,24 +1070,36 @@ allow-plain
    Control the scope of server session re-use if it is enabled by
    :ts:cv:`proxy.config.http.server_session_sharing.match`. Valid values are:
 
-   ========== =================================================================
-   Value      Description
-   ========== =================================================================
-   ``global`` Re-use sessions from a global pool of all server sessions.
-   ``thread`` Re-use sessions from a per-thread pool.
-   ``hybrid`` Try to work as a global pool, but release server sessions to the
-              per-thread pool if there is lock contention on the global pool.
-   ========== =================================================================
-
-
-   Setting :ts:cv:`proxy.config.http.server_session_sharing.pool` to global 
can reduce
-   the number of connections to origin for some traffic loads.  However, if 
many
-   execute threads are active, the thread contention on the global pool can 
reduce the
-   lifetime of connections to origin and reduce effective origin connection 
reuse.
-
-   For a hybrid pool, the operation starts as the global pool, but sessons are 
returned
-   to the local thread pool if the global pool lock is not acquired rather 
than just
-   closing the origin connection as is the case in standard global mode.
+   ================= ==========================================================
+   Value             Description
+   ================= ==========================================================
+   ``global``        Re-use sessions from a global pool of all server sessions.
+   ``thread``        Re-use sessions from a per-thread pool.
+   ``hybrid``        Try to work as a global pool, but release server sessions
+                     to the per-thread pool if there is lock contention on the
+                     global pool.
+   ``global_locked`` Similar to global, except that the session pool is
+                     managed by a blocking mutex.
+   ================= ==========================================================
+
+
+   Setting :ts:cv:`proxy.config.http.server_session_sharing.pool`
+   to global can reduce the number of connections to origin for some
+   traffic loads.  However, if many execute threads are active, the thread
+   contention on the global pool can reduce the lifetime of connections
+   to origin and reduce effective origin connection reuse.
+
+   For a hybrid pool, the operation starts as the global pool, but sessons
+   are returned to the local thread pool if the global pool lock is not
+   acquired rather than just closing the origin connection as is the
+   case in standard global mode.
+
+   For a ``global_locked`` pool connections are managed by a blocking
+   mutex instead of the normal try mutex.  Under extreme transaction
+   loads the connection pool starvation may result in most transactions
+   bypassing the connection pool resulting in runaway upstream
+   connections.  This option will avoid this condition at the cost of
+   latency and ttfb (time to first byte) performance).
 
 .. ts:cv:: CONFIG proxy.config.http.attach_server_session_to_client INT 0
    :overridable:
diff --git a/iocore/eventsystem/I_Lock.h b/iocore/eventsystem/I_Lock.h
index b26a623e47..f79248bbfb 100644
--- a/iocore/eventsystem/I_Lock.h
+++ b/iocore/eventsystem/I_Lock.h
@@ -389,9 +389,11 @@ class WeakMutexLock
 {
 private:
   Ptr<ProxyMutex> m;
-  bool locked_p;
+  bool locked_p{false};
 
 public:
+  WeakMutexLock() = default;
+
   WeakMutexLock(
 #ifdef DEBUG
     const SourceLocation &location, const char *ahandler,
@@ -408,6 +410,20 @@ public:
     }
   }
 
+  WeakMutexLock(WeakMutexLock &)                  = delete;
+  WeakMutexLock &operator=(const WeakMutexLock &) = delete;
+  WeakMutexLock(WeakMutexLock &&)                 = delete;
+
+  WeakMutexLock &
+  operator=(WeakMutexLock &&orig)
+  {
+    if (&orig != this) {
+      std::swap(m, orig.m);
+      std::swap(locked_p, orig.locked_p);
+    }
+    return *this;
+  }
+
   void
   release()
   {
@@ -425,10 +441,12 @@ public:
 class MutexLock
 {
 private:
-  Ptr<ProxyMutex> m;
-  bool locked_p;
+  Ptr<ProxyMutex> m{};
+  bool locked_p{false};
 
 public:
+  MutexLock() = default;
+
   MutexLock(
 #ifdef DEBUG
     const SourceLocation &location, const char *ahandler,
@@ -443,6 +461,20 @@ public:
       m, t);
   }
 
+  MutexLock(MutexLock &)                  = delete;
+  MutexLock &operator=(const MutexLock &) = delete;
+  MutexLock(MutexLock &&)                 = delete;
+
+  MutexLock &
+  operator=(MutexLock &&orig)
+  {
+    if (&orig != this) {
+      std::swap(m, orig.m);
+      std::swap(locked_p, orig.locked_p);
+    }
+    return *this;
+  }
+
   void
   release()
   {
@@ -461,9 +493,11 @@ class WeakMutexTryLock
 {
 private:
   Ptr<ProxyMutex> m;
-  bool lock_acquired;
+  bool lock_acquired{false};
 
 public:
+  WeakMutexTryLock() = default;
+
   WeakMutexTryLock(
 #ifdef DEBUG
     const SourceLocation &location, const char *ahandler,
@@ -482,6 +516,20 @@ public:
     }
   }
 
+  WeakMutexTryLock(WeakMutexTryLock &)                  = delete;
+  WeakMutexTryLock &operator=(const WeakMutexTryLock &) = delete;
+  WeakMutexTryLock(WeakMutexTryLock &&)                 = delete;
+
+  WeakMutexTryLock &
+  operator=(WeakMutexTryLock &&orig)
+  {
+    if (&orig != this) {
+      std::swap(m, orig.m);
+      std::swap(lock_acquired, orig.lock_acquired);
+    }
+    return *this;
+  }
+
   ~WeakMutexTryLock()
   {
     if (lock_acquired && m.get()) {
@@ -529,9 +577,11 @@ class MutexTryLock
 {
 private:
   Ptr<ProxyMutex> m;
-  bool lock_acquired;
+  bool lock_acquired{false};
 
 public:
+  MutexTryLock() = default;
+
   MutexTryLock(
 #ifdef DEBUG
     const SourceLocation &location, const char *ahandler,
@@ -546,6 +596,20 @@ public:
       m, t);
   }
 
+  MutexTryLock(MutexTryLock &)                  = delete;
+  MutexTryLock &operator=(const MutexTryLock &) = delete;
+  MutexTryLock(MutexTryLock &&)                 = delete;
+
+  MutexTryLock &
+  operator=(MutexTryLock &&orig)
+  {
+    if (&orig != this) {
+      std::swap(m, orig.m);
+      std::swap(lock_acquired, orig.lock_acquired);
+    }
+    return *this;
+  }
+
   ~MutexTryLock()
   {
     if (lock_acquired) {
diff --git a/proxy/http/HttpConfig.cc b/proxy/http/HttpConfig.cc
index 03298ce70b..7e736a1bf9 100644
--- a/proxy/http/HttpConfig.cc
+++ b/proxy/http/HttpConfig.cc
@@ -163,9 +163,10 @@ http_config_enum_mask_read(const char *name, MgmtByte 
&value)
 }
 
 static const ConfigEnumPair<TSServerSessionSharingPoolType> 
SessionSharingPoolStrings[] = {
-  {TS_SERVER_SESSION_SHARING_POOL_GLOBAL, "global"},
-  {TS_SERVER_SESSION_SHARING_POOL_THREAD, "thread"},
-  {TS_SERVER_SESSION_SHARING_POOL_HYBRID, "hybrid"}
+  {TS_SERVER_SESSION_SHARING_POOL_GLOBAL,        "global"       },
+  {TS_SERVER_SESSION_SHARING_POOL_THREAD,        "thread"       },
+  {TS_SERVER_SESSION_SHARING_POOL_HYBRID,        "hybrid"       },
+  {TS_SERVER_SESSION_SHARING_POOL_GLOBAL_LOCKED, "global_locked"},
 };
 
 int HttpConfig::m_id = 0;
diff --git a/proxy/http/HttpProxyAPIEnums.h b/proxy/http/HttpProxyAPIEnums.h
index cd2dfa3809..f61bca4cfb 100644
--- a/proxy/http/HttpProxyAPIEnums.h
+++ b/proxy/http/HttpProxyAPIEnums.h
@@ -47,12 +47,13 @@ typedef enum {
   TS_SERVER_SESSION_SHARING_MATCH_MASK_HOSTONLY    = 0x2,
   TS_SERVER_SESSION_SHARING_MATCH_MASK_HOSTSNISYNC = 0x4,
   TS_SERVER_SESSION_SHARING_MATCH_MASK_SNI         = 0x8,
-  TS_SERVER_SESSION_SHARING_MATCH_MASK_CERT        = 0x10
+  TS_SERVER_SESSION_SHARING_MATCH_MASK_CERT        = 0x10,
 } TSServerSessionSharingMatchMask;
 
 /// Server session sharing values - pool
 typedef enum {
   TS_SERVER_SESSION_SHARING_POOL_GLOBAL,
   TS_SERVER_SESSION_SHARING_POOL_THREAD,
-  TS_SERVER_SESSION_SHARING_POOL_HYBRID
+  TS_SERVER_SESSION_SHARING_POOL_HYBRID,
+  TS_SERVER_SESSION_SHARING_POOL_GLOBAL_LOCKED,
 } TSServerSessionSharingPoolType;
diff --git a/proxy/http/HttpSessionManager.cc b/proxy/http/HttpSessionManager.cc
index 5d3745d8b8..031df68071 100644
--- a/proxy/http/HttpSessionManager.cc
+++ b/proxy/http/HttpSessionManager.cc
@@ -378,13 +378,41 @@ HttpSessionManager::acquire_session(HttpSM *sm, sockaddr 
const *ip, const char *
   }
 
   //  If you didn't get a match, and the global pool is an option go there.
-  if (retval != HSM_DONE && (TS_SERVER_SESSION_SHARING_POOL_GLOBAL == 
this->get_pool_type() ||
-                             TS_SERVER_SESSION_SHARING_POOL_HYBRID == 
this->get_pool_type())) {
-    retval = _acquire_session(ip, hostname_hash, sm, match_style, 
TS_SERVER_SESSION_SHARING_POOL_GLOBAL);
+  if (retval != HSM_DONE) {
+    if (TS_SERVER_SESSION_SHARING_POOL_GLOBAL == this->get_pool_type() ||
+        TS_SERVER_SESSION_SHARING_POOL_HYBRID == this->get_pool_type()) {
+      retval = _acquire_session(ip, hostname_hash, sm, match_style, 
TS_SERVER_SESSION_SHARING_POOL_GLOBAL);
+    } else if (TS_SERVER_SESSION_SHARING_POOL_GLOBAL_LOCKED == 
this->get_pool_type())
+      retval = _acquire_session(ip, hostname_hash, sm, match_style, 
TS_SERVER_SESSION_SHARING_POOL_GLOBAL_LOCKED);
   }
+
   return retval;
 }
 
+namespace
+{
+
+// Scoped lock of the session pool based on pool type
+// (global_locked vs everything else)
+inline bool
+lockSessionPool(Ptr<ProxyMutex> &mutex, EThread *const ethread, 
TSServerSessionSharingPoolType const pool_type,
+                MutexLock *const mlock, MutexTryLock *const tlock)
+{
+  bool locked = false;
+  if (TS_SERVER_SESSION_SHARING_POOL_GLOBAL_LOCKED == pool_type) {
+    SCOPED_MUTEX_LOCK(lock, mutex, ethread);
+    *mlock = std::move(lock);
+    locked = true;
+  } else {
+    MUTEX_TRY_LOCK(lock, mutex, ethread);
+    *tlock = std::move(lock);
+    locked = tlock->is_locked();
+  }
+  return locked;
+}
+
+} // namespace
+
 HSMresult_t
 HttpSessionManager::_acquire_session(sockaddr const *ip, CryptoHash const 
&hostname_hash, HttpSM *sm,
                                      TSServerSessionSharingMatchMask 
match_style, TSServerSessionSharingPoolType pool_type)
@@ -400,8 +428,12 @@ HttpSessionManager::_acquire_session(sockaddr const *ip, 
CryptoHash const &hostn
     EThread *ethread = this_ethread();
     Ptr<ProxyMutex> pool_mutex =
       (TS_SERVER_SESSION_SHARING_POOL_THREAD == pool_type) ? 
ethread->server_session_pool->mutex : m_g_pool->mutex;
-    MUTEX_TRY_LOCK(lock, pool_mutex, ethread);
-    if (lock.is_locked()) {
+
+    MutexLock mlock;
+    MutexTryLock tlock;
+    bool const locked = lockSessionPool(pool_mutex, ethread, pool_type, 
&mlock, &tlock);
+
+    if (locked) {
       if (TS_SERVER_SESSION_SHARING_POOL_THREAD == pool_type) {
         retval = ethread->server_session_pool->acquireSession(ip, 
hostname_hash, match_style, sm, to_return);
         Debug("http_ss", "[acquire session] thread pool search %s", to_return 
? "successful" : "failed");
@@ -471,8 +503,12 @@ HttpSessionManager::release_session(PoolableSession 
*to_release)
   bool released_p = true;
 
   // The per thread lock looks like it should not be needed but if it's not 
locked the close checking I/O op will crash.
-  MUTEX_TRY_LOCK(lock, pool->mutex, ethread);
-  if (lock.is_locked()) {
+
+  MutexLock mlock;
+  MutexTryLock tlock;
+  bool const locked = lockSessionPool(pool->mutex, ethread, 
this->get_pool_type(), &mlock, &tlock);
+
+  if (locked) {
     pool->releaseSession(to_release);
   } else if (this->get_pool_type() == TS_SERVER_SESSION_SHARING_POOL_HYBRID) {
     // Try again with the thread pool

Reply via email to