Skip to content

Commit fb9fce4

Browse files
committed
[RocksDB] BackupableDB
Summary: In this diff I present you BackupableDB v1. You can easily use it to backup your DB and it will do incremental snapshots for you. Let's first describe how you would use BackupableDB. It's inheriting StackableDB interface so you can easily construct it with your DB object -- it will add a method RollTheSnapshot() to the DB object. When you call RollTheSnapshot(), current snapshot of the DB will be stored in the backup dir. To restore, you can just call RestoreDBFromBackup() on a BackupableDB (which is a static method) and it will restore all files from the backup dir. In the next version, it will even support automatic backuping every X minutes. There are multiple things you can configure: 1. backup_env and db_env can be different, which is awesome because then you can easily backup to HDFS or wherever you feel like. 2. sync - if true, it *guarantees* backup consistency on machine reboot 3. number of snapshots to keep - this will keep last N snapshots around if you want, for some reason, be able to restore from an earlier snapshot. All the backuping is done in incremental fashion - if we already have 00010.sst, we will not copy it again. *IMPORTANT* -- This is based on assumption that 00010.sst never changes - two files named 00010.sst from the same DB will always be exactly the same. Is this true? I always copy manifest, current and log files. 4. You can decide if you want to flush the memtables before you backup, or you're fine with backing up the log files -- either way, you get a complete and consistent view of the database at a time of backup. 5. More things you can find in BackupableDBOptions Here is the directory structure I use: backup_dir/CURRENT_SNAPSHOT - just 4 bytes holding the latest snapshot 0, 1, 2, ... - files containing serialized version of each snapshot - containing a list of files files/*.sst - sst files shared between snapshots - if one snapshot references 00010.sst and another one needs to backup it from the DB, it will just reference the same file files/ 0/, 1/, 2/, ... - snapshot directories containing private snapshot files - current, manifest and log files All the files are ref counted and deleted immediatelly when they get out of scope. Some other stuff in this diff: 1. Added GetEnv() method to the DB. Discussed with @haobo and we agreed that it seems right thing to do. 2. Fixed StackableDB interface. The way it was set up before, I was not able to implement BackupableDB. Test Plan: I have a unittest, but please don't look at this yet. I just hacked it up to help me with debugging. I will write a lot of good tests and update the diff. Also, `make asan_check` Reviewers: dhruba, haobo, emayanke Reviewed By: dhruba CC: leveldb, haobo Differential Revision: https://reviews.facebook.net/D14295
1 parent 26bc40a commit fb9fce4

File tree

11 files changed

+1609
-0
lines changed

11 files changed

+1609
-0
lines changed

Makefile

+4
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ TESTS = \
7676
skiplist_test \
7777
stringappend_test \
7878
ttl_test \
79+
backupable_db_test \
7980
version_edit_test \
8081
version_set_test \
8182
write_batch_test\
@@ -272,6 +273,9 @@ perf_context_test: db/perf_context_test.o $(LIBOBJECTS) $(TESTHARNESS)
272273
prefix_test: db/prefix_test.o $(LIBOBJECTS) $(TESTHARNESS)
273274
$(CXX) db/prefix_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS)
274275

276+
backupable_db_test: utilities/backupable/backupable_db_test.o $(LIBOBJECTS) $(TESTHARNESS)
277+
$(CXX) utilities/backupable/backupable_db_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
278+
275279
ttl_test: utilities/ttl/ttl_test.o $(LIBOBJECTS) $(TESTHARNESS)
276280
$(CXX) utilities/ttl/ttl_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
277281

db/db_impl.cc

+4
Original file line numberDiff line numberDiff line change
@@ -3176,6 +3176,10 @@ Status DBImpl::MakeRoomForWrite(bool force) {
31763176
return s;
31773177
}
31783178

3179+
const std::string& DBImpl::GetName() const {
3180+
return dbname_;
3181+
}
3182+
31793183
Env* DBImpl::GetEnv() const {
31803184
return env_;
31813185
}

