/src/rocksdb/cache/compressed_secondary_cache.h
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) 2011-present, Facebook, Inc. 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 <cstddef> |
10 | | #include <memory> |
11 | | |
12 | | #include "cache/cache_reservation_manager.h" |
13 | | #include "cache/lru_cache.h" |
14 | | #include "memory/memory_allocator_impl.h" |
15 | | #include "rocksdb/secondary_cache.h" |
16 | | #include "rocksdb/slice.h" |
17 | | #include "rocksdb/status.h" |
18 | | #include "util/compression.h" |
19 | | #include "util/mutexlock.h" |
20 | | |
21 | | namespace ROCKSDB_NAMESPACE { |
22 | | |
23 | | class CompressedSecondaryCacheResultHandle : public SecondaryCacheResultHandle { |
24 | | public: |
25 | | CompressedSecondaryCacheResultHandle(Cache::ObjectPtr value, size_t size) |
26 | 0 | : value_(value), size_(size) {} |
27 | | ~CompressedSecondaryCacheResultHandle() override = default; |
28 | | |
29 | | CompressedSecondaryCacheResultHandle( |
30 | | const CompressedSecondaryCacheResultHandle&) = delete; |
31 | | CompressedSecondaryCacheResultHandle& operator=( |
32 | | const CompressedSecondaryCacheResultHandle&) = delete; |
33 | | |
34 | 0 | bool IsReady() override { return true; } |
35 | | |
36 | 0 | void Wait() override {} |
37 | | |
38 | 0 | Cache::ObjectPtr Value() override { return value_; } |
39 | | |
40 | 0 | size_t Size() override { return size_; } |
41 | | |
42 | | private: |
43 | | Cache::ObjectPtr value_; |
44 | | size_t size_; |
45 | | }; |
46 | | |
47 | | // The CompressedSecondaryCache is a concrete implementation of |
48 | | // rocksdb::SecondaryCache. |
49 | | // |
50 | | // When a block is found from CompressedSecondaryCache::Lookup, we check whether |
51 | | // there is a dummy block with the same key in the primary cache. |
52 | | // 1. If the dummy block exits, we erase the block from |
53 | | // CompressedSecondaryCache and insert it into the primary cache. |
54 | | // 2. If not, we just insert a dummy block into the primary cache |
55 | | // (charging the actual size of the block) and don not erase the block from |
56 | | // CompressedSecondaryCache. A standalone handle is returned to the caller. |
57 | | // |
58 | | // When a block is evicted from the primary cache, we check whether |
59 | | // there is a dummy block with the same key in CompressedSecondaryCache. |
60 | | // 1. If the dummy block exits, the block is inserted into |
61 | | // CompressedSecondaryCache. |
62 | | // 2. If not, we just insert a dummy block (size 0) in CompressedSecondaryCache. |
63 | | // |
64 | | // Users can also cast a pointer to CompressedSecondaryCache and call methods on |
65 | | // it directly, especially custom methods that may be added |
66 | | // in the future. For example - |
67 | | // std::unique_ptr<rocksdb::SecondaryCache> cache = |
68 | | // NewCompressedSecondaryCache(opts); |
69 | | // static_cast<CompressedSecondaryCache*>(cache.get())->Erase(key); |
70 | | |
71 | | class CompressedSecondaryCache : public SecondaryCache { |
72 | | public: |
73 | | explicit CompressedSecondaryCache( |
74 | | const CompressedSecondaryCacheOptions& opts); |
75 | | ~CompressedSecondaryCache() override; |
76 | | |
77 | 0 | const char* Name() const override { return "CompressedSecondaryCache"; } |
78 | | |
79 | | Status Insert(const Slice& key, Cache::ObjectPtr value, |
80 | | const Cache::CacheItemHelper* helper, |
81 | | bool force_insert) override; |
82 | | |
83 | | Status InsertSaved(const Slice& key, const Slice& saved, CompressionType type, |
84 | | CacheTier source) override; |
85 | | |
86 | | std::unique_ptr<SecondaryCacheResultHandle> Lookup( |
87 | | const Slice& key, const Cache::CacheItemHelper* helper, |
88 | | Cache::CreateContext* create_context, bool /*wait*/, bool advise_erase, |
89 | | Statistics* stats, bool& kept_in_sec_cache) override; |
90 | | |
91 | 0 | bool SupportForceErase() const override { return true; } |
92 | | |
93 | | void Erase(const Slice& key) override; |
94 | | |
95 | 0 | void WaitAll(std::vector<SecondaryCacheResultHandle*> /*handles*/) override {} |
96 | | |
97 | | Status SetCapacity(size_t capacity) override; |
98 | | |
99 | | Status GetCapacity(size_t& capacity) override; |
100 | | |
101 | | Status Deflate(size_t decrease) override; |
102 | | |
103 | | Status Inflate(size_t increase) override; |
104 | | |
105 | | std::string GetPrintableOptions() const override; |
106 | | |
107 | 0 | size_t TEST_GetUsage() { return cache_->GetUsage(); } |
108 | | |
109 | | private: |
110 | | friend class CompressedSecondaryCacheTestBase; |
111 | | static constexpr std::array<uint16_t, 8> malloc_bin_sizes_{ |
112 | | 128, 256, 512, 1024, 2048, 4096, 8192, 16384}; |
113 | | |
114 | | struct CacheValueChunk { |
115 | | // TODO try "CacheAllocationPtr next;". |
116 | | CacheValueChunk* next; |
117 | | size_t size; |
118 | | // Beginning of the chunk data (MUST BE THE LAST FIELD IN THIS STRUCT!) |
119 | | char data[1]; |
120 | | |
121 | 0 | void Free() { delete[] reinterpret_cast<char*>(this); } |
122 | | }; |
123 | | |
124 | | // Split value into chunks to better fit into jemalloc bins. The chunks |
125 | | // are stored in CacheValueChunk and extra charge is needed for each chunk, |
126 | | // so the cache charge is recalculated here. |
127 | | CacheValueChunk* SplitValueIntoChunks(const Slice& value, |
128 | | CompressionType compression_type, |
129 | | size_t& charge); |
130 | | |
131 | | // After merging chunks, the extra charge for each chunk is removed, so |
132 | | // the charge is recalculated. |
133 | | CacheAllocationPtr MergeChunksIntoValue(const void* chunks_head, |
134 | | size_t& charge); |
135 | | |
136 | | bool MaybeInsertDummy(const Slice& key); |
137 | | |
138 | | Status InsertInternal(const Slice& key, Cache::ObjectPtr value, |
139 | | const Cache::CacheItemHelper* helper, |
140 | | CompressionType type, CacheTier source); |
141 | | |
142 | | // TODO: clean up to use cleaner interfaces in typed_cache.h |
143 | | const Cache::CacheItemHelper* GetHelper(bool enable_custom_split_merge) const; |
144 | | std::shared_ptr<Cache> cache_; |
145 | | CompressedSecondaryCacheOptions cache_options_; |
146 | | mutable port::Mutex capacity_mutex_; |
147 | | std::shared_ptr<ConcurrentCacheReservationManager> cache_res_mgr_; |
148 | | bool disable_cache_; |
149 | | }; |
150 | | |
151 | | } // namespace ROCKSDB_NAMESPACE |