index_builder_test.cc 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. // Copyright (c) Meta Platforms, Inc. and affiliates.
  2. // This source code is licensed under both the GPLv2 (found in the
  3. // COPYING file in the root directory) and Apache 2.0 License
  4. // (found in the LICENSE.Apache file in the root directory).
  5. #include "table/block_based/index_builder.h"
  6. #include <gtest/gtest.h>
  7. #include <memory>
  8. #include <string>
  9. #include "db/dbformat.h"
  10. #include "rocksdb/comparator.h"
  11. #include "table/format.h"
  12. #include "test_util/testharness.h"
  13. namespace ROCKSDB_NAMESPACE {
  14. class IndexBuilderTest
  15. : public testing::Test,
  16. public testing::WithParamInterface<BlockBasedTableOptions::IndexType> {
  17. public:
  18. IndexBuilderTest() : icomp_(BytewiseComparator()) {}
  19. std::unique_ptr<IndexBuilder> CreateIndexBuilder() {
  20. BlockBasedTableOptions table_options;
  21. BlockBasedTableOptions::IndexType index_type = GetParam();
  22. return std::unique_ptr<IndexBuilder>(IndexBuilder::CreateIndexBuilder(
  23. index_type, &icomp_, nullptr, false /* use_value_delta_encoding */,
  24. table_options, 0 /* ts_sz */,
  25. true /* persist_user_defined_timestamps */));
  26. }
  27. std::string MakeKey(int i) {
  28. return InternalKey(std::string("key") + std::to_string(i), 100 - i,
  29. kTypeValue)
  30. .Encode()
  31. .ToString();
  32. }
  33. BlockHandle MakeBlockHandle(uint64_t offset, uint64_t size) {
  34. BlockHandle handle;
  35. handle.set_offset(offset);
  36. handle.set_size(size);
  37. return handle;
  38. }
  39. void AddEntriesToBuilder(IndexBuilder* builder, int num_entries,
  40. std::vector<uint64_t>* estimates = nullptr) {
  41. for (int i = 1; i <= num_entries; ++i) {
  42. std::string key_current = MakeKey(i);
  43. BlockHandle handle = MakeBlockHandle(i * kBlockOffset, kBlockSize);
  44. std::string separator_scratch;
  45. if (i == num_entries) {
  46. // Last entry - no next key
  47. builder->AddIndexEntry(key_current, nullptr, handle, &separator_scratch,
  48. false);
  49. } else {
  50. std::string key_next = MakeKey(i + 1);
  51. Slice key_next_slice(key_next);
  52. builder->AddIndexEntry(key_current, &key_next_slice, handle,
  53. &separator_scratch, false);
  54. }
  55. if (estimates) {
  56. uint64_t current_estimate = builder->EstimateCurrentIndexSize();
  57. estimates->push_back(current_estimate);
  58. }
  59. }
  60. }
  61. protected:
  62. InternalKeyComparator icomp_;
  63. static const uint64_t kBlockOffset = 1000;
  64. static const uint64_t kBlockSize = 4096;
  65. // BlockBuilder initial overhead
  66. // See BlockBuilder constructor and Reset()
  67. static const uint64_t kBlockBuilderInitialOverhead = 2 * sizeof(uint32_t);
  68. };
  69. const uint64_t IndexBuilderTest::kBlockOffset;
  70. const uint64_t IndexBuilderTest::kBlockSize;
  71. const uint64_t IndexBuilderTest::kBlockBuilderInitialOverhead;
  72. TEST_P(IndexBuilderTest, EstimateCurrentIndexSize) {
  73. auto builder = CreateIndexBuilder();
  74. BlockBasedTableOptions::IndexType index_type = GetParam();
  75. // Empty builder
  76. uint64_t empty_size = builder->EstimateCurrentIndexSize();
  77. if (index_type == BlockBasedTableOptions::kBinarySearch) {
  78. EXPECT_EQ(empty_size, kBlockBuilderInitialOverhead)
  79. << "Empty ShortenedIndexBuilder should return BlockBuilder initial "
  80. "overhead ("
  81. << kBlockBuilderInitialOverhead;
  82. } else {
  83. EXPECT_EQ(empty_size, 0) << "Other builders should return 0 when empty";
  84. }
  85. // Add one entry
  86. AddEntriesToBuilder(builder.get(), 1);
  87. uint64_t size_after_one = builder->EstimateCurrentIndexSize();
  88. if (index_type == BlockBasedTableOptions::kBinarySearch) {
  89. EXPECT_GT(size_after_one, kBlockBuilderInitialOverhead)
  90. << "Estimate should be greater than initial overhead";
  91. } else {
  92. // Other builders currently return 0 (which is expected)
  93. EXPECT_EQ(size_after_one, 0) << "Other index builders currently return 0";
  94. }
  95. // Add multiple entries and capture all estimates
  96. std::vector<uint64_t> estimates;
  97. auto new_builder = CreateIndexBuilder();
  98. AddEntriesToBuilder(new_builder.get(), 5, &estimates);
  99. // Validate reported estimates
  100. for (size_t i = 0; i < estimates.size(); ++i) {
  101. uint64_t estimate = estimates[i];
  102. if (index_type == BlockBasedTableOptions::kBinarySearch) {
  103. EXPECT_GT(estimate, 0)
  104. << "Estimate should be positive for " << i << " entry";
  105. if (i > 0) {
  106. EXPECT_GT(estimate, estimates[i - 1])
  107. << "Estimate should not decrease with more entries (entry " << i - 1
  108. << ": " << estimates[i - 1] << ", entry " << i << ": " << estimate
  109. << ")";
  110. }
  111. } else {
  112. EXPECT_EQ(estimate, 0) << "Other index builders currently return 0";
  113. }
  114. }
  115. // Multiple calls should return the same value if the builder state is not
  116. // modified
  117. uint64_t estimate1 = builder->EstimateCurrentIndexSize();
  118. uint64_t estimate2 = builder->EstimateCurrentIndexSize();
  119. uint64_t estimate3 = builder->EstimateCurrentIndexSize();
  120. EXPECT_EQ(estimate1, estimate2);
  121. EXPECT_EQ(estimate2, estimate3);
  122. // Test behavior after Finish() - only for builders that can be finished
  123. // successfully
  124. if (index_type == BlockBasedTableOptions::kBinarySearch) {
  125. uint64_t estimate_before_finish = builder->EstimateCurrentIndexSize();
  126. IndexBuilder::IndexBlocks index_blocks;
  127. Status s = builder->Finish(&index_blocks);
  128. EXPECT_TRUE(s.ok()) << "ShortenedIndexBuilder should finish successfully: "
  129. << s.ToString();
  130. uint64_t estimate_after_finish = builder->EstimateCurrentIndexSize();
  131. EXPECT_GT(estimate_after_finish, 0);
  132. EXPECT_LE(estimate_before_finish, estimate_after_finish)
  133. << "Estimate should not decrease after finish";
  134. // Ensure that the actual index size is not greater than the estimated size
  135. // after finish is called to prevent underestimation.
  136. uint64_t actual_index_size = builder->IndexSize();
  137. EXPECT_LE(actual_index_size, estimate_after_finish)
  138. << "Actual index size should not be greater than estimated size: "
  139. "actual size: "
  140. << actual_index_size << ", estimated size: " << estimate_after_finish;
  141. }
  142. }
  143. INSTANTIATE_TEST_CASE_P(
  144. IndexBuilderTypes, IndexBuilderTest,
  145. ::testing::Values(BlockBasedTableOptions::kBinarySearch,
  146. BlockBasedTableOptions::kHashSearch,
  147. BlockBasedTableOptions::kTwoLevelIndexSearch));
  148. } // namespace ROCKSDB_NAMESPACE
  149. int main(int argc, char** argv) {
  150. ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
  151. ::testing::InitGoogleTest(&argc, argv);
  152. return RUN_ALL_TESTS();
  153. }