sim_cache.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  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. #include "rocksdb/utilities/sim_cache.h"
  6. #include <atomic>
  7. #include <iomanip>
  8. #include "file/writable_file_writer.h"
  9. #include "monitoring/statistics_impl.h"
  10. #include "port/port.h"
  11. #include "rocksdb/env.h"
  12. #include "rocksdb/file_system.h"
  13. #include "util/mutexlock.h"
  14. namespace ROCKSDB_NAMESPACE {
  15. namespace {
  16. class CacheActivityLogger {
  17. public:
  18. CacheActivityLogger()
  19. : activity_logging_enabled_(false), max_logging_size_(0) {}
  20. ~CacheActivityLogger() {
  21. MutexLock l(&mutex_);
  22. StopLoggingInternal();
  23. bg_status_.PermitUncheckedError();
  24. }
  25. Status StartLogging(const std::string& activity_log_file, Env* env,
  26. uint64_t max_logging_size = 0) {
  27. assert(activity_log_file != "");
  28. assert(env != nullptr);
  29. Status status;
  30. FileOptions file_opts;
  31. MutexLock l(&mutex_);
  32. // Stop existing logging if any
  33. StopLoggingInternal();
  34. // Open log file
  35. status = WritableFileWriter::Create(env->GetFileSystem(), activity_log_file,
  36. file_opts, &file_writer_, nullptr);
  37. if (!status.ok()) {
  38. return status;
  39. }
  40. max_logging_size_ = max_logging_size;
  41. activity_logging_enabled_.store(true);
  42. return status;
  43. }
  44. void StopLogging() {
  45. MutexLock l(&mutex_);
  46. StopLoggingInternal();
  47. }
  48. void ReportLookup(const Slice& key) {
  49. if (activity_logging_enabled_.load() == false) {
  50. return;
  51. }
  52. std::ostringstream oss;
  53. // line format: "LOOKUP - <KEY>"
  54. oss << "LOOKUP - " << key.ToString(true) << std::endl;
  55. MutexLock l(&mutex_);
  56. Status s = file_writer_->Append(IOOptions(), oss.str());
  57. if (!s.ok() && bg_status_.ok()) {
  58. bg_status_ = s;
  59. }
  60. if (MaxLoggingSizeReached() || !bg_status_.ok()) {
  61. // Stop logging if we have reached the max file size or
  62. // encountered an error
  63. StopLoggingInternal();
  64. }
  65. }
  66. void ReportAdd(const Slice& key, size_t size) {
  67. if (activity_logging_enabled_.load() == false) {
  68. return;
  69. }
  70. std::ostringstream oss;
  71. // line format: "ADD - <KEY> - <KEY-SIZE>"
  72. oss << "ADD - " << key.ToString(true) << " - " << size << std::endl;
  73. MutexLock l(&mutex_);
  74. Status s = file_writer_->Append(IOOptions(), oss.str());
  75. if (!s.ok() && bg_status_.ok()) {
  76. bg_status_ = s;
  77. }
  78. if (MaxLoggingSizeReached() || !bg_status_.ok()) {
  79. // Stop logging if we have reached the max file size or
  80. // encountered an error
  81. StopLoggingInternal();
  82. }
  83. }
  84. Status& bg_status() {
  85. MutexLock l(&mutex_);
  86. return bg_status_;
  87. }
  88. private:
  89. bool MaxLoggingSizeReached() {
  90. mutex_.AssertHeld();
  91. return (max_logging_size_ > 0 &&
  92. file_writer_->GetFileSize() >= max_logging_size_);
  93. }
  94. void StopLoggingInternal() {
  95. mutex_.AssertHeld();
  96. if (!activity_logging_enabled_) {
  97. return;
  98. }
  99. activity_logging_enabled_.store(false);
  100. Status s = file_writer_->Close(IOOptions());
  101. if (!s.ok() && bg_status_.ok()) {
  102. bg_status_ = s;
  103. }
  104. }
  105. // Mutex to sync writes to file_writer, and all following
  106. // class data members
  107. port::Mutex mutex_;
  108. // Indicates if logging is currently enabled
  109. // atomic to allow reads without mutex
  110. std::atomic<bool> activity_logging_enabled_;
  111. // When reached, we will stop logging and close the file
  112. // Value of 0 means unlimited
  113. uint64_t max_logging_size_;
  114. std::unique_ptr<WritableFileWriter> file_writer_;
  115. Status bg_status_;
  116. };
  117. // SimCacheImpl definition
  118. class SimCacheImpl : public SimCache {
  119. public:
  120. // capacity for real cache (ShardedLRUCache)
  121. // test_capacity for key only cache
  122. SimCacheImpl(std::shared_ptr<Cache> sim_cache, std::shared_ptr<Cache> cache)
  123. : SimCache(cache),
  124. key_only_cache_(sim_cache),
  125. miss_times_(0),
  126. hit_times_(0),
  127. stats_(nullptr) {}
  128. ~SimCacheImpl() override = default;
  129. const char* Name() const override { return "SimCache"; }
  130. void SetCapacity(size_t capacity) override { target_->SetCapacity(capacity); }
  131. void SetStrictCapacityLimit(bool strict_capacity_limit) override {
  132. target_->SetStrictCapacityLimit(strict_capacity_limit);
  133. }
  134. Status Insert(const Slice& key, Cache::ObjectPtr value,
  135. const CacheItemHelper* helper, size_t charge, Handle** handle,
  136. Priority priority, const Slice& compressed = {},
  137. CompressionType type = kNoCompression) override {
  138. // The handle and value passed in are for real cache, so we pass nullptr
  139. // to key_only_cache_ for both instead. Also, the deleter function pointer
  140. // will be called by user to perform some external operation which should
  141. // be applied only once. Thus key_only_cache accepts an empty function.
  142. // *Lambda function without capture can be assgined to a function pointer
  143. Handle* h = key_only_cache_->Lookup(key);
  144. if (h == nullptr) {
  145. // TODO: Check for error here?
  146. auto s =
  147. key_only_cache_->Insert(key, nullptr, &kNoopCacheItemHelper, charge,
  148. nullptr, priority, compressed, type);
  149. s.PermitUncheckedError();
  150. } else {
  151. key_only_cache_->Release(h);
  152. }
  153. cache_activity_logger_.ReportAdd(key, charge);
  154. if (!target_) {
  155. return Status::OK();
  156. }
  157. return target_->Insert(key, value, helper, charge, handle, priority,
  158. compressed, type);
  159. }
  160. Handle* Lookup(const Slice& key, const CacheItemHelper* helper,
  161. CreateContext* create_context,
  162. Priority priority = Priority::LOW,
  163. Statistics* stats = nullptr) override {
  164. HandleLookup(key, stats);
  165. if (!target_) {
  166. return nullptr;
  167. }
  168. return target_->Lookup(key, helper, create_context, priority, stats);
  169. }
  170. void StartAsyncLookup(AsyncLookupHandle& async_handle) override {
  171. HandleLookup(async_handle.key, async_handle.stats);
  172. if (target_) {
  173. target_->StartAsyncLookup(async_handle);
  174. }
  175. }
  176. bool Ref(Handle* handle) override { return target_->Ref(handle); }
  177. using Cache::Release;
  178. bool Release(Handle* handle, bool erase_if_last_ref = false) override {
  179. return target_->Release(handle, erase_if_last_ref);
  180. }
  181. void Erase(const Slice& key) override {
  182. target_->Erase(key);
  183. key_only_cache_->Erase(key);
  184. }
  185. Cache::ObjectPtr Value(Handle* handle) override {
  186. return target_->Value(handle);
  187. }
  188. uint64_t NewId() override { return target_->NewId(); }
  189. size_t GetCapacity() const override { return target_->GetCapacity(); }
  190. bool HasStrictCapacityLimit() const override {
  191. return target_->HasStrictCapacityLimit();
  192. }
  193. size_t GetUsage() const override { return target_->GetUsage(); }
  194. size_t GetUsage(Handle* handle) const override {
  195. return target_->GetUsage(handle);
  196. }
  197. size_t GetCharge(Handle* handle) const override {
  198. return target_->GetCharge(handle);
  199. }
  200. const CacheItemHelper* GetCacheItemHelper(Handle* handle) const override {
  201. return target_->GetCacheItemHelper(handle);
  202. }
  203. size_t GetPinnedUsage() const override { return target_->GetPinnedUsage(); }
  204. void DisownData() override {
  205. target_->DisownData();
  206. key_only_cache_->DisownData();
  207. }
  208. void ApplyToAllEntries(
  209. const std::function<void(const Slice& key, ObjectPtr value, size_t charge,
  210. const CacheItemHelper* helper)>& callback,
  211. const ApplyToAllEntriesOptions& opts) override {
  212. target_->ApplyToAllEntries(callback, opts);
  213. }
  214. void EraseUnRefEntries() override {
  215. target_->EraseUnRefEntries();
  216. key_only_cache_->EraseUnRefEntries();
  217. }
  218. size_t GetSimCapacity() const override {
  219. return key_only_cache_->GetCapacity();
  220. }
  221. size_t GetSimUsage() const override { return key_only_cache_->GetUsage(); }
  222. void SetSimCapacity(size_t capacity) override {
  223. key_only_cache_->SetCapacity(capacity);
  224. }
  225. uint64_t get_miss_counter() const override {
  226. return miss_times_.load(std::memory_order_relaxed);
  227. }
  228. uint64_t get_hit_counter() const override {
  229. return hit_times_.load(std::memory_order_relaxed);
  230. }
  231. void reset_counter() override {
  232. miss_times_.store(0, std::memory_order_relaxed);
  233. hit_times_.store(0, std::memory_order_relaxed);
  234. SetTickerCount(stats_, SIM_BLOCK_CACHE_HIT, 0);
  235. SetTickerCount(stats_, SIM_BLOCK_CACHE_MISS, 0);
  236. }
  237. std::string ToString() const override {
  238. std::ostringstream oss;
  239. oss << "SimCache MISSes: " << get_miss_counter() << std::endl;
  240. oss << "SimCache HITs: " << get_hit_counter() << std::endl;
  241. auto lookups = get_miss_counter() + get_hit_counter();
  242. oss << "SimCache HITRATE: " << std::fixed << std::setprecision(2)
  243. << (lookups == 0 ? 0 : get_hit_counter() * 100.0f / lookups)
  244. << std::endl;
  245. return oss.str();
  246. }
  247. std::string GetPrintableOptions() const override {
  248. std::ostringstream oss;
  249. oss << " cache_options:" << std::endl;
  250. oss << target_->GetPrintableOptions();
  251. oss << " sim_cache_options:" << std::endl;
  252. oss << key_only_cache_->GetPrintableOptions();
  253. return oss.str();
  254. }
  255. Status StartActivityLogging(const std::string& activity_log_file, Env* env,
  256. uint64_t max_logging_size = 0) override {
  257. return cache_activity_logger_.StartLogging(activity_log_file, env,
  258. max_logging_size);
  259. }
  260. void StopActivityLogging() override { cache_activity_logger_.StopLogging(); }
  261. Status GetActivityLoggingStatus() override {
  262. return cache_activity_logger_.bg_status();
  263. }
  264. private:
  265. std::shared_ptr<Cache> key_only_cache_;
  266. std::atomic<uint64_t> miss_times_;
  267. std::atomic<uint64_t> hit_times_;
  268. Statistics* stats_;
  269. CacheActivityLogger cache_activity_logger_;
  270. void inc_miss_counter() {
  271. miss_times_.fetch_add(1, std::memory_order_relaxed);
  272. }
  273. void inc_hit_counter() { hit_times_.fetch_add(1, std::memory_order_relaxed); }
  274. void HandleLookup(const Slice& key, Statistics* stats) {
  275. Handle* h = key_only_cache_->Lookup(key);
  276. if (h != nullptr) {
  277. key_only_cache_->Release(h);
  278. inc_hit_counter();
  279. RecordTick(stats, SIM_BLOCK_CACHE_HIT);
  280. } else {
  281. inc_miss_counter();
  282. RecordTick(stats, SIM_BLOCK_CACHE_MISS);
  283. }
  284. cache_activity_logger_.ReportLookup(key);
  285. }
  286. };
  287. } // end anonymous namespace
  288. // For instrumentation purpose, use NewSimCache instead
  289. std::shared_ptr<SimCache> NewSimCache(std::shared_ptr<Cache> cache,
  290. size_t sim_capacity, int num_shard_bits) {
  291. LRUCacheOptions co;
  292. co.capacity = sim_capacity;
  293. co.num_shard_bits = num_shard_bits;
  294. co.metadata_charge_policy = kDontChargeCacheMetadata;
  295. return NewSimCache(NewLRUCache(co), cache, num_shard_bits);
  296. }
  297. std::shared_ptr<SimCache> NewSimCache(std::shared_ptr<Cache> sim_cache,
  298. std::shared_ptr<Cache> cache,
  299. int num_shard_bits) {
  300. if (num_shard_bits >= 20) {
  301. return nullptr; // the cache cannot be sharded into too many fine pieces
  302. }
  303. return std::make_shared<SimCacheImpl>(sim_cache, cache);
  304. }
  305. } // namespace ROCKSDB_NAMESPACE