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