Skip to content

Commit

Permalink
Improve comment of ExpectedValue in db stress (facebook#11456)
Browse files Browse the repository at this point in the history
Summary:
**Context/Summary:**
facebook#11424 made me realize there are a couple gaps in my `ExpectedValue` comments so I updated them, along with separating `ExpectedValue` into separate files so it's clearer that `ExpectedValue` can be used without updating `ExpectedState` (e.g, TestMultiGet() where we care about value base of expected value but not updating the ExpectedState).

Pull Request resolved: facebook#11456

Test Plan: CI

Reviewed By: jowlyzhang

Differential Revision: D45965070

Pulled By: hx235

fbshipit-source-id: dcee690c13b00a3119757ea9d43b646f9644e1a9
  • Loading branch information
hx235 authored and facebook-github-bot committed May 18, 2023
1 parent 5004686 commit 7263f51
Show file tree
Hide file tree
Showing 7 changed files with 334 additions and 281 deletions.
1 change: 1 addition & 0 deletions TARGETS
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ rocks_cpp_library_wrapper(name="rocksdb_stress_lib", srcs=[
"db_stress_tool/db_stress_test_base.cc",
"db_stress_tool/db_stress_tool.cc",
"db_stress_tool/expected_state.cc",
"db_stress_tool/expected_value.cc",
"db_stress_tool/multi_ops_txns_stress.cc",
"db_stress_tool/no_batched_ops_stress.cc",
"test_util/testutil.cc",
Expand Down
1 change: 1 addition & 0 deletions db_stress_tool/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ add_executable(db_stress${ARTIFACT_SUFFIX}
db_stress_test_base.cc
db_stress_tool.cc
expected_state.cc
expected_value.cc
multi_ops_txns_stress.cc
no_batched_ops_stress.cc)
target_link_libraries(db_stress${ARTIFACT_SUFFIX} ${ROCKSDB_LIB} ${THIRDPARTY_LIBS})
Expand Down
109 changes: 0 additions & 109 deletions db_stress_tool/expected_state.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,115 +15,6 @@
#include "rocksdb/trace_record_result.h"

namespace ROCKSDB_NAMESPACE {
void ExpectedValue::Put(bool pending) {
if (pending) {
SetPendingWrite();
} else {
SetValueBase(NextValueBase());
ClearDeleted();
ClearPendingWrite();
}
}

bool ExpectedValue::Delete(bool pending) {
if (!Exists()) {
return false;
}
if (pending) {
SetPendingDel();
} else {
SetDelCounter(NextDelCounter());
SetDeleted();
ClearPendingDel();
}
return true;
}

void ExpectedValue::SyncPut(uint32_t value_base) {
assert(ExpectedValue::IsValueBaseValid(value_base));

SetValueBase(value_base);
ClearDeleted();
ClearPendingWrite();

// This is needed in case crash happens during a pending delete of the key
// assocated with this expected value
ClearPendingDel();
}

void ExpectedValue::SyncPendingPut() { Put(true /* pending */); }

void ExpectedValue::SyncDelete() {
Delete(false /* pending */);
// This is needed in case crash happens during a pending write of the key
// assocated with this expected value
ClearPendingWrite();
}

uint32_t ExpectedValue::GetFinalValueBase() const {
return PendingWrite() ? NextValueBase() : GetValueBase();
}

uint32_t ExpectedValue::GetFinalDelCounter() const {
return PendingDelete() ? NextDelCounter() : GetDelCounter();
}

bool ExpectedValueHelper::MustHaveNotExisted(
ExpectedValue pre_read_expected_value,
ExpectedValue post_read_expected_value) {
const bool pre_read_expected_deleted = pre_read_expected_value.IsDeleted();

const uint32_t pre_read_expected_value_base =
pre_read_expected_value.GetValueBase();

const uint32_t post_read_expected_final_value_base =
post_read_expected_value.GetFinalValueBase();

const bool during_read_no_write_happened =
(pre_read_expected_value_base == post_read_expected_final_value_base);
return pre_read_expected_deleted && during_read_no_write_happened;
}

bool ExpectedValueHelper::MustHaveExisted(
ExpectedValue pre_read_expected_value,
ExpectedValue post_read_expected_value) {
const bool pre_read_expected_not_deleted =
!pre_read_expected_value.IsDeleted();

const uint32_t pre_read_expected_del_counter =
pre_read_expected_value.GetDelCounter();
const uint32_t post_read_expected_final_del_counter =
post_read_expected_value.GetFinalDelCounter();

const bool during_read_no_delete_happened =
(pre_read_expected_del_counter == post_read_expected_final_del_counter);

return pre_read_expected_not_deleted && during_read_no_delete_happened;
}

bool ExpectedValueHelper::InExpectedValueBaseRange(
uint32_t value_base, ExpectedValue pre_read_expected_value,
ExpectedValue post_read_expected_value) {
assert(ExpectedValue::IsValueBaseValid(value_base));

const uint32_t pre_read_expected_value_base =
pre_read_expected_value.GetValueBase();
const uint32_t post_read_expected_final_value_base =
post_read_expected_value.GetFinalValueBase();

if (pre_read_expected_value_base <= post_read_expected_final_value_base) {
const uint32_t lower_bound = pre_read_expected_value_base;
const uint32_t upper_bound = post_read_expected_final_value_base;
return lower_bound <= value_base && value_base <= upper_bound;
} else {
const uint32_t upper_bound_1 = post_read_expected_final_value_base;
const uint32_t lower_bound_2 = pre_read_expected_value_base;
const uint32_t upper_bound_2 = ExpectedValue::GetValueBaseMask();
return (value_base <= upper_bound_1) ||
(lower_bound_2 <= value_base && value_base <= upper_bound_2);
}
}

ExpectedState::ExpectedState(size_t max_key, size_t num_column_families)
: max_key_(max_key),
num_column_families_(num_column_families),
Expand Down
175 changes: 3 additions & 172 deletions db_stress_tool/expected_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <memory>

#include "db/dbformat.h"
#include "db_stress_tool/expected_value.h"
#include "file/file_util.h"
#include "rocksdb/db.h"
#include "rocksdb/env.h"
Expand All @@ -22,177 +23,8 @@
#include "util/string_util.h"

namespace ROCKSDB_NAMESPACE {
// This class is not thread-safe.
class ExpectedValue {
public:
static uint32_t GetValueBaseMask() { return VALUE_BASE_MASK; }
static uint32_t GetValueBaseDelta() { return VALUE_BASE_DELTA; }
static uint32_t GetDelCounterDelta() { return DEL_COUNTER_DELTA; }
static uint32_t GetDelMask() { return DEL_MASK; }
static bool IsValueBaseValid(uint32_t value_base) {
return IsValuePartValid(value_base, VALUE_BASE_MASK);
}

explicit ExpectedValue(uint32_t expected_value)
: expected_value_(expected_value) {}

bool Exists() const { return PendingWrite() || !IsDeleted(); }

uint32_t Read() const { return expected_value_; }

void Put(bool pending);

bool Delete(bool pending);

void SyncPut(uint32_t value_base);

void SyncPendingPut();

void SyncDelete();

uint32_t GetValueBase() const { return GetValuePart(VALUE_BASE_MASK); }

uint32_t NextValueBase() const {
return GetIncrementedValuePart(VALUE_BASE_MASK, VALUE_BASE_DELTA);
}

void SetValueBase(uint32_t new_value_base) {
SetValuePart(VALUE_BASE_MASK, new_value_base);
}

bool PendingWrite() const {
const uint32_t pending_write = GetValuePart(PENDING_WRITE_MASK);
return pending_write != 0;
}

void SetPendingWrite() {
SetValuePart(PENDING_WRITE_MASK, PENDING_WRITE_MASK);
}

void ClearPendingWrite() { ClearValuePart(PENDING_WRITE_MASK); }

uint32_t GetDelCounter() const { return GetValuePart(DEL_COUNTER_MASK); }

uint32_t NextDelCounter() const {
return GetIncrementedValuePart(DEL_COUNTER_MASK, DEL_COUNTER_DELTA);
}

void SetDelCounter(uint32_t new_del_counter) {
SetValuePart(DEL_COUNTER_MASK, new_del_counter);
}

bool PendingDelete() const {
const uint32_t pending_del = GetValuePart(PENDING_DEL_MASK);
return pending_del != 0;
}

void SetPendingDel() { SetValuePart(PENDING_DEL_MASK, PENDING_DEL_MASK); }

void ClearPendingDel() { ClearValuePart(PENDING_DEL_MASK); }

bool IsDeleted() const {
const uint32_t deleted = GetValuePart(DEL_MASK);
return deleted != 0;
}

void SetDeleted() { SetValuePart(DEL_MASK, DEL_MASK); }

void ClearDeleted() { ClearValuePart(DEL_MASK); }

uint32_t GetFinalValueBase() const;

uint32_t GetFinalDelCounter() const;

private:
static bool IsValuePartValid(uint32_t value_part, uint32_t value_part_mask) {
return (value_part & (~value_part_mask)) == 0;
}

// The 32-bit expected_value_ is divided into following parts:
// Bit 0 - 14: value base
static constexpr uint32_t VALUE_BASE_MASK = 0x7fff;
static constexpr uint32_t VALUE_BASE_DELTA = 1;
// Bit 15: whether write to this value base is pending (0 equals `false`)
static constexpr uint32_t PENDING_WRITE_MASK = (uint32_t)1 << 15;
// Bit 16 - 29: deletion counter (i.e, number of times this value base has
// been deleted)
static constexpr uint32_t DEL_COUNTER_MASK = 0x3fff0000;
static constexpr uint32_t DEL_COUNTER_DELTA = (uint32_t)1 << 16;
// Bit 30: whether deletion of this value base is pending (0 equals `false`)
static constexpr uint32_t PENDING_DEL_MASK = (uint32_t)1 << 30;
// Bit 31: whether this value base is deleted (0 equals `false`)
static constexpr uint32_t DEL_MASK = (uint32_t)1 << 31;

uint32_t GetValuePart(uint32_t value_part_mask) const {
return expected_value_ & value_part_mask;
}

uint32_t GetIncrementedValuePart(uint32_t value_part_mask,
uint32_t value_part_delta) const {
uint32_t current_value_part = GetValuePart(value_part_mask);
ExpectedValue temp_expected_value(current_value_part + value_part_delta);
return temp_expected_value.GetValuePart(value_part_mask);
}

void SetValuePart(uint32_t value_part_mask, uint32_t new_value_part) {
assert(IsValuePartValid(new_value_part, value_part_mask));
ClearValuePart(value_part_mask);
expected_value_ |= new_value_part;
}

void ClearValuePart(uint32_t value_part_mask) {
expected_value_ &= (~value_part_mask);
}

uint32_t expected_value_;
};

class PendingExpectedValue {
public:
explicit PendingExpectedValue(std::atomic<uint32_t>* value_ptr,
ExpectedValue orig_value,
ExpectedValue final_value)
: value_ptr_(value_ptr),
orig_value_(orig_value),
final_value_(final_value) {}

void Commit() {
// To prevent low-level instruction reordering that results
// in setting expected value happens before db write
std::atomic_thread_fence(std::memory_order_release);
value_ptr_->store(final_value_.Read());
}

uint32_t GetFinalValueBase() { return final_value_.GetValueBase(); }

private:
std::atomic<uint32_t>* const value_ptr_;
const ExpectedValue orig_value_;
const ExpectedValue final_value_;
};

class ExpectedValueHelper {
public:
// Return whether value is expected not to exist from begining till the end
// of the read based on `pre_read_expected_value` and
// `pre_read_expected_value`.
static bool MustHaveNotExisted(ExpectedValue pre_read_expected_value,
ExpectedValue post_read_expected_value);

// Return whether value is expected to exist from begining till the end of
// the read based on `pre_read_expected_value` and
// `pre_read_expected_value`.
static bool MustHaveExisted(ExpectedValue pre_read_expected_value,
ExpectedValue post_read_expected_value);

// Return whether the `value_base` falls within the expected value base
static bool InExpectedValueBaseRange(uint32_t value_base,
ExpectedValue pre_read_expected_value,
ExpectedValue post_read_expected_value);
};

// An `ExpectedState` provides read/write access to expected values for every
// key.
// `ExpectedState` provides read/write access to expected values stored in
// `ExpectedState` for every key.
class ExpectedState {
public:
explicit ExpectedState(size_t max_key, size_t num_column_families);
Expand Down Expand Up @@ -492,7 +324,6 @@ class AnonExpectedStateManager : public ExpectedStateManager {
// member function.
Status Open() override;
};

} // namespace ROCKSDB_NAMESPACE

#endif // GFLAGS
Loading

0 comments on commit 7263f51

Please sign in to comment.