Skip to content
This repository has been archived by the owner on Mar 3, 2020. It is now read-only.

Commit

Permalink
Task/rich/kep 1093 Implement Volatile TTL Eviction Policy (#361)
Browse files Browse the repository at this point in the history
* KEP-1093 Implement Volatile TTL Eviction Policy

* KEP-1093 Implement Volatile TTL Eviction Policy - fixing issues found in code review
  • Loading branch information
rnistuk committed Oct 4, 2019
1 parent 947d9d8 commit cd7487a
Show file tree
Hide file tree
Showing 16 changed files with 779 additions and 105 deletions.
3 changes: 2 additions & 1 deletion CMakeLists.txt
Expand Up @@ -36,7 +36,7 @@ add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY)
# todo: remove -Wno-implicit-fallthrough once CI moves past gcc 7.4.0...
set(warnings "-Wno-deprecated-declarations -Wall -Wextra -Werror -Wpedantic -Wno-implicit-fallthrough")
if (APPLE)
set(warnings "${warnings} -Wno-extended-offsetof")
set(warnings "${warnings} -Wno-invalid-offsetof")
else()
# for beast and gcc release builds...
if("${CMAKE_BUILD_TYPE}" STREQUAL "Release")
Expand Down Expand Up @@ -98,6 +98,7 @@ add_subdirectory(chaos)
add_subdirectory(crypto)
add_subdirectory(monitor)
add_subdirectory(mocks)
add_subdirectory(policy)

include(cmake/static_analysis.cmake)

Expand Down
2 changes: 1 addition & 1 deletion crud/CMakeLists.txt
Expand Up @@ -7,7 +7,7 @@ add_library(crud STATIC
crud.hpp
)

target_link_libraries(crud proto)
target_link_libraries(crud proto policy)
add_dependencies(crud boost jsoncpp openssl)
target_include_directories(crud PRIVATE ${BLUZELLE_STD_INCLUDES})
add_subdirectory(test)
115 changes: 53 additions & 62 deletions crud/crud.cpp
Expand Up @@ -13,6 +13,8 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.

