]> git.proxmox.com Git - ceph.git/blame - ceph/src/rocksdb/db/repair_test.cc
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / rocksdb / db / repair_test.cc
CommitLineData
7c673cae 1// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
11fdf7f2
TL
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).
7c673cae
FG
5
6#ifndef ROCKSDB_LITE
7
8#include <algorithm>
9#include <string>
10#include <vector>
11
f67539c2 12#include "db/db_impl/db_impl.h"
7c673cae 13#include "db/db_test_util.h"
f67539c2 14#include "file/file_util.h"
7c673cae
FG
15#include "rocksdb/comparator.h"
16#include "rocksdb/db.h"
17#include "rocksdb/transaction_log.h"
7c673cae
FG
18#include "util/string_util.h"
19
f67539c2 20namespace ROCKSDB_NAMESPACE {
7c673cae
FG
21
22#ifndef ROCKSDB_LITE
23class RepairTest : public DBTestBase {
24 public:
25 RepairTest() : DBTestBase("/repair_test") {}
26
27 std::string GetFirstSstPath() {
28 uint64_t manifest_size;
29 std::vector<std::string> files;
30 db_->GetLiveFiles(files, &manifest_size);
31 auto sst_iter =
32 std::find_if(files.begin(), files.end(), [](const std::string& file) {
33 uint64_t number;
34 FileType type;
35 bool ok = ParseFileName(file, &number, &type);
36 return ok && type == kTableFile;
37 });
38 return sst_iter == files.end() ? "" : dbname_ + *sst_iter;
39 }
40};
41
42TEST_F(RepairTest, LostManifest) {
43 // Add a couple SST files, delete the manifest, and verify RepairDB() saves
44 // the day.
45 Put("key", "val");
46 Flush();
47 Put("key2", "val2");
48 Flush();
49 // Need to get path before Close() deletes db_, but delete it after Close() to
50 // ensure Close() didn't change the manifest.
51 std::string manifest_path =
52 DescriptorFileName(dbname_, dbfull()->TEST_Current_Manifest_FileNo());
53
54 Close();
55 ASSERT_OK(env_->FileExists(manifest_path));
56 ASSERT_OK(env_->DeleteFile(manifest_path));
57 ASSERT_OK(RepairDB(dbname_, CurrentOptions()));
58 Reopen(CurrentOptions());
59
60 ASSERT_EQ(Get("key"), "val");
61 ASSERT_EQ(Get("key2"), "val2");
62}
63
64TEST_F(RepairTest, CorruptManifest) {
65 // Manifest is in an invalid format. Expect a full recovery.
66 Put("key", "val");
67 Flush();
68 Put("key2", "val2");
69 Flush();
70 // Need to get path before Close() deletes db_, but overwrite it after Close()
71 // to ensure Close() didn't change the manifest.
72 std::string manifest_path =
73 DescriptorFileName(dbname_, dbfull()->TEST_Current_Manifest_FileNo());
74
75 Close();
76 ASSERT_OK(env_->FileExists(manifest_path));
f67539c2
TL
77
78 LegacyFileSystemWrapper fs(env_);
79 CreateFile(&fs, manifest_path, "blah", false /* use_fsync */);
7c673cae
FG
80 ASSERT_OK(RepairDB(dbname_, CurrentOptions()));
81 Reopen(CurrentOptions());
82
83 ASSERT_EQ(Get("key"), "val");
84 ASSERT_EQ(Get("key2"), "val2");
85}
86
87TEST_F(RepairTest, IncompleteManifest) {
88 // In this case, the manifest is valid but does not reference all of the SST
89 // files. Expect a full recovery.
90 Put("key", "val");
91 Flush();
92 std::string orig_manifest_path =
93 DescriptorFileName(dbname_, dbfull()->TEST_Current_Manifest_FileNo());
94 CopyFile(orig_manifest_path, orig_manifest_path + ".tmp");
95 Put("key2", "val2");
96 Flush();
97 // Need to get path before Close() deletes db_, but overwrite it after Close()
98 // to ensure Close() didn't change the manifest.
99 std::string new_manifest_path =
100 DescriptorFileName(dbname_, dbfull()->TEST_Current_Manifest_FileNo());
101
102 Close();
103 ASSERT_OK(env_->FileExists(new_manifest_path));
104 // Replace the manifest with one that is only aware of the first SST file.
105 CopyFile(orig_manifest_path + ".tmp", new_manifest_path);
106 ASSERT_OK(RepairDB(dbname_, CurrentOptions()));
107 Reopen(CurrentOptions());
108
109 ASSERT_EQ(Get("key"), "val");
110 ASSERT_EQ(Get("key2"), "val2");
111}
112
11fdf7f2
TL
113TEST_F(RepairTest, PostRepairSstFileNumbering) {
114 // Verify after a DB is repaired, new files will be assigned higher numbers
115 // than old files.
116 Put("key", "val");
117 Flush();
118 Put("key2", "val2");
119 Flush();
120 uint64_t pre_repair_file_num = dbfull()->TEST_Current_Next_FileNo();
121 Close();
122
123 ASSERT_OK(RepairDB(dbname_, CurrentOptions()));
124
125 Reopen(CurrentOptions());
126 uint64_t post_repair_file_num = dbfull()->TEST_Current_Next_FileNo();
127 ASSERT_GE(post_repair_file_num, pre_repair_file_num);
128}
129
7c673cae
FG
130TEST_F(RepairTest, LostSst) {
131 // Delete one of the SST files but preserve the manifest that refers to it,
132 // then verify the DB is still usable for the intact SST.
133 Put("key", "val");
134 Flush();
135 Put("key2", "val2");
136 Flush();
137 auto sst_path = GetFirstSstPath();
138 ASSERT_FALSE(sst_path.empty());
139 ASSERT_OK(env_->DeleteFile(sst_path));
140
141 Close();
142 ASSERT_OK(RepairDB(dbname_, CurrentOptions()));
143 Reopen(CurrentOptions());
144
145 // Exactly one of the key-value pairs should be in the DB now.
146 ASSERT_TRUE((Get("key") == "val") != (Get("key2") == "val2"));
147}
148
149TEST_F(RepairTest, CorruptSst) {
150 // Corrupt one of the SST files but preserve the manifest that refers to it,
151 // then verify the DB is still usable for the intact SST.
152 Put("key", "val");
153 Flush();
154 Put("key2", "val2");
155 Flush();
156 auto sst_path = GetFirstSstPath();
157 ASSERT_FALSE(sst_path.empty());
f67539c2
TL
158
159 LegacyFileSystemWrapper fs(env_);
160 CreateFile(&fs, sst_path, "blah", false /* use_fsync */);
7c673cae
FG
161
162 Close();
163 ASSERT_OK(RepairDB(dbname_, CurrentOptions()));
164 Reopen(CurrentOptions());
165
166 // Exactly one of the key-value pairs should be in the DB now.
167 ASSERT_TRUE((Get("key") == "val") != (Get("key2") == "val2"));
168}
169
170TEST_F(RepairTest, UnflushedSst) {
171 // This test case invokes repair while some data is unflushed, then verifies
172 // that data is in the db.
173 Put("key", "val");
174 VectorLogPtr wal_files;
175 ASSERT_OK(dbfull()->GetSortedWalFiles(wal_files));
176 ASSERT_EQ(wal_files.size(), 1);
177 uint64_t total_ssts_size;
178 GetAllSSTFiles(&total_ssts_size);
179 ASSERT_EQ(total_ssts_size, 0);
180 // Need to get path before Close() deletes db_, but delete it after Close() to
181 // ensure Close() didn't change the manifest.
182 std::string manifest_path =
183 DescriptorFileName(dbname_, dbfull()->TEST_Current_Manifest_FileNo());
184
185 Close();
186 ASSERT_OK(env_->FileExists(manifest_path));
187 ASSERT_OK(env_->DeleteFile(manifest_path));
188 ASSERT_OK(RepairDB(dbname_, CurrentOptions()));
189 Reopen(CurrentOptions());
190
191 ASSERT_OK(dbfull()->GetSortedWalFiles(wal_files));
192 ASSERT_EQ(wal_files.size(), 0);
193 GetAllSSTFiles(&total_ssts_size);
194 ASSERT_GT(total_ssts_size, 0);
195 ASSERT_EQ(Get("key"), "val");
196}
197
11fdf7f2
TL
198TEST_F(RepairTest, SeparateWalDir) {
199 do {
200 Options options = CurrentOptions();
201 DestroyAndReopen(options);
202 Put("key", "val");
203 Put("foo", "bar");
204 VectorLogPtr wal_files;
205 ASSERT_OK(dbfull()->GetSortedWalFiles(wal_files));
206 ASSERT_EQ(wal_files.size(), 1);
207 uint64_t total_ssts_size;
208 GetAllSSTFiles(&total_ssts_size);
209 ASSERT_EQ(total_ssts_size, 0);
210 std::string manifest_path =
211 DescriptorFileName(dbname_, dbfull()->TEST_Current_Manifest_FileNo());
212
213 Close();
214 ASSERT_OK(env_->FileExists(manifest_path));
215 ASSERT_OK(env_->DeleteFile(manifest_path));
216 ASSERT_OK(RepairDB(dbname_, options));
217
218 // make sure that all WALs are converted to SSTables.
219 options.wal_dir = "";
220
221 Reopen(options);
222 ASSERT_OK(dbfull()->GetSortedWalFiles(wal_files));
223 ASSERT_EQ(wal_files.size(), 0);
224 GetAllSSTFiles(&total_ssts_size);
225 ASSERT_GT(total_ssts_size, 0);
226 ASSERT_EQ(Get("key"), "val");
227 ASSERT_EQ(Get("foo"), "bar");
228
229 } while(ChangeWalOptions());
230}
231
7c673cae
FG
232TEST_F(RepairTest, RepairMultipleColumnFamilies) {
233 // Verify repair logic associates SST files with their original column
234 // families.
235 const int kNumCfs = 3;
236 const int kEntriesPerCf = 2;
237 DestroyAndReopen(CurrentOptions());
238 CreateAndReopenWithCF({"pikachu1", "pikachu2"}, CurrentOptions());
239 for (int i = 0; i < kNumCfs; ++i) {
240 for (int j = 0; j < kEntriesPerCf; ++j) {
241 Put(i, "key" + ToString(j), "val" + ToString(j));
242 if (j == kEntriesPerCf - 1 && i == kNumCfs - 1) {
243 // Leave one unflushed so we can verify WAL entries are properly
244 // associated with column families.
245 continue;
246 }
247 Flush(i);
248 }
249 }
250
251 // Need to get path before Close() deletes db_, but delete it after Close() to
252 // ensure Close() doesn't re-create the manifest.
253 std::string manifest_path =
254 DescriptorFileName(dbname_, dbfull()->TEST_Current_Manifest_FileNo());
255 Close();
256 ASSERT_OK(env_->FileExists(manifest_path));
257 ASSERT_OK(env_->DeleteFile(manifest_path));
258
259 ASSERT_OK(RepairDB(dbname_, CurrentOptions()));
260
261 ReopenWithColumnFamilies({"default", "pikachu1", "pikachu2"},
262 CurrentOptions());
263 for (int i = 0; i < kNumCfs; ++i) {
264 for (int j = 0; j < kEntriesPerCf; ++j) {
265 ASSERT_EQ(Get(i, "key" + ToString(j)), "val" + ToString(j));
266 }
267 }
268}
269
270TEST_F(RepairTest, RepairColumnFamilyOptions) {
271 // Verify repair logic uses correct ColumnFamilyOptions when repairing a
272 // database with different options for column families.
273 const int kNumCfs = 2;
274 const int kEntriesPerCf = 2;
275
276 Options opts(CurrentOptions()), rev_opts(CurrentOptions());
277 opts.comparator = BytewiseComparator();
278 rev_opts.comparator = ReverseBytewiseComparator();
279
280 DestroyAndReopen(opts);
281 CreateColumnFamilies({"reverse"}, rev_opts);
282 ReopenWithColumnFamilies({"default", "reverse"},
283 std::vector<Options>{opts, rev_opts});
284 for (int i = 0; i < kNumCfs; ++i) {
285 for (int j = 0; j < kEntriesPerCf; ++j) {
286 Put(i, "key" + ToString(j), "val" + ToString(j));
287 if (i == kNumCfs - 1 && j == kEntriesPerCf - 1) {
288 // Leave one unflushed so we can verify RepairDB's flush logic
289 continue;
290 }
291 Flush(i);
292 }
293 }
294 Close();
295
296 // RepairDB() records the comparator in the manifest, and DB::Open would fail
297 // if a different comparator were used.
298 ASSERT_OK(RepairDB(dbname_, opts, {{"default", opts}, {"reverse", rev_opts}},
299 opts /* unknown_cf_opts */));
300 ASSERT_OK(TryReopenWithColumnFamilies({"default", "reverse"},
301 std::vector<Options>{opts, rev_opts}));
302 for (int i = 0; i < kNumCfs; ++i) {
303 for (int j = 0; j < kEntriesPerCf; ++j) {
304 ASSERT_EQ(Get(i, "key" + ToString(j)), "val" + ToString(j));
305 }
306 }
307
308 // Examine table properties to verify RepairDB() used the right options when
309 // converting WAL->SST
310 TablePropertiesCollection fname_to_props;
311 db_->GetPropertiesOfAllTables(handles_[1], &fname_to_props);
312 ASSERT_EQ(fname_to_props.size(), 2U);
313 for (const auto& fname_and_props : fname_to_props) {
314 std::string comparator_name (
315 InternalKeyComparator(rev_opts.comparator).Name());
316 comparator_name = comparator_name.substr(comparator_name.find(':') + 1);
317 ASSERT_EQ(comparator_name,
318 fname_and_props.second->comparator_name);
319 }
494da23a 320 Close();
7c673cae
FG
321
322 // Also check comparator when it's provided via "unknown" CF options
323 ASSERT_OK(RepairDB(dbname_, opts, {{"default", opts}},
324 rev_opts /* unknown_cf_opts */));
325 ASSERT_OK(TryReopenWithColumnFamilies({"default", "reverse"},
326 std::vector<Options>{opts, rev_opts}));
327 for (int i = 0; i < kNumCfs; ++i) {
328 for (int j = 0; j < kEntriesPerCf; ++j) {
329 ASSERT_EQ(Get(i, "key" + ToString(j)), "val" + ToString(j));
330 }
331 }
332}
333
11fdf7f2
TL
334TEST_F(RepairTest, DbNameContainsTrailingSlash) {
335 {
336 bool tmp;
337 if (env_->AreFilesSame("", "", &tmp).IsNotSupported()) {
338 fprintf(stderr,
339 "skipping RepairTest.DbNameContainsTrailingSlash due to "
340 "unsupported Env::AreFilesSame\n");
341 return;
342 }
343 }
344
345 Put("key", "val");
346 Flush();
347 Close();
348
349 ASSERT_OK(RepairDB(dbname_ + "/", CurrentOptions()));
350 Reopen(CurrentOptions());
351 ASSERT_EQ(Get("key"), "val");
352}
7c673cae 353#endif // ROCKSDB_LITE
f67539c2 354} // namespace ROCKSDB_NAMESPACE
7c673cae
FG
355
356int main(int argc, char** argv) {
357 ::testing::InitGoogleTest(&argc, argv);
358 return RUN_ALL_TESTS();
359}
360
361#else
362#include <stdio.h>
363
11fdf7f2 364int main(int /*argc*/, char** /*argv*/) {
7c673cae
FG
365 fprintf(stderr, "SKIPPED as RepairDB is not supported in ROCKSDB_LITE\n");
366 return 0;
367}
368
369#endif // ROCKSDB_LITE