db/db_impl.h

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ class DBImpl : public DB {
6767
virtual int NumberLevels();
6868
virtual int MaxMemCompactionLevel();
6969
virtual int Level0StopWriteTrigger();
70+
virtual const std::string& GetName() const;
7071
virtual Env* GetEnv() const;
7172
virtual const Options& GetOptions() const;
7273
virtual Status Flush(const FlushOptions& options);

db/db_test.cc

+5
Original file line numberDiff line numberDiff line change
@@ -4444,6 +4444,10 @@ class ModelDB: public DB {
44444444
return -1;
44454445
}
44464446

4447+
virtual const std::string& GetName() const {
4448+
return name_;
4449+
}
4450+
44474451
virtual Env* GetEnv() const {
44484452
return nullptr;
44494453
}
@@ -4521,6 +4525,7 @@ class ModelDB: public DB {
45214525
};
45224526
const Options options_;
45234527
KVMap map_;
4528+
std::string name_ = "";
45244529
};
45254530

45264531
static std::string RandomKey(Random* rnd, int minimum = 0) {

include/rocksdb/db.h

+4
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,10 @@ class DB {
228228
// Number of files in level-0 that would stop writes.
229229
virtual int Level0StopWriteTrigger() = 0;
230230

231+
// Get DB name -- the exact same name that was provided as an argument to
232+
// DB::Open()
233+
virtual const std::string& GetName() const = 0;
234+
231235
// Get Env object from the DB
232236
virtual Env* GetEnv() const = 0;
233237

include/utilities/backupable_db.h

+128
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
// Copyright (c) 2013, Facebook, Inc. All rights reserved.
2+
// This source code is licensed under the BSD-style license found in the
3+
// LICENSE file in the root directory of this source tree. An additional grant
4+
// of patent rights can be found in the PATENTS file in the same directory.
5+
//
6+
// 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+
#pragma once
11+
#include "utilities/stackable_db.h"
12+
#include "rocksdb/env.h"
13+
#include "rocksdb/status.h"
14+
15+
#include <string>
16+
#include <map>
17+
#include <vector>
18+
19+
namespace rocksdb {
20+
21+
struct BackupableDBOptions {
22+
// Where to keep the backup files. Has to be different than dbname_
23+
// Best to set this to dbname_ + "/backups"
24+
// Required
25+
std::string backup_dir;
26+
27+
// Backup Env object. It will be used for backup file I/O. If it's
28+
// nullptr, backups will be written out using DBs Env. If it's
29+
// non-nullptr, backup's I/O will be performed using this object.
30+
// If you want to have backups on HDFS, use HDFS Env here!
31+
// Default: nullptr
32+
Env* backup_env;
33+
34+
// Backup info and error messages will be written to info_log
35+
// if non-nullptr.
36+
// Default: nullptr
37+
Logger* info_log;
38+
39+
// If sync == true, we can guarantee you'll get consistent backup even
40+
// on a machine crash/reboot. Backup process is slower with sync enabled.
41+
// If sync == false, we don't guarantee anything on machine reboot. However,
42+
// chances are some of the backups are consistent.
43+
// Default: true
44+
bool sync;
45+
46+
// If true, it will delete whatever backups there are already
47+
// Default: false
48+
bool destroy_old_data;
49+
50+
explicit BackupableDBOptions(const std::string& _backup_dir,
51+
Env* _backup_env = nullptr,
52+
Logger* _info_log = nullptr,
53+
bool _sync = true,
54+
bool _destroy_old_data = false) :
55+
backup_dir(_backup_dir),
56+
backup_env(_backup_env),
57+
info_log(_info_log),
58+
sync(_sync),
59+
destroy_old_data(_destroy_old_data) { }
60+
};
61+
62+
class BackupEngine;
63+
64+
typedef uint32_t BackupID;
65+
66+
struct BackupInfo {
67+
BackupID backup_id;
68+
int64_t timestamp;
69+
uint64_t size;
70+
71+
BackupInfo() {}
72+
BackupInfo(BackupID _backup_id, int64_t _timestamp, uint64_t _size)
73+
: backup_id(_backup_id), timestamp(_timestamp), size(_size) {}
74+
};
75+
76+
// Stack your DB with BackupableDB to be able to backup the DB
77+
class BackupableDB : public StackableDB {
78+
public:
79+
// BackupableDBOptions have to be the same as the ones used in a previous
80+
// incarnation of the DB
81+
BackupableDB(DB* db, const BackupableDBOptions& options);
82+
virtual ~BackupableDB();
83+
84+
// Captures the state of the database in the latest backup
85+
// NOT a thread safe call
86+
Status CreateNewBackup(bool flush_before_backup = false);
87+
// Returns info about backups in backup_info
88+
void GetBackupInfo(std::vector<BackupInfo>* backup_info);
89+
// deletes old backups, keeping latest num_backups_to_keep alive
90+
Status PurgeOldBackups(uint32_t num_backups_to_keep);
91+
// deletes a specific backup
92+
Status DeleteBackup(BackupID backup_id);
93+
94+
private:
95+
BackupEngine* backup_engine_;
96+
};
97+
98+
// Use this class to access information about backups and restore from them
99+
class RestoreBackupableDB {
100+
public:
101+
RestoreBackupableDB(Env* db_env, const BackupableDBOptions& options);
102+
~RestoreBackupableDB();
103+
104+
// Returns info about backups in backup_info
105+
void GetBackupInfo(std::vector<BackupInfo>* backup_info);
106+
107+
// restore from backup with backup_id
108+
// IMPORTANT -- if you restore from some backup that is not the latest,
109+
// you HAVE to delete all the newer backups immediately, before creating
110+
// new backup on the restored database. Otherwise, your new backups
111+
// will be corrupted.
112+
// TODO should we enforce this somehow?
113+
Status RestoreDBFromBackup(BackupID backup_id, const std::string& db_dir,
114+
const std::string& wal_dir);
115+
116+
// restore from the latest backup
117+
Status RestoreDBFromLatestBackup(const std::string& db_dir,
118+
const std::string& wal_dir);
119+
// deletes old backups, keeping latest num_backups_to_keep alive
120+
Status PurgeOldBackups(uint32_t num_backups_to_keep);
121+
// deletes a specific backup
122+
Status DeleteBackup(BackupID backup_id);
123+
124+
private:
125+
BackupEngine* backup_engine_;
126+
};
127+
128+
} // rocksdb namespace

include/utilities/stackable_db.h

+4
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ class StackableDB : public DB {
103103
return db_->Level0StopWriteTrigger();
104104
}
105105

106+
virtual const std::string& GetName() const override {
107+
return db_->GetName();
108+
}
109+
106110
virtual Env* GetEnv() const override {
107111
return db_->GetEnv();
108112
}

util/coding.cc

+11
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,17 @@ Slice GetLengthPrefixedSlice(const char* data) {
217217
return Slice(p, len);
218218
}
219219

220+
Slice GetSliceUntil(Slice* slice, char delimiter) {
221+
uint32_t len;
222+
for (len = 0; len < slice->size() && slice->data()[len] != delimiter; ++len) {
223+
// nothing
224+
}
225+
226+
Slice ret(slice->data(), len);
227+
slice->remove_prefix(len + ((len < slice->size()) ? 1 : 0));
228+
return ret;
229+
}
230+
220231
void BitStreamPutInt(char* dst, size_t dstlen, size_t offset,
221232
uint32_t bits, uint64_t value) {
222233
assert((offset + bits + 7)/8 <= dstlen);

util/coding.h

+2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ extern bool GetVarint64(Slice* input, uint64_t* value);
4040
extern bool GetLengthPrefixedSlice(Slice* input, Slice* result);
4141
extern Slice GetLengthPrefixedSlice(const char* data);
4242

43+
extern Slice GetSliceUntil(Slice* slice, char delimiter);
44+
4345
// Pointer-based variants of GetVarint... These either store a value
4446
// in *v and return a pointer just past the parsed value, or return
4547
// nullptr on error. These routines only look at bytes in the range

0 commit comments

Comments
 (0)