Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 51 additions & 1 deletion src/cpp/src/kvs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Kvs::Kvs()
, parser(std::make_unique<score::json::JsonParser>())
, writer(std::make_unique<score::json::JsonWriter>())
, logger(std::make_unique<score::mw::log::Logger>("SKVS"))
, storage_mode_(score::os::Stat::Mode::kReadUser|score::os::Stat::Mode::kWriteUser|score::os::Stat::Mode::kReadGroup|score::os::Stat::Mode::kReadOthers) /* Default storage mode */
{
}

Expand All @@ -48,6 +49,7 @@ Kvs::Kvs(Kvs&& other) noexcept
, parser(std::move(other.parser)) /* Not absolutely necessary, because a new JSON writer/parser object would also be okay*/
, writer(std::move(other.writer))
, logger(std::move(other.logger))
, storage_mode_(other.storage_mode_)
{
{
std::lock_guard<std::mutex> lock(other.kvs_mutex);
Expand Down Expand Up @@ -88,6 +90,7 @@ Kvs& Kvs::operator=(Kvs&& other) noexcept
parser = std::move(other.parser);
writer = std::move(other.writer);
logger = std::move(other.logger);
storage_mode_ = other.storage_mode_;
}
return *this;
}
Expand Down Expand Up @@ -194,7 +197,7 @@ score::Result<std::unordered_map<string, KvsValue>> Kvs::open_json(const score::
}

