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