YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/rocksdb/db/db_properties_test.cc
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
2
// This source code is licensed under the BSD-style license found in the
3
// LICENSE file in the root directory of this source tree. An additional grant
4
// of patent rights can be found in the PATENTS file in the same directory.
5
//
6
// The following only applies to changes made to this file as part of YugaByte development.
7
//
8
// Portions Copyright (c) YugaByte, Inc.
9
//
10
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
11
// in compliance with the License.  You may obtain a copy of the License at
12
//
13
// http://www.apache.org/licenses/LICENSE-2.0
14
//
15
// Unless required by applicable law or agreed to in writing, software distributed under the License
16
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
17
// or implied.  See the License for the specific language governing permissions and limitations
18
// under the License.
19
//
20
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
21
// Use of this source code is governed by a BSD-style license that can be
22
// found in the LICENSE file. See the AUTHORS file for names of contributors.
23
24
#include <string>
25
26
#include "yb/rocksdb/db/db_test_util.h"
27
#include "yb/rocksdb/perf_context.h"
28
#include "yb/rocksdb/perf_level.h"
29
#include "yb/rocksdb/port/stack_trace.h"
30
31
namespace rocksdb {
32
33
class DBPropertiesTest : public DBTestBase {
34
 public:
35
15
  DBPropertiesTest() : DBTestBase("/db_properties_test") {}
36
};
37
38
#ifndef ROCKSDB_LITE
39
1
TEST_F(DBPropertiesTest, Empty) {
40
30
  do {
41
30
    Options options;
42
30
    options.env = env_;
43
30
    options.write_buffer_size = 100000;  // Small write buffer
44
30
    options = CurrentOptions(options);
45
30
    CreateAndReopenWithCF({"pikachu"}, options);
46
47
30
    std::string num;
48
30
    ASSERT_TRUE(dbfull()->GetProperty(
49
30
        handles_[1], "rocksdb.num-entries-active-mem-table", &num));
50
30
    ASSERT_EQ("0", num);
51
52
30
    ASSERT_OK(Put(1, "foo", "v1"));
53
30
    ASSERT_EQ("v1", Get(1, "foo"));
54
30
    ASSERT_TRUE(dbfull()->GetProperty(
55
30
        handles_[1], "rocksdb.num-entries-active-mem-table", &num));
56
30
    ASSERT_EQ("1", num);
57
58
    // Block sync calls
59
30
    env_->delay_sstable_sync_.store(true, std::memory_order_release);
60
30
    ASSERT_OK(Put(1, "k1", std::string(100000, 'x')));  // Fill memtable
61
30
    ASSERT_TRUE(dbfull()->GetProperty(
62
30
        handles_[1], "rocksdb.num-entries-active-mem-table", &num));
63
30
    ASSERT_EQ("2", num);
64
65
30
    ASSERT_OK(Put(1, "k2", std::string(100000, 'y')));  // Trigger compaction
66
30
    ASSERT_TRUE(dbfull()->GetProperty(
67
30
        handles_[1], "rocksdb.num-entries-active-mem-table", &num));
68
30
    ASSERT_EQ("1", num);
69
70
30
    ASSERT_EQ("v1", Get(1, "foo"));
71
    // Release sync calls
72
30
    env_->delay_sstable_sync_.store(false, std::memory_order_release);
73
74
30
    ASSERT_OK(db_->DisableFileDeletions());
75
30
    ASSERT_TRUE(
76
30
        dbfull()->GetProperty("rocksdb.is-file-deletions-enabled", &num));
77
30
    ASSERT_EQ("1", num);
78
79
30
    ASSERT_OK(db_->DisableFileDeletions());
80
30
    ASSERT_TRUE(
81
30
        dbfull()->GetProperty("rocksdb.is-file-deletions-enabled", &num));
82
30
    ASSERT_EQ("2", num);
83
84
30
    ASSERT_OK(db_->DisableFileDeletions());
85
30
    ASSERT_TRUE(
86
30
        dbfull()->GetProperty("rocksdb.is-file-deletions-enabled", &num));
87
30
    ASSERT_EQ("3", num);
88
89
30
    ASSERT_OK(db_->EnableFileDeletions(false));
90
30
    ASSERT_TRUE(
91
30
        dbfull()->GetProperty("rocksdb.is-file-deletions-enabled", &num));
92
30
    ASSERT_EQ("2", num);
93
94
30
    ASSERT_OK(db_->EnableFileDeletions());
95
30
    ASSERT_TRUE(
96
30
        dbfull()->GetProperty("rocksdb.is-file-deletions-enabled", &num));
97
30
    ASSERT_EQ("0", num);
98
30
  } while (ChangeOptions());
99
1
}
100
101
1
TEST_F(DBPropertiesTest, CurrentVersionNumber) {
102
1
  uint64_t v1, v2, v3;
103
1
  ASSERT_TRUE(
104
1
      dbfull()->GetIntProperty("rocksdb.current-super-version-number", &v1));
105
1
  ASSERT_OK(Put("12345678", ""));
106
1
  ASSERT_TRUE(
107
1
      dbfull()->GetIntProperty("rocksdb.current-super-version-number", &v2));
108
1
  ASSERT_OK(Flush());;
109
1
  ASSERT_TRUE(
110
1
      dbfull()->GetIntProperty("rocksdb.current-super-version-number", &v3));
111
112
1
  ASSERT_EQ(v1, v2);
113
1
  ASSERT_GT(v3, v2);
114
1
}
115
116
1
TEST_F(DBPropertiesTest, GetAggregatedIntPropertyTest) {
117
1
  const int kKeySize = 100;
118
1
  const int kValueSize = 500;
119
1
  const int kKeyNum = 100;
120
121
1
  Options options;
122
1
  options.env = env_;
123
1
  options.create_if_missing = true;
124
1
  options.write_buffer_size = (kKeySize + kValueSize) * kKeyNum / 10;
125
  // Make them never flush
126
1
  options.min_write_buffer_number_to_merge = 1000;
127
1
  options.max_write_buffer_number = 1000;
128
1
  options = CurrentOptions(options);
129
1
  CreateAndReopenWithCF({"one", "two", "three", "four"}, options);
130
131
1
  Random rnd(301);
132
5
  for (auto* handle : handles_) {
133
505
    for (int i = 0; i < kKeyNum; ++i) {
134
500
      ASSERT_OK(db_->Put(
135
500
          WriteOptions(), handle, RandomString(&rnd, kKeySize), RandomString(&rnd, kValueSize)));
136
500
    }
137
5
  }
138
139
1
  uint64_t manual_sum = 0;
140
1
  uint64_t api_sum = 0;
141
1
  uint64_t value = 0;
142
5
  for (auto* handle : handles_) {
143
5
    ASSERT_TRUE(
144
5
        db_->GetIntProperty(handle, DB::Properties::kSizeAllMemTables, &value));
145
5
    manual_sum += value;
146
5
  }
147
1
  ASSERT_TRUE(db_->GetAggregatedIntProperty(DB::Properties::kSizeAllMemTables,
148
1
                                            &api_sum));
149
1
  ASSERT_GT(manual_sum, 0);
150
1
  ASSERT_EQ(manual_sum, api_sum);
151
152
1
  ASSERT_FALSE(db_->GetAggregatedIntProperty(DB::Properties::kDBStats, &value));
153
154
1
  uint64_t before_flush_trm;
155
1
  uint64_t after_flush_trm;
156
5
  for (auto* handle : handles_) {
157
5
    ASSERT_TRUE(db_->GetAggregatedIntProperty(
158
5
        DB::Properties::kEstimateTableReadersMem, &before_flush_trm));
159
160
    // Issue flush and expect larger memory usage of table readers.
161
5
    ASSERT_OK(db_->Flush(FlushOptions(), handle));
162
163
5
    ASSERT_TRUE(db_->GetAggregatedIntProperty(
164
5
        DB::Properties::kEstimateTableReadersMem, &after_flush_trm));
165
5
    ASSERT_GT(after_flush_trm, before_flush_trm);
166
5
  }
167
1
}
168
169
namespace {
170
903
void ResetTableProperties(TableProperties* tp) {
171
903
  tp->data_size = 0;
172
903
  tp->data_index_size = 0;
173
903
  tp->filter_size = 0;
174
903
  tp->filter_index_size = 0;
175
903
  tp->raw_key_size = 0;
176
903
  tp->raw_value_size = 0;
177
903
  tp->num_data_blocks = 0;
178
903
  tp->num_entries = 0;
179
903
  tp->num_filter_blocks = 0;
180
903
  tp->num_data_index_blocks = 0;
181
903
}
182
183
803
void ParseTablePropertiesString(std::string tp_string, TableProperties* tp) {
184
803
  double dummy_double;
185
803
  std::replace(tp_string.begin(), tp_string.end(), ';', ' ');
186
803
  std::replace(tp_string.begin(), tp_string.end(), '=', ' ');
187
803
  ResetTableProperties(tp);
188
189
803
  sscanf(tp_string.c_str(),
190
803
         "# data blocks %" SCNu64
191
803
         " # data index blocks %" SCNu64
192
803
         " # filter blocks %" SCNu64
193
803
         " # entries %" SCNu64
194
803
         " raw key size %" SCNu64
195
803
         " raw average key size %lf "
196
803
         " raw value size %" SCNu64
197
803
         " raw average value size %lf "
198
803
         " data blocks total size %" SCNu64
199
803
         " data index size %" SCNu64
200
803
         " filter blocks total size %" SCNu64
201
803
         " filter index block size %" SCNu64,
202
803
         &tp->num_data_blocks, &tp->num_data_index_blocks, &tp->num_filter_blocks, &tp->num_entries,
203
803
         &tp->raw_key_size, &dummy_double, &tp->raw_value_size, &dummy_double, &tp->data_size,
204
803
         &tp->data_index_size, &tp->filter_size, &tp->filter_index_size);
205
803
}
206
207
500
void VerifySimilar(uint64_t a, uint64_t b, double bias, const std::string& label) {
208
1.00k
  ASSERT_EQ(a == 0U, b == 0U) << " " << label << ": " << a << " vs " << b;
209
500
  if (a == 0) {
210
100
    return;
211
100
  }
212
400
  const double dbl_a = static_cast<double>(a);
213
400
  const double dbl_b = static_cast<double>(b);
214
800
  ASSERT_LT(fabs(dbl_a - dbl_b) / (dbl_a + dbl_b), bias)
215
800
      << " " << label << ": " << a << " vs " << b;
216
400
}
217
218
void VerifyTableProperties(const TableProperties& base_tp,
219
                           const TableProperties& new_tp,
220
                           double filter_size_bias = 0.1,
221
                           double index_size_bias = 0.1,
222
                           double data_size_bias = 0.1,
223
100
                           double num_data_blocks_bias = 0.05) {
224
100
  VerifySimilar(base_tp.data_size, new_tp.data_size, data_size_bias, "data_size");
225
100
  VerifySimilar(
226
100
      base_tp.data_index_size, new_tp.data_index_size, index_size_bias, "data_index_size");
227
100
  VerifySimilar(base_tp.filter_size, new_tp.filter_size, filter_size_bias, "filter_size");
228
100
  VerifySimilar(
229
100
      base_tp.filter_index_size, new_tp.filter_index_size, index_size_bias, "filter_index_size");
230
100
  VerifySimilar(
231
100
      base_tp.num_data_blocks, new_tp.num_data_blocks, num_data_blocks_bias, "num_data_blocks");
232
100
  ASSERT_EQ(base_tp.raw_key_size, new_tp.raw_key_size);
233
100
  ASSERT_EQ(base_tp.raw_value_size, new_tp.raw_value_size);
234
100
  ASSERT_EQ(base_tp.num_entries, new_tp.num_entries);
235
100
}
236
237
void GetExpectedTableProperties(TableProperties* expected_tp,
238
                                const int kKeySize, const int kValueSize,
239
                                const int kKeysPerTable, const int kTableCount,
240
                                const int kBloomBitsPerKey,
241
100
                                const size_t kBlockSize) {
242
100
  const int kKeyCount = kTableCount * kKeysPerTable;
243
100
  const int kAvgSuccessorSize = kKeySize / 6;
244
100
  const int kEncodingSavePerKey = kKeySize / 4;
245
100
  expected_tp->raw_key_size = kKeyCount * (kKeySize + 8);
246
100
  expected_tp->raw_value_size = kKeyCount * kValueSize;
247
100
  expected_tp->num_entries = kKeyCount;
248
100
  expected_tp->num_data_blocks =
249
100
      kTableCount *
250
100
      (kKeysPerTable * (kKeySize - kEncodingSavePerKey + kValueSize)) /
251
100
      kBlockSize;
252
100
  expected_tp->data_size =
253
100
      kTableCount * (kKeysPerTable * (kKeySize + 8 + kValueSize));
254
100
  expected_tp->data_index_size =
255
100
      expected_tp->num_data_blocks * (kAvgSuccessorSize + 12);
256
100
  expected_tp->filter_index_size = 0; // Only applicable to fixed-size filter.
257
100
  expected_tp->filter_size =
258
100
      kTableCount * (kKeysPerTable * kBloomBitsPerKey / 8);
259
  // For block-based filter number of data and filter blocks is the same.
260
100
  expected_tp->num_filter_blocks = expected_tp->num_data_blocks;
261
100
}
262
}  // anonymous namespace
263
264
1
TEST_F(DBPropertiesTest, ValidatePropertyInfo) {
265
33
  for (const auto& ppt_name_and_info : InternalStats::ppt_name_to_info) {
266
    // If C++ gets a std::string_literal, this would be better to check at
267
    // compile-time using static_assert.
268
33
    ASSERT_TRUE(ppt_name_and_info.first.empty() ||
269
33
                !isdigit(ppt_name_and_info.first.back()));
270
271
33
    ASSERT_TRUE((ppt_name_and_info.second.handle_string == nullptr) !=
272
33
                (ppt_name_and_info.second.handle_int == nullptr));
273
33
  }
274
1
}
275
276
1
TEST_F(DBPropertiesTest, AggregatedTableProperties) {
277
4
  for (int kTableCount = 40; kTableCount <= 100; kTableCount += 30) {
278
3
    const int kKeysPerTable = 100;
279
3
    const int kKeySize = 80;
280
3
    const int kValueSize = 200;
281
3
    const int kBloomBitsPerKey = 20;
282
283
3
    Options options = CurrentOptions();
284
3
    options.level0_file_num_compaction_trigger = 8;
285
3
    options.compression = kNoCompression;
286
3
    options.create_if_missing = true;
287
288
3
    BlockBasedTableOptions table_options;
289
3
    table_options.filter_policy.reset(
290
3
        NewBloomFilterPolicy(kBloomBitsPerKey, false));
291
3
    table_options.block_size = 1024;
292
3
    options.table_factory.reset(new BlockBasedTableFactory(table_options));
293
294
3
    DestroyAndReopen(options);
295
296
3
    Random rnd(5632);
297
213
    for (int table = 1; table <= kTableCount; ++table) {
298
21.2k
      for (int i = 0; i < kKeysPerTable; ++i) {
299
21.0k
        ASSERT_OK(db_->Put(
300
21.0k
            WriteOptions(), RandomString(&rnd, kKeySize), RandomString(&rnd, kValueSize)));
301
21.0k
      }
302
210
      ASSERT_OK(db_->Flush(FlushOptions()));
303
210
    }
304
3
    std::string property;
305
3
    db_->GetProperty(DB::Properties::kAggregatedTableProperties, &property);
306
307
3
    TableProperties expected_tp;
308
3
    GetExpectedTableProperties(&expected_tp, kKeySize, kValueSize,
309
3
                               kKeysPerTable, kTableCount, kBloomBitsPerKey,
310
3
                               table_options.block_size);
311
312
3
    TableProperties output_tp;
313
3
    ParseTablePropertiesString(property, &output_tp);
314
315
3
    VerifyTableProperties(expected_tp, output_tp);
316
3
  }
317
1
}
318
319
1
TEST_F(DBPropertiesTest, ReadLatencyHistogramByLevel) {
320
1
  Options options = CurrentOptions();
321
1
  options.write_buffer_size = 110 << 10;
322
1
  options.level0_file_num_compaction_trigger = 6;
323
1
  options.num_levels = 4;
324
1
  options.compression = kNoCompression;
325
1
  options.max_bytes_for_level_base = 4500 << 10;
326
1
  options.target_file_size_base = 98 << 10;
327
1
  options.max_write_buffer_number = 2;
328
1
  options.statistics = rocksdb::CreateDBStatisticsForTests();
329
1
  options.max_open_files = 100;
330
331
1
  BlockBasedTableOptions table_options;
332
1
  table_options.no_block_cache = true;
333
334
1
  DestroyAndReopen(options);
335
1
  int key_index = 0;
336
1
  Random rnd(301);
337
9
  for (int num = 0; num < 8; num++) {
338
8
    ASSERT_OK(Put("foo", "bar"));
339
8
    GenerateNewFile(&rnd, &key_index);
340
8
    ASSERT_OK(dbfull()->TEST_WaitForCompact());
341
8
  }
342
1
  ASSERT_OK(dbfull()->TEST_WaitForCompact());
343
344
1
  std::string prop;
345
1
  ASSERT_TRUE(dbfull()->GetProperty("rocksdb.dbstats", &prop));
346
347
  // Get() after flushes, See latency histogram tracked.
348
801
  for (int key = 0; key < key_index; key++) {
349
800
    Get(Key(key));
350
800
  }
351
1
  ASSERT_TRUE(dbfull()->GetProperty("rocksdb.dbstats", &prop));
352
1
  ASSERT_NE(std::string::npos, prop.find("** Level 0 read latency histogram"));
353
1
  ASSERT_NE(std::string::npos, prop.find("** Level 1 read latency histogram"));
354
1
  ASSERT_EQ(std::string::npos, prop.find("** Level 2 read latency histogram"));
355
356
  // Reopen and issue Get(). See thee latency tracked
357
1
  Reopen(options);
358
1
  ASSERT_OK(dbfull()->TEST_WaitForCompact());
359
801
  for (int key = 0; key < key_index; key++) {
360
800
    Get(Key(key));
361
800
  }
362
1
  ASSERT_TRUE(dbfull()->GetProperty("rocksdb.dbstats", &prop));
363
1
  ASSERT_NE(std::string::npos, prop.find("** Level 0 read latency histogram"));
364
1
  ASSERT_NE(std::string::npos, prop.find("** Level 1 read latency histogram"));
365
1
  ASSERT_EQ(std::string::npos, prop.find("** Level 2 read latency histogram"));
366
367
  // Reopen and issue iterating. See thee latency tracked
368
1
  Reopen(options);
369
1
  ASSERT_TRUE(dbfull()->GetProperty("rocksdb.dbstats", &prop));
370
1
  ASSERT_EQ(std::string::npos, prop.find("** Level 0 read latency histogram"));
371
1
  ASSERT_EQ(std::string::npos, prop.find("** Level 1 read latency histogram"));
372
1
  ASSERT_EQ(std::string::npos, prop.find("** Level 2 read latency histogram"));
373
1
  {
374
1
    unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions()));
375
801
    for (iter->Seek(Key(0)); iter->Valid(); iter->Next()) {
376
800
    }
377
1
  }
