]> git.proxmox.com Git - ceph.git/blame - ceph/src/rocksdb/db/deletefile_test.cc
import 14.2.4 nautilus point release
[ceph.git] / ceph / src / rocksdb / db / deletefile_test.cc
CommitLineData
7c673cae 1// Copyright (c) 2011-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// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
7// Use of this source code is governed by a BSD-style license that can be
8// found in the LICENSE file. See the AUTHORS file for names of contributors.
9
10#ifndef ROCKSDB_LITE
11
12#include <stdlib.h>
13#include <map>
14#include <string>
15#include <vector>
16#include "db/db_impl.h"
17#include "db/version_set.h"
18#include "db/write_batch_internal.h"
19#include "rocksdb/db.h"
20#include "rocksdb/env.h"
21#include "rocksdb/transaction_log.h"
22#include "util/filename.h"
23#include "util/string_util.h"
24#include "util/sync_point.h"
25#include "util/testharness.h"
26#include "util/testutil.h"
27
28namespace rocksdb {
29
30class DeleteFileTest : public testing::Test {
31 public:
32 std::string dbname_;
33 Options options_;
34 DB* db_;
35 Env* env_;
36 int numlevels_;
37
38 DeleteFileTest() {
39 db_ = nullptr;
40 env_ = Env::Default();
41 options_.delete_obsolete_files_period_micros = 0; // always do full purge
42 options_.enable_thread_tracking = true;
43 options_.write_buffer_size = 1024*1024*1000;
44 options_.target_file_size_base = 1024*1024*1000;
45 options_.max_bytes_for_level_base = 1024*1024*1000;
46 options_.WAL_ttl_seconds = 300; // Used to test log files
47 options_.WAL_size_limit_MB = 1024; // Used to test log files
11fdf7f2 48 dbname_ = test::PerThreadDBPath("deletefile_test");
7c673cae
FG
49 options_.wal_dir = dbname_ + "/wal_files";
50
51 // clean up all the files that might have been there before
52 std::vector<std::string> old_files;
53 env_->GetChildren(dbname_, &old_files);
54 for (auto file : old_files) {
55 env_->DeleteFile(dbname_ + "/" + file);
56 }
57 env_->GetChildren(options_.wal_dir, &old_files);
58 for (auto file : old_files) {
59 env_->DeleteFile(options_.wal_dir + "/" + file);
60 }
61
62 DestroyDB(dbname_, options_);
63 numlevels_ = 7;
64 EXPECT_OK(ReopenDB(true));
65 }
66
67 Status ReopenDB(bool create) {
68 delete db_;
69 if (create) {
70 DestroyDB(dbname_, options_);
71 }
72 db_ = nullptr;
73 options_.create_if_missing = create;
494da23a
TL
74 Status s = DB::Open(options_, dbname_, &db_);
75 assert(db_);
76 return s;
7c673cae
FG
77 }
78
79 void CloseDB() {
80 delete db_;
81 db_ = nullptr;
82 }
83
84 void AddKeys(int numkeys, int startkey = 0) {
85 WriteOptions options;
86 options.sync = false;
87 ReadOptions roptions;
88 for (int i = startkey; i < (numkeys + startkey) ; i++) {
89 std::string temp = ToString(i);
90 Slice key(temp);
91 Slice value(temp);
92 ASSERT_OK(db_->Put(options, key, value));
93 }
94 }
95
96 int numKeysInLevels(
97 std::vector<LiveFileMetaData> &metadata,
98 std::vector<int> *keysperlevel = nullptr) {
99
100 if (keysperlevel != nullptr) {
101 keysperlevel->resize(numlevels_);
102 }
103
104 int numKeys = 0;
105 for (size_t i = 0; i < metadata.size(); i++) {
106 int startkey = atoi(metadata[i].smallestkey.c_str());
107 int endkey = atoi(metadata[i].largestkey.c_str());
108 int numkeysinfile = (endkey - startkey + 1);
109 numKeys += numkeysinfile;
110 if (keysperlevel != nullptr) {
111 (*keysperlevel)[(int)metadata[i].level] += numkeysinfile;
112 }
113 fprintf(stderr, "level %d name %s smallest %s largest %s\n",
114 metadata[i].level, metadata[i].name.c_str(),
115 metadata[i].smallestkey.c_str(),
116 metadata[i].largestkey.c_str());
117 }
118 return numKeys;
119 }
120
121 void CreateTwoLevels() {
122 AddKeys(50000, 10000);
123 DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
124 ASSERT_OK(dbi->TEST_FlushMemTable());
125 ASSERT_OK(dbi->TEST_WaitForFlushMemTable());
126 for (int i = 0; i < 2; ++i) {
127 ASSERT_OK(dbi->TEST_CompactRange(i, nullptr, nullptr));
128 }
129
130 AddKeys(50000, 10000);
131 ASSERT_OK(dbi->TEST_FlushMemTable());
132 ASSERT_OK(dbi->TEST_WaitForFlushMemTable());
133 ASSERT_OK(dbi->TEST_CompactRange(0, nullptr, nullptr));
134 }
135
136 void CheckFileTypeCounts(std::string& dir,
137 int required_log,
138 int required_sst,
139 int required_manifest) {
140 std::vector<std::string> filenames;
141 env_->GetChildren(dir, &filenames);
142
143 int log_cnt = 0, sst_cnt = 0, manifest_cnt = 0;
144 for (auto file : filenames) {
145 uint64_t number;
146 FileType type;
147 if (ParseFileName(file, &number, &type)) {
148 log_cnt += (type == kLogFile);
149 sst_cnt += (type == kTableFile);
150 manifest_cnt += (type == kDescriptorFile);
151 }
152 }
153 ASSERT_EQ(required_log, log_cnt);
154 ASSERT_EQ(required_sst, sst_cnt);
155 ASSERT_EQ(required_manifest, manifest_cnt);
156 }
157
158 static void DoSleep(void* arg) {
159 auto test = reinterpret_cast<DeleteFileTest*>(arg);
160 test->env_->SleepForMicroseconds(2 * 1000 * 1000);
161 }
162
163 // An empty job to guard all jobs are processed
11fdf7f2 164 static void GuardFinish(void* /*arg*/) {
7c673cae
FG
165 TEST_SYNC_POINT("DeleteFileTest::GuardFinish");
166 }
167};
168
169TEST_F(DeleteFileTest, AddKeysAndQueryLevels) {
170 CreateTwoLevels();
171 std::vector<LiveFileMetaData> metadata;
172 db_->GetLiveFilesMetaData(&metadata);
173
174 std::string level1file = "";
175 int level1keycount = 0;
176 std::string level2file = "";
177 int level2keycount = 0;
178 int level1index = 0;
179 int level2index = 1;
180
181 ASSERT_EQ((int)metadata.size(), 2);
182 if (metadata[0].level == 2) {
183 level1index = 1;
184 level2index = 0;
185 }
186
187 level1file = metadata[level1index].name;
188 int startkey = atoi(metadata[level1index].smallestkey.c_str());
189 int endkey = atoi(metadata[level1index].largestkey.c_str());
190 level1keycount = (endkey - startkey + 1);
191 level2file = metadata[level2index].name;
192 startkey = atoi(metadata[level2index].smallestkey.c_str());
193 endkey = atoi(metadata[level2index].largestkey.c_str());
194 level2keycount = (endkey - startkey + 1);
195
196 // COntrolled setup. Levels 1 and 2 should both have 50K files.
197 // This is a little fragile as it depends on the current
198 // compaction heuristics.
199 ASSERT_EQ(level1keycount, 50000);
200 ASSERT_EQ(level2keycount, 50000);
201
202 Status status = db_->DeleteFile("0.sst");
203 ASSERT_TRUE(status.IsInvalidArgument());
204
205 // intermediate level files cannot be deleted.
206 status = db_->DeleteFile(level1file);
207 ASSERT_TRUE(status.IsInvalidArgument());
208
209 // Lowest level file deletion should succeed.
210 ASSERT_OK(db_->DeleteFile(level2file));
211
212 CloseDB();
213}
214
215TEST_F(DeleteFileTest, PurgeObsoleteFilesTest) {
216 CreateTwoLevels();
217 // there should be only one (empty) log file because CreateTwoLevels()
218 // flushes the memtables to disk
219 CheckFileTypeCounts(options_.wal_dir, 1, 0, 0);
220 // 2 ssts, 1 manifest
221 CheckFileTypeCounts(dbname_, 0, 2, 1);
222 std::string first("0"), last("999999");
223 CompactRangeOptions compact_options;
224 compact_options.change_level = true;
225 compact_options.target_level = 2;
226 Slice first_slice(first), last_slice(last);
227 db_->CompactRange(compact_options, &first_slice, &last_slice);
228 // 1 sst after compaction
229 CheckFileTypeCounts(dbname_, 0, 1, 1);
230
231 // this time, we keep an iterator alive
232 ReopenDB(true);
11fdf7f2 233 Iterator *itr = nullptr;
7c673cae
FG
234 CreateTwoLevels();
235 itr = db_->NewIterator(ReadOptions());
236 db_->CompactRange(compact_options, &first_slice, &last_slice);
237 // 3 sst after compaction with live iterator
238 CheckFileTypeCounts(dbname_, 0, 3, 1);
239 delete itr;
240 // 1 sst after iterator deletion
241 CheckFileTypeCounts(dbname_, 0, 1, 1);
242
243 CloseDB();
244}
245
494da23a 246TEST_F(DeleteFileTest, BackgroundPurgeIteratorTest) {
7c673cae
FG
247 std::string first("0"), last("999999");
248 CompactRangeOptions compact_options;
249 compact_options.change_level = true;
250 compact_options.target_level = 2;
251 Slice first_slice(first), last_slice(last);
252
253 // We keep an iterator alive
11fdf7f2 254 Iterator* itr = nullptr;
7c673cae
FG
255 CreateTwoLevels();
256 ReadOptions options;
257 options.background_purge_on_iterator_cleanup = true;
258 itr = db_->NewIterator(options);
259 db_->CompactRange(compact_options, &first_slice, &last_slice);
260 // 3 sst after compaction with live iterator
261 CheckFileTypeCounts(dbname_, 0, 3, 1);
262 test::SleepingBackgroundTask sleeping_task_before;
263 env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
264 &sleeping_task_before, Env::Priority::HIGH);
265 delete itr;
266 test::SleepingBackgroundTask sleeping_task_after;
267 env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
268 &sleeping_task_after, Env::Priority::HIGH);
269
270 // Make sure no purges are executed foreground
271 CheckFileTypeCounts(dbname_, 0, 3, 1);
272 sleeping_task_before.WakeUp();
273 sleeping_task_before.WaitUntilDone();
274
275 // Make sure all background purges are executed
276 sleeping_task_after.WakeUp();
277 sleeping_task_after.WaitUntilDone();
278 // 1 sst after iterator deletion
279 CheckFileTypeCounts(dbname_, 0, 1, 1);
280
281 CloseDB();
282}
283
494da23a
TL
284TEST_F(DeleteFileTest, BackgroundPurgeCFDropTest) {
285 auto do_test = [&](bool bg_purge) {
286 ColumnFamilyOptions co;
287 WriteOptions wo;
288 FlushOptions fo;
289 ColumnFamilyHandle* cfh = nullptr;
290
291 ASSERT_OK(db_->CreateColumnFamily(co, "dropme", &cfh));
292
293 ASSERT_OK(db_->Put(wo, cfh, "pika", "chu"));
294 ASSERT_OK(db_->Flush(fo, cfh));
295 // Expect 1 sst file.
296 CheckFileTypeCounts(dbname_, 0, 1, 1);
297
298 ASSERT_OK(db_->DropColumnFamily(cfh));
299 // Still 1 file, it won't be deleted while ColumnFamilyHandle is alive.
300 CheckFileTypeCounts(dbname_, 0, 1, 1);
301
302 delete cfh;
303 test::SleepingBackgroundTask sleeping_task_after;
304 env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
305 &sleeping_task_after, Env::Priority::HIGH);
306 // If background purge is enabled, the file should still be there.
307 CheckFileTypeCounts(dbname_, 0, bg_purge ? 1 : 0, 1);
308
309 // Execute background purges.
310 sleeping_task_after.WakeUp();
311 sleeping_task_after.WaitUntilDone();
312 // The file should have been deleted.
313 CheckFileTypeCounts(dbname_, 0, 0, 1);
314 };
315
316 {
317 SCOPED_TRACE("avoid_unnecessary_blocking_io = false");
318 do_test(false);
319 }
320
321 options_.avoid_unnecessary_blocking_io = true;
322 ASSERT_OK(ReopenDB(false));
323 {
324 SCOPED_TRACE("avoid_unnecessary_blocking_io = true");
325 do_test(true);
326 }
327
328 CloseDB();
329}
330
7c673cae
FG
331// This test is to reproduce a bug that read invalid ReadOption in iterator
332// cleanup function
333TEST_F(DeleteFileTest, BackgroundPurgeCopyOptions) {
334 std::string first("0"), last("999999");
335 CompactRangeOptions compact_options;
336 compact_options.change_level = true;
337 compact_options.target_level = 2;
338 Slice first_slice(first), last_slice(last);
339
340 // We keep an iterator alive
11fdf7f2 341 Iterator* itr = nullptr;
7c673cae
FG
342 CreateTwoLevels();
343 ReadOptions* options = new ReadOptions();
344 options->background_purge_on_iterator_cleanup = true;
345 itr = db_->NewIterator(*options);
346 // ReadOptions is deleted, but iterator cleanup function should not be
347 // affected
348 delete options;
349
350 db_->CompactRange(compact_options, &first_slice, &last_slice);
351 // 3 sst after compaction with live iterator
352 CheckFileTypeCounts(dbname_, 0, 3, 1);
353 delete itr;
354
355 test::SleepingBackgroundTask sleeping_task_after;
356 env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
357 &sleeping_task_after, Env::Priority::HIGH);
358
359 // Make sure all background purges are executed
360 sleeping_task_after.WakeUp();
361 sleeping_task_after.WaitUntilDone();
362 // 1 sst after iterator deletion
363 CheckFileTypeCounts(dbname_, 0, 1, 1);
364
365 CloseDB();
366}
367
368TEST_F(DeleteFileTest, BackgroundPurgeTestMultipleJobs) {
369 std::string first("0"), last("999999");
370 CompactRangeOptions compact_options;
371 compact_options.change_level = true;
372 compact_options.target_level = 2;
373 Slice first_slice(first), last_slice(last);
374
375 // We keep an iterator alive
376 CreateTwoLevels();
377 ReadOptions options;
378 options.background_purge_on_iterator_cleanup = true;
379 Iterator* itr1 = db_->NewIterator(options);
380 CreateTwoLevels();
381 Iterator* itr2 = db_->NewIterator(options);
382 db_->CompactRange(compact_options, &first_slice, &last_slice);
383 // 5 sst files after 2 compactions with 2 live iterators
384 CheckFileTypeCounts(dbname_, 0, 5, 1);
385
386 // ~DBImpl should wait until all BGWorkPurge are finished
387 rocksdb::SyncPoint::GetInstance()->LoadDependency(
388 {{"DBImpl::~DBImpl:WaitJob", "DBImpl::BGWorkPurge"},
389 {"DeleteFileTest::GuardFinish",
390 "DeleteFileTest::BackgroundPurgeTestMultipleJobs:DBClose"}});
391 rocksdb::SyncPoint::GetInstance()->EnableProcessing();
392
393 delete itr1;
394 env_->Schedule(&DeleteFileTest::DoSleep, this, Env::Priority::HIGH);
395 delete itr2;
396 env_->Schedule(&DeleteFileTest::GuardFinish, nullptr, Env::Priority::HIGH);
397 CloseDB();
398
399 TEST_SYNC_POINT("DeleteFileTest::BackgroundPurgeTestMultipleJobs:DBClose");
400 // 1 sst after iterator deletion
401 CheckFileTypeCounts(dbname_, 0, 1, 1);
402 rocksdb::SyncPoint::GetInstance()->DisableProcessing();
403}
404
405TEST_F(DeleteFileTest, DeleteFileWithIterator) {
406 CreateTwoLevels();
407 ReadOptions options;
408 Iterator* it = db_->NewIterator(options);
409 std::vector<LiveFileMetaData> metadata;
410 db_->GetLiveFilesMetaData(&metadata);
411
412 std::string level2file = "";
413
414 ASSERT_EQ((int)metadata.size(), 2);
415 if (metadata[0].level == 1) {
416 level2file = metadata[1].name;
417 } else {
418 level2file = metadata[0].name;
419 }
420
421 Status status = db_->DeleteFile(level2file);
422 fprintf(stdout, "Deletion status %s: %s\n",
423 level2file.c_str(), status.ToString().c_str());
424 ASSERT_TRUE(status.ok());
425 it->SeekToFirst();
426 int numKeysIterated = 0;
427 while(it->Valid()) {
428 numKeysIterated++;
429 it->Next();
430 }
431 ASSERT_EQ(numKeysIterated, 50000);
432 delete it;
433 CloseDB();
434}
435
436TEST_F(DeleteFileTest, DeleteLogFiles) {
437 AddKeys(10, 0);
438 VectorLogPtr logfiles;
439 db_->GetSortedWalFiles(logfiles);
440 ASSERT_GT(logfiles.size(), 0UL);
441 // Take the last log file which is expected to be alive and try to delete it
442 // Should not succeed because live logs are not allowed to be deleted
443 std::unique_ptr<LogFile> alive_log = std::move(logfiles.back());
444 ASSERT_EQ(alive_log->Type(), kAliveLogFile);
445 ASSERT_OK(env_->FileExists(options_.wal_dir + "/" + alive_log->PathName()));
446 fprintf(stdout, "Deleting alive log file %s\n",
447 alive_log->PathName().c_str());
448 ASSERT_TRUE(!db_->DeleteFile(alive_log->PathName()).ok());
449 ASSERT_OK(env_->FileExists(options_.wal_dir + "/" + alive_log->PathName()));
450 logfiles.clear();
451
452 // Call Flush to bring about a new working log file and add more keys
453 // Call Flush again to flush out memtable and move alive log to archived log
454 // and try to delete the archived log file
455 FlushOptions fopts;
456 db_->Flush(fopts);
457 AddKeys(10, 0);
458 db_->Flush(fopts);
459 db_->GetSortedWalFiles(logfiles);
460 ASSERT_GT(logfiles.size(), 0UL);
461 std::unique_ptr<LogFile> archived_log = std::move(logfiles.front());
462 ASSERT_EQ(archived_log->Type(), kArchivedLogFile);
463 ASSERT_OK(
464 env_->FileExists(options_.wal_dir + "/" + archived_log->PathName()));
465 fprintf(stdout, "Deleting archived log file %s\n",
466 archived_log->PathName().c_str());
467 ASSERT_OK(db_->DeleteFile(archived_log->PathName()));
468 ASSERT_EQ(Status::NotFound(), env_->FileExists(options_.wal_dir + "/" +
469 archived_log->PathName()));
470 CloseDB();
471}
472
473TEST_F(DeleteFileTest, DeleteNonDefaultColumnFamily) {
474 CloseDB();
475 DBOptions db_options;
476 db_options.create_if_missing = true;
477 db_options.create_missing_column_families = true;
478 std::vector<ColumnFamilyDescriptor> column_families;
479 column_families.emplace_back();
480 column_families.emplace_back("new_cf", ColumnFamilyOptions());
481
482 std::vector<rocksdb::ColumnFamilyHandle*> handles;
483 rocksdb::DB* db;
484 ASSERT_OK(DB::Open(db_options, dbname_, column_families, &handles, &db));
485
486 Random rnd(5);
487 for (int i = 0; i < 1000; ++i) {
488 ASSERT_OK(db->Put(WriteOptions(), handles[1], test::RandomKey(&rnd, 10),
489 test::RandomKey(&rnd, 10)));
490 }
491 ASSERT_OK(db->Flush(FlushOptions(), handles[1]));
492 for (int i = 0; i < 1000; ++i) {
493 ASSERT_OK(db->Put(WriteOptions(), handles[1], test::RandomKey(&rnd, 10),
494 test::RandomKey(&rnd, 10)));
495 }
496 ASSERT_OK(db->Flush(FlushOptions(), handles[1]));
497
498 std::vector<LiveFileMetaData> metadata;
499 db->GetLiveFilesMetaData(&metadata);
500 ASSERT_EQ(2U, metadata.size());
501 ASSERT_EQ("new_cf", metadata[0].column_family_name);
502 ASSERT_EQ("new_cf", metadata[1].column_family_name);
503 auto old_file = metadata[0].smallest_seqno < metadata[1].smallest_seqno
504 ? metadata[0].name
505 : metadata[1].name;
506 auto new_file = metadata[0].smallest_seqno > metadata[1].smallest_seqno
507 ? metadata[0].name
508 : metadata[1].name;
509 ASSERT_TRUE(db->DeleteFile(new_file).IsInvalidArgument());
510 ASSERT_OK(db->DeleteFile(old_file));
511
512 {
513 std::unique_ptr<Iterator> itr(db->NewIterator(ReadOptions(), handles[1]));
514 int count = 0;
515 for (itr->SeekToFirst(); itr->Valid(); itr->Next()) {
516 ASSERT_OK(itr->status());
517 ++count;
518 }
519 ASSERT_EQ(count, 1000);
520 }
521
522 delete handles[0];
523 delete handles[1];
524 delete db;
525
526 ASSERT_OK(DB::Open(db_options, dbname_, column_families, &handles, &db));
527 {
528 std::unique_ptr<Iterator> itr(db->NewIterator(ReadOptions(), handles[1]));
529 int count = 0;
530 for (itr->SeekToFirst(); itr->Valid(); itr->Next()) {
531 ASSERT_OK(itr->status());
532 ++count;
533 }
534 ASSERT_EQ(count, 1000);
535 }
536
537 delete handles[0];
538 delete handles[1];
539 delete db;
540}
541
542} //namespace rocksdb
543
544int main(int argc, char** argv) {
545 ::testing::InitGoogleTest(&argc, argv);
546 return RUN_ALL_TESTS();
547}
548
549#else
550#include <stdio.h>
551
11fdf7f2 552int main(int /*argc*/, char** /*argv*/) {
7c673cae
FG
553 fprintf(stderr,
554 "SKIPPED as DBImpl::DeleteFile is not supported in ROCKSDB_LITE\n");
555 return 0;
556}
557
558#endif // !ROCKSDB_LITE