clipping_iterator_test.cc 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
  2. // This source code is licensed under both the GPLv2 (found in the
  3. // COPYING file in the root directory) and Apache 2.0 License
  4. // (found in the LICENSE.Apache file in the root directory).
  5. #include "db/compaction/clipping_iterator.h"
  6. #include <algorithm>
  7. #include <memory>
  8. #include <string>
  9. #include <vector>
  10. #include "db/dbformat.h"
  11. #include "rocksdb/comparator.h"
  12. #include "test_util/testharness.h"
  13. #include "test_util/testutil.h"
  14. #include "util/vector_iterator.h"
  15. namespace ROCKSDB_NAMESPACE {
  16. // A vector iterator which does its own bounds checking. This is for testing the
  17. // optimizations in the clipping iterator where we bypass the bounds checking if
  18. // the input iterator has already performed it.
  19. class BoundsCheckingVectorIterator : public VectorIterator {
  20. public:
  21. BoundsCheckingVectorIterator(const std::vector<std::string>& keys,
  22. const std::vector<std::string>& values,
  23. const Slice* start, const Slice* end,
  24. const Comparator* cmp)
  25. : VectorIterator(keys, values, cmp), start_(start), end_(end), cmp_(cmp) {
  26. assert(cmp_);
  27. }
  28. bool NextAndGetResult(IterateResult* result) override {
  29. assert(Valid());
  30. assert(result);
  31. Next();
  32. if (!Valid()) {
  33. return false;
  34. }
  35. result->key = key();
  36. result->bound_check_result = UpperBoundCheckResult();
  37. result->value_prepared = true;
  38. return true;
  39. }
  40. bool MayBeOutOfLowerBound() override {
  41. assert(Valid());
  42. if (!start_) {
  43. return false;
  44. }
  45. return cmp_->Compare(key(), *start_) < 0;
  46. }
  47. IterBoundCheck UpperBoundCheckResult() override {
  48. assert(Valid());
  49. if (!end_) {
  50. return IterBoundCheck::kInbound;
  51. }
  52. return cmp_->Compare(key(), *end_) >= 0 ? IterBoundCheck::kOutOfBound
  53. : IterBoundCheck::kInbound;
  54. }
  55. private:
  56. const Slice* start_;
  57. const Slice* end_;
  58. const Comparator* cmp_;
  59. };
  60. class ClippingIteratorTest
  61. : public ::testing::Test,
  62. public ::testing::WithParamInterface<std::tuple<bool, size_t, size_t>> {};
  63. TEST_P(ClippingIteratorTest, Clip) {
  64. const std::vector<std::string> keys{"key0", "key1", "key2", "key3", "key4",
  65. "key5", "key6", "key7", "key8", "key9"};
  66. const std::vector<std::string> values{
  67. "unused0", "value1", "value2", "value3", "unused4",
  68. "unused5", "unused6", "unused7", "unused8", "unused9"};
  69. assert(keys.size() == values.size());
  70. // Note: the input always contains key1, key2, and key3; however, the clipping
  71. // window is based on the test parameters: its left edge is a value in the
  72. // range [0, 4], and its size is a value in the range [0, 5]
  73. const std::vector<std::string> input_keys{keys[1], keys[2], keys[3]};
  74. const std::vector<std::string> input_values{values[1], values[2], values[3]};
  75. const bool use_bounds_checking_vec_it = std::get<0>(GetParam());
  76. const size_t clip_start_idx = std::get<1>(GetParam());
  77. const size_t clip_window_size = std::get<2>(GetParam());
  78. const size_t clip_end_idx = clip_start_idx + clip_window_size;
  79. const Slice start(keys[clip_start_idx]);
  80. const Slice end(keys[clip_end_idx]);
  81. std::unique_ptr<InternalIterator> input(
  82. use_bounds_checking_vec_it
  83. ? new BoundsCheckingVectorIterator(input_keys, input_values, &start,
  84. &end, BytewiseComparator())
  85. : new VectorIterator(input_keys, input_values, BytewiseComparator()));
  86. ClippingIterator clip(input.get(), &start, &end, BytewiseComparator());
  87. // The range the clipping iterator should return values from. This is
  88. // essentially the intersection of the input range [1, 4) and the clipping
  89. // window [clip_start_idx, clip_end_idx)
  90. const size_t data_start_idx =
  91. std::max(clip_start_idx, static_cast<size_t>(1));
  92. const size_t data_end_idx = std::min(clip_end_idx, static_cast<size_t>(4));
  93. // Range is empty; all Seeks should fail
  94. if (data_start_idx >= data_end_idx) {
  95. clip.SeekToFirst();
  96. ASSERT_FALSE(clip.Valid());
  97. clip.SeekToLast();
  98. ASSERT_FALSE(clip.Valid());
  99. for (size_t i = 0; i < keys.size(); ++i) {
  100. clip.Seek(keys[i]);
  101. ASSERT_FALSE(clip.Valid());
  102. clip.SeekForPrev(keys[i]);
  103. ASSERT_FALSE(clip.Valid());
  104. }
  105. return;
  106. }
  107. // Range is non-empty; call SeekToFirst and iterate forward
  108. clip.SeekToFirst();
  109. ASSERT_TRUE(clip.Valid());
  110. ASSERT_EQ(clip.key(), keys[data_start_idx]);
  111. ASSERT_EQ(clip.value(), values[data_start_idx]);
  112. ASSERT_FALSE(clip.MayBeOutOfLowerBound());
  113. ASSERT_EQ(clip.UpperBoundCheckResult(), IterBoundCheck::kInbound);
  114. for (size_t i = data_start_idx + 1; i < data_end_idx; ++i) {
  115. clip.Next();
  116. ASSERT_TRUE(clip.Valid());
  117. ASSERT_EQ(clip.key(), keys[i]);
  118. ASSERT_EQ(clip.value(), values[i]);
  119. ASSERT_FALSE(clip.MayBeOutOfLowerBound());
  120. ASSERT_EQ(clip.UpperBoundCheckResult(), IterBoundCheck::kInbound);
  121. }
  122. clip.Next();
  123. ASSERT_FALSE(clip.Valid());
  124. // Do it again using NextAndGetResult
  125. clip.SeekToFirst();
  126. ASSERT_TRUE(clip.Valid());
  127. ASSERT_EQ(clip.key(), keys[data_start_idx]);
  128. ASSERT_EQ(clip.value(), values[data_start_idx]);
  129. ASSERT_FALSE(clip.MayBeOutOfLowerBound());
  130. ASSERT_EQ(clip.UpperBoundCheckResult(), IterBoundCheck::kInbound);
  131. for (size_t i = data_start_idx + 1; i < data_end_idx; ++i) {
  132. IterateResult result;
  133. ASSERT_TRUE(clip.NextAndGetResult(&result));
  134. ASSERT_EQ(result.key, keys[i]);
  135. ASSERT_EQ(result.bound_check_result, IterBoundCheck::kInbound);
  136. ASSERT_TRUE(clip.Valid());
  137. ASSERT_EQ(clip.key(), keys[i]);
  138. ASSERT_EQ(clip.value(), values[i]);
  139. ASSERT_FALSE(clip.MayBeOutOfLowerBound());
  140. ASSERT_EQ(clip.UpperBoundCheckResult(), IterBoundCheck::kInbound);
  141. }
  142. IterateResult result;
  143. ASSERT_FALSE(clip.NextAndGetResult(&result));
  144. ASSERT_FALSE(clip.Valid());
  145. // Call SeekToLast and iterate backward
  146. clip.SeekToLast();
  147. ASSERT_TRUE(clip.Valid());
  148. ASSERT_EQ(clip.key(), keys[data_end_idx - 1]);
  149. ASSERT_EQ(clip.value(), values[data_end_idx - 1]);
  150. ASSERT_FALSE(clip.MayBeOutOfLowerBound());
  151. ASSERT_EQ(clip.UpperBoundCheckResult(), IterBoundCheck::kInbound);
  152. for (size_t i = data_end_idx - 2; i >= data_start_idx; --i) {
  153. clip.Prev();
  154. ASSERT_TRUE(clip.Valid());
  155. ASSERT_EQ(clip.key(), keys[i]);
  156. ASSERT_EQ(clip.value(), values[i]);
  157. ASSERT_FALSE(clip.MayBeOutOfLowerBound());
  158. ASSERT_EQ(clip.UpperBoundCheckResult(), IterBoundCheck::kInbound);
  159. }
  160. clip.Prev();
  161. ASSERT_FALSE(clip.Valid());
  162. // Call Seek/SeekForPrev for all keys; Seek should return the smallest key
  163. // which is >= the target; SeekForPrev should return the largest key which is
  164. // <= the target
  165. for (size_t i = 0; i < keys.size(); ++i) {
  166. clip.Seek(keys[i]);
  167. if (i < data_start_idx) {
  168. ASSERT_TRUE(clip.Valid());
  169. ASSERT_EQ(clip.key(), keys[data_start_idx]);
  170. ASSERT_EQ(clip.value(), values[data_start_idx]);
  171. ASSERT_FALSE(clip.MayBeOutOfLowerBound());
  172. ASSERT_EQ(clip.UpperBoundCheckResult(), IterBoundCheck::kInbound);
  173. } else if (i < data_end_idx) {
  174. ASSERT_TRUE(clip.Valid());
  175. ASSERT_EQ(clip.key(), keys[i]);
  176. ASSERT_EQ(clip.value(), values[i]);
  177. ASSERT_FALSE(clip.MayBeOutOfLowerBound());
  178. ASSERT_EQ(clip.UpperBoundCheckResult(), IterBoundCheck::kInbound);
  179. } else {
  180. ASSERT_FALSE(clip.Valid());
  181. }
  182. clip.SeekForPrev(keys[i]);
  183. if (i < data_start_idx) {
  184. ASSERT_FALSE(clip.Valid());
  185. } else if (i < data_end_idx) {
  186. ASSERT_TRUE(clip.Valid());
  187. ASSERT_EQ(clip.key(), keys[i]);
  188. ASSERT_EQ(clip.value(), values[i]);
  189. ASSERT_FALSE(clip.MayBeOutOfLowerBound());
  190. ASSERT_EQ(clip.UpperBoundCheckResult(), IterBoundCheck::kInbound);
  191. } else {
  192. ASSERT_TRUE(clip.Valid());
  193. ASSERT_EQ(clip.key(), keys[data_end_idx - 1]);
  194. ASSERT_EQ(clip.value(), values[data_end_idx - 1]);
  195. ASSERT_FALSE(clip.MayBeOutOfLowerBound());
  196. ASSERT_EQ(clip.UpperBoundCheckResult(), IterBoundCheck::kInbound);
  197. }
  198. }
  199. }
  200. INSTANTIATE_TEST_CASE_P(
  201. ClippingIteratorTest, ClippingIteratorTest,
  202. ::testing::Combine(
  203. ::testing::Bool(),
  204. ::testing::Range(static_cast<size_t>(0), static_cast<size_t>(5)),
  205. ::testing::Range(static_cast<size_t>(0), static_cast<size_t>(6))));
  206. } // namespace ROCKSDB_NAMESPACE
  207. int main(int argc, char** argv) {
  208. ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
  209. ::testing::InitGoogleTest(&argc, argv);
  210. return RUN_ALL_TESTS();
  211. }