sim_cache.cc 10 KB

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