#include <crud/crud.hpp>
#include <policy/random.hpp>
#include <policy/volatile_ttl.hpp>
#include <utils/make_endpoint.hpp>
#include <functional>
#include <boost/algorithm/string/trim_all.hpp>
Expand Down Expand Up @@ -210,6 +212,14 @@ crud::handle_create(const bzn::caller_id_t& caller_id, const database_msg& reque
}
else
{
// bail on key value pairs that are too large right away!
if (this->max_database_size(perms) && request.create().key().length() + request.create().value().length() > this->max_database_size(perms))
{
this->send_response(request, bzn::storage_result::value_too_large, database_response(), session);

return;
}

if (this->expired(request.header().db_uuid(), request.create().key()))
{
this->send_response(request, bzn::storage_result::delete_pending, database_response(), session);
Expand All @@ -219,16 +229,7 @@ crud::handle_create(const bzn::caller_id_t& caller_id, const database_msg& reque

if (this->operation_exceeds_available_space(request, perms))
{
const auto key_value_size{request.create().key().length() + request.create().value().length()};

// Bail if the size of the key/value pair is larger than the database! If not, then check for a cache
// replacement policy
if (this->uses_random_eviction_policy(perms) && key_value_size < this->max_database_size(perms))
{
// TODO: at present there is only one policy, later refactor this to use the strategy pattern
this->random_cache_replacement(request, key_value_size, this->max_database_size(perms));
}
else
if (!this->do_eviction(request, this->max_database_size(perms)))
{
this->send_response(request, bzn::storage_result::db_full, database_response(), session);

Expand Down Expand Up @@ -313,6 +314,14 @@ crud::handle_update(const bzn::caller_id_t& caller_id, const database_msg& reque
}
else
{
// bail on key value pairs that are too large right away!
if (this->max_database_size(perms) && request.create().key().length() + request.create().value().length() > this->max_database_size(perms))
{
this->send_response(request, bzn::storage_result::value_too_large, database_response(), session);

return;
}

// expired?
if (this->expired(request.header().db_uuid(), request.update().key()))
{
Expand All @@ -323,19 +332,8 @@ crud::handle_update(const bzn::caller_id_t& caller_id, const database_msg& reque

if (this->operation_exceeds_available_space(request, perms))
{
// since this is an update, we do not change the key, so it's length can be ignored.
const auto key_value_size{request.update().value().length()};

// Bail if the size of the key/value pair is larger than the database! If not, then check for a cache
// replacement policy
if (this->uses_random_eviction_policy(perms) && key_value_size < this->max_database_size(perms))
{
// TODO: at present there is only one policy, later refactor this to use the strategy pattern
// Since this is an update, we need to ensure that the key we are updating does not get randomly
// selected for eviction, so we need to tell the policy what key that is.
this->random_cache_replacement(request, key_value_size, this->max_database_size(perms), request.update().key());
}
else
// let's try evicting some key/value pairs
if (!this->do_eviction(request, this->max_database_size(perms)))
{
this->send_response(request, bzn::storage_result::db_full, database_response(), session);

Expand Down Expand Up @@ -995,10 +993,21 @@ crud::is_caller_a_writer(const bzn::caller_id_t& caller_id, const Json::Value& p
}


bool
crud::uses_random_eviction_policy(const Json::Value& perms) const
std::shared_ptr<policy::eviction_base>
crud::get_eviction_policy(const Json::Value& perms)
{
return perms[EVICTION_POLICY_KEY] == database_create_db::RANDOM;
// TODO: As we add more policies we may want to turn this into the strategy pattern and use
// a registry based approach here
if (perms[EVICTION_POLICY_KEY] == database_create_db::RANDOM)
{
return std::make_shared<policy::random>(this->storage);
}
else if (perms[EVICTION_POLICY_KEY] == database_create_db::VOLATILE_TTL)
{
return std::make_shared<policy::volatile_ttl>(this->storage);
}

return nullptr;
}


Expand Down Expand Up @@ -1103,7 +1112,7 @@ crud::update_expiration_entry(const bzn::key_t& generated_key, uint64_t expire)

if (result == bzn::storage_result::ok)
{
LOG(debug) << "created ttl entry for: " << generated_key;
LOG(debug) << "created ttl entry [" << expires << "] for: " << generated_key;

return;
}
Expand Down Expand Up @@ -1335,46 +1344,28 @@ crud::get_swarm_storage_usage()
}


size_t
crud::evict_key(const bzn::uuid_t& db_uuid, const bzn::key_t& key)
{
const auto pair_size = this->storage->get_key_size(db_uuid, key);
if (pair_size)
{
return this->storage->remove(db_uuid, key) == bzn::storage_result::ok ? *pair_size : 0;
}
return 0;
}


void
crud::random_cache_replacement(const database_msg& request, size_t key_value_size, size_t max_size, const bzn::key_t& key_exception)
bool
crud::do_eviction(const database_msg& request, size_t max_size)
{
const auto [keys, size]{this->storage->get_size(request.header().db_uuid())};

uint64_t storage_to_free = key_value_size - (max_size - size);

// We may need to remove one or more key/value pairs to make room for the new one
std::hash<std::string> hasher;
size_t random_seed = hasher(request.header().request_hash());

boost::random::mt19937 mt(random_seed);
const boost::random::uniform_int_distribution<> dist(0, keys - 1);

std::vector<bzn::key_t> keys_to_evict;

const auto available_keys = this->storage->get_keys(request.header().db_uuid());
while (storage_to_free)
const auto PERMS{this->get_database_permissions(request.header().db_uuid()).second};
if (auto eviction_policy = this->get_eviction_policy(PERMS))
{
const auto key_index = dist(mt);
const auto key_to_evict = this->storage->get_keys(request.header().db_uuid())[key_index];
if ((key_to_evict != key_exception) && (keys_to_evict.end() == std::find(keys_to_evict.begin(), keys_to_evict.end(), key_to_evict)))
auto keys_to_evict {eviction_policy->keys_to_evict(request, max_size)};
if (keys_to_evict.empty())
{
const size_t evicted_size = evict_key(request.header().db_uuid(), key_to_evict);
storage_to_free -= evicted_size < storage_to_free ? evicted_size : storage_to_free;
keys_to_evict.emplace_back(key_to_evict);
return false;
}

std::for_each( keys_to_evict.begin(), keys_to_evict.end(),
[&](const auto& key)
{
this->storage->remove(request.header().db_uuid(), key);
});

return true;
}

return false;
}


Expand Down
12 changes: 6 additions & 6 deletions crud/crud.hpp
Expand Up @@ -21,19 +21,20 @@
#include <node/node_base.hpp>
#include <options/options_base.hpp>
#include <pbft/pbft_base.hpp>
#include <policy/eviction_base.hpp>
#include <status/status_provider_base.hpp>
#include <storage/storage_base.hpp>
#include <shared_mutex>
#include <gtest/gtest_prod.h>


namespace bzn
{
class crud final : public bzn::crud_base, public bzn::status_provider_base, public std::enable_shared_from_this<crud>
{
public:
crud(std::shared_ptr<bzn::asio::io_context_base> io_context, std::shared_ptr<bzn::storage_base> storage, std::shared_ptr<bzn::subscription_manager_base> subscription_manager,
std::shared_ptr<bzn::node_base> node, bzn::key_t owner_public_key = "");
crud(std::shared_ptr<bzn::asio::io_context_base> io_context, std::shared_ptr<bzn::storage_base> storage
, std::shared_ptr<bzn::subscription_manager_base> subscription_manager
, std::shared_ptr<bzn::node_base> node, bzn::key_t owner_public_key = "");

void handle_request(const bzn::caller_id_t& caller_id, const database_msg& request, std::shared_ptr<bzn::session_base> session) override;

Expand Down Expand Up @@ -82,7 +83,6 @@ namespace bzn
bool is_caller_a_writer(const bzn::caller_id_t& caller_id, const Json::Value& perms) const;
void add_writers(const database_msg& request, Json::Value& perms);
void remove_writers(const database_msg& request, Json::Value& perms);
bool uses_random_eviction_policy(const Json::Value& perms) const;
uint64_t max_database_size(const Json::Value& perms) const;
bool operation_exceeds_available_space(const database_msg& request, const Json::Value& perms);

Expand All @@ -95,8 +95,8 @@ namespace bzn
std::optional<uint64_t> get_ttl(const bzn::uuid_t& uuid, const bzn::key_t& key) const;

// cache replacement policy
size_t evict_key(const bzn::uuid_t& db_uuid, const bzn::key_t& key);
void random_cache_replacement(const database_msg& request, size_t key_value_size, size_t max_size, const bzn::key_t& key_exception = "");
std::shared_ptr<policy::eviction_base> get_eviction_policy(const Json::Value& perms);
bool do_eviction(const database_msg& request, size_t max_size);

std::shared_ptr<bzn::storage_base> storage;
std::shared_ptr<bzn::subscription_manager_base> subscription_manager;
Expand Down
1 change: 0 additions & 1 deletion crud/crud_base.hpp
Expand Up @@ -18,7 +18,6 @@
#include <node/session_base.hpp>
#include <proto/database.pb.h>


namespace bzn
{
class pbft_base;
Expand Down

0 comments on commit cd7487a

Please sign in to comment.