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 "port/port.h"
7 #include "port/stack_trace.h"
8 #include "rocksdb/file_system.h"
9 #include "file/random_access_file_reader.h"
10 #include "test_util/testharness.h"
11 #include "test_util/testutil.h"
12 
13 namespace ROCKSDB_NAMESPACE {
14 
15 class RandomAccessFileReaderTest : public testing::Test {
16  public:
SetUp()17   void SetUp() override {
18 #ifdef OS_LINUX
19     // TEST_TMPDIR may be set to /dev/shm in Makefile,
20     // but /dev/shm does not support direct IO.
21     // The default TEST_TMPDIR is under /tmp, but /tmp might also be a tmpfs
22     // which does not support direct IO neither.
23     unsetenv("TEST_TMPDIR");
24     char* tmpdir = getenv("DISK_TEMP_DIR");
25     if (tmpdir == nullptr) {
26       tmpdir = getenv("HOME");
27     }
28     if (tmpdir != nullptr) {
29       setenv("TEST_TMPDIR", tmpdir, 1);
30     }
31 #endif
32     env_ = Env::Default();
33     fs_ = FileSystem::Default();
34     test_dir_ = test::PerThreadDBPath("random_access_file_reader_test");
35     ASSERT_OK(fs_->CreateDir(test_dir_, IOOptions(), nullptr));
36     alignment_ = GetAlignment();
37   }
38 
TearDown()39   void TearDown() override {
40     EXPECT_OK(test::DestroyDir(env_, test_dir_));
41   }
42 
IsDirectIOSupported()43   bool IsDirectIOSupported() {
44     Write(".direct", "");
45     FileOptions opt;
46     opt.use_direct_reads = true;
47     std::unique_ptr<FSRandomAccessFile> f;
48     auto s = fs_->NewRandomAccessFile(Path(".direct"), opt, &f, nullptr);
49     return s.ok();
50   }
51 
Write(const std::string & fname,const std::string & content)52   void Write(const std::string& fname, const std::string& content) {
53     std::unique_ptr<FSWritableFile> f;
54     ASSERT_OK(fs_->NewWritableFile(Path(fname), FileOptions(), &f, nullptr));
55     ASSERT_OK(f->Append(content, IOOptions(), nullptr));
56     ASSERT_OK(f->Close(IOOptions(), nullptr));
57   }
58 
Read(const std::string & fname,const FileOptions & opts,std::unique_ptr<RandomAccessFileReader> * reader)59   void Read(const std::string& fname, const FileOptions& opts,
60                 std::unique_ptr<RandomAccessFileReader>* reader) {
61     std::string fpath = Path(fname);
62     std::unique_ptr<FSRandomAccessFile> f;
63     ASSERT_OK(fs_->NewRandomAccessFile(fpath, opts, &f, nullptr));
64     (*reader).reset(new RandomAccessFileReader(std::move(f), fpath, env_));
65   }
66 
AssertResult(const std::string & content,const std::vector<FSReadRequest> & reqs)67   void AssertResult(const std::string& content,
68                     const std::vector<FSReadRequest>& reqs) {
69     for (const auto& r : reqs) {
70       ASSERT_OK(r.status);
71       ASSERT_EQ(r.len, r.result.size());
72       ASSERT_EQ(content.substr(r.offset, r.len), r.result.ToString());
73     }
74   }
75 
alignment() const76   size_t alignment() const { return alignment_; }
77 
78  private:
79   Env* env_;
80   std::shared_ptr<FileSystem> fs_;
81   std::string test_dir_;
82   size_t alignment_;
83 
Path(const std::string & fname)84   std::string Path(const std::string& fname) {
85     return test_dir_ + "/" + fname;
86   }
87 
GetAlignment()88   size_t GetAlignment() {
89     std::string f = "get_alignment";
90     Write(f, "");
91     std::unique_ptr<RandomAccessFileReader> r;
92     Read(f, FileOptions(), &r);
93     size_t alignment = r->file()->GetRequiredBufferAlignment();
94     EXPECT_OK(fs_->DeleteFile(Path(f), IOOptions(), nullptr));
95     return alignment;
96   }
97 };
98 
TEST_F(RandomAccessFileReaderTest,MultiReadDirectIO)99 TEST_F(RandomAccessFileReaderTest, MultiReadDirectIO) {
100   if (!IsDirectIOSupported()) {
101     printf("Direct IO is not supported, skip this test\n");
102     return;
103   }
104 
105   // Creates a file with 3 pages.
106   std::string fname = "multi-read-direct-io";
107   Random rand(0);
108   std::string content;
109   test::RandomString(&rand, 3 * static_cast<int>(alignment()), &content);
110   Write(fname, content);
111 
112   FileOptions opts;
113   opts.use_direct_reads = true;
114   std::unique_ptr<RandomAccessFileReader> r;
115   Read(fname, opts, &r);
116   ASSERT_TRUE(r->use_direct_io());
117 
118   {
119     // Reads 2 blocks in the 1st page.
120     // The results should be SharedSlices of the same underlying buffer.
121     //
122     // Illustration (each x is a 1/4 page)
123     // First page: xxxx
124     // 1st block:  x
125     // 2nd block:    xx
126     FSReadRequest r0;
127     r0.offset = 0;
128     r0.len = alignment() / 4;
129     r0.scratch = nullptr;
130 
131     FSReadRequest r1;
132     r1.offset = alignment() / 2;
133     r1.len = alignment() / 2;
134     r1.scratch = nullptr;
135 
136     std::vector<FSReadRequest> reqs;
137     reqs.push_back(std::move(r0));
138     reqs.push_back(std::move(r1));
139     AlignedBuf aligned_buf;
140     ASSERT_OK(r->MultiRead(reqs.data(), reqs.size(), &aligned_buf));
141 
142     AssertResult(content, reqs);
143   }
144 
145   {
146     // Reads 3 blocks:
147     // 1st block in the 1st page;
148     // 2nd block from the middle of the 1st page to the middle of the 2nd page;
149     // 3rd block in the 2nd page.
150     // The results should be SharedSlices of the same underlying buffer.
151     //
152     // Illustration (each x is a 1/4 page)
153     // 2 pages:   xxxxxxxx
154     // 1st block: x
155     // 2nd block:   xxxx
156     // 3rd block:        x
157     FSReadRequest r0;
158     r0.offset = 0;
159     r0.len = alignment() / 4;
160     r0.scratch = nullptr;
161 
162     FSReadRequest r1;
163     r1.offset = alignment() / 2;
164     r1.len = alignment();
165     r1.scratch = nullptr;
166 
167     FSReadRequest r2;
168     r2.offset = 2 * alignment() - alignment() / 4;
169     r2.len = alignment() / 4;
170     r2.scratch = nullptr;
171 
172     std::vector<FSReadRequest> reqs;
173     reqs.push_back(std::move(r0));
174     reqs.push_back(std::move(r1));
175     reqs.push_back(std::move(r2));
176     AlignedBuf aligned_buf;
177     ASSERT_OK(r->MultiRead(reqs.data(), reqs.size(), &aligned_buf));
178 
179     AssertResult(content, reqs);
180   }
181 
182   {
183     // Reads 3 blocks:
184     // 1st block in the middle of the 1st page;
185     // 2nd block in the middle of the 2nd page;
186     // 3rd block in the middle of the 3rd page.
187     // The results should be SharedSlices of the same underlying buffer.
188     //
189     // Illustration (each x is a 1/4 page)
190     // 3 pages:   xxxxxxxxxxxx
191     // 1st block:  xx
192     // 2nd block:      xx
193     // 3rd block:          xx
194     FSReadRequest r0;
195     r0.offset = alignment() / 4;
196     r0.len = alignment() / 2;
197     r0.scratch = nullptr;
198 
199     FSReadRequest r1;
200     r1.offset = alignment() + alignment() / 4;
201     r1.len = alignment() / 2;
202     r1.scratch = nullptr;
203 
204     FSReadRequest r2;
205     r2.offset = 2 * alignment() + alignment() / 4;
206     r2.len = alignment() / 2;
207     r2.scratch = nullptr;
208 
209     std::vector<FSReadRequest> reqs;
210     reqs.push_back(std::move(r0));
211     reqs.push_back(std::move(r1));
212     reqs.push_back(std::move(r2));
213     AlignedBuf aligned_buf;
214     ASSERT_OK(r->MultiRead(reqs.data(), reqs.size(), &aligned_buf));
215 
216     AssertResult(content, reqs);
217   }
218 
219   {
220     // Reads 2 blocks:
221     // 1st block in the middle of the 1st page;
222     // 2nd block in the middle of the 3rd page.
223     // The results are two different buffers.
224     //
225     // Illustration (each x is a 1/4 page)
226     // 3 pages:   xxxxxxxxxxxx
227     // 1st block:  xx
228     // 2nd block:          xx
229     FSReadRequest r0;
230     r0.offset = alignment() / 4;
231     r0.len = alignment() / 2;
232     r0.scratch = nullptr;
233 
234     FSReadRequest r1;
235     r1.offset = 2 * alignment() + alignment() / 4;
236     r1.len = alignment() / 2;
237     r1.scratch = nullptr;
238 
239     std::vector<FSReadRequest> reqs;
240     reqs.push_back(std::move(r0));
241     reqs.push_back(std::move(r1));
242     AlignedBuf aligned_buf;
243     ASSERT_OK(r->MultiRead(reqs.data(), reqs.size(), &aligned_buf));
244 
245     AssertResult(content, reqs);
246   }
247 }
248 
249 }  // namespace ROCKSDB_NAMESPACE
250 
main(int argc,char ** argv)251 int main(int argc, char** argv) {
252   ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
253   ::testing::InitGoogleTest(&argc, argv);
254   return RUN_ALL_TESTS();
255 }
256