]> git.proxmox.com Git - ceph.git/blame - ceph/src/rocksdb/db/db_properties_test.cc
import 14.2.4 nautilus point release
[ceph.git] / ceph / src / rocksdb / db / db_properties_test.cc
CommitLineData
7c673cae 1// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
11fdf7f2
TL
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).
7c673cae
FG
5//
6// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
7// Use of this source code is governed by a BSD-style license that can be
8// found in the LICENSE file. See the AUTHORS file for names of contributors.
9
10#include <stdio.h>
11
12#include <algorithm>
13#include <string>
14
15#include "db/db_test_util.h"
16#include "port/stack_trace.h"
11fdf7f2 17#include "rocksdb/listener.h"
7c673cae
FG
18#include "rocksdb/options.h"
19#include "rocksdb/perf_context.h"
20#include "rocksdb/perf_level.h"
21#include "rocksdb/table.h"
22#include "util/random.h"
23#include "util/string_util.h"
24
25namespace rocksdb {
26
27class DBPropertiesTest : public DBTestBase {
28 public:
29 DBPropertiesTest() : DBTestBase("/db_properties_test") {}
30};
31
32#ifndef ROCKSDB_LITE
33TEST_F(DBPropertiesTest, Empty) {
34 do {
35 Options options;
36 options.env = env_;
37 options.write_buffer_size = 100000; // Small write buffer
38 options.allow_concurrent_memtable_write = false;
39 options = CurrentOptions(options);
40 CreateAndReopenWithCF({"pikachu"}, options);
41
42 std::string num;
43 ASSERT_TRUE(dbfull()->GetProperty(
44 handles_[1], "rocksdb.num-entries-active-mem-table", &num));
45 ASSERT_EQ("0", num);
46
47 ASSERT_OK(Put(1, "foo", "v1"));
48 ASSERT_EQ("v1", Get(1, "foo"));
49 ASSERT_TRUE(dbfull()->GetProperty(
50 handles_[1], "rocksdb.num-entries-active-mem-table", &num));
51 ASSERT_EQ("1", num);
52
53 // Block sync calls
54 env_->delay_sstable_sync_.store(true, std::memory_order_release);
55 Put(1, "k1", std::string(100000, 'x')); // Fill memtable
56 ASSERT_TRUE(dbfull()->GetProperty(
57 handles_[1], "rocksdb.num-entries-active-mem-table", &num));
58 ASSERT_EQ("2", num);
59
60 Put(1, "k2", std::string(100000, 'y')); // Trigger compaction
61 ASSERT_TRUE(dbfull()->GetProperty(
62 handles_[1], "rocksdb.num-entries-active-mem-table", &num));
63 ASSERT_EQ("1", num);
64
65 ASSERT_EQ("v1", Get(1, "foo"));
66 // Release sync calls
67 env_->delay_sstable_sync_.store(false, std::memory_order_release);
68
69 ASSERT_OK(db_->DisableFileDeletions());
70 ASSERT_TRUE(
71 dbfull()->GetProperty("rocksdb.is-file-deletions-enabled", &num));
11fdf7f2 72 ASSERT_EQ("0", num);
7c673cae
FG
73
74 ASSERT_OK(db_->DisableFileDeletions());
75 ASSERT_TRUE(
76 dbfull()->GetProperty("rocksdb.is-file-deletions-enabled", &num));
11fdf7f2 77 ASSERT_EQ("0", num);
7c673cae
FG
78
79 ASSERT_OK(db_->DisableFileDeletions());
80 ASSERT_TRUE(
81 dbfull()->GetProperty("rocksdb.is-file-deletions-enabled", &num));
11fdf7f2 82 ASSERT_EQ("0", num);
7c673cae
FG
83
84 ASSERT_OK(db_->EnableFileDeletions(false));
85 ASSERT_TRUE(
86 dbfull()->GetProperty("rocksdb.is-file-deletions-enabled", &num));
11fdf7f2 87 ASSERT_EQ("0", num);
7c673cae
FG
88
89 ASSERT_OK(db_->EnableFileDeletions());
90 ASSERT_TRUE(
91 dbfull()->GetProperty("rocksdb.is-file-deletions-enabled", &num));
11fdf7f2 92 ASSERT_EQ("1", num);
7c673cae
FG
93 } while (ChangeOptions());
94}
95
96TEST_F(DBPropertiesTest, CurrentVersionNumber) {
97 uint64_t v1, v2, v3;
98 ASSERT_TRUE(
99 dbfull()->GetIntProperty("rocksdb.current-super-version-number", &v1));
100 Put("12345678", "");
101 ASSERT_TRUE(
102 dbfull()->GetIntProperty("rocksdb.current-super-version-number", &v2));
103 Flush();
104 ASSERT_TRUE(
105 dbfull()->GetIntProperty("rocksdb.current-super-version-number", &v3));
106
107 ASSERT_EQ(v1, v2);
108 ASSERT_GT(v3, v2);
109}
110
111TEST_F(DBPropertiesTest, GetAggregatedIntPropertyTest) {
112 const int kKeySize = 100;
113 const int kValueSize = 500;
114 const int kKeyNum = 100;
115
116 Options options;
117 options.env = env_;
118 options.create_if_missing = true;
119 options.write_buffer_size = (kKeySize + kValueSize) * kKeyNum / 10;
120 // Make them never flush
121 options.min_write_buffer_number_to_merge = 1000;
122 options.max_write_buffer_number = 1000;
123 options = CurrentOptions(options);
124 CreateAndReopenWithCF({"one", "two", "three", "four"}, options);
125
126 Random rnd(301);
127 for (auto* handle : handles_) {
128 for (int i = 0; i < kKeyNum; ++i) {
129 db_->Put(WriteOptions(), handle, RandomString(&rnd, kKeySize),
130 RandomString(&rnd, kValueSize));
131 }
132 }
133
134 uint64_t manual_sum = 0;
135 uint64_t api_sum = 0;
136 uint64_t value = 0;
137 for (auto* handle : handles_) {
138 ASSERT_TRUE(
139 db_->GetIntProperty(handle, DB::Properties::kSizeAllMemTables, &value));
140 manual_sum += value;
141 }
142 ASSERT_TRUE(db_->GetAggregatedIntProperty(DB::Properties::kSizeAllMemTables,
143 &api_sum));
144 ASSERT_GT(manual_sum, 0);
145 ASSERT_EQ(manual_sum, api_sum);
146
147 ASSERT_FALSE(db_->GetAggregatedIntProperty(DB::Properties::kDBStats, &value));
148
149 uint64_t before_flush_trm;
150 uint64_t after_flush_trm;
151 for (auto* handle : handles_) {
152 ASSERT_TRUE(db_->GetAggregatedIntProperty(
153 DB::Properties::kEstimateTableReadersMem, &before_flush_trm));
154
155 // Issue flush and expect larger memory usage of table readers.
156 db_->Flush(FlushOptions(), handle);
157
158 ASSERT_TRUE(db_->GetAggregatedIntProperty(
159 DB::Properties::kEstimateTableReadersMem, &after_flush_trm));
160 ASSERT_GT(after_flush_trm, before_flush_trm);
161 }
162}
163
164namespace {
165void ResetTableProperties(TableProperties* tp) {
166 tp->data_size = 0;
167 tp->index_size = 0;
168 tp->filter_size = 0;
169 tp->raw_key_size = 0;
170 tp->raw_value_size = 0;
171 tp->num_data_blocks = 0;
172 tp->num_entries = 0;
494da23a
TL
173 tp->num_deletions = 0;
174 tp->num_merge_operands = 0;
11fdf7f2 175 tp->num_range_deletions = 0;
7c673cae
FG
176}
177
178void ParseTablePropertiesString(std::string tp_string, TableProperties* tp) {
179 double dummy_double;
180 std::replace(tp_string.begin(), tp_string.end(), ';', ' ');
181 std::replace(tp_string.begin(), tp_string.end(), '=', ' ');
182 ResetTableProperties(tp);
7c673cae 183 sscanf(tp_string.c_str(),
494da23a
TL
184 "# data blocks %" SCNu64 " # entries %" SCNu64 " # deletions %" SCNu64
185 " # merge operands %" SCNu64 " # range deletions %" SCNu64
186 " raw key size %" SCNu64
7c673cae
FG
187 " raw average key size %lf "
188 " raw value size %" SCNu64
189 " raw average value size %lf "
11fdf7f2
TL
190 " data block size %" SCNu64 " index block size (user-key? %" SCNu64
191 ", delta-value? %" SCNu64 ") %" SCNu64 " filter block size %" SCNu64,
494da23a
TL
192 &tp->num_data_blocks, &tp->num_entries, &tp->num_deletions,
193 &tp->num_merge_operands, &tp->num_range_deletions, &tp->raw_key_size,
194 &dummy_double, &tp->raw_value_size, &dummy_double, &tp->data_size,
195 &tp->index_key_is_user_key, &tp->index_value_is_delta_encoded,
196 &tp->index_size, &tp->filter_size);
7c673cae
FG
197}
198
199void VerifySimilar(uint64_t a, uint64_t b, double bias) {
200 ASSERT_EQ(a == 0U, b == 0U);
201 if (a == 0) {
202 return;
203 }
204 double dbl_a = static_cast<double>(a);
205 double dbl_b = static_cast<double>(b);
206 if (dbl_a > dbl_b) {
207 ASSERT_LT(static_cast<double>(dbl_a - dbl_b) / (dbl_a + dbl_b), bias);
208 } else {
209 ASSERT_LT(static_cast<double>(dbl_b - dbl_a) / (dbl_a + dbl_b), bias);
210 }
211}
212
213void VerifyTableProperties(const TableProperties& base_tp,
214 const TableProperties& new_tp,
215 double filter_size_bias = 0.1,
216 double index_size_bias = 0.1,
217 double data_size_bias = 0.1,
218 double num_data_blocks_bias = 0.05) {
219 VerifySimilar(base_tp.data_size, new_tp.data_size, data_size_bias);
220 VerifySimilar(base_tp.index_size, new_tp.index_size, index_size_bias);
221 VerifySimilar(base_tp.filter_size, new_tp.filter_size, filter_size_bias);
222 VerifySimilar(base_tp.num_data_blocks, new_tp.num_data_blocks,
223 num_data_blocks_bias);
494da23a 224
7c673cae
FG
225 ASSERT_EQ(base_tp.raw_key_size, new_tp.raw_key_size);
226 ASSERT_EQ(base_tp.raw_value_size, new_tp.raw_value_size);
227 ASSERT_EQ(base_tp.num_entries, new_tp.num_entries);
494da23a 228 ASSERT_EQ(base_tp.num_deletions, new_tp.num_deletions);
11fdf7f2 229 ASSERT_EQ(base_tp.num_range_deletions, new_tp.num_range_deletions);
494da23a
TL
230
231 // Merge operands may become Puts, so we only have an upper bound the exact
232 // number of merge operands.
233 ASSERT_GE(base_tp.num_merge_operands, new_tp.num_merge_operands);
7c673cae
FG
234}
235
11fdf7f2
TL
236void GetExpectedTableProperties(
237 TableProperties* expected_tp, const int kKeySize, const int kValueSize,
494da23a
TL
238 const int kPutsPerTable, const int kDeletionsPerTable,
239 const int kMergeOperandsPerTable, const int kRangeDeletionsPerTable,
11fdf7f2
TL
240 const int kTableCount, const int kBloomBitsPerKey, const size_t kBlockSize,
241 const bool index_key_is_user_key, const bool value_delta_encoding) {
494da23a
TL
242 const int kKeysPerTable =
243 kPutsPerTable + kDeletionsPerTable + kMergeOperandsPerTable;
244 const int kPutCount = kTableCount * kPutsPerTable;
245 const int kDeletionCount = kTableCount * kDeletionsPerTable;
246 const int kMergeCount = kTableCount * kMergeOperandsPerTable;
11fdf7f2 247 const int kRangeDeletionCount = kTableCount * kRangeDeletionsPerTable;
494da23a 248 const int kKeyCount = kPutCount + kDeletionCount + kMergeCount + kRangeDeletionCount;
7c673cae
FG
249 const int kAvgSuccessorSize = kKeySize / 5;
250 const int kEncodingSavePerKey = kKeySize / 4;
494da23a
TL
251 expected_tp->raw_key_size = kKeyCount * (kKeySize + 8);
252 expected_tp->raw_value_size =
253 (kPutCount + kMergeCount + kRangeDeletionCount) * kValueSize;
7c673cae 254 expected_tp->num_entries = kKeyCount;
494da23a
TL
255 expected_tp->num_deletions = kDeletionCount + kRangeDeletionCount;
256 expected_tp->num_merge_operands = kMergeCount;
11fdf7f2 257 expected_tp->num_range_deletions = kRangeDeletionCount;
7c673cae 258 expected_tp->num_data_blocks =
494da23a 259 kTableCount * (kKeysPerTable * (kKeySize - kEncodingSavePerKey + kValueSize)) /
7c673cae
FG
260 kBlockSize;
261 expected_tp->data_size =
262 kTableCount * (kKeysPerTable * (kKeySize + 8 + kValueSize));
263 expected_tp->index_size =
11fdf7f2
TL
264 expected_tp->num_data_blocks *
265 (kAvgSuccessorSize + (index_key_is_user_key ? 0 : 8) -
266 // discount 1 byte as value size is not encoded in value delta encoding
267 (value_delta_encoding ? 1 : 0));
7c673cae
FG
268 expected_tp->filter_size =
269 kTableCount * (kKeysPerTable * kBloomBitsPerKey / 8);
270}
271} // anonymous namespace
272
273TEST_F(DBPropertiesTest, ValidatePropertyInfo) {
274 for (const auto& ppt_name_and_info : InternalStats::ppt_name_to_info) {
275 // If C++ gets a std::string_literal, this would be better to check at
276 // compile-time using static_assert.
277 ASSERT_TRUE(ppt_name_and_info.first.empty() ||
278 !isdigit(ppt_name_and_info.first.back()));
279
11fdf7f2
TL
280 int count = 0;
281 count += (ppt_name_and_info.second.handle_string == nullptr) ? 0 : 1;
282 count += (ppt_name_and_info.second.handle_int == nullptr) ? 0 : 1;
283 count += (ppt_name_and_info.second.handle_string_dbimpl == nullptr) ? 0 : 1;
284 ASSERT_TRUE(count == 1);
7c673cae
FG
285 }
286}
287
288TEST_F(DBPropertiesTest, ValidateSampleNumber) {
289 // When "max_open_files" is -1, we read all the files for
290 // "rocksdb.estimate-num-keys" computation, which is the ground truth.
291 // Otherwise, we sample 20 newest files to make an estimation.
292 // Formula: lastest_20_files_active_key_ratio * total_files
293 Options options = CurrentOptions();
294 options.disable_auto_compactions = true;
295 options.level0_stop_writes_trigger = 1000;
296 DestroyAndReopen(options);
297 int key = 0;
298 for (int files = 20; files >= 10; files -= 10) {
299 for (int i = 0; i < files; i++) {
300 int rows = files / 10;
301 for (int j = 0; j < rows; j++) {
302 db_->Put(WriteOptions(), std::to_string(++key), "foo");
303 }
304 db_->Flush(FlushOptions());
305 }
306 }
307 std::string num;
308 Reopen(options);
309 ASSERT_TRUE(dbfull()->GetProperty("rocksdb.estimate-num-keys", &num));
310 ASSERT_EQ("45", num);
311 options.max_open_files = -1;
312 Reopen(options);
313 ASSERT_TRUE(dbfull()->GetProperty("rocksdb.estimate-num-keys", &num));
314 ASSERT_EQ("50", num);
315}
316
317TEST_F(DBPropertiesTest, AggregatedTableProperties) {
318 for (int kTableCount = 40; kTableCount <= 100; kTableCount += 30) {
494da23a
TL
319 const int kDeletionsPerTable = 5;
320 const int kMergeOperandsPerTable = 15;
11fdf7f2 321 const int kRangeDeletionsPerTable = 5;
494da23a 322 const int kPutsPerTable = 100;
7c673cae
FG
323 const int kKeySize = 80;
324 const int kValueSize = 200;
325 const int kBloomBitsPerKey = 20;
326
327 Options options = CurrentOptions();
328 options.level0_file_num_compaction_trigger = 8;
329 options.compression = kNoCompression;
330 options.create_if_missing = true;
494da23a
TL
331 options.preserve_deletes = true;
332 options.merge_operator.reset(new TestPutOperator());
7c673cae
FG
333
334 BlockBasedTableOptions table_options;
335 table_options.filter_policy.reset(
336 NewBloomFilterPolicy(kBloomBitsPerKey, false));
337 table_options.block_size = 1024;
338 options.table_factory.reset(new BlockBasedTableFactory(table_options));
339
340 DestroyAndReopen(options);
341
11fdf7f2
TL
342 // Hold open a snapshot to prevent range tombstones from being compacted
343 // away.
344 ManagedSnapshot snapshot(db_);
345
7c673cae
FG
346 Random rnd(5632);
347 for (int table = 1; table <= kTableCount; ++table) {
494da23a 348 for (int i = 0; i < kPutsPerTable; ++i) {
7c673cae
FG
349 db_->Put(WriteOptions(), RandomString(&rnd, kKeySize),
350 RandomString(&rnd, kValueSize));
351 }
494da23a
TL
352 for (int i = 0; i < kDeletionsPerTable; i++) {
353 db_->Delete(WriteOptions(), RandomString(&rnd, kKeySize));
354 }
355 for (int i = 0; i < kMergeOperandsPerTable; i++) {
356 db_->Merge(WriteOptions(), RandomString(&rnd, kKeySize),
357 RandomString(&rnd, kValueSize));
358 }
11fdf7f2
TL
359 for (int i = 0; i < kRangeDeletionsPerTable; i++) {
360 std::string start = RandomString(&rnd, kKeySize);
361 std::string end = start;
362 end.resize(kValueSize);
363 db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), start, end);
364 }
7c673cae
FG
365 db_->Flush(FlushOptions());
366 }
367 std::string property;
368 db_->GetProperty(DB::Properties::kAggregatedTableProperties, &property);
11fdf7f2
TL
369 TableProperties output_tp;
370 ParseTablePropertiesString(property, &output_tp);
371 bool index_key_is_user_key = output_tp.index_key_is_user_key > 0;
372 bool value_is_delta_encoded = output_tp.index_value_is_delta_encoded > 0;
7c673cae
FG
373
374 TableProperties expected_tp;
494da23a
TL
375 GetExpectedTableProperties(
376 &expected_tp, kKeySize, kValueSize, kPutsPerTable, kDeletionsPerTable,
377 kMergeOperandsPerTable, kRangeDeletionsPerTable, kTableCount,
378 kBloomBitsPerKey, table_options.block_size, index_key_is_user_key,
379 value_is_delta_encoded);
7c673cae
FG
380
381 VerifyTableProperties(expected_tp, output_tp);
382 }
383}
384
385TEST_F(DBPropertiesTest, ReadLatencyHistogramByLevel) {
386 Options options = CurrentOptions();
387 options.write_buffer_size = 110 << 10;
388 options.level0_file_num_compaction_trigger = 6;
389 options.num_levels = 4;
390 options.compression = kNoCompression;
391 options.max_bytes_for_level_base = 4500 << 10;
392 options.target_file_size_base = 98 << 10;
393 options.max_write_buffer_number = 2;
394 options.statistics = rocksdb::CreateDBStatistics();
494da23a
TL
395 options.max_open_files = 11; // Make sure no proloading of table readers
396
397 // RocksDB sanitize max open files to at least 20. Modify it back.
398 rocksdb::SyncPoint::GetInstance()->SetCallBack(
399 "SanitizeOptions::AfterChangeMaxOpenFiles", [&](void* arg) {
400 int* max_open_files = static_cast<int*>(arg);
401 *max_open_files = 11;
402 });
403 rocksdb::SyncPoint::GetInstance()->EnableProcessing();
7c673cae
FG
404
405 BlockBasedTableOptions table_options;
406 table_options.no_block_cache = true;
407
408 CreateAndReopenWithCF({"pikachu"}, options);
409 int key_index = 0;
410 Random rnd(301);
411 for (int num = 0; num < 8; num++) {
412 Put("foo", "bar");
413 GenerateNewFile(&rnd, &key_index);
414 dbfull()->TEST_WaitForCompact();
415 }
416 dbfull()->TEST_WaitForCompact();
417
418 std::string prop;
419 ASSERT_TRUE(dbfull()->GetProperty("rocksdb.dbstats", &prop));
420
421 // Get() after flushes, See latency histogram tracked.
422 for (int key = 0; key < key_index; key++) {
423 Get(Key(key));
424 }
425 ASSERT_TRUE(dbfull()->GetProperty("rocksdb.cfstats", &prop));
426 ASSERT_NE(std::string::npos, prop.find("** Level 0 read latency histogram"));
427 ASSERT_NE(std::string::npos, prop.find("** Level 1 read latency histogram"));
428 ASSERT_EQ(std::string::npos, prop.find("** Level 2 read latency histogram"));
429
430 // Reopen and issue Get(). See thee latency tracked
431 ReopenWithColumnFamilies({"default", "pikachu"}, options);
432 dbfull()->TEST_WaitForCompact();
433 for (int key = 0; key < key_index; key++) {
434 Get(Key(key));
435 }
11fdf7f2
TL
436
437 // Test for getting immutable_db_options_.statistics
438 ASSERT_TRUE(dbfull()->GetProperty(dbfull()->DefaultColumnFamily(),
439 "rocksdb.options-statistics", &prop));
440 ASSERT_NE(std::string::npos, prop.find("rocksdb.block.cache.miss"));
441 ASSERT_EQ(std::string::npos, prop.find("rocksdb.db.f.micros"));
442
7c673cae
FG
443 ASSERT_TRUE(dbfull()->GetProperty(dbfull()->DefaultColumnFamily(),
444 "rocksdb.cf-file-histogram", &prop));
445 ASSERT_NE(std::string::npos, prop.find("** Level 0 read latency histogram"));
446 ASSERT_NE(std::string::npos, prop.find("** Level 1 read latency histogram"));
447 ASSERT_EQ(std::string::npos, prop.find("** Level 2 read latency histogram"));
448
449 // Reopen and issue iterating. See thee latency tracked
450 ReopenWithColumnFamilies({"default", "pikachu"}, options);
494da23a 451 rocksdb::SyncPoint::GetInstance()->DisableProcessing();
7c673cae
FG
452 ASSERT_TRUE(dbfull()->GetProperty("rocksdb.cf-file-histogram", &prop));
453 ASSERT_EQ(std::string::npos, prop.find("** Level 0 read latency histogram"));
454 ASSERT_EQ(std::string::npos, prop.find("** Level 1 read latency histogram"));
455 ASSERT_EQ(std::string::npos, prop.find("** Level 2 read latency histogram"));
456 {
494da23a 457 std::unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions()));
7c673cae
FG
458 for (iter->Seek(Key(0)); iter->Valid(); iter->Next()) {
459 }
460 }
461 ASSERT_TRUE(dbfull()->GetProperty("rocksdb.cf-file-histogram", &prop));
462 ASSERT_NE(std::string::npos, prop.find("** Level 0 read latency histogram"));
463 ASSERT_NE(std::string::npos, prop.find("** Level 1 read latency histogram"));
464 ASSERT_EQ(std::string::npos, prop.find("** Level 2 read latency histogram"));
465
466 // CF 1 should show no histogram.
467 ASSERT_TRUE(
468 dbfull()->GetProperty(handles_[1], "rocksdb.cf-file-histogram", &prop));
469 ASSERT_EQ(std::string::npos, prop.find("** Level 0 read latency histogram"));
470 ASSERT_EQ(std::string::npos, prop.find("** Level 1 read latency histogram"));
471 ASSERT_EQ(std::string::npos, prop.find("** Level 2 read latency histogram"));
472 // put something and read it back , CF 1 should show histogram.
473 Put(1, "foo", "bar");
474 Flush(1);
475 dbfull()->TEST_WaitForCompact();
476 ASSERT_EQ("bar", Get(1, "foo"));
477
478 ASSERT_TRUE(
479 dbfull()->GetProperty(handles_[1], "rocksdb.cf-file-histogram", &prop));
480 ASSERT_NE(std::string::npos, prop.find("** Level 0 read latency histogram"));
481 ASSERT_EQ(std::string::npos, prop.find("** Level 1 read latency histogram"));
482 ASSERT_EQ(std::string::npos, prop.find("** Level 2 read latency histogram"));
483
484 // options.max_open_files preloads table readers.
485 options.max_open_files = -1;
486 ReopenWithColumnFamilies({"default", "pikachu"}, options);
487 ASSERT_TRUE(dbfull()->GetProperty(dbfull()->DefaultColumnFamily(),
488 "rocksdb.cf-file-histogram", &prop));
489 ASSERT_NE(std::string::npos, prop.find("** Level 0 read latency histogram"));
490 ASSERT_NE(std::string::npos, prop.find("** Level 1 read latency histogram"));
491 ASSERT_EQ(std::string::npos, prop.find("** Level 2 read latency histogram"));
492 for (int key = 0; key < key_index; key++) {
493 Get(Key(key));
494 }
495 ASSERT_TRUE(dbfull()->GetProperty("rocksdb.cfstats", &prop));
496 ASSERT_NE(std::string::npos, prop.find("** Level 0 read latency histogram"));
497 ASSERT_NE(std::string::npos, prop.find("** Level 1 read latency histogram"));
498 ASSERT_EQ(std::string::npos, prop.find("** Level 2 read latency histogram"));
499
500 // Clear internal stats
501 dbfull()->ResetStats();
502 ASSERT_TRUE(dbfull()->GetProperty("rocksdb.cfstats", &prop));
503 ASSERT_EQ(std::string::npos, prop.find("** Level 0 read latency histogram"));
504 ASSERT_EQ(std::string::npos, prop.find("** Level 1 read latency histogram"));
505 ASSERT_EQ(std::string::npos, prop.find("** Level 2 read latency histogram"));
506}
507
508TEST_F(DBPropertiesTest, AggregatedTablePropertiesAtLevel) {
509 const int kTableCount = 100;
494da23a
TL
510 const int kDeletionsPerTable = 2;
511 const int kMergeOperandsPerTable = 2;
11fdf7f2 512 const int kRangeDeletionsPerTable = 2;
494da23a 513 const int kPutsPerTable = 10;
7c673cae
FG
514 const int kKeySize = 50;
515 const int kValueSize = 400;
516 const int kMaxLevel = 7;
517 const int kBloomBitsPerKey = 20;
518 Random rnd(301);
519 Options options = CurrentOptions();
520 options.level0_file_num_compaction_trigger = 8;
521 options.compression = kNoCompression;
522 options.create_if_missing = true;
523 options.level0_file_num_compaction_trigger = 2;
524 options.target_file_size_base = 8192;
525 options.max_bytes_for_level_base = 10000;
526 options.max_bytes_for_level_multiplier = 2;
527 // This ensures there no compaction happening when we call GetProperty().
528 options.disable_auto_compactions = true;
494da23a
TL
529 options.preserve_deletes = true;
530 options.merge_operator.reset(new TestPutOperator());
7c673cae
FG
531
532 BlockBasedTableOptions table_options;
533 table_options.filter_policy.reset(
534 NewBloomFilterPolicy(kBloomBitsPerKey, false));
535 table_options.block_size = 1024;
536 options.table_factory.reset(new BlockBasedTableFactory(table_options));
537
538 DestroyAndReopen(options);
539
11fdf7f2
TL
540 // Hold open a snapshot to prevent range tombstones from being compacted away.
541 ManagedSnapshot snapshot(db_);
542
7c673cae
FG
543 std::string level_tp_strings[kMaxLevel];
544 std::string tp_string;
545 TableProperties level_tps[kMaxLevel];
546 TableProperties tp, sum_tp, expected_tp;
547 for (int table = 1; table <= kTableCount; ++table) {
494da23a 548 for (int i = 0; i < kPutsPerTable; ++i) {
7c673cae
FG
549 db_->Put(WriteOptions(), RandomString(&rnd, kKeySize),
550 RandomString(&rnd, kValueSize));
551 }
494da23a
TL
552 for (int i = 0; i < kDeletionsPerTable; i++) {
553 db_->Delete(WriteOptions(), RandomString(&rnd, kKeySize));
554 }
555 for (int i = 0; i < kMergeOperandsPerTable; i++) {
556 db_->Merge(WriteOptions(), RandomString(&rnd, kKeySize),
557 RandomString(&rnd, kValueSize));
558 }
11fdf7f2
TL
559 for (int i = 0; i < kRangeDeletionsPerTable; i++) {
560 std::string start = RandomString(&rnd, kKeySize);
561 std::string end = start;
562 end.resize(kValueSize);
563 db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), start, end);
564 }
7c673cae
FG
565 db_->Flush(FlushOptions());
566 db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
567 ResetTableProperties(&sum_tp);
568 for (int level = 0; level < kMaxLevel; ++level) {
569 db_->GetProperty(
570 DB::Properties::kAggregatedTablePropertiesAtLevel + ToString(level),
571 &level_tp_strings[level]);
572 ParseTablePropertiesString(level_tp_strings[level], &level_tps[level]);
573 sum_tp.data_size += level_tps[level].data_size;
574 sum_tp.index_size += level_tps[level].index_size;
575 sum_tp.filter_size += level_tps[level].filter_size;
576 sum_tp.raw_key_size += level_tps[level].raw_key_size;
577 sum_tp.raw_value_size += level_tps[level].raw_value_size;
578 sum_tp.num_data_blocks += level_tps[level].num_data_blocks;
579 sum_tp.num_entries += level_tps[level].num_entries;
494da23a
TL
580 sum_tp.num_deletions += level_tps[level].num_deletions;
581 sum_tp.num_merge_operands += level_tps[level].num_merge_operands;
11fdf7f2 582 sum_tp.num_range_deletions += level_tps[level].num_range_deletions;
7c673cae
FG
583 }
584 db_->GetProperty(DB::Properties::kAggregatedTableProperties, &tp_string);
585 ParseTablePropertiesString(tp_string, &tp);
11fdf7f2
TL
586 bool index_key_is_user_key = tp.index_key_is_user_key > 0;
587 bool value_is_delta_encoded = tp.index_value_is_delta_encoded > 0;
7c673cae
FG
588 ASSERT_EQ(sum_tp.data_size, tp.data_size);
589 ASSERT_EQ(sum_tp.index_size, tp.index_size);
590 ASSERT_EQ(sum_tp.filter_size, tp.filter_size);
591 ASSERT_EQ(sum_tp.raw_key_size, tp.raw_key_size);
592 ASSERT_EQ(sum_tp.raw_value_size, tp.raw_value_size);
593 ASSERT_EQ(sum_tp.num_data_blocks, tp.num_data_blocks);
594 ASSERT_EQ(sum_tp.num_entries, tp.num_entries);
494da23a
TL
595 ASSERT_EQ(sum_tp.num_deletions, tp.num_deletions);
596 ASSERT_EQ(sum_tp.num_merge_operands, tp.num_merge_operands);
11fdf7f2 597 ASSERT_EQ(sum_tp.num_range_deletions, tp.num_range_deletions);
7c673cae 598 if (table > 3) {
494da23a
TL
599 GetExpectedTableProperties(
600 &expected_tp, kKeySize, kValueSize, kPutsPerTable, kDeletionsPerTable,
601 kMergeOperandsPerTable, kRangeDeletionsPerTable, table,
602 kBloomBitsPerKey, table_options.block_size, index_key_is_user_key,
603 value_is_delta_encoded);
7c673cae
FG
604 // Gives larger bias here as index block size, filter block size,
605 // and data block size become much harder to estimate in this test.
11fdf7f2 606 VerifyTableProperties(expected_tp, tp, 0.5, 0.4, 0.4, 0.25);
7c673cae
FG
607 }
608 }
609}
610
611TEST_F(DBPropertiesTest, NumImmutableMemTable) {
612 do {
613 Options options = CurrentOptions();
614 WriteOptions writeOpt = WriteOptions();
615 writeOpt.disableWAL = true;
616 options.max_write_buffer_number = 4;
617 options.min_write_buffer_number_to_merge = 3;
618 options.max_write_buffer_number_to_maintain = 4;
619 options.write_buffer_size = 1000000;
620 CreateAndReopenWithCF({"pikachu"}, options);
621
622 std::string big_value(1000000 * 2, 'x');
623 std::string num;
624 uint64_t value;
625 SetPerfLevel(kEnableTime);
626 ASSERT_TRUE(GetPerfLevel() == kEnableTime);
627
628 ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "k1", big_value));
629 ASSERT_TRUE(dbfull()->GetProperty(handles_[1],
630 "rocksdb.num-immutable-mem-table", &num));
631 ASSERT_EQ(num, "0");
632 ASSERT_TRUE(dbfull()->GetProperty(
633 handles_[1], DB::Properties::kNumImmutableMemTableFlushed, &num));
634 ASSERT_EQ(num, "0");
635 ASSERT_TRUE(dbfull()->GetProperty(
636 handles_[1], "rocksdb.num-entries-active-mem-table", &num));
637 ASSERT_EQ(num, "1");
11fdf7f2 638 get_perf_context()->Reset();
7c673cae 639 Get(1, "k1");
11fdf7f2 640 ASSERT_EQ(1, static_cast<int>(get_perf_context()->get_from_memtable_count));
7c673cae
FG
641
642 ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "k2", big_value));
643 ASSERT_TRUE(dbfull()->GetProperty(handles_[1],
644 "rocksdb.num-immutable-mem-table", &num));
645 ASSERT_EQ(num, "1");
646 ASSERT_TRUE(dbfull()->GetProperty(
647 handles_[1], "rocksdb.num-entries-active-mem-table", &num));
648 ASSERT_EQ(num, "1");
649 ASSERT_TRUE(dbfull()->GetProperty(
650 handles_[1], "rocksdb.num-entries-imm-mem-tables", &num));
651 ASSERT_EQ(num, "1");
652
11fdf7f2 653 get_perf_context()->Reset();
7c673cae 654 Get(1, "k1");
11fdf7f2
TL
655 ASSERT_EQ(2, static_cast<int>(get_perf_context()->get_from_memtable_count));
656 get_perf_context()->Reset();
7c673cae 657 Get(1, "k2");
11fdf7f2 658 ASSERT_EQ(1, static_cast<int>(get_perf_context()->get_from_memtable_count));
7c673cae
FG
659
660 ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "k3", big_value));
661 ASSERT_TRUE(dbfull()->GetProperty(
662 handles_[1], "rocksdb.cur-size-active-mem-table", &num));
663 ASSERT_TRUE(dbfull()->GetProperty(handles_[1],
664 "rocksdb.num-immutable-mem-table", &num));
665 ASSERT_EQ(num, "2");
666 ASSERT_TRUE(dbfull()->GetProperty(
667 handles_[1], "rocksdb.num-entries-active-mem-table", &num));
668 ASSERT_EQ(num, "1");
669 ASSERT_TRUE(dbfull()->GetProperty(
670 handles_[1], "rocksdb.num-entries-imm-mem-tables", &num));
671 ASSERT_EQ(num, "2");
11fdf7f2 672 get_perf_context()->Reset();
7c673cae 673 Get(1, "k2");
11fdf7f2
TL
674 ASSERT_EQ(2, static_cast<int>(get_perf_context()->get_from_memtable_count));
675 get_perf_context()->Reset();
7c673cae 676 Get(1, "k3");
11fdf7f2
TL
677 ASSERT_EQ(1, static_cast<int>(get_perf_context()->get_from_memtable_count));
678 get_perf_context()->Reset();
7c673cae 679 Get(1, "k1");
11fdf7f2 680 ASSERT_EQ(3, static_cast<int>(get_perf_context()->get_from_memtable_count));
7c673cae
FG
681
682 ASSERT_OK(Flush(1));
683 ASSERT_TRUE(dbfull()->GetProperty(handles_[1],
684 "rocksdb.num-immutable-mem-table", &num));
685 ASSERT_EQ(num, "0");
686 ASSERT_TRUE(dbfull()->GetProperty(
687 handles_[1], DB::Properties::kNumImmutableMemTableFlushed, &num));
688 ASSERT_EQ(num, "3");
689 ASSERT_TRUE(dbfull()->GetIntProperty(
690 handles_[1], "rocksdb.cur-size-active-mem-table", &value));
691 // "192" is the size of the metadata of two empty skiplists, this would
692 // break if we change the default skiplist implementation
693 ASSERT_GE(value, 192);
694
695 uint64_t int_num;
696 uint64_t base_total_size;
697 ASSERT_TRUE(dbfull()->GetIntProperty(
698 handles_[1], "rocksdb.estimate-num-keys", &base_total_size));
699
700 ASSERT_OK(dbfull()->Delete(writeOpt, handles_[1], "k2"));
701 ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "k3", ""));
702 ASSERT_OK(dbfull()->Delete(writeOpt, handles_[1], "k3"));
703 ASSERT_TRUE(dbfull()->GetIntProperty(
704 handles_[1], "rocksdb.num-deletes-active-mem-table", &int_num));
705 ASSERT_EQ(int_num, 2U);
706 ASSERT_TRUE(dbfull()->GetIntProperty(
707 handles_[1], "rocksdb.num-entries-active-mem-table", &int_num));
708 ASSERT_EQ(int_num, 3U);
709
710 ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "k2", big_value));
711 ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "k2", big_value));
712 ASSERT_TRUE(dbfull()->GetIntProperty(
713 handles_[1], "rocksdb.num-entries-imm-mem-tables", &int_num));
714 ASSERT_EQ(int_num, 4U);
715 ASSERT_TRUE(dbfull()->GetIntProperty(
716 handles_[1], "rocksdb.num-deletes-imm-mem-tables", &int_num));
717 ASSERT_EQ(int_num, 2U);
718
719 ASSERT_TRUE(dbfull()->GetIntProperty(
720 handles_[1], "rocksdb.estimate-num-keys", &int_num));
721 ASSERT_EQ(int_num, base_total_size + 1);
722
723 SetPerfLevel(kDisable);
724 ASSERT_TRUE(GetPerfLevel() == kDisable);
725 } while (ChangeCompactOptions());
726}
727
728// TODO(techdept) : Disabled flaky test #12863555
729TEST_F(DBPropertiesTest, DISABLED_GetProperty) {
730 // Set sizes to both background thread pool to be 1 and block them.
731 env_->SetBackgroundThreads(1, Env::HIGH);
732 env_->SetBackgroundThreads(1, Env::LOW);
733 test::SleepingBackgroundTask sleeping_task_low;
734 env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task_low,
735 Env::Priority::LOW);
736 test::SleepingBackgroundTask sleeping_task_high;
737 env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
738 &sleeping_task_high, Env::Priority::HIGH);
739
740 Options options = CurrentOptions();
741 WriteOptions writeOpt = WriteOptions();
742 writeOpt.disableWAL = true;
743 options.compaction_style = kCompactionStyleUniversal;
744 options.level0_file_num_compaction_trigger = 1;
745 options.compaction_options_universal.size_ratio = 50;
746 options.max_background_compactions = 1;
747 options.max_background_flushes = 1;
748 options.max_write_buffer_number = 10;
749 options.min_write_buffer_number_to_merge = 1;
750 options.max_write_buffer_number_to_maintain = 0;
751 options.write_buffer_size = 1000000;
752 Reopen(options);
753
754 std::string big_value(1000000 * 2, 'x');
755 std::string num;
756 uint64_t int_num;
757 SetPerfLevel(kEnableTime);
758
759 ASSERT_TRUE(
760 dbfull()->GetIntProperty("rocksdb.estimate-table-readers-mem", &int_num));
761 ASSERT_EQ(int_num, 0U);
762 ASSERT_TRUE(
763 dbfull()->GetIntProperty("rocksdb.estimate-live-data-size", &int_num));
764 ASSERT_EQ(int_num, 0U);
765
766 ASSERT_OK(dbfull()->Put(writeOpt, "k1", big_value));
767 ASSERT_TRUE(dbfull()->GetProperty("rocksdb.num-immutable-mem-table", &num));
768 ASSERT_EQ(num, "0");
769 ASSERT_TRUE(dbfull()->GetProperty("rocksdb.mem-table-flush-pending", &num));
770 ASSERT_EQ(num, "0");
771 ASSERT_TRUE(dbfull()->GetProperty("rocksdb.compaction-pending", &num));
772 ASSERT_EQ(num, "0");
773 ASSERT_TRUE(dbfull()->GetProperty("rocksdb.estimate-num-keys", &num));
774 ASSERT_EQ(num, "1");
11fdf7f2 775 get_perf_context()->Reset();
7c673cae
FG
776
777 ASSERT_OK(dbfull()->Put(writeOpt, "k2", big_value));
778 ASSERT_TRUE(dbfull()->GetProperty("rocksdb.num-immutable-mem-table", &num));
779 ASSERT_EQ(num, "1");
780 ASSERT_OK(dbfull()->Delete(writeOpt, "k-non-existing"));
781 ASSERT_OK(dbfull()->Put(writeOpt, "k3", big_value));
782 ASSERT_TRUE(dbfull()->GetProperty("rocksdb.num-immutable-mem-table", &num));
783 ASSERT_EQ(num, "2");
784 ASSERT_TRUE(dbfull()->GetProperty("rocksdb.mem-table-flush-pending", &num));
785 ASSERT_EQ(num, "1");
786 ASSERT_TRUE(dbfull()->GetProperty("rocksdb.compaction-pending", &num));
787 ASSERT_EQ(num, "0");
788 ASSERT_TRUE(dbfull()->GetProperty("rocksdb.estimate-num-keys", &num));
789 ASSERT_EQ(num, "2");
790 // Verify the same set of properties through GetIntProperty
791 ASSERT_TRUE(
792 dbfull()->GetIntProperty("rocksdb.num-immutable-mem-table", &int_num));
793 ASSERT_EQ(int_num, 2U);
794 ASSERT_TRUE(
795 dbfull()->GetIntProperty("rocksdb.mem-table-flush-pending", &int_num));
796 ASSERT_EQ(int_num, 1U);
797 ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.compaction-pending", &int_num));
798 ASSERT_EQ(int_num, 0U);
799 ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.estimate-num-keys", &int_num));
800 ASSERT_EQ(int_num, 2U);
801
802 ASSERT_TRUE(
803 dbfull()->GetIntProperty("rocksdb.estimate-table-readers-mem", &int_num));
804 ASSERT_EQ(int_num, 0U);
805
806 sleeping_task_high.WakeUp();
807 sleeping_task_high.WaitUntilDone();
808 dbfull()->TEST_WaitForFlushMemTable();
809
810 ASSERT_OK(dbfull()->Put(writeOpt, "k4", big_value));
811 ASSERT_OK(dbfull()->Put(writeOpt, "k5", big_value));
812 dbfull()->TEST_WaitForFlushMemTable();
813 ASSERT_TRUE(dbfull()->GetProperty("rocksdb.mem-table-flush-pending", &num));
814 ASSERT_EQ(num, "0");
815 ASSERT_TRUE(dbfull()->GetProperty("rocksdb.compaction-pending", &num));
816 ASSERT_EQ(num, "1");
817 ASSERT_TRUE(dbfull()->GetProperty("rocksdb.estimate-num-keys", &num));
818 ASSERT_EQ(num, "4");
819
820 ASSERT_TRUE(
821 dbfull()->GetIntProperty("rocksdb.estimate-table-readers-mem", &int_num));
822 ASSERT_GT(int_num, 0U);
823
824 sleeping_task_low.WakeUp();
825 sleeping_task_low.WaitUntilDone();
826
827 // Wait for compaction to be done. This is important because otherwise RocksDB
828 // might schedule a compaction when reopening the database, failing assertion
829 // (A) as a result.
830 dbfull()->TEST_WaitForCompact();
831 options.max_open_files = 10;
832 Reopen(options);
833 // After reopening, no table reader is loaded, so no memory for table readers
834 ASSERT_TRUE(
835 dbfull()->GetIntProperty("rocksdb.estimate-table-readers-mem", &int_num));
836 ASSERT_EQ(int_num, 0U); // (A)
837 ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.estimate-num-keys", &int_num));
838 ASSERT_GT(int_num, 0U);
839
840 // After reading a key, at least one table reader is loaded.
841 Get("k5");
842 ASSERT_TRUE(
843 dbfull()->GetIntProperty("rocksdb.estimate-table-readers-mem", &int_num));
844 ASSERT_GT(int_num, 0U);
845
846 // Test rocksdb.num-live-versions
847 {
848 options.level0_file_num_compaction_trigger = 20;
849 Reopen(options);
850 ASSERT_TRUE(
851 dbfull()->GetIntProperty("rocksdb.num-live-versions", &int_num));
852 ASSERT_EQ(int_num, 1U);
853
854 // Use an iterator to hold current version
855 std::unique_ptr<Iterator> iter1(dbfull()->NewIterator(ReadOptions()));
856
857 ASSERT_OK(dbfull()->Put(writeOpt, "k6", big_value));
858 Flush();
859 ASSERT_TRUE(
860 dbfull()->GetIntProperty("rocksdb.num-live-versions", &int_num));
861 ASSERT_EQ(int_num, 2U);
862
863 // Use an iterator to hold current version
864 std::unique_ptr<Iterator> iter2(dbfull()->NewIterator(ReadOptions()));
865
866 ASSERT_OK(dbfull()->Put(writeOpt, "k7", big_value));
867 Flush();
868 ASSERT_TRUE(
869 dbfull()->GetIntProperty("rocksdb.num-live-versions", &int_num));
870 ASSERT_EQ(int_num, 3U);
871
872 iter2.reset();
873 ASSERT_TRUE(
874 dbfull()->GetIntProperty("rocksdb.num-live-versions", &int_num));
875 ASSERT_EQ(int_num, 2U);
876
877 iter1.reset();
878 ASSERT_TRUE(
879 dbfull()->GetIntProperty("rocksdb.num-live-versions", &int_num));
880 ASSERT_EQ(int_num, 1U);
881 }
882}
883
884TEST_F(DBPropertiesTest, ApproximateMemoryUsage) {
885 const int kNumRounds = 10;
886 // TODO(noetzli) kFlushesPerRound does not really correlate with how many
887 // flushes happen.
888 const int kFlushesPerRound = 10;
889 const int kWritesPerFlush = 10;
890 const int kKeySize = 100;
891 const int kValueSize = 1000;
892 Options options;
893 options.write_buffer_size = 1000; // small write buffer
894 options.min_write_buffer_number_to_merge = 4;
895 options.compression = kNoCompression;
896 options.create_if_missing = true;
897 options = CurrentOptions(options);
898 DestroyAndReopen(options);
899
900 Random rnd(301);
901
902 std::vector<Iterator*> iters;
903
904 uint64_t active_mem;
905 uint64_t unflushed_mem;
906 uint64_t all_mem;
907 uint64_t prev_all_mem;
908
909 // Phase 0. The verify the initial value of all these properties are the same
910 // as we have no mem-tables.
911 dbfull()->GetIntProperty("rocksdb.cur-size-active-mem-table", &active_mem);
912 dbfull()->GetIntProperty("rocksdb.cur-size-all-mem-tables", &unflushed_mem);
913 dbfull()->GetIntProperty("rocksdb.size-all-mem-tables", &all_mem);
914 ASSERT_EQ(all_mem, active_mem);
915 ASSERT_EQ(all_mem, unflushed_mem);
916
917 // Phase 1. Simply issue Put() and expect "cur-size-all-mem-tables" equals to
918 // "size-all-mem-tables"
919 for (int r = 0; r < kNumRounds; ++r) {
920 for (int f = 0; f < kFlushesPerRound; ++f) {
921 for (int w = 0; w < kWritesPerFlush; ++w) {
922 Put(RandomString(&rnd, kKeySize), RandomString(&rnd, kValueSize));
923 }
924 }
925 // Make sure that there is no flush between getting the two properties.
926 dbfull()->TEST_WaitForFlushMemTable();
927 dbfull()->GetIntProperty("rocksdb.cur-size-all-mem-tables", &unflushed_mem);
928 dbfull()->GetIntProperty("rocksdb.size-all-mem-tables", &all_mem);
929 // in no iterator case, these two number should be the same.
930 ASSERT_EQ(unflushed_mem, all_mem);
931 }
932 prev_all_mem = all_mem;
933
934 // Phase 2. Keep issuing Put() but also create new iterators. This time we
935 // expect "size-all-mem-tables" > "cur-size-all-mem-tables".
936 for (int r = 0; r < kNumRounds; ++r) {
937 iters.push_back(db_->NewIterator(ReadOptions()));
938 for (int f = 0; f < kFlushesPerRound; ++f) {
939 for (int w = 0; w < kWritesPerFlush; ++w) {
940 Put(RandomString(&rnd, kKeySize), RandomString(&rnd, kValueSize));
941 }
942 }
943 // Force flush to prevent flush from happening between getting the
944 // properties or after getting the properties and before the new round.
945 Flush();
946
947 // In the second round, add iterators.
948 dbfull()->GetIntProperty("rocksdb.cur-size-active-mem-table", &active_mem);
949 dbfull()->GetIntProperty("rocksdb.cur-size-all-mem-tables", &unflushed_mem);
950 dbfull()->GetIntProperty("rocksdb.size-all-mem-tables", &all_mem);
951 ASSERT_GT(all_mem, active_mem);
952 ASSERT_GT(all_mem, unflushed_mem);
953 ASSERT_GT(all_mem, prev_all_mem);
954 prev_all_mem = all_mem;
955 }
956
957 // Phase 3. Delete iterators and expect "size-all-mem-tables" shrinks
958 // whenever we release an iterator.
959 for (auto* iter : iters) {
960 delete iter;
961 dbfull()->GetIntProperty("rocksdb.size-all-mem-tables", &all_mem);
962 // Expect the size shrinking
963 ASSERT_LT(all_mem, prev_all_mem);
964 prev_all_mem = all_mem;
965 }
966
967 // Expect all these three counters to be the same.
968 dbfull()->GetIntProperty("rocksdb.cur-size-active-mem-table", &active_mem);
969 dbfull()->GetIntProperty("rocksdb.cur-size-all-mem-tables", &unflushed_mem);
970 dbfull()->GetIntProperty("rocksdb.size-all-mem-tables", &all_mem);
971 ASSERT_EQ(active_mem, unflushed_mem);
972 ASSERT_EQ(unflushed_mem, all_mem);
973
974 // Phase 5. Reopen, and expect all these three counters to be the same again.
975 Reopen(options);
976 dbfull()->GetIntProperty("rocksdb.cur-size-active-mem-table", &active_mem);
977 dbfull()->GetIntProperty("rocksdb.cur-size-all-mem-tables", &unflushed_mem);
978 dbfull()->GetIntProperty("rocksdb.size-all-mem-tables", &all_mem);
979 ASSERT_EQ(active_mem, unflushed_mem);
980 ASSERT_EQ(unflushed_mem, all_mem);
981}
982
983TEST_F(DBPropertiesTest, EstimatePendingCompBytes) {
984 // Set sizes to both background thread pool to be 1 and block them.
985 env_->SetBackgroundThreads(1, Env::HIGH);
986 env_->SetBackgroundThreads(1, Env::LOW);
987 test::SleepingBackgroundTask sleeping_task_low;
988 env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task_low,
989 Env::Priority::LOW);
990
991 Options options = CurrentOptions();
992 WriteOptions writeOpt = WriteOptions();
993 writeOpt.disableWAL = true;
994 options.compaction_style = kCompactionStyleLevel;
995 options.level0_file_num_compaction_trigger = 2;
996 options.max_background_compactions = 1;
997 options.max_background_flushes = 1;
998 options.max_write_buffer_number = 10;
999 options.min_write_buffer_number_to_merge = 1;
1000 options.max_write_buffer_number_to_maintain = 0;
1001 options.write_buffer_size = 1000000;
1002 Reopen(options);
1003
1004 std::string big_value(1000000 * 2, 'x');
1005 std::string num;
1006 uint64_t int_num;
1007
1008 ASSERT_OK(dbfull()->Put(writeOpt, "k1", big_value));
1009 Flush();
1010 ASSERT_TRUE(dbfull()->GetIntProperty(
1011 "rocksdb.estimate-pending-compaction-bytes", &int_num));
1012 ASSERT_EQ(int_num, 0U);
1013
1014 ASSERT_OK(dbfull()->Put(writeOpt, "k2", big_value));
1015 Flush();
1016 ASSERT_TRUE(dbfull()->GetIntProperty(
1017 "rocksdb.estimate-pending-compaction-bytes", &int_num));
11fdf7f2 1018 ASSERT_GT(int_num, 0U);
7c673cae
FG
1019
1020 ASSERT_OK(dbfull()->Put(writeOpt, "k3", big_value));
1021 Flush();
1022 ASSERT_TRUE(dbfull()->GetIntProperty(
1023 "rocksdb.estimate-pending-compaction-bytes", &int_num));
1024 ASSERT_GT(int_num, 0U);
1025
1026 sleeping_task_low.WakeUp();
1027 sleeping_task_low.WaitUntilDone();
1028
1029 dbfull()->TEST_WaitForCompact();
1030 ASSERT_TRUE(dbfull()->GetIntProperty(
1031 "rocksdb.estimate-pending-compaction-bytes", &int_num));
1032 ASSERT_EQ(int_num, 0U);
1033}
1034
1035TEST_F(DBPropertiesTest, EstimateCompressionRatio) {
1036 if (!Snappy_Supported()) {
1037 return;
1038 }
1039 const int kNumL0Files = 3;
1040 const int kNumEntriesPerFile = 1000;
1041
1042 Options options = CurrentOptions();
1043 options.compression_per_level = {kNoCompression, kSnappyCompression};
1044 options.disable_auto_compactions = true;
7c673cae
FG
1045 options.num_levels = 2;
1046 Reopen(options);
1047
1048 // compression ratio is -1.0 when no open files at level
1049 ASSERT_EQ(CompressionRatioAtLevel(0), -1.0);
1050
1051 const std::string kVal(100, 'a');
1052 for (int i = 0; i < kNumL0Files; ++i) {
1053 for (int j = 0; j < kNumEntriesPerFile; ++j) {
1054 // Put common data ("key") at end to prevent delta encoding from
1055 // compressing the key effectively
1056 std::string key = ToString(i) + ToString(j) + "key";
1057 ASSERT_OK(dbfull()->Put(WriteOptions(), key, kVal));
1058 }
1059 Flush();
1060 }
1061
1062 // no compression at L0, so ratio is less than one
1063 ASSERT_LT(CompressionRatioAtLevel(0), 1.0);
1064 ASSERT_GT(CompressionRatioAtLevel(0), 0.0);
1065 ASSERT_EQ(CompressionRatioAtLevel(1), -1.0);
1066
1067 dbfull()->TEST_CompactRange(0, nullptr, nullptr);
1068
1069 ASSERT_EQ(CompressionRatioAtLevel(0), -1.0);
1070 // Data at L1 should be highly compressed thanks to Snappy and redundant data
1071 // in values (ratio is 12.846 as of 4/19/2016).
1072 ASSERT_GT(CompressionRatioAtLevel(1), 10.0);
1073}
1074
1075#endif // ROCKSDB_LITE
1076
1077class CountingUserTblPropCollector : public TablePropertiesCollector {
1078 public:
1079 const char* Name() const override { return "CountingUserTblPropCollector"; }
1080
1081 Status Finish(UserCollectedProperties* properties) override {
1082 std::string encoded;
1083 PutVarint32(&encoded, count_);
1084 *properties = UserCollectedProperties{
1085 {"CountingUserTblPropCollector", message_}, {"Count", encoded},
1086 };
1087 return Status::OK();
1088 }
1089
11fdf7f2
TL
1090 Status AddUserKey(const Slice& /*user_key*/, const Slice& /*value*/,
1091 EntryType /*type*/, SequenceNumber /*seq*/,
1092 uint64_t /*file_size*/) override {
7c673cae
FG
1093 ++count_;
1094 return Status::OK();
1095 }
1096
494da23a 1097 UserCollectedProperties GetReadableProperties() const override {
7c673cae
FG
1098 return UserCollectedProperties{};
1099 }
1100
1101 private:
1102 std::string message_ = "Rocksdb";
1103 uint32_t count_ = 0;
1104};
1105
1106class CountingUserTblPropCollectorFactory
1107 : public TablePropertiesCollectorFactory {
1108 public:
1109 explicit CountingUserTblPropCollectorFactory(
1110 uint32_t expected_column_family_id)
1111 : expected_column_family_id_(expected_column_family_id),
1112 num_created_(0) {}
494da23a 1113 TablePropertiesCollector* CreateTablePropertiesCollector(
7c673cae
FG
1114 TablePropertiesCollectorFactory::Context context) override {
1115 EXPECT_EQ(expected_column_family_id_, context.column_family_id);
1116 num_created_++;
1117 return new CountingUserTblPropCollector();
1118 }
1119 const char* Name() const override {
1120 return "CountingUserTblPropCollectorFactory";
1121 }
1122 void set_expected_column_family_id(uint32_t v) {
1123 expected_column_family_id_ = v;
1124 }
1125 uint32_t expected_column_family_id_;
1126 uint32_t num_created_;
1127};
1128
1129class CountingDeleteTabPropCollector : public TablePropertiesCollector {
1130 public:
1131 const char* Name() const override { return "CountingDeleteTabPropCollector"; }
1132
11fdf7f2
TL
1133 Status AddUserKey(const Slice& /*user_key*/, const Slice& /*value*/,
1134 EntryType type, SequenceNumber /*seq*/,
1135 uint64_t /*file_size*/) override {
7c673cae
FG
1136 if (type == kEntryDelete) {
1137 num_deletes_++;
1138 }
1139 return Status::OK();
1140 }
1141
1142 bool NeedCompact() const override { return num_deletes_ > 10; }
1143
1144 UserCollectedProperties GetReadableProperties() const override {
1145 return UserCollectedProperties{};
1146 }
1147
1148 Status Finish(UserCollectedProperties* properties) override {
1149 *properties =
1150 UserCollectedProperties{{"num_delete", ToString(num_deletes_)}};
1151 return Status::OK();
1152 }
1153
1154 private:
1155 uint32_t num_deletes_ = 0;
1156};
1157
1158class CountingDeleteTabPropCollectorFactory
1159 : public TablePropertiesCollectorFactory {
1160 public:
494da23a 1161 TablePropertiesCollector* CreateTablePropertiesCollector(
11fdf7f2 1162 TablePropertiesCollectorFactory::Context /*context*/) override {
7c673cae
FG
1163 return new CountingDeleteTabPropCollector();
1164 }
1165 const char* Name() const override {
1166 return "CountingDeleteTabPropCollectorFactory";
1167 }
1168};
1169
1170#ifndef ROCKSDB_LITE
1171TEST_F(DBPropertiesTest, GetUserDefinedTableProperties) {
1172 Options options = CurrentOptions();
1173 options.level0_file_num_compaction_trigger = (1 << 30);
7c673cae
FG
1174 options.table_properties_collector_factories.resize(1);
1175 std::shared_ptr<CountingUserTblPropCollectorFactory> collector_factory =
1176 std::make_shared<CountingUserTblPropCollectorFactory>(0);
1177 options.table_properties_collector_factories[0] = collector_factory;
1178 Reopen(options);
1179 // Create 4 tables
1180 for (int table = 0; table < 4; ++table) {
1181 for (int i = 0; i < 10 + table; ++i) {
1182 db_->Put(WriteOptions(), ToString(table * 100 + i), "val");
1183 }
1184 db_->Flush(FlushOptions());
1185 }
1186
1187 TablePropertiesCollection props;
1188 ASSERT_OK(db_->GetPropertiesOfAllTables(&props));
1189 ASSERT_EQ(4U, props.size());
1190 uint32_t sum = 0;
1191 for (const auto& item : props) {
1192 auto& user_collected = item.second->user_collected_properties;
1193 ASSERT_TRUE(user_collected.find("CountingUserTblPropCollector") !=
1194 user_collected.end());
1195 ASSERT_EQ(user_collected.at("CountingUserTblPropCollector"), "Rocksdb");
1196 ASSERT_TRUE(user_collected.find("Count") != user_collected.end());
1197 Slice key(user_collected.at("Count"));
1198 uint32_t count;
1199 ASSERT_TRUE(GetVarint32(&key, &count));
1200 sum += count;
1201 }
1202 ASSERT_EQ(10u + 11u + 12u + 13u, sum);
1203
1204 ASSERT_GT(collector_factory->num_created_, 0U);
1205 collector_factory->num_created_ = 0;
1206 dbfull()->TEST_CompactRange(0, nullptr, nullptr);
1207 ASSERT_GT(collector_factory->num_created_, 0U);
1208}
1209#endif // ROCKSDB_LITE
1210
1211TEST_F(DBPropertiesTest, UserDefinedTablePropertiesContext) {
1212 Options options = CurrentOptions();
1213 options.level0_file_num_compaction_trigger = 3;
7c673cae
FG
1214 options.table_properties_collector_factories.resize(1);
1215 std::shared_ptr<CountingUserTblPropCollectorFactory> collector_factory =
1216 std::make_shared<CountingUserTblPropCollectorFactory>(1);
1217 options.table_properties_collector_factories[0] = collector_factory,
1218 CreateAndReopenWithCF({"pikachu"}, options);
1219 // Create 2 files
1220 for (int table = 0; table < 2; ++table) {
1221 for (int i = 0; i < 10 + table; ++i) {
1222 Put(1, ToString(table * 100 + i), "val");
1223 }
1224 Flush(1);
1225 }
1226 ASSERT_GT(collector_factory->num_created_, 0U);
1227
1228 collector_factory->num_created_ = 0;
1229 // Trigger automatic compactions.
1230 for (int table = 0; table < 3; ++table) {
1231 for (int i = 0; i < 10 + table; ++i) {
1232 Put(1, ToString(table * 100 + i), "val");
1233 }
1234 Flush(1);
1235 dbfull()->TEST_WaitForCompact();
1236 }
1237 ASSERT_GT(collector_factory->num_created_, 0U);
1238
1239 collector_factory->num_created_ = 0;
1240 dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1]);
1241 ASSERT_GT(collector_factory->num_created_, 0U);
1242
1243 // Come back to write to default column family
1244 collector_factory->num_created_ = 0;
1245 collector_factory->set_expected_column_family_id(0); // default CF
1246 // Create 4 tables in default column family
1247 for (int table = 0; table < 2; ++table) {
1248 for (int i = 0; i < 10 + table; ++i) {
1249 Put(ToString(table * 100 + i), "val");
1250 }
1251 Flush();
1252 }
1253 ASSERT_GT(collector_factory->num_created_, 0U);
1254
1255 collector_factory->num_created_ = 0;
1256 // Trigger automatic compactions.
1257 for (int table = 0; table < 3; ++table) {
1258 for (int i = 0; i < 10 + table; ++i) {
1259 Put(ToString(table * 100 + i), "val");
1260 }
1261 Flush();
1262 dbfull()->TEST_WaitForCompact();
1263 }
1264 ASSERT_GT(collector_factory->num_created_, 0U);
1265
1266 collector_factory->num_created_ = 0;
1267 dbfull()->TEST_CompactRange(0, nullptr, nullptr);
1268 ASSERT_GT(collector_factory->num_created_, 0U);
1269}
1270
1271#ifndef ROCKSDB_LITE
1272TEST_F(DBPropertiesTest, TablePropertiesNeedCompactTest) {
1273 Random rnd(301);
1274
1275 Options options;
1276 options.create_if_missing = true;
1277 options.write_buffer_size = 4096;
1278 options.max_write_buffer_number = 8;
1279 options.level0_file_num_compaction_trigger = 2;
1280 options.level0_slowdown_writes_trigger = 2;
1281 options.level0_stop_writes_trigger = 4;
1282 options.target_file_size_base = 2048;
1283 options.max_bytes_for_level_base = 10240;
1284 options.max_bytes_for_level_multiplier = 4;
1285 options.soft_pending_compaction_bytes_limit = 1024 * 1024;
1286 options.num_levels = 8;
1287 options.env = env_;
1288
1289 std::shared_ptr<TablePropertiesCollectorFactory> collector_factory =
1290 std::make_shared<CountingDeleteTabPropCollectorFactory>();
1291 options.table_properties_collector_factories.resize(1);
1292 options.table_properties_collector_factories[0] = collector_factory;
1293
1294 DestroyAndReopen(options);
1295
1296 const int kMaxKey = 1000;
1297 for (int i = 0; i < kMaxKey; i++) {
1298 ASSERT_OK(Put(Key(i), RandomString(&rnd, 102)));
1299 ASSERT_OK(Put(Key(kMaxKey + i), RandomString(&rnd, 102)));
1300 }
1301 Flush();
1302 dbfull()->TEST_WaitForCompact();
1303 if (NumTableFilesAtLevel(0) == 1) {
1304 // Clear Level 0 so that when later flush a file with deletions,
1305 // we don't trigger an organic compaction.
1306 ASSERT_OK(Put(Key(0), ""));
1307 ASSERT_OK(Put(Key(kMaxKey * 2), ""));
1308 Flush();
1309 dbfull()->TEST_WaitForCompact();
1310 }
1311 ASSERT_EQ(NumTableFilesAtLevel(0), 0);
1312
1313 {
1314 int c = 0;
1315 std::unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions()));
1316 iter->Seek(Key(kMaxKey - 100));
1317 while (iter->Valid() && iter->key().compare(Key(kMaxKey + 100)) < 0) {
1318 iter->Next();
1319 ++c;
1320 }
1321 ASSERT_EQ(c, 200);
1322 }
1323
1324 Delete(Key(0));
1325 for (int i = kMaxKey - 100; i < kMaxKey + 100; i++) {
1326 Delete(Key(i));
1327 }
1328 Delete(Key(kMaxKey * 2));
1329
1330 Flush();
1331 dbfull()->TEST_WaitForCompact();
1332
1333 {
1334 SetPerfLevel(kEnableCount);
11fdf7f2 1335 get_perf_context()->Reset();
7c673cae
FG
1336 int c = 0;
1337 std::unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions()));
1338 iter->Seek(Key(kMaxKey - 100));
1339 while (iter->Valid() && iter->key().compare(Key(kMaxKey + 100)) < 0) {
1340 iter->Next();
1341 }
1342 ASSERT_EQ(c, 0);
11fdf7f2
TL
1343 ASSERT_LT(get_perf_context()->internal_delete_skipped_count, 30u);
1344 ASSERT_LT(get_perf_context()->internal_key_skipped_count, 30u);
7c673cae
FG
1345 SetPerfLevel(kDisable);
1346 }
1347}
1348
1349TEST_F(DBPropertiesTest, NeedCompactHintPersistentTest) {
1350 Random rnd(301);
1351
1352 Options options;
1353 options.create_if_missing = true;
1354 options.max_write_buffer_number = 8;
1355 options.level0_file_num_compaction_trigger = 10;
1356 options.level0_slowdown_writes_trigger = 10;
1357 options.level0_stop_writes_trigger = 10;
1358 options.disable_auto_compactions = true;
1359 options.env = env_;
1360
1361 std::shared_ptr<TablePropertiesCollectorFactory> collector_factory =
1362 std::make_shared<CountingDeleteTabPropCollectorFactory>();
1363 options.table_properties_collector_factories.resize(1);
1364 options.table_properties_collector_factories[0] = collector_factory;
1365
1366 DestroyAndReopen(options);
1367
1368 const int kMaxKey = 100;
1369 for (int i = 0; i < kMaxKey; i++) {
1370 ASSERT_OK(Put(Key(i), ""));
1371 }
1372 Flush();
1373 dbfull()->TEST_WaitForFlushMemTable();
1374
1375 for (int i = 1; i < kMaxKey - 1; i++) {
1376 Delete(Key(i));
1377 }
1378 Flush();
1379 dbfull()->TEST_WaitForFlushMemTable();
1380 ASSERT_EQ(NumTableFilesAtLevel(0), 2);
1381
1382 // Restart the DB. Although number of files didn't reach
1383 // options.level0_file_num_compaction_trigger, compaction should
1384 // still be triggered because of the need-compaction hint.
1385 options.disable_auto_compactions = false;
1386 Reopen(options);
1387 dbfull()->TEST_WaitForCompact();
1388 ASSERT_EQ(NumTableFilesAtLevel(0), 0);
1389 {
1390 SetPerfLevel(kEnableCount);
11fdf7f2 1391 get_perf_context()->Reset();
7c673cae
FG
1392 int c = 0;
1393 std::unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions()));
1394 for (iter->Seek(Key(0)); iter->Valid(); iter->Next()) {
1395 c++;
1396 }
1397 ASSERT_EQ(c, 2);
11fdf7f2 1398 ASSERT_EQ(get_perf_context()->internal_delete_skipped_count, 0);
7c673cae 1399 // We iterate every key twice. Is it a bug?
11fdf7f2 1400 ASSERT_LE(get_perf_context()->internal_key_skipped_count, 2);
7c673cae
FG
1401 SetPerfLevel(kDisable);
1402 }
1403}
11fdf7f2
TL
1404
1405TEST_F(DBPropertiesTest, EstimateNumKeysUnderflow) {
1406 Options options;
1407 Reopen(options);
1408 Put("foo", "bar");
1409 Delete("foo");
1410 Delete("foo");
1411 uint64_t num_keys = 0;
1412 ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.estimate-num-keys", &num_keys));
1413 ASSERT_EQ(0, num_keys);
1414}
1415
1416TEST_F(DBPropertiesTest, EstimateOldestKeyTime) {
1417 std::unique_ptr<MockTimeEnv> mock_env(new MockTimeEnv(Env::Default()));
1418 uint64_t oldest_key_time = 0;
1419 Options options;
1420 options.env = mock_env.get();
1421
1422 // "rocksdb.estimate-oldest-key-time" only available to fifo compaction.
1423 mock_env->set_current_time(100);
1424 for (auto compaction : {kCompactionStyleLevel, kCompactionStyleUniversal,
1425 kCompactionStyleNone}) {
1426 options.compaction_style = compaction;
1427 options.create_if_missing = true;
1428 DestroyAndReopen(options);
1429 ASSERT_OK(Put("foo", "bar"));
1430 ASSERT_FALSE(dbfull()->GetIntProperty(
1431 DB::Properties::kEstimateOldestKeyTime, &oldest_key_time));
1432 }
1433
1434 options.compaction_style = kCompactionStyleFIFO;
494da23a 1435 options.ttl = 300;
11fdf7f2
TL
1436 options.compaction_options_fifo.allow_compaction = false;
1437 DestroyAndReopen(options);
1438
1439 mock_env->set_current_time(100);
1440 ASSERT_OK(Put("k1", "v1"));
1441 ASSERT_TRUE(dbfull()->GetIntProperty(DB::Properties::kEstimateOldestKeyTime,
1442 &oldest_key_time));
1443 ASSERT_EQ(100, oldest_key_time);
1444 ASSERT_OK(Flush());
1445 ASSERT_EQ("1", FilesPerLevel());
1446 ASSERT_TRUE(dbfull()->GetIntProperty(DB::Properties::kEstimateOldestKeyTime,
1447 &oldest_key_time));
1448 ASSERT_EQ(100, oldest_key_time);
1449
1450 mock_env->set_current_time(200);
1451 ASSERT_OK(Put("k2", "v2"));
1452 ASSERT_OK(Flush());
1453 ASSERT_EQ("2", FilesPerLevel());
1454 ASSERT_TRUE(dbfull()->GetIntProperty(DB::Properties::kEstimateOldestKeyTime,
1455 &oldest_key_time));
1456 ASSERT_EQ(100, oldest_key_time);
1457
1458 mock_env->set_current_time(300);
1459 ASSERT_OK(Put("k3", "v3"));
1460 ASSERT_OK(Flush());
1461 ASSERT_EQ("3", FilesPerLevel());
1462 ASSERT_TRUE(dbfull()->GetIntProperty(DB::Properties::kEstimateOldestKeyTime,
1463 &oldest_key_time));
1464 ASSERT_EQ(100, oldest_key_time);
1465
1466 mock_env->set_current_time(450);
1467 ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
1468 ASSERT_EQ("2", FilesPerLevel());
1469 ASSERT_TRUE(dbfull()->GetIntProperty(DB::Properties::kEstimateOldestKeyTime,
1470 &oldest_key_time));
1471 ASSERT_EQ(200, oldest_key_time);
1472
1473 mock_env->set_current_time(550);
1474 ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
1475 ASSERT_EQ("1", FilesPerLevel());
1476 ASSERT_TRUE(dbfull()->GetIntProperty(DB::Properties::kEstimateOldestKeyTime,
1477 &oldest_key_time));
1478 ASSERT_EQ(300, oldest_key_time);
1479
1480 mock_env->set_current_time(650);
1481 ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
1482 ASSERT_EQ("", FilesPerLevel());
1483 ASSERT_FALSE(dbfull()->GetIntProperty(DB::Properties::kEstimateOldestKeyTime,
1484 &oldest_key_time));
1485
1486 // Close before mock_env destructs.
1487 Close();
1488}
1489
1490TEST_F(DBPropertiesTest, SstFilesSize) {
1491 struct TestListener : public EventListener {
1492 void OnCompactionCompleted(DB* db,
1493 const CompactionJobInfo& /*info*/) override {
1494 assert(callback_triggered == false);
1495 assert(size_before_compaction > 0);
1496 callback_triggered = true;
1497 uint64_t total_sst_size = 0;
1498 uint64_t live_sst_size = 0;
1499 bool ok = db->GetIntProperty(DB::Properties::kTotalSstFilesSize,
1500 &total_sst_size);
1501 ASSERT_TRUE(ok);
1502 // total_sst_size include files before and after compaction.
1503 ASSERT_GT(total_sst_size, size_before_compaction);
1504 ok =
1505 db->GetIntProperty(DB::Properties::kLiveSstFilesSize, &live_sst_size);
1506 ASSERT_TRUE(ok);
1507 // live_sst_size only include files after compaction.
1508 ASSERT_GT(live_sst_size, 0);
1509 ASSERT_LT(live_sst_size, size_before_compaction);
1510 }
1511
1512 uint64_t size_before_compaction = 0;
1513 bool callback_triggered = false;
1514 };
1515 std::shared_ptr<TestListener> listener = std::make_shared<TestListener>();
1516
1517 Options options;
1518 options.disable_auto_compactions = true;
1519 options.listeners.push_back(listener);
1520 Reopen(options);
1521
1522 for (int i = 0; i < 10; i++) {
1523 ASSERT_OK(Put("key" + ToString(i), std::string(1000, 'v')));
1524 }
1525 ASSERT_OK(Flush());
1526 for (int i = 0; i < 5; i++) {
1527 ASSERT_OK(Delete("key" + ToString(i)));
1528 }
1529 ASSERT_OK(Flush());
1530 uint64_t sst_size;
1531 bool ok = db_->GetIntProperty(DB::Properties::kTotalSstFilesSize, &sst_size);
1532 ASSERT_TRUE(ok);
1533 ASSERT_GT(sst_size, 0);
1534 listener->size_before_compaction = sst_size;
1535 // Compact to clean all keys and trigger listener.
1536 ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
1537 ASSERT_TRUE(listener->callback_triggered);
1538}
1539
494da23a
TL
1540TEST_F(DBPropertiesTest, MinObsoleteSstNumberToKeep) {
1541 class TestListener : public EventListener {
1542 public:
1543 void OnTableFileCreated(const TableFileCreationInfo& info) override {
1544 if (info.reason == TableFileCreationReason::kCompaction) {
1545 // Verify the property indicates that SSTs created by a running
1546 // compaction cannot be deleted.
1547 uint64_t created_file_num;
1548 FileType created_file_type;
1549 std::string filename =
1550 info.file_path.substr(info.file_path.rfind('/') + 1);
1551 ASSERT_TRUE(
1552 ParseFileName(filename, &created_file_num, &created_file_type));
1553 ASSERT_EQ(kTableFile, created_file_type);
1554
1555 uint64_t keep_sst_lower_bound;
1556 ASSERT_TRUE(
1557 db_->GetIntProperty(DB::Properties::kMinObsoleteSstNumberToKeep,
1558 &keep_sst_lower_bound));
1559
1560 ASSERT_LE(keep_sst_lower_bound, created_file_num);
1561 validated_ = true;
1562 }
1563 }
1564
1565 void SetDB(DB* db) { db_ = db; }
1566
1567 int GetNumCompactions() { return num_compactions_; }
1568
1569 // True if we've verified the property for at least one output file
1570 bool Validated() { return validated_; }
1571
1572 private:
1573 int num_compactions_ = 0;
1574 bool validated_ = false;
1575 DB* db_ = nullptr;
1576 };
1577
1578 const int kNumL0Files = 4;
1579
1580 std::shared_ptr<TestListener> listener = std::make_shared<TestListener>();
1581
1582 Options options = CurrentOptions();
1583 options.listeners.push_back(listener);
1584 options.level0_file_num_compaction_trigger = kNumL0Files;
1585 DestroyAndReopen(options);
1586 listener->SetDB(db_);
1587
1588 for (int i = 0; i < kNumL0Files; ++i) {
1589 // Make sure they overlap in keyspace to prevent trivial move
1590 Put("key1", "val");
1591 Put("key2", "val");
1592 Flush();
1593 }
1594 dbfull()->TEST_WaitForCompact();
1595 ASSERT_TRUE(listener->Validated());
1596}
1597
11fdf7f2
TL
1598TEST_F(DBPropertiesTest, BlockCacheProperties) {
1599 Options options;
1600 uint64_t value;
1601
1602 // Block cache properties are not available for tables other than
1603 // block-based table.
1604 options.table_factory.reset(NewPlainTableFactory());
1605 Reopen(options);
1606 ASSERT_FALSE(
1607 db_->GetIntProperty(DB::Properties::kBlockCacheCapacity, &value));
1608 ASSERT_FALSE(db_->GetIntProperty(DB::Properties::kBlockCacheUsage, &value));
1609 ASSERT_FALSE(
1610 db_->GetIntProperty(DB::Properties::kBlockCachePinnedUsage, &value));
1611
1612 options.table_factory.reset(NewCuckooTableFactory());
1613 Reopen(options);
1614 ASSERT_FALSE(
1615 db_->GetIntProperty(DB::Properties::kBlockCacheCapacity, &value));
1616 ASSERT_FALSE(db_->GetIntProperty(DB::Properties::kBlockCacheUsage, &value));
1617 ASSERT_FALSE(
1618 db_->GetIntProperty(DB::Properties::kBlockCachePinnedUsage, &value));
1619
1620 // Block cache properties are not available if block cache is not used.
1621 BlockBasedTableOptions table_options;
1622 table_options.no_block_cache = true;
1623 options.table_factory.reset(NewBlockBasedTableFactory(table_options));
1624 Reopen(options);
1625 ASSERT_FALSE(
1626 db_->GetIntProperty(DB::Properties::kBlockCacheCapacity, &value));
1627 ASSERT_FALSE(db_->GetIntProperty(DB::Properties::kBlockCacheUsage, &value));
1628 ASSERT_FALSE(
1629 db_->GetIntProperty(DB::Properties::kBlockCachePinnedUsage, &value));
1630
1631 // Test with empty block cache.
1632 constexpr size_t kCapacity = 100;
1633 auto block_cache = NewLRUCache(kCapacity, 0 /*num_shard_bits*/);
1634 table_options.block_cache = block_cache;
1635 table_options.no_block_cache = false;
1636 options.table_factory.reset(NewBlockBasedTableFactory(table_options));
1637 Reopen(options);
1638 ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kBlockCacheCapacity, &value));
1639 ASSERT_EQ(kCapacity, value);
1640 ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kBlockCacheUsage, &value));
1641 ASSERT_EQ(0, value);
1642 ASSERT_TRUE(
1643 db_->GetIntProperty(DB::Properties::kBlockCachePinnedUsage, &value));
1644 ASSERT_EQ(0, value);
1645
1646 // Insert unpinned item to the cache and check size.
1647 constexpr size_t kSize1 = 50;
1648 block_cache->Insert("item1", nullptr /*value*/, kSize1, nullptr /*deleter*/);
1649 ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kBlockCacheCapacity, &value));
1650 ASSERT_EQ(kCapacity, value);
1651 ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kBlockCacheUsage, &value));
1652 ASSERT_EQ(kSize1, value);
1653 ASSERT_TRUE(
1654 db_->GetIntProperty(DB::Properties::kBlockCachePinnedUsage, &value));
1655 ASSERT_EQ(0, value);
1656
1657 // Insert pinned item to the cache and check size.
1658 constexpr size_t kSize2 = 30;
1659 Cache::Handle* item2 = nullptr;
1660 block_cache->Insert("item2", nullptr /*value*/, kSize2, nullptr /*deleter*/,
1661 &item2);
1662 ASSERT_NE(nullptr, item2);
1663 ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kBlockCacheCapacity, &value));
1664 ASSERT_EQ(kCapacity, value);
1665 ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kBlockCacheUsage, &value));
1666 ASSERT_EQ(kSize1 + kSize2, value);
1667 ASSERT_TRUE(
1668 db_->GetIntProperty(DB::Properties::kBlockCachePinnedUsage, &value));
1669 ASSERT_EQ(kSize2, value);
1670
1671 // Insert another pinned item to make the cache over-sized.
1672 constexpr size_t kSize3 = 80;
1673 Cache::Handle* item3 = nullptr;
1674 block_cache->Insert("item3", nullptr /*value*/, kSize3, nullptr /*deleter*/,
1675 &item3);
1676 ASSERT_NE(nullptr, item2);
1677 ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kBlockCacheCapacity, &value));
1678 ASSERT_EQ(kCapacity, value);
1679 ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kBlockCacheUsage, &value));
1680 // Item 1 is evicted.
1681 ASSERT_EQ(kSize2 + kSize3, value);
1682 ASSERT_TRUE(
1683 db_->GetIntProperty(DB::Properties::kBlockCachePinnedUsage, &value));
1684 ASSERT_EQ(kSize2 + kSize3, value);
1685
1686 // Check size after release.
1687 block_cache->Release(item2);
1688 block_cache->Release(item3);
1689 ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kBlockCacheCapacity, &value));
1690 ASSERT_EQ(kCapacity, value);
1691 ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kBlockCacheUsage, &value));
1692 // item2 will be evicted, while item3 remain in cache after release.
1693 ASSERT_EQ(kSize3, value);
1694 ASSERT_TRUE(
1695 db_->GetIntProperty(DB::Properties::kBlockCachePinnedUsage, &value));
1696 ASSERT_EQ(0, value);
1697}
1698
7c673cae
FG
1699#endif // ROCKSDB_LITE
1700} // namespace rocksdb
1701
1702int main(int argc, char** argv) {
1703 rocksdb::port::InstallStackTraceHandler();
1704 ::testing::InitGoogleTest(&argc, argv);
1705 return RUN_ALL_TESTS();
1706}