Skip to content

Commit

Permalink
Add non-volatile cache to reduce the cost of storage (#1099)
Browse files Browse the repository at this point in the history
* runnable navycache

add nvm cache

lookup draft done

add some comments

rename file and add comments

finish core funcs

unit test done

adjust the dram size and now nvm cache test can pass

return nullptr when itemhandle is nullptr on Lookup()

changed the interface

multiple rocksdb share the same secondary instance on the same node

for debug

set the default secondary cache size to 0 to meta will not use it

change gflag type

add UUL

add bucket power and lock power to conf

move impl from CahcelibLRU.h to cpp and clean

* type and config

* remove comment

* cmake

* add override

* change the default cache file size to 0

* set cache size unsigned long

* rebase and add comments

* rebase

Co-authored-by: Sophie <[email protected]>
  • Loading branch information
wenhaocs and Sophie-Xie authored Aug 3, 2022
1 parent 592cd96 commit c20297f
Show file tree
Hide file tree
Showing 23 changed files with 830 additions and 116 deletions.
12 changes: 12 additions & 0 deletions conf/nebula-standalone.conf.default
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,15 @@
--meta_data_path=data/meta
--default_replica_factor=1
--default_parts_num=100

############## non-volatile cache ##############
# Cache file location
--nv_cache_path=/tmp/cache
# Cache file size in MB
--nv_cache_size=0
# DRAM part size of non-volatile cache in MB
--nv_dram_size=50
# DRAM part bucket power. The value is a logarithm with a base of 2. Optional values are 0-32.
--nv_bucket_power=20
# DRAM part lock power. The value is a logarithm with a base of 2. The recommended value is max(1, nv_bucket_power - 10).
--nv_lock_power=10
12 changes: 12 additions & 0 deletions conf/nebula-storaged.conf.default
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,15 @@
--rebuild_index_part_rate_limit=4194304
# The amount of data sent in each batch when leader synchronizes rebuilding index
--rebuild_index_batch_size=1048576

############## non-volatile cache ##############
# Cache file location
--nv_cache_path=/tmp/cache
# Cache file size in MB
--nv_cache_size=0
# DRAM part size of non-volatile cache in MB
--nv_dram_size=50
# DRAM part bucket power. The value is a logarithm with a base of 2. Optional values are 0-32.
--nv_bucket_power=20
# DRAM part lock power. The value is a logarithm with a base of 2. The recommended value is max(1, nv_bucket_power - 10).
--nv_lock_power=10
12 changes: 12 additions & 0 deletions conf/nebula-storaged.conf.production
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,18 @@
# TTL in seconds for empty key items in the cache
--empty_key_item_ttl=300

############## non-volatile cache ##############
# Cache file location
--nv_cache_path=/tmp/cache
# Cache file size in MB
--nv_cache_size=0
# DRAM part size of non-volatile cache in MB
--nv_dram_size=50
# DRAM part bucket power. The value is a logarithm with a base of 2. Optional values are 0-32.
--nv_bucket_power=20
# DRAM part lock power. The value is a logarithm with a base of 2. The recommended value is max(1, nv_bucket_power - 10).
--nv_lock_power=10

############### misc ####################
# Whether remove outdated space data
--auto_remove_invalid_space=true
Expand Down
5 changes: 5 additions & 0 deletions src/common/base/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,9 @@ nebula_add_library(
${gdb_debug_script}
)

nebula_add_library(
cache_obj OBJECT
CacheLibLRU.cpp
)

nebula_add_subdirectory(test)
137 changes: 137 additions & 0 deletions src/common/base/CacheLibLRU.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/* Copyright (c) 2022 vesoft inc. All rights reserved.
*
* This source code is licensed under Apache 2.0 License.
*/

#include "common/base/CacheLibLRU.h"

#include <folly/DynamicConverter.h>
#include <folly/dynamic.h>

namespace nebula {

nebula::cpp2::ErrorCode CacheLibLRU::initializeCache() {
VLOG(4) << "Using the following cache config"
<< folly::toPrettyJson(folly::toDynamic(allocConfig_.serialize()));
try {
allocConfig_.validate(); // will throw if bad config
nebulaCache_ = std::make_unique<Cache>(allocConfig_);
} catch (const std::exception& e) {
// We do not stop the service. Users should refer to the log to determine whether to restart
// the service.
LOG(ERROR) << "Cache configuration error: " << e.what();
return nebula::cpp2::ErrorCode::E_UNKNOWN;
}

return nebula::cpp2::ErrorCode::SUCCEEDED;
}

nebula::cpp2::ErrorCode CacheLibLRU::addPool(std::string poolName, uint32_t poolSize) {
if (poolIdMap_.find(poolName) != poolIdMap_.end()) {
LOG(ERROR) << "Cache pool creation error. Cache pool exists: " << poolName.data();
return nebula::cpp2::ErrorCode::E_EXISTED;
}
try {
auto poolId = nebulaCache_->addPool(poolName, poolSize * 1024UL * 1024UL);
poolIdMap_[poolName] = poolId;
} catch (const std::exception& e) {
LOG(ERROR) << "Adding cache pool error: " << e.what();
return nebula::cpp2::ErrorCode::E_NOT_ENOUGH_SPACE;
}
return nebula::cpp2::ErrorCode::SUCCEEDED;
}

nebula::cpp2::ErrorCode CacheLibLRU::get(const std::string& key, std::string* value) {
folly::SharedMutex::ReadHolder rHolder(lock_);
auto itemHandle = nebulaCache_->find(key);
if (itemHandle) {
value->assign(reinterpret_cast<const char*>(itemHandle->getMemory()), itemHandle->getSize());
return nebula::cpp2::ErrorCode::SUCCEEDED;
}
VLOG(4) << "Cache miss: " << key << " Not Found";
return nebula::cpp2::ErrorCode::E_CACHE_MISS;
}

Cache::ItemHandle CacheLibLRU::find(const std::string& key) {
return nebulaCache_->find(key);
}

nebula::cpp2::ErrorCode CacheLibLRU::put(const std::string& key,
const std::string& value,
std::string poolName,
uint32_t ttl) {
if (poolIdMap_.find(poolName) == poolIdMap_.end()) {
LOG(ERROR) << "Cache write error. Pool does not exist: " << poolName.data();
return nebula::cpp2::ErrorCode::E_POOL_NOT_FOUND;
}
auto itemHandle = nebulaCache_->allocate(poolIdMap_[poolName], key, value.size(), ttl);
if (!itemHandle) {
LOG(ERROR) << "Cache write error. Too many pending writes.";
return nebula::cpp2::ErrorCode::E_CACHE_WRITE_FAILURE;
}

{
folly::SharedMutex::WriteHolder wHolder(lock_);
std::memcpy(itemHandle->getMemory(), value.data(), value.size());
nebulaCache_->insertOrReplace(itemHandle);
}
return nebula::cpp2::ErrorCode::SUCCEEDED;
}

ErrorOr<nebula::cpp2::ErrorCode, Cache::ItemHandle> CacheLibLRU::allocateItem(
const std::string& key, size_t size, std::string poolName, uint32_t ttl) {
if (poolIdMap_.find(poolName) == poolIdMap_.end()) {
LOG(ERROR) << "Cache write error. Pool does not exist: " << poolName;
return nebula::cpp2::ErrorCode::E_POOL_NOT_FOUND;
}
auto itemHandle = nebulaCache_->allocate(poolIdMap_[poolName], key, size, ttl);
if (!itemHandle) {
LOG(ERROR) << "Cache write error. Too many pending writes.";
return nebula::cpp2::ErrorCode::E_CACHE_WRITE_FAILURE;
}
return itemHandle;
}

Cache::ItemHandle CacheLibLRU::insertOrReplace(Cache::ItemHandle& handle) {
return nebulaCache_->insertOrReplace(handle);
}

void CacheLibLRU::erase(const std::string& key) {
nebulaCache_->remove(key);
}

nebula::cpp2::ErrorCode CacheLibLRU::invalidateItem(const std::string& key) {
folly::SharedMutex::WriteHolder wHolder(lock_);
VLOG(4) << "Invalidate vertex key: " << folly::hexlify(key);
nebulaCache_->remove(key);
return nebula::cpp2::ErrorCode::SUCCEEDED;
}

nebula::cpp2::ErrorCode CacheLibLRU::invalidateItems(const std::vector<std::string>& keys) {
folly::SharedMutex::WriteHolder wHolder(lock_);
for (auto key : keys) {
VLOG(4) << "Invalidate vertex key: " << folly::hexlify(key);
nebulaCache_->remove(key);
}
return nebula::cpp2::ErrorCode::SUCCEEDED;
}

ErrorOr<nebula::cpp2::ErrorCode, uint64_t> CacheLibLRU::getConfiguredPoolSize(
const std::string& poolName) {
if (poolIdMap_.find(poolName) == poolIdMap_.end()) {
LOG(ERROR) << "Get cache pool size error. Pool does not exist: " << poolName.data();
return nebula::cpp2::ErrorCode::E_POOL_NOT_FOUND;
}
return nebulaCache_->getPoolStats(poolIdMap_[poolName]).poolSize;
}

ErrorOr<nebula::cpp2::ErrorCode, uint64_t> CacheLibLRU::getPoolCacheHitCount(
const std::string& poolName) {
if (poolIdMap_.find(poolName) == poolIdMap_.end()) {
LOG(ERROR) << "Get cache hit count error. Pool does not exist: " << poolName.data();
return nebula::cpp2::ErrorCode::E_POOL_NOT_FOUND;
}
return nebulaCache_->getPoolStats(poolIdMap_[poolName]).numPoolGetHits;
}

} // namespace nebula
Loading

0 comments on commit c20297f

Please sign in to comment.