// Copyright Epic Games, Inc. All Rights Reserved. #include "storageserverinstance.h" #include "hydration.h" #include #include #include #include namespace zen { StorageServerInstance::StorageServerInstance(ZenServerEnvironment& RunEnvironment, const Configuration& Config, std::string_view ModuleId) : m_Config(Config) , m_ModuleId(ModuleId) , m_ServerInstance(RunEnvironment, ZenServerInstance::ServerMode::kStorageServer) { m_BaseDir = RunEnvironment.CreateChildDir(ModuleId); m_TempDir = Config.HydrationTempPath / ModuleId; } StorageServerInstance::~StorageServerInstance() { } void StorageServerInstance::SpawnServerProcess() { ZEN_ASSERT_FORMAT(!m_ServerInstance.IsRunning(), "Storage server instance for module '{}' is already running", m_ModuleId); m_ServerInstance.SetServerExecutablePath(GetRunningExecutablePath()); m_ServerInstance.SetDataDir(m_BaseDir); #if ZEN_PLATFORM_WINDOWS m_ServerInstance.SetJobObject(m_JobObject); #endif ExtendableStringBuilder<256> AdditionalOptions; AdditionalOptions << "--allow-port-probing=false"; if (m_Config.HttpThreadCount != 0) { AdditionalOptions << " --http-threads=" << m_Config.HttpThreadCount; } if (m_Config.CoreLimit != 0) { AdditionalOptions << " --corelimit=" << m_Config.CoreLimit; } m_ServerInstance.SpawnServerAndWaitUntilReady(m_Config.BasePort, AdditionalOptions.ToView()); ZEN_DEBUG("Storage server instance for module '{}' started, listening on port {}", m_ModuleId, m_Config.BasePort); m_ServerInstance.EnableShutdownOnDestroy(); } void StorageServerInstance::Provision() { RwLock::ExclusiveLockScope _(m_Lock); if (m_IsProvisioned) { ZEN_WARN("Storage server instance for module '{}' is already provisioned", m_ModuleId); return; } if (m_IsHibernated) { WakeLocked(); } else { ZEN_INFO("Provisioning storage server instance for module '{}', at '{}'", m_ModuleId, m_BaseDir); Hydrate(); SpawnServerProcess(); } m_IsProvisioned = true; } void StorageServerInstance::Deprovision() { RwLock::ExclusiveLockScope _(m_Lock); if (!m_IsProvisioned) { ZEN_WARN("Attempted to deprovision storage server instance for module '{}' which is not provisioned", m_ModuleId); return; } ZEN_INFO("Deprovisioning storage server instance for module '{}'", m_ModuleId); m_ServerInstance.Shutdown(); Dehydrate(); m_IsProvisioned = false; } void StorageServerInstance::Hibernate() { // Signal server to shut down, but keep data around for later wake RwLock::ExclusiveLockScope _(m_Lock); if (!m_IsProvisioned) { ZEN_WARN("Attempted to hibernate storage server instance for module '{}' which is not provisioned", m_ModuleId); return; } if (m_IsHibernated) { ZEN_WARN("Storage server instance for module '{}' is already hibernated", m_ModuleId); return; } if (!m_ServerInstance.IsRunning()) { ZEN_WARN("Attempted to hibernate storage server instance for module '{}' which is not running", m_ModuleId); // This is an unexpected state. Should consider the instance invalid? return; } try { m_ServerInstance.Shutdown(); m_IsHibernated = true; m_IsProvisioned = false; return; } catch (const std::exception& Ex) { ZEN_ERROR("Failed to hibernate storage server instance for module '{}': {}", m_ModuleId, Ex.what()); } } void StorageServerInstance::Wake() { RwLock::ExclusiveLockScope _(m_Lock); WakeLocked(); } void StorageServerInstance::WakeLocked() { // Start server in-place using existing data if (!m_IsHibernated) { ZEN_WARN("Attempted to wake storage server instance for module '{}' which is not hibernated", m_ModuleId); return; } ZEN_ASSERT_FORMAT(!m_ServerInstance.IsRunning(), "Storage server instance for module '{}' is already running", m_ModuleId); try { SpawnServerProcess(); m_IsHibernated = false; } catch (const std::exception& Ex) { ZEN_ERROR("Failed to wake storage server instance for module '{}': {}", m_ModuleId, Ex.what()); // TODO: this instance should be marked as invalid } } void StorageServerInstance::Hydrate() { HydrationConfig Config{.ServerStateDir = m_BaseDir, .TempDir = m_TempDir, .ModuleId = m_ModuleId, .TargetSpecification = WideToUtf8(m_Config.FileHydrationPath.native())}; std::unique_ptr Hydrator = CreateFileHydrator(); Hydrator->Configure(Config); Hydrator->Hydrate(); } void StorageServerInstance::Dehydrate() { HydrationConfig Config{.ServerStateDir = m_BaseDir, .TempDir = m_TempDir, .ModuleId = m_ModuleId, .TargetSpecification = WideToUtf8(m_Config.FileHydrationPath.native())}; std::unique_ptr Hydrator = CreateFileHydrator(); Hydrator->Configure(Config); Hydrator->Dehydrate(); } } // namespace zen