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