diff options
Diffstat (limited to 'src/rocksdb/cache/cache_reservation_manager.cc')
-rw-r--r-- | src/rocksdb/cache/cache_reservation_manager.cc | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/src/rocksdb/cache/cache_reservation_manager.cc b/src/rocksdb/cache/cache_reservation_manager.cc new file mode 100644 index 000000000..53dee5d79 --- /dev/null +++ b/src/rocksdb/cache/cache_reservation_manager.cc @@ -0,0 +1,185 @@ +// Copyright (c) 2011-present, Facebook, Inc. All rights reserved. +// This source code is licensed under both the GPLv2 (found in the +// COPYING file in the root directory) and Apache 2.0 License +// (found in the LICENSE.Apache file in the root directory). +// +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +#include "cache/cache_reservation_manager.h" + +#include <cassert> +#include <cstddef> +#include <cstring> +#include <memory> + +#include "cache/cache_entry_roles.h" +#include "rocksdb/cache.h" +#include "rocksdb/slice.h" +#include "rocksdb/status.h" +#include "table/block_based/reader_common.h" +#include "util/coding.h" + +namespace ROCKSDB_NAMESPACE { + +template <CacheEntryRole R> +CacheReservationManagerImpl<R>::CacheReservationHandle::CacheReservationHandle( + std::size_t incremental_memory_used, + std::shared_ptr<CacheReservationManagerImpl> cache_res_mgr) + : incremental_memory_used_(incremental_memory_used) { + assert(cache_res_mgr); + cache_res_mgr_ = cache_res_mgr; +} + +template <CacheEntryRole R> +CacheReservationManagerImpl< + R>::CacheReservationHandle::~CacheReservationHandle() { + Status s = cache_res_mgr_->ReleaseCacheReservation(incremental_memory_used_); + s.PermitUncheckedError(); +} + +template <CacheEntryRole R> +CacheReservationManagerImpl<R>::CacheReservationManagerImpl( + std::shared_ptr<Cache> cache, bool delayed_decrease) + : delayed_decrease_(delayed_decrease), + cache_allocated_size_(0), + memory_used_(0) { + assert(cache != nullptr); + cache_ = cache; +} + +template <CacheEntryRole R> +CacheReservationManagerImpl<R>::~CacheReservationManagerImpl() { + for (auto* handle : dummy_handles_) { + cache_->Release(handle, true); + } +} + +template <CacheEntryRole R> +Status CacheReservationManagerImpl<R>::UpdateCacheReservation( + std::size_t new_mem_used) { + memory_used_ = new_mem_used; + std::size_t cur_cache_allocated_size = + cache_allocated_size_.load(std::memory_order_relaxed); + if (new_mem_used == cur_cache_allocated_size) { + return Status::OK(); + } else if (new_mem_used > cur_cache_allocated_size) { + Status s = IncreaseCacheReservation(new_mem_used); + return s; + } else { + // In delayed decrease mode, we don't decrease cache reservation + // untill the memory usage is less than 3/4 of what we reserve + // in the cache. + // We do this because + // (1) Dummy entry insertion is expensive in block cache + // (2) Delayed releasing previously inserted dummy entries can save such + // expensive dummy entry insertion on memory increase in the near future, + // which is likely to happen when the memory usage is greater than or equal + // to 3/4 of what we reserve + if (delayed_decrease_ && new_mem_used >= cur_cache_allocated_size / 4 * 3) { + return Status::OK(); + } else { + Status s = DecreaseCacheReservation(new_mem_used); + return s; + } + } +} + +template <CacheEntryRole R> +Status CacheReservationManagerImpl<R>::MakeCacheReservation( + std::size_t incremental_memory_used, + std::unique_ptr<CacheReservationManager::CacheReservationHandle>* handle) { + assert(handle); + Status s = + UpdateCacheReservation(GetTotalMemoryUsed() + incremental_memory_used); + (*handle).reset(new CacheReservationManagerImpl::CacheReservationHandle( + incremental_memory_used, + std::enable_shared_from_this< + CacheReservationManagerImpl<R>>::shared_from_this())); + return s; +} + +template <CacheEntryRole R> +Status CacheReservationManagerImpl<R>::ReleaseCacheReservation( + std::size_t incremental_memory_used) { + assert(GetTotalMemoryUsed() >= incremental_memory_used); + std::size_t updated_total_mem_used = + GetTotalMemoryUsed() - incremental_memory_used; + Status s = UpdateCacheReservation(updated_total_mem_used); + return s; +} + +template <CacheEntryRole R> +Status CacheReservationManagerImpl<R>::IncreaseCacheReservation( + std::size_t new_mem_used) { + Status return_status = Status::OK(); + while (new_mem_used > cache_allocated_size_.load(std::memory_order_relaxed)) { + Cache::Handle* handle = nullptr; + return_status = cache_->Insert(GetNextCacheKey(), nullptr, kSizeDummyEntry, + GetNoopDeleterForRole<R>(), &handle); + + if (return_status != Status::OK()) { + return return_status; + } + + dummy_handles_.push_back(handle); + cache_allocated_size_ += kSizeDummyEntry; + } + return return_status; +} + +template <CacheEntryRole R> +Status CacheReservationManagerImpl<R>::DecreaseCacheReservation( + std::size_t new_mem_used) { + Status return_status = Status::OK(); + + // Decrease to the smallest multiple of kSizeDummyEntry that is greater than + // or equal to new_mem_used We do addition instead of new_mem_used <= + // cache_allocated_size_.load(std::memory_order_relaxed) - kSizeDummyEntry to + // avoid underflow of size_t when cache_allocated_size_ = 0 + while (new_mem_used + kSizeDummyEntry <= + cache_allocated_size_.load(std::memory_order_relaxed)) { + assert(!dummy_handles_.empty()); + auto* handle = dummy_handles_.back(); + cache_->Release(handle, true); + dummy_handles_.pop_back(); + cache_allocated_size_ -= kSizeDummyEntry; + } + return return_status; +} + +template <CacheEntryRole R> +std::size_t CacheReservationManagerImpl<R>::GetTotalReservedCacheSize() { + return cache_allocated_size_.load(std::memory_order_relaxed); +} + +template <CacheEntryRole R> +std::size_t CacheReservationManagerImpl<R>::GetTotalMemoryUsed() { + return memory_used_; +} + +template <CacheEntryRole R> +Slice CacheReservationManagerImpl<R>::GetNextCacheKey() { + // Calling this function will have the side-effect of changing the + // underlying cache_key_ that is shared among other keys generated from this + // fucntion. Therefore please make sure the previous keys are saved/copied + // before calling this function. + cache_key_ = CacheKey::CreateUniqueForCacheLifetime(cache_.get()); + return cache_key_.AsSlice(); +} + +template <CacheEntryRole R> +Cache::DeleterFn CacheReservationManagerImpl<R>::TEST_GetNoopDeleterForRole() { + return GetNoopDeleterForRole<R>(); +} + +template class CacheReservationManagerImpl< + CacheEntryRole::kBlockBasedTableReader>; +template class CacheReservationManagerImpl< + CacheEntryRole::kCompressionDictionaryBuildingBuffer>; +template class CacheReservationManagerImpl<CacheEntryRole::kFilterConstruction>; +template class CacheReservationManagerImpl<CacheEntryRole::kMisc>; +template class CacheReservationManagerImpl<CacheEntryRole::kWriteBuffer>; +template class CacheReservationManagerImpl<CacheEntryRole::kFileMetadata>; +template class CacheReservationManagerImpl<CacheEntryRole::kBlobCache>; +} // namespace ROCKSDB_NAMESPACE |