compressed_secondary_cache.h 5.3 KB
Newer Older
1 2 3 4 5 6 7
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
//  This source code is licensed under both the GPLv2 (found in the
//  COPYING file in the root directory) and Apache 2.0 License
//  (found in the LICENSE.Apache file in the root directory).

#pragma once

8 9
#include <array>
#include <cstddef>
10 11 12 13 14 15 16 17
#include <memory>

#include "cache/lru_cache.h"
#include "memory/memory_allocator.h"
#include "rocksdb/secondary_cache.h"
#include "rocksdb/slice.h"
#include "rocksdb/status.h"
#include "util/compression.h"
18
#include "util/mutexlock.h"
19 20 21

namespace ROCKSDB_NAMESPACE {

22
class CompressedSecondaryCacheResultHandle : public SecondaryCacheResultHandle {
23
 public:
24
  CompressedSecondaryCacheResultHandle(Cache::ObjectPtr value, size_t size)
25
      : value_(value), size_(size) {}
26
  ~CompressedSecondaryCacheResultHandle() override = default;
27

28 29 30 31
  CompressedSecondaryCacheResultHandle(
      const CompressedSecondaryCacheResultHandle&) = delete;
  CompressedSecondaryCacheResultHandle& operator=(
      const CompressedSecondaryCacheResultHandle&) = delete;
32 33 34 35 36

  bool IsReady() override { return true; }

  void Wait() override {}

37
  Cache::ObjectPtr Value() override { return value_; }
38 39 40 41

  size_t Size() override { return size_; }

 private:
42
  Cache::ObjectPtr value_;
43 44 45
  size_t size_;
};

46
// The CompressedSecondaryCache is a concrete implementation of
47 48
// rocksdb::SecondaryCache.
//
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
// When a block is found from CompressedSecondaryCache::Lookup, we check whether
// there is a dummy block with the same key in the primary cache.
// 1. If the dummy block exits, we erase the block from
//    CompressedSecondaryCache and insert it into the primary cache.
// 2. If not, we just insert a dummy block into the primary cache
//    (charging the actual size of the block) and don not erase the block from
//    CompressedSecondaryCache. A standalone handle is returned to the caller.
//
// When a block is evicted from the primary cache, we check whether
// there is a dummy block with the same key in CompressedSecondaryCache.
// 1. If the dummy block exits, the block is inserted into
//    CompressedSecondaryCache.
// 2. If not, we just insert a dummy block (size 0) in CompressedSecondaryCache.
//
// Users can also cast a pointer to CompressedSecondaryCache and call methods on
64 65 66
// it directly, especially custom methods that may be added
// in the future.  For example -
// std::unique_ptr<rocksdb::SecondaryCache> cache =
67 68
//      NewCompressedSecondaryCache(opts);
// static_cast<CompressedSecondaryCache*>(cache.get())->Erase(key);
69

70
class CompressedSecondaryCache : public SecondaryCache {
71
 public:
72
  CompressedSecondaryCache(
73
      size_t capacity, int num_shard_bits, bool strict_capacity_limit,
74
      double high_pri_pool_ratio, double low_pri_pool_ratio,
75 76 77
      std::shared_ptr<MemoryAllocator> memory_allocator = nullptr,
      bool use_adaptive_mutex = kDefaultToAdaptiveMutex,
      CacheMetadataChargePolicy metadata_charge_policy =
78
          kDefaultCacheMetadataChargePolicy,
79
      CompressionType compression_type = CompressionType::kLZ4Compression,
80
      uint32_t compress_format_version = 2,
81 82 83
      bool enable_custom_split_merge = false,
      const CacheEntryRoleSet& do_not_compress_roles = {
          CacheEntryRole::kFilterBlock});
84
  ~CompressedSecondaryCache() override;
85

86
  const char* Name() const override { return "CompressedSecondaryCache"; }
87

88
  Status Insert(const Slice& key, Cache::ObjectPtr value,
89 90 91
                const Cache::CacheItemHelper* helper) override;

  std::unique_ptr<SecondaryCacheResultHandle> Lookup(
92 93
      const Slice& key, const Cache::CacheItemHelper* helper,
      Cache::CreateContext* create_context, bool /*wait*/, bool advise_erase,
94
      bool& kept_in_sec_cache) override;
95 96

  bool SupportForceErase() const override { return true; }
97 98 99 100 101

  void Erase(const Slice& key) override;

  void WaitAll(std::vector<SecondaryCacheResultHandle*> /*handles*/) override {}

102 103 104 105
  Status SetCapacity(size_t capacity) override;

  Status GetCapacity(size_t& capacity) override;

106 107 108
  std::string GetPrintableOptions() const override;

 private:
109
  friend class CompressedSecondaryCacheTest;
110 111
  static constexpr std::array<uint16_t, 8> malloc_bin_sizes_{
      128, 256, 512, 1024, 2048, 4096, 8192, 16384};
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126

  struct CacheValueChunk {
    // TODO try "CacheAllocationPtr next;".
    CacheValueChunk* next;
    size_t size;
    // Beginning of the chunk data (MUST BE THE LAST FIELD IN THIS STRUCT!)
    char data[1];

    void Free() { delete[] reinterpret_cast<char*>(this); }
  };

  // Split value into chunks to better fit into jemalloc bins. The chunks
  // are stored in CacheValueChunk and extra charge is needed for each chunk,
  // so the cache charge is recalculated here.
  CacheValueChunk* SplitValueIntoChunks(const Slice& value,
127
                                        CompressionType compression_type,
128 129 130 131 132 133 134
                                        size_t& charge);

  // After merging chunks, the extra charge for each chunk is removed, so
  // the charge is recalculated.
  CacheAllocationPtr MergeChunksIntoValue(const void* chunks_head,
                                          size_t& charge);

135 136
  // TODO: clean up to use cleaner interfaces in typed_cache.h
  const Cache::CacheItemHelper* GetHelper(bool enable_custom_split_merge) const;
137
  std::shared_ptr<Cache> cache_;
138
  CompressedSecondaryCacheOptions cache_options_;
139
  mutable port::Mutex capacity_mutex_;
140 141 142
};

}  // namespace ROCKSDB_NAMESPACE