diff --git a/Version.props b/Version.props index e3b95fd3cb..97df7ba6f4 100644 --- a/Version.props +++ b/Version.props @@ -1,6 +1,6 @@ - 1.0.44 + 1.0.45 \ No newline at end of file diff --git a/libs/host/Configuration/Options.cs b/libs/host/Configuration/Options.cs index 95592b8eb5..96fdd62433 100644 --- a/libs/host/Configuration/Options.cs +++ b/libs/host/Configuration/Options.cs @@ -339,6 +339,10 @@ internal sealed class Options [Option("maxthreads", Required = false, HelpText = "Maximum worker and completion threads in thread pool, 0 uses the system default.")] public int ThreadPoolMaxThreads { get; set; } + [IntRangeValidation(-1, int.MaxValue)] + [Option("network-connection-limit", Required = false, Default = -1, HelpText = "Maximum number of simultaneously active network connections.")] + public int NetworkConnectionLimit { get; set; } + [OptionValidation] [Option("use-azure-storage", Required = false, HelpText = "Use Azure Page Blobs for storage instead of local storage.")] public bool? UseAzureStorage { get; set; } @@ -676,6 +680,7 @@ public GarnetServerOptions GetServerOptions(ILogger logger = null) QuietMode = QuietMode.GetValueOrDefault(), ThreadPoolMinThreads = ThreadPoolMinThreads, ThreadPoolMaxThreads = ThreadPoolMaxThreads, + NetworkConnectionLimit = NetworkConnectionLimit, DeviceFactoryCreator = useAzureStorage ? () => new AzureStorageNamedDeviceFactory(AzureStorageConnectionString, logger) : () => new LocalStorageNamedDeviceFactory(useNativeDeviceLinux: UseNativeDeviceLinux.GetValueOrDefault(), logger: logger), diff --git a/libs/host/GarnetServer.cs b/libs/host/GarnetServer.cs index db9f912824..32389d8aa6 100644 --- a/libs/host/GarnetServer.cs +++ b/libs/host/GarnetServer.cs @@ -216,7 +216,7 @@ private void InitializeServer() } // Create Garnet TCP server if none was provided. - this.server ??= new GarnetServerTcp(opts.Address, opts.Port, 0, opts.TlsOptions, opts.NetworkSendThrottleMax, logger); + this.server ??= new GarnetServerTcp(opts.Address, opts.Port, 0, opts.TlsOptions, opts.NetworkSendThrottleMax, opts.NetworkConnectionLimit, logger); storeWrapper = new StoreWrapper(version, redisProtocolVersion, server, store, objectStore, objectStoreSizeTracker, customCommandManager, appendOnlyFile, opts, clusterFactory: clusterFactory, loggerFactory: loggerFactory); diff --git a/libs/host/defaults.conf b/libs/host/defaults.conf index 5b4c66d9fe..aa18841004 100644 --- a/libs/host/defaults.conf +++ b/libs/host/defaults.conf @@ -251,6 +251,9 @@ /* Maximum worker and completion threads in thread pool, 0 uses the system default. */ "ThreadPoolMaxThreads" : 0, + /* Maximum number of simultaneously active network connections. */ + "NetworkConnectionLimit" : -1, + /* Use Azure Page Blobs for storage instead of local storage. */ "UseAzureStorage" : false, diff --git a/libs/server/Servers/GarnetServerOptions.cs b/libs/server/Servers/GarnetServerOptions.cs index 465d9b77e2..0093cfd8e0 100644 --- a/libs/server/Servers/GarnetServerOptions.cs +++ b/libs/server/Servers/GarnetServerOptions.cs @@ -246,6 +246,11 @@ public class GarnetServerOptions : ServerOptions /// public int ThreadPoolMaxThreads = 0; + /// + /// Maximum client connection limit + /// + public int NetworkConnectionLimit = -1; + /// /// Creator of device factories /// diff --git a/libs/server/Servers/GarnetServerTcp.cs b/libs/server/Servers/GarnetServerTcp.cs index 017248d122..09a1c4fd71 100644 --- a/libs/server/Servers/GarnetServerTcp.cs +++ b/libs/server/Servers/GarnetServerTcp.cs @@ -24,6 +24,7 @@ public class GarnetServerTcp : GarnetServerBase, IServerHook readonly int networkSendThrottleMax; readonly NetworkBufferSettings networkBufferSettings; readonly LimitedFixedBufferPool networkPool; + readonly int networkConnectionLimit; public IPEndPoint GetEndPoint { @@ -69,9 +70,10 @@ public IEnumerable ActiveClusterSessions() /// /// /// - public GarnetServerTcp(string address, int port, int networkBufferSize = default, IGarnetTlsOptions tlsOptions = null, int networkSendThrottleMax = 8, ILogger logger = null) + public GarnetServerTcp(string address, int port, int networkBufferSize = default, IGarnetTlsOptions tlsOptions = null, int networkSendThrottleMax = 8, int networkConnectionLimit = -1, ILogger logger = null) : base(address, port, networkBufferSize, logger) { + this.networkConnectionLimit = networkConnectionLimit; this.tlsOptions = tlsOptions; this.networkSendThrottleMax = networkSendThrottleMax; var serverBufferSize = BufferSizeUtils.ServerBufferSize(new MaxSizeSettings()); @@ -134,11 +136,11 @@ private unsafe bool HandleNewConnection(SocketAsyncEventArgs e) string remoteEndpointName = e.AcceptSocket.RemoteEndPoint?.ToString(); logger?.LogDebug("Accepted TCP connection from {remoteEndpoint}", remoteEndpointName); - ServerTcpNetworkHandler handler = null; if (activeHandlerCount >= 0) { - if (Interlocked.Increment(ref activeHandlerCount) > 0) + var currentActiveHandlerCount = Interlocked.Increment(ref activeHandlerCount); + if (currentActiveHandlerCount > 0 && (networkConnectionLimit == -1 || currentActiveHandlerCount <= networkConnectionLimit)) { try { @@ -157,6 +159,11 @@ private unsafe bool HandleNewConnection(SocketAsyncEventArgs e) handler?.Dispose(); } } + else + { + Interlocked.Decrement(ref activeHandlerCount); + e.AcceptSocket.Dispose(); + } } return true; }