1 //  Copyright (c) Facebook, Inc. and its affiliates. 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 #ifndef ROCKSDB_LITE
7 
8 #include "tools/simulated_hybrid_file_system.h"
9 
10 #include <algorithm>
11 #include <sstream>
12 #include <string>
13 
14 #include "rocksdb/rate_limiter.h"
15 
16 namespace ROCKSDB_NAMESPACE {
17 
18 const int kLatencyAddedPerRequestUs = 15000;
19 const int64_t kRequestPerSec = 100;
20 const int64_t kDummyBytesPerRequest = 1024 * 1024;
21 
22 // The metadata file format: each line is a full filename of a file which is
23 // warm
SimulatedHybridFileSystem(const std::shared_ptr<FileSystem> & base,const std::string & metadata_file_name)24 SimulatedHybridFileSystem::SimulatedHybridFileSystem(
25     const std::shared_ptr<FileSystem>& base,
26     const std::string& metadata_file_name)
27     : FileSystemWrapper(base),
28       // Limit to 100 requests per second.
29       rate_limiter_(NewGenericRateLimiter(
30           kDummyBytesPerRequest * kRequestPerSec /* rate_bytes_per_sec */,
31           1000 /* refill_period_us */)),
32       metadata_file_name_(metadata_file_name),
33       name_("SimulatedHybridFileSystem: " + std::string(target()->Name())) {
34   IOStatus s = base->FileExists(metadata_file_name, IOOptions(), nullptr);
35   if (s.IsNotFound()) {
36     return;
37   }
38   std::string metadata;
39   s = ReadFileToString(base.get(), metadata_file_name, &metadata);
40   if (!s.ok()) {
41     fprintf(stderr, "Error reading from file %s: %s",
42             metadata_file_name.c_str(), s.ToString().c_str());
43     // Exit rather than assert as this file system is built to run with
44     // benchmarks, which usually run on release mode.
45     std::exit(1);
46   }
47   std::istringstream input;
48   input.str(metadata);
49   std::string line;
50   while (std::getline(input, line)) {
51     fprintf(stderr, "Warm file %s\n", line.c_str());
52     warm_file_set_.insert(line);
53   }
54 }
55 
56 // Need to write out the metadata file to file. See comment of
57 // SimulatedHybridFileSystem::SimulatedHybridFileSystem() for format of the
58 // file.
~SimulatedHybridFileSystem()59 SimulatedHybridFileSystem::~SimulatedHybridFileSystem() {
60   std::string metadata;
61   for (const auto& f : warm_file_set_) {
62     metadata += f;
63     metadata += "\n";
64   }
65   IOStatus s = WriteStringToFile(target(), metadata, metadata_file_name_, true);
66   if (!s.ok()) {
67     fprintf(stderr, "Error writing to file %s: %s", metadata_file_name_.c_str(),
68             s.ToString().c_str());
69   }
70 }
71 
NewRandomAccessFile(const std::string & fname,const FileOptions & file_opts,std::unique_ptr<FSRandomAccessFile> * result,IODebugContext * dbg)72 IOStatus SimulatedHybridFileSystem::NewRandomAccessFile(
73     const std::string& fname, const FileOptions& file_opts,
74     std::unique_ptr<FSRandomAccessFile>* result, IODebugContext* dbg) {
75   Temperature temperature = Temperature::kUnknown;
76   {
77     const std::lock_guard<std::mutex> lock(mutex_);
78     if (warm_file_set_.find(fname) != warm_file_set_.end()) {
79       temperature = Temperature::kWarm;
80     }
81   }
82   IOStatus s = target()->NewRandomAccessFile(fname, file_opts, result, dbg);
83   result->reset(
84       new SimulatedHybridRaf(result->release(), rate_limiter_, temperature));
85   return s;
86 }
87 
NewWritableFile(const std::string & fname,const FileOptions & file_opts,std::unique_ptr<FSWritableFile> * result,IODebugContext * dbg)88 IOStatus SimulatedHybridFileSystem::NewWritableFile(
89     const std::string& fname, const FileOptions& file_opts,
90     std::unique_ptr<FSWritableFile>* result, IODebugContext* dbg) {
91   if (file_opts.temperature == Temperature::kWarm) {
92     const std::lock_guard<std::mutex> lock(mutex_);
93     fprintf(stderr, "warm file %s\n", fname.c_str());
94     warm_file_set_.insert(fname);
95   }
96   return target()->NewWritableFile(fname, file_opts, result, dbg);
97 }
98 
DeleteFile(const std::string & fname,const IOOptions & options,IODebugContext * dbg)99 IOStatus SimulatedHybridFileSystem::DeleteFile(const std::string& fname,
100                                                const IOOptions& options,
101                                                IODebugContext* dbg) {
102   {
103     const std::lock_guard<std::mutex> lock(mutex_);
104     warm_file_set_.erase(fname);
105   }
106   return target()->DeleteFile(fname, options, dbg);
107 }
108 
Read(uint64_t offset,size_t n,const IOOptions & options,Slice * result,char * scratch,IODebugContext * dbg) const109 IOStatus SimulatedHybridRaf::Read(uint64_t offset, size_t n,
110                                   const IOOptions& options, Slice* result,
111                                   char* scratch, IODebugContext* dbg) const {
112   if (temperature_ == Temperature::kWarm) {
113     Env::Default()->SleepForMicroseconds(kLatencyAddedPerRequestUs);
114     RequestRateLimit(1);
115   }
116   return target()->Read(offset, n, options, result, scratch, dbg);
117 }
118 
MultiRead(FSReadRequest * reqs,size_t num_reqs,const IOOptions & options,IODebugContext * dbg)119 IOStatus SimulatedHybridRaf::MultiRead(FSReadRequest* reqs, size_t num_reqs,
120                                        const IOOptions& options,
121                                        IODebugContext* dbg) {
122   if (temperature_ == Temperature::kWarm) {
123     RequestRateLimit(static_cast<int64_t>(num_reqs));
124     Env::Default()->SleepForMicroseconds(kLatencyAddedPerRequestUs *
125                                          static_cast<int>(num_reqs));
126   }
127   return target()->MultiRead(reqs, num_reqs, options, dbg);
128 }
129 
Prefetch(uint64_t offset,size_t n,const IOOptions & options,IODebugContext * dbg)130 IOStatus SimulatedHybridRaf::Prefetch(uint64_t offset, size_t n,
131                                       const IOOptions& options,
132                                       IODebugContext* dbg) {
133   if (temperature_ == Temperature::kWarm) {
134     RequestRateLimit(1);
135     Env::Default()->SleepForMicroseconds(kLatencyAddedPerRequestUs);
136   }
137   return target()->Prefetch(offset, n, options, dbg);
138 }
139 
RequestRateLimit(int64_t num_requests) const140 void SimulatedHybridRaf::RequestRateLimit(int64_t num_requests) const {
141   int64_t left = num_requests * kDummyBytesPerRequest;
142   const int64_t kMaxToRequest = kDummyBytesPerRequest / 100;
143   while (left > 0) {
144     int64_t to_request = std::min(kMaxToRequest, left);
145     rate_limiter_->Request(to_request, Env::IOPriority::IO_LOW, nullptr);
146     left -= to_request;
147   }
148 }
149 
150 }  // namespace ROCKSDB_NAMESPACE
151 
152 #endif  // ROCKSDB_LITE
153