Skip to content

Commit

Permalink
net::Defaults.maxTCPConnections: Use WSD-config or system's maximum c…
Browse files Browse the repository at this point in the history
…oncurrent TCP connections

`net::Defaults.maxExtConnections` is set:
- use COOLWSD config `net.max_ext_connections` if >= `COOLWSD::MinConnectedSessions`
- or is disabled (zeroed) if config < 0.
- otherwise set to system-value, see `Util::getMaxConcurrentTCPConnections`.

`Util::getMaxConcurrentTCPConnections` uses Linux kernel values
- /proc/sys/net/ipv4/tcp_max_orphans
  See https://www.kernel.org/doc/html/latest/networking/ip-sysctl.html
- /proc/sys/net/nf_conntrack_max
  See https://www.kernel.org/doc/html/latest/networking/nf_conntrack-sysctl.html
- or returns zero if undefined

The Linux kernel values are memory bound, approximately 4 concurrent TCP connections per MB system memory are provided,
e.g. {4096M -> 16384}, {16384M -> 65536}, {65407M -> 262144}, ...

`COOLWSD::MinConnectedSessions` is a static constexpr with value 3, symbolized for clarity.

Signed-off-by: Sven Göthel <[email protected]>
Change-Id: Iad74f253bdac5636757b130b299b5deacda658db
  • Loading branch information
Sven Göthel committed Nov 20, 2024
1 parent 17cce81 commit f22b4cc
Show file tree
Hide file tree
Showing 11 changed files with 89 additions and 10 deletions.
1 change: 1 addition & 0 deletions common/ConfigUtil.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ class ConfigValueGetter

void operator()(int& value) { value = _config.getInt(_name); }
void operator()(unsigned int& value) { value = _config.getUInt(_name); }
void operator()(int64_t& value) { value = _config.getInt64(_name); }
void operator()(uint64_t& value) { value = _config.getUInt64(_name); }
void operator()(bool& value) { value = _config.getBool(_name); }
void operator()(std::string& value) { value = _config.getString(_name); }
Expand Down
3 changes: 2 additions & 1 deletion common/Util-mobile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ int spawnProcess(const std::string& cmd, const StringVector& args) { return 0; }

std::string getHumanizedBytes(unsigned long nBytes) { return std::string(); }
size_t getTotalSystemMemoryKb() { return 0; }
std::size_t getFromFile(const char* path) { return 0; }
size_t getMaxConcurrentTCPConnections() { return 0; }
std::size_t getFromFile(const char* /*path*/, const size_t /*defaultValue*/) { return 0; }
std::size_t getCGroupMemLimit() { return 0; }
std::size_t getCGroupMemSoftLimit() { return 0; }
size_t getMemoryUsagePSS(const pid_t pid) { return 0; }
Expand Down
37 changes: 37 additions & 0 deletions common/Util-server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <config.h>
#include <limits>

#include "Util.hpp"

Expand Down Expand Up @@ -160,6 +161,42 @@ std::size_t getTotalSystemMemoryKb()
return totalMemKb;
}

std::size_t getFromFile(const char* path, const size_t defaultValue)
{
FILE* file = fopen(path, "r");
if (!file)
return defaultValue;
char line[4096] = { 0 };
// coverity[tainted_data_argument : FALSE] - we trust the kernel-provided data
const char *line2 = fgets(line, sizeof(line), file);
fclose(file);
if(!line2)
return defaultValue;
char* endptr = nullptr;
errno = 0; // Flush previous error indicator. Reminder: errno is thread-local
const unsigned long long num = std::strtoull(line, &endptr, 10);
if (errno != 0 || endptr <= line || num > std::numeric_limits<size_t>::max())
return defaultValue;
return size_t(num);
}

