Re-arrange cache.h to prepare for refactoring (#10942)

Summary:
No material changes to code or comments, just re-arranging things to prepare for a big refactoring, making it easier to what changed. Some specifics:
* This groups things together in Cache in anticipation of secondary cache features being marked production-ready (vs. experimental).
* CacheEntryRole will be needed in definition of class Cache, so that has been moved above it.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/10942

Test Plan: existing tests

Reviewed By: anand1976

Differential Revision: D41205509

Pulled By: pdillinger

fbshipit-source-id: 3f2559ab1651c758918dc97056951fa2b5eb0348
main
Peter Dillinger 2 years ago committed by Facebook GitHub Bot
parent b644baa1eb
commit b55e70357c
  1. 339
      include/rocksdb/cache.h

@ -39,6 +39,66 @@ class Cache;
struct ConfigOptions; struct ConfigOptions;
class SecondaryCache; class SecondaryCache;
// Classifications of block cache entries.
//
// Developer notes: Adding a new enum to this class requires corresponding
// updates to `kCacheEntryRoleToCamelString` and
// `kCacheEntryRoleToHyphenString`. Do not add to this enum after `kMisc` since
// `kNumCacheEntryRoles` assumes `kMisc` comes last.
enum class CacheEntryRole {
// Block-based table data block
kDataBlock,
// Block-based table filter block (full or partitioned)
kFilterBlock,
// Block-based table metadata block for partitioned filter
kFilterMetaBlock,
// OBSOLETE / DEPRECATED: old/removed block-based filter
kDeprecatedFilterBlock,
// Block-based table index block
kIndexBlock,
// Other kinds of block-based table block
kOtherBlock,
// WriteBufferManager's charge to account for its memtable usage
kWriteBuffer,
// Compression dictionary building buffer's charge to account for
// its memory usage
kCompressionDictionaryBuildingBuffer,
// Filter's charge to account for
// (new) bloom and ribbon filter construction's memory usage
kFilterConstruction,
// BlockBasedTableReader's charge to account for its memory usage
kBlockBasedTableReader,
// FileMetadata's charge to account for its memory usage
kFileMetadata,
// Blob value (when using the same cache as block cache and blob cache)
kBlobValue,
// Blob cache's charge to account for its memory usage (when using a
// separate block cache and blob cache)
kBlobCache,
// Default bucket, for miscellaneous cache entries. Do not use for
// entries that could potentially add up to large usage.
kMisc,
};
constexpr uint32_t kNumCacheEntryRoles =
static_cast<uint32_t>(CacheEntryRole::kMisc) + 1;
// Obtain a hyphen-separated, lowercase name of a `CacheEntryRole`.
const std::string& GetCacheEntryRoleName(CacheEntryRole);
// For use with `GetMapProperty()` for property
// `DB::Properties::kBlockCacheEntryStats`. On success, the map will
// be populated with all keys that can be obtained from these functions.
struct BlockCacheEntryStatsMapKeys {
static const std::string& CacheId();
static const std::string& CacheCapacityBytes();
static const std::string& LastCollectionDurationSeconds();
static const std::string& LastCollectionAgeSeconds();
static std::string EntryCount(CacheEntryRole);
static std::string UsedBytes(CacheEntryRole);
static std::string UsedPercent(CacheEntryRole);
};
extern const bool kDefaultToAdaptiveMutex; extern const bool kDefaultToAdaptiveMutex;
enum CacheMetadataChargePolicy { enum CacheMetadataChargePolicy {
@ -293,7 +353,11 @@ extern std::shared_ptr<Cache> NewClockCache(
kDefaultCacheMetadataChargePolicy); kDefaultCacheMetadataChargePolicy);
class Cache { class Cache {
public: public: // opaque types
// Opaque handle to an entry stored in the cache.
struct Handle {};
public: // type defs
// Depending on implementation, cache entries with higher priority levels // Depending on implementation, cache entries with higher priority levels
// could be less likely to get evicted than entries with lower priority // could be less likely to get evicted than entries with lower priority
// levels. The "high" priority level applies to certain SST metablocks (e.g. // levels. The "high" priority level applies to certain SST metablocks (e.g.
@ -367,12 +431,16 @@ class Cache {
using CreateCallback = std::function<Status(const void* buf, size_t size, using CreateCallback = std::function<Status(const void* buf, size_t size,
void** out_obj, size_t* charge)>; void** out_obj, size_t* charge)>;
public: // ctor/dtor/create
Cache(std::shared_ptr<MemoryAllocator> allocator = nullptr) Cache(std::shared_ptr<MemoryAllocator> allocator = nullptr)
: memory_allocator_(std::move(allocator)) {} : memory_allocator_(std::move(allocator)) {}
// No copying allowed // No copying allowed
Cache(const Cache&) = delete; Cache(const Cache&) = delete;
Cache& operator=(const Cache&) = delete; Cache& operator=(const Cache&) = delete;
// Destroys all remaining entries by calling the associated "deleter"
virtual ~Cache() {}
// Creates a new Cache based on the input value string and returns the result. // Creates a new Cache based on the input value string and returns the result.
// Currently, this method can be used to create LRUCaches only // Currently, this method can be used to create LRUCaches only
// @param config_options // @param config_options
@ -388,18 +456,23 @@ class Cache {
const std::string& value, const std::string& value,
std::shared_ptr<Cache>* result); std::shared_ptr<Cache>* result);
// Destroys all existing entries by calling the "deleter" public: // functions
// function that was passed via the Insert() function.
//
// @See Insert
virtual ~Cache() {}
// Opaque handle to an entry stored in the cache.
struct Handle {};
// The type of the Cache // The type of the Cache
virtual const char* Name() const = 0; virtual const char* Name() const = 0;
// EXPERIMENTAL SecondaryCache support:
// Some APIs here are experimental and might change in the future.
// The Insert and Lookup APIs below are intended to allow cached objects
// to be demoted/promoted between the primary block cache and a secondary
// cache. The secondary cache could be a non-volatile cache, and will
// likely store the object in a different representation. They rely on a
// per object CacheItemHelper to do the conversions.
// The secondary cache may persist across process and system restarts,
// and may even be moved between hosts. Therefore, the cache key must
// be repeatable across restarts/reboots, and globally unique if
// multiple DBs share the same cache and the set of DBs can change
// over time.
// Insert a mapping from key->value into the volatile cache only // Insert a mapping from key->value into the volatile cache only
// and assign it with the specified charge against the total cache capacity. // and assign it with the specified charge against the total cache capacity.
// If strict_capacity_limit is true and cache reaches its full capacity, // If strict_capacity_limit is true and cache reaches its full capacity,
@ -421,6 +494,45 @@ class Cache {
DeleterFn deleter, Handle** handle = nullptr, DeleterFn deleter, Handle** handle = nullptr,
Priority priority = Priority::LOW) = 0; Priority priority = Priority::LOW) = 0;
// EXPERIMENTAL
// Insert a mapping from key->value into the cache and assign it
// the specified charge against the total cache capacity. If
// strict_capacity_limit is true and cache reaches its full capacity,
// return Status::MemoryLimit. `value` must be non-nullptr for this
// Insert() because Value() == nullptr is reserved for indicating failure
// with secondary-cache-compatible mappings.
//
// The helper argument is saved by the cache and will be used when the
// inserted object is evicted or promoted to the secondary cache. It,
// therefore, must outlive the cache.
//
// If handle is not nullptr, returns a handle that corresponds to the
// mapping. The caller must call this->Release(handle) when the returned
// mapping is no longer needed. In case of error caller is responsible to
// cleanup the value (i.e. calling "deleter").
//
// If handle is nullptr, it is as if Release is called immediately after
// insert. In case of error value will be cleanup.
//
// Regardless of whether the item was inserted into the cache,
// it will attempt to insert it into the secondary cache if one is
// configured, and the helper supports it.
// The cache implementation must support a secondary cache, otherwise
// the item is only inserted into the primary cache. It may
// defer the insertion to the secondary cache as it sees fit.
//
// When the inserted entry is no longer needed, the key and
// value will be passed to "deleter".
virtual Status Insert(const Slice& key, void* value,
const CacheItemHelper* helper, size_t charge,
Handle** handle = nullptr,
Priority priority = Priority::LOW) {
if (!helper) {
return Status::InvalidArgument();
}
return Insert(key, value, charge, helper->del_cb, handle, priority);
}
// If the cache has no mapping for "key", returns nullptr. // If the cache has no mapping for "key", returns nullptr.
// //
// Else return a handle that corresponds to the mapping. The caller // Else return a handle that corresponds to the mapping. The caller
@ -430,6 +542,54 @@ class Cache {
// function. // function.
virtual Handle* Lookup(const Slice& key, Statistics* stats = nullptr) = 0; virtual Handle* Lookup(const Slice& key, Statistics* stats = nullptr) = 0;
// EXPERIMENTAL
// Lookup the key in the primary and secondary caches (if one is configured).
// The create_cb callback function object will be used to contruct the
// cached object.
// If none of the caches have the mapping for the key, returns nullptr.
// Else, returns a handle that corresponds to the mapping.
//
// This call may promote the object from the secondary cache (if one is
// configured, and has the given key) to the primary cache.
//
// The helper argument should be provided if the caller wants the lookup
// to include the secondary cache (if one is configured) and the object,
// if it exists, to be promoted to the primary cache. The helper may be
// saved and used later when the object is evicted. Therefore, it must
// outlive the cache.
//
// ======================== Async Lookup (wait=false) ======================
// When wait=false, the handle returned might be in any of three states:
// * Present - If Value() != nullptr, then the result is present and
// the handle can be used just as if wait=true.
// * Pending, not ready (IsReady() == false) - secondary cache is still
// working to retrieve the value. Might become ready any time.
// * Pending, ready (IsReady() == true) - secondary cache has the value
// but it has not been loaded into primary cache. Call to Wait()/WaitAll()
// will not block.
//
// IMPORTANT: Pending handles are not thread-safe, and only these functions
// are allowed on them: Value(), IsReady(), Wait(), WaitAll(). Even Release()
// can only come after Wait() or WaitAll() even though a reference is held.
//
// Only Wait()/WaitAll() gets a Handle out of a Pending state. (Waiting is
// safe and has no effect on other handle states.) After waiting on a Handle,
// it is in one of two states:
// * Present - if Value() != nullptr
// * Failed - if Value() == nullptr, such as if the secondary cache
// initially thought it had the value but actually did not.
//
// Note that given an arbitrary Handle, the only way to distinguish the
// Pending+ready state from the Failed state is to Wait() on it. A cache
// entry not compatible with secondary cache can also have Value()==nullptr
// like the Failed state, but this is not generally a concern.
virtual Handle* Lookup(const Slice& key, const CacheItemHelper* /*helper_cb*/,
const CreateCallback& /*create_cb*/,
Priority /*priority*/, bool /*wait*/,
Statistics* stats = nullptr) {
return Lookup(key, stats);
}
// Increments the reference count for the handle if it refers to an entry in // Increments the reference count for the handle if it refers to an entry in
// the cache. Returns true if refcount was incremented; otherwise, returns // the cache. Returns true if refcount was incremented; otherwise, returns
// false. // false.
@ -556,103 +716,6 @@ class Cache {
MemoryAllocator* memory_allocator() const { return memory_allocator_.get(); } MemoryAllocator* memory_allocator() const { return memory_allocator_.get(); }
// EXPERIMENTAL // EXPERIMENTAL
// The following APIs are experimental and might change in the future.
// The Insert and Lookup APIs below are intended to allow cached objects
// to be demoted/promoted between the primary block cache and a secondary
// cache. The secondary cache could be a non-volatile cache, and will
// likely store the object in a different representation. They rely on a
// per object CacheItemHelper to do the conversions.
// The secondary cache may persist across process and system restarts,
// and may even be moved between hosts. Therefore, the cache key must
// be repeatable across restarts/reboots, and globally unique if
// multiple DBs share the same cache and the set of DBs can change
// over time.
// Insert a mapping from key->value into the cache and assign it
// the specified charge against the total cache capacity. If
// strict_capacity_limit is true and cache reaches its full capacity,
// return Status::MemoryLimit. `value` must be non-nullptr for this
// Insert() because Value() == nullptr is reserved for indicating failure
// with secondary-cache-compatible mappings.
//
// The helper argument is saved by the cache and will be used when the
// inserted object is evicted or promoted to the secondary cache. It,
// therefore, must outlive the cache.
//
// If handle is not nullptr, returns a handle that corresponds to the
// mapping. The caller must call this->Release(handle) when the returned
// mapping is no longer needed. In case of error caller is responsible to
// cleanup the value (i.e. calling "deleter").
//
// If handle is nullptr, it is as if Release is called immediately after
// insert. In case of error value will be cleanup.
//
// Regardless of whether the item was inserted into the cache,
// it will attempt to insert it into the secondary cache if one is
// configured, and the helper supports it.
// The cache implementation must support a secondary cache, otherwise
// the item is only inserted into the primary cache. It may
// defer the insertion to the secondary cache as it sees fit.
//
// When the inserted entry is no longer needed, the key and
// value will be passed to "deleter".
virtual Status Insert(const Slice& key, void* value,
const CacheItemHelper* helper, size_t charge,
Handle** handle = nullptr,
Priority priority = Priority::LOW) {
if (!helper) {
return Status::InvalidArgument();
}
return Insert(key, value, charge, helper->del_cb, handle, priority);
}
// Lookup the key in the primary and secondary caches (if one is configured).
// The create_cb callback function object will be used to contruct the
// cached object.
// If none of the caches have the mapping for the key, returns nullptr.
// Else, returns a handle that corresponds to the mapping.
//
// This call may promote the object from the secondary cache (if one is
// configured, and has the given key) to the primary cache.
//
// The helper argument should be provided if the caller wants the lookup
// to include the secondary cache (if one is configured) and the object,
// if it exists, to be promoted to the primary cache. The helper may be
// saved and used later when the object is evicted. Therefore, it must
// outlive the cache.
//
// ======================== Async Lookup (wait=false) ======================
// When wait=false, the handle returned might be in any of three states:
// * Present - If Value() != nullptr, then the result is present and
// the handle can be used just as if wait=true.
// * Pending, not ready (IsReady() == false) - secondary cache is still
// working to retrieve the value. Might become ready any time.
// * Pending, ready (IsReady() == true) - secondary cache has the value
// but it has not been loaded into primary cache. Call to Wait()/WaitAll()
// will not block.
//
// IMPORTANT: Pending handles are not thread-safe, and only these functions
// are allowed on them: Value(), IsReady(), Wait(), WaitAll(). Even Release()
// can only come after Wait() or WaitAll() even though a reference is held.
//
// Only Wait()/WaitAll() gets a Handle out of a Pending state. (Waiting is
// safe and has no effect on other handle states.) After waiting on a Handle,
// it is in one of two states:
// * Present - if Value() != nullptr
// * Failed - if Value() == nullptr, such as if the secondary cache
// initially thought it had the value but actually did not.
//
// Note that given an arbitrary Handle, the only way to distinguish the
// Pending+ready state from the Failed state is to Wait() on it. A cache
// entry not compatible with secondary cache can also have Value()==nullptr
// like the Failed state, but this is not generally a concern.
virtual Handle* Lookup(const Slice& key, const CacheItemHelper* /*helper_cb*/,
const CreateCallback& /*create_cb*/,
Priority /*priority*/, bool /*wait*/,
Statistics* stats = nullptr) {
return Lookup(key, stats);
}
// Release a mapping returned by a previous Lookup(). The "useful" // Release a mapping returned by a previous Lookup(). The "useful"
// parameter specifies whether the data was actually used or not, // parameter specifies whether the data was actually used or not,
// which may be used by the cache implementation to decide whether // which may be used by the cache implementation to decide whether
@ -663,12 +726,14 @@ class Cache {
return Release(handle, erase_if_last_ref); return Release(handle, erase_if_last_ref);
} }
// EXPERIMENTAL
// Determines if the handle returned by Lookup() can give a value without // Determines if the handle returned by Lookup() can give a value without
// blocking, though Wait()/WaitAll() might be required to publish it to // blocking, though Wait()/WaitAll() might be required to publish it to
// Value(). See secondary cache compatible Lookup() above for details. // Value(). See secondary cache compatible Lookup() above for details.
// This call is not thread safe on "pending" handles. // This call is not thread safe on "pending" handles.
virtual bool IsReady(Handle* /*handle*/) { return true; } virtual bool IsReady(Handle* /*handle*/) { return true; }
// EXPERIMENTAL
// Convert a "pending" handle into a full thread-shareable handle by // Convert a "pending" handle into a full thread-shareable handle by
// * If necessary, wait until secondary cache finishes loading the value. // * If necessary, wait until secondary cache finishes loading the value.
// * Construct the value for primary cache and set it in the handle. // * Construct the value for primary cache and set it in the handle.
@ -678,6 +743,7 @@ class Cache {
// See secondary cache compatible Lookup() above for details. // See secondary cache compatible Lookup() above for details.
virtual void Wait(Handle* /*handle*/) {} virtual void Wait(Handle* /*handle*/) {}
// EXPERIMENTAL
// Wait for a vector of handles to become ready. As with Wait(), the user // Wait for a vector of handles to become ready. As with Wait(), the user
// should check the Value() of each handle for nullptr. This call is not // should check the Value() of each handle for nullptr. This call is not
// thread-safe on pending handles. // thread-safe on pending handles.
@ -687,64 +753,5 @@ class Cache {
std::shared_ptr<MemoryAllocator> memory_allocator_; std::shared_ptr<MemoryAllocator> memory_allocator_;
}; };
// Classifications of block cache entries.
//
// Developer notes: Adding a new enum to this class requires corresponding
// updates to `kCacheEntryRoleToCamelString` and
// `kCacheEntryRoleToHyphenString`. Do not add to this enum after `kMisc` since
// `kNumCacheEntryRoles` assumes `kMisc` comes last.
enum class CacheEntryRole {
// Block-based table data block
kDataBlock,
// Block-based table filter block (full or partitioned)
kFilterBlock,
// Block-based table metadata block for partitioned filter
kFilterMetaBlock,
// OBSOLETE / DEPRECATED: old/removed block-based filter
kDeprecatedFilterBlock,
// Block-based table index block
kIndexBlock,
// Other kinds of block-based table block
kOtherBlock,
// WriteBufferManager's charge to account for its memtable usage
kWriteBuffer,
// Compression dictionary building buffer's charge to account for
// its memory usage
kCompressionDictionaryBuildingBuffer,
// Filter's charge to account for
// (new) bloom and ribbon filter construction's memory usage
kFilterConstruction,
// BlockBasedTableReader's charge to account for its memory usage
kBlockBasedTableReader,
// FileMetadata's charge to account for its memory usage
kFileMetadata,
// Blob value (when using the same cache as block cache and blob cache)
kBlobValue,
// Blob cache's charge to account for its memory usage (when using a
// separate block cache and blob cache)
kBlobCache,
// Default bucket, for miscellaneous cache entries. Do not use for
// entries that could potentially add up to large usage.
kMisc,
};
constexpr uint32_t kNumCacheEntryRoles =
static_cast<uint32_t>(CacheEntryRole::kMisc) + 1;
// Obtain a hyphen-separated, lowercase name of a `CacheEntryRole`.
const std::string& GetCacheEntryRoleName(CacheEntryRole);
// For use with `GetMapProperty()` for property
// `DB::Properties::kBlockCacheEntryStats`. On success, the map will
// be populated with all keys that can be obtained from these functions.
struct BlockCacheEntryStatsMapKeys {
static const std::string& CacheId();
static const std::string& CacheCapacityBytes();
static const std::string& LastCollectionDurationSeconds();
static const std::string& LastCollectionAgeSeconds();
static std::string EntryCount(CacheEntryRole);
static std::string UsedBytes(CacheEntryRole);
static std::string UsedPercent(CacheEntryRole);
};
} // namespace ROCKSDB_NAMESPACE } // namespace ROCKSDB_NAMESPACE

Loading…
Cancel
Save