378
1
  ASSERT_TRUE(dbfull()->GetProperty("rocksdb.dbstats", &prop));
379
1
  ASSERT_NE(std::string::npos, prop.find("** Level 0 read latency histogram"));
380
1
  ASSERT_NE(std::string::npos, prop.find("** Level 1 read latency histogram"));
381
1
  ASSERT_EQ(std::string::npos, prop.find("** Level 2 read latency histogram"));
382
383
  // options.max_open_files preloads table readers.
384
1
  options.max_open_files = -1;
385
1
  Reopen(options);
386
1
  ASSERT_TRUE(dbfull()->GetProperty("rocksdb.dbstats", &prop));
387
1
  ASSERT_NE(std::string::npos, prop.find("** Level 0 read latency histogram"));
388
1
  ASSERT_NE(std::string::npos, prop.find("** Level 1 read latency histogram"));
389
1
  ASSERT_EQ(std::string::npos, prop.find("** Level 2 read latency histogram"));
390
801
  for (int key = 0; key < key_index; key++) {
391
800
    Get(Key(key));
392
800
  }
393
1
  ASSERT_TRUE(dbfull()->GetProperty("rocksdb.dbstats", &prop));
394
1
  ASSERT_NE(std::string::npos, prop.find("** Level 0 read latency histogram"));
