Adding support for sharing throttler between multiple backup and restores

Summary:
Rocksdb backup and restore rate limiting is currently done per backup/restore.
So, it is difficult to control rate across multiple backup/restores. With this
change, a throttler can be provided. If a throttler is provided, it is used.
Otherwise, a new throttler is created based on the actual rate limits specified
in the options.

Test Plan: Added unit tests

Reviewers: ldemailly, andrewkr, sdong

Reviewed By: andrewkr

Subscribers: igor, yiwu, andrewkr, dhruba

Differential Revision: https://reviews.facebook.net/D56265
This commit is contained in:
Uddipta Maity 2016-06-03 17:02:07 -07:00
parent 6e6622abb9
commit 1147e5b05a
3 changed files with 79 additions and 47 deletions

View File

@ -75,11 +75,21 @@ struct BackupableDBOptions {
// Default: 0 // Default: 0
uint64_t backup_rate_limit; uint64_t backup_rate_limit;
// Backup rate limiter. Used to control transfer speed for backup. If this is
// not null, backup_rate_limit is ignored.
// Default: nullptr
std::shared_ptr<RateLimiter> backup_rate_limiter{nullptr};
// Max bytes that can be transferred in a second during restore. // Max bytes that can be transferred in a second during restore.
// If 0, go as fast as you can // If 0, go as fast as you can
// Default: 0 // Default: 0
uint64_t restore_rate_limit; uint64_t restore_rate_limit;
// Restore rate limiter. Used to control transfer speed during restore. If
// this is not null, restore_rate_limit is ignored.
// Default: nullptr
std::shared_ptr<RateLimiter> restore_rate_limiter{nullptr};
// Only used if share_table_files is set to true. If true, will consider that // Only used if share_table_files is set to true. If true, will consider that
// backups can come from different databases, hence a sst is not uniquely // backups can come from different databases, hence a sst is not uniquely
// identifed by its name, but by the triple (file name, crc32, file length) // identifed by its name, but by the triple (file name, crc32, file length)

View File

@ -497,7 +497,18 @@ BackupEngineImpl::BackupEngineImpl(Env* db_env,
db_env_(db_env), db_env_(db_env),
backup_env_(options.backup_env != nullptr ? options.backup_env : db_env_), backup_env_(options.backup_env != nullptr ? options.backup_env : db_env_),
copy_file_buffer_size_(kDefaultCopyFileBufferSize), copy_file_buffer_size_(kDefaultCopyFileBufferSize),
read_only_(read_only) {} read_only_(read_only) {
if (options_.backup_rate_limiter == nullptr &&
options_.backup_rate_limit > 0) {
options_.backup_rate_limiter.reset(
NewGenericRateLimiter(options_.backup_rate_limit));
}
if (options_.restore_rate_limiter == nullptr &&
options_.restore_rate_limit > 0) {
options_.restore_rate_limiter.reset(
NewGenericRateLimiter(options_.restore_rate_limit));
}
}
BackupEngineImpl::~BackupEngineImpl() { BackupEngineImpl::~BackupEngineImpl() {
files_to_copy_or_create_.sendEof(); files_to_copy_or_create_.sendEof();
@ -703,9 +714,8 @@ Status BackupEngineImpl::CreateNewBackupWithMetadata(
s = backup_env_->CreateDir( s = backup_env_->CreateDir(
GetAbsolutePath(GetPrivateFileRel(new_backup_id, true))); GetAbsolutePath(GetPrivateFileRel(new_backup_id, true)));
unique_ptr<RateLimiter> rate_limiter; RateLimiter* rate_limiter = options_.backup_rate_limiter.get();
if (options_.backup_rate_limit > 0) { if (rate_limiter) {
rate_limiter.reset(NewGenericRateLimiter(options_.backup_rate_limit));
copy_file_buffer_size_ = rate_limiter->GetSingleBurstBytes(); copy_file_buffer_size_ = rate_limiter->GetSingleBurstBytes();
} }
@ -758,7 +768,7 @@ Status BackupEngineImpl::CreateNewBackupWithMetadata(
s = AddBackupFileWorkItem( s = AddBackupFileWorkItem(
live_dst_paths, backup_items_to_finish, new_backup_id, live_dst_paths, backup_items_to_finish, new_backup_id,
options_.share_table_files && type == kTableFile, db->GetName(), options_.share_table_files && type == kTableFile, db->GetName(),
live_files[i], rate_limiter.get(), size_bytes, live_files[i], rate_limiter, size_bytes,
(type == kDescriptorFile) ? manifest_file_size : 0, (type == kDescriptorFile) ? manifest_file_size : 0,
options_.share_files_with_checksum && type == kTableFile, options_.share_files_with_checksum && type == kTableFile,
progress_callback); progress_callback);
@ -767,10 +777,9 @@ Status BackupEngineImpl::CreateNewBackupWithMetadata(
// Write the current file with the manifest filename as its contents. // Write the current file with the manifest filename as its contents.
s = AddBackupFileWorkItem( s = AddBackupFileWorkItem(
live_dst_paths, backup_items_to_finish, new_backup_id, live_dst_paths, backup_items_to_finish, new_backup_id,
false /* shared */, "" /* src_dir */, CurrentFileName(""), false /* shared */, "" /* src_dir */, CurrentFileName(""), rate_limiter,
rate_limiter.get(), manifest_fname.size(), 0 /* size_limit */, manifest_fname.size(), 0 /* size_limit */, false /* shared_checksum */,
false /* shared_checksum */, progress_callback, progress_callback, manifest_fname.substr(1) + "\n");
manifest_fname.substr(1) + "\n");
} }
// Pre-fetch sizes for WAL files // Pre-fetch sizes for WAL files
@ -797,8 +806,8 @@ Status BackupEngineImpl::CreateNewBackupWithMetadata(
s = AddBackupFileWorkItem(live_dst_paths, backup_items_to_finish, s = AddBackupFileWorkItem(live_dst_paths, backup_items_to_finish,
new_backup_id, false, /* not shared */ new_backup_id, false, /* not shared */
db->GetOptions().wal_dir, db->GetOptions().wal_dir,
live_wal_files[i]->PathName(), live_wal_files[i]->PathName(), rate_limiter,
rate_limiter.get(), size_bytes); size_bytes);
} }
} }
@ -1043,9 +1052,8 @@ Status BackupEngineImpl::RestoreDBFromBackup(
DeleteChildren(db_dir); DeleteChildren(db_dir);
} }
unique_ptr<RateLimiter> rate_limiter; RateLimiter* rate_limiter = options_.restore_rate_limiter.get();
if (options_.restore_rate_limit > 0) { if (rate_limiter) {
rate_limiter.reset(NewGenericRateLimiter(options_.restore_rate_limit));
copy_file_buffer_size_ = rate_limiter->GetSingleBurstBytes(); copy_file_buffer_size_ = rate_limiter->GetSingleBurstBytes();
} }
Status s; Status s;
@ -1081,7 +1089,7 @@ Status BackupEngineImpl::RestoreDBFromBackup(
Log(options_.info_log, "Restoring %s to %s\n", file.c_str(), dst.c_str()); Log(options_.info_log, "Restoring %s to %s\n", file.c_str(), dst.c_str());
CopyOrCreateWorkItem copy_or_create_work_item( CopyOrCreateWorkItem copy_or_create_work_item(
GetAbsolutePath(file), dst, "" /* contents */, backup_env_, db_env_, GetAbsolutePath(file), dst, "" /* contents */, backup_env_, db_env_,
false, rate_limiter.get(), 0 /* size_limit */); false, rate_limiter, 0 /* size_limit */);
RestoreAfterCopyOrCreateWorkItem after_copy_or_create_work_item( RestoreAfterCopyOrCreateWorkItem after_copy_or_create_work_item(
copy_or_create_work_item.result.get_future(), copy_or_create_work_item.result.get_future(),
file_info->checksum_value); file_info->checksum_value);

View File

@ -16,6 +16,7 @@
#include "db/filename.h" #include "db/filename.h"
#include "port/port.h" #include "port/port.h"
#include "port/stack_trace.h" #include "port/stack_trace.h"
#include "rocksdb/rate_limiter.h"
#include "rocksdb/transaction_log.h" #include "rocksdb/transaction_log.h"
#include "rocksdb/types.h" #include "rocksdb/types.h"
#include "rocksdb/utilities/backupable_db.h" #include "rocksdb/utilities/backupable_db.h"
@ -1117,45 +1118,58 @@ TEST_F(BackupableDBTest, KeepLogFiles) {
} }
TEST_F(BackupableDBTest, RateLimiting) { TEST_F(BackupableDBTest, RateLimiting) {
// iter 0 -- single threaded size_t const kMicrosPerSec = 1000 * 1000LL;
// iter 1 -- multi threaded uint64_t const MB = 1024 * 1024;
for (int iter = 0; iter < 2; ++iter) {
uint64_t const KB = 1024 * 1024;
size_t const kMicrosPerSec = 1000 * 1000LL;
std::vector<std::pair<uint64_t, uint64_t>> limits( const std::vector<std::pair<uint64_t, uint64_t>> limits(
{{KB, 5 * KB}, {2 * KB, 3 * KB}}); {{1 * MB, 5 * MB}, {2 * MB, 3 * MB}});
for (const auto& limit : limits) { std::shared_ptr<RateLimiter> backupThrottler(NewGenericRateLimiter(1));
// destroy old data std::shared_ptr<RateLimiter> restoreThrottler(NewGenericRateLimiter(1));
DestroyDB(dbname_, options_);
backupable_options_->backup_rate_limit = limit.first; for (bool makeThrottler : {false, true}) {
backupable_options_->restore_rate_limit = limit.second; if (makeThrottler) {
backupable_options_->max_background_operations = (iter == 0) ? 1 : 10; backupable_options_->backup_rate_limiter = backupThrottler;
options_.compression = kNoCompression; backupable_options_->restore_rate_limiter = restoreThrottler;
OpenDBAndBackupEngine(true); }
size_t bytes_written = FillDB(db_.get(), 0, 100000); // iter 0 -- single threaded
// iter 1 -- multi threaded
for (int iter = 0; iter < 2; ++iter) {
for (const auto& limit : limits) {
// destroy old data
DestroyDB(dbname_, Options());
if (makeThrottler) {
backupThrottler->SetBytesPerSecond(limit.first);
restoreThrottler->SetBytesPerSecond(limit.second);
} else {
backupable_options_->backup_rate_limit = limit.first;
backupable_options_->restore_rate_limit = limit.second;
}
backupable_options_->max_background_operations = (iter == 0) ? 1 : 10;
options_.compression = kNoCompression;
OpenDBAndBackupEngine(true);
size_t bytes_written = FillDB(db_.get(), 0, 100000);
auto start_backup = db_chroot_env_->NowMicros(); auto start_backup = db_chroot_env_->NowMicros();
ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), false)); ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), false));
auto backup_time = db_chroot_env_->NowMicros() - start_backup; auto backup_time = db_chroot_env_->NowMicros() - start_backup;
auto rate_limited_backup_time = (bytes_written * kMicrosPerSec) / auto rate_limited_backup_time =
backupable_options_->backup_rate_limit; (bytes_written * kMicrosPerSec) / limit.first;
ASSERT_GT(backup_time, 0.8 * rate_limited_backup_time); ASSERT_GT(backup_time, 0.8 * rate_limited_backup_time);
CloseDBAndBackupEngine(); CloseDBAndBackupEngine();
OpenBackupEngine(); OpenBackupEngine();
auto start_restore = db_chroot_env_->NowMicros(); auto start_restore = db_chroot_env_->NowMicros();
ASSERT_OK(backup_engine_->RestoreDBFromLatestBackup(dbname_, dbname_)); ASSERT_OK(backup_engine_->RestoreDBFromLatestBackup(dbname_, dbname_));
auto restore_time = db_chroot_env_->NowMicros() - start_restore; auto restore_time = db_chroot_env_->NowMicros() - start_restore;
CloseBackupEngine(); CloseBackupEngine();
auto rate_limited_restore_time = (bytes_written * kMicrosPerSec) / auto rate_limited_restore_time =
backupable_options_->restore_rate_limit; (bytes_written * kMicrosPerSec) / limit.second;
ASSERT_GT(restore_time, 0.8 * rate_limited_restore_time); ASSERT_GT(restore_time, 0.8 * rate_limited_restore_time);
AssertBackupConsistency(0, 0, 100000, 100010); AssertBackupConsistency(0, 0, 100000, 100010);
}
} }
} }
} }