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