395
1
  ASSERT_NE(std::string::npos, prop.find("** Level 1 read latency histogram"));
396
1
  ASSERT_EQ(std::string::npos, prop.find("** Level 2 read latency histogram"));
397
1
}
398
399
1
TEST_F(DBPropertiesTest, AggregatedTablePropertiesAtLevel) {
400
1
  const int kTableCount = 100;
401
1
  const int kKeysPerTable = 10;
402
1
  const int kKeySize = 50;
403
1
  const int kValueSize = 400;
404
1
  const int kMaxLevel = 7;
405
1
  const int kBloomBitsPerKey = 20;
406
1
  Random rnd(301);
407
1
  Options options = CurrentOptions();
408
1
  options.level0_file_num_compaction_trigger = 8;
409
1
  options.compression = kNoCompression;
410
1
  options.create_if_missing = true;
411
1
  options.level0_file_num_compaction_trigger = 2;
412
1
  options.target_file_size_base = 8192;
413
1
  options.max_bytes_for_level_base = 10000;
414
1
  options.max_bytes_for_level_multiplier = 2;
415
  // This ensures there no compaction happening when we call GetProperty().
416
1
  options.disable_auto_compactions = true;
417
418
1
  BlockBasedTableOptions table_options;
419
1
  table_options.filter_policy.reset(
420
1
      NewBloomFilterPolicy(kBloomBitsPerKey, false));
421
1
  table_options.block_size = 1024;
422
1
  options.table_factory.reset(new BlockBasedTableFactory(table_options));
423
424
1
  DestroyAndReopen(options);
425
426
1
  std::string level_tp_strings[kMaxLevel];
427
1
  std::string tp_string;
428
1
  TableProperties level_tps[kMaxLevel];
429
1
  TableProperties tp, sum_tp, expected_tp;
430
101
  for (int table = 1; table <= kTableCount; ++table) {
431
1.10k
    for (int i = 0; i < kKeysPerTable; ++i) {
432
1.00k
      ASSERT_OK(db_->Put(WriteOptions(), RandomString(&rnd, kKeySize),
433
1.00k
                         RandomString(&rnd, kValueSize)));
434
1.00k
    }
435
100
    ASSERT_OK(db_->Flush(FlushOptions()));
436
100
    ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
437
100
    ResetTableProperties(&sum_tp);
438
800
    for (int level = 0; level < kMaxLevel; ++level) {
439
700
      db_->GetProperty(
440
700
          DB::Properties::kAggregatedTablePropertiesAtLevel + ToString(level),
441
700
          &level_tp_strings[level]);
442
700
      ParseTablePropertiesString(level_tp_strings[level], &level_tps[level]);
443
700
      sum_tp.data_size += level_tps[level].data_size;
444
700
      sum_tp.data_index_size += level_tps[level].data_index_size;
445
700
      sum_tp.filter_size += level_tps[level].filter_size;
446
700
      sum_tp.filter_index_size += level_tps[level].filter_index_size;
447
700
      sum_tp.raw_key_size += level_tps[level].raw_key_size;
448
700
      sum_tp.raw_value_size += level_tps[level].raw_value_size;
449
700
      sum_tp.num_data_blocks += level_tps[level].num_data_blocks;
450
700
      sum_tp.num_entries += level_tps[level].num_entries;
451
700
      sum_tp.num_filter_blocks += level_tps[level].num_filter_blocks;
452
700
      sum_tp.num_data_index_blocks += level_tps[level].num_data_index_blocks;
453
700
    }
454
100
    db_->GetProperty(DB::Properties::kAggregatedTableProperties, &tp_string);
455
100
    ParseTablePropertiesString(tp_string, &tp);
456
100
    ASSERT_EQ(sum_tp.data_size, tp.data_size);
457
100
    ASSERT_EQ(sum_tp.data_index_size, tp.data_index_size);
458
100
    ASSERT_EQ(sum_tp.filter_size, tp.filter_size);
459
100
    ASSERT_EQ(sum_tp.filter_index_size, tp.filter_index_size);
460
100
    ASSERT_EQ(sum_tp.raw_key_size, tp.raw_key_size);
461
100
    ASSERT_EQ(sum_tp.raw_value_size, tp.raw_value_size);
462
100
    ASSERT_EQ(sum_tp.num_data_blocks, tp.num_data_blocks);
463
100
    ASSERT_EQ(sum_tp.num_entries, tp.num_entries);
464
100
    ASSERT_EQ(sum_tp.num_filter_blocks, tp.num_filter_blocks);
465
100
    ASSERT_EQ(sum_tp.num_data_index_blocks, tp.num_data_index_blocks);
466
100
    if (table > 3) {
467
97
      GetExpectedTableProperties(&expected_tp, kKeySize, kValueSize,
468
97
                                 kKeysPerTable, table, kBloomBitsPerKey,
469
97
                                 table_options.block_size);
470
      // Gives larger bias here as index block size, filter block size,
471
      // and data block size become much harder to estimate in this test.
472
97
      VerifyTableProperties(tp, expected_tp, 0.5, 0.4, 0.4, 0.25);
473
97
    }
474
100
  }
475
1
}
476
477
1
TEST_F(DBPropertiesTest, NumImmutableMemTable) {
478
5
  do {
479
5
    Options options = CurrentOptions();
480
5
    WriteOptions writeOpt = WriteOptions();
481
5
    writeOpt.disableWAL = true;
482
5
    options.max_write_buffer_number = 4;
483
5
    options.min_write_buffer_number_to_merge = 3;
484
5
    options.max_write_buffer_number_to_maintain = 4;
485
5
    options.write_buffer_size = 1000000;
486
5
    CreateAndReopenWithCF({"pikachu"}, options);
487
488
5
    std::string big_value(1000000 * 2, 'x');
489
5
    std::string num;
490
5
    SetPerfLevel(PerfLevel::kEnableTime);
491
5
    ASSERT_TRUE(GetPerfLevel() == PerfLevel::kEnableTime);
492
493
5
    ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "k1", big_value));
