1 // Copyright (c) 2020-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 "test_util/testharness.h"
7
8 #ifdef ROCKSDB_LIB_IO_POSIX
9 #include "env/io_posix.h"
10
11 namespace ROCKSDB_NAMESPACE {
12
13 #ifdef OS_LINUX
14 class LogicalBlockSizeCacheTest : public testing::Test {};
15
16 // Tests the caching behavior.
TEST_F(LogicalBlockSizeCacheTest,Cache)17 TEST_F(LogicalBlockSizeCacheTest, Cache) {
18 int ncall = 0;
19 auto get_fd_block_size = [&](int fd) {
20 ncall++;
21 return fd;
22 };
23 std::map<std::string, int> dir_fds{
24 {"/", 0},
25 {"/db", 1},
26 {"/db1", 2},
27 {"/db2", 3},
28 };
29 auto get_dir_block_size = [&](const std::string& dir, size_t* size) {
30 ncall++;
31 *size = dir_fds[dir];
32 return Status::OK();
33 };
34 LogicalBlockSizeCache cache(get_fd_block_size, get_dir_block_size);
35 ASSERT_EQ(0, ncall);
36 ASSERT_EQ(0, cache.Size());
37
38 ASSERT_EQ(6, cache.GetLogicalBlockSize("/sst", 6));
39 ASSERT_EQ(1, ncall);
40 ASSERT_EQ(7, cache.GetLogicalBlockSize("/db/sst1", 7));
41 ASSERT_EQ(2, ncall);
42 ASSERT_EQ(8, cache.GetLogicalBlockSize("/db/sst2", 8));
43 ASSERT_EQ(3, ncall);
44
45 ASSERT_OK(cache.RefAndCacheLogicalBlockSize({"/", "/db1/", "/db2"}));
46 ASSERT_EQ(3, cache.Size());
47 ASSERT_TRUE(cache.Contains("/"));
48 ASSERT_TRUE(cache.Contains("/db1"));
49 ASSERT_TRUE(cache.Contains("/db2"));
50 ASSERT_EQ(6, ncall);
51 // Block size for / is cached.
52 ASSERT_EQ(0, cache.GetLogicalBlockSize("/sst", 6));
53 ASSERT_EQ(6, ncall);
54 // No cached size for /db.
55 ASSERT_EQ(7, cache.GetLogicalBlockSize("/db/sst1", 7));
56 ASSERT_EQ(7, ncall);
57 ASSERT_EQ(8, cache.GetLogicalBlockSize("/db/sst2", 8));
58 ASSERT_EQ(8, ncall);
59 // Block size for /db1 is cached.
60 ASSERT_EQ(2, cache.GetLogicalBlockSize("/db1/sst1", 4));
61 ASSERT_EQ(8, ncall);
62 ASSERT_EQ(2, cache.GetLogicalBlockSize("/db1/sst2", 5));
63 ASSERT_EQ(8, ncall);
64 // Block size for /db2 is cached.
65 ASSERT_EQ(3, cache.GetLogicalBlockSize("/db2/sst1", 6));
66 ASSERT_EQ(8, ncall);
67 ASSERT_EQ(3, cache.GetLogicalBlockSize("/db2/sst2", 7));
68 ASSERT_EQ(8, ncall);
69
70 ASSERT_OK(cache.RefAndCacheLogicalBlockSize({"/db"}));
71 ASSERT_EQ(4, cache.Size());
72 ASSERT_TRUE(cache.Contains("/"));
73 ASSERT_TRUE(cache.Contains("/db1"));
74 ASSERT_TRUE(cache.Contains("/db2"));
75 ASSERT_TRUE(cache.Contains("/db"));
76
77 ASSERT_EQ(9, ncall);
78 // Block size for /db is cached.
79 ASSERT_EQ(1, cache.GetLogicalBlockSize("/db/sst1", 7));
80 ASSERT_EQ(9, ncall);
81 ASSERT_EQ(1, cache.GetLogicalBlockSize("/db/sst2", 8));
82 ASSERT_EQ(9, ncall);
83 }
84
85 // Tests the reference counting behavior.
TEST_F(LogicalBlockSizeCacheTest,Ref)86 TEST_F(LogicalBlockSizeCacheTest, Ref) {
87 int ncall = 0;
88 auto get_fd_block_size = [&](int fd) {
89 ncall++;
90 return fd;
91 };
92 std::map<std::string, int> dir_fds{
93 {"/db", 0},
94 };
95 auto get_dir_block_size = [&](const std::string& dir, size_t* size) {
96 ncall++;
97 *size = dir_fds[dir];
98 return Status::OK();
99 };
100 LogicalBlockSizeCache cache(get_fd_block_size, get_dir_block_size);
101
102 ASSERT_EQ(0, ncall);
103
104 ASSERT_EQ(1, cache.GetLogicalBlockSize("/db/sst0", 1));
105 ASSERT_EQ(1, ncall);
106
107 ASSERT_OK(cache.RefAndCacheLogicalBlockSize({"/db"}));
108 ASSERT_EQ(2, ncall);
109 ASSERT_EQ(1, cache.GetRefCount("/db"));
110 // Block size for /db is cached. Ref count = 1.
111 ASSERT_EQ(0, cache.GetLogicalBlockSize("/db/sst1", 1));
112 ASSERT_EQ(2, ncall);
113
114 // Ref count = 2, but won't recompute the cached buffer size.
115 ASSERT_OK(cache.RefAndCacheLogicalBlockSize({"/db"}));
116 ASSERT_EQ(2, cache.GetRefCount("/db"));
117 ASSERT_EQ(2, ncall);
118
119 // Ref count = 1.
120 cache.UnrefAndTryRemoveCachedLogicalBlockSize({"/db"});
121 ASSERT_EQ(1, cache.GetRefCount("/db"));
122 // Block size for /db is still cached.
123 ASSERT_EQ(0, cache.GetLogicalBlockSize("/db/sst2", 1));
124 ASSERT_EQ(2, ncall);
125
126 // Ref count = 0 and cached buffer size for /db is removed.
127 cache.UnrefAndTryRemoveCachedLogicalBlockSize({"/db"});
128 ASSERT_EQ(0, cache.Size());
129 ASSERT_EQ(1, cache.GetLogicalBlockSize("/db/sst0", 1));
130 ASSERT_EQ(3, ncall);
131 }
132 #endif
133
134 } // namespace ROCKSDB_NAMESPACE
135 #endif
136
main(int argc,char ** argv)137 int main(int argc, char** argv) {
138 ::testing::InitGoogleTest(&argc, argv);
139 return RUN_ALL_TESTS();
140 }
141