size_t getMaxConcurrentTCPConnections()
{
#ifdef __linux__
// - 4 tcp_max_orphans sockets per MB
// - {4096M -> 16384}, {16384M -> 65536}, {65407M -> 262144}, ...
const ssize_t tcp_max_orphans = getFromFile("/proc/sys/net/ipv4/tcp_max_orphans", 0); // ignored if n/a
const ssize_t nf_conntrack_max = getFromFile("/proc/sys/net/nf_conntrack_max", 0); // ignored if n/a
const size_t res = nf_conntrack_max > 0 ? std::min(tcp_max_orphans, nf_conntrack_max) : tcp_max_orphans;
LOG_DBG("MaxConcurrentTCPConnections: min(orphans " << tcp_max_orphans
<< ", conntrack " << nf_conntrack_max << ") -> "
<< res);
return res;
#else
return 0;
#endif
}

std::size_t getFromCGroup(const std::string& group, const std::string& key)
{
std::size_t num = 0;
Expand Down
6 changes: 5 additions & 1 deletion common/Util.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -333,8 +333,12 @@ namespace Util
/// Returns the total physical memory (in kB) available in the system
size_t getTotalSystemMemoryKb();

/// Returns the maximum number of concurrent TCP connections, zero if undefined.
/// Value is memory bound. On GNU/Linux approximately 4 concurrent TCP connections per MB system memory are provided.
size_t getMaxConcurrentTCPConnections();

/// Returns the numerical content of a file at @path
std::size_t getFromFile(const char *path);
std::size_t getFromFile(const char *path, const size_t defaultValue=0);

/// Returns the cgroup's memory limit, or 0 if not available in bytes
std::size_t getCGroupMemLimit();
Expand Down
3 changes: 3 additions & 0 deletions net/NetUtil.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ class DefaultValues
std::chrono::microseconds inactivityTimeout;

/// Maximum number of concurrent external TCP connections. Zero disables instrument.
/// Set to COOLWSD config `net.max_ext_connections` if >= `COOLWSD::MinConnectedSessions`
/// or is disabled (zeroed) if config < 0.
/// Otherwise set to system-value, see `Util::getMaxConcurrentTCPConnections`.
size_t maxExtConnections;
};
extern DefaultValues Defaults;
Expand Down
2 changes: 1 addition & 1 deletion net/Socket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ std::unique_ptr<Watchdog> SocketPoll::PollWatchdog;
std::atomic<size_t> StreamSocket::ExternalConnectionCount = 0;

net::DefaultValues net::Defaults = { .inactivityTimeout = std::chrono::seconds(3600),
.maxExtConnections = 200000 /* arbitrary value to be resolved */ };
.maxExtConnections = 0 /* disabled by default */};

#define SOCKET_ABSTRACT_UNIX_NAME "0coolwsd-"

Expand Down
5 changes: 3 additions & 2 deletions test/UnitTimeoutConnections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include <config.h>

#include <cstdint>
#include <string>