/* Open KVS Instance */
score::Result<Kvs> Kvs::open(const InstanceId& instance_id, OpenNeedDefaults need_defaults, OpenNeedKvs need_kvs, const std::string&& dir)
score::Result<Kvs> Kvs::open(const InstanceId& instance_id, OpenNeedDefaults need_defaults, OpenNeedKvs need_kvs, const std::string&& dir, score::os::Stat::Mode storage_mode)
{
score::Result<Kvs> result = score::MakeUnexpected(ErrorCode::UnmappedError); /* Redundant initialization needed, since Resul<KVS> would call the implicitly-deleted default constructor of KVS */

Expand All @@ -221,6 +224,7 @@ score::Result<Kvs> Kvs::open(const InstanceId& instance_id, OpenNeedDefaults nee
kvs.default_values = std::move(default_res.value());
kvs.filename_prefix = filename_prefix;
kvs.flush_on_exit.store(true, std::memory_order_relaxed);
kvs.storage_mode_ = storage_mode;
kvs.logger->LogInfo() << "opened KVS: instance '" << instance_id.id << "'";
kvs.logger->LogInfo() << "max snapshot count: " << KVS_MAX_SNAPSHOTS;
result = std::move(kvs);
Expand Down Expand Up @@ -397,6 +401,33 @@ score::ResultBlank Kvs::remove_key(const std::string_view key) {
return result;
}

score::Result<bool> Kvs::create_file_if_not_exist(const std::string& path)
{
score::Result<bool> result = score::MakeUnexpected(ErrorCode::UnmappedError);
std::unique_lock<std::mutex> lock(kvs_mutex, std::try_to_lock);
if (lock.owns_lock()) {
auto isFileExist = filesystem->standard->Exists(path);
if (!isFileExist.value()) {
std::ofstream out(path, std::ios::binary);
if (out) {
out.close();
filesystem->standard->Permissions(path, this->storage_mode_);
result = true;
} else {
logger->LogError() << "Failed to create file '" << path << "'";
result = score::MakeUnexpected(ErrorCode::PhysicalStorageFailure);
}
} else {
logger->LogInfo() << "File '" << path << "' already exists. Skipping creation.";
result = false;
}
} else {
result = score::MakeUnexpected(ErrorCode::MutexLockFailed);
}
return result;
}


/* Helper Function to write JSON data to a file for flush process (also adds Hash file)*/
score::ResultBlank Kvs::write_json_data(const std::string& buf)
{
Expand All @@ -408,13 +439,32 @@ score::ResultBlank Kvs::write_json_data(const std::string& buf)
if(!create_path_res.has_value()) {
result = score::MakeUnexpected(ErrorCode::PhysicalStorageFailure);
} else {
auto create_file_res = create_file_if_not_exist(json_path.Native());
if (!create_file_res) {
logger->LogError() << "Failed to create KVS file '" << json_path << "'";
result = score::MakeUnexpected(static_cast<ErrorCode>(*create_file_res.error()));
} else {
if (create_file_res.value()) {
logger->LogInfo() << "Created new KVS file '" << json_path << "'";
}
}
std::ofstream out(json_path.CStr(), std::ios::binary);
if (!out.write(buf.data(), buf.size())) {
result = score::MakeUnexpected(ErrorCode::PhysicalStorageFailure);
} else {
/* Write Hash File */
std::array<uint8_t, 4> hash_bytes = get_hash_bytes(buf);
score::filesystem::Path fn_hash = filename_prefix.Native() + "_0.hash";
auto create_hash_file_res = create_file_if_not_exist(fn_hash.Native());
if(!create_hash_file_res) {
logger->LogError() << "Failed to create KVS hash file '" << fn_hash << "'";
result = score::MakeUnexpected(static_cast<ErrorCode>(*create_hash_file_res.error()));
}
else {
if (create_hash_file_res.value()) {
logger->LogInfo() << "Created new KVS hash file '" << fn_hash << "'";
}
}
std::ofstream hout(fn_hash.CStr(), std::ios::binary);
if (!hout.write(reinterpret_cast<const char*>(hash_bytes.data()), hash_bytes.size())) {
result = score::MakeUnexpected(ErrorCode::PhysicalStorageFailure);
Expand Down
4 changes: 3 additions & 1 deletion src/cpp/src/kvs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ class Kvs final {
* IMPORTANT: Instead of using the Kvs::open method directly, it is recommended to use the KvsBuilder class.
*
*/
static score::Result<Kvs> open(const InstanceId& instance_id, OpenNeedDefaults need_defaults, OpenNeedKvs need_kvs, const std::string&& dir);
static score::Result<Kvs> open(const InstanceId& instance_id, OpenNeedDefaults need_defaults, OpenNeedKvs need_kvs, const std::string&& dir, score::os::Stat::Mode storage_mode);

/**
* @brief Sets whether the key-value store should flush its contents to
Expand Down Expand Up @@ -383,12 +383,14 @@ class Kvs final {

/* Logging */
std::unique_ptr<score::mw::log::Logger> logger;
score::os::Stat::Mode storage_mode_;

/* Private Methods */
score::ResultBlank snapshot_rotate();
score::Result<std::unordered_map<std::string, KvsValue>> parse_json_data(const std::string& data);
score::Result<std::unordered_map<std::string, KvsValue>> open_json(const score::filesystem::Path& prefix, OpenJsonNeedFile need_file);
score::ResultBlank write_json_data(const std::string& buf);
score::Result<bool> create_file_if_not_exist(const std::string& path);

};

Expand Down
8 changes: 7 additions & 1 deletion src/cpp/src/kvsbuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ KvsBuilder::KvsBuilder(const InstanceId& instance_id)
, need_defaults(false)
, need_kvs(false)
, directory("./data_folder/") /* Default Directory */
, storage_mode_(score::os::Stat::Mode::kReadUser|score::os::Stat::Mode::kWriteUser|score::os::Stat::Mode::kReadGroup|score::os::Stat::Mode::kReadOthers) /* Default storage mode */
{}

KvsBuilder& KvsBuilder::need_defaults_flag(bool flag) {
Expand All @@ -37,6 +38,10 @@ KvsBuilder& KvsBuilder::dir(std::string&& dir_path) {
return *this;
}

KvsBuilder& KvsBuilder::storage_mode(score::os::Stat::Mode mode) {
storage_mode_ = mode;
return *this;
}

score::Result<Kvs> KvsBuilder::build() {
score::Result<Kvs> result = score::MakeUnexpected(ErrorCode::UnmappedError);
Expand All @@ -50,7 +55,8 @@ score::Result<Kvs> KvsBuilder::build() {
instance_id,
need_defaults ? OpenNeedDefaults::Required : OpenNeedDefaults::Optional,
need_kvs ? OpenNeedKvs::Required : OpenNeedKvs::Optional,
std::move(directory)
std::move(directory),
storage_mode_
);

return result;
Expand Down
8 changes: 8 additions & 0 deletions src/cpp/src/kvsbuilder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,13 @@ class KvsBuilder final {
*/
KvsBuilder& dir(std::string&& dir_path);

/**
* @brief Specify the storage mode for the KVS files.
* @param mode The storage mode to use (storage file permissions).
* @return Reference to this builder (for chaining).
*/
KvsBuilder& storage_mode(score::os::Stat::Mode mode);

/**
* @brief Builds and opens the Kvs instance with the configured options.
*
Expand All @@ -101,6 +108,7 @@ class KvsBuilder final {
bool need_defaults; ///< Whether default values are required
bool need_kvs; ///< Whether an existing KVS is required
std::string directory; ///< Directory where to store the KVS Files
score::os::Stat::Mode storage_mode_; ///< Storage mode for the KVS files
};

} /* namespace score::mw::per::kvs */
Expand Down
Loading