494
5
    ASSERT_TRUE(dbfull()->GetProperty(handles_[1],
495
5
                                      "rocksdb.num-immutable-mem-table", &num));
496
5
    ASSERT_EQ(num, "0");
497
5
    ASSERT_TRUE(dbfull()->GetProperty(
498
5
        handles_[1], DB::Properties::kNumImmutableMemTableFlushed, &num));
499
5
    ASSERT_EQ(num, "0");
500
5
    ASSERT_TRUE(dbfull()->GetProperty(
501
5
        handles_[1], "rocksdb.num-entries-active-mem-table", &num));
502
5
    ASSERT_EQ(num, "1");
503
5
    perf_context.Reset();
504
5
    Get(1, "k1");
505
5
    ASSERT_EQ(1, static_cast<int>(perf_context.get_from_memtable_count));
506
507
5
    ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "k2", big_value));
508
5
    ASSERT_TRUE(dbfull()->GetProperty(handles_[1],
509
5
                                      "rocksdb.num-immutable-mem-table", &num));
510
5
    ASSERT_EQ(num, "1");
511
5
    ASSERT_TRUE(dbfull()->GetProperty(
512
5
        handles_[1], "rocksdb.num-entries-active-mem-table", &num));
513
5
    ASSERT_EQ(num, "1");
514
5
    ASSERT_TRUE(dbfull()->GetProperty(
515
5
        handles_[1], "rocksdb.num-entries-imm-mem-tables", &num));
516
5
    ASSERT_EQ(num, "1");
517
518
5
    perf_context.Reset();
519
5
    Get(1, "k1");
520
5
    ASSERT_EQ(2, static_cast<int>(perf_context.get_from_memtable_count));
521
5
    perf_context.Reset();
522
5
    Get(1, "k2");
523
5
    ASSERT_EQ(1, static_cast<int>(perf_context.get_from_memtable_count));
524
525
5
    ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "k3", big_value));
526
5
    ASSERT_TRUE(dbfull()->GetProperty(
527
5
        handles_[1], "rocksdb.cur-size-active-mem-table", &num));
528
5
    ASSERT_TRUE(dbfull()->GetProperty(handles_[1],
529
5
                                      "rocksdb.num-immutable-mem-table", &num));
530
5
    ASSERT_EQ(num, "2");
531
5
    ASSERT_TRUE(dbfull()->GetProperty(
532
5
        handles_[1], "rocksdb.num-entries-active-mem-table", &num));
533
5
    ASSERT_EQ(num, "1");
534
5
    ASSERT_TRUE(dbfull()->GetProperty(
535
5
        handles_[1], "rocksdb.num-entries-imm-mem-tables", &num));
536
5
    ASSERT_EQ(num, "2");
537
5
    perf_context.Reset();
538
5
    Get(1, "k2");
539
5
    ASSERT_EQ(2, static_cast<int>(perf_context.get_from_memtable_count));
540
5
    perf_context.Reset();
541
5
    Get(1, "k3");
542
5
    ASSERT_EQ(1, static_cast<int>(perf_context.get_from_memtable_count));
543
5
    perf_context.Reset();
544
5
    Get(1, "k1");
545
5
    ASSERT_EQ(3, static_cast<int>(perf_context.get_from_memtable_count));
546
547
5
    ASSERT_OK(Flush(1));
548
5
    ASSERT_TRUE(dbfull()->GetProperty(handles_[1],
549
5
                                      "rocksdb.num-immutable-mem-table", &num));
550
5
    ASSERT_EQ(num, "0");
551
5
    ASSERT_TRUE(dbfull()->GetProperty(
552
5
        handles_[1], DB::Properties::kNumImmutableMemTableFlushed, &num));
553
5
    ASSERT_EQ(num, "3");
554
5
    ASSERT_TRUE(dbfull()->GetProperty(
555
5
        handles_[1], "rocksdb.cur-size-active-mem-table", &num));
556
    // "192" is the size of the metadata of an empty skiplist, this would
557
    // break if we change the default skiplist implementation
558
5
    ASSERT_EQ(num, "192");
559
560
5
    uint64_t int_num;
561
5
    uint64_t base_total_size;
562
5
    ASSERT_TRUE(dbfull()->GetIntProperty(
563
5
        handles_[1], "rocksdb.estimate-num-keys", &base_total_size));
564
565
5
    ASSERT_OK(dbfull()->Delete(writeOpt, handles_[1], "k2"));
566
5
    ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "k3", ""));
567
5
    ASSERT_OK(dbfull()->Delete(writeOpt, handles_[1], "k3"));
568
5
    ASSERT_TRUE(dbfull()->GetIntProperty(
569
5
        handles_[1], "rocksdb.num-deletes-active-mem-table", &int_num));
570
5
    ASSERT_EQ(int_num, 2U);
571
5
    ASSERT_TRUE(dbfull()->GetIntProperty(
572
5
        handles_[1], "rocksdb.num-entries-active-mem-table", &int_num));
573
5
    ASSERT_EQ(int_num, 3U);
574
575
5
    ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "k2", big_value));
576
5
    ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "k2", big_value));
577
5
    ASSERT_TRUE(dbfull()->GetIntProperty(
578
5
        handles_[1], "rocksdb.num-entries-imm-mem-tables", &int_num));
579
5
    ASSERT_EQ(int_num, 4U);
580
5
    ASSERT_TRUE(dbfull()->GetIntProperty(
581
5
        handles_[1], "rocksdb.num-deletes-imm-mem-tables", &int_num));
582
5
    ASSERT_EQ(int_num, 2U);
583
584
5
    ASSERT_TRUE(dbfull()->GetIntProperty(
585
5
        handles_[1], "rocksdb.estimate-num-keys", &int_num));
586
5
    ASSERT_EQ(int_num, base_total_size + 1);
587
588
5
    SetPerfLevel(PerfLevel::kDisable);
589
5
    ASSERT_TRUE(GetPerfLevel() == PerfLevel::kDisable);
590
5
  } while (ChangeCompactOptions());
591
1
}
592
593
1
TEST_F(DBPropertiesTest, GetProperty) {
594
  // Set sizes to both background thread pool to be 1 and block them.
595
1
  env_->SetBackgroundThreads(1, Env::HIGH);
596
1
  env_->SetBackgroundThreads(1, Env::LOW);
597
1
  test::SleepingBackgroundTask sleeping_task_low;
598
1
  env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task_low,
599
1
                 Env::Priority::LOW);
