/Users/deen/code/yugabyte-db/src/yb/rocksdb/utilities/table_properties_collectors/compact_on_deletion_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 | | // 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 | | |
25 | | #ifndef ROCKSDB_LITE |
26 | | |
27 | | #include <algorithm> |
28 | | #include <vector> |
29 | | |
30 | | #include "yb/rocksdb/table_properties.h" |
31 | | #include "yb/rocksdb/util/random.h" |
32 | | #include "yb/rocksdb/utilities/table_properties_collectors.h" |
33 | | |
34 | | #include "yb/util/stopwatch.h" |
35 | | #include "yb/util/tsan_util.h" |
36 | | |
37 | 1 | int main(int argc, char** argv) { |
38 | 1 | const int kWindowSizes[] = |
39 | 1 | {1000, 10000, 10000, 127, 128, 129, 255, 256, 257, 2, 10000}; |
40 | 1 | const int kDeletionTriggers[] = |
41 | 1 | {500, 9500, 4323, 47, 61, 128, 250, 250, 250, 2, 2}; |
42 | 1 | rocksdb::TablePropertiesCollectorFactory::Context context; |
43 | 1 | context.column_family_id = |
44 | 1 | rocksdb::TablePropertiesCollectorFactory::Context::kUnknownColumnFamily; |
45 | | |
46 | 1 | std::vector<int> window_sizes; |
47 | 1 | std::vector<int> deletion_triggers; |
48 | | // deterministic tests |
49 | 10 | for (int test = 0; test < 9; ++test) { |
50 | 9 | window_sizes.emplace_back(kWindowSizes[test]); |
51 | 9 | deletion_triggers.emplace_back(kDeletionTriggers[test]); |
52 | 9 | } |
53 | | |
54 | | |
55 | | // Lower number of runs for tsan due to low perf. |
56 | 1 | constexpr int kNumRandomTests = yb::NonTsanVsTsan(100, 20); |
57 | | |
58 | | // randomize tests |
59 | 1 | rocksdb::Random rnd(301); |
60 | 1 | const int kMaxTestSize = 100000l; |
61 | 101 | for (int random_test = 0; random_test < kNumRandomTests; random_test++) { |
62 | 100 | int window_size = rnd.Uniform(kMaxTestSize) + 1; |
63 | 100 | int deletion_trigger = rnd.Uniform(window_size); |
64 | 100 | window_sizes.emplace_back(window_size); |
65 | 100 | deletion_triggers.emplace_back(deletion_trigger); |
66 | 100 | } |
67 | | |
68 | 1 | assert(window_sizes.size() == deletion_triggers.size()); |
69 | | |
70 | 110 | for (size_t test = 0; test < window_sizes.size(); ++test) { |
71 | 109 | const int kBucketSize = 128; |
72 | 109 | const int kWindowSize = window_sizes[test]; |
73 | 109 | const int kPaddedWindowSize = |
74 | 109 | kBucketSize * ((window_sizes[test] + kBucketSize - 1) / kBucketSize); |
75 | 109 | const int kNumDeletionTrigger = deletion_triggers[test]; |
76 | 109 | const int kBias = (kNumDeletionTrigger + kBucketSize - 1) / kBucketSize; |
77 | | // Simple test |
78 | 109 | LOG_TIMING(INFO, "TEST1") { |
79 | 109 | std::unique_ptr<rocksdb::TablePropertiesCollector> collector; |
80 | 109 | auto factory = rocksdb::NewCompactOnDeletionCollectorFactory( |
81 | 109 | kWindowSize, kNumDeletionTrigger); |
82 | 109 | collector.reset(factory->CreateTablePropertiesCollector(context)); |
83 | 109 | const int kSample = 10; |
84 | 1.30k | for (int delete_rate = 0; delete_rate <= kSample; ++delete_rate) { |
85 | 1.19k | int deletions = 0; |
86 | 56.9M | for (int i = 0; i < kPaddedWindowSize; ++i) { |
87 | 56.9M | if (i % kSample < delete_rate) { |
88 | 28.4M | CHECK_OK(collector->AddUserKey( |
89 | 28.4M | "hello", "rocksdb", rocksdb::kEntryDelete, 0, 0)); |
90 | 28.4M | deletions++; |
91 | 28.4M | } else { |
92 | 28.4M | CHECK_OK(collector->AddUserKey( |
93 | 28.4M | "hello", "rocksdb", rocksdb::kEntryPut, 0, 0)); |
94 | 28.4M | } |
95 | 56.9M | } |
96 | 1.19k | if (collector->NeedCompact() != |
97 | 1.19k | (deletions >= kNumDeletionTrigger) && |
98 | 0 | std::abs(deletions - kNumDeletionTrigger) > kBias) { |
99 | 0 | fprintf(stderr, "[Error] collector->NeedCompact() != (%d >= %d)" |
100 | 0 | " with kWindowSize = %d and kNumDeletionTrigger = %d\n", |
101 | 0 | deletions, kNumDeletionTrigger, |
102 | 0 | kWindowSize, kNumDeletionTrigger); |
103 | 0 | assert(false); |
104 | 0 | } |
105 | 1.19k | CHECK_OK(collector->Finish(nullptr)); |
106 | 1.19k | } |
107 | 109 | } |
108 | | |
109 | | // Only one section of a file satisfies the compaction trigger |
110 | 109 | LOG_TIMING(INFO, "TEST2") { |
111 | 109 | std::unique_ptr<rocksdb::TablePropertiesCollector> collector; |
112 | 109 | auto factory = rocksdb::NewCompactOnDeletionCollectorFactory( |
113 | 109 | kWindowSize, kNumDeletionTrigger); |
114 | 109 | collector.reset(factory->CreateTablePropertiesCollector(context)); |
115 | 109 | const int kSample = 10; |
116 | 1.30k | for (int delete_rate = 0; delete_rate <= kSample; ++delete_rate) { |
117 | 1.19k | int deletions = 0; |
118 | 7.19k | for (int section = 0; section < 5; ++section) { |
119 | 5.99k | int initial_entries = rnd.Uniform(kWindowSize) + kWindowSize; |
120 | 427M | for (int i = 0; i < initial_entries; ++i) { |
121 | 427M | CHECK_OK(collector->AddUserKey( |
122 | 427M | "hello", "rocksdb", rocksdb::kEntryPut, 0, 0)); |
123 | 427M | } |
124 | 5.99k | } |
125 | 56.9M | for (int i = 0; i < kPaddedWindowSize; ++i) { |
126 | 56.9M | if (i % kSample < delete_rate) { |
127 | 28.4M | CHECK_OK(collector->AddUserKey( |
128 | 28.4M | "hello", "rocksdb", rocksdb::kEntryDelete, 0, 0)); |
129 | 28.4M | deletions++; |
130 | 28.4M | } else { |
131 | 28.4M | CHECK_OK(collector->AddUserKey( |
132 | 28.4M | "hello", "rocksdb", rocksdb::kEntryPut, 0, 0)); |
133 | 28.4M | } |
134 | 56.9M | } |
135 | 7.19k | for (int section = 0; section < 5; ++section) { |
136 | 5.99k | int ending_entries = rnd.Uniform(kWindowSize) + kWindowSize; |
137 | 426M | for (int i = 0; i < ending_entries; ++i) { |
138 | 426M | CHECK_OK(collector->AddUserKey( |
139 | 426M | "hello", "rocksdb", rocksdb::kEntryPut, 0, 0)); |
140 | 426M | } |
141 | 5.99k | } |
142 | 1.19k | if (collector->NeedCompact() != (deletions >= kNumDeletionTrigger) && |
143 | 1 | std::abs(deletions - kNumDeletionTrigger) > kBias) { |
144 | 0 | fprintf(stderr, "[Error] collector->NeedCompact() %d != (%d >= %d)" |
145 | 0 | " with kWindowSize = %d, kNumDeletionTrigger = %d\n", |
146 | 0 | collector->NeedCompact(), |
147 | 0 | deletions, kNumDeletionTrigger, kWindowSize, |
148 | 0 | kNumDeletionTrigger); |
149 | 0 | assert(false); |
150 | 0 | } |
151 | 1.19k | CHECK_OK(collector->Finish(nullptr)); |
152 | 1.19k | } |
153 | 109 | } |
154 | | |
155 | | // TEST 3: Issues a lots of deletes, but their density is not |
156 | | // high enough to trigger compaction. |
157 | 109 | LOG_TIMING(INFO, "TEST3") { |
158 | 109 | std::unique_ptr<rocksdb::TablePropertiesCollector> collector; |
159 | 109 | auto factory = rocksdb::NewCompactOnDeletionCollectorFactory( |
160 | 109 | kWindowSize, kNumDeletionTrigger); |
161 | 109 | collector.reset(factory->CreateTablePropertiesCollector(context)); |
162 | 109 | assert(collector->NeedCompact() == false); |
163 | | // Insert "kNumDeletionTrigger * 0.95" deletions for every |
164 | | // "kWindowSize" and verify compaction is not needed. |
165 | 109 | const int kDeletionsPerSection = kNumDeletionTrigger * 95 / 100; |
166 | 109 | if (kDeletionsPerSection >= 0) { |
167 | 21.9k | for (int section = 0; section < 200; ++section) { |
168 | 1.03G | for (int i = 0; i < kPaddedWindowSize; ++i) { |
169 | 1.03G | if (i < kDeletionsPerSection) { |
170 | 513M | CHECK_OK(collector->AddUserKey( |
171 | 513M | "hello", "rocksdb", rocksdb::kEntryDelete, 0, 0)); |
172 | 521M | } else { |
173 | 521M | CHECK_OK(collector->AddUserKey( |
174 | 521M | "hello", "rocksdb", rocksdb::kEntryPut, 0, 0)); |
175 | 521M | } |
176 | 1.03G | } |
177 | 21.8k | } |
178 | 109 | if (collector->NeedCompact() && |
179 | 0 | std::abs(kDeletionsPerSection - kNumDeletionTrigger) > kBias) { |
180 | 0 | fprintf(stderr, "[Error] collector->NeedCompact() != false" |
181 | 0 | " with kWindowSize = %d and kNumDeletionTrigger = %d\n", |
182 | 0 | kWindowSize, kNumDeletionTrigger); |
183 | 0 | assert(false); |
184 | 0 | } |
185 | 109 | CHECK_OK(collector->Finish(nullptr)); |
186 | 109 | } |
187 | 109 | } |
188 | 109 | } |
189 | 1 | fprintf(stderr, "PASSED\n"); |
190 | 1 | } |
191 | | #else |
192 | | int main(int argc, char** argv) { |
193 | | fprintf(stderr, "SKIPPED as RocksDBLite does not include utilities.\n"); |
194 | | return 0; |
195 | | } |
196 | | #endif // !ROCKSDB_LITE |