Add IteratorTraceExecutionResult for iterator related trace records. (#8687)

Summary:
- Allow to get `Valid()`, `status()`, `key()` and `value()` of an iterator from `IteratorTraceExecutionResult`.
- Move lower bound and upper bound from `IteratorSeekQueryTraceRecord` to `IteratorQueryTraceRecord`.

Added test in `DBTest2.TraceAndReplay`.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/8687

Reviewed By: zhichao-cao

Differential Revision: D30457630

Pulled By: autopear

fbshipit-source-id: be433099a25895b3aa6f0c00f95ad7b1d7489c1d
This commit is contained in:
Merlin Mao 2021-08-20 15:32:55 -07:00 committed by Facebook GitHub Bot
parent f35042ca40
commit baf22b4ee6
6 changed files with 188 additions and 87 deletions

View File

@ -4256,13 +4256,6 @@ class TraceExecutionResultHandler : public TraceRecordResult::Handler {
writes_++;
break;
}
case kTraceIteratorSeek:
case kTraceIteratorSeekForPrev: {
total_latency_ += result.GetLatency();
cnt_++;
seeks_++;
break;
}
default:
return Status::Corruption("Type mismatch.");
}
@ -4309,6 +4302,25 @@ class TraceExecutionResultHandler : public TraceRecordResult::Handler {
return Status::OK();
}
virtual Status Handle(const IteratorTraceExecutionResult& result) override {
if (result.GetStartTimestamp() > result.GetEndTimestamp()) {
return Status::InvalidArgument("Invalid timestamps.");
}
result.GetStatus().PermitUncheckedError();
switch (result.GetTraceType()) {
case kTraceIteratorSeek:
case kTraceIteratorSeekForPrev: {
total_latency_ += result.GetLatency();
cnt_++;
seeks_++;
break;
}
default:
return Status::Corruption("Type mismatch.");
}
return Status::OK();
}
void Reset() {
total_latency_ = 0;
cnt_ = 0;
@ -4644,23 +4656,39 @@ TEST_F(DBTest2, TraceAndManualReplay) {
continue;
}
if (s.ok()) {
if (record->GetTraceType() == kTraceIteratorSeek ||
record->GetTraceType() == kTraceIteratorSeekForPrev) {
IteratorSeekQueryTraceRecord* iter_r =
dynamic_cast<IteratorSeekQueryTraceRecord*>(record.get());
// Check if lower/upper bounds are correctly saved and decoded.
lower_bound = iter_r->GetLowerBound();
if (!lower_bound.empty()) {
ASSERT_EQ(lower_bound.ToString(), "iter-1");
}
upper_bound = iter_r->GetUpperBound();
if (!upper_bound.empty()) {
ASSERT_EQ(upper_bound.ToString(), "iter-3");
}
}
ASSERT_OK(replayer->Execute(record, &result));
if (result != nullptr) {
ASSERT_OK(result->Accept(&res_handler));
if (record->GetTraceType() == kTraceIteratorSeek ||
record->GetTraceType() == kTraceIteratorSeekForPrev) {
IteratorSeekQueryTraceRecord* iter_rec =
dynamic_cast<IteratorSeekQueryTraceRecord*>(record.get());
IteratorTraceExecutionResult* iter_res =
dynamic_cast<IteratorTraceExecutionResult*>(result.get());
// Check if lower/upper bounds are correctly saved and decoded.
std::string lower_str = iter_rec->GetLowerBound().ToString();
std::string upper_str = iter_rec->GetUpperBound().ToString();
std::string iter_key = iter_res->GetKey().ToString();
std::string iter_value = iter_res->GetValue().ToString();
if (!lower_str.empty() && !upper_str.empty()) {
ASSERT_EQ(lower_str, "iter-1");
ASSERT_EQ(upper_str, "iter-3");
if (iter_res->GetValid()) {
// If iterator is valid, then lower_bound <= key < upper_bound.
ASSERT_GE(iter_key, lower_str);
ASSERT_LT(iter_key, upper_str);
} else {
// If iterator is invalid, then
// key < lower_bound or key >= upper_bound.
ASSERT_TRUE(iter_key < lower_str || iter_key >= upper_str);
}
}
// If iterator is invalid, the key and value should be empty.
if (!iter_res->GetValid()) {
ASSERT_TRUE(iter_key.empty());
ASSERT_TRUE(iter_value.empty());
}
}
result.reset();
}
}

View File

@ -65,19 +65,15 @@ class TraceRecord {
public:
virtual ~Handler() = default;
// Handle WriteQueryTraceRecord
virtual Status Handle(const WriteQueryTraceRecord& record,
std::unique_ptr<TraceRecordResult>* result) = 0;
// Handle GetQueryTraceRecord
virtual Status Handle(const GetQueryTraceRecord& record,
std::unique_ptr<TraceRecordResult>* result) = 0;
// Handle IteratorSeekQueryTraceRecord
virtual Status Handle(const IteratorSeekQueryTraceRecord& record,
std::unique_ptr<TraceRecordResult>* result) = 0;
// Handle MultiGetQueryTraceRecord
virtual Status Handle(const MultiGetQueryTraceRecord& record,
std::unique_ptr<TraceRecordResult>* result) = 0;
};
@ -152,6 +148,23 @@ class GetQueryTraceRecord : public QueryTraceRecord {
class IteratorQueryTraceRecord : public QueryTraceRecord {
public:
explicit IteratorQueryTraceRecord(uint64_t timestamp);
IteratorQueryTraceRecord(PinnableSlice&& lower_bound,
PinnableSlice&& upper_bound, uint64_t timestamp);
IteratorQueryTraceRecord(const std::string& lower_bound,
const std::string& upper_bound, uint64_t timestamp);
virtual ~IteratorQueryTraceRecord() override;
// Get the iterator's lower/upper bound. They may be used in ReadOptions to
// create an Iterator instance.
virtual Slice GetLowerBound() const;
virtual Slice GetUpperBound() const;
private:
PinnableSlice lower_;
PinnableSlice upper_;
};
// Trace record for Iterator::Seek() and Iterator::SeekForPrev() operation.
@ -193,12 +206,6 @@ class IteratorSeekQueryTraceRecord : public IteratorQueryTraceRecord {
// Key to seek to.
virtual Slice GetKey() const;
// Iterate lower bound.
virtual Slice GetLowerBound() const;
// Iterate upper bound.
virtual Slice GetUpperBound() const;
Status Accept(Handler* handler,
std::unique_ptr<TraceRecordResult>* result) override;
@ -206,8 +213,6 @@ class IteratorSeekQueryTraceRecord : public IteratorQueryTraceRecord {
SeekType type_;
uint32_t cf_id_;
PinnableSlice key_;
PinnableSlice lower_;
PinnableSlice upper_;
};
// Trace record for DB::MultiGet() operation.

View File

@ -9,11 +9,13 @@
#include <vector>
#include "rocksdb/rocksdb_namespace.h"
#include "rocksdb/slice.h"
#include "rocksdb/status.h"
#include "rocksdb/trace_record.h"
namespace ROCKSDB_NAMESPACE {
class IteratorTraceExecutionResult;
class MultiValuesTraceExecutionResult;
class SingleValueTraceExecutionResult;
class StatusOnlyTraceExecutionResult;
@ -34,42 +36,14 @@ class TraceRecordResult {
public:
virtual ~Handler() = default;
// Handle StatusOnlyTraceExecutionResult
virtual Status Handle(const StatusOnlyTraceExecutionResult& result) = 0;
// Handle SingleValueTraceExecutionResult
virtual Status Handle(const SingleValueTraceExecutionResult& result) = 0;
// Handle MultiValuesTraceExecutionResult
virtual Status Handle(const MultiValuesTraceExecutionResult& result) = 0;
};
/*
* Example handler to just print the trace record execution results.
*
* class ResultPrintHandler : public TraceRecordResult::Handler {
* public:
* ResultPrintHandler();
* ~ResultPrintHandler() override {}
*
* Status Handle(const StatusOnlyTraceExecutionResult& result) override {
* std::cout << "Status: " << result.GetStatus().ToString() << std::endl;
* }
*
* Status Handle(const SingleValueTraceExecutionResult& result) override {
* std::cout << "Status: " << result.GetStatus().ToString()
* << ", value: " << result.GetValue() << std::endl;
* }
*
* Status Handle(const MultiValuesTraceExecutionResult& result) override {
* size_t size = result.GetMultiStatus().size();
* for (size_t i = 0; i < size; i++) {
* std::cout << "Status: " << result.GetMultiStatus()[i].ToString()
* << ", value: " << result.GetValues()[i] << std::endl;
* }
* }
* };
* */
virtual Status Handle(const IteratorTraceExecutionResult& result) = 0;
};
// Accept the handler.
virtual Status Accept(Handler* handler) = 0;
@ -106,8 +80,7 @@ class TraceExecutionResult : public TraceRecordResult {
};
// Result for operations that only return a single Status.
// Example operations: DB::Write(), Iterator::Seek() and
// Iterator::SeekForPrev().
// Example operation: DB::Write()
class StatusOnlyTraceExecutionResult : public TraceExecutionResult {
public:
StatusOnlyTraceExecutionResult(Status status, uint64_t start_timestamp,
@ -138,7 +111,7 @@ class SingleValueTraceExecutionResult : public TraceExecutionResult {
virtual ~SingleValueTraceExecutionResult() override;
// Return status of DB::Get(), etc.
// Return status of DB::Get().
virtual const Status& GetStatus() const;
// Value for the searched key.
@ -151,7 +124,7 @@ class SingleValueTraceExecutionResult : public TraceExecutionResult {
std::string value_;
};
// Result for operations that return multiple Status(es) and values.
// Result for operations that return multiple Status(es) and values as vectors.
// Example operation: DB::MultiGet()
class MultiValuesTraceExecutionResult : public TraceExecutionResult {
public:
@ -162,7 +135,7 @@ class MultiValuesTraceExecutionResult : public TraceExecutionResult {
virtual ~MultiValuesTraceExecutionResult() override;
// Returned Status(es) of DB::MultiGet(), etc.
// Returned Status(es) of DB::MultiGet().
virtual const std::vector<Status>& GetMultiStatus() const;
// Returned values for the searched keys.
@ -175,4 +148,40 @@ class MultiValuesTraceExecutionResult : public TraceExecutionResult {
std::vector<std::string> values_;
};
// Result for Iterator operations.
// Example operations: Iterator::Seek(), Iterator::SeekForPrev()
class IteratorTraceExecutionResult : public TraceExecutionResult {
public:
IteratorTraceExecutionResult(bool valid, Status status, PinnableSlice&& key,
PinnableSlice&& value, uint64_t start_timestamp,
uint64_t end_timestamp, TraceType trace_type);
IteratorTraceExecutionResult(bool valid, Status status,
const std::string& key, const std::string& value,
uint64_t start_timestamp, uint64_t end_timestamp,
TraceType trace_type);
virtual ~IteratorTraceExecutionResult() override;
// Return if the Iterator is valid.
virtual bool GetValid() const;
// Return the status of the Iterator.
virtual const Status& GetStatus() const;
// Key of the current iterating entry, empty if GetValid() is false.
virtual Slice GetKey() const;
// Value of the current iterating entry, empty if GetValid() is false.
virtual Slice GetValue() const;
virtual Status Accept(Handler* handler) override;
private:
bool valid_;
Status status_;
PinnableSlice key_;
PinnableSlice value_;
};
} // namespace ROCKSDB_NAMESPACE

View File

@ -82,6 +82,27 @@ Status GetQueryTraceRecord::Accept(Handler* handler,
IteratorQueryTraceRecord::IteratorQueryTraceRecord(uint64_t timestamp)
: QueryTraceRecord(timestamp) {}
IteratorQueryTraceRecord::IteratorQueryTraceRecord(PinnableSlice&& lower_bound,
PinnableSlice&& upper_bound,
uint64_t timestamp)
: QueryTraceRecord(timestamp),
lower_(std::move(lower_bound)),
upper_(std::move(upper_bound)) {}
IteratorQueryTraceRecord::IteratorQueryTraceRecord(
const std::string& lower_bound, const std::string& upper_bound,
uint64_t timestamp)
: QueryTraceRecord(timestamp) {
lower_.PinSelf(lower_bound);
upper_.PinSelf(upper_bound);
}
IteratorQueryTraceRecord::~IteratorQueryTraceRecord() {}
Slice IteratorQueryTraceRecord::GetLowerBound() const { return Slice(lower_); }
Slice IteratorQueryTraceRecord::GetUpperBound() const { return Slice(upper_); }
// IteratorSeekQueryTraceRecord
IteratorSeekQueryTraceRecord::IteratorSeekQueryTraceRecord(
SeekType seek_type, uint32_t column_family_id, PinnableSlice&& key,
@ -104,23 +125,20 @@ IteratorSeekQueryTraceRecord::IteratorSeekQueryTraceRecord(
SeekType seek_type, uint32_t column_family_id, PinnableSlice&& key,
PinnableSlice&& lower_bound, PinnableSlice&& upper_bound,
uint64_t timestamp)
: IteratorQueryTraceRecord(timestamp),
: IteratorQueryTraceRecord(std::move(lower_bound), std::move(upper_bound),
timestamp),
type_(seek_type),
cf_id_(column_family_id),
key_(std::move(key)),
lower_(std::move(lower_bound)),
upper_(std::move(upper_bound)) {}
key_(std::move(key)) {}
IteratorSeekQueryTraceRecord::IteratorSeekQueryTraceRecord(
SeekType seek_type, uint32_t column_family_id, const std::string& key,
const std::string& lower_bound, const std::string& upper_bound,
uint64_t timestamp)
: IteratorQueryTraceRecord(timestamp),
: IteratorQueryTraceRecord(lower_bound, upper_bound, timestamp),
type_(seek_type),
cf_id_(column_family_id) {
key_.PinSelf(key);
lower_.PinSelf(lower_bound);
upper_.PinSelf(upper_bound);
}
IteratorSeekQueryTraceRecord::~IteratorSeekQueryTraceRecord() { key_.clear(); }
@ -140,14 +158,6 @@ uint32_t IteratorSeekQueryTraceRecord::GetColumnFamilyID() const {
Slice IteratorSeekQueryTraceRecord::GetKey() const { return Slice(key_); }
Slice IteratorSeekQueryTraceRecord::GetLowerBound() const {
return Slice(lower_);
}
Slice IteratorSeekQueryTraceRecord::GetUpperBound() const {
return Slice(upper_);
}
Status IteratorSeekQueryTraceRecord::Accept(
Handler* handler, std::unique_ptr<TraceRecordResult>* result) {
assert(handler != nullptr);

View File

@ -120,12 +120,21 @@ Status TraceExecutionHandler::Handle(
uint64_t end = clock_->NowMicros();
Status s = single_iter->status();
delete single_iter;
if (s.ok() && result != nullptr) {
result->reset(new StatusOnlyTraceExecutionResult(s, start, end,
record.GetTraceType()));
if (single_iter->Valid()) {
PinnableSlice ps_key;
ps_key.PinSelf(single_iter->key());
PinnableSlice ps_value;
ps_value.PinSelf(single_iter->value());
result->reset(new IteratorTraceExecutionResult(
true, s, std::move(ps_key), std::move(ps_value), start, end,
record.GetTraceType()));
} else {
result->reset(new IteratorTraceExecutionResult(
false, s, "", "", start, end, record.GetTraceType()));
}
}
delete single_iter;
return s;
}

View File

@ -103,4 +103,44 @@ Status MultiValuesTraceExecutionResult::Accept(Handler* handler) {
return handler->Handle(*this);
}
// IteratorTraceExecutionResult
IteratorTraceExecutionResult::IteratorTraceExecutionResult(
bool valid, Status status, PinnableSlice&& key, PinnableSlice&& value,
uint64_t start_timestamp, uint64_t end_timestamp, TraceType trace_type)
: TraceExecutionResult(start_timestamp, end_timestamp, trace_type),
valid_(valid),
status_(std::move(status)),
key_(std::move(key)),
value_(std::move(value)) {}
IteratorTraceExecutionResult::IteratorTraceExecutionResult(
bool valid, Status status, const std::string& key, const std::string& value,
uint64_t start_timestamp, uint64_t end_timestamp, TraceType trace_type)
: TraceExecutionResult(start_timestamp, end_timestamp, trace_type),
valid_(valid),
status_(std::move(status)) {
key_.PinSelf(key);
value_.PinSelf(value);
}
IteratorTraceExecutionResult::~IteratorTraceExecutionResult() {
key_.clear();
value_.clear();
}
bool IteratorTraceExecutionResult::GetValid() const { return valid_; }
const Status& IteratorTraceExecutionResult::GetStatus() const {
return status_;
}
Slice IteratorTraceExecutionResult::GetKey() const { return Slice(key_); }
Slice IteratorTraceExecutionResult::GetValue() const { return Slice(value_); }
Status IteratorTraceExecutionResult::Accept(Handler* handler) {
assert(handler != nullptr);
return handler->Handle(*this);
}
} // namespace ROCKSDB_NAMESPACE