600
1
  test::SleepingBackgroundTask sleeping_task_high;
601
1
  env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
602
1
                 &sleeping_task_high, Env::Priority::HIGH);
603
604
1
  Options options = CurrentOptions();
605
1
  WriteOptions writeOpt = WriteOptions();
606
1
  writeOpt.disableWAL = true;
607
1
  options.compaction_style = kCompactionStyleUniversal;
608
1
  options.level0_file_num_compaction_trigger = 1;
609
1
  options.compaction_options_universal.size_ratio = 50;
610
1
  options.max_background_compactions = 1;
611
1
  options.max_background_flushes = 1;
612
1
  options.max_write_buffer_number = 10;
613
1
  options.min_write_buffer_number_to_merge = 1;
614
1
  options.max_write_buffer_number_to_maintain = 0;
615
1
  options.write_buffer_size = 1000000;
616
1
  Reopen(options);
617
618
1
  std::string big_value(1000000 * 2, 'x');
619
1
  std::string num;
620
1
  uint64_t int_num;
621
1
  SetPerfLevel(PerfLevel::kEnableTime);
622
623
1
  ASSERT_TRUE(
624
1
      dbfull()->GetIntProperty("rocksdb.estimate-table-readers-mem", &int_num));
625
1
  ASSERT_EQ(int_num, 0U);
626
1
  ASSERT_TRUE(
627
1
      dbfull()->GetIntProperty("rocksdb.estimate-live-data-size", &int_num));
628
1
  ASSERT_EQ(int_num, 0U);
629
630
1
  ASSERT_OK(dbfull()->Put(writeOpt, "k1", big_value));
631
1
  ASSERT_TRUE(dbfull()->GetProperty("rocksdb.num-immutable-mem-table", &num));
632
1
  ASSERT_EQ(num, "0");
633
1
  ASSERT_TRUE(dbfull()->GetProperty("rocksdb.mem-table-flush-pending", &num));
634
1
  ASSERT_EQ(num, "0");
635
1
  ASSERT_TRUE(dbfull()->GetProperty("rocksdb.compaction-pending", &num));
636
1
  ASSERT_EQ(num, "0");
637
1
  ASSERT_TRUE(dbfull()->GetProperty("rocksdb.estimate-num-keys", &num));
638
1
  ASSERT_EQ(num, "1");
639
1
  perf_context.Reset();
640
641
1
  ASSERT_OK(dbfull()->Put(writeOpt, "k2", big_value));
642
1
  ASSERT_TRUE(dbfull()->GetProperty("rocksdb.num-immutable-mem-table", &num));
643
1
  ASSERT_EQ(num, "1");
644
1
  ASSERT_OK(dbfull()->Delete(writeOpt, "k-non-existing"));
645
1
  ASSERT_OK(dbfull()->Put(writeOpt, "k3", big_value));
646
1
  ASSERT_TRUE(dbfull()->GetProperty("rocksdb.num-immutable-mem-table", &num));
647
1
  ASSERT_EQ(num, "2");
648
1
  ASSERT_TRUE(dbfull()->GetProperty("rocksdb.mem-table-flush-pending", &num));
649
1
  ASSERT_EQ(num, "1");
650
1
  ASSERT_TRUE(dbfull()->GetProperty("rocksdb.compaction-pending", &num));
651
1
  ASSERT_EQ(num, "0");
652
1
  ASSERT_TRUE(dbfull()->GetProperty("rocksdb.estimate-num-keys", &num));
653
1
  ASSERT_EQ(num, "2");
654
  // Verify the same set of properties through GetIntProperty
655
1
  ASSERT_TRUE(
656
1
      dbfull()->GetIntProperty("rocksdb.num-immutable-mem-table", &int_num));
657
1
  ASSERT_EQ(int_num, 2U);
658
1
  ASSERT_TRUE(
659
1
      dbfull()->GetIntProperty("rocksdb.mem-table-flush-pending", &int_num));
660
1
  ASSERT_EQ(int_num, 1U);
661
1
  ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.compaction-pending", &int_num));
662
1
  ASSERT_EQ(int_num, 0U);
663
1
  ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.estimate-num-keys", &int_num));
664
1
  ASSERT_EQ(int_num, 2U);
665
666
1
  ASSERT_TRUE(
667
1
      dbfull()->GetIntProperty("rocksdb.estimate-table-readers-mem", &int_num));
668
1
  ASSERT_EQ(int_num, 0U);
669
670
1
  sleeping_task_high.WakeUp();
671
1
  sleeping_task_high.WaitUntilDone();
672
1
  ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
673
674
1
  ASSERT_OK(dbfull()->Put(writeOpt, "k4", big_value));
675
1
  ASSERT_OK(dbfull()->Put(writeOpt, "k5", big_value));
676
1
  ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
677
1
  ASSERT_TRUE(dbfull()->GetProperty("rocksdb.mem-table-flush-pending", &num));
678
1
  ASSERT_EQ(num, "0");
679
1
  ASSERT_TRUE(dbfull()->GetProperty("rocksdb.compaction-pending", &num));
680
1
  ASSERT_EQ(num, "1");
681
1
  ASSERT_TRUE(dbfull()->GetProperty("rocksdb.estimate-num-keys", &num));
682
1
  ASSERT_EQ(num, "4");
683
684
1
  ASSERT_TRUE(
685
1
      dbfull()->GetIntProperty("rocksdb.estimate-table-readers-mem", &int_num));
686
1
  ASSERT_GT(int_num, 0U);
687
688
1
  sleeping_task_low.WakeUp();
689
1
  sleeping_task_low.WaitUntilDone();
690
691
  // Wait for compaction to be done. This is important because otherwise RocksDB
692
  // might schedule a compaction when reopening the database, failing assertion
693
  // (A) as a result.
694
1
  ASSERT_OK(dbfull()->TEST_WaitForCompact());
695
1
  options.max_open_files = 10;
696
1
  Reopen(options);
697
  // After reopening, no table reader is loaded, so no memory for table readers
698
1
  ASSERT_TRUE(
699
1
      dbfull()->GetIntProperty("rocksdb.estimate-table-readers-mem", &int_num));
700
1
  ASSERT_EQ(int_num, 0U);  // (A)
701
1
  ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.estimate-num-keys", &int_num));
702
1
  ASSERT_GT(int_num, 0U);
703
704
  // After reading a key, at least one table reader is loaded.
705
1
  Get("k5");
706
1
  ASSERT_TRUE(
707
1
      dbfull()->GetIntProperty("rocksdb.estimate-table-readers-mem", &int_num));
708
1
  ASSERT_GT(int_num, 0U);
709
710
  // Test rocksdb.num-live-versions
711
1
  {
712
1
    options.level0_file_num_compaction_trigger = 20;
713
1
    Reopen(options);
714
1
    ASSERT_TRUE(
715
1
        dbfull()->GetIntProperty("rocksdb.num-live-versions", &int_num));
716
1
    ASSERT_EQ(int_num, 1U);
717
718
    // Use an iterator to hold current version
719
1
    std::unique_ptr<Iterator> iter1(dbfull()->NewIterator(ReadOptions()));
720
721
1
    ASSERT_OK(dbfull()->Put(writeOpt, "k6", big_value));
722
1
    ASSERT_OK(Flush());;
723
1
    ASSERT_TRUE(
724
1
        dbfull()->GetIntProperty("rocksdb.num-live-versions", &int_num));
725
1
    ASSERT_EQ(int_num, 2U);
726
727
    // Use an iterator to hold current version
728
1
    std::unique_ptr<Iterator> iter2(dbfull()->NewIterator(ReadOptions()));
729
730
1
    ASSERT_OK(dbfull()->Put(writeOpt, "k7", big_value));
731
1
    ASSERT_OK(Flush());;
732
1
    ASSERT_TRUE(
733
1
        dbfull()->GetIntProperty("rocksdb.num-live-versions", &int_num));
734
1
    ASSERT_EQ(int_num, 3U);
735
736
1
    iter2.reset();
737
1
    ASSERT_TRUE(
738
1
        dbfull()->GetIntProperty("rocksdb.num-live-versions", &int_num));
739
1
    ASSERT_EQ(int_num, 2U);
740
741
1
    iter1.reset();
742
1
    ASSERT_TRUE(
743
1
        dbfull()->GetIntProperty("rocksdb.num-live-versions", &int_num));
744
1
    ASSERT_EQ(int_num, 1U);
745
1
  }
