Coverage Report

Created: 2026-03-31 07:51

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