aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2023-10-12 10:17:17 +0200
committerGitHub <[email protected]>2023-10-12 10:17:17 +0200
commit4b97a66d2ea8e75bcf8a93b321514e9050a9ecdd (patch)
tree8220079e7124b0b6395b15b9d1ef449e815682a1 /src
parentadded explicit implementation of IoHash equals operator (#464) (diff)
downloadzen-4b97a66d2ea8e75bcf8a93b321514e9050a9ecdd.tar.xz
zen-4b97a66d2ea8e75bcf8a93b321514e9050a9ecdd.zip
adjust resource usage for dedicated servers (#466)
when dedicated mode is enabled via `--dedicated` or `server.dedicated` then we tune http.sys server settings to be more suitable for a shared server initially we tune two things * the thread pool used to service I/O requests allows a larger number of threads to be created when needed. The minimum thread count is unchanged but in dedicated server mode we double the maximum number of threads allowed * the http.sys request queue length (`HttpServerQueueLengthProperty`) is increased to 50,000 in dedicated mode. The regular default is 1,000
Diffstat (limited to 'src')
-rw-r--r--src/zenhttp/httpserver.cpp3
-rw-r--r--src/zenhttp/httpsys.cpp82
-rw-r--r--src/zenhttp/httpsys.h1
-rw-r--r--src/zenhttp/include/zenhttp/httpserver.h3
-rw-r--r--src/zenhttp/iothreadpool.cpp11
-rw-r--r--src/zenhttp/iothreadpool.h8
-rw-r--r--src/zenserver/config.cpp2
7 files changed, 84 insertions, 26 deletions
diff --git a/src/zenhttp/httpserver.cpp b/src/zenhttp/httpserver.cpp
index a2ea4cff8..eb13f77fc 100644
--- a/src/zenhttp/httpserver.cpp
+++ b/src/zenhttp/httpserver.cpp
@@ -792,7 +792,8 @@ CreateHttpServer(const HttpServerConfig& Config)
return Ref<HttpServer>(CreateHttpSysServer({.ThreadCount = Config.ThreadCount,
.AsyncWorkThreadCount = Config.HttpSys.AsyncWorkThreadCount,
.IsAsyncResponseEnabled = Config.HttpSys.IsAsyncResponseEnabled,
- .IsRequestLoggingEnabled = Config.HttpSys.IsRequestLoggingEnabled}));
+ .IsRequestLoggingEnabled = Config.HttpSys.IsRequestLoggingEnabled,
+ .IsDedicatedServer = Config.IsDedicatedServer}));
#endif
case HttpServerClass::kHttpNull:
diff --git a/src/zenhttp/httpsys.cpp b/src/zenhttp/httpsys.cpp
index 8401dcf83..75d8bfe7a 100644
--- a/src/zenhttp/httpsys.cpp
+++ b/src/zenhttp/httpsys.cpp
@@ -32,7 +32,10 @@ namespace zen {
/**
* @brief Windows implementation of HTTP server based on http.sys
*
- * This requires elevation to function
+ * This requires elevation to function by default but system configuration
+ * can soften this requirement.
+ *
+ * See README.md for details.
*/
class HttpSysServer : public HttpServer
{
@@ -76,7 +79,8 @@ private:
bool m_IsRequestLoggingEnabled = false;
bool m_IsAsyncResponseEnabled = true;
- WinIoThreadPool m_ThreadPool;
+ std::unique_ptr<WinIoThreadPool> m_IoThreadPool;
+
RwLock m_AsyncWorkPoolInitLock;
WorkerThreadPool* m_AsyncWorkPool = nullptr;
@@ -89,7 +93,7 @@ private:
int32_t m_MinPendingRequests = 16;
int32_t m_MaxPendingRequests = 128;
Event m_ShutdownEvent;
- const HttpSysConfig m_InitialConfig;
+ HttpSysConfig m_InitialConfig;
};
} // namespace zen
@@ -889,14 +893,45 @@ HttpAsyncWorkRequest::AsyncWorkItem::Execute()
\/ \/ \/
*/
-HttpSysServer::HttpSysServer(const HttpSysConfig& Config)
+HttpSysServer::HttpSysServer(const HttpSysConfig& InConfig)
: m_Log(logging::Get("http"))
, m_RequestLog(logging::Get("http_requests"))
-, m_IsRequestLoggingEnabled(Config.IsRequestLoggingEnabled)
-, m_IsAsyncResponseEnabled(Config.IsAsyncResponseEnabled)
-, m_ThreadPool(Config.ThreadCount != 0 ? Config.ThreadCount : std::thread::hardware_concurrency())
-, m_InitialConfig(Config)
+, m_IsRequestLoggingEnabled(InConfig.IsRequestLoggingEnabled)
+, m_IsAsyncResponseEnabled(InConfig.IsAsyncResponseEnabled)
+, m_InitialConfig(InConfig)
{
+ // Initialize thread pool
+
+ int MinThreadCount;
+ int MaxThreadCount;
+
+ if (m_InitialConfig.ThreadCount == 0)
+ {
+ MinThreadCount = Max(8u, std::thread::hardware_concurrency());
+ }
+ else
+ {
+ MinThreadCount = m_InitialConfig.ThreadCount;
+ }
+
+ MaxThreadCount = MinThreadCount * 2;
+
+ if (m_InitialConfig.IsDedicatedServer)
+ {
+ // In order to limit the potential impact of threads stuck
+ // in locks we allow the thread pool to be oversubscribed
+ // by a fair amount
+
+ MaxThreadCount *= 2;
+ }
+
+ m_IoThreadPool = std::make_unique<WinIoThreadPool>(MinThreadCount, MaxThreadCount);
+
+ if (m_InitialConfig.AsyncWorkThreadCount == 0)
+ {
+ m_InitialConfig.AsyncWorkThreadCount = 16;
+ }
+
ULONG Result = HttpInitialize(HTTPAPI_VERSION_2, HTTP_INITIALIZE_SERVER, nullptr);
if (Result != NO_ERROR)
@@ -907,7 +942,11 @@ HttpSysServer::HttpSysServer(const HttpSysConfig& Config)
m_IsHttpInitialized = true;
m_IsOk = true;
- ZEN_INFO("http.sys server started, using {} I/O threads and {} async worker threads", Config.ThreadCount, Config.AsyncWorkThreadCount);
+ ZEN_INFO("http.sys server started in {} mode, using {}-{} I/O threads and {} async worker threads",
+ m_InitialConfig.IsDedicatedServer ? "DEDICATED" : "NORMAL",
+ MinThreadCount,
+ MaxThreadCount,
+ m_InitialConfig.AsyncWorkThreadCount);
}
HttpSysServer::~HttpSysServer()
@@ -1070,10 +1109,27 @@ HttpSysServer::InitializeServer(int BasePort)
0);
}
+ // Tune the maximum number of pending requests in the http.sys request queue. By default
+ // the value is 1000 which is plenty for single user machines but for dedicated servers
+ // serving many users it makes sense to increase this to a higher number to help smooth
+ // out intermittent stalls like we might experience when GC is triggered
+
+ if (m_InitialConfig.IsDedicatedServer)
+ {
+ ULONG QueueLength = 50000;
+
+ Result = HttpSetRequestQueueProperty(m_RequestQueueHandle, HttpServerQueueLengthProperty, &QueueLength, sizeof QueueLength, 0, 0);
+
+ if (Result != NO_ERROR)
+ {
+ ZEN_WARN("changing request queue length to {} failed: {}", QueueLength, Result);
+ }
+ }
+
// Create I/O completion port
std::error_code ErrorCode;
- m_ThreadPool.CreateIocp(m_RequestQueueHandle, HttpSysTransaction::IoCompletionCallback, /* Context */ this, /* out */ ErrorCode);
+ m_IoThreadPool->CreateIocp(m_RequestQueueHandle, HttpSysTransaction::IoCompletionCallback, /* Context */ this, /* out */ ErrorCode);
if (ErrorCode)
{
@@ -1137,9 +1193,7 @@ HttpSysServer::WorkPool()
if (!m_AsyncWorkPool)
{
- unsigned int WorkerThreadCount = m_InitialConfig.AsyncWorkThreadCount != 0 ? m_InitialConfig.AsyncWorkThreadCount : 16;
-
- m_AsyncWorkPool = new WorkerThreadPool(WorkerThreadCount, "http_async");
+ m_AsyncWorkPool = new WorkerThreadPool(m_InitialConfig.AsyncWorkThreadCount, "http_async");
}
}
@@ -1305,7 +1359,7 @@ HttpSysTransaction::~HttpSysTransaction()
PTP_IO
HttpSysTransaction::Iocp()
{
- return m_HttpServer.m_ThreadPool.Iocp();
+ return m_HttpServer.m_IoThreadPool->Iocp();
}
HANDLE
diff --git a/src/zenhttp/httpsys.h b/src/zenhttp/httpsys.h
index 1553d56ef..6a6b16525 100644
--- a/src/zenhttp/httpsys.h
+++ b/src/zenhttp/httpsys.h
@@ -20,6 +20,7 @@ struct HttpSysConfig
unsigned int AsyncWorkThreadCount = 0;
bool IsAsyncResponseEnabled = true;
bool IsRequestLoggingEnabled = false;
+ bool IsDedicatedServer = false;
};
Ref<HttpServer> CreateHttpSysServer(HttpSysConfig Config);
diff --git a/src/zenhttp/include/zenhttp/httpserver.h b/src/zenhttp/include/zenhttp/httpserver.h
index c233075be..3cbe05dd6 100644
--- a/src/zenhttp/include/zenhttp/httpserver.h
+++ b/src/zenhttp/include/zenhttp/httpserver.h
@@ -184,7 +184,8 @@ public:
struct HttpServerConfig
{
- std::string ServerClass; // Choice of HTTP server implementation
+ bool IsDedicatedServer = false; // Should be set to true for shared servers
+ std::string ServerClass; // Choice of HTTP server implementation
unsigned int ThreadCount = 0;
struct
diff --git a/src/zenhttp/iothreadpool.cpp b/src/zenhttp/iothreadpool.cpp
index 6087e69ec..da4b42e28 100644
--- a/src/zenhttp/iothreadpool.cpp
+++ b/src/zenhttp/iothreadpool.cpp
@@ -8,14 +8,19 @@
namespace zen {
-WinIoThreadPool::WinIoThreadPool(int InThreadCount)
+WinIoThreadPool::WinIoThreadPool(int InThreadCount, int InMaxThreadCount)
{
- // Thread pool setup
+ ZEN_ASSERT(InThreadCount);
+
+ if (InMaxThreadCount < InThreadCount)
+ {
+ InMaxThreadCount = InThreadCount;
+ }
m_ThreadPool = CreateThreadpool(NULL);
SetThreadpoolThreadMinimum(m_ThreadPool, InThreadCount);
- SetThreadpoolThreadMaximum(m_ThreadPool, InThreadCount * 2);
+ SetThreadpoolThreadMaximum(m_ThreadPool, InMaxThreadCount);
InitializeThreadpoolEnvironment(&m_CallbackEnvironment);
diff --git a/src/zenhttp/iothreadpool.h b/src/zenhttp/iothreadpool.h
index 8333964c3..e75e95e58 100644
--- a/src/zenhttp/iothreadpool.h
+++ b/src/zenhttp/iothreadpool.h
@@ -11,16 +11,10 @@
namespace zen {
-//////////////////////////////////////////////////////////////////////////
-//
-// Thread pool. Implemented in terms of Windows thread pool right now, will
-// need a cross-platform implementation eventually
-//
-
class WinIoThreadPool
{
public:
- WinIoThreadPool(int InThreadCount);
+ WinIoThreadPool(int InThreadCount, int InMaxThreadCount);
~WinIoThreadPool();
void CreateIocp(HANDLE IoHandle, PTP_WIN32_IO_CALLBACK Callback, void* Context, std::error_code& ErrorCode);
diff --git a/src/zenserver/config.cpp b/src/zenserver/config.cpp
index 85db7bade..cdd1fb031 100644
--- a/src/zenserver/config.cpp
+++ b/src/zenserver/config.cpp
@@ -1353,6 +1353,8 @@ ParseCliOptions(int argc, char* argv[], ZenServerOptions& ServerOptions)
{
ServerOptions.AbsLogFile = ServerOptions.DataDir / "logs" / "zenserver.log";
}
+
+ ServerOptions.HttpServerConfig.IsDedicatedServer = ServerOptions.IsDedicated;
}
} // namespace zen