// Copyright (c) Meta Platforms, Inc. and affiliates. // 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). #pragma once #include "rocksdb/cache.h" #include "rocksdb/secondary_cache.h" namespace ROCKSDB_NAMESPACE { // A SecondaryCache that implements stacking of a compressed secondary cache // and a non-volatile (local flash) cache. It implements an admission // policy of warming the bottommost tier (local flash) with compressed // blocks from the SST on misses, and on hits in the bottommost tier, // promoting to the compressed and/or primary block cache. The admission // policies of the primary block cache and compressed secondary cache remain // unchanged - promote on second access. There is no demotion ofablocks // evicted from a tier. They are just discarded. // // In order to properly handle compressed blocks directly read from SSTs, and // to allow writeback of blocks compressed by the compressed secondary // cache in the future, we make use of the compression type and source // cache tier arguments in InsertSaved. class TieredSecondaryCache : public SecondaryCacheWrapper { public: TieredSecondaryCache(std::shared_ptr comp_sec_cache, std::shared_ptr nvm_sec_cache, TieredAdmissionPolicy adm_policy) : SecondaryCacheWrapper(comp_sec_cache), nvm_sec_cache_(nvm_sec_cache) { #ifndef NDEBUG assert(adm_policy == TieredAdmissionPolicy::kAdmPolicyThreeQueue); #else (void)adm_policy; #endif } ~TieredSecondaryCache() override {} const char* Name() const override { return "TieredSecondaryCache"; } // This is a no-op as we currently don't allow demotion (i.e // insertion by the upper layer) of evicted blocks. Status Insert(const Slice& /*key*/, Cache::ObjectPtr /*obj*/, const Cache::CacheItemHelper* /*helper*/, bool /*force_insert*/) override { return Status::OK(); } // Warm up the nvm tier directly Status InsertSaved(const Slice& key, const Slice& saved, CompressionType type = CompressionType::kNoCompression, CacheTier source = CacheTier::kVolatileTier) override { return nvm_sec_cache_->InsertSaved(key, saved, type, source); } std::unique_ptr Lookup( const Slice& key, const Cache::CacheItemHelper* helper, Cache::CreateContext* create_context, bool wait, bool advise_erase, Statistics* stats, bool& kept_in_sec_cache) override; void WaitAll(std::vector handles) override; private: struct CreateContext : public Cache::CreateContext { const Slice* key; bool advise_erase; const Cache::CacheItemHelper* helper; Cache::CreateContext* inner_ctx; std::shared_ptr inner_handle; SecondaryCache* comp_sec_cache; Statistics* stats; }; class ResultHandle : public SecondaryCacheResultHandle { public: ~ResultHandle() override {} bool IsReady() override { if (inner_handle_ && inner_handle_->IsReady()) { Complete(); } return ready_; } void Wait() override { inner_handle_->Wait(); Complete(); } size_t Size() override { return size_; } Cache::ObjectPtr Value() override { return value_; } void Complete() { size_ = inner_handle_->Size(); value_ = inner_handle_->Value(); inner_handle_.reset(); ready_ = true; } void SetInnerHandle(std::unique_ptr&& handle) { inner_handle_ = std::move(handle); } void SetSize(size_t size) { size_ = size; } void SetValue(Cache::ObjectPtr val) { value_ = val; } CreateContext* ctx() { return &ctx_; } SecondaryCacheResultHandle* inner_handle() { return inner_handle_.get(); } private: std::unique_ptr inner_handle_; CreateContext ctx_; size_t size_; Cache::ObjectPtr value_; bool ready_ = false; }; static void NoopDelete(Cache::ObjectPtr /*obj*/, MemoryAllocator* /*allocator*/) { assert(false); } static size_t ZeroSize(Cache::ObjectPtr /*obj*/) { assert(false); return 0; } static Status NoopSaveTo(Cache::ObjectPtr /*from_obj*/, size_t /*from_offset*/, size_t /*length*/, char* /*out_buf*/) { assert(false); return Status::OK(); } static Status MaybeInsertAndCreate(const Slice& data, CompressionType type, CacheTier source, Cache::CreateContext* ctx, MemoryAllocator* allocator, Cache::ObjectPtr* out_obj, size_t* out_charge); static const Cache::CacheItemHelper* GetHelper() { const static Cache::CacheItemHelper basic_helper(CacheEntryRole::kMisc, &NoopDelete); const static Cache::CacheItemHelper maybe_insert_and_create_helper{ CacheEntryRole::kMisc, &NoopDelete, &ZeroSize, &NoopSaveTo, &MaybeInsertAndCreate, &basic_helper, }; return &maybe_insert_and_create_helper; } std::shared_ptr comp_sec_cache_; std::shared_ptr nvm_sec_cache_; }; } // namespace ROCKSDB_NAMESPACE