aboutsummaryrefslogtreecommitdiff
path: root/src/zenserver/hub/zenhubserver.h
blob: 0d5ac600f43caac4e0676b14662889b2fc959c1d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "hubinstancestate.h"
#include "hydrationdefaults.h"
#include "resourcemetrics.h"
#include "zenserver.h"

#include <zencore/workthreadpool.h>
#include <zenutil/consul.h>

namespace cxxopts {
class Options;
}
namespace zen::LuaConfig {
struct Options;
}

namespace zen {

class HttpApiService;
class HttpFrontendService;
class HttpHubService;
class HttpProxyHandler;

struct ZenHubWatchdogConfig
{
	uint32_t CycleIntervalMs					 = 3000;
	uint32_t CycleProcessingBudgetMs			 = 500;
	uint32_t InstanceCheckThrottleMs			 = 5;
	uint32_t ProvisionedInactivityTimeoutSeconds = 600;
	uint32_t HibernatedInactivityTimeoutSeconds	 = 1800;
	uint32_t InactivityCheckMarginSeconds		 = 60;	// Activity check is triggered this far before the inactivity timeout
	uint32_t ActivityCheckConnectTimeoutMs		 = 100;
	uint32_t ActivityCheckRequestTimeoutMs		 = 200;
};

struct ZenHubServerConfig : public ZenServerConfig
{
	std::string UpstreamNotificationEndpoint;
	std::string InstanceId;		 // For use in notifications
	std::string ConsulEndpoint;	 // If set, enables Consul service registration
	std::string ConsulTokenEnv;	 // Environment variable name to read a Consul token from; defaults to CONSUL_HTTP_TOKEN if empty
	uint32_t	ConsulHealthIntervalSeconds	 = 10;	// Interval in seconds between Consul health checks
	uint32_t	ConsulDeregisterAfterSeconds = 30;	// Seconds before Consul deregisters an unhealthy service
	bool		ConsulRegisterHub	 = true;  // Whether to register the hub parent service with Consul (instance registration unaffected)
	uint16_t	HubBasePortNumber	 = 21000;
	int			HubInstanceLimit	 = 1000;
	bool		HubUseJobObject		 = true;
	bool		HubEnableHydration	 = true;			// Load instance state from hydration target on provision
	bool		HubEnableDehydration = true;			// Save instance state to hydration target on deprovision
	bool		HubHydrationPackEnabled		   = true;	// Concatenate small files into raw CAS pack blobs during dehydrate
	uint64_t	HubHydrationPackThresholdBytes = DefaultPackThresholdBytes;	 // Files strictly smaller than this are pack candidates
	uint64_t	HubHydrationMaxPackBytes	   = DefaultMaxPackBytes;		 // Upper bound on a pack's concatenation size
	bool		HubHydrationAsyncEnabled	   = true;						 // Route S3 hydration through AsyncHttpClient
	uint32_t	HubHydrationAsyncMaxConcurrentRequests =
		128;  // Hub-wide cap on concurrent S3 hydration requests (only when HubHydrationAsyncEnabled)
	std::string			  HubInstanceHttpClass = "asio";
	std::string			  HubInstanceMalloc;
	std::string			  HubInstanceTrace;
	std::string			  HubInstanceTraceHost;
	std::string			  HubInstanceTraceFile;
	uint32_t			  HubInstanceHttpThreadCount	  = 0;	// Automatic
	uint32_t			  HubInstanceProvisionThreadCount = 0;	// Hub-wide hydrate/dehydrate scheduling pool size
	uint32_t			  HubInstanceSpawnThreadCount	  = 0;	// Hub-wide child process spawn/despawn pool size
	uint32_t			  HubHydrationThreadCount		  = 0;	// Internal hydration parallelism (per-file)
	int					  HubInstanceCoreLimit			  = 0;	// Automatic
	std::filesystem::path HubInstanceConfigPath;				// Path to Lua config file
	std::string			  HydrationTargetSpecification;			// hydration/dehydration target specification
	std::filesystem::path HydrationTargetConfigPath;  // path to JSON config file (mutually exclusive with HydrationTargetSpecification)
	ZenHubWatchdogConfig  WatchdogConfig;
	uint64_t			  HubProvisionDiskLimitBytes	 = 0;
	uint32_t			  HubProvisionDiskLimitPercent	 = 0;
	uint64_t			  HubProvisionMemoryLimitBytes	 = 0;
	uint32_t			  HubProvisionMemoryLimitPercent = 0;
};

class Hub;
struct HubProvisionedInstanceInfo;

struct ZenHubServerConfigurator : public ZenServerConfiguratorBase
{
	ZenHubServerConfigurator(ZenHubServerConfig& ServerOptions) : ZenServerConfiguratorBase(ServerOptions), m_ServerOptions(ServerOptions)
	{
	}

