Skip to content

Commit

Permalink
Add BackupEngine feature to exclude files (#11030)
Browse files Browse the repository at this point in the history
Summary:
We have a request for RocksDB to essentially support
disconnected incremental backup. In other words, if there is limited
or no connectivity to the primary backup dir, we should still be able to
take an incremental backup relative to that primary backup dir,
assuming some metadata about that primary dir is available (and
obviously anticipating primary backup dir will be fully available if
restore is needed).

To support that, this feature allows the API user to "exclude" DB
files from backup. This only applies to files that can be shared
between backups (sst and blob files), and excluded files are
tracked in the backup metadata sufficiently to ensure they are
restored at restore time. At restore time, the user provides
a set of alternate backup directories (as open BackupEngines, which
can be read-only), and excluded files must be found in one of the
backup directories ("included" in some backup).

This feature depends on backup schema version 2 features, though
schema version 2.0 support is not sufficient to read / restore a
backup with exclusions. This change updates the schema version to
2.1 because of this feature, so that it's easy to recognize whether
a RocksDB release supports this feature, while backups not using the
feature are fully compatible with 2.0.

Also in this PR:
* Stacked on facebook/rocksdb#11029
* Allow progress_callback to be empty, not just no-op function, and
recover from exceptions thrown by BackupEngine callbacks.
* The internal-only `AsBackupEngine()` function is working around the
diamond hierarchy of `BackupEngineImplThreadSafe` to get to the
internals, without using confusing features like virtual inheritance.

Pull Request resolved: facebook/rocksdb#11030

Test Plan: unit tests added / updated

Reviewed By: ajkr

Differential Revision: D42004388

Pulled By: pdillinger

fbshipit-source-id: 31b6e533d308a5462e528d9012d650482d974077
  • Loading branch information
pdillinger authored and facebook-github-bot committed Dec 29, 2022
1 parent bec4264 commit 02f2b20
Show file tree
Hide file tree
Showing 4 changed files with 510 additions and 87 deletions.
3 changes: 3 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
### New Features
* When an SstPartitionerFactory is configured, CompactRange() now automatically selects for compaction any files overlapping a partition boundary that is in the compaction range, even if no actual entries are in the requested compaction range. With this feature, manual compaction can be used to (re-)establish SST partition points when SstPartitioner changes, without a full compaction.

### New Features
* Add BackupEngine feature to exclude files from backup that are known to be backed up elsewhere, using `CreateBackupOptions::exclude_files_callback`. To restore the DB, the excluded files must be provided in alternative backup directories using `RestoreOptions::alternate_dirs`.

## 7.9.0 (11/21/2022)
### Performance Improvements
* Fixed an iterator performance regression for delete range users when scanning through a consecutive sequence of range tombstones (#10877).
Expand Down
78 changes: 69 additions & 9 deletions include/rocksdb/utilities/backup_engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#ifndef ROCKSDB_LITE

#include <cstdint>
#include <forward_list>
#include <functional>
#include <map>
#include <string>
Expand All @@ -23,6 +24,8 @@
#include "rocksdb/status.h"

namespace ROCKSDB_NAMESPACE {
class BackupEngineReadOnlyBase;
class BackupEngine;

// The default DB file checksum function name.
constexpr char kDbFileChecksumFuncName[] = "FileChecksumCrc32c";
Expand Down Expand Up @@ -270,6 +273,28 @@ inline BackupEngineOptions::ShareFilesNaming operator|(
return static_cast<BackupEngineOptions::ShareFilesNaming>(l | r);
}

// Identifying information about a backup shared file that is (or might be)
// excluded from a backup using exclude_files_callback.
struct BackupExcludedFileInfo {
explicit BackupExcludedFileInfo(const std::string& _relative_file)
: relative_file(_relative_file) {}

// File name and path relative to the backup dir.
std::string relative_file;
};

// An auxiliary structure for exclude_files_callback
struct MaybeExcludeBackupFile {
explicit MaybeExcludeBackupFile(BackupExcludedFileInfo&& _info)
: info(std::move(_info)) {}

// Identifying information about a backup shared file that could be excluded
const BackupExcludedFileInfo info;

// API user sets to true if the file should be excluded from this backup
bool exclude_decision = false;
};

struct CreateBackupOptions {
// Flush will always trigger if 2PC is enabled.
// If write-ahead logs are disabled, set flush_before_backup=true to
Expand All @@ -278,10 +303,31 @@ struct CreateBackupOptions {

// Callback for reporting progress, based on callback_trigger_interval_size.
//
// RocksDB callbacks are NOT exception-safe. A callback completing with an
// exception can lead to undefined behavior in RocksDB, including data loss,
// unreported corruption, deadlocks, and more.
std::function<void()> progress_callback = []() {};
// An exception thrown from the callback will result in Status::Aborted from
// the operation.
std::function<void()> progress_callback = {};

// A callback that allows the API user to select files for exclusion, such
// as if the files are known to exist in an alternate backup directory.
// Only "shared" files can be excluded from backups. This is an advanced
// feature because the BackupEngine user is trusted to keep track of files
// such that the DB can be restored.
//
// Input to the callback is a [begin,end) range of sharable files live in
// the DB being backed up, and the callback implementation sets
// exclude_decision=true for files to exclude. A callback offers maximum
// flexibility, e.g. if remote files are unavailable at backup time but
// whose existence has been recorded somewhere. In case of an empty or
// no-op callback, all files are included in the backup .
//
// To restore the DB, RestoreOptions::alternate_dirs must be used to provide
// the excluded files.
//
// An exception thrown from the callback will result in Status::Aborted from
// the operation.
std::function<void(MaybeExcludeBackupFile* files_begin,
MaybeExcludeBackupFile* files_end)>
exclude_files_callback = {};

// If false, background_thread_cpu_priority is ignored.
// Otherwise, the cpu priority can be decreased,
Expand All @@ -300,6 +346,11 @@ struct RestoreOptions {
// Default: false
bool keep_log_files;

// For backups that were created using exclude_files_callback, this
// option enables restoring those backups by providing BackupEngines on
// directories known to contain the required files.
std::forward_list<BackupEngineReadOnlyBase*> alternate_dirs;

explicit RestoreOptions(bool _keep_log_files = false)
: keep_log_files(_keep_log_files) {}
};
Expand All @@ -324,9 +375,15 @@ struct BackupInfo {
// Backup API user metadata
std::string app_metadata;

// Backup file details, if requested with include_file_details=true
// Backup file details, if requested with include_file_details=true.
// Does not include excluded_files.
std::vector<BackupFileInfo> file_details;

// Identifying information about shared files that were excluded from the
// created backup. See exclude_files_callback and alternate_dirs.
// This information is only provided if include_file_details=true.
std::vector<BackupExcludedFileInfo> excluded_files;

// DB "name" (a directory in the backup_env) for opening this backup as a
// read-only DB. This should also be used as the DBOptions::wal_dir, such
// as by default setting wal_dir="". See also env_for_open.
Expand All @@ -348,8 +405,8 @@ struct BackupInfo {

BackupInfo() {}

BackupInfo(BackupID _backup_id, int64_t _timestamp, uint64_t _size,
uint32_t _number_files, const std::string& _app_metadata)
explicit BackupInfo(BackupID _backup_id, int64_t _timestamp, uint64_t _size,
uint32_t _number_files, const std::string& _app_metadata)
: backup_id(_backup_id),
timestamp(_timestamp),
size(_size),
Expand All @@ -364,8 +421,8 @@ class BackupStatistics {
number_fail_backup = 0;
}

BackupStatistics(uint32_t _number_success_backup,
uint32_t _number_fail_backup)
explicit BackupStatistics(uint32_t _number_success_backup,
uint32_t _number_fail_backup)
: number_success_backup(_number_success_backup),
number_fail_backup(_number_fail_backup) {}

Expand Down Expand Up @@ -462,6 +519,9 @@ class BackupEngineReadOnlyBase {
// Returns Status::OK() if all checks are good
virtual IOStatus VerifyBackup(BackupID backup_id,
bool verify_with_checksum = false) const = 0;

// Internal use only
virtual BackupEngine* AsBackupEngine() = 0;
};

// Append-only functions of a BackupEngine. See BackupEngine comment for
Expand Down
Loading

0 comments on commit 02f2b20

Please sign in to comment.