#pragma once
#ifndef OS_WIN
#include <unistd.h>
#endif
#include <atomic>
#include <list>
#include <memory>
#include <set>
#include <sstream>
#include <stdexcept>
#include <string>
#include <thread>
#include "memory/arena.h"
#include "memtable/skiplist.h"
#include "monitoring/histogram.h"
#include "port/port.h"
#include "rocksdb/cache.h"
#include "rocksdb/comparator.h"
#include "rocksdb/persistent_cache.h"
#include "rocksdb/system_clock.h"
#include "util/coding.h"
#include "util/crc32c.h"
#include "util/mutexlock.h"
#include "utilities/persistent_cache/block_cache_tier_file.h"
#include "utilities/persistent_cache/block_cache_tier_metadata.h"
#include "utilities/persistent_cache/persistent_cache_util.h"
namespace ROCKSDB_NAMESPACE {
class BlockCacheTier : public PersistentCacheTier {
public:
explicit BlockCacheTier(const PersistentCacheConfig& opt)
: opt_(opt),
insert_ops_(static_cast<size_t>(opt_.max_write_pipeline_backlog_size)),
buffer_allocator_(opt.write_buffer_size, opt.write_buffer_count()),
writer_(this, opt_.writer_qdepth,
static_cast<size_t>(opt_.writer_dispatch_size)) {
Info(opt_.log, "Initializing allocator. size=%d B count=%" ROCKSDB_PRIszt,
opt_.write_buffer_size, opt_.write_buffer_count());
}
virtual ~BlockCacheTier() {
Close().PermitUncheckedError();
assert(!insert_th_.joinable());
}
Status Insert(const Slice& key, const char* data, const size_t size) override;
Status Lookup(const Slice& key, std::unique_ptr<char[]>* data,
size_t* size) override;
Status Open() override;
Status Close() override;
bool Erase(const Slice& key) override;
bool Reserve(const size_t size) override;
bool IsCompressed() override { return opt_.is_compressed; }
std::string GetPrintableOptions() const override { return opt_.ToString(); }
PersistentCache::StatsType Stats() override;
void TEST_Flush() override {
while (insert_ops_.Size()) {
SystemClock::Default()->SleepForMicroseconds(1000000);
}
}
private:
static const size_t kEvictPct = 10;
static const size_t kMaxRetry = 3;
struct InsertOp {
explicit InsertOp(const bool signal) : signal_(signal) {}
explicit InsertOp(std::string&& key, const std::string& data)
: key_(std::move(key)), data_(data) {}
~InsertOp() {}
InsertOp() = delete;
InsertOp(InsertOp&& ) = default;
InsertOp& operator=(InsertOp&& rhs) = default;
size_t Size() { return data_.size() + key_.size(); }
std::string key_;
std::string data_;
bool signal_ = false; };
void InsertMain();
Status InsertImpl(const Slice& key, const Slice& data);
Status NewCacheFile();
std::string GetCachePath() const { return opt_.path + "/cache"; }
Status CleanupCacheFolder(const std::string& folder);
struct Statistics {
HistogramImpl bytes_pipelined_;
HistogramImpl bytes_written_;
HistogramImpl bytes_read_;
HistogramImpl read_hit_latency_;
HistogramImpl read_miss_latency_;
HistogramImpl write_latency_;
std::atomic<uint64_t> cache_hits_{0};
std::atomic<uint64_t> cache_misses_{0};
std::atomic<uint64_t> cache_errors_{0};
std::atomic<uint64_t> insert_dropped_{0};
double CacheHitPct() const {
const auto lookups = cache_hits_ + cache_misses_;
return lookups ? 100 * cache_hits_ / static_cast<double>(lookups) : 0.0;
}
double CacheMissPct() const {
const auto lookups = cache_hits_ + cache_misses_;
return lookups ? 100 * cache_misses_ / static_cast<double>(lookups) : 0.0;
}
};
port::RWMutex lock_; const PersistentCacheConfig opt_; BoundedQueue<InsertOp> insert_ops_; ROCKSDB_NAMESPACE::port::Thread insert_th_; uint32_t writer_cache_id_ = 0; WriteableCacheFile* cache_file_ = nullptr; CacheWriteBufferAllocator buffer_allocator_; ThreadedWriter writer_; BlockCacheTierMetadata metadata_; std::atomic<uint64_t> size_{0}; Statistics stats_; };
}