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