compressed_secondary_cache.h 5.0 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 18 19 20
#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"

namespace ROCKSDB_NAMESPACE {

21
class CompressedSecondaryCacheResultHandle : public SecondaryCacheResultHandle {
22
 public:
23
  CompressedSecondaryCacheResultHandle(void* value, size_t size)
24
      : value_(value), size_(size) {}
25
  virtual ~CompressedSecondaryCacheResultHandle() override = default;
26

27 28 29 30
  CompressedSecondaryCacheResultHandle(
      const CompressedSecondaryCacheResultHandle&) = delete;
  CompressedSecondaryCacheResultHandle& operator=(
      const CompressedSecondaryCacheResultHandle&) = delete;
31 32 33 34 35 36 37 38 39 40 41 42 43 44

  bool IsReady() override { return true; }

  void Wait() override {}

  void* Value() override { return value_; }

  size_t Size() override { return size_; }

 private:
  void* value_;
  size_t size_;
};

45
// The CompressedSecondaryCache is a concrete implementation of
46 47
// rocksdb::SecondaryCache.
//
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
// 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
63 64 65
// it directly, especially custom methods that may be added
// in the future.  For example -
// std::unique_ptr<rocksdb::SecondaryCache> cache =
66 67
//      NewCompressedSecondaryCache(opts);
// static_cast<CompressedSecondaryCache*>(cache.get())->Erase(key);
68

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

83
  const char* Name() const override { return "CompressedSecondaryCache"; }
84 85 86 87 88

  Status Insert(const Slice& key, void* value,
                const Cache::CacheItemHelper* helper) override;

  std::unique_ptr<SecondaryCacheResultHandle> Lookup(
89
      const Slice& key, const Cache::CreateCallback& create_cb, bool /*wait*/,
90 91 92
      bool advise_erase, bool& is_in_sec_cache) override;

  bool SupportForceErase() const override { return true; }
93 94 95 96 97 98 99 100

  void Erase(const Slice& key) override;

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

  std::string GetPrintableOptions() const override;

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

  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,
                                        const CompressionType compression_type,
                                        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);

  // An implementation of Cache::DeleterFn.
128
  static Cache::DeleterFn GetDeletionCallback(bool enable_custom_split_merge);
129
  std::shared_ptr<Cache> cache_;
130
  CompressedSecondaryCacheOptions cache_options_;
131 132 133
};

}  // namespace ROCKSDB_NAMESPACE