Files
hpcore/src/hpfs/hpfs.cpp
Ravin Perera fe9e276f8d Consensus deadlock fixes and reliability improvements. (#187)
* Consensus time vote improvements.
* Skipped self proposal in Observer mode.
* Added docker cluster script params.
* Added state/lcl sync abandon threshold.
* Added pubkey display for cluster script.
* Added proposal latency log.
* Added sync completion check to prevent deadlocks.
2020-12-08 15:17:27 +05:30

310 lines
11 KiB
C++

#include "hpfs.hpp"
#include "h32.hpp"
#include "../conf.hpp"
#include "../hplog.hpp"
#include "../util/util.hpp"
namespace hpfs
{
constexpr const char *HPFS_TRACE_ARG_ERROR = "trace=error";
constexpr const char *HPFS_TRACE_ARG_DEBUG = "trace=error";
constexpr const char *HPFS_HMAP_HASH = "::hpfs.hmap.hash";
constexpr const char *HPFS_HMAP_CHILDREN = "::hpfs.hmap.children";
constexpr const char *HPFS_SESSION = "::hpfs.session";
constexpr ino_t HPFS_ROOT_INO = 2;
constexpr uint16_t INIT_CHECK_INTERVAL = 20;
/**
* Starts hpfs merge process.
*/
int start_merge_process(pid_t &hpfs_pid)
{
const pid_t pid = fork();
if (pid > 0)
{
LOG_DEBUG << "Starting hpfs merge process...";
// HotPocket process.
util::sleep(INIT_CHECK_INTERVAL);
// Check if hpfs process is still running.
// Sending signal 0 to test whether process exist.
if (util::kill_process(pid, false, 0) == -1)
return -1;
hpfs_pid = pid;
LOG_DEBUG << "hpfs merge process started. pid:" << hpfs_pid;
}
else if (pid == 0)
{
// hpfs process.
util::fork_detach();
const char *active_hpfs_trace_arg = (conf::cfg.loglevel_type == conf::LOG_SEVERITY::DEBUG ? HPFS_TRACE_ARG_DEBUG : HPFS_TRACE_ARG_ERROR);
// Fill process args.
char *execv_args[] = {
conf::ctx.hpfs_exe_path.data(),
(char *)"merge",
conf::ctx.state_dir.data(),
(char *)active_hpfs_trace_arg,
NULL};
const int ret = execv(execv_args[0], execv_args);
std::cerr << errno << ": hpfs merge process execv failed.\n";
exit(1);
}
else
{
LOG_ERROR << errno << ": fork() failed when starting hpfs merge process.";
return -1;
}
return 0;
}
/**
* Starts hpfs readonly/readwrite process and also starts a virtual fs session.
*/
int start_ro_rw_process(pid_t &hpfs_pid, std::string &mount_dir, const bool readonly,
const bool hash_map_enabled, const bool auto_start_session, const uint16_t timeout)
{
const pid_t pid = fork();
const char *mode = readonly ? "ro" : "rw";
if (pid > 0)
{
// HotPocket process.
LOG_DEBUG << "Starting hpfs " << mode << " process at " << mount_dir;
// If the mount dir is not specified, assign a mount dir based on hpfs process id.
if (mount_dir.empty())
mount_dir = std::string(conf::ctx.state_dir)
.append("/")
.append(std::to_string(pid));
// Wait until hpfs is initialized properly.
const uint16_t max_retries = timeout / INIT_CHECK_INTERVAL;
bool hpfs_initialized = false;
uint16_t retry_count = 0;
do
{
util::sleep(INIT_CHECK_INTERVAL);
// Check if hpfs process is still running.
// Sending signal 0 to test whether process exist.
if (util::kill_process(pid, false, 0) == -1)
{
LOG_ERROR << "hpfs process " << pid << " has stopped at " << mount_dir;
break;
}
// We check for the specific inode no. of the mounted root dir. That means hpfs FUSE interface is up.
struct stat st;
if (stat(mount_dir.c_str(), &st) == -1)
{
LOG_ERROR << errno << ": Error in checking hpfs status at " << mount_dir;
break;
}
hpfs_initialized = (st.st_ino == HPFS_ROOT_INO);
// Keep retrying until root inode no. matches or timeout occurs.
} while (!hpfs_initialized && ++retry_count <= max_retries);
// If hpfs FUSE interface initialized within the timeout period, we then attempt to start up a virtual fs session.
// hpfs achieves this by having a 'session' file created.
if (hpfs_initialized && auto_start_session)
start_fs_session(mount_dir);
// Kill the process if hpfs couldn't be initialized properly.
if (!hpfs_initialized)
{
LOG_ERROR << "Couldn't initialize hpfs session at " << mount_dir;
util::kill_process(pid, true);
return -1;
}
hpfs_pid = pid;
LOG_DEBUG << "hpfs " << mode << " process started at " << mount_dir << " pid:" << hpfs_pid;
}
else if (pid == 0)
{
// hpfs process.
util::fork_detach();
// If the mount dir is not specified, assign a mount dir based on hpfs process id.
const pid_t self_pid = getpid();
if (mount_dir.empty())
mount_dir = std::string(conf::ctx.state_dir)
.append("/")
.append(std::to_string(self_pid));
const char *active_hpfs_trace_arg = (conf::cfg.loglevel_type == conf::LOG_SEVERITY::DEBUG ? HPFS_TRACE_ARG_DEBUG : HPFS_TRACE_ARG_ERROR);
// Fill process args.
char *execv_args[] = {
conf::ctx.hpfs_exe_path.data(),
(char *)mode, // hpfs mode: rw | ro
conf::ctx.state_dir.data(),
mount_dir.data(),
(char *)(hash_map_enabled ? "hmap=true" : "hmap=false"),
(char *)active_hpfs_trace_arg,
NULL};
const int ret = execv(execv_args[0], execv_args);
std::cerr << errno << ": hpfs session process execv failed.\n";
exit(1);
}
else
{
LOG_ERROR << errno << ": fork() failed when starting hpfs process.";
return -1;
}
return 0;
}
/**
* Starts a virtual fs session on the hpfs process attached to the specified mount dir.
*/
int start_fs_session(std::string_view mount_dir)
{
LOG_DEBUG << "Starting hpfs fs session at " << mount_dir;
const std::string session_file = std::string(mount_dir).append("/").append(HPFS_SESSION);
if (mknod(session_file.c_str(), 0, 0) == -1)
{
LOG_ERROR << errno << ": Error starting hpfs fs session at " << mount_dir;
return -1;
}
return 0;
}
/**
* Stops the active virtual fs session on the hpfs process attached to the specified mount dir.
*/
int stop_fs_session(std::string_view mount_dir)
{
LOG_DEBUG << "Stopping hpfs fs session at " << mount_dir;
const std::string session_file = std::string(mount_dir).append("/").append(HPFS_SESSION);
if (unlink(session_file.c_str()) == -1)
{
LOG_ERROR << errno << ": Error stopping hpfs fs session at " << mount_dir;
return -1;
}
return 0;
}
/**
* Populates the hash of the specified vpath.
* @return 1 on success. 0 if vpath not found. -1 on error.
*/
int get_hash(h32 &hash, const std::string_view mount_dir, const std::string_view vpath)
{
const std::string path = std::string(mount_dir).append(vpath).append(HPFS_HMAP_HASH);
const int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
if (fd == -1 && errno == ENOENT)
{
LOG_DEBUG << "Cannot get hash. vpath not found. " << vpath;
return 0;
}
else if (fd == -1)
{
LOG_ERROR << errno << ": Error opening hash file. " << vpath;
return -1;
}
const int res = read(fd, &hash, sizeof(h32));
close(fd);
if (res == -1)
{
LOG_ERROR << errno << ": Error reading hash file. " << vpath;
return -1;
}
return 1;
}
/**
* Populates the list of file block hashes for the specified vpath.
* @return 1 on success. 0 if vpath not found. -1 on error.
*/
int get_file_block_hashes(std::vector<h32> &hashes, const std::string_view mount_dir, const std::string_view vpath)
{
const std::string path = std::string(mount_dir).append(vpath).append(HPFS_HMAP_CHILDREN);
const int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
if (fd == -1 && errno == ENOENT)
{
LOG_DEBUG << "Cannot get file block hashes. vpath not found. " << vpath;
return 0;
}
else if (fd == -1)
{
LOG_DEBUG << errno << ": Error opening hashmap children. " << vpath;
return -1;
}
struct stat st;
if (fstat(fd, &st) == -1)
{
close(fd);
LOG_ERROR << errno << ": Error reading block hashes length. " << vpath;
return -1;
}
const int children_count = st.st_size / sizeof(h32);
hashes.resize(children_count);
const int res = read(fd, hashes.data(), st.st_size);
close(fd);
if (res == -1)
{
LOG_ERROR << errno << ": Error reading block hashes. " << vpath;
return -1;
}
return 1;
}
/**
* Populates the list of dir entry hashes for the specified vpath.
* @return 1 on success. 0 if vpath not found. -1 on error.
*/
int get_dir_children_hashes(std::vector<child_hash_node> &hash_nodes, const std::string_view mount_dir, const std::string_view dir_vpath)
{
const std::string path = std::string(mount_dir).append(dir_vpath).append("::hpfs.hmap.children");
const int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
if (fd == -1 && errno == ENOENT)
{
LOG_DEBUG << "Cannot get dir children hashes. Dir vpath not found. " << dir_vpath;
return 0;
}
else if (fd == -1)
{
LOG_ERROR << errno << ": Error opening dir hash children nodes. " << dir_vpath;
return -1;
}
struct stat st;
if (fstat(fd, &st) == -1)
{
close(fd);
LOG_ERROR << errno << ": Error reading hash children nodes length. " << dir_vpath;
return -1;
}
const int children_count = st.st_size / sizeof(child_hash_node);
hash_nodes.resize(children_count);
const int res = read(fd, hash_nodes.data(), st.st_size);
close(fd);
if (res == -1)
{
LOG_ERROR << errno << ": Error reading hash children nodes. " << dir_vpath;
return -1;
}
return 1;
}
} // namespace hpfs