746
1
}
747
748
1
TEST_F(DBPropertiesTest, ApproximateMemoryUsage) {
749
1
  const int kNumRounds = 10;
750
  // TODO(noetzli) kFlushesPerRound does not really correlate with how many
751
  // flushes happen.
752
1
  const int kFlushesPerRound = 10;
753
1
  const int kWritesPerFlush = 10;
754
1
  const int kKeySize = 100;
755
1
  const int kValueSize = 1000;
756
1
  Options options;
757
1
  options.write_buffer_size = 1000;  // small write buffer
758
1
  options.min_write_buffer_number_to_merge = 4;
759
1
  options.compression = kNoCompression;
760
1
  options.create_if_missing = true;
761
1
  options = CurrentOptions(options);
762
1
  DestroyAndReopen(options);
763
764
1
  Random rnd(301);
765
766
1
  std::vector<Iterator*> iters;
767
768
1
  uint64_t active_mem;
769
1
  uint64_t unflushed_mem;
770
1
  uint64_t all_mem;
771
1
  uint64_t prev_all_mem;
772
773
  // Phase 0. The verify the initial value of all these properties are the same
774
  // as we have no mem-tables.
775
1
  dbfull()->GetIntProperty("rocksdb.cur-size-active-mem-table", &active_mem);
776
1
  dbfull()->GetIntProperty("rocksdb.cur-size-all-mem-tables", &unflushed_mem);
777
1
  dbfull()->GetIntProperty("rocksdb.size-all-mem-tables", &all_mem);
778
1
  ASSERT_EQ(all_mem, active_mem);
779
1
  ASSERT_EQ(all_mem, unflushed_mem);
780
781
  // Phase 1. Simply issue Put() and expect "cur-size-all-mem-tables" equals to
782
  // "size-all-mem-tables"
783
11
  for (int r = 0; r < kNumRounds; ++r) {
784
110
    for (int f = 0; f < kFlushesPerRound; ++f) {
785
1.10k
      for (int w = 0; w < kWritesPerFlush; ++w) {
786
1.00k
        ASSERT_OK(Put(RandomString(&rnd, kKeySize), RandomString(&rnd, kValueSize)));
787
1.00k
      }
788
100
    }
789
    // Make sure that there is no flush between getting the two properties.
790
10
    ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
791
10
    dbfull()->GetIntProperty("rocksdb.cur-size-all-mem-tables", &unflushed_mem);
792
10
    dbfull()->GetIntProperty("rocksdb.size-all-mem-tables", &all_mem);
793
    // in no iterator case, these two number should be the same.
794
10
    ASSERT_EQ(unflushed_mem, all_mem);
795
10
  }
796
1
  prev_all_mem = all_mem;
797
798
  // Phase 2. Keep issuing Put() but also create new iterators. This time we
799
  // expect "size-all-mem-tables" > "cur-size-all-mem-tables".
800
11
  for (int r = 0; r < kNumRounds; ++r) {
801
10
    iters.push_back(db_->NewIterator(ReadOptions()));
802
110
    for (int f = 0; f < kFlushesPerRound; ++f) {
803
1.10k
      for (int w = 0; w < kWritesPerFlush; ++w) {
804
1.00k
        ASSERT_OK(Put(RandomString(&rnd, kKeySize), RandomString(&rnd, kValueSize)));
805
1.00k
      }
806
100
    }
807
    // Force flush to prevent flush from happening between getting the
808
    // properties or after getting the properties and before the new round.
809
10
    ASSERT_OK(Flush());;
810
811
    // In the second round, add iterators.
812
10
    dbfull()->GetIntProperty("rocksdb.cur-size-active-mem-table", &active_mem);
813
10
    dbfull()->GetIntProperty("rocksdb.cur-size-all-mem-tables", &unflushed_mem);
814
10
    dbfull()->GetIntProperty("rocksdb.size-all-mem-tables", &all_mem);
815
10
    ASSERT_GT(all_mem, active_mem);
816
10
    ASSERT_GT(all_mem, unflushed_mem);
817
10
    ASSERT_GT(all_mem, prev_all_mem);
818
10
    prev_all_mem = all_mem;
819
10
  }
820
821
  // Phase 3. Delete iterators and expect "size-all-mem-tables" shrinks
822
  // whenever we release an iterator.
823
10
  for (auto* iter : iters) {
824
10
    delete iter;
825
10
    dbfull()->GetIntProperty("rocksdb.size-all-mem-tables", &all_mem);
826
    // Expect the size shrinking
827
10
    ASSERT_LT(all_mem, prev_all_mem);
828
10
    prev_all_mem = all_mem;
829
10
  }
830
831
  // Expect all these three counters to be the same.
832
1
  dbfull()->GetIntProperty("rocksdb.cur-size-active-mem-table", &active_mem);
833
1
  dbfull()->GetIntProperty("rocksdb.cur-size-all-mem-tables", &unflushed_mem);
834
1
  dbfull()->GetIntProperty("rocksdb.size-all-mem-tables", &all_mem);
835
1
  ASSERT_EQ(active_mem, unflushed_mem);
836
1
  ASSERT_EQ(unflushed_mem, all_mem);
837
838
  // Phase 5. Reopen, and expect all these three counters to be the same again.
839
1
  Reopen(options);
840
1
  dbfull()->GetIntProperty("rocksdb.cur-size-active-mem-table", &active_mem);
841
1
  dbfull()->GetIntProperty("rocksdb.cur-size-all-mem-tables", &unflushed_mem);
842
1
  dbfull()->GetIntProperty("rocksdb.size-all-mem-tables", &all_mem);
843
1
  ASSERT_EQ(active_mem, unflushed_mem);
844
1
  ASSERT_EQ(unflushed_mem, all_mem);
845
1
}
846
847
1
TEST_F(DBPropertiesTest, EstimatePendingCompBytes) {
848
  // Set sizes to both background thread pool to be 1 and block them.
849
1
  env_->SetBackgroundThreads(1, Env::HIGH);
850
1
  env_->SetBackgroundThreads(1, Env::LOW);
851
1
  test::SleepingBackgroundTask sleeping_task_low;
852
1
  env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task_low,
853
1
                 Env::Priority::LOW);
854
855
1
  Options options = CurrentOptions();
856
1
  WriteOptions writeOpt = WriteOptions();
857
1
  writeOpt.disableWAL = true;
858
1
  options.compaction_style = kCompactionStyleLevel;
859
1
  options.level0_file_num_compaction_trigger = 2;
860
1
  options.max_background_compactions = 1;
861
1
  options.max_background_flushes = 1;
862
1
  options.max_write_buffer_number = 10;
863
1
  options.min_write_buffer_number_to_merge = 1;
864
1
  options.max_write_buffer_number_to_maintain = 0;
865
1
  options.write_buffer_size = 1000000;
866
1
  Reopen(options);
867
868
1
  std::string big_value(1000000 * 2, 'x');
869
1
  std::string num;
870
1
  uint64_t int_num;
871
872
1
  ASSERT_OK(dbfull()->Put(writeOpt, "k1", big_value));
873
1
  ASSERT_OK(Flush());;
874
1
  ASSERT_TRUE(dbfull()->GetIntProperty(
875
1
      "rocksdb.estimate-pending-compaction-bytes", &int_num));
876
1
  ASSERT_EQ(int_num, 0U);
877
878
1
  ASSERT_OK(dbfull()->Put(writeOpt, "k2", big_value));
879
1
  ASSERT_OK(Flush());;
880
1
  ASSERT_TRUE(dbfull()->GetIntProperty(
881
1
      "rocksdb.estimate-pending-compaction-bytes", &int_num));
882
1
  ASSERT_EQ(int_num, 0U);
883
884
1
  ASSERT_OK(dbfull()->Put(writeOpt, "k3", big_value));
885
1
  ASSERT_OK(Flush());;
886
1
  ASSERT_TRUE(dbfull()->GetIntProperty(
887
1
      "rocksdb.estimate-pending-compaction-bytes", &int_num));
888
1
  ASSERT_GT(int_num, 0U);
889
890
1
  sleeping_task_low.WakeUp();
891
1
  sleeping_task_low.WaitUntilDone();
