mirror of
https://github.com/facebook/rocksdb.git
synced 2026-07-07 14:47:40 +08:00
Compare commits
8 Commits
f2c0eb41ef
...
v6.25.1
| Author | SHA1 | Date | |
|---|---|---|---|
| 307a65525a | |||
| 9e15f7bff3 | |||
| 14e3f8cd9b | |||
| 0fb79b0aca | |||
| ff033921d8 | |||
| 33f11b2625 | |||
| c92f7a29aa | |||
| e74dfee7fc |
+5
-4
@@ -1,8 +1,7 @@
|
||||
# Rocksdb Change Log
|
||||
## Unreleased
|
||||
## 6.25.1 (2021-09-28)
|
||||
### Bug Fixes
|
||||
### New Features
|
||||
### Public API change
|
||||
* Fixes a bug in directed IO mode when calling MultiGet() for blobs in the same blob file. The bug is caused by not sorting the blob read requests by file offsets.
|
||||
|
||||
## 6.25.0 (2021-09-20)
|
||||
### Bug Fixes
|
||||
@@ -18,6 +17,7 @@
|
||||
* Fix WAL log data corruption when using DBOptions.manual_wal_flush(true) and WriteOptions.sync(true) together. The sync WAL should work with locked log_write_mutex_.
|
||||
* Add checks for validity of the IO uring completion queue entries, and fail the BlockBasedTableReader MultiGet sub-batch if there's an invalid completion
|
||||
* Add an interface RocksDbIOUringEnable() that, if defined by the user, will allow them to enable/disable the use of IO uring by RocksDB
|
||||
* Fix the bug that when direct I/O is used and MultiRead() returns a short result, RandomAccessFileReader::MultiRead() still returns full size buffer, with returned short value together with some data in original buffer. This bug is unlikely cause incorrect results, because (1) since FileSystem layer is expected to retry on short result, returning short results is only possible when asking more bytes in the end of the file, which RocksDB doesn't do when using MultiRead(); (2) checksum is unlikely to match.
|
||||
|
||||
### New Features
|
||||
* RemoteCompaction's interface now includes `db_name`, `db_id`, `session_id`, which could help the user uniquely identify compaction job between db instances and sessions.
|
||||
@@ -31,14 +31,15 @@
|
||||
* Added new callback APIs `OnBlobFileCreationStarted`,`OnBlobFileCreated`and `OnBlobFileDeleted` in `EventListener` class of listener.h. It notifies listeners during creation/deletion of individual blob files in Integrated BlobDB. It also log blob file creation finished event and deletion event in LOG file.
|
||||
* Batch blob read requests for `DB::MultiGet` using `MultiRead`.
|
||||
* Add support for fallback to local compaction, the user can return `CompactionServiceJobStatus::kUseLocal` to instruct RocksDB to run the compaction locally instead of waiting for the remote compaction result.
|
||||
* Add built-in rate limiter's implementation of `RateLimiter::GetTotalPendingRequest(int64_t* total_pending_requests, const Env::IOPriority pri)` for the total number of requests that are pending for bytes in the rate limiter.
|
||||
|
||||
### Public API change
|
||||
* Remove obsolete implementation details FullKey and ParseFullKey from public API
|
||||
* Add a public API RateLimiter::GetTotalPendingRequests() for the total number of requests that are pending for bytes in the rate limiter.
|
||||
* Change `SstFileMetaData::size` from `size_t` to `uint64_t`.
|
||||
* Made Statistics extend the Customizable class and added a CreateFromString method. Implementations of Statistics need to be registered with the ObjectRegistry and to implement a Name() method in order to be created via this method.
|
||||
* Extended `FlushJobInfo` and `CompactionJobInfo` in listener.h to provide information about the blob files generated by a flush/compaction and garbage collected during compaction in Integrated BlobDB. Added struct members `blob_file_addition_infos` and `blob_file_garbage_infos` that contain this information.
|
||||
* Extended parameter `output_file_names` of `CompactFiles` API to also include paths of the blob files generated by the compaction in Integrated BlobDB.
|
||||
* Most `BackupEngine` functions now return `IOStatus` instead of `Status`. Most existing code should be compatible with this change but some calls might need to be updated.
|
||||
|
||||
## 6.24.0 (2021-08-20)
|
||||
### Bug Fixes
|
||||
|
||||
@@ -127,6 +127,197 @@ TEST_F(DBBlobBasicTest, MultiGetBlobs) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBBlobBasicTest, MultiGetWithDirectIO) {
|
||||
Options options = GetDefaultOptions();
|
||||
|
||||
// First, create an external SST file ["b"].
|
||||
const std::string file_path = dbname_ + "/test.sst";
|
||||
{
|
||||
SstFileWriter sst_file_writer(EnvOptions(), GetDefaultOptions());
|
||||
Status s = sst_file_writer.Open(file_path);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_OK(sst_file_writer.Put("b", "b_value"));
|
||||
ASSERT_OK(sst_file_writer.Finish());
|
||||
}
|
||||
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 1000;
|
||||
options.use_direct_reads = true;
|
||||
options.allow_ingest_behind = true;
|
||||
|
||||
// Open DB with fixed-prefix sst-partitioner so that compaction will cut
|
||||
// new table file when encountering a new key whose 1-byte prefix changes.
|
||||
constexpr size_t key_len = 1;
|
||||
options.sst_partitioner_factory =
|
||||
NewSstPartitionerFixedPrefixFactory(key_len);
|
||||
|
||||
Status s = TryReopen(options);
|
||||
if (s.IsInvalidArgument()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires direct IO support");
|
||||
return;
|
||||
}
|
||||
ASSERT_OK(s);
|
||||
|
||||
constexpr size_t num_keys = 3;
|
||||
constexpr size_t blob_size = 3000;
|
||||
|
||||
constexpr char first_key[] = "a";
|
||||
const std::string first_blob(blob_size, 'a');
|
||||
ASSERT_OK(Put(first_key, first_blob));
|
||||
|
||||
constexpr char second_key[] = "b";
|
||||
const std::string second_blob(2 * blob_size, 'b');
|
||||
ASSERT_OK(Put(second_key, second_blob));
|
||||
|
||||
constexpr char third_key[] = "d";
|
||||
const std::string third_blob(blob_size, 'd');
|
||||
ASSERT_OK(Put(third_key, third_blob));
|
||||
|
||||
// first_blob, second_blob and third_blob in the same blob file.
|
||||
// SST Blob file
|
||||
// L0 ["a", "b", "d"] |'aaaa', 'bbbb', 'dddd'|
|
||||
// | | | ^ ^ ^
|
||||
// | | | | | |
|
||||
// | | +---------|-------|--------+
|
||||
// | +-----------------|-------+
|
||||
// +-------------------------+
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
constexpr char fourth_key[] = "c";
|
||||
const std::string fourth_blob(blob_size, 'c');
|
||||
ASSERT_OK(Put(fourth_key, fourth_blob));
|
||||
// fourth_blob in another blob file.
|
||||
// SST Blob file SST Blob file
|
||||
// L0 ["a", "b", "d"] |'aaaa', 'bbbb', 'dddd'| ["c"] |'cccc'|
|
||||
// | | | ^ ^ ^ | ^
|
||||
// | | | | | | | |
|
||||
// | | +---------|-------|--------+ +-------+
|
||||
// | +-----------------|-------+
|
||||
// +-------------------------+
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), /*begin=*/nullptr,
|
||||
/*end=*/nullptr));
|
||||
|
||||
// Due to the above sst partitioner, we get 4 L1 files. The blob files are
|
||||
// unchanged.
|
||||
// |'aaaa', 'bbbb', 'dddd'| |'cccc'|
|
||||
// ^ ^ ^ ^
|
||||
// | | | |
|
||||
// L0 | | | |
|
||||
// L1 ["a"] ["b"] ["c"] | | ["d"] |
|
||||
// | | | | | |
|
||||
// | | +---------|-------|---------------+
|
||||
// | +-----------------|-------+
|
||||
// +-------------------------+
|
||||
ASSERT_EQ(4, NumTableFilesAtLevel(/*level=*/1));
|
||||
|
||||
{
|
||||
// Ingest the external SST file into bottommost level.
|
||||
std::vector<std::string> ext_files{file_path};
|
||||
IngestExternalFileOptions opts;
|
||||
opts.ingest_behind = true;
|
||||
ASSERT_OK(
|
||||
db_->IngestExternalFile(db_->DefaultColumnFamily(), ext_files, opts));
|
||||
}
|
||||
|
||||
// Now the database becomes as follows.
|
||||
// |'aaaa', 'bbbb', 'dddd'| |'cccc'|
|
||||
// ^ ^ ^ ^
|
||||
// | | | |
|
||||
// L0 | | | |
|
||||
// L1 ["a"] ["b"] ["c"] | | ["d"] |
|
||||
// | | | | | |
|
||||
// | | +---------|-------|---------------+
|
||||
// | +-----------------|-------+
|
||||
// +-------------------------+
|
||||
//
|
||||
// L6 ["b"]
|
||||
|
||||
{
|
||||
// Compact ["b"] to bottommost level.
|
||||
Slice begin = Slice(second_key);
|
||||
Slice end = Slice(second_key);
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
ASSERT_OK(db_->CompactRange(cro, &begin, &end));
|
||||
}
|
||||
|
||||
// |'aaaa', 'bbbb', 'dddd'| |'cccc'|
|
||||
// ^ ^ ^ ^
|
||||
// | | | |
|
||||
// L0 | | | |
|
||||
// L1 ["a"] ["c"] | | ["d"] |
|
||||
// | | | | |
|
||||
// | +---------|-------|---------------+
|
||||
// | +-----------------|-------+
|
||||
// +-------|-----------------+
|
||||
// |
|
||||
// L6 ["b"]
|
||||
ASSERT_EQ(3, NumTableFilesAtLevel(/*level=*/1));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(/*level=*/6));
|
||||
|
||||
bool called = false;
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"RandomAccessFileReader::MultiRead:AlignedReqs", [&](void* arg) {
|
||||
auto* aligned_reqs = static_cast<std::vector<FSReadRequest>*>(arg);
|
||||
assert(aligned_reqs);
|
||||
ASSERT_EQ(1, aligned_reqs->size());
|
||||
called = true;
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::array<Slice, num_keys> keys{{first_key, third_key, second_key}};
|
||||
|
||||
{
|
||||
std::array<PinnableSlice, num_keys> values;
|
||||
std::array<Status, num_keys> statuses;
|
||||
|
||||
// The MultiGet(), when constructing the KeyContexts, will process the keys
|
||||
// in such order: a, d, b. The reason is that ["a"] and ["d"] are in L1,
|
||||
// while ["b"] resides in L6.
|
||||
// Consequently, the original FSReadRequest list prepared by
|
||||
// Version::MultiGetblob() will be for "a", "d" and "b". It is unsorted as
|
||||
// follows:
|
||||
//
|
||||
// ["a", offset=30, len=3033],
|
||||
// ["d", offset=9096, len=3033],
|
||||
// ["b", offset=3063, len=6033]
|
||||
//
|
||||
// If we do not sort them before calling MultiRead() in DirectIO, then the
|
||||
// underlying IO merging logic will yield two requests.
|
||||
//
|
||||
// [offset=0, len=4096] (for "a")
|
||||
// [offset=0, len=12288] (result of merging the request for "d" and "b")
|
||||
//
|
||||
// We need to sort them in Version::MultiGetBlob() so that the underlying
|
||||
// IO merging logic in DirectIO mode works as expected. The correct
|
||||
// behavior will be one aligned request:
|
||||
//
|
||||
// [offset=0, len=12288]
|
||||
|
||||
db_->MultiGet(ReadOptions(), db_->DefaultColumnFamily(), num_keys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
ASSERT_TRUE(called);
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_EQ(values[0], first_blob);
|
||||
|
||||
ASSERT_OK(statuses[1]);
|
||||
ASSERT_EQ(values[1], third_blob);
|
||||
|
||||
ASSERT_OK(statuses[2]);
|
||||
ASSERT_EQ(values[2], second_blob);
|
||||
}
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBBlobBasicTest, MultiGetBlobsFromMultipleFiles) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.enable_blob_files = true;
|
||||
|
||||
@@ -3937,6 +3937,56 @@ TEST_F(DBTest, DISABLED_RateLimitingTest) {
|
||||
ASSERT_LT(ratio, 0.6);
|
||||
}
|
||||
|
||||
// This is a mocked customed rate limiter without implementing optional APIs
|
||||
// (e.g, RateLimiter::GetTotalPendingRequests())
|
||||
class MockedRateLimiterWithNoOptionalAPIImpl : public RateLimiter {
|
||||
public:
|
||||
MockedRateLimiterWithNoOptionalAPIImpl() {}
|
||||
|
||||
~MockedRateLimiterWithNoOptionalAPIImpl() override {}
|
||||
|
||||
void SetBytesPerSecond(int64_t bytes_per_second) override {
|
||||
(void)bytes_per_second;
|
||||
}
|
||||
|
||||
using RateLimiter::Request;
|
||||
void Request(const int64_t bytes, const Env::IOPriority pri,
|
||||
Statistics* stats) override {
|
||||
(void)bytes;
|
||||
(void)pri;
|
||||
(void)stats;
|
||||
}
|
||||
|
||||
int64_t GetSingleBurstBytes() const override { return 200; }
|
||||
|
||||
int64_t GetTotalBytesThrough(
|
||||
const Env::IOPriority pri = Env::IO_TOTAL) const override {
|
||||
(void)pri;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64_t GetTotalRequests(
|
||||
const Env::IOPriority pri = Env::IO_TOTAL) const override {
|
||||
(void)pri;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64_t GetBytesPerSecond() const override { return 0; }
|
||||
};
|
||||
|
||||
// To test that customed rate limiter not implementing optional APIs (e.g,
|
||||
// RateLimiter::GetTotalPendingRequests()) works fine with RocksDB basic
|
||||
// operations (e.g, Put, Get, Flush)
|
||||
TEST_F(DBTest, CustomedRateLimiterWithNoOptionalAPIImplTest) {
|
||||
Options options = CurrentOptions();
|
||||
options.rate_limiter.reset(new MockedRateLimiterWithNoOptionalAPIImpl());
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("abc", "def"));
|
||||
ASSERT_EQ(Get("abc"), "def");
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(Get("abc"), "def");
|
||||
}
|
||||
|
||||
TEST_F(DBTest, TableOptionsSanitizeTest) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
|
||||
+9
-2
@@ -1866,7 +1866,7 @@ Status Version::GetBlob(const ReadOptions& read_options, const Slice& user_key,
|
||||
|
||||
void Version::MultiGetBlob(
|
||||
const ReadOptions& read_options, MultiGetRange& range,
|
||||
const std::unordered_map<uint64_t, BlobReadRequests>& blob_rqs) {
|
||||
std::unordered_map<uint64_t, BlobReadRequests>& blob_rqs) {
|
||||
if (read_options.read_tier == kBlockCacheTier) {
|
||||
Status s = Status::Incomplete("Cannot read blob(s): no disk I/O allowed");
|
||||
for (const auto& elem : blob_rqs) {
|
||||
@@ -1916,7 +1916,14 @@ void Version::MultiGetBlob(
|
||||
const CompressionType compression =
|
||||
blob_file_reader.GetValue()->GetCompressionType();
|
||||
|
||||
// TODO: sort blobs_in_file by file offset.
|
||||
// sort blobs_in_file by file offset.
|
||||
std::sort(
|
||||
blobs_in_file.begin(), blobs_in_file.end(),
|
||||
[](const BlobReadRequest& lhs, const BlobReadRequest& rhs) -> bool {
|
||||
assert(lhs.first.file_number() == rhs.first.file_number());
|
||||
return lhs.first.offset() < rhs.first.offset();
|
||||
});
|
||||
|
||||
autovector<std::reference_wrapper<const KeyContext>> blob_read_key_contexts;
|
||||
autovector<std::reference_wrapper<const Slice>> user_keys;
|
||||
autovector<uint64_t> offsets;
|
||||
|
||||
+5
-5
@@ -713,11 +713,11 @@ class Version {
|
||||
const BlobIndex& blob_index, PinnableSlice* value,
|
||||
uint64_t* bytes_read) const;
|
||||
|
||||
using BlobReadRequests = std::vector<
|
||||
std::pair<BlobIndex, std::reference_wrapper<const KeyContext>>>;
|
||||
void MultiGetBlob(
|
||||
const ReadOptions& read_options, MultiGetRange& range,
|
||||
const std::unordered_map<uint64_t, BlobReadRequests>& blob_rqs);
|
||||
using BlobReadRequest =
|
||||
std::pair<BlobIndex, std::reference_wrapper<const KeyContext>>;
|
||||
using BlobReadRequests = std::vector<BlobReadRequest>;
|
||||
void MultiGetBlob(const ReadOptions& read_options, MultiGetRange& range,
|
||||
std::unordered_map<uint64_t, BlobReadRequests>& blob_rqs);
|
||||
|
||||
// Loads some stats information from files. Call without mutex held. It needs
|
||||
// to be called before applying the version to the version set.
|
||||
|
||||
@@ -296,8 +296,14 @@ IOStatus RandomAccessFileReader::MultiRead(const IOOptions& opts,
|
||||
r.status = fs_r.status;
|
||||
if (r.status.ok()) {
|
||||
uint64_t offset = r.offset - fs_r.offset;
|
||||
size_t len = std::min(r.len, static_cast<size_t>(fs_r.len - offset));
|
||||
r.result = Slice(fs_r.scratch + offset, len);
|
||||
if (fs_r.result.size() <= offset) {
|
||||
// No byte in the read range is returned.
|
||||
r.result = Slice();
|
||||
} else {
|
||||
size_t len = std::min(
|
||||
r.len, static_cast<size_t>(fs_r.result.size() - offset));
|
||||
r.result = Slice(fs_r.scratch + offset, len);
|
||||
}
|
||||
} else {
|
||||
r.result = Slice();
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
#include "rocksdb/status.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -90,8 +91,18 @@ class RateLimiter {
|
||||
const Env::IOPriority pri = Env::IO_TOTAL) const = 0;
|
||||
|
||||
// Total # of requests that are pending for bytes in rate limiter
|
||||
virtual int64_t GetTotalPendingRequests(
|
||||
const Env::IOPriority pri = Env::IO_TOTAL) const = 0;
|
||||
// For convenience, this function is supported by the RateLimiter returned
|
||||
// by NewGenericRateLimiter but is not required by RocksDB.
|
||||
//
|
||||
// REQUIRED: total_pending_request != nullptr
|
||||
virtual Status GetTotalPendingRequests(
|
||||
int64_t* total_pending_requests,
|
||||
const Env::IOPriority pri = Env::IO_TOTAL) const {
|
||||
assert(total_pending_requests != nullptr);
|
||||
(void)total_pending_requests;
|
||||
(void)pri;
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
virtual int64_t GetBytesPerSecond() const = 0;
|
||||
|
||||
|
||||
@@ -392,10 +392,10 @@ class BackupEngineReadOnlyBase {
|
||||
virtual Status GetBackupInfo(BackupID backup_id, BackupInfo* backup_info,
|
||||
bool include_file_details = false) const = 0;
|
||||
|
||||
// Returns info about backups in backup_info
|
||||
// Returns info about non-corrupt backups in backup_infos.
|
||||
// Setting include_file_details=true provides information about each
|
||||
// backed-up file in BackupInfo::file_details and more.
|
||||
virtual void GetBackupInfo(std::vector<BackupInfo>* backup_info,
|
||||
virtual void GetBackupInfo(std::vector<BackupInfo>* backup_infos,
|
||||
bool include_file_details = false) const = 0;
|
||||
|
||||
// Returns info about corrupt backups in corrupt_backups.
|
||||
@@ -475,13 +475,13 @@ class BackupEngineAppendOnlyBase {
|
||||
// Captures the state of the database by creating a new (latest) backup.
|
||||
// On success (OK status), the BackupID of the new backup is saved to
|
||||
// *new_backup_id when not nullptr.
|
||||
virtual Status CreateNewBackup(const CreateBackupOptions& options, DB* db,
|
||||
BackupID* new_backup_id = nullptr) {
|
||||
virtual IOStatus CreateNewBackup(const CreateBackupOptions& options, DB* db,
|
||||
BackupID* new_backup_id = nullptr) {
|
||||
return CreateNewBackupWithMetadata(options, db, "", new_backup_id);
|
||||
}
|
||||
|
||||
// keep here for backward compatibility.
|
||||
virtual Status CreateNewBackup(
|
||||
virtual IOStatus CreateNewBackup(
|
||||
DB* db, bool flush_before_backup = false,
|
||||
std::function<void()> progress_callback = []() {}) {
|
||||
CreateBackupOptions options;
|
||||
@@ -575,12 +575,12 @@ class BackupEngine : public BackupEngineReadOnlyBase,
|
||||
|
||||
// BackupEngineOptions have to be the same as the ones used in previous
|
||||
// BackupEngines for the same backup directory.
|
||||
static Status Open(const BackupEngineOptions& options, Env* db_env,
|
||||
BackupEngine** backup_engine_ptr);
|
||||
static IOStatus Open(const BackupEngineOptions& options, Env* db_env,
|
||||
BackupEngine** backup_engine_ptr);
|
||||
|
||||
// keep for backward compatibility.
|
||||
static Status Open(Env* db_env, const BackupEngineOptions& options,
|
||||
BackupEngine** backup_engine_ptr) {
|
||||
static IOStatus Open(Env* db_env, const BackupEngineOptions& options,
|
||||
BackupEngine** backup_engine_ptr) {
|
||||
return BackupEngine::Open(options, db_env, backup_engine_ptr);
|
||||
}
|
||||
|
||||
@@ -601,11 +601,11 @@ class BackupEngineReadOnly : public BackupEngineReadOnlyBase {
|
||||
public:
|
||||
virtual ~BackupEngineReadOnly() {}
|
||||
|
||||
static Status Open(const BackupEngineOptions& options, Env* db_env,
|
||||
BackupEngineReadOnly** backup_engine_ptr);
|
||||
static IOStatus Open(const BackupEngineOptions& options, Env* db_env,
|
||||
BackupEngineReadOnly** backup_engine_ptr);
|
||||
// keep for backward compatibility.
|
||||
static Status Open(Env* db_env, const BackupEngineOptions& options,
|
||||
BackupEngineReadOnly** backup_engine_ptr) {
|
||||
static IOStatus Open(Env* db_env, const BackupEngineOptions& options,
|
||||
BackupEngineReadOnly** backup_engine_ptr) {
|
||||
return BackupEngineReadOnly::Open(options, db_env, backup_engine_ptr);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
#define ROCKSDB_MAJOR 6
|
||||
#define ROCKSDB_MINOR 25
|
||||
#define ROCKSDB_PATCH 0
|
||||
#define ROCKSDB_PATCH 1
|
||||
|
||||
// Do not use these. We made the mistake of declaring macros starting with
|
||||
// double underscore. Now we have to live with our choice. We'll deprecate these
|
||||
|
||||
+8
-3
@@ -17,6 +17,7 @@
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/rate_limiter.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/system_clock.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/random.h"
|
||||
@@ -72,17 +73,21 @@ class GenericRateLimiter : public RateLimiter {
|
||||
return total_requests_[pri];
|
||||
}
|
||||
|
||||
virtual int64_t GetTotalPendingRequests(
|
||||
virtual Status GetTotalPendingRequests(
|
||||
int64_t* total_pending_requests,
|
||||
const Env::IOPriority pri = Env::IO_TOTAL) const override {
|
||||
assert(total_pending_requests != nullptr);
|
||||
MutexLock g(&request_mutex_);
|
||||
if (pri == Env::IO_TOTAL) {
|
||||
int64_t total_pending_requests_sum = 0;
|
||||
for (int i = Env::IO_LOW; i < Env::IO_TOTAL; ++i) {
|
||||
total_pending_requests_sum += static_cast<int64_t>(queue_[i].size());
|
||||
}
|
||||
return total_pending_requests_sum;
|
||||
*total_pending_requests = total_pending_requests_sum;
|
||||
} else {
|
||||
*total_pending_requests = static_cast<int64_t>(queue_[pri].size());
|
||||
}
|
||||
return static_cast<int64_t>(queue_[pri].size());
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual int64_t GetBytesPerSecond() const override {
|
||||
|
||||
+31
-12
@@ -100,9 +100,11 @@ TEST_F(RateLimiterTest, GetTotalPendingRequests) {
|
||||
std::unique_ptr<RateLimiter> limiter(NewGenericRateLimiter(
|
||||
200 /* rate_bytes_per_sec */, 1000 * 1000 /* refill_period_us */,
|
||||
10 /* fairness */));
|
||||
int64_t total_pending_requests = 0;
|
||||
for (int i = Env::IO_LOW; i <= Env::IO_TOTAL; ++i) {
|
||||
ASSERT_EQ(limiter->GetTotalPendingRequests(static_cast<Env::IOPriority>(i)),
|
||||
0);
|
||||
ASSERT_OK(limiter->GetTotalPendingRequests(
|
||||
&total_pending_requests, static_cast<Env::IOPriority>(i)));
|
||||
ASSERT_EQ(total_pending_requests, 0);
|
||||
}
|
||||
// This is a variable for making sure the following callback is called
|
||||
// and the assertions in it are indeed excuted
|
||||
@@ -113,11 +115,23 @@ TEST_F(RateLimiterTest, GetTotalPendingRequests) {
|
||||
// We temporarily unlock the mutex so that the following
|
||||
// GetTotalPendingRequests() can acquire it
|
||||
request_mutex->Unlock();
|
||||
EXPECT_EQ(limiter->GetTotalPendingRequests(Env::IO_USER), 1);
|
||||
EXPECT_EQ(limiter->GetTotalPendingRequests(Env::IO_HIGH), 0);
|
||||
EXPECT_EQ(limiter->GetTotalPendingRequests(Env::IO_MID), 0);
|
||||
EXPECT_EQ(limiter->GetTotalPendingRequests(Env::IO_LOW), 0);
|
||||
EXPECT_EQ(limiter->GetTotalPendingRequests(Env::IO_TOTAL), 1);
|
||||
for (int i = Env::IO_LOW; i <= Env::IO_TOTAL; ++i) {
|
||||
EXPECT_OK(limiter->GetTotalPendingRequests(
|
||||
&total_pending_requests, static_cast<Env::IOPriority>(i)))
|
||||
<< "Failed to return total pending requests for priority level = "
|
||||
<< static_cast<Env::IOPriority>(i);
|
||||
if (i == Env::IO_USER || i == Env::IO_TOTAL) {
|
||||
EXPECT_EQ(total_pending_requests, 1)
|
||||
<< "Failed to correctly return total pending requests for "
|
||||
"priority level = "
|
||||
<< static_cast<Env::IOPriority>(i);
|
||||
} else {
|
||||
EXPECT_EQ(total_pending_requests, 0)
|
||||
<< "Failed to correctly return total pending requests for "
|
||||
"priority level = "
|
||||
<< static_cast<Env::IOPriority>(i);
|
||||
}
|
||||
}
|
||||
// We lock the mutex again so that the request thread can resume running
|
||||
// with the mutex locked
|
||||
request_mutex->Lock();
|
||||
@@ -128,11 +142,16 @@ TEST_F(RateLimiterTest, GetTotalPendingRequests) {
|
||||
limiter->Request(200, Env::IO_USER, nullptr /* stats */,
|
||||
RateLimiter::OpType::kWrite);
|
||||
ASSERT_EQ(nonzero_pending_requests_verified, true);
|
||||
EXPECT_EQ(limiter->GetTotalPendingRequests(Env::IO_USER), 0);
|
||||
EXPECT_EQ(limiter->GetTotalPendingRequests(Env::IO_HIGH), 0);
|
||||
EXPECT_EQ(limiter->GetTotalPendingRequests(Env::IO_MID), 0);
|
||||
EXPECT_EQ(limiter->GetTotalPendingRequests(Env::IO_LOW), 0);
|
||||
EXPECT_EQ(limiter->GetTotalPendingRequests(Env::IO_TOTAL), 0);
|
||||
for (int i = Env::IO_LOW; i <= Env::IO_TOTAL; ++i) {
|
||||
EXPECT_OK(limiter->GetTotalPendingRequests(&total_pending_requests,
|
||||
static_cast<Env::IOPriority>(i)))
|
||||
<< "Failed to return total pending requests for priority level = "
|
||||
<< static_cast<Env::IOPriority>(i);
|
||||
EXPECT_EQ(total_pending_requests, 0)
|
||||
<< "Failed to correctly return total pending requests for priority "
|
||||
"level = "
|
||||
<< static_cast<Env::IOPriority>(i);
|
||||
}
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearCallBack(
|
||||
"GenericRateLimiter::Request:PostEnqueueRequest");
|
||||
|
||||
@@ -895,7 +895,7 @@ class BackupEngineImplThreadSafe : public BackupEngine,
|
||||
}
|
||||
|
||||
// Not public API but needed
|
||||
Status Initialize() {
|
||||
IOStatus Initialize() {
|
||||
// No locking needed
|
||||
return impl_.Initialize();
|
||||
}
|
||||
@@ -912,8 +912,8 @@ class BackupEngineImplThreadSafe : public BackupEngine,
|
||||
BackupEngineImpl impl_;
|
||||
};
|
||||
|
||||
Status BackupEngine::Open(const BackupEngineOptions& options, Env* env,
|
||||
BackupEngine** backup_engine_ptr) {
|
||||
IOStatus BackupEngine::Open(const BackupEngineOptions& options, Env* env,
|
||||
BackupEngine** backup_engine_ptr) {
|
||||
std::unique_ptr<BackupEngineImplThreadSafe> backup_engine(
|
||||
new BackupEngineImplThreadSafe(options, env));
|
||||
auto s = backup_engine->Initialize();
|
||||
@@ -922,7 +922,7 @@ Status BackupEngine::Open(const BackupEngineOptions& options, Env* env,
|
||||
return s;
|
||||
}
|
||||
*backup_engine_ptr = backup_engine.release();
|
||||
return Status::OK();
|
||||
return IOStatus::OK();
|
||||
}
|
||||
|
||||
BackupEngineImpl::BackupEngineImpl(const BackupEngineOptions& options,
|
||||
@@ -2986,10 +2986,11 @@ IOStatus BackupEngineImpl::BackupMeta::StoreToFile(
|
||||
return io_s;
|
||||
}
|
||||
|
||||
Status BackupEngineReadOnly::Open(const BackupEngineOptions& options, Env* env,
|
||||
BackupEngineReadOnly** backup_engine_ptr) {
|
||||
IOStatus BackupEngineReadOnly::Open(const BackupEngineOptions& options,
|
||||
Env* env,
|
||||
BackupEngineReadOnly** backup_engine_ptr) {
|
||||
if (options.destroy_old_data) {
|
||||
return Status::InvalidArgument(
|
||||
return IOStatus::InvalidArgument(
|
||||
"Can't destroy old data with ReadOnly BackupEngine");
|
||||
}
|
||||
std::unique_ptr<BackupEngineImplThreadSafe> backup_engine(
|
||||
@@ -3000,7 +3001,7 @@ Status BackupEngineReadOnly::Open(const BackupEngineOptions& options, Env* env,
|
||||
return s;
|
||||
}
|
||||
*backup_engine_ptr = backup_engine.release();
|
||||
return Status::OK();
|
||||
return IOStatus::OK();
|
||||
}
|
||||
|
||||
void TEST_EnableWriteFutureSchemaVersion2(
|
||||
|
||||
Reference in New Issue
Block a user