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 //
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 #if !defined(ROCKSDB_LITE) && !defined(OS_WIN)
11
12 #include <algorithm>
13 #include <limits>
14 #include <string>
15 #include <utility>
16
17 #include "db/db_impl/db_impl.h"
18 #include "env/env_chroot.h"
19 #include "file/filename.h"
20 #include "port/port.h"
21 #include "port/stack_trace.h"
22 #include "rocksdb/rate_limiter.h"
23 #include "rocksdb/transaction_log.h"
24 #include "rocksdb/types.h"
25 #include "rocksdb/utilities/backupable_db.h"
26 #include "rocksdb/utilities/options_util.h"
27 #include "test_util/sync_point.h"
28 #include "test_util/testharness.h"
29 #include "test_util/testutil.h"
30 #include "util/mutexlock.h"
31 #include "util/random.h"
32 #include "util/stderr_logger.h"
33 #include "util/string_util.h"
34
35 namespace ROCKSDB_NAMESPACE {
36
37 namespace {
38
39 class DummyDB : public StackableDB {
40 public:
41 /* implicit */
DummyDB(const Options & options,const std::string & dbname)42 DummyDB(const Options& options, const std::string& dbname)
43 : StackableDB(nullptr), options_(options), dbname_(dbname),
44 deletions_enabled_(true), sequence_number_(0) {}
45
GetLatestSequenceNumber() const46 SequenceNumber GetLatestSequenceNumber() const override {
47 return ++sequence_number_;
48 }
49
GetName() const50 const std::string& GetName() const override { return dbname_; }
51
GetEnv() const52 Env* GetEnv() const override { return options_.env; }
53
54 using DB::GetOptions;
GetOptions(ColumnFamilyHandle *) const55 Options GetOptions(ColumnFamilyHandle* /*column_family*/) const override {
56 return options_;
57 }
58
GetDBOptions() const59 DBOptions GetDBOptions() const override { return DBOptions(options_); }
60
EnableFileDeletions(bool)61 Status EnableFileDeletions(bool /*force*/) override {
62 EXPECT_TRUE(!deletions_enabled_);
63 deletions_enabled_ = true;
64 return Status::OK();
65 }
66
DisableFileDeletions()67 Status DisableFileDeletions() override {
68 EXPECT_TRUE(deletions_enabled_);
69 deletions_enabled_ = false;
70 return Status::OK();
71 }
72
GetLiveFiles(std::vector<std::string> & vec,uint64_t * mfs,bool=true)73 Status GetLiveFiles(std::vector<std::string>& vec, uint64_t* mfs,
74 bool /*flush_memtable*/ = true) override {
75 EXPECT_TRUE(!deletions_enabled_);
76 vec = live_files_;
77 *mfs = 100;
78 return Status::OK();
79 }
80
DefaultColumnFamily() const81 ColumnFamilyHandle* DefaultColumnFamily() const override { return nullptr; }
82
83 class DummyLogFile : public LogFile {
84 public:
85 /* implicit */
DummyLogFile(const std::string & path,bool alive=true)86 DummyLogFile(const std::string& path, bool alive = true)
87 : path_(path), alive_(alive) {}
88
PathName() const89 std::string PathName() const override { return path_; }
90
LogNumber() const91 uint64_t LogNumber() const override {
92 // what business do you have calling this method?
93 ADD_FAILURE();
94 return 0;
95 }
96
Type() const97 WalFileType Type() const override {
98 return alive_ ? kAliveLogFile : kArchivedLogFile;
99 }
100
StartSequence() const101 SequenceNumber StartSequence() const override {
102 // this seqnum guarantees the dummy file will be included in the backup
103 // as long as it is alive.
104 return kMaxSequenceNumber;
105 }
106
SizeFileBytes() const107 uint64_t SizeFileBytes() const override { return 0; }
108
109 private:
110 std::string path_;
111 bool alive_;
112 }; // DummyLogFile
113
GetSortedWalFiles(VectorLogPtr & files)114 Status GetSortedWalFiles(VectorLogPtr& files) override {
115 EXPECT_TRUE(!deletions_enabled_);
116 files.resize(wal_files_.size());
117 for (size_t i = 0; i < files.size(); ++i) {
118 files[i].reset(
119 new DummyLogFile(wal_files_[i].first, wal_files_[i].second));
120 }
121 return Status::OK();
122 }
123
124 // To avoid FlushWAL called on stacked db which is nullptr
FlushWAL(bool)125 Status FlushWAL(bool /*sync*/) override { return Status::OK(); }
126
127 std::vector<std::string> live_files_;
128 // pair<filename, alive?>
129 std::vector<std::pair<std::string, bool>> wal_files_;
130 private:
131 Options options_;
132 std::string dbname_;
133 bool deletions_enabled_;
134 mutable SequenceNumber sequence_number_;
135 }; // DummyDB
136
137 class TestEnv : public EnvWrapper {
138 public:
TestEnv(Env * t)139 explicit TestEnv(Env* t) : EnvWrapper(t) {}
140
141 class DummySequentialFile : public SequentialFile {
142 public:
DummySequentialFile(bool fail_reads)143 explicit DummySequentialFile(bool fail_reads)
144 : SequentialFile(), rnd_(5), fail_reads_(fail_reads) {}
Read(size_t n,Slice * result,char * scratch)145 Status Read(size_t n, Slice* result, char* scratch) override {
146 if (fail_reads_) {
147 return Status::IOError();
148 }
149 size_t read_size = (n > size_left) ? size_left : n;
150 for (size_t i = 0; i < read_size; ++i) {
151 scratch[i] = rnd_.Next() & 255;
152 }
153 *result = Slice(scratch, read_size);
154 size_left -= read_size;
155 return Status::OK();
156 }
157
Skip(uint64_t n)158 Status Skip(uint64_t n) override {
159 size_left = (n > size_left) ? size_left - n : 0;
160 return Status::OK();
161 }
162
163 private:
164 size_t size_left = 200;
165 Random rnd_;
166 bool fail_reads_;
167 };
168
NewSequentialFile(const std::string & f,std::unique_ptr<SequentialFile> * r,const EnvOptions & options)169 Status NewSequentialFile(const std::string& f,
170 std::unique_ptr<SequentialFile>* r,
171 const EnvOptions& options) override {
172 MutexLock l(&mutex_);
173 if (dummy_sequential_file_) {
174 r->reset(
175 new TestEnv::DummySequentialFile(dummy_sequential_file_fail_reads_));
176 return Status::OK();
177 } else {
178 Status s = EnvWrapper::NewSequentialFile(f, r, options);
179 if (s.ok()) {
180 if ((*r)->use_direct_io()) {
181 ++num_direct_seq_readers_;
182 }
183 ++num_seq_readers_;
184 }
185 return s;
186 }
187 }
188
NewWritableFile(const std::string & f,std::unique_ptr<WritableFile> * r,const EnvOptions & options)189 Status NewWritableFile(const std::string& f, std::unique_ptr<WritableFile>* r,
190 const EnvOptions& options) override {
191 MutexLock l(&mutex_);
192 written_files_.push_back(f);
193 if (limit_written_files_ <= 0) {
194 return Status::NotSupported("Sorry, can't do this");
195 }
196 limit_written_files_--;
197 Status s = EnvWrapper::NewWritableFile(f, r, options);
198 if (s.ok()) {
199 if ((*r)->use_direct_io()) {
200 ++num_direct_writers_;
201 }
202 ++num_writers_;
203 }
204 return s;
205 }
206
NewRandomAccessFile(const std::string & fname,std::unique_ptr<RandomAccessFile> * result,const EnvOptions & options)207 Status NewRandomAccessFile(const std::string& fname,
208 std::unique_ptr<RandomAccessFile>* result,
209 const EnvOptions& options) override {
210 MutexLock l(&mutex_);
211 Status s = EnvWrapper::NewRandomAccessFile(fname, result, options);
212 if (s.ok()) {
213 if ((*result)->use_direct_io()) {
214 ++num_direct_rand_readers_;
215 }
216 ++num_rand_readers_;
217 }
218 return s;
219 }
220
DeleteFile(const std::string & fname)221 Status DeleteFile(const std::string& fname) override {
222 MutexLock l(&mutex_);
223 if (fail_delete_files_) {
224 return Status::IOError();
225 }
226 EXPECT_GT(limit_delete_files_, 0U);
227 limit_delete_files_--;
228 return EnvWrapper::DeleteFile(fname);
229 }
230
DeleteDir(const std::string & dirname)231 Status DeleteDir(const std::string& dirname) override {
232 MutexLock l(&mutex_);
233 if (fail_delete_files_) {
234 return Status::IOError();
235 }
236 return EnvWrapper::DeleteDir(dirname);
237 }
238
AssertWrittenFiles(std::vector<std::string> & should_have_written)239 void AssertWrittenFiles(std::vector<std::string>& should_have_written) {
240 MutexLock l(&mutex_);
241 std::sort(should_have_written.begin(), should_have_written.end());
242 std::sort(written_files_.begin(), written_files_.end());
243
244 ASSERT_EQ(should_have_written, written_files_);
245 }
246
ClearWrittenFiles()247 void ClearWrittenFiles() {
248 MutexLock l(&mutex_);
249 written_files_.clear();
250 }
251
SetLimitWrittenFiles(uint64_t limit)252 void SetLimitWrittenFiles(uint64_t limit) {
253 MutexLock l(&mutex_);
254 limit_written_files_ = limit;
255 }
256
SetLimitDeleteFiles(uint64_t limit)257 void SetLimitDeleteFiles(uint64_t limit) {
258 MutexLock l(&mutex_);
259 limit_delete_files_ = limit;
260 }
261
SetDeleteFileFailure(bool fail)262 void SetDeleteFileFailure(bool fail) {
263 MutexLock l(&mutex_);
264 fail_delete_files_ = fail;
265 }
266
SetDummySequentialFile(bool dummy_sequential_file)267 void SetDummySequentialFile(bool dummy_sequential_file) {
268 MutexLock l(&mutex_);
269 dummy_sequential_file_ = dummy_sequential_file;
270 }
SetDummySequentialFileFailReads(bool dummy_sequential_file_fail_reads)271 void SetDummySequentialFileFailReads(bool dummy_sequential_file_fail_reads) {
272 MutexLock l(&mutex_);
273 dummy_sequential_file_fail_reads_ = dummy_sequential_file_fail_reads;
274 }
275
SetGetChildrenFailure(bool fail)276 void SetGetChildrenFailure(bool fail) { get_children_failure_ = fail; }
GetChildren(const std::string & dir,std::vector<std::string> * r)277 Status GetChildren(const std::string& dir,
278 std::vector<std::string>* r) override {
279 if (get_children_failure_) {
280 return Status::IOError("SimulatedFailure");
281 }
282 return EnvWrapper::GetChildren(dir, r);
283 }
284
285 // Some test cases do not actually create the test files (e.g., see
286 // DummyDB::live_files_) - for those cases, we mock those files' attributes
287 // so CreateNewBackup() can get their attributes.
SetFilenamesForMockedAttrs(const std::vector<std::string> & filenames)288 void SetFilenamesForMockedAttrs(const std::vector<std::string>& filenames) {
289 filenames_for_mocked_attrs_ = filenames;
290 }
GetChildrenFileAttributes(const std::string & dir,std::vector<Env::FileAttributes> * r)291 Status GetChildrenFileAttributes(
292 const std::string& dir, std::vector<Env::FileAttributes>* r) override {
293 if (filenames_for_mocked_attrs_.size() > 0) {
294 for (const auto& filename : filenames_for_mocked_attrs_) {
295 r->push_back({dir + filename, 10 /* size_bytes */});
296 }
297 return Status::OK();
298 }
299 return EnvWrapper::GetChildrenFileAttributes(dir, r);
300 }
GetFileSize(const std::string & path,uint64_t * size_bytes)301 Status GetFileSize(const std::string& path, uint64_t* size_bytes) override {
302 if (filenames_for_mocked_attrs_.size() > 0) {
303 auto fname = path.substr(path.find_last_of('/'));
304 auto filename_iter = std::find(filenames_for_mocked_attrs_.begin(),
305 filenames_for_mocked_attrs_.end(), fname);
306 if (filename_iter != filenames_for_mocked_attrs_.end()) {
307 *size_bytes = 10;
308 return Status::OK();
309 }
310 return Status::NotFound(fname);
311 }
312 return EnvWrapper::GetFileSize(path, size_bytes);
313 }
314
SetCreateDirIfMissingFailure(bool fail)315 void SetCreateDirIfMissingFailure(bool fail) {
316 create_dir_if_missing_failure_ = fail;
317 }
CreateDirIfMissing(const std::string & d)318 Status CreateDirIfMissing(const std::string& d) override {
319 if (create_dir_if_missing_failure_) {
320 return Status::IOError("SimulatedFailure");
321 }
322 return EnvWrapper::CreateDirIfMissing(d);
323 }
324
SetNewDirectoryFailure(bool fail)325 void SetNewDirectoryFailure(bool fail) { new_directory_failure_ = fail; }
NewDirectory(const std::string & name,std::unique_ptr<Directory> * result)326 Status NewDirectory(const std::string& name,
327 std::unique_ptr<Directory>* result) override {
328 if (new_directory_failure_) {
329 return Status::IOError("SimulatedFailure");
330 }
331 return EnvWrapper::NewDirectory(name, result);
332 }
333
ClearFileOpenCounters()334 void ClearFileOpenCounters() {
335 MutexLock l(&mutex_);
336 num_rand_readers_ = 0;
337 num_direct_rand_readers_ = 0;
338 num_seq_readers_ = 0;
339 num_direct_seq_readers_ = 0;
340 num_writers_ = 0;
341 num_direct_writers_ = 0;
342 }
343
num_rand_readers()344 int num_rand_readers() { return num_rand_readers_; }
num_direct_rand_readers()345 int num_direct_rand_readers() { return num_direct_rand_readers_; }
num_seq_readers()346 int num_seq_readers() { return num_seq_readers_; }
num_direct_seq_readers()347 int num_direct_seq_readers() { return num_direct_seq_readers_; }
num_writers()348 int num_writers() { return num_writers_; }
num_direct_writers()349 int num_direct_writers() { return num_direct_writers_; }
350
351 private:
352 port::Mutex mutex_;
353 bool dummy_sequential_file_ = false;
354 bool dummy_sequential_file_fail_reads_ = false;
355 std::vector<std::string> written_files_;
356 std::vector<std::string> filenames_for_mocked_attrs_;
357 uint64_t limit_written_files_ = 1000000;
358 uint64_t limit_delete_files_ = 1000000;
359 bool fail_delete_files_ = false;
360
361 bool get_children_failure_ = false;
362 bool create_dir_if_missing_failure_ = false;
363 bool new_directory_failure_ = false;
364
365 // Keeps track of how many files of each type were successfully opened, and
366 // out of those, how many were opened with direct I/O.
367 std::atomic<int> num_rand_readers_;
368 std::atomic<int> num_direct_rand_readers_;
369 std::atomic<int> num_seq_readers_;
370 std::atomic<int> num_direct_seq_readers_;
371 std::atomic<int> num_writers_;
372 std::atomic<int> num_direct_writers_;
373 }; // TestEnv
374
375 class FileManager : public EnvWrapper {
376 public:
FileManager(Env * t)377 explicit FileManager(Env* t) : EnvWrapper(t), rnd_(5) {}
378
DeleteRandomFileInDir(const std::string & dir)379 Status DeleteRandomFileInDir(const std::string& dir) {
380 std::vector<std::string> children;
381 GetChildren(dir, &children);
382 if (children.size() <= 2) { // . and ..
383 return Status::NotFound("");
384 }
385 while (true) {
386 int i = rnd_.Next() % children.size();
387 if (children[i] != "." && children[i] != "..") {
388 return DeleteFile(dir + "/" + children[i]);
389 }
390 }
391 // should never get here
392 assert(false);
393 return Status::NotFound("");
394 }
395
AppendToRandomFileInDir(const std::string & dir,const std::string & data)396 Status AppendToRandomFileInDir(const std::string& dir,
397 const std::string& data) {
398 std::vector<std::string> children;
399 GetChildren(dir, &children);
400 if (children.size() <= 2) {
401 return Status::NotFound("");
402 }
403 while (true) {
404 int i = rnd_.Next() % children.size();
405 if (children[i] != "." && children[i] != "..") {
406 return WriteToFile(dir + "/" + children[i], data);
407 }
408 }
409 // should never get here
410 assert(false);
411 return Status::NotFound("");
412 }
413
CorruptFile(const std::string & fname,uint64_t bytes_to_corrupt)414 Status CorruptFile(const std::string& fname, uint64_t bytes_to_corrupt) {
415 std::string file_contents;
416 Status s = ReadFileToString(this, fname, &file_contents);
417 if (!s.ok()) {
418 return s;
419 }
420 s = DeleteFile(fname);
421 if (!s.ok()) {
422 return s;
423 }
424
425 for (uint64_t i = 0; i < bytes_to_corrupt; ++i) {
426 std::string tmp;
427 test::RandomString(&rnd_, 1, &tmp);
428 file_contents[rnd_.Next() % file_contents.size()] = tmp[0];
429 }
430 return WriteToFile(fname, file_contents);
431 }
432
CorruptChecksum(const std::string & fname,bool appear_valid)433 Status CorruptChecksum(const std::string& fname, bool appear_valid) {
434 std::string metadata;
435 Status s = ReadFileToString(this, fname, &metadata);
436 if (!s.ok()) {
437 return s;
438 }
439 s = DeleteFile(fname);
440 if (!s.ok()) {
441 return s;
442 }
443
444 auto pos = metadata.find("private");
445 if (pos == std::string::npos) {
446 return Status::Corruption("private file is expected");
447 }
448 pos = metadata.find(" crc32 ", pos + 6);
449 if (pos == std::string::npos) {
450 return Status::Corruption("checksum not found");
451 }
452
453 if (metadata.size() < pos + 7) {
454 return Status::Corruption("bad CRC32 checksum value");
455 }
456
457 if (appear_valid) {
458 if (metadata[pos + 8] == '\n') {
459 // single digit value, safe to insert one more digit
460 metadata.insert(pos + 8, 1, '0');
461 } else {
462 metadata.erase(pos + 8, 1);
463 }
464 } else {
465 metadata[pos + 7] = 'a';
466 }
467
468 return WriteToFile(fname, metadata);
469 }
470
WriteToFile(const std::string & fname,const std::string & data)471 Status WriteToFile(const std::string& fname, const std::string& data) {
472 std::unique_ptr<WritableFile> file;
473 EnvOptions env_options;
474 env_options.use_mmap_writes = false;
475 Status s = EnvWrapper::NewWritableFile(fname, &file, env_options);
476 if (!s.ok()) {
477 return s;
478 }
479 return file->Append(Slice(data));
480 }
481
482 private:
483 Random rnd_;
484 }; // FileManager
485
486 // utility functions
FillDB(DB * db,int from,int to)487 static size_t FillDB(DB* db, int from, int to) {
488 size_t bytes_written = 0;
489 for (int i = from; i < to; ++i) {
490 std::string key = "testkey" + ToString(i);
491 std::string value = "testvalue" + ToString(i);
492 bytes_written += key.size() + value.size();
493
494 EXPECT_OK(db->Put(WriteOptions(), Slice(key), Slice(value)));
495 }
496 return bytes_written;
497 }
498
AssertExists(DB * db,int from,int to)499 static void AssertExists(DB* db, int from, int to) {
500 for (int i = from; i < to; ++i) {
501 std::string key = "testkey" + ToString(i);
502 std::string value;
503 Status s = db->Get(ReadOptions(), Slice(key), &value);
504 ASSERT_EQ(value, "testvalue" + ToString(i));
505 }
506 }
507
AssertEmpty(DB * db,int from,int to)508 static void AssertEmpty(DB* db, int from, int to) {
509 for (int i = from; i < to; ++i) {
510 std::string key = "testkey" + ToString(i);
511 std::string value = "testvalue" + ToString(i);
512
513 Status s = db->Get(ReadOptions(), Slice(key), &value);
514 ASSERT_TRUE(s.IsNotFound());
515 }
516 }
517
518 class BackupableDBTest : public testing::Test {
519 public:
520 enum ShareOption {
521 kNoShare,
522 kShareNoChecksum,
523 kShareWithChecksum,
524 };
525
526 const std::vector<ShareOption> kAllShareOptions = {
527 kNoShare, kShareNoChecksum, kShareWithChecksum};
528
BackupableDBTest()529 BackupableDBTest() {
530 // set up files
531 std::string db_chroot = test::PerThreadDBPath("backupable_db");
532 std::string backup_chroot = test::PerThreadDBPath("backupable_db_backup");
533 Env::Default()->CreateDir(db_chroot);
534 Env::Default()->CreateDir(backup_chroot);
535 dbname_ = "/tempdb";
536 backupdir_ = "/tempbk";
537
538 // set up envs
539 db_chroot_env_.reset(NewChrootEnv(Env::Default(), db_chroot));
540 backup_chroot_env_.reset(NewChrootEnv(Env::Default(), backup_chroot));
541 test_db_env_.reset(new TestEnv(db_chroot_env_.get()));
542 test_backup_env_.reset(new TestEnv(backup_chroot_env_.get()));
543 file_manager_.reset(new FileManager(backup_chroot_env_.get()));
544
545 // set up db options
546 options_.create_if_missing = true;
547 options_.paranoid_checks = true;
548 options_.write_buffer_size = 1 << 17; // 128KB
549 options_.env = test_db_env_.get();
550 options_.wal_dir = dbname_;
551
552 // Create logger
553 DBOptions logger_options;
554 logger_options.env = db_chroot_env_.get();
555 CreateLoggerFromOptions(dbname_, logger_options, &logger_);
556
557 // set up backup db options
558 backupable_options_.reset(new BackupableDBOptions(
559 backupdir_, test_backup_env_.get(), true, logger_.get(), true));
560
561 // most tests will use multi-threaded backups
562 backupable_options_->max_background_operations = 7;
563
564 // delete old files in db
565 DestroyDB(dbname_, options_);
566 }
567
OpenDB()568 DB* OpenDB() {
569 DB* db;
570 EXPECT_OK(DB::Open(options_, dbname_, &db));
571 return db;
572 }
573
OpenDBAndBackupEngine(bool destroy_old_data=false,bool dummy=false,ShareOption shared_option=kShareNoChecksum)574 void OpenDBAndBackupEngine(bool destroy_old_data = false, bool dummy = false,
575 ShareOption shared_option = kShareNoChecksum) {
576 // reset all the defaults
577 test_backup_env_->SetLimitWrittenFiles(1000000);
578 test_db_env_->SetLimitWrittenFiles(1000000);
579 test_db_env_->SetDummySequentialFile(dummy);
580
581 DB* db;
582 if (dummy) {
583 dummy_db_ = new DummyDB(options_, dbname_);
584 db = dummy_db_;
585 } else {
586 ASSERT_OK(DB::Open(options_, dbname_, &db));
587 }
588 db_.reset(db);
589 backupable_options_->destroy_old_data = destroy_old_data;
590 backupable_options_->share_table_files = shared_option != kNoShare;
591 backupable_options_->share_files_with_checksum =
592 shared_option == kShareWithChecksum;
593 BackupEngine* backup_engine;
594 ASSERT_OK(BackupEngine::Open(test_db_env_.get(), *backupable_options_,
595 &backup_engine));
596 backup_engine_.reset(backup_engine);
597 }
598
CloseDBAndBackupEngine()599 void CloseDBAndBackupEngine() {
600 db_.reset();
601 backup_engine_.reset();
602 }
603
OpenBackupEngine()604 void OpenBackupEngine() {
605 backupable_options_->destroy_old_data = false;
606 BackupEngine* backup_engine;
607 ASSERT_OK(BackupEngine::Open(test_db_env_.get(), *backupable_options_,
608 &backup_engine));
609 backup_engine_.reset(backup_engine);
610 }
611
CloseBackupEngine()612 void CloseBackupEngine() { backup_engine_.reset(nullptr); }
613
614 // restores backup backup_id and asserts the existence of
615 // [start_exist, end_exist> and not-existence of
616 // [end_exist, end>
617 //
618 // if backup_id == 0, it means restore from latest
619 // if end == 0, don't check AssertEmpty
AssertBackupConsistency(BackupID backup_id,uint32_t start_exist,uint32_t end_exist,uint32_t end=0,bool keep_log_files=false)620 void AssertBackupConsistency(BackupID backup_id, uint32_t start_exist,
621 uint32_t end_exist, uint32_t end = 0,
622 bool keep_log_files = false) {
623 RestoreOptions restore_options(keep_log_files);
624 bool opened_backup_engine = false;
625 if (backup_engine_.get() == nullptr) {
626 opened_backup_engine = true;
627 OpenBackupEngine();
628 }
629 if (backup_id > 0) {
630 ASSERT_OK(backup_engine_->RestoreDBFromBackup(backup_id, dbname_, dbname_,
631 restore_options));
632 } else {
633 ASSERT_OK(backup_engine_->RestoreDBFromLatestBackup(dbname_, dbname_,
634 restore_options));
635 }
636 DB* db = OpenDB();
637 AssertExists(db, start_exist, end_exist);
638 if (end != 0) {
639 AssertEmpty(db, end_exist, end);
640 }
641 delete db;
642 if (opened_backup_engine) {
643 CloseBackupEngine();
644 }
645 }
646
DeleteLogFiles()647 void DeleteLogFiles() {
648 std::vector<std::string> delete_logs;
649 db_chroot_env_->GetChildren(dbname_, &delete_logs);
650 for (auto f : delete_logs) {
651 uint64_t number;
652 FileType type;
653 bool ok = ParseFileName(f, &number, &type);
654 if (ok && type == kLogFile) {
655 db_chroot_env_->DeleteFile(dbname_ + "/" + f);
656 }
657 }
658 }
659
660 // files
661 std::string dbname_;
662 std::string backupdir_;
663
664 // logger_ must be above backup_engine_ such that the engine's destructor,
665 // which uses a raw pointer to the logger, executes first.
666 std::shared_ptr<Logger> logger_;
667
668 // envs
669 std::unique_ptr<Env> db_chroot_env_;
670 std::unique_ptr<Env> backup_chroot_env_;
671 std::unique_ptr<TestEnv> test_db_env_;
672 std::unique_ptr<TestEnv> test_backup_env_;
673 std::unique_ptr<FileManager> file_manager_;
674
675 // all the dbs!
676 DummyDB* dummy_db_; // BackupableDB owns dummy_db_
677 std::unique_ptr<DB> db_;
678 std::unique_ptr<BackupEngine> backup_engine_;
679
680 // options
681 Options options_;
682
683 protected:
684 std::unique_ptr<BackupableDBOptions> backupable_options_;
685 }; // BackupableDBTest
686
AppendPath(const std::string & path,std::vector<std::string> & v)687 void AppendPath(const std::string& path, std::vector<std::string>& v) {
688 for (auto& f : v) {
689 f = path + f;
690 }
691 }
692
693 class BackupableDBTestWithParam : public BackupableDBTest,
694 public testing::WithParamInterface<bool> {
695 public:
BackupableDBTestWithParam()696 BackupableDBTestWithParam() {
697 backupable_options_->share_files_with_checksum = GetParam();
698 }
699 };
700
701 // This test verifies that the verifyBackup method correctly identifies
702 // invalid backups
TEST_P(BackupableDBTestWithParam,VerifyBackup)703 TEST_P(BackupableDBTestWithParam, VerifyBackup) {
704 const int keys_iteration = 5000;
705 Random rnd(6);
706 Status s;
707 OpenDBAndBackupEngine(true);
708 // create five backups
709 for (int i = 0; i < 5; ++i) {
710 FillDB(db_.get(), keys_iteration * i, keys_iteration * (i + 1));
711 ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), true));
712 }
713 CloseDBAndBackupEngine();
714
715 OpenDBAndBackupEngine();
716 // ---------- case 1. - valid backup -----------
717 ASSERT_TRUE(backup_engine_->VerifyBackup(1).ok());
718
719 // ---------- case 2. - delete a file -----------i
720 file_manager_->DeleteRandomFileInDir(backupdir_ + "/private/1");
721 ASSERT_TRUE(backup_engine_->VerifyBackup(1).IsNotFound());
722
723 // ---------- case 3. - corrupt a file -----------
724 std::string append_data = "Corrupting a random file";
725 file_manager_->AppendToRandomFileInDir(backupdir_ + "/private/2",
726 append_data);
727 ASSERT_TRUE(backup_engine_->VerifyBackup(2).IsCorruption());
728
729 // ---------- case 4. - invalid backup -----------
730 ASSERT_TRUE(backup_engine_->VerifyBackup(6).IsNotFound());
731 CloseDBAndBackupEngine();
732 }
733
734 // open DB, write, close DB, backup, restore, repeat
TEST_P(BackupableDBTestWithParam,OfflineIntegrationTest)735 TEST_P(BackupableDBTestWithParam, OfflineIntegrationTest) {
736 // has to be a big number, so that it triggers the memtable flush
737 const int keys_iteration = 5000;
738 const int max_key = keys_iteration * 4 + 10;
739 // first iter -- flush before backup
740 // second iter -- don't flush before backup
741 for (int iter = 0; iter < 2; ++iter) {
742 // delete old data
743 DestroyDB(dbname_, options_);
744 bool destroy_data = true;
745
746 // every iteration --
747 // 1. insert new data in the DB
748 // 2. backup the DB
749 // 3. destroy the db
750 // 4. restore the db, check everything is still there
751 for (int i = 0; i < 5; ++i) {
752 // in last iteration, put smaller amount of data,
753 int fill_up_to = std::min(keys_iteration * (i + 1), max_key);
754 // ---- insert new data and back up ----
755 OpenDBAndBackupEngine(destroy_data);
756 destroy_data = false;
757 FillDB(db_.get(), keys_iteration * i, fill_up_to);
758 ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), iter == 0));
759 CloseDBAndBackupEngine();
760 DestroyDB(dbname_, options_);
761
762 // ---- make sure it's empty ----
763 DB* db = OpenDB();
764 AssertEmpty(db, 0, fill_up_to);
765 delete db;
766
767 // ---- restore the DB ----
768 OpenBackupEngine();
769 if (i >= 3) { // test purge old backups
770 // when i == 4, purge to only 1 backup
771 // when i == 3, purge to 2 backups
772 ASSERT_OK(backup_engine_->PurgeOldBackups(5 - i));
773 }
774 // ---- make sure the data is there ---
775 AssertBackupConsistency(0, 0, fill_up_to, max_key);
776 CloseBackupEngine();
777 }
778 }
779 }
780
781 // open DB, write, backup, write, backup, close, restore
TEST_P(BackupableDBTestWithParam,OnlineIntegrationTest)782 TEST_P(BackupableDBTestWithParam, OnlineIntegrationTest) {
783 // has to be a big number, so that it triggers the memtable flush
784 const int keys_iteration = 5000;
785 const int max_key = keys_iteration * 4 + 10;
786 Random rnd(7);
787 // delete old data
788 DestroyDB(dbname_, options_);
789
790 OpenDBAndBackupEngine(true);
791 // write some data, backup, repeat
792 for (int i = 0; i < 5; ++i) {
793 if (i == 4) {
794 // delete backup number 2, online delete!
795 ASSERT_OK(backup_engine_->DeleteBackup(2));
796 }
797 // in last iteration, put smaller amount of data,
798 // so that backups can share sst files
799 int fill_up_to = std::min(keys_iteration * (i + 1), max_key);
800 FillDB(db_.get(), keys_iteration * i, fill_up_to);
801 // we should get consistent results with flush_before_backup
802 // set to both true and false
803 ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), !!(rnd.Next() % 2)));
804 }
805 // close and destroy
806 CloseDBAndBackupEngine();
807 DestroyDB(dbname_, options_);
808
809 // ---- make sure it's empty ----
810 DB* db = OpenDB();
811 AssertEmpty(db, 0, max_key);
812 delete db;
813
814 // ---- restore every backup and verify all the data is there ----
815 OpenBackupEngine();
816 for (int i = 1; i <= 5; ++i) {
817 if (i == 2) {
818 // we deleted backup 2
819 Status s = backup_engine_->RestoreDBFromBackup(2, dbname_, dbname_);
820 ASSERT_TRUE(!s.ok());
821 } else {
822 int fill_up_to = std::min(keys_iteration * i, max_key);
823 AssertBackupConsistency(i, 0, fill_up_to, max_key);
824 }
825 }
826
827 // delete some backups -- this should leave only backups 3 and 5 alive
828 ASSERT_OK(backup_engine_->DeleteBackup(4));
829 ASSERT_OK(backup_engine_->PurgeOldBackups(2));
830
831 std::vector<BackupInfo> backup_info;
832 backup_engine_->GetBackupInfo(&backup_info);
833 ASSERT_EQ(2UL, backup_info.size());
834
835 // check backup 3
836 AssertBackupConsistency(3, 0, 3 * keys_iteration, max_key);
837 // check backup 5
838 AssertBackupConsistency(5, 0, max_key);
839
840 CloseBackupEngine();
841 }
842
843 INSTANTIATE_TEST_CASE_P(BackupableDBTestWithParam, BackupableDBTestWithParam,
844 ::testing::Bool());
845
846 // this will make sure that backup does not copy the same file twice
TEST_F(BackupableDBTest,NoDoubleCopy_And_AutoGC)847 TEST_F(BackupableDBTest, NoDoubleCopy_And_AutoGC) {
848 OpenDBAndBackupEngine(true, true);
849
850 // should write 5 DB files + one meta file
851 test_backup_env_->SetLimitWrittenFiles(7);
852 test_backup_env_->ClearWrittenFiles();
853 test_db_env_->SetLimitWrittenFiles(0);
854 dummy_db_->live_files_ = {"/00010.sst", "/00011.sst", "/CURRENT",
855 "/MANIFEST-01"};
856 dummy_db_->wal_files_ = {{"/00011.log", true}, {"/00012.log", false}};
857 test_db_env_->SetFilenamesForMockedAttrs(dummy_db_->live_files_);
858 ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), false));
859 std::vector<std::string> should_have_written = {
860 "/shared/.00010.sst.tmp", "/shared/.00011.sst.tmp", "/private/1/CURRENT",
861 "/private/1/MANIFEST-01", "/private/1/00011.log", "/meta/.1.tmp"};
862 AppendPath(backupdir_, should_have_written);
863 test_backup_env_->AssertWrittenFiles(should_have_written);
864
865 char db_number = '1';
866
867 for (std::string other_sst : {"00015.sst", "00017.sst", "00019.sst"}) {
868 // should write 4 new DB files + one meta file
869 // should not write/copy 00010.sst, since it's already there!
870 test_backup_env_->SetLimitWrittenFiles(6);
871 test_backup_env_->ClearWrittenFiles();
872
873 dummy_db_->live_files_ = {"/00010.sst", "/" + other_sst, "/CURRENT",
874 "/MANIFEST-01"};
875 dummy_db_->wal_files_ = {{"/00011.log", true}, {"/00012.log", false}};
876 test_db_env_->SetFilenamesForMockedAttrs(dummy_db_->live_files_);
877 ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), false));
878 // should not open 00010.sst - it's already there
879
880 ++db_number;
881 std::string private_dir = std::string("/private/") + db_number;
882 should_have_written = {
883 "/shared/." + other_sst + ".tmp", private_dir + "/CURRENT",
884 private_dir + "/MANIFEST-01", private_dir + "/00011.log",
885 std::string("/meta/.") + db_number + ".tmp"};
886 AppendPath(backupdir_, should_have_written);
887 test_backup_env_->AssertWrittenFiles(should_have_written);
888 }
889
890 ASSERT_OK(backup_engine_->DeleteBackup(1));
891 ASSERT_OK(test_backup_env_->FileExists(backupdir_ + "/shared/00010.sst"));
892
893 // 00011.sst was only in backup 1, should be deleted
894 ASSERT_EQ(Status::NotFound(),
895 test_backup_env_->FileExists(backupdir_ + "/shared/00011.sst"));
896 ASSERT_OK(test_backup_env_->FileExists(backupdir_ + "/shared/00015.sst"));
897
898 // MANIFEST file size should be only 100
899 uint64_t size = 0;
900 test_backup_env_->GetFileSize(backupdir_ + "/private/2/MANIFEST-01", &size);
901 ASSERT_EQ(100UL, size);
902 test_backup_env_->GetFileSize(backupdir_ + "/shared/00015.sst", &size);
903 ASSERT_EQ(200UL, size);
904
905 CloseBackupEngine();
906
907 //
908 // Now simulate incomplete delete by removing just meta
909 //
910 ASSERT_OK(test_backup_env_->DeleteFile(backupdir_ + "/meta/2"));
911
912 OpenBackupEngine();
913
914 // 1 appears to be removed, so
915 // 2 non-corrupt and 0 corrupt seen
916 std::vector<BackupInfo> backup_info;
917 std::vector<BackupID> corrupt_backup_ids;
918 backup_engine_->GetBackupInfo(&backup_info);
919 backup_engine_->GetCorruptedBackups(&corrupt_backup_ids);
920 ASSERT_EQ(2UL, backup_info.size());
921 ASSERT_EQ(0UL, corrupt_backup_ids.size());
922
923 // Keep the two we see, but this should suffice to purge unreferenced
924 // shared files from incomplete delete.
925 ASSERT_OK(backup_engine_->PurgeOldBackups(2));
926
927 // Make sure dangling sst file has been removed (somewhere along this
928 // process). GarbageCollect should not be needed.
929 ASSERT_EQ(Status::NotFound(),
930 test_backup_env_->FileExists(backupdir_ + "/shared/00015.sst"));
931 ASSERT_OK(test_backup_env_->FileExists(backupdir_ + "/shared/00017.sst"));
932 ASSERT_OK(test_backup_env_->FileExists(backupdir_ + "/shared/00019.sst"));
933
934 // Now actually purge a good one
935 ASSERT_OK(backup_engine_->PurgeOldBackups(1));
936
937 ASSERT_EQ(Status::NotFound(),
938 test_backup_env_->FileExists(backupdir_ + "/shared/00017.sst"));
939 ASSERT_OK(test_backup_env_->FileExists(backupdir_ + "/shared/00019.sst"));
940
941 CloseDBAndBackupEngine();
942 }
943
944 // test various kind of corruptions that may happen:
945 // 1. Not able to write a file for backup - that backup should fail,
946 // everything else should work
947 // 2. Corrupted backup meta file or missing backuped file - we should
948 // not be able to open that backup, but all other backups should be
949 // fine
950 // 3. Corrupted checksum value - if the checksum is not a valid uint32_t,
951 // db open should fail, otherwise, it aborts during the restore process.
TEST_F(BackupableDBTest,CorruptionsTest)952 TEST_F(BackupableDBTest, CorruptionsTest) {
953 const int keys_iteration = 5000;
954 Random rnd(6);
955 Status s;
956
957 OpenDBAndBackupEngine(true);
958 // create five backups
959 for (int i = 0; i < 5; ++i) {
960 FillDB(db_.get(), keys_iteration * i, keys_iteration * (i + 1));
961 ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), !!(rnd.Next() % 2)));
962 }
963
964 // ---------- case 1. - fail a write -----------
965 // try creating backup 6, but fail a write
966 FillDB(db_.get(), keys_iteration * 5, keys_iteration * 6);
967 test_backup_env_->SetLimitWrittenFiles(2);
968 // should fail
969 s = backup_engine_->CreateNewBackup(db_.get(), !!(rnd.Next() % 2));
970 ASSERT_TRUE(!s.ok());
971 test_backup_env_->SetLimitWrittenFiles(1000000);
972 // latest backup should have all the keys
973 CloseDBAndBackupEngine();
974 AssertBackupConsistency(0, 0, keys_iteration * 5, keys_iteration * 6);
975
976 // --------- case 2. corrupted backup meta or missing backuped file ----
977 ASSERT_OK(file_manager_->CorruptFile(backupdir_ + "/meta/5", 3));
978 // since 5 meta is now corrupted, latest backup should be 4
979 AssertBackupConsistency(0, 0, keys_iteration * 4, keys_iteration * 5);
980 OpenBackupEngine();
981 s = backup_engine_->RestoreDBFromBackup(5, dbname_, dbname_);
982 ASSERT_TRUE(!s.ok());
983 CloseBackupEngine();
984 ASSERT_OK(file_manager_->DeleteRandomFileInDir(backupdir_ + "/private/4"));
985 // 4 is corrupted, 3 is the latest backup now
986 AssertBackupConsistency(0, 0, keys_iteration * 3, keys_iteration * 5);
987 OpenBackupEngine();
988 s = backup_engine_->RestoreDBFromBackup(4, dbname_, dbname_);
989 CloseBackupEngine();
990 ASSERT_TRUE(!s.ok());
991
992 // --------- case 3. corrupted checksum value ----
993 ASSERT_OK(file_manager_->CorruptChecksum(backupdir_ + "/meta/3", false));
994 // checksum of backup 3 is an invalid value, this can be detected at
995 // db open time, and it reverts to the previous backup automatically
996 AssertBackupConsistency(0, 0, keys_iteration * 2, keys_iteration * 5);
997 // checksum of the backup 2 appears to be valid, this can cause checksum
998 // mismatch and abort restore process
999 ASSERT_OK(file_manager_->CorruptChecksum(backupdir_ + "/meta/2", true));
1000 ASSERT_OK(file_manager_->FileExists(backupdir_ + "/meta/2"));
1001 OpenBackupEngine();
1002 ASSERT_OK(file_manager_->FileExists(backupdir_ + "/meta/2"));
1003 s = backup_engine_->RestoreDBFromBackup(2, dbname_, dbname_);
1004 ASSERT_TRUE(!s.ok());
1005
1006 // make sure that no corrupt backups have actually been deleted!
1007 ASSERT_OK(file_manager_->FileExists(backupdir_ + "/meta/1"));
1008 ASSERT_OK(file_manager_->FileExists(backupdir_ + "/meta/2"));
1009 ASSERT_OK(file_manager_->FileExists(backupdir_ + "/meta/3"));
1010 ASSERT_OK(file_manager_->FileExists(backupdir_ + "/meta/4"));
1011 ASSERT_OK(file_manager_->FileExists(backupdir_ + "/meta/5"));
1012 ASSERT_OK(file_manager_->FileExists(backupdir_ + "/private/1"));
1013 ASSERT_OK(file_manager_->FileExists(backupdir_ + "/private/2"));
1014 ASSERT_OK(file_manager_->FileExists(backupdir_ + "/private/3"));
1015 ASSERT_OK(file_manager_->FileExists(backupdir_ + "/private/4"));
1016 ASSERT_OK(file_manager_->FileExists(backupdir_ + "/private/5"));
1017
1018 // delete the corrupt backups and then make sure they're actually deleted
1019 ASSERT_OK(backup_engine_->DeleteBackup(5));
1020 ASSERT_OK(backup_engine_->DeleteBackup(4));
1021 ASSERT_OK(backup_engine_->DeleteBackup(3));
1022 ASSERT_OK(backup_engine_->DeleteBackup(2));
1023 // Should not be needed anymore with auto-GC on DeleteBackup
1024 //(void)backup_engine_->GarbageCollect();
1025 ASSERT_EQ(Status::NotFound(),
1026 file_manager_->FileExists(backupdir_ + "/meta/5"));
1027 ASSERT_EQ(Status::NotFound(),
1028 file_manager_->FileExists(backupdir_ + "/private/5"));
1029 ASSERT_EQ(Status::NotFound(),
1030 file_manager_->FileExists(backupdir_ + "/meta/4"));
1031 ASSERT_EQ(Status::NotFound(),
1032 file_manager_->FileExists(backupdir_ + "/private/4"));
1033 ASSERT_EQ(Status::NotFound(),
1034 file_manager_->FileExists(backupdir_ + "/meta/3"));
1035 ASSERT_EQ(Status::NotFound(),
1036 file_manager_->FileExists(backupdir_ + "/private/3"));
1037 ASSERT_EQ(Status::NotFound(),
1038 file_manager_->FileExists(backupdir_ + "/meta/2"));
1039 ASSERT_EQ(Status::NotFound(),
1040 file_manager_->FileExists(backupdir_ + "/private/2"));
1041
1042 CloseBackupEngine();
1043 AssertBackupConsistency(0, 0, keys_iteration * 1, keys_iteration * 5);
1044
1045 // new backup should be 2!
1046 OpenDBAndBackupEngine();
1047 FillDB(db_.get(), keys_iteration * 1, keys_iteration * 2);
1048 ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), !!(rnd.Next() % 2)));
1049 CloseDBAndBackupEngine();
1050 AssertBackupConsistency(2, 0, keys_iteration * 2, keys_iteration * 5);
1051 }
1052
TEST_F(BackupableDBTest,InterruptCreationTest)1053 TEST_F(BackupableDBTest, InterruptCreationTest) {
1054 // Interrupt backup creation by failing new writes and failing cleanup of the
1055 // partial state. Then verify a subsequent backup can still succeed.
1056 const int keys_iteration = 5000;
1057 Random rnd(6);
1058
1059 OpenDBAndBackupEngine(true /* destroy_old_data */);
1060 FillDB(db_.get(), 0, keys_iteration);
1061 test_backup_env_->SetLimitWrittenFiles(2);
1062 test_backup_env_->SetDeleteFileFailure(true);
1063 // should fail creation
1064 ASSERT_FALSE(
1065 backup_engine_->CreateNewBackup(db_.get(), !!(rnd.Next() % 2)).ok());
1066 CloseDBAndBackupEngine();
1067 // should also fail cleanup so the tmp directory stays behind
1068 ASSERT_OK(backup_chroot_env_->FileExists(backupdir_ + "/private/1/"));
1069
1070 OpenDBAndBackupEngine(false /* destroy_old_data */);
1071 test_backup_env_->SetLimitWrittenFiles(1000000);
1072 test_backup_env_->SetDeleteFileFailure(false);
1073 ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), !!(rnd.Next() % 2)));
1074 // latest backup should have all the keys
1075 CloseDBAndBackupEngine();
1076 AssertBackupConsistency(0, 0, keys_iteration);
1077 }
1078
OptionsPath(std::string ret,int backupID)1079 inline std::string OptionsPath(std::string ret, int backupID) {
1080 ret += "/private/";
1081 ret += std::to_string(backupID);
1082 ret += "/";
1083 return ret;
1084 }
1085
1086 // Backup the LATEST options file to
1087 // "<backup_dir>/private/<backup_id>/OPTIONS<number>"
1088
TEST_F(BackupableDBTest,BackupOptions)1089 TEST_F(BackupableDBTest, BackupOptions) {
1090 OpenDBAndBackupEngine(true);
1091 for (int i = 1; i < 5; i++) {
1092 std::string name;
1093 std::vector<std::string> filenames;
1094 // Must reset() before reset(OpenDB()) again.
1095 // Calling OpenDB() while *db_ is existing will cause LOCK issue
1096 db_.reset();
1097 db_.reset(OpenDB());
1098 ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), true));
1099 ROCKSDB_NAMESPACE::GetLatestOptionsFileName(db_->GetName(), options_.env,
1100 &name);
1101 ASSERT_OK(file_manager_->FileExists(OptionsPath(backupdir_, i) + name));
1102 backup_chroot_env_->GetChildren(OptionsPath(backupdir_, i), &filenames);
1103 for (auto fn : filenames) {
1104 if (fn.compare(0, 7, "OPTIONS") == 0) {
1105 ASSERT_EQ(name, fn);
1106 }
1107 }
1108 }
1109
1110 CloseDBAndBackupEngine();
1111 }
1112
TEST_F(BackupableDBTest,SetOptionsBackupRaceCondition)1113 TEST_F(BackupableDBTest, SetOptionsBackupRaceCondition) {
1114 OpenDBAndBackupEngine(true);
1115 SyncPoint::GetInstance()->LoadDependency(
1116 {{"CheckpointImpl::CreateCheckpoint:SavedLiveFiles1",
1117 "BackupableDBTest::SetOptionsBackupRaceCondition:BeforeSetOptions"},
1118 {"BackupableDBTest::SetOptionsBackupRaceCondition:AfterSetOptions",
1119 "CheckpointImpl::CreateCheckpoint:SavedLiveFiles2"}});
1120 SyncPoint::GetInstance()->EnableProcessing();
1121 ROCKSDB_NAMESPACE::port::Thread setoptions_thread{[this]() {
1122 TEST_SYNC_POINT(
1123 "BackupableDBTest::SetOptionsBackupRaceCondition:BeforeSetOptions");
1124 DBImpl* dbi = static_cast<DBImpl*>(db_.get());
1125 // Change arbitrary option to trigger OPTIONS file deletion
1126 ASSERT_OK(dbi->SetOptions(dbi->DefaultColumnFamily(),
1127 {{"paranoid_file_checks", "false"}}));
1128 ASSERT_OK(dbi->SetOptions(dbi->DefaultColumnFamily(),
1129 {{"paranoid_file_checks", "true"}}));
1130 ASSERT_OK(dbi->SetOptions(dbi->DefaultColumnFamily(),
1131 {{"paranoid_file_checks", "false"}}));
1132 TEST_SYNC_POINT(
1133 "BackupableDBTest::SetOptionsBackupRaceCondition:AfterSetOptions");
1134 }};
1135 ASSERT_OK(backup_engine_->CreateNewBackup(db_.get()));
1136 setoptions_thread.join();
1137 CloseDBAndBackupEngine();
1138 }
1139
1140 // This test verifies we don't delete the latest backup when read-only option is
1141 // set
TEST_F(BackupableDBTest,NoDeleteWithReadOnly)1142 TEST_F(BackupableDBTest, NoDeleteWithReadOnly) {
1143 const int keys_iteration = 5000;
1144 Random rnd(6);
1145 Status s;
1146
1147 OpenDBAndBackupEngine(true);
1148 // create five backups
1149 for (int i = 0; i < 5; ++i) {
1150 FillDB(db_.get(), keys_iteration * i, keys_iteration * (i + 1));
1151 ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), !!(rnd.Next() % 2)));
1152 }
1153 CloseDBAndBackupEngine();
1154 ASSERT_OK(file_manager_->WriteToFile(backupdir_ + "/LATEST_BACKUP", "4"));
1155
1156 backupable_options_->destroy_old_data = false;
1157 BackupEngineReadOnly* read_only_backup_engine;
1158 ASSERT_OK(BackupEngineReadOnly::Open(backup_chroot_env_.get(),
1159 *backupable_options_,
1160 &read_only_backup_engine));
1161
1162 // assert that data from backup 5 is still here (even though LATEST_BACKUP
1163 // says 4 is latest)
1164 ASSERT_OK(file_manager_->FileExists(backupdir_ + "/meta/5"));
1165 ASSERT_OK(file_manager_->FileExists(backupdir_ + "/private/5"));
1166
1167 // Behavior change: We now ignore LATEST_BACKUP contents. This means that
1168 // we should have 5 backups, even if LATEST_BACKUP says 4.
1169 std::vector<BackupInfo> backup_info;
1170 read_only_backup_engine->GetBackupInfo(&backup_info);
1171 ASSERT_EQ(5UL, backup_info.size());
1172 delete read_only_backup_engine;
1173 }
1174
TEST_F(BackupableDBTest,FailOverwritingBackups)1175 TEST_F(BackupableDBTest, FailOverwritingBackups) {
1176 options_.write_buffer_size = 1024 * 1024 * 1024; // 1GB
1177 options_.disable_auto_compactions = true;
1178
1179 // create backups 1, 2, 3, 4, 5
1180 OpenDBAndBackupEngine(true);
1181 for (int i = 0; i < 5; ++i) {
1182 CloseDBAndBackupEngine();
1183 DeleteLogFiles();
1184 OpenDBAndBackupEngine(false);
1185 FillDB(db_.get(), 100 * i, 100 * (i + 1));
1186 ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), true));
1187 }
1188 CloseDBAndBackupEngine();
1189
1190 // restore 3
1191 OpenBackupEngine();
1192 ASSERT_OK(backup_engine_->RestoreDBFromBackup(3, dbname_, dbname_));
1193 CloseBackupEngine();
1194
1195 OpenDBAndBackupEngine(false);
1196 FillDB(db_.get(), 0, 300);
1197 Status s = backup_engine_->CreateNewBackup(db_.get(), true);
1198 // the new backup fails because new table files
1199 // clash with old table files from backups 4 and 5
1200 // (since write_buffer_size is huge, we can be sure that
1201 // each backup will generate only one sst file and that
1202 // a file generated by a new backup is the same as
1203 // sst file generated by backup 4)
1204 ASSERT_TRUE(s.IsCorruption());
1205 ASSERT_OK(backup_engine_->DeleteBackup(4));
1206 ASSERT_OK(backup_engine_->DeleteBackup(5));
1207 // now, the backup can succeed
1208 ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), true));
1209 CloseDBAndBackupEngine();
1210 }
1211
TEST_F(BackupableDBTest,NoShareTableFiles)1212 TEST_F(BackupableDBTest, NoShareTableFiles) {
1213 const int keys_iteration = 5000;
1214 OpenDBAndBackupEngine(true, false, kNoShare);
1215 for (int i = 0; i < 5; ++i) {
1216 FillDB(db_.get(), keys_iteration * i, keys_iteration * (i + 1));
1217 ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), !!(i % 2)));
1218 }
1219 CloseDBAndBackupEngine();
1220
1221 for (int i = 0; i < 5; ++i) {
1222 AssertBackupConsistency(i + 1, 0, keys_iteration * (i + 1),
1223 keys_iteration * 6);
1224 }
1225 }
1226
1227 // Verify that you can backup and restore with share_files_with_checksum on
TEST_F(BackupableDBTest,ShareTableFilesWithChecksums)1228 TEST_F(BackupableDBTest, ShareTableFilesWithChecksums) {
1229 const int keys_iteration = 5000;
1230 OpenDBAndBackupEngine(true, false, kShareWithChecksum);
1231 for (int i = 0; i < 5; ++i) {
1232 FillDB(db_.get(), keys_iteration * i, keys_iteration * (i + 1));
1233 ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), !!(i % 2)));
1234 }
1235 CloseDBAndBackupEngine();
1236
1237 for (int i = 0; i < 5; ++i) {
1238 AssertBackupConsistency(i + 1, 0, keys_iteration * (i + 1),
1239 keys_iteration * 6);
1240 }
1241 }
1242
1243 // Verify that you can backup and restore using share_files_with_checksum set to
1244 // false and then transition this option to true
TEST_F(BackupableDBTest,ShareTableFilesWithChecksumsTransition)1245 TEST_F(BackupableDBTest, ShareTableFilesWithChecksumsTransition) {
1246 const int keys_iteration = 5000;
1247 // set share_files_with_checksum to false
1248 OpenDBAndBackupEngine(true, false, kShareNoChecksum);
1249 for (int i = 0; i < 5; ++i) {
1250 FillDB(db_.get(), keys_iteration * i, keys_iteration * (i + 1));
1251 ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), true));
1252 }
1253 CloseDBAndBackupEngine();
1254
1255 for (int i = 0; i < 5; ++i) {
1256 AssertBackupConsistency(i + 1, 0, keys_iteration * (i + 1),
1257 keys_iteration * 6);
1258 }
1259
1260 // set share_files_with_checksum to true and do some more backups
1261 OpenDBAndBackupEngine(false /* destroy_old_data */, false,
1262 kShareWithChecksum);
1263 for (int i = 5; i < 10; ++i) {
1264 FillDB(db_.get(), keys_iteration * i, keys_iteration * (i + 1));
1265 ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), true));
1266 }
1267 CloseDBAndBackupEngine();
1268
1269 // Verify first (about to delete)
1270 AssertBackupConsistency(1, 0, keys_iteration, keys_iteration * 11);
1271
1272 // For an extra challenge, make sure that GarbageCollect / DeleteBackup
1273 // is OK even if we open without share_table_files
1274 OpenDBAndBackupEngine(false /* destroy_old_data */, false, kNoShare);
1275 backup_engine_->DeleteBackup(1);
1276 backup_engine_->GarbageCollect();
1277 CloseDBAndBackupEngine();
1278
1279 // Verify rest (not deleted)
1280 for (int i = 1; i < 10; ++i) {
1281 AssertBackupConsistency(i + 1, 0, keys_iteration * (i + 1),
1282 keys_iteration * 11);
1283 }
1284 }
1285
1286 // This test simulates cleaning up after aborted or incomplete creation
1287 // of a new backup.
TEST_F(BackupableDBTest,DeleteTmpFiles)1288 TEST_F(BackupableDBTest, DeleteTmpFiles) {
1289 for (int cleanup_fn : {1, 2, 3, 4}) {
1290 for (ShareOption shared_option : kAllShareOptions) {
1291 OpenDBAndBackupEngine(false /* destroy_old_data */, false /* dummy */,
1292 shared_option);
1293 ASSERT_OK(backup_engine_->CreateNewBackup(db_.get()));
1294 BackupID next_id = 1;
1295 BackupID oldest_id = std::numeric_limits<BackupID>::max();
1296 {
1297 std::vector<BackupInfo> backup_info;
1298 backup_engine_->GetBackupInfo(&backup_info);
1299 for (const auto& bi : backup_info) {
1300 next_id = std::max(next_id, bi.backup_id + 1);
1301 oldest_id = std::min(oldest_id, bi.backup_id);
1302 }
1303 }
1304 CloseDBAndBackupEngine();
1305
1306 // An aborted or incomplete new backup will always be in the next
1307 // id (maybe more)
1308 std::string next_private = "private/" + std::to_string(next_id);
1309
1310 // NOTE: both shared and shared_checksum should be cleaned up
1311 // regardless of how the backup engine is opened.
1312 std::vector<std::string> tmp_files_and_dirs;
1313 for (const auto& dir_and_file : {
1314 std::make_pair(std::string("shared"),
1315 std::string(".00006.sst.tmp")),
1316 std::make_pair(std::string("shared_checksum"),
1317 std::string(".00007.sst.tmp")),
1318 std::make_pair(next_private, std::string("00003.sst")),
1319 }) {
1320 std::string dir = backupdir_ + "/" + dir_and_file.first;
1321 file_manager_->CreateDir(dir);
1322 ASSERT_OK(file_manager_->FileExists(dir));
1323
1324 std::string file = dir + "/" + dir_and_file.second;
1325 file_manager_->WriteToFile(file, "tmp");
1326 ASSERT_OK(file_manager_->FileExists(file));
1327
1328 tmp_files_and_dirs.push_back(file);
1329 }
1330 if (cleanup_fn != /*CreateNewBackup*/ 4) {
1331 // This exists after CreateNewBackup because it's deleted then
1332 // re-created.
1333 tmp_files_and_dirs.push_back(backupdir_ + "/" + next_private);
1334 }
1335
1336 OpenDBAndBackupEngine(false /* destroy_old_data */, false /* dummy */,
1337 shared_option);
1338 // Need to call one of these explicitly to delete tmp files
1339 switch (cleanup_fn) {
1340 case 1:
1341 ASSERT_OK(backup_engine_->GarbageCollect());
1342 break;
1343 case 2:
1344 ASSERT_OK(backup_engine_->DeleteBackup(oldest_id));
1345 break;
1346 case 3:
1347 ASSERT_OK(backup_engine_->PurgeOldBackups(1));
1348 break;
1349 case 4:
1350 // Does a garbage collect if it sees that next private dir exists
1351 ASSERT_OK(backup_engine_->CreateNewBackup(db_.get()));
1352 break;
1353 default:
1354 assert(false);
1355 }
1356 CloseDBAndBackupEngine();
1357 for (std::string file_or_dir : tmp_files_and_dirs) {
1358 if (file_manager_->FileExists(file_or_dir) != Status::NotFound()) {
1359 FAIL() << file_or_dir << " was expected to be deleted." << cleanup_fn;
1360 }
1361 }
1362 }
1363 }
1364 }
1365
TEST_F(BackupableDBTest,KeepLogFiles)1366 TEST_F(BackupableDBTest, KeepLogFiles) {
1367 backupable_options_->backup_log_files = false;
1368 // basically infinite
1369 options_.WAL_ttl_seconds = 24 * 60 * 60;
1370 OpenDBAndBackupEngine(true);
1371 FillDB(db_.get(), 0, 100);
1372 ASSERT_OK(db_->Flush(FlushOptions()));
1373 FillDB(db_.get(), 100, 200);
1374 ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), false));
1375 FillDB(db_.get(), 200, 300);
1376 ASSERT_OK(db_->Flush(FlushOptions()));
1377 FillDB(db_.get(), 300, 400);
1378 ASSERT_OK(db_->Flush(FlushOptions()));
1379 FillDB(db_.get(), 400, 500);
1380 ASSERT_OK(db_->Flush(FlushOptions()));
1381 CloseDBAndBackupEngine();
1382
1383 // all data should be there if we call with keep_log_files = true
1384 AssertBackupConsistency(0, 0, 500, 600, true);
1385 }
1386
TEST_F(BackupableDBTest,RateLimiting)1387 TEST_F(BackupableDBTest, RateLimiting) {
1388 size_t const kMicrosPerSec = 1000 * 1000LL;
1389 uint64_t const MB = 1024 * 1024;
1390
1391 const std::vector<std::pair<uint64_t, uint64_t>> limits(
1392 {{1 * MB, 5 * MB}, {2 * MB, 3 * MB}});
1393
1394 std::shared_ptr<RateLimiter> backupThrottler(NewGenericRateLimiter(1));
1395 std::shared_ptr<RateLimiter> restoreThrottler(NewGenericRateLimiter(1));
1396
1397 for (bool makeThrottler : {false, true}) {
1398 if (makeThrottler) {
1399 backupable_options_->backup_rate_limiter = backupThrottler;
1400 backupable_options_->restore_rate_limiter = restoreThrottler;
1401 }
1402 // iter 0 -- single threaded
1403 // iter 1 -- multi threaded
1404 for (int iter = 0; iter < 2; ++iter) {
1405 for (const auto& limit : limits) {
1406 // destroy old data
1407 DestroyDB(dbname_, Options());
1408 if (makeThrottler) {
1409 backupThrottler->SetBytesPerSecond(limit.first);
1410 restoreThrottler->SetBytesPerSecond(limit.second);
1411 } else {
1412 backupable_options_->backup_rate_limit = limit.first;
1413 backupable_options_->restore_rate_limit = limit.second;
1414 }
1415 backupable_options_->max_background_operations = (iter == 0) ? 1 : 10;
1416 options_.compression = kNoCompression;
1417 OpenDBAndBackupEngine(true);
1418 size_t bytes_written = FillDB(db_.get(), 0, 100000);
1419
1420 auto start_backup = db_chroot_env_->NowMicros();
1421 ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), false));
1422 auto backup_time = db_chroot_env_->NowMicros() - start_backup;
1423 auto rate_limited_backup_time =
1424 (bytes_written * kMicrosPerSec) / limit.first;
1425 ASSERT_GT(backup_time, 0.8 * rate_limited_backup_time);
1426
1427 CloseDBAndBackupEngine();
1428
1429 OpenBackupEngine();
1430 auto start_restore = db_chroot_env_->NowMicros();
1431 ASSERT_OK(backup_engine_->RestoreDBFromLatestBackup(dbname_, dbname_));
1432 auto restore_time = db_chroot_env_->NowMicros() - start_restore;
1433 CloseBackupEngine();
1434 auto rate_limited_restore_time =
1435 (bytes_written * kMicrosPerSec) / limit.second;
1436 ASSERT_GT(restore_time, 0.8 * rate_limited_restore_time);
1437
1438 AssertBackupConsistency(0, 0, 100000, 100010);
1439 }
1440 }
1441 }
1442 }
1443
TEST_F(BackupableDBTest,ReadOnlyBackupEngine)1444 TEST_F(BackupableDBTest, ReadOnlyBackupEngine) {
1445 DestroyDB(dbname_, options_);
1446 OpenDBAndBackupEngine(true);
1447 FillDB(db_.get(), 0, 100);
1448 ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), true));
1449 FillDB(db_.get(), 100, 200);
1450 ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), true));
1451 CloseDBAndBackupEngine();
1452 DestroyDB(dbname_, options_);
1453
1454 backupable_options_->destroy_old_data = false;
1455 test_backup_env_->ClearWrittenFiles();
1456 test_backup_env_->SetLimitDeleteFiles(0);
1457 BackupEngineReadOnly* read_only_backup_engine;
1458 ASSERT_OK(BackupEngineReadOnly::Open(
1459 db_chroot_env_.get(), *backupable_options_, &read_only_backup_engine));
1460 std::vector<BackupInfo> backup_info;
1461 read_only_backup_engine->GetBackupInfo(&backup_info);
1462 ASSERT_EQ(backup_info.size(), 2U);
1463
1464 RestoreOptions restore_options(false);
1465 ASSERT_OK(read_only_backup_engine->RestoreDBFromLatestBackup(
1466 dbname_, dbname_, restore_options));
1467 delete read_only_backup_engine;
1468 std::vector<std::string> should_have_written;
1469 test_backup_env_->AssertWrittenFiles(should_have_written);
1470
1471 DB* db = OpenDB();
1472 AssertExists(db, 0, 200);
1473 delete db;
1474 }
1475
TEST_F(BackupableDBTest,ProgressCallbackDuringBackup)1476 TEST_F(BackupableDBTest, ProgressCallbackDuringBackup) {
1477 DestroyDB(dbname_, options_);
1478 OpenDBAndBackupEngine(true);
1479 FillDB(db_.get(), 0, 100);
1480 bool is_callback_invoked = false;
1481 ASSERT_OK(backup_engine_->CreateNewBackup(
1482 db_.get(), true,
1483 [&is_callback_invoked]() { is_callback_invoked = true; }));
1484
1485 ASSERT_TRUE(is_callback_invoked);
1486 CloseDBAndBackupEngine();
1487 DestroyDB(dbname_, options_);
1488 }
1489
TEST_F(BackupableDBTest,GarbageCollectionBeforeBackup)1490 TEST_F(BackupableDBTest, GarbageCollectionBeforeBackup) {
1491 DestroyDB(dbname_, options_);
1492 OpenDBAndBackupEngine(true);
1493
1494 backup_chroot_env_->CreateDirIfMissing(backupdir_ + "/shared");
1495 std::string file_five = backupdir_ + "/shared/000007.sst";
1496 std::string file_five_contents = "I'm not really a sst file";
1497 // this depends on the fact that 00007.sst is the first file created by the DB
1498 ASSERT_OK(file_manager_->WriteToFile(file_five, file_five_contents));
1499
1500 FillDB(db_.get(), 0, 100);
1501 // backup overwrites file 000007.sst
1502 ASSERT_TRUE(backup_engine_->CreateNewBackup(db_.get(), true).ok());
1503
1504 std::string new_file_five_contents;
1505 ASSERT_OK(ReadFileToString(backup_chroot_env_.get(), file_five,
1506 &new_file_five_contents));
1507 // file 000007.sst was overwritten
1508 ASSERT_TRUE(new_file_five_contents != file_five_contents);
1509
1510 CloseDBAndBackupEngine();
1511
1512 AssertBackupConsistency(0, 0, 100);
1513 }
1514
1515 // Test that we properly propagate Env failures
TEST_F(BackupableDBTest,EnvFailures)1516 TEST_F(BackupableDBTest, EnvFailures) {
1517 BackupEngine* backup_engine;
1518
1519 // get children failure
1520 {
1521 test_backup_env_->SetGetChildrenFailure(true);
1522 ASSERT_NOK(BackupEngine::Open(test_db_env_.get(), *backupable_options_,
1523 &backup_engine));
1524 test_backup_env_->SetGetChildrenFailure(false);
1525 }
1526
1527 // created dir failure
1528 {
1529 test_backup_env_->SetCreateDirIfMissingFailure(true);
1530 ASSERT_NOK(BackupEngine::Open(test_db_env_.get(), *backupable_options_,
1531 &backup_engine));
1532 test_backup_env_->SetCreateDirIfMissingFailure(false);
1533 }
1534
1535 // new directory failure
1536 {
1537 test_backup_env_->SetNewDirectoryFailure(true);
1538 ASSERT_NOK(BackupEngine::Open(test_db_env_.get(), *backupable_options_,
1539 &backup_engine));
1540 test_backup_env_->SetNewDirectoryFailure(false);
1541 }
1542
1543 // Read from meta-file failure
1544 {
1545 DestroyDB(dbname_, options_);
1546 OpenDBAndBackupEngine(true);
1547 FillDB(db_.get(), 0, 100);
1548 ASSERT_TRUE(backup_engine_->CreateNewBackup(db_.get(), true).ok());
1549 CloseDBAndBackupEngine();
1550 test_backup_env_->SetDummySequentialFile(true);
1551 test_backup_env_->SetDummySequentialFileFailReads(true);
1552 backupable_options_->destroy_old_data = false;
1553 ASSERT_NOK(BackupEngine::Open(test_db_env_.get(), *backupable_options_,
1554 &backup_engine));
1555 test_backup_env_->SetDummySequentialFile(false);
1556 test_backup_env_->SetDummySequentialFileFailReads(false);
1557 }
1558
1559 // no failure
1560 {
1561 ASSERT_OK(BackupEngine::Open(test_db_env_.get(), *backupable_options_,
1562 &backup_engine));
1563 delete backup_engine;
1564 }
1565 }
1566
1567 // Verify manifest can roll while a backup is being created with the old
1568 // manifest.
TEST_F(BackupableDBTest,ChangeManifestDuringBackupCreation)1569 TEST_F(BackupableDBTest, ChangeManifestDuringBackupCreation) {
1570 DestroyDB(dbname_, options_);
1571 options_.max_manifest_file_size = 0; // always rollover manifest for file add
1572 OpenDBAndBackupEngine(true);
1573 FillDB(db_.get(), 0, 100);
1574
1575 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({
1576 {"CheckpointImpl::CreateCheckpoint:SavedLiveFiles1",
1577 "VersionSet::LogAndApply:WriteManifest"},
1578 {"VersionSet::LogAndApply:WriteManifestDone",
1579 "CheckpointImpl::CreateCheckpoint:SavedLiveFiles2"},
1580 });
1581 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
1582
1583 ROCKSDB_NAMESPACE::port::Thread flush_thread{
1584 [this]() { ASSERT_OK(db_->Flush(FlushOptions())); }};
1585
1586 ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), false));
1587
1588 flush_thread.join();
1589 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
1590
1591 // The last manifest roll would've already been cleaned up by the full scan
1592 // that happens when CreateNewBackup invokes EnableFileDeletions. We need to
1593 // trigger another roll to verify non-full scan purges stale manifests.
1594 DBImpl* db_impl = reinterpret_cast<DBImpl*>(db_.get());
1595 std::string prev_manifest_path =
1596 DescriptorFileName(dbname_, db_impl->TEST_Current_Manifest_FileNo());
1597 FillDB(db_.get(), 0, 100);
1598 ASSERT_OK(db_chroot_env_->FileExists(prev_manifest_path));
1599 ASSERT_OK(db_->Flush(FlushOptions()));
1600 ASSERT_TRUE(db_chroot_env_->FileExists(prev_manifest_path).IsNotFound());
1601
1602 CloseDBAndBackupEngine();
1603 DestroyDB(dbname_, options_);
1604 AssertBackupConsistency(0, 0, 100);
1605 }
1606
1607 // see https://github.com/facebook/rocksdb/issues/921
TEST_F(BackupableDBTest,Issue921Test)1608 TEST_F(BackupableDBTest, Issue921Test) {
1609 BackupEngine* backup_engine;
1610 backupable_options_->share_table_files = false;
1611 backup_chroot_env_->CreateDirIfMissing(backupable_options_->backup_dir);
1612 backupable_options_->backup_dir += "/new_dir";
1613 ASSERT_OK(BackupEngine::Open(backup_chroot_env_.get(), *backupable_options_,
1614 &backup_engine));
1615
1616 delete backup_engine;
1617 }
1618
TEST_F(BackupableDBTest,BackupWithMetadata)1619 TEST_F(BackupableDBTest, BackupWithMetadata) {
1620 const int keys_iteration = 5000;
1621 OpenDBAndBackupEngine(true);
1622 // create five backups
1623 for (int i = 0; i < 5; ++i) {
1624 const std::string metadata = std::to_string(i);
1625 FillDB(db_.get(), keys_iteration * i, keys_iteration * (i + 1));
1626 ASSERT_OK(
1627 backup_engine_->CreateNewBackupWithMetadata(db_.get(), metadata, true));
1628 }
1629 CloseDBAndBackupEngine();
1630
1631 OpenDBAndBackupEngine();
1632 std::vector<BackupInfo> backup_infos;
1633 backup_engine_->GetBackupInfo(&backup_infos);
1634 ASSERT_EQ(5, backup_infos.size());
1635 for (int i = 0; i < 5; i++) {
1636 ASSERT_EQ(std::to_string(i), backup_infos[i].app_metadata);
1637 }
1638 CloseDBAndBackupEngine();
1639 DestroyDB(dbname_, options_);
1640 }
1641
TEST_F(BackupableDBTest,BinaryMetadata)1642 TEST_F(BackupableDBTest, BinaryMetadata) {
1643 OpenDBAndBackupEngine(true);
1644 std::string binaryMetadata = "abc\ndef";
1645 binaryMetadata.push_back('\0');
1646 binaryMetadata.append("ghi");
1647 ASSERT_OK(
1648 backup_engine_->CreateNewBackupWithMetadata(db_.get(), binaryMetadata));
1649 CloseDBAndBackupEngine();
1650
1651 OpenDBAndBackupEngine();
1652 std::vector<BackupInfo> backup_infos;
1653 backup_engine_->GetBackupInfo(&backup_infos);
1654 ASSERT_EQ(1, backup_infos.size());
1655 ASSERT_EQ(binaryMetadata, backup_infos[0].app_metadata);
1656 CloseDBAndBackupEngine();
1657 DestroyDB(dbname_, options_);
1658 }
1659
TEST_F(BackupableDBTest,MetadataTooLarge)1660 TEST_F(BackupableDBTest, MetadataTooLarge) {
1661 OpenDBAndBackupEngine(true);
1662 std::string largeMetadata(1024 * 1024 + 1, 0);
1663 ASSERT_NOK(
1664 backup_engine_->CreateNewBackupWithMetadata(db_.get(), largeMetadata));
1665 CloseDBAndBackupEngine();
1666 DestroyDB(dbname_, options_);
1667 }
1668
TEST_F(BackupableDBTest,LimitBackupsOpened)1669 TEST_F(BackupableDBTest, LimitBackupsOpened) {
1670 // Verify the specified max backups are opened, including skipping over
1671 // corrupted backups.
1672 //
1673 // Setup:
1674 // - backups 1, 2, and 4 are valid
1675 // - backup 3 is corrupt
1676 // - max_valid_backups_to_open == 2
1677 //
1678 // Expectation: the engine opens backups 4 and 2 since those are latest two
1679 // non-corrupt backups.
1680 const int kNumKeys = 5000;
1681 OpenDBAndBackupEngine(true);
1682 for (int i = 1; i <= 4; ++i) {
1683 FillDB(db_.get(), kNumKeys * i, kNumKeys * (i + 1));
1684 ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), true));
1685 if (i == 3) {
1686 ASSERT_OK(file_manager_->CorruptFile(backupdir_ + "/meta/3", 3));
1687 }
1688 }
1689 CloseDBAndBackupEngine();
1690
1691 backupable_options_->max_valid_backups_to_open = 2;
1692 backupable_options_->destroy_old_data = false;
1693 BackupEngineReadOnly* read_only_backup_engine;
1694 ASSERT_OK(BackupEngineReadOnly::Open(backup_chroot_env_.get(),
1695 *backupable_options_,
1696 &read_only_backup_engine));
1697
1698 std::vector<BackupInfo> backup_infos;
1699 read_only_backup_engine->GetBackupInfo(&backup_infos);
1700 ASSERT_EQ(2, backup_infos.size());
1701 ASSERT_EQ(2, backup_infos[0].backup_id);
1702 ASSERT_EQ(4, backup_infos[1].backup_id);
1703 delete read_only_backup_engine;
1704 }
1705
TEST_F(BackupableDBTest,IgnoreLimitBackupsOpenedWhenNotReadOnly)1706 TEST_F(BackupableDBTest, IgnoreLimitBackupsOpenedWhenNotReadOnly) {
1707 // Verify the specified max_valid_backups_to_open is ignored if the engine
1708 // is not read-only.
1709 //
1710 // Setup:
1711 // - backups 1, 2, and 4 are valid
1712 // - backup 3 is corrupt
1713 // - max_valid_backups_to_open == 2
1714 //
1715 // Expectation: the engine opens backups 4, 2, and 1 since those are latest
1716 // non-corrupt backups, by ignoring max_valid_backups_to_open == 2.
1717 const int kNumKeys = 5000;
1718 OpenDBAndBackupEngine(true);
1719 for (int i = 1; i <= 4; ++i) {
1720 FillDB(db_.get(), kNumKeys * i, kNumKeys * (i + 1));
1721 ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), true));
1722 if (i == 3) {
1723 ASSERT_OK(file_manager_->CorruptFile(backupdir_ + "/meta/3", 3));
1724 }
1725 }
1726 CloseDBAndBackupEngine();
1727
1728 backupable_options_->max_valid_backups_to_open = 2;
1729 OpenDBAndBackupEngine();
1730 std::vector<BackupInfo> backup_infos;
1731 backup_engine_->GetBackupInfo(&backup_infos);
1732 ASSERT_EQ(3, backup_infos.size());
1733 ASSERT_EQ(1, backup_infos[0].backup_id);
1734 ASSERT_EQ(2, backup_infos[1].backup_id);
1735 ASSERT_EQ(4, backup_infos[2].backup_id);
1736 CloseDBAndBackupEngine();
1737 DestroyDB(dbname_, options_);
1738 }
1739
TEST_F(BackupableDBTest,CreateWhenLatestBackupCorrupted)1740 TEST_F(BackupableDBTest, CreateWhenLatestBackupCorrupted) {
1741 // we should pick an ID greater than corrupted backups' IDs so creation can
1742 // succeed even when latest backup is corrupted.
1743 const int kNumKeys = 5000;
1744 OpenDBAndBackupEngine(true /* destroy_old_data */);
1745 FillDB(db_.get(), 0 /* from */, kNumKeys);
1746 ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(),
1747 true /* flush_before_backup */));
1748 ASSERT_OK(file_manager_->CorruptFile(backupdir_ + "/meta/1",
1749 3 /* bytes_to_corrupt */));
1750 CloseDBAndBackupEngine();
1751
1752 OpenDBAndBackupEngine();
1753 ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(),
1754 true /* flush_before_backup */));
1755 std::vector<BackupInfo> backup_infos;
1756 backup_engine_->GetBackupInfo(&backup_infos);
1757 ASSERT_EQ(1, backup_infos.size());
1758 ASSERT_EQ(2, backup_infos[0].backup_id);
1759 }
1760
TEST_F(BackupableDBTest,WriteOnlyEngineNoSharedFileDeletion)1761 TEST_F(BackupableDBTest, WriteOnlyEngineNoSharedFileDeletion) {
1762 // Verifies a write-only BackupEngine does not delete files belonging to valid
1763 // backups when GarbageCollect, PurgeOldBackups, or DeleteBackup are called.
1764 const int kNumKeys = 5000;
1765 for (int i = 0; i < 3; ++i) {
1766 OpenDBAndBackupEngine(i == 0 /* destroy_old_data */);
1767 FillDB(db_.get(), i * kNumKeys, (i + 1) * kNumKeys);
1768 ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), true));
1769 CloseDBAndBackupEngine();
1770
1771 backupable_options_->max_valid_backups_to_open = 0;
1772 OpenDBAndBackupEngine();
1773 switch (i) {
1774 case 0:
1775 ASSERT_OK(backup_engine_->GarbageCollect());
1776 break;
1777 case 1:
1778 ASSERT_OK(backup_engine_->PurgeOldBackups(1 /* num_backups_to_keep */));
1779 break;
1780 case 2:
1781 ASSERT_OK(backup_engine_->DeleteBackup(2 /* backup_id */));
1782 break;
1783 default:
1784 assert(false);
1785 }
1786 CloseDBAndBackupEngine();
1787
1788 backupable_options_->max_valid_backups_to_open = port::kMaxInt32;
1789 AssertBackupConsistency(i + 1, 0, (i + 1) * kNumKeys);
1790 }
1791 }
1792
TEST_P(BackupableDBTestWithParam,BackupUsingDirectIO)1793 TEST_P(BackupableDBTestWithParam, BackupUsingDirectIO) {
1794 // Tests direct I/O on the backup engine's reads and writes on the DB env and
1795 // backup env
1796 // We use ChrootEnv underneath so the below line checks for direct I/O support
1797 // in the chroot directory, not the true filesystem root.
1798 if (!test::IsDirectIOSupported(test_db_env_.get(), "/")) {
1799 return;
1800 }
1801 const int kNumKeysPerBackup = 100;
1802 const int kNumBackups = 3;
1803 options_.use_direct_reads = true;
1804 OpenDBAndBackupEngine(true /* destroy_old_data */);
1805 for (int i = 0; i < kNumBackups; ++i) {
1806 FillDB(db_.get(), i * kNumKeysPerBackup /* from */,
1807 (i + 1) * kNumKeysPerBackup /* to */);
1808 ASSERT_OK(db_->Flush(FlushOptions()));
1809
1810 // Clear the file open counters and then do a bunch of backup engine ops.
1811 // For all ops, files should be opened in direct mode.
1812 test_backup_env_->ClearFileOpenCounters();
1813 test_db_env_->ClearFileOpenCounters();
1814 CloseBackupEngine();
1815 OpenBackupEngine();
1816 ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(),
1817 false /* flush_before_backup */));
1818 ASSERT_OK(backup_engine_->VerifyBackup(i + 1));
1819 CloseBackupEngine();
1820 OpenBackupEngine();
1821 std::vector<BackupInfo> backup_infos;
1822 backup_engine_->GetBackupInfo(&backup_infos);
1823 ASSERT_EQ(static_cast<size_t>(i + 1), backup_infos.size());
1824
1825 // Verify backup engine always opened files with direct I/O
1826 ASSERT_EQ(0, test_db_env_->num_writers());
1827 ASSERT_EQ(0, test_db_env_->num_rand_readers());
1828 ASSERT_GT(test_db_env_->num_direct_seq_readers(), 0);
1829 // Currently the DB doesn't support reading WALs or manifest with direct
1830 // I/O, so subtract two.
1831 ASSERT_EQ(test_db_env_->num_seq_readers() - 2,
1832 test_db_env_->num_direct_seq_readers());
1833 ASSERT_EQ(0, test_db_env_->num_rand_readers());
1834 }
1835 CloseDBAndBackupEngine();
1836
1837 for (int i = 0; i < kNumBackups; ++i) {
1838 AssertBackupConsistency(i + 1 /* backup_id */,
1839 i * kNumKeysPerBackup /* start_exist */,
1840 (i + 1) * kNumKeysPerBackup /* end_exist */,
1841 (i + 2) * kNumKeysPerBackup /* end */);
1842 }
1843 }
1844
TEST_F(BackupableDBTest,BackgroundThreadCpuPriority)1845 TEST_F(BackupableDBTest, BackgroundThreadCpuPriority) {
1846 std::atomic<CpuPriority> priority(CpuPriority::kNormal);
1847 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
1848 "BackupEngineImpl::Initialize:SetCpuPriority", [&](void* new_priority) {
1849 priority.store(*reinterpret_cast<CpuPriority*>(new_priority));
1850 });
1851 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
1852
1853 // 1 thread is easier to test, otherwise, we may not be sure which thread
1854 // actually does the work during CreateNewBackup.
1855 backupable_options_->max_background_operations = 1;
1856 OpenDBAndBackupEngine(true);
1857
1858 {
1859 FillDB(db_.get(), 0, 100);
1860
1861 // by default, cpu priority is not changed.
1862 CreateBackupOptions options;
1863 ASSERT_OK(backup_engine_->CreateNewBackup(options, db_.get()));
1864
1865 ASSERT_EQ(priority, CpuPriority::kNormal);
1866 }
1867
1868 {
1869 FillDB(db_.get(), 101, 200);
1870
1871 // decrease cpu priority from normal to low.
1872 CreateBackupOptions options;
1873 options.decrease_background_thread_cpu_priority = true;
1874 options.background_thread_cpu_priority = CpuPriority::kLow;
1875 ASSERT_OK(backup_engine_->CreateNewBackup(options, db_.get()));
1876
1877 ASSERT_EQ(priority, CpuPriority::kLow);
1878 }
1879
1880 {
1881 FillDB(db_.get(), 201, 300);
1882
1883 // try to upgrade cpu priority back to normal,
1884 // the priority should still low.
1885 CreateBackupOptions options;
1886 options.decrease_background_thread_cpu_priority = true;
1887 options.background_thread_cpu_priority = CpuPriority::kNormal;
1888 ASSERT_OK(backup_engine_->CreateNewBackup(options, db_.get()));
1889
1890 ASSERT_EQ(priority, CpuPriority::kLow);
1891 }
1892
1893 {
1894 FillDB(db_.get(), 301, 400);
1895
1896 // decrease cpu priority from low to idle.
1897 CreateBackupOptions options;
1898 options.decrease_background_thread_cpu_priority = true;
1899 options.background_thread_cpu_priority = CpuPriority::kIdle;
1900 ASSERT_OK(backup_engine_->CreateNewBackup(options, db_.get()));
1901
1902 ASSERT_EQ(priority, CpuPriority::kIdle);
1903 }
1904
1905 {
1906 FillDB(db_.get(), 301, 400);
1907
1908 // reset priority to later verify that it's not updated by SetCpuPriority.
1909 priority = CpuPriority::kNormal;
1910
1911 // setting the same cpu priority won't call SetCpuPriority.
1912 CreateBackupOptions options;
1913 options.decrease_background_thread_cpu_priority = true;
1914 options.background_thread_cpu_priority = CpuPriority::kIdle;
1915 ASSERT_OK(backup_engine_->CreateNewBackup(options, db_.get()));
1916
1917 ASSERT_EQ(priority, CpuPriority::kNormal);
1918 }
1919
1920 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
1921 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
1922 CloseDBAndBackupEngine();
1923 DestroyDB(dbname_, options_);
1924 }
1925
1926 } // anon namespace
1927
1928 } // namespace ROCKSDB_NAMESPACE
1929
main(int argc,char ** argv)1930 int main(int argc, char** argv) {
1931 ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
1932 ::testing::InitGoogleTest(&argc, argv);
1933 return RUN_ALL_TESTS();
1934 }
1935
1936 #else
1937 #include <stdio.h>
1938
main(int,char **)1939 int main(int /*argc*/, char** /*argv*/) {
1940 fprintf(stderr, "SKIPPED as BackupableDB is not supported in ROCKSDB_LITE\n");
1941 return 0;
1942 }
1943
1944 #endif // !defined(ROCKSDB_LITE) && !defined(OS_WIN)
1945