892
893
1
  ASSERT_OK(dbfull()->TEST_WaitForCompact());
894
1
  ASSERT_TRUE(dbfull()->GetIntProperty(
895
1
      "rocksdb.estimate-pending-compaction-bytes", &int_num));
896
1
  ASSERT_EQ(int_num, 0U);
897
1
}
898
#endif  // ROCKSDB_LITE
899
900
class CountingUserTblPropCollector : public TablePropertiesCollector {
901
 public:
902
0
  const char* Name() const override { return "CountingUserTblPropCollector"; }
903
904
38
  Status Finish(UserCollectedProperties* properties) override {
905
38
    std::string encoded;
906
38
    PutVarint32(&encoded, count_);
907
38
    *properties = UserCollectedProperties{
908
38
        {"CountingUserTblPropCollector", message_}, {"Count", encoded},
909
38
    };
910
38
    return Status::OK();
911
38
  }
912
913
  Status AddUserKey(const Slice& user_key, const Slice& value, EntryType type,
914
308
                    SequenceNumber seq, uint64_t file_size) override {
915
308
    ++count_;
916
308
    return Status::OK();
917
308
  }
918
919
19
  UserCollectedProperties GetReadableProperties() const override {
920
19
    return UserCollectedProperties{};
921
19
  }
922
923
 private:
924
  std::string message_ = "Rocksdb";
925
  uint32_t count_ = 0;
926
};
927
928
class CountingUserTblPropCollectorFactory
929
    : public TablePropertiesCollectorFactory {
930
 public:
931
  explicit CountingUserTblPropCollectorFactory(
932
      uint32_t expected_column_family_id)
933
      : expected_column_family_id_(expected_column_family_id),
934
2
        num_created_(0) {}
935
  virtual TablePropertiesCollector* CreateTablePropertiesCollector(
936
19
      TablePropertiesCollectorFactory::Context context) override {
937
19
    EXPECT_EQ(expected_column_family_id_, context.column_family_id);
938
19
    num_created_++;
939
19
    return new CountingUserTblPropCollector();
940
19
  }
941
4
  const char* Name() const override {
942
4
    return "CountingUserTblPropCollectorFactory";
943
4
  }
944
1
  void set_expected_column_family_id(uint32_t v) {
945
1
    expected_column_family_id_ = v;
946
1
  }
947
  uint32_t expected_column_family_id_;
948
  uint32_t num_created_;
949
};
950
951
class CountingDeleteTabPropCollector : public TablePropertiesCollector {
952
 public:
953
0
  const char* Name() const override { return "CountingDeleteTabPropCollector"; }
954
955
  Status AddUserKey(const Slice& user_key, const Slice& value, EntryType type,
956
5.88k
                    SequenceNumber seq, uint64_t file_size) override {
957
5.88k
    if (type == kEntryDelete) {
958
804
      num_deletes_++;
959
804
    }
960
5.88k
    return Status::OK();
961
5.88k
  }
962
963
99
  bool NeedCompact() const override { return num_deletes_ > 10; }
964
965
99
  UserCollectedProperties GetReadableProperties() const override {
966
99
    return UserCollectedProperties{};
967
99
  }
968
969
198
  Status Finish(UserCollectedProperties* properties) override {
970
198
    *properties =
971
198
        UserCollectedProperties{{"num_delete", ToString(num_deletes_)}};
972
198
    return Status::OK();
973
198
  }
974
975
 private:
976
  uint32_t num_deletes_ = 0;
977
};
978
979
class CountingDeleteTabPropCollectorFactory
980
    : public TablePropertiesCollectorFactory {
981
 public:
982
  virtual TablePropertiesCollector* CreateTablePropertiesCollector(
983
99
      TablePropertiesCollectorFactory::Context context) override {
984
99
    return new CountingDeleteTabPropCollector();
985
99
  }
986
3
  const char* Name() const override {
987
3
    return "CountingDeleteTabPropCollectorFactory";
988
3
  }
989
};
990
991
#ifndef ROCKSDB_LITE
992
1
TEST_F(DBPropertiesTest, GetUserDefinedTableProperties) {
993
1
  Options options = CurrentOptions();
994
1
  options.level0_file_num_compaction_trigger = (1 << 30);
995
1
  options.max_background_flushes = 0;
996
1
  options.table_properties_collector_factories.resize(1);
997
1
  std::shared_ptr<CountingUserTblPropCollectorFactory> collector_factory =
998
1
      std::make_shared<CountingUserTblPropCollectorFactory>(0);
999
1
  options.table_properties_collector_factories[0] = collector_factory;
1000
1
  Reopen(options);
1001
  // Create 4 tables
1002
5
  for (int table = 0; table < 4; ++table) {
1003
50
    for (int i = 0; i < 10 + table; ++i) {
1004
46
      ASSERT_OK(db_->Put(WriteOptions(), ToString(table * 100 + i), "val"));
1005
46
    }
1006
4
    ASSERT_OK(db_->Flush(FlushOptions()));
1007
4
  }
1008
1009
1
  TablePropertiesCollection props;
1010
1
  ASSERT_OK(db_->GetPropertiesOfAllTables(&props));
1011
1
  ASSERT_EQ(4U, props.size());
1012
1
  uint32_t sum = 0;
1013
4
  for (const auto& item : props) {
1014
4
    auto& user_collected = item.second->user_collected_properties;
1015
4
    ASSERT_TRUE(user_collected.find("CountingUserTblPropCollector") !=
1016
4
                user_collected.end());
1017
4
    ASSERT_EQ(user_collected.at("CountingUserTblPropCollector"), "Rocksdb");
1018
4
    ASSERT_TRUE(user_collected.find("Count") != user_collected.end());
1019
4
    Slice key(user_collected.at("Count"));
1020
4
    uint32_t count;
1021
4
    ASSERT_TRUE(GetVarint32(&key, &count));
1022
4
    sum += count;
1023
4
  }
1024
1
  ASSERT_EQ(10u + 11u + 12u + 13u, sum);
1025
1026
1
  ASSERT_GT(collector_factory->num_created_, 0U);
1027
1
  collector_factory->num_created_ = 0;
1028
1
  ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr));
1029
1
  ASSERT_GT(collector_factory->num_created_, 0U);
1030
1
}
1031
#endif  // ROCKSDB_LITE
1032
1033
1
TEST_F(DBPropertiesTest, UserDefinedTablePropertiesContext) {
1034
1
  Options options = CurrentOptions();
1035
1
  options.level0_file_num_compaction_trigger = 3;
1036
1
  options.max_background_flushes = 0;
1037
1
  options.table_properties_collector_factories.resize(1);
1038
1
  std::shared_ptr<CountingUserTblPropCollectorFactory> collector_factory =
1039
1
      std::make_shared<CountingUserTblPropCollectorFactory>(1);
1040
1
  options.table_properties_collector_factories[0] = collector_factory,
1041
1
  CreateAndReopenWithCF({"pikachu"}, options);
1042
  // Create 2 files
1043
3
  for (int table = 0; table < 2; ++table) {
1044
23
    for (int i = 0; i < 10 + table; ++i) {
1045
21
      ASSERT_OK(Put(1, ToString(table * 100 + i), "val"));
1046
21
    }
1047
2
    ASSERT_OK(Flush(1));
1048
2
  }
1049
1
  ASSERT_GT(collector_factory->num_created_, 0U);
1050
1051
1
  collector_factory->num_created_ = 0;
1052
  // Trigger automatic compactions.
1053
4
  for (int table = 0; table < 3; ++table) {
1054
36
    for (int i = 0; i < 10 + table; ++i) {
1055
33
      ASSERT_OK(Put(1, ToString(table * 100 + i), "val"));
1056
33
    }
1057
3
    ASSERT_OK(Flush(1));
1058
3
    ASSERT_OK(dbfull()->TEST_WaitForCompact());
1059
3
  }
1060
1
  ASSERT_GT(collector_factory->num_created_, 0U);
1061
1062
1
  collector_factory->num_created_ = 0;
1063
1
  ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1]));
1064
1
  ASSERT_GT(collector_factory->num_created_, 0U);
1065
1066
  // Come back to write to default column family
1067
1
  collector_factory->num_created_ = 0;
1068
1
  collector_factory->set_expected_column_family_id(0);  // default CF
