1 //  Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
2 //  This source code is licensed under both the GPLv2 (found in the
3 //  COPYING file in the root directory) and Apache 2.0 License
4 //  (found in the LICENSE.Apache file in the root directory).
5 
6 #pragma once
7 
8 #include <array>
9 #include <cstdint>
10 #include <unordered_map>
11 
12 #include "rocksdb/cache.h"
13 
14 namespace ROCKSDB_NAMESPACE {
15 
16 // Classifications of block cache entries, for reporting statistics
17 // Adding new enum to this class requires corresponding updates to
18 // kCacheEntryRoleToCamelString and kCacheEntryRoleToHyphenString
19 enum class CacheEntryRole {
20   // Block-based table data block
21   kDataBlock,
22   // Block-based table filter block (full or partitioned)
23   kFilterBlock,
24   // Block-based table metadata block for partitioned filter
25   kFilterMetaBlock,
26   // Block-based table deprecated filter block (old "block-based" filter)
27   kDeprecatedFilterBlock,
28   // Block-based table index block
29   kIndexBlock,
30   // Other kinds of block-based table block
31   kOtherBlock,
32   // WriteBufferManager reservations to account for memtable usage
33   kWriteBuffer,
34   // BlockBasedTableBuilder reservations to account for
35   // compression dictionary building buffer's memory usage
36   kCompressionDictionaryBuildingBuffer,
37   // Default bucket, for miscellaneous cache entries. Do not use for
38   // entries that could potentially add up to large usage.
39   kMisc,
40 };
41 constexpr uint32_t kNumCacheEntryRoles =
42     static_cast<uint32_t>(CacheEntryRole::kMisc) + 1;
43 
44 extern std::array<const char*, kNumCacheEntryRoles>
45     kCacheEntryRoleToCamelString;
46 extern std::array<const char*, kNumCacheEntryRoles>
47     kCacheEntryRoleToHyphenString;
48 
49 // To associate cache entries with their role, we use a hack on the
50 // existing Cache interface. Because the deleter of an entry can authenticate
51 // the code origin of an entry, we can elaborate the choice of deleter to
52 // also encode role information, without inferring false role information
53 // from entries not choosing to encode a role.
54 //
55 // The rest of this file is for handling mappings between deleters and
56 // roles.
57 
58 // To infer a role from a deleter, the deleter must be registered. This
59 // can be done "manually" with this function. This function is thread-safe,
60 // and the registration mappings go into private but static storage. (Note
61 // that DeleterFn is a function pointer, not std::function. Registrations
62 // should not be too many.)
63 void RegisterCacheDeleterRole(Cache::DeleterFn fn, CacheEntryRole role);
64 
65 // Gets a copy of the registered deleter -> role mappings. This is the only
66 // function for reading the mappings made with RegisterCacheDeleterRole.
67 // Why only this interface for reading?
68 // * This function has to be thread safe, which could incur substantial
69 // overhead. We should not pay this overhead for every deleter look-up.
70 // * This is suitable for preparing for batch operations, like with
71 // CacheEntryStatsCollector.
72 // * The number of mappings should be sufficiently small (dozens).
73 std::unordered_map<Cache::DeleterFn, CacheEntryRole> CopyCacheDeleterRoleMap();
74 
75 // ************************************************************** //
76 // An automatic registration infrastructure. This enables code
77 // to simply ask for a deleter associated with a particular type
78 // and role, and registration is automatic. In a sense, this is
79 // a small dependency injection infrastructure, because linking
80 // in new deleter instantiations is essentially sufficient for
81 // making stats collection (using CopyCacheDeleterRoleMap) aware
82 // of them.
83 
84 namespace cache_entry_roles_detail {
85 
86 template <typename T, CacheEntryRole R>
87 struct RegisteredDeleter {
RegisteredDeleterRegisteredDeleter88   RegisteredDeleter() { RegisterCacheDeleterRole(Delete, R); }
89 
90   // These have global linkage to help ensure compiler optimizations do not
91   // break uniqueness for each <T,R>
DeleteRegisteredDeleter92   static void Delete(const Slice& /* key */, void* value) {
93     delete static_cast<T*>(value);
94   }
95 };
96 
97 template <CacheEntryRole R>
98 struct RegisteredNoopDeleter {
RegisteredNoopDeleterRegisteredNoopDeleter99   RegisteredNoopDeleter() { RegisterCacheDeleterRole(Delete, R); }
100 
DeleteRegisteredNoopDeleter101   static void Delete(const Slice& /* key */, void* value) {
102     (void)value;
103     assert(value == nullptr);
104   }
105 };
106 
107 }  // namespace cache_entry_roles_detail
108 
109 // Get an automatically registered deleter for value type T and role R.
110 // Based on C++ semantics, registration is invoked exactly once in a
111 // thread-safe way on first call to this function, for each <T, R>.
112 template <typename T, CacheEntryRole R>
GetCacheEntryDeleterForRole()113 Cache::DeleterFn GetCacheEntryDeleterForRole() {
114   static cache_entry_roles_detail::RegisteredDeleter<T, R> reg;
115   return reg.Delete;
116 }
117 
118 // Get an automatically registered no-op deleter (value should be nullptr)
119 // and associated with role R. This is used for Cache "reservation" entries
120 // such as for WriteBufferManager.
121 template <CacheEntryRole R>
GetNoopDeleterForRole()122 Cache::DeleterFn GetNoopDeleterForRole() {
123   static cache_entry_roles_detail::RegisteredNoopDeleter<R> reg;
124   return reg.Delete;
125 }
126 
127 }  // namespace ROCKSDB_NAMESPACE
128