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 <string>
7 
8 #include "db/db_test_util.h"
9 #include "monitoring/thread_status_util.h"
10 #include "port/stack_trace.h"
11 #include "rocksdb/statistics.h"
12 #include "util/random.h"
13 
14 namespace ROCKSDB_NAMESPACE {
15 
16 class DBStatisticsTest : public DBTestBase {
17  public:
DBStatisticsTest()18   DBStatisticsTest()
19       : DBTestBase("db_statistics_test", /*env_do_fsync=*/true) {}
20 };
21 
TEST_F(DBStatisticsTest,CompressionStatsTest)22 TEST_F(DBStatisticsTest, CompressionStatsTest) {
23   CompressionType type;
24 
25   if (Snappy_Supported()) {
26     type = kSnappyCompression;
27     fprintf(stderr, "using snappy\n");
28   } else if (Zlib_Supported()) {
29     type = kZlibCompression;
30     fprintf(stderr, "using zlib\n");
31   } else if (BZip2_Supported()) {
32     type = kBZip2Compression;
33     fprintf(stderr, "using bzip2\n");
34   } else if (LZ4_Supported()) {
35     type = kLZ4Compression;
36     fprintf(stderr, "using lz4\n");
37   } else if (XPRESS_Supported()) {
38     type = kXpressCompression;
39     fprintf(stderr, "using xpress\n");
40   } else if (ZSTD_Supported()) {
41     type = kZSTD;
42     fprintf(stderr, "using ZSTD\n");
43   } else {
44     fprintf(stderr, "skipping test, compression disabled\n");
45     return;
46   }
47 
48   Options options = CurrentOptions();
49   options.compression = type;
50   options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
51   options.statistics->set_stats_level(StatsLevel::kExceptTimeForMutex);
52   DestroyAndReopen(options);
53 
54   int kNumKeysWritten = 100000;
55 
56   // Check that compressions occur and are counted when compression is turned on
57   Random rnd(301);
58   for (int i = 0; i < kNumKeysWritten; ++i) {
59     // compressible string
60     ASSERT_OK(Put(Key(i), rnd.RandomString(128) + std::string(128, 'a')));
61   }
62   ASSERT_OK(Flush());
63   ASSERT_GT(options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED), 0);
64 
65   for (int i = 0; i < kNumKeysWritten; ++i) {
66     auto r = Get(Key(i));
67   }
68   ASSERT_GT(options.statistics->getTickerCount(NUMBER_BLOCK_DECOMPRESSED), 0);
69 
70   options.compression = kNoCompression;
71   DestroyAndReopen(options);
72   uint64_t currentCompressions =
73             options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED);
74   uint64_t currentDecompressions =
75             options.statistics->getTickerCount(NUMBER_BLOCK_DECOMPRESSED);
76 
77   // Check that compressions do not occur when turned off
78   for (int i = 0; i < kNumKeysWritten; ++i) {
79     // compressible string
80     ASSERT_OK(Put(Key(i), rnd.RandomString(128) + std::string(128, 'a')));
81   }
82   ASSERT_OK(Flush());
83   ASSERT_EQ(options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED)
84             - currentCompressions, 0);
85 
86   for (int i = 0; i < kNumKeysWritten; ++i) {
87     auto r = Get(Key(i));
88   }
89   ASSERT_EQ(options.statistics->getTickerCount(NUMBER_BLOCK_DECOMPRESSED)
90             - currentDecompressions, 0);
91 }
92 
TEST_F(DBStatisticsTest,MutexWaitStatsDisabledByDefault)93 TEST_F(DBStatisticsTest, MutexWaitStatsDisabledByDefault) {
94   Options options = CurrentOptions();
95   options.create_if_missing = true;
96   options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
97   CreateAndReopenWithCF({"pikachu"}, options);
98   const uint64_t kMutexWaitDelay = 100;
99   ThreadStatusUtil::TEST_SetStateDelay(ThreadStatus::STATE_MUTEX_WAIT,
100                                        kMutexWaitDelay);
101   ASSERT_OK(Put("hello", "rocksdb"));
102   ASSERT_EQ(TestGetTickerCount(options, DB_MUTEX_WAIT_MICROS), 0);
103   ThreadStatusUtil::TEST_SetStateDelay(ThreadStatus::STATE_MUTEX_WAIT, 0);
104 }
105 
TEST_F(DBStatisticsTest,MutexWaitStats)106 TEST_F(DBStatisticsTest, MutexWaitStats) {
107   Options options = CurrentOptions();
108   options.create_if_missing = true;
109   options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
110   options.statistics->set_stats_level(StatsLevel::kAll);
111   CreateAndReopenWithCF({"pikachu"}, options);
112   const uint64_t kMutexWaitDelay = 100;
113   ThreadStatusUtil::TEST_SetStateDelay(ThreadStatus::STATE_MUTEX_WAIT,
114                                        kMutexWaitDelay);
115   ASSERT_OK(Put("hello", "rocksdb"));
116   ASSERT_GE(TestGetTickerCount(options, DB_MUTEX_WAIT_MICROS), kMutexWaitDelay);
117   ThreadStatusUtil::TEST_SetStateDelay(ThreadStatus::STATE_MUTEX_WAIT, 0);
118 }
119 
TEST_F(DBStatisticsTest,ResetStats)120 TEST_F(DBStatisticsTest, ResetStats) {
121   Options options = CurrentOptions();
122   options.create_if_missing = true;
123   options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
124   DestroyAndReopen(options);
125   for (int i = 0; i < 2; ++i) {
126     // pick arbitrary ticker and histogram. On first iteration they're zero
127     // because db is unused. On second iteration they're zero due to Reset().
128     ASSERT_EQ(0, TestGetTickerCount(options, NUMBER_KEYS_WRITTEN));
129     HistogramData histogram_data;
130     options.statistics->histogramData(DB_WRITE, &histogram_data);
131     ASSERT_EQ(0.0, histogram_data.max);
132 
133     if (i == 0) {
134       // The Put() makes some of the ticker/histogram stats nonzero until we
135       // Reset().
136       ASSERT_OK(Put("hello", "rocksdb"));
137       ASSERT_EQ(1, TestGetTickerCount(options, NUMBER_KEYS_WRITTEN));
138       options.statistics->histogramData(DB_WRITE, &histogram_data);
139       ASSERT_GT(histogram_data.max, 0.0);
140       ASSERT_OK(options.statistics->Reset());
141     }
142   }
143 }
144 
TEST_F(DBStatisticsTest,ExcludeTickers)145 TEST_F(DBStatisticsTest, ExcludeTickers) {
146   Options options = CurrentOptions();
147   options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
148   DestroyAndReopen(options);
149   options.statistics->set_stats_level(StatsLevel::kExceptTickers);
150   ASSERT_OK(Put("foo", "value"));
151   ASSERT_EQ(0, options.statistics->getTickerCount(BYTES_WRITTEN));
152   options.statistics->set_stats_level(StatsLevel::kExceptHistogramOrTimers);
153   Reopen(options);
154   ASSERT_EQ("value", Get("foo"));
155   ASSERT_GT(options.statistics->getTickerCount(BYTES_READ), 0);
156 }
157 
158 #ifndef ROCKSDB_LITE
159 
TEST_F(DBStatisticsTest,VerifyChecksumReadStat)160 TEST_F(DBStatisticsTest, VerifyChecksumReadStat) {
161   Options options = CurrentOptions();
162   options.file_checksum_gen_factory = GetFileChecksumGenCrc32cFactory();
163   options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
164   Reopen(options);
165 
166   // Expected to be populated regardless of `PerfLevel` in user thread
167   SetPerfLevel(kDisable);
168 
169   {
170     // Scenario 0: only WAL data. Not verified so require ticker to be zero.
171     ASSERT_OK(Put("foo", "value"));
172     ASSERT_OK(db_->VerifyFileChecksums(ReadOptions()));
173     ASSERT_OK(db_->VerifyChecksum());
174     ASSERT_EQ(0,
175               options.statistics->getTickerCount(VERIFY_CHECKSUM_READ_BYTES));
176   }
177 
178   // Create one SST.
179   ASSERT_OK(Flush());
180   std::unordered_map<std::string, uint64_t> table_files;
181   uint64_t table_files_size = 0;
182   GetAllDataFiles(kTableFile, &table_files, &table_files_size);
183 
184   {
185     // Scenario 1: Table verified in `VerifyFileChecksums()`. This should read
186     // the whole file so we require the ticker stat exactly matches the file
187     // size.
188     ASSERT_OK(options.statistics->Reset());
189     ASSERT_OK(db_->VerifyFileChecksums(ReadOptions()));
190     ASSERT_EQ(table_files_size,
191               options.statistics->getTickerCount(VERIFY_CHECKSUM_READ_BYTES));
192   }
193 
194   {
195     // Scenario 2: Table verified in `VerifyChecksum()`. This opens a
196     // `TableReader` to verify each block. It can involve duplicate reads of the
197     // same data so we set a lower-bound only.
198     ASSERT_OK(options.statistics->Reset());
199     ASSERT_OK(db_->VerifyChecksum());
200     ASSERT_GE(options.statistics->getTickerCount(VERIFY_CHECKSUM_READ_BYTES),
201               table_files_size);
202   }
203 }
204 
205 #endif  // !ROCKSDB_LITE
206 
207 }  // namespace ROCKSDB_NAMESPACE
208 
main(int argc,char ** argv)209 int main(int argc, char** argv) {
210   ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
211   ::testing::InitGoogleTest(&argc, argv);
212   return RUN_ALL_TESTS();
213 }
214