1069
  // Create 4 tables in default column family
1070
3
  for (int table = 0; table < 2; ++table) {
1071
23
    for (int i = 0; i < 10 + table; ++i) {
1072
21
      ASSERT_OK(Put(ToString(table * 100 + i), "val"));
1073
21
    }
1074
2
    ASSERT_OK(Flush());;
1075
2
  }
1076
1
  ASSERT_GT(collector_factory->num_created_, 0U);
1077
1078
1
  collector_factory->num_created_ = 0;
1079
  // Trigger automatic compactions.
1080
4
  for (int table = 0; table < 3; ++table) {
1081
36
    for (int i = 0; i < 10 + table; ++i) {
1082
33
      ASSERT_OK(Put(ToString(table * 100 + i), "val"));
1083
33
    }
1084
3
    ASSERT_OK(Flush());;
1085
3
    ASSERT_OK(dbfull()->TEST_WaitForCompact());
1086
3
  }
1087
1
  ASSERT_GT(collector_factory->num_created_, 0U);
1088
1089
1
  collector_factory->num_created_ = 0;
1090
1
  ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr));
1091
1
  ASSERT_GT(collector_factory->num_created_, 0U);
1092
1
}
1093
1094
#ifndef ROCKSDB_LITE
1095
1
TEST_F(DBPropertiesTest, TablePropertiesNeedCompactTest) {
1096
1
  Random rnd(301);
1097
1098
1
  Options options;
1099
1
  options.create_if_missing = true;
1100
1
  options.write_buffer_size = 4096;
1101
1
  options.max_write_buffer_number = 8;
1102
1
  options.level0_file_num_compaction_trigger = 2;
1103
1
  options.level0_slowdown_writes_trigger = 2;
1104
1
  options.level0_stop_writes_trigger = 4;
1105
1
  options.target_file_size_base = 2048;
1106
1
  options.max_bytes_for_level_base = 10240;
1107
1
  options.max_bytes_for_level_multiplier = 4;
1108
1
  options.soft_pending_compaction_bytes_limit = 1024 * 1024;
1109
1
  options.num_levels = 8;
1110
1111
1
  std::shared_ptr<TablePropertiesCollectorFactory> collector_factory =
1112
1
      std::make_shared<CountingDeleteTabPropCollectorFactory>();
1113
1
  options.table_properties_collector_factories.resize(1);
1114
1
  options.table_properties_collector_factories[0] = collector_factory;
1115
1116
1
  DestroyAndReopen(options);
1117
1118
1
  const int kMaxKey = 1000;
1119
1.00k
  for (int i = 0; i < kMaxKey; i++) {
1120
1.00k
    ASSERT_OK(Put(Key(i), RandomString(&rnd, 102)));
1121
1.00k
    ASSERT_OK(Put(Key(kMaxKey + i), RandomString(&rnd, 102)));
1122
1.00k
  }
1123
1
  ASSERT_OK(Flush());;
1124
1
  ASSERT_OK(dbfull()->TEST_WaitForCompact());
1125
1
  if (NumTableFilesAtLevel(0) == 1) {
1126
    // Clear Level 0 so that when later flush a file with deletions,
1127
    // we don't trigger an organic compaction.
1128
1
    ASSERT_OK(Put(Key(0), ""));
1129
1
    ASSERT_OK(Put(Key(kMaxKey * 2), ""));
1130
1
    ASSERT_OK(Flush());;
1131
1
    ASSERT_OK(dbfull()->TEST_WaitForCompact());
1132
1
  }
1133
1
  ASSERT_EQ(NumTableFilesAtLevel(0), 0);
1134
1135
1
  {
1136
1
    int c = 0;
1137
1
    std::unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions()));
1138
1
    iter->Seek(Key(kMaxKey - 100));
1139
201
    while (iter->Valid() && iter->key().compare(Key(kMaxKey + 100)) < 0) {
1140
200
      iter->Next();
1141
200
      ++c;
1142
200
    }
1143
1
    ASSERT_EQ(c, 200);
1144
1
  }
1145
1146
1
  ASSERT_OK(Delete(Key(0)));
1147
201
  for (int i = kMaxKey - 100; i < kMaxKey + 100; i++) {
1148
200
    ASSERT_OK(Delete(Key(i)));
1149
200
  }
1150
1
  ASSERT_OK(Delete(Key(kMaxKey * 2)));
1151
1152
1
  ASSERT_OK(Flush());;
1153
1
  ASSERT_OK(dbfull()->TEST_WaitForCompact());
1154
1155
1
  {
1156
1
    SetPerfLevel(PerfLevel::kEnableCount);
1157
1
    perf_context.Reset();
1158
1
    int c = 0;
1159
1
    std::unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions()));
1160
1
    iter->Seek(Key(kMaxKey - 100));
1161
1
    while (iter->Valid() && iter->key().compare(Key(kMaxKey + 100)) < 0) {
1162
0
      iter->Next();
1163
0
    }
1164
1
    ASSERT_EQ(c, 0);
1165
1
    ASSERT_LT(perf_context.internal_delete_skipped_count, 30u);
1166
1
    ASSERT_LT(perf_context.internal_key_skipped_count, 30u);
1167
1
    SetPerfLevel(PerfLevel::kDisable);
1168
1
  }
1169
1
}
1170
1171
1
TEST_F(DBPropertiesTest, NeedCompactHintPersistentTest) {
1172
1
  Random rnd(301);
1173
1174
1
  Options options;
1175
1
  options.create_if_missing = true;
1176
1
  options.max_write_buffer_number = 8;
1177
1
  options.level0_file_num_compaction_trigger = 10;
1178
1
  options.level0_slowdown_writes_trigger = 10;
1179
1
  options.level0_stop_writes_trigger = 10;
1180
1
  options.disable_auto_compactions = true;
1181
1182
1
  std::shared_ptr<TablePropertiesCollectorFactory> collector_factory =
1183
1
      std::make_shared<CountingDeleteTabPropCollectorFactory>();
1184
1
  options.table_properties_collector_factories.resize(1);
1185
1
  options.table_properties_collector_factories[0] = collector_factory;
1186
1187
1
  DestroyAndReopen(options);
1188
1189
1
  const int kMaxKey = 100;
1190
101
  for (int i = 0; i < kMaxKey; i++) {
1191
100
    ASSERT_OK(Put(Key(i), ""));
1192
100
  }
1193
1
  ASSERT_OK(Flush());;
1194
1
  ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
1195
1196
99
  for (int i = 1; i < kMaxKey - 1; i++) {
1197
98
    ASSERT_OK(Delete(Key(i)));
1198
98
  }
1199
1
  ASSERT_OK(Flush());;
1200
1
  ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
1201
1
  ASSERT_EQ(NumTableFilesAtLevel(0), 2);
1202
1203
  // Restart the DB. Although number of files didn't reach
1204
  // options.level0_file_num_compaction_trigger, compaction should
1205
  // still be triggered because of the need-compaction hint.
1206
1
  options.disable_auto_compactions = false;
1207
1
  Reopen(options);
1208
1
  ASSERT_OK(dbfull()->TEST_WaitForCompact());
1209
1
  ASSERT_EQ(NumTableFilesAtLevel(0), 0);
1210
1
  {
1211
1
    SetPerfLevel(PerfLevel::kEnableCount);
1212
1
    perf_context.Reset();
1213
1
    int c = 0;
1214
1
    std::unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions()));
1215
3
    for (iter->Seek(Key(0)); iter->Valid(); iter->Next()) {
1216
2
      c++;
1217
2
    }
1218
1
    ASSERT_EQ(c, 2);
1219
1
    ASSERT_EQ(perf_context.internal_delete_skipped_count, 0);
1220
    // We iterate every key twice. Is it a bug?
1221
1
    ASSERT_LE(perf_context.internal_key_skipped_count, 2);
1222
1
    SetPerfLevel(PerfLevel::kDisable);
1223
1
  }
1224
1
}
1225
#endif  // ROCKSDB_LITE
1226
}  // namespace rocksdb
1227
1228
13.2k
int main(int argc, char** argv) {
1229
13.2k
  rocksdb::port::InstallStackTraceHandler();
1230
13.2k
  ::testing::InitGoogleTest(&argc, argv);
1231
13.2k
  return RUN_ALL_TESTS();
1232
13.2k
}