// Copyright Epic Games, Inc. All Rights Reserved. #include "storageserverinstance.h" #include "hydration.h" #include #include #include #include namespace zen { StorageServerInstance::StorageServerInstance(ZenServerEnvironment& RunEnvironment, std::string_view ModuleId, std::filesystem::path FileHydrationPath, std::filesystem::path HydrationTempPath) : m_ModuleId(ModuleId) , m_ServerInstance(RunEnvironment, ZenServerInstance::ServerMode::kStorageServer) , m_HydrationPath(FileHydrationPath) { m_BaseDir = RunEnvironment.CreateChildDir(ModuleId); m_TempDir = 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 const uint16_t BasePort = m_ServerInstance.SpawnServerAndWaitUntilReady(); ZEN_DEBUG("Storage server instance for module '{}' started, listening on port {}", m_ModuleId, 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_HydrationPath.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_HydrationPath.native())}; std::unique_ptr Hydrator = CreateFileHydrator(); Hydrator->Configure(Config); Hydrator->Dehydrate(); } } // namespace zen