aboutsummaryrefslogtreecommitdiff
path: root/src/zenserver/hub/hydration.cpp
blob: 52c17fe1a577835ddd30d922f29a3444229c6f8e (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
// Copyright Epic Games, Inc. All Rights Reserved.

#include "hydration.h"

#include <zencore/filesystem.h>
#include <zencore/fmtutils.h>

namespace zen {

///////////////////////////////////////////////////////////////////////////

struct FileHydrator : public HydrationStrategyBase
{
	virtual void Configure(const HydrationConfig& Config) override;
	virtual void Hydrate() override;
	virtual void Dehydrate() override;

private:
	HydrationConfig		  m_Config;
	std::filesystem::path m_StorageModuleRootDir;
};

void
FileHydrator::Configure(const HydrationConfig& Config)
{
	m_Config = Config;

	std::filesystem::path ConfigPath(Utf8ToWide(m_Config.TargetSpecification));

	if (!std::filesystem::exists(ConfigPath))
	{
		throw std::invalid_argument(fmt::format("Target does not exist: '{}'", ConfigPath.string()));
	}

	m_StorageModuleRootDir = ConfigPath / m_Config.ModuleId;

	CreateDirectories(m_StorageModuleRootDir);
}

void
FileHydrator::Hydrate()
{
	ZEN_INFO("Hydrating state from '{}' to '{}'", m_StorageModuleRootDir, m_Config.ServerStateDir);

	// Ensure target is clean
	ZEN_DEBUG("Wiping server state at '{}'", m_Config.ServerStateDir);
	const bool ForceRemoveReadOnlyFiles = true;
	CleanDirectory(m_Config.ServerStateDir, ForceRemoveReadOnlyFiles);

	bool WipeServerState = false;

	try
	{
		ZEN_DEBUG("Copying '{}' to '{}'", m_StorageModuleRootDir, m_Config.ServerStateDir);
		CopyTree(m_StorageModuleRootDir, m_Config.ServerStateDir, {.EnableClone = true});
	}
	catch (std::exception& Ex)
	{
		ZEN_WARN("Copy failed: {}. Will wipe any partially copied state from '{}'", Ex.what(), m_Config.ServerStateDir);

		// We don't do the clean right here to avoid potentially running into double-throws
		WipeServerState = true;
	}

	if (WipeServerState)
	{
		ZEN_DEBUG("Cleaning server state '{}'", m_Config.ServerStateDir);
		CleanDirectory(m_Config.ServerStateDir, ForceRemoveReadOnlyFiles);
	}

	// Note that we leave the storage state intact until next dehydration replaces the content
}

void
FileHydrator::Dehydrate()
{
	ZEN_INFO("Dehydrating state from '{}' to '{}'", m_Config.ServerStateDir, m_StorageModuleRootDir);

	const std::filesystem::path TargetDir = m_StorageModuleRootDir;

	// Ensure target is clean. This could be replaced with an atomic copy at a later date
	// (i.e copy into a temporary directory name and rename it once complete)

	ZEN_DEBUG("Cleaning storage root '{}'", TargetDir);
	const bool ForceRemoveReadOnlyFiles = true;
	CleanDirectory(TargetDir, ForceRemoveReadOnlyFiles);

	bool CopySuccess = true;

	try
	{
		ZEN_DEBUG("Copying '{}' to '{}'", m_Config.ServerStateDir, TargetDir);
		CopyTree(m_Config.ServerStateDir, TargetDir, {.EnableClone = true});
	}
	catch (std::exception& Ex)
	{
		ZEN_WARN("Copy failed: {}. Will wipe any partially copied state from '{}'", Ex.what(), m_StorageModuleRootDir);

		// We don't do the clean right here to avoid potentially running into double-throws
		CopySuccess = false;
	}

	if (!CopySuccess)
	{
		ZEN_DEBUG("Removing partially copied state from '{}'", TargetDir);
		CleanDirectory(TargetDir, ForceRemoveReadOnlyFiles);
	}

	ZEN_DEBUG("Wiping server state '{}'", m_Config.ServerStateDir);
	CleanDirectory(m_Config.ServerStateDir, ForceRemoveReadOnlyFiles);
}

std::unique_ptr<HydrationStrategyBase>
CreateFileHydrator()
{
	return std::make_unique<FileHydrator>();
}

}  // namespace zen