	~ZenHubServerConfigurator() = default;

private:
	virtual void AddCliOptions(cxxopts::Options& Options) override;
	virtual void AddConfigOptions(LuaConfig::Options& Options) override;
	virtual void ApplyOptions(cxxopts::Options& Options) override;
	virtual void OnConfigFileParsed(LuaConfig::Options& LuaOptions) override;
	virtual void ValidateOptions() override;

	ZenHubServerConfig& m_ServerOptions;
};

class ZenHubServerMain : public ZenServerMain
{
public:
	ZenHubServerMain(ZenHubServerConfig& ServerOptions);
	virtual void DoRun(ZenServerState::ZenServerEntry* Entry) override;

	ZenHubServerMain(const ZenHubServerMain&) = delete;
	ZenHubServerMain& operator=(const ZenHubServerMain&) = delete;

	typedef ZenHubServerConfig		 Config;
	typedef ZenHubServerConfigurator Configurator;

private:
	ZenHubServerConfig& m_ServerOptions;
};

class ZenHubServer : public ZenServerBase
{
	ZenHubServer& operator=(ZenHubServer&&) = delete;
	ZenHubServer(ZenHubServer&&)			= delete;

public:
	ZenHubServer();
	~ZenHubServer();

	int	 Initialize(const ZenHubServerConfig& ServerConfig, ZenServerState::ZenServerEntry* ServerEntry);
	void Run();
	void Cleanup();

	void SetDataRoot(std::filesystem::path Root) { m_DataRoot = Root; }
	void SetContentRoot(std::filesystem::path Root) { m_ContentRoot = Root; }

private:
	void OnModuleStateChanged(std::string_view					HubInstanceId,
							  std::string_view					ModuleId,
							  const HubProvisionedInstanceInfo& Info,
							  HubInstanceState					PreviousState,
							  HubInstanceState					NewState);

	std::filesystem::path m_DataRoot;
	std::filesystem::path m_ContentRoot;
	bool				  m_DebugOptionForcedCrash = false;

	std::unique_ptr<HttpProxyHandler> m_Proxy;
	std::unique_ptr<WorkerThreadPool> m_ProvisionPool;
	std::unique_ptr<WorkerThreadPool> m_SpawnPool;
	std::unique_ptr<WorkerThreadPool> m_HydrationPool;
	std::unique_ptr<Hub>			  m_Hub;

	std::unique_ptr<HttpHubService>		 m_HubService;
	std::unique_ptr<HttpApiService>		 m_ApiService;
	std::unique_ptr<HttpFrontendService> m_FrontendService;

	std::unique_ptr<consul::ConsulClient>		 m_ConsulClient;
	std::unique_ptr<consul::ServiceRegistration> m_ConsulRegistration;
	uint32_t									 m_ConsulHealthIntervalSeconds	= 10;
	uint32_t									 m_ConsulDeregisterAfterSeconds = 30;

	static ResourceMetrics ResolveLimits(const ZenHubServerConfig& ServerConfig);

	void InitializeState(const ZenHubServerConfig& ServerConfig);
	void InitializeServices(const ZenHubServerConfig& ServerConfig);
	void RegisterServices(const ZenHubServerConfig& ServerConfig);
	void InitializeConsulRegistration(const ZenHubServerConfig& ServerConfig, int EffectivePort);
};

}  // namespace zen