#include <HttpRequest.hpp>
Expand All @@ -32,9 +33,9 @@ static constexpr size_t ConnectionCount = 9;
/// Base test suite class for connection limit (limited) using HTTP and WS sessions.
class UnitTimeoutConnections : public UnitTimeoutBase1
{
void configure(Poco::Util::LayeredConfiguration& /* config */) override
void configure(Poco::Util::LayeredConfiguration& config) override
{
net::Defaults.maxExtConnections = ConnectionLimit;
config.setInt64("net.max_ext_connections", static_cast<int64_t>(ConnectionLimit)); // Sets `net::Defaults.maxExtConnections`
}

public:
Expand Down
5 changes: 4 additions & 1 deletion test/UnitTimeoutInactive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@ class UnitTimeoutInactivity : public UnitTimeoutBase0
TestResult testHttp(bool forceInactivityTO);
TestResult testWS(bool forceInactivityTO);

void configure(Poco::Util::LayeredConfiguration& /* config */) override
void configure(Poco::Util::LayeredConfiguration& config) override
{
// Ensured config-default zero to use system-default for `net::Defaults.maxExtConnections`
config.setInt64("net.max_ext_connections", 0);

// net::Defaults.inactivityTimeout = std::chrono::seconds(3600);
net::Defaults.inactivityTimeout = std::chrono::milliseconds(100);
//
Expand Down
5 changes: 4 additions & 1 deletion test/UnitTimeoutNone.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@ static constexpr size_t ConnectionCount = 9;
/// Base test suite class for connection limit (no limits) using HTTP and WS sessions.
class UnitTimeoutNone : public UnitTimeoutBase1
{
void configure(Poco::Util::LayeredConfiguration& /* config */) override
void configure(Poco::Util::LayeredConfiguration& config) override
{
// Keep original values -> No timeout

// Disable `net::Defaults.maxExtConnections` instrument
config.setInt64("net.max_ext_connections", -1);
}

public:
Expand Down
31 changes: 28 additions & 3 deletions wsd/COOLWSD.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

#include <config.h>
#include <config_version.h>
#include <cstdint>
#include <limits>

#include "COOLWSD.hpp"

Expand Down Expand Up @@ -1208,6 +1210,8 @@ void COOLWSD::innerInitialize(Poco::Util::Application& self)
{ "net.proxy_prefix", "false" },
{ "net.content_security_policy", "" },
{ "net.frame_ancestors", "" },
// Maximum number of concurrent external TCP connections. See `net::Defaults.maxExtConnections`.
{ "net.max_ext_connections", "0" },
{ "num_prespawn_children", NUM_PRESPAWN_CHILDREN },
{ "per_document.always_save_on_exit", "false" },
{ "per_document.autosave_duration_secs", "300" },
Expand Down Expand Up @@ -1533,6 +1537,27 @@ void COOLWSD::innerInitialize(Poco::Util::Application& self)
// Allow UT to manipulate before using configuration values.
UnitWSD::get().configure(conf);

{
// Determine `net::Defaults.maxExtConnections` (maximum external concurrent TCP connections)
const size_t maxSessions = std::max<size_t>(COOLWSD::MinConnectedSessions, MAX_CONNECTIONS); // min. ext session count
const int64_t confMaxConcurrentTCPConnections = ConfigUtil::getConfigValue<int64_t>(conf, "net.max_ext_connections", 0);
const size_t sysMaxConcurrentTCPConnections = Util::getMaxConcurrentTCPConnections();
size_t maxConcurrentTCPConnections;
if (confMaxConcurrentTCPConnections >= COOLWSD::MinConnectedSessions)
maxConcurrentTCPConnections = static_cast<size_t>(confMaxConcurrentTCPConnections); // use config value
else if (confMaxConcurrentTCPConnections < 0)
maxConcurrentTCPConnections = 0; // ignores instrument
else if (sysMaxConcurrentTCPConnections >= maxSessions)
maxConcurrentTCPConnections = sysMaxConcurrentTCPConnections; // use system value
else
maxConcurrentTCPConnections = 0; // ignores instrument
net::Defaults.maxExtConnections = maxConcurrentTCPConnections;
LOG_DBG("net::Defaults.maxExtConnections: maxSessions " << maxSessions
<< ", maxExtConnections[conf " << confMaxConcurrentTCPConnections
<< ", sys " << sysMaxConcurrentTCPConnections << "] -> "
<< net::Defaults.maxExtConnections);
}

// Trace Event Logging.
EnableTraceEventLogging = ConfigUtil::getConfigValue<bool>(conf, "trace_event[@enable]", false);

Expand Down Expand Up @@ -2049,10 +2074,10 @@ void COOLWSD::innerInitialize(Poco::Util::Application& self)
}
}

if (COOLWSD::MaxConnections < 3)
if (COOLWSD::MaxConnections < COOLWSD::MinConnectedSessions)
{
LOG_ERR("MAX_CONNECTIONS must be at least 3");
COOLWSD::MaxConnections = 3;
LOG_ERR("MAX_CONNECTIONS must be at least " << COOLWSD::MinConnectedSessions);
COOLWSD::MaxConnections = COOLWSD::MinConnectedSessions;
}

if (COOLWSD::MaxDocuments > COOLWSD::MaxConnections)
Expand Down
1 change: 1 addition & 0 deletions wsd/COOLWSD.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ class COOLWSD final : public Poco::Util::ServerApplication,
#endif

static std::unordered_set<std::string> EditFileExtensions;
static constexpr unsigned MinConnectedSessions = 3; ///< Minimum value for MaxConnections
static unsigned MaxConnections;
static unsigned MaxDocuments;
static std::string HardwareResourceWarning;
Expand Down

0 comments on commit f22b4cc

Please sign in to comment.