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 "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"
16 namespace ROCKSDB_NAMESPACE
{
20 class CacheActivityLogger
{
23 : activity_logging_enabled_(false), max_logging_size_(0) {}
25 ~CacheActivityLogger() {
28 StopLoggingInternal();
29 bg_status_
.PermitUncheckedError();
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);
39 std::unique_ptr
<WritableFile
> log_file
;
43 // Stop existing logging if any
44 StopLoggingInternal();
47 status
= env
->NewWritableFile(activity_log_file
, &log_file
, env_opts
);
51 file_writer_
.reset(new WritableFileWriter(
52 NewLegacyWritableFileWrapper(std::move(log_file
)), activity_log_file
,
55 max_logging_size_
= max_logging_size
;
56 activity_logging_enabled_
.store(true);
64 StopLoggingInternal();
67 void ReportLookup(const Slice
& key
) {
68 if (activity_logging_enabled_
.load() == false) {
72 std::string log_line
= "LOOKUP - " + key
.ToString(true) + "\n";
74 // line format: "LOOKUP - <KEY>"
76 Status s
= file_writer_
->Append(log_line
);
77 if (!s
.ok() && bg_status_
.ok()) {
80 if (MaxLoggingSizeReached() || !bg_status_
.ok()) {
81 // Stop logging if we have reached the max file size or
82 // encountered an error
83 StopLoggingInternal();
87 void ReportAdd(const Slice
& key
, size_t size
) {
88 if (activity_logging_enabled_
.load() == false) {
92 std::string log_line
= "ADD - ";
93 log_line
+= key
.ToString(true);
95 AppendNumberTo(&log_line
, size
);
98 // line format: "ADD - <KEY> - <KEY-SIZE>"
100 Status s
= file_writer_
->Append(log_line
);
101 if (!s
.ok() && bg_status_
.ok()) {
105 if (MaxLoggingSizeReached() || !bg_status_
.ok()) {
106 // Stop logging if we have reached the max file size or
107 // encountered an error
108 StopLoggingInternal();
112 Status
& bg_status() {
113 MutexLock
l(&mutex_
);
118 bool MaxLoggingSizeReached() {
121 return (max_logging_size_
> 0 &&
122 file_writer_
->GetFileSize() >= max_logging_size_
);
125 void StopLoggingInternal() {
128 if (!activity_logging_enabled_
) {
132 activity_logging_enabled_
.store(false);
133 Status s
= file_writer_
->Close();
134 if (!s
.ok() && bg_status_
.ok()) {
139 // Mutex to sync writes to file_writer, and all following
140 // class data members
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_
;
152 // SimCacheImpl definition
153 class SimCacheImpl
: public SimCache
{
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
)
159 key_only_cache_(sim_cache
),
164 ~SimCacheImpl() override
{}
165 void SetCapacity(size_t capacity
) override
{ cache_
->SetCapacity(capacity
); }
167 void SetStrictCapacityLimit(bool strict_capacity_limit
) override
{
168 cache_
->SetStrictCapacityLimit(strict_capacity_limit
);
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
);
181 // TODO: Check for error here?
182 auto s
= key_only_cache_
->Insert(
183 key
, nullptr, charge
, [](const Slice
& /*k*/, void* /*v*/) {}, nullptr,
185 s
.PermitUncheckedError();
187 key_only_cache_
->Release(h
);
190 cache_activity_logger_
.ReportAdd(key
, charge
);
194 return cache_
->Insert(key
, value
, charge
, deleter
, handle
, priority
);
197 Handle
* Lookup(const Slice
& key
, Statistics
* stats
) override
{
198 Handle
* h
= key_only_cache_
->Lookup(key
);
200 key_only_cache_
->Release(h
);
202 RecordTick(stats
, SIM_BLOCK_CACHE_HIT
);
205 RecordTick(stats
, SIM_BLOCK_CACHE_MISS
);
208 cache_activity_logger_
.ReportLookup(key
);
212 return cache_
->Lookup(key
, stats
);
215 bool Ref(Handle
* handle
) override
{ return cache_
->Ref(handle
); }
217 bool Release(Handle
* handle
, bool force_erase
= false) override
{
218 return cache_
->Release(handle
, force_erase
);
221 void Erase(const Slice
& key
) override
{
223 key_only_cache_
->Erase(key
);
226 void* Value(Handle
* handle
) override
{ return cache_
->Value(handle
); }
228 uint64_t NewId() override
{ return cache_
->NewId(); }
230 size_t GetCapacity() const override
{ return cache_
->GetCapacity(); }
232 bool HasStrictCapacityLimit() const override
{
233 return cache_
->HasStrictCapacityLimit();
236 size_t GetUsage() const override
{ return cache_
->GetUsage(); }
238 size_t GetUsage(Handle
* handle
) const override
{
239 return cache_
->GetUsage(handle
);
242 size_t GetCharge(Handle
* handle
) const override
{
243 return cache_
->GetCharge(handle
);
246 size_t GetPinnedUsage() const override
{ return cache_
->GetPinnedUsage(); }
248 void DisownData() override
{
249 cache_
->DisownData();
250 key_only_cache_
->DisownData();
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
);
259 void EraseUnRefEntries() override
{
260 cache_
->EraseUnRefEntries();
261 key_only_cache_
->EraseUnRefEntries();
264 size_t GetSimCapacity() const override
{
265 return key_only_cache_
->GetCapacity();
267 size_t GetSimUsage() const override
{ return key_only_cache_
->GetUsage(); }
268 void SetSimCapacity(size_t capacity
) override
{
269 key_only_cache_
->SetCapacity(capacity
);
272 uint64_t get_miss_counter() const override
{
273 return miss_times_
.load(std::memory_order_relaxed
);
276 uint64_t get_hit_counter() const override
{
277 return hit_times_
.load(std::memory_order_relaxed
);
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);
287 std::string
ToString() const override
{
289 res
.append("SimCache MISSes: " + std::to_string(get_miss_counter()) + "\n");
290 res
.append("SimCache HITs: " + std::to_string(get_hit_counter()) + "\n");
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
));
299 std::string
GetPrintableOptions() const override
{
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());
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
,
315 void StopActivityLogging() override
{ cache_activity_logger_
.StopLogging(); }
317 Status
GetActivityLoggingStatus() override
{
318 return cache_activity_logger_
.bg_status();
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_
;
327 CacheActivityLogger cache_activity_logger_
;
329 void inc_miss_counter() {
330 miss_times_
.fetch_add(1, std::memory_order_relaxed
);
332 void inc_hit_counter() { hit_times_
.fetch_add(1, std::memory_order_relaxed
); }
335 } // end anonymous namespace
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
) {
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
);
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
353 return std::make_shared
<SimCacheImpl
>(sim_cache
, cache
);
356 } // namespace ROCKSDB_NAMESPACE