/Users/deen/code/yugabyte-db/src/yb/rocksdb/db/manual_compaction_test.cc
Line | Count | Source |
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 | | // Test for issue 178: a manual compaction causes deleted data to reappear. |
21 | | |
22 | | #include <sstream> |
23 | | |
24 | | #include <gtest/gtest.h> |
25 | | |
26 | | #include "yb/rocksdb/compaction_filter.h" |
27 | | #include "yb/rocksdb/db.h" |
28 | | #include "yb/rocksdb/util/testharness.h" |
29 | | |
30 | | #include "yb/util/test_macros.h" |
31 | | #include "yb/rocksdb/util/testutil.h" |
32 | | #include "yb/util/tsan_util.h" |
33 | | |
34 | | using namespace rocksdb; // NOLINT |
35 | | |
36 | | namespace { |
37 | | |
38 | | const int kNumKeys = yb::NonTsanVsTsan(1100000, 100000); |
39 | | |
40 | 3.30M | std::string Key1(int i) { |
41 | 3.30M | char buf[100]; |
42 | 3.30M | snprintf(buf, sizeof(buf), "my_key_%d", i); |
43 | 3.30M | return buf; |
44 | 3.30M | } |
45 | | |
46 | 2.20M | std::string Key2(int i) { |
47 | 2.20M | return Key1(i) + "_xxx"; |
48 | 2.20M | } |
49 | | |
50 | | class ManualCompactionTest : public RocksDBTest { |
51 | | public: |
52 | 2 | ManualCompactionTest() { |
53 | | // Get rid of any state from an old run. |
54 | 2 | dbname_ = rocksdb::test::TmpDir() + "/rocksdb_cbug_test"; |
55 | 2 | CHECK_OK(DestroyDB(dbname_, rocksdb::Options())); |
56 | 2 | LOG(INFO) << "Starting test with " << kNumKeys; |
57 | 2 | } |
58 | | |
59 | | std::string dbname_; |
60 | | }; |
61 | | |
62 | | class DestroyAllCompactionFilter : public CompactionFilter { |
63 | | public: |
64 | 2 | DestroyAllCompactionFilter() {} |
65 | | |
66 | | FilterDecision Filter(int level, const Slice& key, const Slice& existing_value, |
67 | 8 | std::string* new_value, bool* value_changed) override { |
68 | 6 | return existing_value.ToBuffer() == "destroy" ? FilterDecision::kDiscard |
69 | 2 | : FilterDecision::kKeep; |
70 | 8 | } |
71 | | |
72 | 6 | const char* Name() const override { |
73 | 6 | return "DestroyAllCompactionFilter"; |
74 | 6 | } |
75 | | }; |
76 | | |
77 | 1 | TEST_F(ManualCompactionTest, CompactTouchesAllKeys) { |
78 | 3 | for (int iter = 0; iter < 2; ++iter) { |
79 | 2 | DB* db; |
80 | 2 | Options options; |
81 | 2 | if (iter == 0) { // level compaction |
82 | 1 | options.num_levels = 3; |
83 | 1 | options.compaction_style = kCompactionStyleLevel; |
84 | 1 | } else { // universal compaction |
85 | 1 | options.compaction_style = kCompactionStyleUniversal; |
86 | 1 | } |
87 | 2 | options.create_if_missing = true; |
88 | 2 | options.compression = rocksdb::kNoCompression; |
89 | 2 | options.compaction_filter = new DestroyAllCompactionFilter(); |
90 | 2 | ASSERT_OK(DB::Open(options, dbname_, &db)); |
91 | | |
92 | 2 | ASSERT_OK(db->Put(WriteOptions(), Slice("key1"), Slice("destroy"))); |
93 | 2 | ASSERT_OK(db->Put(WriteOptions(), Slice("key2"), Slice("destroy"))); |
94 | 2 | ASSERT_OK(db->Put(WriteOptions(), Slice("key3"), Slice("value3"))); |
95 | 2 | ASSERT_OK(db->Put(WriteOptions(), Slice("key4"), Slice("destroy"))); |
96 | | |
97 | 2 | Slice key4("key4"); |
98 | 2 | ASSERT_OK(db->CompactRange(CompactRangeOptions(), nullptr, &key4)); |
99 | 2 | Iterator* itr = db->NewIterator(ReadOptions()); |
100 | 2 | itr->SeekToFirst(); |
101 | 2 | ASSERT_TRUE(itr->Valid()); |
102 | 2 | ASSERT_EQ("key3", itr->key().ToString()); |
103 | 2 | itr->Next(); |
104 | 2 | ASSERT_TRUE(!itr->Valid()); |
105 | 2 | delete itr; |
106 | | |
107 | 2 | delete options.compaction_filter; |
108 | 2 | delete db; |
109 | 2 | ASSERT_OK(DestroyDB(dbname_, options)); |
110 | 2 | } |
111 | 1 | } |
112 | | |
113 | 1 | TEST_F(ManualCompactionTest, Test) { |
114 | | // Open database. Disable compression since it affects the creation |
115 | | // of layers and the code below is trying to test against a very |
116 | | // specific scenario. |
117 | 1 | rocksdb::DB* db; |
118 | 1 | rocksdb::Options db_options; |
119 | 1 | db_options.create_if_missing = true; |
120 | 1 | db_options.compression = rocksdb::kNoCompression; |
121 | 1 | ASSERT_OK(rocksdb::DB::Open(db_options, dbname_, &db)); |
122 | | |
123 | | // create first key range |
124 | 1 | rocksdb::WriteBatch batch; |
125 | 1.10M | for (int i = 0; i < kNumKeys; i++) { |
126 | 1.10M | batch.Put(Key1(i), "value for range 1 key"); |
127 | 1.10M | } |
128 | 1 | ASSERT_OK(db->Write(rocksdb::WriteOptions(), &batch)); |
129 | | |
130 | | // create second key range |
131 | 1 | batch.Clear(); |
132 | 1.10M | for (int i = 0; i < kNumKeys; i++) { |
133 | 1.10M | batch.Put(Key2(i), "value for range 2 key"); |
134 | 1.10M | } |
135 | 1 | ASSERT_OK(db->Write(rocksdb::WriteOptions(), &batch)); |
136 | | |
137 | | // delete second key range |
138 | 1 | batch.Clear(); |
139 | 1.10M | for (int i = 0; i < kNumKeys; i++) { |
140 | 1.10M | batch.Delete(Key2(i)); |
141 | 1.10M | } |
142 | 1 | ASSERT_OK(db->Write(rocksdb::WriteOptions(), &batch)); |
143 | | |
144 | | // compact database |
145 | 1 | std::string start_key = Key1(0); |
146 | 1 | std::string end_key = Key1(kNumKeys - 1); |
147 | 1 | rocksdb::Slice least(start_key.data(), start_key.size()); |
148 | 1 | rocksdb::Slice greatest(end_key.data(), end_key.size()); |
149 | | |
150 | | // commenting out the line below causes the example to work correctly |
151 | 1 | ASSERT_OK(db->CompactRange(CompactRangeOptions(), &least, &greatest)); |
152 | | |
153 | | // count the keys |
154 | 1 | rocksdb::Iterator* iter = db->NewIterator(rocksdb::ReadOptions()); |
155 | 1 | int num_keys = 0; |
156 | 1.10M | for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { |
157 | 1.10M | num_keys++; |
158 | 1.10M | } |
159 | 1 | delete iter; |
160 | 2 | ASSERT_EQ(kNumKeys, num_keys) << "Bad number of keys"; |
161 | | |
162 | | // close database |
163 | 1 | delete db; |
164 | 1 | ASSERT_OK(DestroyDB(dbname_, rocksdb::Options())); |
165 | 1 | } |
166 | | |
167 | | } // anonymous namespace |
168 | | |
169 | 13.2k | int main(int argc, char** argv) { |
170 | 13.2k | ::testing::InitGoogleTest(&argc, argv); |
171 | 13.2k | return RUN_ALL_TESTS(); |
172 | 13.2k | } |