Support static Status messages

Summary: Provide a way to specify a detailed static error message for a Status without incurring a memcpy.  Let me know what people think of this approach.

Test Plan: added simple test

Reviewers: igor, yhchiang, rven, sdong

Reviewed By: sdong

Subscribers: dhruba, leveldb

Differential Revision: https://reviews.facebook.net/D44259
This commit is contained in:
agiardullo 2015-08-14 16:45:05 -07:00
parent 18db1e4695
commit 77a28615ec
7 changed files with 107 additions and 42 deletions

View File

@ -191,6 +191,7 @@ set(SOURCES
util/sst_dump_tool.cc util/sst_dump_tool.cc
util/statistics.cc util/statistics.cc
util/status.cc util/status.cc
util/status_message.cc
util/string_util.cc util/string_util.cc
util/sync_point.cc util/sync_point.cc
util/testharness.cc util/testharness.cc

View File

@ -25,7 +25,7 @@ namespace rocksdb {
class Status { class Status {
public: public:
// Create a success status. // Create a success status.
Status() : code_(kOk), state_(nullptr) { } Status() : code_(kOk), subcode_(kNone), state_(nullptr) {}
~Status() { delete[] state_; } ~Status() { delete[] state_; }
// Copy the specified status. // Copy the specified status.
@ -34,6 +34,35 @@ class Status {
bool operator==(const Status& rhs) const; bool operator==(const Status& rhs) const;
bool operator!=(const Status& rhs) const; bool operator!=(const Status& rhs) const;
enum Code {
kOk = 0,
kNotFound = 1,
kCorruption = 2,
kNotSupported = 3,
kInvalidArgument = 4,
kIOError = 5,
kMergeInProgress = 6,
kIncomplete = 7,
kShutdownInProgress = 8,
kTimedOut = 9,
kAborted = 10,
kBusy = 11,
kExpired = 12,
kTryAgain = 13
};
Code code() const { return code_; }
enum SubCode {
kNone = 0,
kMutexTimeout = 1,
kLockTimeout = 2,
kLockLimit = 3,
kMaxSubCode
};
SubCode subcode() const { return subcode_; }
// Return a success status. // Return a success status.
static Status OK() { return Status(); } static Status OK() { return Status(); }
@ -42,51 +71,76 @@ class Status {
return Status(kNotFound, msg, msg2); return Status(kNotFound, msg, msg2);
} }
// Fast path for not found without malloc; // Fast path for not found without malloc;
static Status NotFound() { static Status NotFound(SubCode msg = kNone) { return Status(kNotFound, msg); }
return Status(kNotFound);
}
static Status Corruption(const Slice& msg, const Slice& msg2 = Slice()) { static Status Corruption(const Slice& msg, const Slice& msg2 = Slice()) {
return Status(kCorruption, msg, msg2); return Status(kCorruption, msg, msg2);
} }
static Status Corruption(SubCode msg = kNone) {
return Status(kCorruption, msg);
}
static Status NotSupported(const Slice& msg, const Slice& msg2 = Slice()) { static Status NotSupported(const Slice& msg, const Slice& msg2 = Slice()) {
return Status(kNotSupported, msg, msg2); return Status(kNotSupported, msg, msg2);
} }
static Status NotSupported(SubCode msg = kNone) {
return Status(kNotSupported, msg);
}
static Status InvalidArgument(const Slice& msg, const Slice& msg2 = Slice()) { static Status InvalidArgument(const Slice& msg, const Slice& msg2 = Slice()) {
return Status(kInvalidArgument, msg, msg2); return Status(kInvalidArgument, msg, msg2);
} }
static Status InvalidArgument(SubCode msg = kNone) {
return Status(kInvalidArgument, msg);
}
static Status IOError(const Slice& msg, const Slice& msg2 = Slice()) { static Status IOError(const Slice& msg, const Slice& msg2 = Slice()) {
return Status(kIOError, msg, msg2); return Status(kIOError, msg, msg2);
} }
static Status MergeInProgress() { return Status(kMergeInProgress); } static Status IOError(SubCode msg = kNone) { return Status(kIOError, msg); }
static Status MergeInProgress(const Slice& msg, const Slice& msg2 = Slice()) {
return Status(kMergeInProgress, msg, msg2);
}
static Status MergeInProgress(SubCode msg = kNone) {
return Status(kMergeInProgress, msg);
}
static Status Incomplete(const Slice& msg, const Slice& msg2 = Slice()) { static Status Incomplete(const Slice& msg, const Slice& msg2 = Slice()) {
return Status(kIncomplete, msg, msg2); return Status(kIncomplete, msg, msg2);
} }
static Status ShutdownInProgress() { static Status Incomplete(SubCode msg = kNone) {
return Status(kShutdownInProgress); return Status(kIncomplete, msg);
}
static Status ShutdownInProgress(SubCode msg = kNone) {
return Status(kShutdownInProgress, msg);
} }
static Status ShutdownInProgress(const Slice& msg, static Status ShutdownInProgress(const Slice& msg,
const Slice& msg2 = Slice()) { const Slice& msg2 = Slice()) {
return Status(kShutdownInProgress, msg, msg2); return Status(kShutdownInProgress, msg, msg2);
} }
static Status Aborted() { static Status Aborted(SubCode msg = kNone) { return Status(kAborted, msg); }
return Status(kAborted);
}
static Status Aborted(const Slice& msg, const Slice& msg2 = Slice()) { static Status Aborted(const Slice& msg, const Slice& msg2 = Slice()) {
return Status(kAborted, msg, msg2); return Status(kAborted, msg, msg2);
} }
static Status Busy() { return Status(kBusy); }
static Status Busy(SubCode msg = kNone) { return Status(kBusy, msg); }
static Status Busy(const Slice& msg, const Slice& msg2 = Slice()) { static Status Busy(const Slice& msg, const Slice& msg2 = Slice()) {
return Status(kBusy, msg, msg2); return Status(kBusy, msg, msg2);
} }
static Status TimedOut() { return Status(kTimedOut); }
static Status TimedOut(SubCode msg = kNone) { return Status(kTimedOut, msg); }
static Status TimedOut(const Slice& msg, const Slice& msg2 = Slice()) { static Status TimedOut(const Slice& msg, const Slice& msg2 = Slice()) {
return Status(kTimedOut, msg, msg2); return Status(kTimedOut, msg, msg2);
} }
static Status Expired() { return Status(kExpired); }
static Status Expired(SubCode msg = kNone) { return Status(kExpired, msg); }
static Status Expired(const Slice& msg, const Slice& msg2 = Slice()) { static Status Expired(const Slice& msg, const Slice& msg2 = Slice()) {
return Status(kExpired, msg, msg2); return Status(kExpired, msg, msg2);
} }
static Status TryAgain() { return Status(kTryAgain); }
static Status TryAgain(SubCode msg = kNone) { return Status(kTryAgain, msg); }
static Status TryAgain(const Slice& msg, const Slice& msg2 = Slice()) { static Status TryAgain(const Slice& msg, const Slice& msg2 = Slice()) {
return Status(kTryAgain, msg, msg2); return Status(kTryAgain, msg, msg2);
} }
@ -138,26 +192,6 @@ class Status {
// Returns the string "OK" for success. // Returns the string "OK" for success.
std::string ToString() const; std::string ToString() const;
enum Code {
kOk = 0,
kNotFound = 1,
kCorruption = 2,
kNotSupported = 3,
kInvalidArgument = 4,
kIOError = 5,
kMergeInProgress = 6,
kIncomplete = 7,
kShutdownInProgress = 8,
kTimedOut = 9,
kAborted = 10,
kBusy = 11,
kExpired = 12,
kTryAgain = 13
};
Code code() const {
return code_;
}
private: private:
// A nullptr state_ (which is always the case for OK) means the message // A nullptr state_ (which is always the case for OK) means the message
// is empty. // is empty.
@ -165,21 +199,26 @@ class Status {
// state_[0..3] == length of message // state_[0..3] == length of message
// state_[4..] == message // state_[4..] == message
Code code_; Code code_;
SubCode subcode_;
const char* state_; const char* state_;
explicit Status(Code _code) : code_(_code), state_(nullptr) {} static const char* msgs[static_cast<int>(kMaxSubCode)];
explicit Status(Code _code, SubCode _subcode = kNone)
: code_(_code), subcode_(_subcode), state_(nullptr) {}
Status(Code _code, const Slice& msg, const Slice& msg2); Status(Code _code, const Slice& msg, const Slice& msg2);
static const char* CopyState(const char* s); static const char* CopyState(const char* s);
}; };
inline Status::Status(const Status& s) { inline Status::Status(const Status& s) : code_(s.code_), subcode_(s.subcode_) {
code_ = s.code_;
state_ = (s.state_ == nullptr) ? nullptr : CopyState(s.state_); state_ = (s.state_ == nullptr) ? nullptr : CopyState(s.state_);
} }
inline void Status::operator=(const Status& s) { inline void Status::operator=(const Status& s) {
// The following condition catches both aliasing (when this == &s), // The following condition catches both aliasing (when this == &s),
// and the common case where both s and *this are ok. // and the common case where both s and *this are ok.
code_ = s.code_; code_ = s.code_;
subcode_ = s.subcode_;
if (state_ != s.state_) { if (state_ != s.state_) {
delete[] state_; delete[] state_;
state_ = (s.state_ == nullptr) ? nullptr : CopyState(s.state_); state_ = (s.state_ == nullptr) ? nullptr : CopyState(s.state_);

1
src.mk
View File

@ -145,6 +145,7 @@ LIB_SOURCES = \
util/sst_dump_tool.cc \ util/sst_dump_tool.cc \
util/statistics.cc \ util/statistics.cc \
util/status.cc \ util/status.cc \
util/status_message.cc \
util/string_util.cc \ util/string_util.cc \
util/sync_point.cc \ util/sync_point.cc \
util/thread_local.cc \ util/thread_local.cc \

View File

@ -21,7 +21,8 @@ const char* Status::CopyState(const char* state) {
return result; return result;
} }
Status::Status(Code _code, const Slice& msg, const Slice& msg2) : code_(_code) { Status::Status(Code _code, const Slice& msg, const Slice& msg2)
: code_(_code), subcode_(kNone) {
assert(code_ != kOk); assert(code_ != kOk);
const uint32_t len1 = static_cast<uint32_t>(msg.size()); const uint32_t len1 = static_cast<uint32_t>(msg.size());
const uint32_t len2 = static_cast<uint32_t>(msg2.size()); const uint32_t len2 = static_cast<uint32_t>(msg2.size());
@ -89,6 +90,12 @@ std::string Status::ToString() const {
break; break;
} }
std::string result(type); std::string result(type);
if (subcode_ != kNone) {
uint32_t index = static_cast<int32_t>(subcode_);
assert(sizeof(msgs) > index);
result.append(msgs[index]);
}
if (state_ != nullptr) { if (state_ != nullptr) {
uint32_t length; uint32_t length;
memcpy(&length, state_, sizeof(length)); memcpy(&length, state_, sizeof(length));

17
util/status_message.cc Normal file
View File

@ -0,0 +1,17 @@
// Copyright (c) 2015, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
#include "rocksdb/status.h"
namespace rocksdb {
const char* Status::msgs[] = {
"", // kNone
"Timeout Acquiring Mutex", // kMutexTimeout
"Timeout waiting to lock key", // kLockTimeout
"Failed to acquire lock due to max_num_locks limit" // kLockLimit
};
} // namespace rocksdb

View File

@ -232,7 +232,7 @@ Status TransactionLockMgr::AcquireWithTimeout(LockMap* lock_map,
if (!locked) { if (!locked) {
// timeout acquiring mutex // timeout acquiring mutex
return Status::TimedOut("Timeout Acquiring Mutex"); return Status::TimedOut(Status::SubCode::kMutexTimeout);
} }
// Acquire lock if we are able to // Acquire lock if we are able to
@ -313,15 +313,14 @@ Status TransactionLockMgr::AcquireLocked(LockMap* lock_map,
lock_info.expiration_time = txn_lock_info.expiration_time; lock_info.expiration_time = txn_lock_info.expiration_time;
// lock_cnt does not change // lock_cnt does not change
} else { } else {
result = Status::TimedOut("lock held"); result = Status::TimedOut(Status::SubCode::kLockTimeout);
} }
} }
} else { // Lock not held. } else { // Lock not held.
// Check lock limit // Check lock limit
if (max_num_locks_ > 0 && if (max_num_locks_ > 0 &&
lock_map->lock_cnt.load(std::memory_order_acquire) >= max_num_locks_) { lock_map->lock_cnt.load(std::memory_order_acquire) >= max_num_locks_) {
result = result = Status::Busy(Status::SubCode::kLockLimit);
Status::Busy("Failed to acquire lock due to max_num_locks limit");
} else { } else {
// acquire lock // acquire lock
stripe->keys.insert({key, txn_lock_info}); stripe->keys.insert({key, txn_lock_info});

View File

@ -1564,6 +1564,7 @@ TEST_F(TransactionTest, TimeoutTest) {
// txn2 has a smaller lock timeout than txn1's expiration, so it will time out // txn2 has a smaller lock timeout than txn1's expiration, so it will time out
s = txn2->Delete("asdf"); s = txn2->Delete("asdf");
ASSERT_TRUE(s.IsTimedOut()); ASSERT_TRUE(s.IsTimedOut());
ASSERT_EQ(s.ToString(), "Operation timed out: Timeout waiting to lock key");
s = txn1->Commit(); s = txn1->Commit();
ASSERT_OK(s); ASSERT_OK(s);