This is an automated email from the ASF dual-hosted git repository.
zwoop 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 5cb31cd627 Support rate limit on active connections instead of txn
(#11172)
5cb31cd627 is described below
commit 5cb31cd6273f23e0e29e8fe4e2f78c1dc2d3a025
Author: Leif Hedstrom <[email protected]>
AuthorDate: Thu Apr 4 08:07:02 2024 -0600
Support rate limit on active connections instead of txn (#11172)
---
doc/admin-guide/plugins/rate_limit.en.rst | 9 ++++++
plugins/experimental/rate_limit/rate_limit.cc | 24 +++++++++++---
plugins/experimental/rate_limit/txn_limiter.cc | 44 +++++++++++++++++++-------
plugins/experimental/rate_limit/txn_limiter.h | 14 ++++++--
4 files changed, 73 insertions(+), 18 deletions(-)
diff --git a/doc/admin-guide/plugins/rate_limit.en.rst
b/doc/admin-guide/plugins/rate_limit.en.rst
index 8e0f8c8cbd..1b1b85af8a 100644
--- a/doc/admin-guide/plugins/rate_limit.en.rst
+++ b/doc/admin-guide/plugins/rate_limit.en.rst
@@ -101,6 +101,15 @@ are available:
noting that in the latter exampe, the non-standard scheme and port led to
":8080" being appended to the string.
+.. option:: --conntrack
+ This flag tells the limiter that rather than limiting the number of active
transactions,
+ it should limit the number of active connections. This allows an
established connection
+ to make any number of transactions, but limits the number of connections
that can be
+ active at any one time.
+
+ Note that it's highly recommended that you keep a very low ``keep-alive``
timeout for the
+ connections that are using this rate limiter.
+
Global Plugin
-------------
diff --git a/plugins/experimental/rate_limit/rate_limit.cc
b/plugins/experimental/rate_limit/rate_limit.cc
index f7ba630eb2..f67541165f 100644
--- a/plugins/experimental/rate_limit/rate_limit.cc
+++ b/plugins/experimental/rate_limit/rate_limit.cc
@@ -95,8 +95,8 @@ TSRemapNewInstance(int argc, char *argv[], void **ih, char *
/* errbuf ATS_UNUSE
limiter->initialize(argc, const_cast<const char **>(argv));
*ih = static_cast<void *>(limiter);
- Dbg(dbg_ctl, "Added active_in limiter rule (limit=%u, queue=%u,
max-age=%ldms, error=%u)", limiter->limit(), limiter->max_queue(),
- static_cast<long>(limiter->max_age().count()), limiter->error());
+ Dbg(dbg_ctl, "Added active_in limiter rule (limit=%u, queue=%u,
max-age=%ldms, error=%u, conntrack=%s)", limiter->limit(),
+ limiter->max_queue(), static_cast<long>(limiter->max_age().count()),
limiter->error(), limiter->conntrack() ? "yes" : "no");
return TS_SUCCESS;
}
@@ -110,6 +110,17 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp,
TSRemapRequestInfo *rri)
auto *limiter = static_cast<TxnRateLimiter *>(ih);
if (limiter) {
+ TSHttpSsn ssnp = TSHttpTxnSsnGet(txnp);
+
+ if (limiter->conntrack()) {
+ int count = TSHttpSsnTransactionCount(ssnp);
+
+ if (count > 1) { // The first transaction is the connect, so we need to
have at least 2 to be "established"
+ Dbg(dbg_ctl, "Allowing an established connection to pass through,
txn=%d", count);
+ return TSREMAP_NO_REMAP;
+ }
+ }
+
if (!limiter->reserve()) {
if (!limiter->max_queue() || limiter->full()) {
// We are running at limit, and the queue has reached max capacity,
give back an error and be done.
@@ -121,8 +132,13 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp,
TSRemapRequestInfo *rri)
Dbg(dbg_ctl, "Adding rate limiting hook, we are at capacity");
}
} else {
- limiter->setupTxnCont(txnp, TS_HTTP_TXN_CLOSE_HOOK);
- Dbg(dbg_ctl, "Adding txn-close hook, we're not at capacity");
+ if (limiter->conntrack()) {
+ limiter->setupSsnCont(ssnp);
+ Dbg(dbg_ctl, "Adding ssn-close hook, we're not at capacity");
+ } else {
+ limiter->setupTxnCont(txnp, TS_HTTP_TXN_CLOSE_HOOK);
+ Dbg(dbg_ctl, "Adding txn-close hook, we're not at capacity");
+ }
}
}
diff --git a/plugins/experimental/rate_limit/txn_limiter.cc
b/plugins/experimental/rate_limit/txn_limiter.cc
index 507c402e0a..294d843efe 100644
--- a/plugins/experimental/rate_limit/txn_limiter.cc
+++ b/plugins/experimental/rate_limit/txn_limiter.cc
@@ -38,6 +38,13 @@ txn_limit_cont(TSCont cont, TSEvent event, void *edata)
return TS_EVENT_CONTINUE;
break;
+ case TS_EVENT_HTTP_SSN_CLOSE:
+ limiter->free();
+ TSContDestroy(cont); // We are done with this continuation now
+ TSHttpSsnReenable(static_cast<TSHttpSsn>(edata), TS_EVENT_HTTP_CONTINUE);
+ return TS_EVENT_NONE;
+ break;
+
case TS_EVENT_HTTP_POST_REMAP:
limiter->push(static_cast<TSHttpTxn>(edata), cont);
limiter->incrementMetric(RATE_LIMITER_METRIC_QUEUED);
@@ -75,8 +82,8 @@ txn_queue_cont(TSCont cont, TSEvent event, void *edata)
Dbg(dbg_ctl, "Enabling queued txn after %ldms",
static_cast<long>(delay.count()));
// Since this was a delayed transaction, we need to add the TXN_CLOSE hook
to free the slot when done
TSHttpTxnHookAdd(txnp, TS_HTTP_TXN_CLOSE_HOOK, contp);
- TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
limiter->incrementMetric(RATE_LIMITER_METRIC_RESUMED);
+ TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
}
// Kill any queued txns if they are too old
@@ -92,8 +99,8 @@ txn_queue_cont(TSCont cont, TSEvent event, void *edata)
Dbg(dbg_ctl, "Queued TXN is too old (%ldms), erroring out",
static_cast<long>(age.count()));
TSHttpTxnStatusSet(txnp, static_cast<TSHttpStatus>(limiter->error()));
TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, contp);
- TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR);
limiter->incrementMetric(RATE_LIMITER_METRIC_EXPIRED);
+ TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR);
}
}
@@ -107,16 +114,17 @@ bool
TxnRateLimiter::initialize(int argc, const char *argv[])
{
static const struct option longopt[] = {
- {const_cast<char *>("limit"), required_argument, nullptr, 'l' },
- {const_cast<char *>("queue"), required_argument, nullptr, 'q' },
- {const_cast<char *>("error"), required_argument, nullptr, 'e' },
- {const_cast<char *>("retry"), required_argument, nullptr, 'r' },
- {const_cast<char *>("header"), required_argument, nullptr, 'h' },
- {const_cast<char *>("maxage"), required_argument, nullptr, 'm' },
- {const_cast<char *>("prefix"), required_argument, nullptr, 'p' },
- {const_cast<char *>("tag"), required_argument, nullptr, 't' },
+ {const_cast<char *>("limit"), required_argument, nullptr, 'l' },
+ {const_cast<char *>("queue"), required_argument, nullptr, 'q' },
+ {const_cast<char *>("error"), required_argument, nullptr, 'e' },
+ {const_cast<char *>("retry"), required_argument, nullptr, 'r' },
+ {const_cast<char *>("header"), required_argument, nullptr, 'h' },
+ {const_cast<char *>("maxage"), required_argument, nullptr, 'm' },
+ {const_cast<char *>("prefix"), required_argument, nullptr, 'p' },
+ {const_cast<char *>("tag"), required_argument, nullptr, 't' },
+ {const_cast<char *>("conntrack"), no_argument, nullptr, 'c' },
// EOF
- {nullptr, no_argument, nullptr, '\0'},
+ {nullptr, no_argument, nullptr, '\0'},
};
optind = 1;
std::string prefix = RATE_LIMITER_METRIC_PREFIX;
@@ -150,6 +158,9 @@ TxnRateLimiter::initialize(int argc, const char *argv[])
case 't':
tag = optarg;
break;
+ case 'c':
+ this->_conntrack = true;
+ break;
}
if (opt == -1) {
break;
@@ -180,3 +191,14 @@ TxnRateLimiter::setupTxnCont(TSHttpTxn txnp, TSHttpHookID
hook)
TSContDataSet(cont, this);
TSHttpTxnHookAdd(txnp, hook, cont);
}
+
+// This only needs the TS_HTTP_SSN_CLOSE_HOOK, for now at least, so not passed
as argument.
+void
+TxnRateLimiter::setupSsnCont(TSHttpSsn ssnp)
+{
+ TSCont cont = TSContCreate(txn_limit_cont, nullptr);
+ TSReleaseAssert(cont);
+
+ TSContDataSet(cont, this);
+ TSHttpSsnHookAdd(ssnp, TS_HTTP_SSN_CLOSE_HOOK, cont);
+}
diff --git a/plugins/experimental/rate_limit/txn_limiter.h
b/plugins/experimental/rate_limit/txn_limiter.h
index 6c7a651226..434a66ecbd 100644
--- a/plugins/experimental/rate_limit/txn_limiter.h
+++ b/plugins/experimental/rate_limit/txn_limiter.h
@@ -37,6 +37,7 @@ public:
}
void setupTxnCont(TSHttpTxn txnp, TSHttpHookID hook);
+ void setupSsnCont(TSHttpSsn ssnp);
bool initialize(int argc, const char *argv[]);
const std::string &
@@ -57,10 +58,17 @@ public:
return _retry;
}
+ bool
+ conntrack() const
+ {
+ return _conntrack;
+ }
+
private:
- std::string _header = ""; // Header to put the latency metrics in, e.g.
@RateLimit-Delay
- unsigned _error = 429; // Error code when we decide not to allow a txn
to be processed (e.g. queue full)
- unsigned _retry = 0; // If > 0, we will also send a Retry-After:
header with this retry value
+ std::string _header = ""; // Header to put the latency metrics in, e.g.
@RateLimit-Delay
+ unsigned _error = 429; // Error code when we decide not to allow a txn
to be processed (e.g. queue full)
+ unsigned _retry = 0; // If > 0, we will also send a Retry-After:
header with this retry value
+ bool _conntrack = false; // If true, we will track connections and limit
based on that instead of transactions
TSCont _queue_cont = nullptr; // Continuation processing the queue
periodically
TSAction _action = nullptr; // The action associated with the queue
continuation, needed to shut it down