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).
6 #include "rocksdb/utilities/sim_cache.h"
8 #include "monitoring/statistics.h"
10 #include "rocksdb/env.h"
11 #include "util/file_reader_writer.h"
12 #include "util/mutexlock.h"
13 #include "util/string_util.h"
19 class CacheActivityLogger
{
22 : activity_logging_enabled_(false), max_logging_size_(0) {}
24 ~CacheActivityLogger() {
27 StopLoggingInternal();
30 Status
StartLogging(const std::string
& activity_log_file
, Env
* env
,
31 uint64_t max_logging_size
= 0) {
32 assert(activity_log_file
!= "");
33 assert(env
!= nullptr);
37 std::unique_ptr
<WritableFile
> log_file
;
41 // Stop existing logging if any
42 StopLoggingInternal();
45 status
= env
->NewWritableFile(activity_log_file
, &log_file
, env_opts
);
49 file_writer_
.reset(new WritableFileWriter(std::move(log_file
),
50 activity_log_file
, env_opts
));
52 max_logging_size_
= max_logging_size
;
53 activity_logging_enabled_
.store(true);
61 StopLoggingInternal();
64 void ReportLookup(const Slice
& key
) {
65 if (activity_logging_enabled_
.load() == false) {
69 std::string log_line
= "LOOKUP - " + key
.ToString(true) + "\n";
71 // line format: "LOOKUP - <KEY>"
73 Status s
= file_writer_
->Append(log_line
);
74 if (!s
.ok() && bg_status_
.ok()) {
77 if (MaxLoggingSizeReached() || !bg_status_
.ok()) {
78 // Stop logging if we have reached the max file size or
79 // encountered an error
80 StopLoggingInternal();
84 void ReportAdd(const Slice
& key
, size_t size
) {
85 if (activity_logging_enabled_
.load() == false) {
89 std::string log_line
= "ADD - ";
90 log_line
+= key
.ToString(true);
92 AppendNumberTo(&log_line
, size
);
93 // @lint-ignore TXT2 T25377293 Grandfathered in
96 // line format: "ADD - <KEY> - <KEY-SIZE>"
98 Status s
= file_writer_
->Append(log_line
);
99 if (!s
.ok() && bg_status_
.ok()) {
103 if (MaxLoggingSizeReached() || !bg_status_
.ok()) {
104 // Stop logging if we have reached the max file size or
105 // encountered an error
106 StopLoggingInternal();
110 Status
& bg_status() {
111 MutexLock
l(&mutex_
);
116 bool MaxLoggingSizeReached() {
119 return (max_logging_size_
> 0 &&
120 file_writer_
->GetFileSize() >= max_logging_size_
);
123 void StopLoggingInternal() {
126 if (!activity_logging_enabled_
) {
130 activity_logging_enabled_
.store(false);
131 Status s
= file_writer_
->Close();
132 if (!s
.ok() && bg_status_
.ok()) {
137 // Mutex to sync writes to file_writer, and all following
138 // class data members
140 // Indicates if logging is currently enabled
141 // atomic to allow reads without mutex
142 std::atomic
<bool> activity_logging_enabled_
;
143 // When reached, we will stop logging and close the file
144 // Value of 0 means unlimited
145 uint64_t max_logging_size_
;
146 std::unique_ptr
<WritableFileWriter
> file_writer_
;
150 // SimCacheImpl definition
151 class SimCacheImpl
: public SimCache
{
153 // capacity for real cache (ShardedLRUCache)
154 // test_capacity for key only cache
155 SimCacheImpl(std::shared_ptr
<Cache
> cache
, size_t sim_capacity
,
158 key_only_cache_(NewLRUCache(sim_capacity
, num_shard_bits
)),
163 virtual ~SimCacheImpl() {}
164 virtual void SetCapacity(size_t capacity
) override
{
165 cache_
->SetCapacity(capacity
);
168 virtual void SetStrictCapacityLimit(bool strict_capacity_limit
) override
{
169 cache_
->SetStrictCapacityLimit(strict_capacity_limit
);
172 virtual Status
Insert(const Slice
& key
, void* value
, size_t charge
,
173 void (*deleter
)(const Slice
& key
, void* value
),
174 Handle
** handle
, Priority priority
) override
{
175 // The handle and value passed in are for real cache, so we pass nullptr
176 // to key_only_cache_ for both instead. Also, the deleter function pointer
177 // will be called by user to perform some external operation which should
178 // be applied only once. Thus key_only_cache accepts an empty function.
179 // *Lambda function without capture can be assgined to a function pointer
180 Handle
* h
= key_only_cache_
->Lookup(key
);
182 key_only_cache_
->Insert(key
, nullptr, charge
,
183 [](const Slice
& /*k*/, void* /*v*/) {}, nullptr,
186 key_only_cache_
->Release(h
);
189 cache_activity_logger_
.ReportAdd(key
, charge
);
191 return cache_
->Insert(key
, value
, charge
, deleter
, handle
, priority
);
194 virtual Handle
* Lookup(const Slice
& key
, Statistics
* stats
) override
{
195 Handle
* h
= key_only_cache_
->Lookup(key
);
197 key_only_cache_
->Release(h
);
199 RecordTick(stats
, SIM_BLOCK_CACHE_HIT
);
202 RecordTick(stats
, SIM_BLOCK_CACHE_MISS
);
205 cache_activity_logger_
.ReportLookup(key
);
207 return cache_
->Lookup(key
, stats
);
210 virtual bool Ref(Handle
* handle
) override
{ return cache_
->Ref(handle
); }
212 virtual bool Release(Handle
* handle
, bool force_erase
= false) override
{
213 return cache_
->Release(handle
, force_erase
);
216 virtual void Erase(const Slice
& key
) override
{
218 key_only_cache_
->Erase(key
);
221 virtual void* Value(Handle
* handle
) override
{ return cache_
->Value(handle
); }
223 virtual uint64_t NewId() override
{ return cache_
->NewId(); }
225 virtual size_t GetCapacity() const override
{ return cache_
->GetCapacity(); }
227 virtual bool HasStrictCapacityLimit() const override
{
228 return cache_
->HasStrictCapacityLimit();
231 virtual size_t GetUsage() const override
{ return cache_
->GetUsage(); }
233 virtual size_t GetUsage(Handle
* handle
) const override
{
234 return cache_
->GetUsage(handle
);
237 virtual size_t GetPinnedUsage() const override
{
238 return cache_
->GetPinnedUsage();
241 virtual void DisownData() override
{
242 cache_
->DisownData();
243 key_only_cache_
->DisownData();
246 virtual void ApplyToAllCacheEntries(void (*callback
)(void*, size_t),
247 bool thread_safe
) override
{
248 // only apply to _cache since key_only_cache doesn't hold value
249 cache_
->ApplyToAllCacheEntries(callback
, thread_safe
);
252 virtual void EraseUnRefEntries() override
{
253 cache_
->EraseUnRefEntries();
254 key_only_cache_
->EraseUnRefEntries();
257 virtual size_t GetSimCapacity() const override
{
258 return key_only_cache_
->GetCapacity();
260 virtual size_t GetSimUsage() const override
{
261 return key_only_cache_
->GetUsage();
263 virtual void SetSimCapacity(size_t capacity
) override
{
264 key_only_cache_
->SetCapacity(capacity
);
267 virtual uint64_t get_miss_counter() const override
{
268 return miss_times_
.load(std::memory_order_relaxed
);
271 virtual uint64_t get_hit_counter() const override
{
272 return hit_times_
.load(std::memory_order_relaxed
);
275 virtual void reset_counter() override
{
276 miss_times_
.store(0, std::memory_order_relaxed
);
277 hit_times_
.store(0, std::memory_order_relaxed
);
278 SetTickerCount(stats_
, SIM_BLOCK_CACHE_HIT
, 0);
279 SetTickerCount(stats_
, SIM_BLOCK_CACHE_MISS
, 0);
282 virtual std::string
ToString() const override
{
284 res
.append("SimCache MISSes: " + std::to_string(get_miss_counter()) + "\n");
285 res
.append("SimCache HITs: " + std::to_string(get_hit_counter()) + "\n");
287 auto lookups
= get_miss_counter() + get_hit_counter();
288 snprintf(buff
, sizeof(buff
), "SimCache HITRATE: %.2f%%\n",
289 (lookups
== 0 ? 0 : get_hit_counter() * 100.0f
/ lookups
));
294 virtual std::string
GetPrintableOptions() const override
{
297 ret
.append(" cache_options:\n");
298 ret
.append(cache_
->GetPrintableOptions());
299 ret
.append(" sim_cache_options:\n");
300 ret
.append(key_only_cache_
->GetPrintableOptions());
304 virtual Status
StartActivityLogging(const std::string
& activity_log_file
,
306 uint64_t max_logging_size
= 0) override
{
307 return cache_activity_logger_
.StartLogging(activity_log_file
, env
,
311 virtual void StopActivityLogging() override
{
312 cache_activity_logger_
.StopLogging();
315 virtual Status
GetActivityLoggingStatus() override
{
316 return cache_activity_logger_
.bg_status();
320 std::shared_ptr
<Cache
> cache_
;
321 std::shared_ptr
<Cache
> key_only_cache_
;
322 std::atomic
<uint64_t> miss_times_
;
323 std::atomic
<uint64_t> hit_times_
;
325 CacheActivityLogger cache_activity_logger_
;
327 void inc_miss_counter() {
328 miss_times_
.fetch_add(1, std::memory_order_relaxed
);
330 void inc_hit_counter() { hit_times_
.fetch_add(1, std::memory_order_relaxed
); }
333 } // end anonymous namespace
335 // For instrumentation purpose, use NewSimCache instead
336 std::shared_ptr
<SimCache
> NewSimCache(std::shared_ptr
<Cache
> cache
,
337 size_t sim_capacity
, int num_shard_bits
) {
338 if (num_shard_bits
>= 20) {
339 return nullptr; // the cache cannot be sharded into too many fine pieces
341 return std::make_shared
<SimCacheImpl
>(cache
, sim_capacity
, num_shard_bits
);
344 } // end namespace rocksdb