From ccd33cd63495cca5294b4a87d7990a0941f949cd Mon Sep 17 00:00:00 2001 From: tamirms Date: Wed, 13 Mar 2024 19:45:03 +0000 Subject: [PATCH 1/2] Add limit on number of horizon requests in flight --- services/horizon/internal/app.go | 1 + services/horizon/internal/config.go | 11 ++++++----- services/horizon/internal/flags.go | 14 ++++++++++++-- services/horizon/internal/httpx/router.go | 12 ++++++++---- 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/services/horizon/internal/app.go b/services/horizon/internal/app.go index ed338f2ac3..8fb86b5f54 100644 --- a/services/horizon/internal/app.go +++ b/services/horizon/internal/app.go @@ -533,6 +533,7 @@ func (a *App) init() error { StaleThreshold: a.config.StaleThreshold, ConnectionTimeout: a.config.ConnectionTimeout, ClientQueryTimeout: a.config.ClientQueryTimeout, + MaxConcurrentRequests: a.config.MaxConcurrentRequests, MaxHTTPRequestSize: a.config.MaxHTTPRequestSize, NetworkPassphrase: a.config.NetworkPassphrase, MaxPathLength: a.config.MaxPathLength, diff --git a/services/horizon/internal/config.go b/services/horizon/internal/config.go index eb89d72efd..94b6f5514b 100644 --- a/services/horizon/internal/config.go +++ b/services/horizon/internal/config.go @@ -40,11 +40,12 @@ type Config struct { ConnectionTimeout time.Duration ClientQueryTimeout time.Duration // MaxHTTPRequestSize is the maximum allowed request payload size - MaxHTTPRequestSize uint - RateQuota *throttled.RateQuota - FriendbotURL *url.URL - LogLevel logrus.Level - LogFile string + MaxHTTPRequestSize uint + RateQuota *throttled.RateQuota + MaxConcurrentRequests uint + FriendbotURL *url.URL + LogLevel logrus.Level + LogFile string // MaxPathLength is the maximum length of the path returned by `/paths` endpoint. MaxPathLength uint diff --git a/services/horizon/internal/flags.go b/services/horizon/internal/flags.go index 48a83057f4..be7d5ae0c1 100644 --- a/services/horizon/internal/flags.go +++ b/services/horizon/internal/flags.go @@ -69,8 +69,9 @@ const ( // StellarTestnet is a constant representing the Stellar test network StellarTestnet = "testnet" - defaultMaxHTTPRequestSize = uint(200 * 1024) - clientQueryTimeoutNotSet = -1 + defaultMaxConcurrentRequests = 1000 + defaultMaxHTTPRequestSize = uint(200 * 1024) + clientQueryTimeoutNotSet = -1 ) var ( @@ -471,6 +472,15 @@ func Flags() (*Config, support.ConfigOptions) { Usage: "sets the limit on the maximum allowed http request payload size, default is 200kb, to disable the limit check, set to 0, only do so if you acknowledge the implications of accepting unbounded http request payload sizes.", UsedInCommands: ApiServerCommands, }, + &support.ConfigOption{ + Name: "max-concurrent-requests", + ConfigKey: &config.MaxConcurrentRequests, + OptType: types.Uint, + FlagDefault: defaultMaxConcurrentRequests, + Usage: "sets the limit on the maximum number of concurrent http requests, default is 1000, to disable the limit set to 0. " + + "If Horizon receives a request which would exceed the limit of concurrent http requests, Horizon will respond with a 429 status code.", + UsedInCommands: ApiServerCommands, + }, &support.ConfigOption{ Name: "per-hour-rate-limit", ConfigKey: &config.RateQuota, diff --git a/services/horizon/internal/httpx/router.go b/services/horizon/internal/httpx/router.go index 60cf617468..4ba978a96a 100644 --- a/services/horizon/internal/httpx/router.go +++ b/services/horizon/internal/httpx/router.go @@ -28,10 +28,11 @@ import ( ) type RouterConfig struct { - DBSession db.SessionInterface - PrimaryDBSession db.SessionInterface - TxSubmitter *txsub.System - RateQuota *throttled.RateQuota + DBSession db.SessionInterface + PrimaryDBSession db.SessionInterface + TxSubmitter *txsub.System + RateQuota *throttled.RateQuota + MaxConcurrentRequests uint BehindCloudflare bool BehindAWSLoadBalancer bool @@ -91,6 +92,9 @@ func (r *Router) addMiddleware(config *RouterConfig, BehindAWSLoadBalancer: config.BehindAWSLoadBalancer, })) r.Use(loggerMiddleware(serverMetrics)) + if config.MaxConcurrentRequests > 0 { + r.Use(chimiddleware.Throttle(int(config.MaxConcurrentRequests))) + } r.Use(timeoutMiddleware(config.ConnectionTimeout)) if config.MaxHTTPRequestSize > 0 { r.Use(func(handler http.Handler) http.Handler { From 04fabefc06ed2803dd7e99f6ee3e8d99cd9cfa77 Mon Sep 17 00:00:00 2001 From: tamirms Date: Wed, 13 Mar 2024 21:42:55 +0000 Subject: [PATCH 2/2] fix defaultMaxConcurrentRequests --- services/horizon/internal/flags.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/horizon/internal/flags.go b/services/horizon/internal/flags.go index be7d5ae0c1..9df44af44d 100644 --- a/services/horizon/internal/flags.go +++ b/services/horizon/internal/flags.go @@ -69,7 +69,7 @@ const ( // StellarTestnet is a constant representing the Stellar test network StellarTestnet = "testnet" - defaultMaxConcurrentRequests = 1000 + defaultMaxConcurrentRequests = uint(1000) defaultMaxHTTPRequestSize = uint(200 * 1024) clientQueryTimeoutNotSet = -1 )