YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/rocksdb/db/table_properties_collector_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
21
#include <map>
22
#include <memory>
23
#include <string>
24
#include <utility>
25
#include <vector>
26
27
#include <gtest/gtest.h>
28
29
#include "yb/rocksdb/db/builder.h"
30
#include "yb/rocksdb/db/db_impl.h"
31
#include "yb/rocksdb/db/dbformat.h"
32
#include "yb/rocksdb/db/table_properties_collector.h"
33
#include "yb/rocksdb/flush_block_policy.h"
34
#include "yb/rocksdb/immutable_options.h"
35
#include "yb/rocksdb/table.h"
36
#include "yb/rocksdb/table/block_based_table_factory.h"
37
#include "yb/rocksdb/table/meta_blocks.h"
38
#include "yb/rocksdb/table/plain_table_factory.h"
39
#include "yb/rocksdb/table/table_builder.h"
40
#include "yb/rocksdb/util/coding.h"
41
#include "yb/rocksdb/util/file_reader_writer.h"
42
#include "yb/rocksdb/util/testutil.h"
43
44
#include "yb/util/test_macros.h"
45
46
namespace rocksdb {
47
48
class TablePropertiesTest : public RocksDBTest,
49
                            public testing::WithParamInterface<bool> {
50
 public:
51
8
  void SetUp() override { backward_mode_ = GetParam(); }
52
53
  bool backward_mode_;
54
};
55
56
// Utilities test functions
57
namespace {
58
static const uint32_t kTestColumnFamilyId = 66;
59
60
void MakeBuilder(const Options& options, const ImmutableCFOptions& ioptions,
61
                 const InternalKeyComparatorPtr& internal_comparator,
62
                 const IntTblPropCollectorFactories& int_tbl_prop_collector_factories,
63
                 std::unique_ptr<WritableFileWriter>* writable,
64
36
                 std::unique_ptr<TableBuilder>* builder) {
65
36
  unique_ptr<WritableFile> wf(new test::StringSink);
66
36
  writable->reset(new WritableFileWriter(std::move(wf), EnvOptions()));
67
68
36
  builder->reset(NewTableBuilder(
69
36
      ioptions, internal_comparator, int_tbl_prop_collector_factories,
70
36
      kTestColumnFamilyId /* column_family_id */, writable->get(),
71
36
      options.compression, options.compression_opts));
72
36
}
73
}  // namespace
74
75
// Collects keys that starts with "A" in a table.
76
class RegularKeysStartWithA: public TablePropertiesCollector {
77
 public:
78
0
  const char* Name() const override { return "RegularKeysStartWithA"; }
79
80
8
  Status Finish(UserCollectedProperties* properties) override {
81
8
     std::string encoded;
82
8
     std::string encoded_num_puts;
83
8
     std::string encoded_num_deletes;
84
8
     std::string encoded_num_single_deletes;
85
8
     std::string encoded_num_size_changes;
86
8
     PutVarint32(&encoded, count_);
87
8
     PutVarint32(&encoded_num_puts, num_puts_);
88
8
     PutVarint32(&encoded_num_deletes, num_deletes_);
89
8
     PutVarint32(&encoded_num_single_deletes, num_single_deletes_);
90
8
     PutVarint32(&encoded_num_size_changes, num_size_changes_);
91
8
     *properties = UserCollectedProperties{
92
8
         {"TablePropertiesTest", message_},
93
8
         {"Count", encoded},
94
8
         {"NumPuts", encoded_num_puts},
95
8
         {"NumDeletes", encoded_num_deletes},
96
8
         {"NumSingleDeletes", encoded_num_single_deletes},
97
8
         {"NumSizeChanges", encoded_num_size_changes},
98
8
     };
99
8
     return Status::OK();
100
8
  }
101
102
  Status AddUserKey(const Slice& user_key, const Slice& value, EntryType type,
103
72
                    SequenceNumber seq, uint64_t file_size) override {
104
    // simply asssume all user keys are not empty.
105
72
    if (user_key.data()[0] == 'A') {
106
16
      ++count_;
107
16
    }
108
72
    if (type == kEntryPut) {
109
40
      num_puts_++;
110
32
    } else if (type == kEntryDelete) {
111
24
      num_deletes_++;
112
8
    } else if (type == kEntrySingleDelete) {
113
8
      num_single_deletes_++;
114
8
    }
115
72
    if (file_size < file_size_) {
116
0
      message_ = "File size should not decrease.";
117
72
    } else if (file_size != file_size_) {
118
36
      num_size_changes_++;
119
36
    }
120
121
72
    return Status::OK();
122
72
  }
123
124
0
  UserCollectedProperties GetReadableProperties() const override {
125
0
    return UserCollectedProperties{};
126
0
  }
127
128
 private:
129
  std::string message_ = "Rocksdb";
130
  uint32_t count_ = 0;
131
  uint32_t num_puts_ = 0;
132
  uint32_t num_deletes_ = 0;
133
  uint32_t num_single_deletes_ = 0;
134
  uint32_t num_size_changes_ = 0;
135
  uint64_t file_size_ = 0;
136
};
137
138
// Collects keys that starts with "A" in a table. Backward compatible mode
139
// It is also used to test internal key table property collector
140
class RegularKeysStartWithABackwardCompatible
141
    : public TablePropertiesCollector {
142
 public:
143
0
  const char* Name() const override { return "RegularKeysStartWithA"; }
144
145
8
  Status Finish(UserCollectedProperties* properties) override {
146
8
    std::string encoded;
147
8
    PutVarint32(&encoded, count_);
148
8
    *properties = UserCollectedProperties{{"TablePropertiesTest", "Rocksdb"},
149
8
                                          {"Count", encoded}};
150
8
    return Status::OK();
151
8
  }
152
153
72
  Status Add(const Slice& user_key, const Slice& value) override {
154
    // simply asssume all user keys are not empty.
155
72
    if (user_key.data()[0] == 'A') {
156
16
      ++count_;
157
16
    }
158
72
    return Status::OK();
159
72
  }
160
161
0
  UserCollectedProperties GetReadableProperties() const override {
162
0
    return UserCollectedProperties{};
163
0
  }
164
165
 private:
166
  uint32_t count_ = 0;
167
};
168
169
class RegularKeysStartWithAInternal : public IntTblPropCollector {
170
 public:
171
0
  const char* Name() const override { return "RegularKeysStartWithA"; }
172
173
8
  Status Finish(UserCollectedProperties* properties) override {
174
8
    std::string encoded;
175
8
    PutVarint32(&encoded, count_);
176
8
    *properties = UserCollectedProperties{{"TablePropertiesTest", "Rocksdb"},
177
8
                                          {"Count", encoded}};
178
8
    return Status::OK();
179
8
  }
180
181
  Status InternalAdd(const Slice& user_key, const Slice& value,
182
80
                     uint64_t file_size) override {
183
    // simply asssume all user keys are not empty.
184
80
    if (user_key.data()[0] == 'A') {
185
24
      ++count_;
186
24
    }
187
80
    return Status::OK();
188
80
  }
189
190
0
  UserCollectedProperties GetReadableProperties() const override {
191
0
    return UserCollectedProperties{};
192
0
  }
193
194
 private:
195
  uint32_t count_ = 0;
196
};
197
198
class RegularKeysStartWithAFactory : public IntTblPropCollectorFactory,
199
                                     public TablePropertiesCollectorFactory {
200
 public:
201
  explicit RegularKeysStartWithAFactory(bool backward_mode)
202
20
      : backward_mode_(backward_mode) {}
203
  virtual TablePropertiesCollector* CreateTablePropertiesCollector(
204
16
      TablePropertiesCollectorFactory::Context context) override {
205
16
    EXPECT_EQ(kTestColumnFamilyId, context.column_family_id);
206
16
    if (!backward_mode_) {
207
8
      return new RegularKeysStartWithA();
208
8
    } else {
209
8
      return new RegularKeysStartWithABackwardCompatible();
210
8
    }
211
16
  }
212
  virtual IntTblPropCollector* CreateIntTblPropCollector(
213
8
      uint32_t column_family_id) override {
214
8
    return new RegularKeysStartWithAInternal();
215
8
  }
216
0
  const char* Name() const override { return "RegularKeysStartWithA"; }
217
218
  bool backward_mode_;
219
};
220
221
class FlushBlockEveryThreePolicy : public FlushBlockPolicy {
222
 public:
223
80
  bool Update(const Slice& key, const Slice& value) override {
224
80
    return (++count_ % 3U == 0);
225
80
  }
226
227
 private:
228
  uint64_t count_ = 0;
229
};
230
231
class FlushBlockEveryThreePolicyFactory : public FlushBlockPolicyFactory {
232
 public:
233
8
  FlushBlockEveryThreePolicyFactory() {}
234
235
0
  const char* Name() const override {
236
0
    return "FlushBlockEveryThreePolicyFactory";
237
0
  }
238
239
  FlushBlockPolicy* NewFlushBlockPolicy(
240
      const BlockBasedTableOptions& table_options,
241
8
      const BlockBuilder& data_block_builder) const override {
242
8
    return new FlushBlockEveryThreePolicy;
243
8
  }
244
};
245
246
extern const uint64_t kBlockBasedTableMagicNumber;
247
extern const uint64_t kPlainTableMagicNumber;
248
namespace {
249
void TestCustomizedTablePropertiesCollector(
250
    bool backward_mode, uint64_t magic_number, bool test_int_tbl_prop_collector,
251
16
    const Options& options, const InternalKeyComparatorPtr& internal_comparator) {
252
  // make sure the entries will be inserted with order.
253
16
  std::map<std::pair<std::string, ValueType>, std::string> kvs = {
254
16
      {{"About   ", kTypeValue}, "val5"},  // starts with 'A'
255
16
      {{"Abstract", kTypeValue}, "val2"},  // starts with 'A'
256
16
      {{"Around  ", kTypeValue}, "val7"},  // starts with 'A'
257
16
      {{"Beyond  ", kTypeValue}, "val3"},
258
16
      {{"Builder ", kTypeValue}, "val1"},
259
16
      {{"Love    ", kTypeDeletion}, ""},
260
16
      {{"Cancel  ", kTypeValue}, "val4"},
261
16
      {{"Find    ", kTypeValue}, "val6"},
262
16
      {{"Rocks   ", kTypeDeletion}, ""},
263
16
      {{"Foo     ", kTypeSingleDeletion}, ""},
264
16
  };
265
266
  // -- Step 1: build table
267
16
  std::unique_ptr<TableBuilder> builder;
268
16
  std::unique_ptr<WritableFileWriter> writer;
269
16
  const ImmutableCFOptions ioptions(options);
270
16
  IntTblPropCollectorFactories int_tbl_prop_collector_factories;
271
16
  if (test_int_tbl_prop_collector) {
272
8
    int_tbl_prop_collector_factories.emplace_back(
273
8
        new RegularKeysStartWithAFactory(backward_mode));
274
8
  } else {
275
8
    GetIntTblPropCollectorFactory(options, &int_tbl_prop_collector_factories);
276
8
  }
277
16
  MakeBuilder(options,
278
16
              ioptions,
279
16
              internal_comparator,
280
16
              int_tbl_prop_collector_factories,
281
16
              &writer,
282
16
              &builder);
283
284
16
  SequenceNumber seqNum = 0U;
285
160
  for (const auto& kv : kvs) {
286
160
    InternalKey ikey(kv.first.first, seqNum++, kv.first.second);
287
160
    builder->Add(ikey.Encode(), kv.second);
288
160
  }
289
16
  ASSERT_OK(builder->Finish());
290
16
  ASSERT_OK(writer->Flush());
291
292
  // -- Step 2: Read properties
293
16
  test::StringSink* fwf =
294
16
      static_cast<test::StringSink*>(writer->writable_file());
295
16
  std::unique_ptr<RandomAccessFileReader> fake_file_reader(
296
16
      test::GetRandomAccessFileReader(
297
16
          new test::StringSource(fwf->contents())));
298
16
  TableProperties* props;
299
16
  Status s = ReadTableProperties(fake_file_reader.get(), fwf->contents().size(),
300
16
                                 magic_number, Env::Default(), nullptr, &props);
301
16
  std::unique_ptr<TableProperties> props_guard(props);
302
16
  ASSERT_OK(s);
303
304
16
  auto user_collected = props->user_collected_properties;
305
306
16
  ASSERT_NE(user_collected.find("TablePropertiesTest"), user_collected.end());
307
16
  ASSERT_EQ("Rocksdb", user_collected.at("TablePropertiesTest"));
308
309
16
  uint32_t starts_with_A = 0;
310
16
  ASSERT_NE(user_collected.find("Count"), user_collected.end());
311
16
  Slice key(user_collected.at("Count"));
312
16
  ASSERT_TRUE(GetVarint32(&key, &starts_with_A));
313
16
  ASSERT_EQ(3u, starts_with_A);
314
315
16
  if (!backward_mode && !test_int_tbl_prop_collector) {
316
4
    uint32_t num_puts;
317
4
    ASSERT_NE(user_collected.find("NumPuts"), user_collected.end());
318
4
    Slice key_puts(user_collected.at("NumPuts"));
319
4
    ASSERT_TRUE(GetVarint32(&key_puts, &num_puts));
320
4
    ASSERT_EQ(7u, num_puts);
321
322
4
    uint32_t num_deletes;
323
4
    ASSERT_NE(user_collected.find("NumDeletes"), user_collected.end());
324
4
    Slice key_deletes(user_collected.at("NumDeletes"));
325
4
    ASSERT_TRUE(GetVarint32(&key_deletes, &num_deletes));
326
4
    ASSERT_EQ(2u, num_deletes);
327
328
4
    uint32_t num_single_deletes;
329
4
    ASSERT_NE(user_collected.find("NumSingleDeletes"), user_collected.end());
330
4
    Slice key_single_deletes(user_collected.at("NumSingleDeletes"));
331
4
    ASSERT_TRUE(GetVarint32(&key_single_deletes, &num_single_deletes));
332
4
    ASSERT_EQ(1u, num_single_deletes);
333
334
4
    uint32_t num_size_changes;
335
4
    ASSERT_NE(user_collected.find("NumSizeChanges"), user_collected.end());
336
4
    Slice key_size_changes(user_collected.at("NumSizeChanges"));
337
4
    ASSERT_TRUE(GetVarint32(&key_size_changes, &num_size_changes));
338
4
    ASSERT_GE(num_size_changes, 2u);
339
4
  }
340
16
}
341
}  // namespace
342
343
4
TEST_P(TablePropertiesTest, CustomizedTablePropertiesCollector) {
344
  // Test properties collectors with internal keys or regular keys
345
  // for block based table
346
8
  for (bool encode_as_internal : { true, false }) {
347
8
    Options options;
348
8
    BlockBasedTableOptions table_options;
349
8
    table_options.flush_block_policy_factory =
350
8
        std::make_shared<FlushBlockEveryThreePolicyFactory>();
351
8
    options.table_factory.reset(NewBlockBasedTableFactory(table_options));
352
353
8
    auto ikc = std::make_shared<test::PlainInternalKeyComparator>(options.comparator);
354
8
    std::shared_ptr<TablePropertiesCollectorFactory> collector_factory(
355
8
        new RegularKeysStartWithAFactory(backward_mode_));
356
8
    options.table_properties_collector_factories.resize(1);
357
8
    options.table_properties_collector_factories[0] = collector_factory;
358
359
8
    TestCustomizedTablePropertiesCollector(backward_mode_,
360
8
                                           kBlockBasedTableMagicNumber,
361
8
                                           encode_as_internal, options, ikc);
362
363
8
#ifndef ROCKSDB_LITE  // PlainTable is not supported in Lite
364
    // test plain table
365
8
    PlainTableOptions plain_table_options;
366
8
    plain_table_options.user_key_len = 8;
367
8
    plain_table_options.bloom_bits_per_key = 8;
368
8
    plain_table_options.hash_table_ratio = 0;
369
370
8
    options.table_factory =
371
8
        std::make_shared<PlainTableFactory>(plain_table_options);
372
8
    TestCustomizedTablePropertiesCollector(backward_mode_,
373
8
                                           kPlainTableMagicNumber,
374
8
                                           encode_as_internal, options, ikc);
375
8
#endif  // !ROCKSDB_LITE
376
8
  }
377
4
}
378
379
namespace {
380
void TestInternalKeyPropertiesCollector(
381
    bool backward_mode, uint64_t magic_number, bool sanitized,
382
10
    std::shared_ptr<TableFactory> table_factory) {
383
10
  InternalKey keys[] = {
384
10
      InternalKey("A       ", 0, ValueType::kTypeValue),
385
10
      InternalKey("B       ", 1, ValueType::kTypeValue),
386
10
      InternalKey("C       ", 2, ValueType::kTypeValue),
387
10
      InternalKey("W       ", 3, ValueType::kTypeDeletion),
388
10
      InternalKey("X       ", 4, ValueType::kTypeDeletion),
389
10
      InternalKey("Y       ", 5, ValueType::kTypeDeletion),
390
10
      InternalKey("Z       ", 6, ValueType::kTypeDeletion),
391
10
      InternalKey("a       ", 7, ValueType::kTypeSingleDeletion),
392
10
  };
393
394
10
  std::unique_ptr<TableBuilder> builder;
395
10
  std::unique_ptr<WritableFileWriter> writable;
396
10
  Options options;
397
10
  auto pikc = std::make_shared<test::PlainInternalKeyComparator>(options.comparator);
398
399
10
  IntTblPropCollectorFactories int_tbl_prop_collector_factories;
400
10
  options.table_factory = table_factory;
401
10
  if (sanitized) {
402
4
    options.table_properties_collector_factories.emplace_back(
403
4
        new RegularKeysStartWithAFactory(backward_mode));
404
    // with sanitization, even regular properties collector will be able to
405
    // handle internal keys.
406
4
    auto comparator = options.comparator;
407
    // HACK: Set options.info_log to avoid writing log in
408
    // SanitizeOptions().
409
4
    options.info_log = std::make_shared<test::NullLogger>();
410
4
    options = SanitizeOptions("db",            // just a place holder
411
4
                              pikc.get(),
412
4
                              options);
413
4
    GetIntTblPropCollectorFactory(options, &int_tbl_prop_collector_factories);
414
4
    options.comparator = comparator;
415
6
  } else {
416
6
    int_tbl_prop_collector_factories.emplace_back(
417
6
        new InternalKeyPropertiesCollectorFactory);
418
6
  }
419
10
  const ImmutableCFOptions ioptions(options);
420
421
30
  for (int iter = 0; iter < 2; ++iter) {
422
20
    MakeBuilder(options,
423
20
                ioptions,
424
20
                pikc,
425
20
                int_tbl_prop_collector_factories,
426
20
                &writable,
427
20
                &builder);
428
160
    for (const auto& k : keys) {
429
160
      builder->Add(k.Encode(), "val");
430
160
    }
431
432
20
    ASSERT_OK(builder->Finish());
433
20
    ASSERT_OK(writable->Flush());
434
435
20
    test::StringSink* fwf =
436
20
        static_cast<test::StringSink*>(writable->writable_file());
437
20
    unique_ptr<RandomAccessFileReader> reader(test::GetRandomAccessFileReader(
438
20
        new test::StringSource(fwf->contents())));
439
20
    TableProperties* props;
440
20
    Status s =
441
20
        ReadTableProperties(reader.get(), fwf->contents().size(), magic_number,
442
20
                            Env::Default(), nullptr, &props);
443
20
    ASSERT_OK(s);
444
445
20
    std::unique_ptr<TableProperties> props_guard(props);
446
20
    auto user_collected = props->user_collected_properties;
447
20
    uint64_t deleted = GetDeletedKeys(user_collected);
448
20
    ASSERT_EQ(5u, deleted);  // deletes + single-deletes
449
450
20
    if (sanitized) {
451
8
      uint32_t starts_with_A = 0;
452
8
      ASSERT_NE(user_collected.find("Count"), user_collected.end());
453
8
      Slice key(user_collected.at("Count"));
454
8
      ASSERT_TRUE(GetVarint32(&key, &starts_with_A));
455
8
      ASSERT_EQ(1u, starts_with_A);
456
457
8
      if (!backward_mode) {
458
4
        uint32_t num_puts;
459
4
        ASSERT_NE(user_collected.find("NumPuts"), user_collected.end());
460
4
        Slice key_puts(user_collected.at("NumPuts"));
461
4
        ASSERT_TRUE(GetVarint32(&key_puts, &num_puts));
462
4
        ASSERT_EQ(3u, num_puts);
463
464
4
        uint32_t num_deletes;
465
4
        ASSERT_NE(user_collected.find("NumDeletes"), user_collected.end());
466
4
        Slice key_deletes(user_collected.at("NumDeletes"));
467
4
        ASSERT_TRUE(GetVarint32(&key_deletes, &num_deletes));
468
4
        ASSERT_EQ(4u, num_deletes);
469
470
4
        uint32_t num_single_deletes;
471
4
        ASSERT_NE(user_collected.find("NumSingleDeletes"),
472
4
                  user_collected.end());
473
4
        Slice key_single_deletes(user_collected.at("NumSingleDeletes"));
474
4
        ASSERT_TRUE(GetVarint32(&key_single_deletes, &num_single_deletes));
475
4
        ASSERT_EQ(1u, num_single_deletes);
476
4
      }
477
8
    }
478
20
  }
479
10
}
480
}  // namespace
481
482
4
TEST_P(TablePropertiesTest, InternalKeyPropertiesCollector) {
483
4
  TestInternalKeyPropertiesCollector(
484
4
      backward_mode_, kBlockBasedTableMagicNumber, true /* sanitize */,
485
4
      std::make_shared<BlockBasedTableFactory>());
486
4
  if (backward_mode_) {
487
2
    TestInternalKeyPropertiesCollector(
488
2
        backward_mode_, kBlockBasedTableMagicNumber, false /* not sanitize */,
489
2
        std::make_shared<BlockBasedTableFactory>());
490
2
  }
491
492
4
#ifndef ROCKSDB_LITE  // PlainTable is not supported in Lite
493
4
  PlainTableOptions plain_table_options;
494
4
  plain_table_options.user_key_len = 8;
495
4
  plain_table_options.bloom_bits_per_key = 8;
496
4
  plain_table_options.hash_table_ratio = 0;
497
498
4
  TestInternalKeyPropertiesCollector(
499
4
      backward_mode_, kPlainTableMagicNumber, false /* not sanitize */,
500
4
      std::make_shared<PlainTableFactory>(plain_table_options));
501
4
#endif  // !ROCKSDB_LITE
502
4
}
503
504
INSTANTIATE_TEST_CASE_P(InternalKeyPropertiesCollector, TablePropertiesTest,
505
                        ::testing::Bool());
506
507
INSTANTIATE_TEST_CASE_P(CustomizedTablePropertiesCollector, TablePropertiesTest,
508
                        ::testing::Bool());
509
510
}  // namespace rocksdb
511
512
13.2k
int main(int argc, char** argv) {
513
13.2k
  ::testing::InitGoogleTest(&argc, argv);
514
13.2k
  return RUN_ALL_TESTS();
515
13.2k
}