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