diff --git a/TARGETS b/TARGETS index bbd4530cf..52a29b774 100644 --- a/TARGETS +++ b/TARGETS @@ -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", diff --git a/db_stress_tool/CMakeLists.txt b/db_stress_tool/CMakeLists.txt index 96d70dd0e..51d6ea0d6 100644 --- a/db_stress_tool/CMakeLists.txt +++ b/db_stress_tool/CMakeLists.txt @@ -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}) diff --git a/db_stress_tool/expected_state.cc b/db_stress_tool/expected_state.cc index d1b8c57d0..dd210cab4 100644 --- a/db_stress_tool/expected_state.cc +++ b/db_stress_tool/expected_state.cc @@ -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), diff --git a/db_stress_tool/expected_state.h b/db_stress_tool/expected_state.h index aa78941c3..f3f924cb8 100644 --- a/db_stress_tool/expected_state.h +++ b/db_stress_tool/expected_state.h @@ -13,6 +13,7 @@ #include #include "db/dbformat.h" +#include "db_stress_tool/expected_value.h" #include "file/file_util.h" #include "rocksdb/db.h" #include "rocksdb/env.h" @@ -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* 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* 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); @@ -492,7 +324,6 @@ class AnonExpectedStateManager : public ExpectedStateManager { // member function. Status Open() override; }; - } // namespace ROCKSDB_NAMESPACE #endif // GFLAGS diff --git a/db_stress_tool/expected_value.cc b/db_stress_tool/expected_value.cc new file mode 100644 index 000000000..d280055a2 --- /dev/null +++ b/db_stress_tool/expected_value.cc @@ -0,0 +1,122 @@ +// Copyright (c) 2021-present, Facebook, Inc. All rights reserved. +// This source code is licensed under both the GPLv2 (found in the +// COPYING file in the root directory) and Apache 2.0 License +// (found in the LICENSE.Apache file in the root directory). + +#ifdef GFLAGS + +#include "db_stress_tool/expected_value.h" + +#include + +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); + } +} +} // namespace ROCKSDB_NAMESPACE +#endif // GFLAGS diff --git a/db_stress_tool/expected_value.h b/db_stress_tool/expected_value.h new file mode 100644 index 000000000..2e41b130c --- /dev/null +++ b/db_stress_tool/expected_value.h @@ -0,0 +1,206 @@ +// Copyright (c) 2021-present, Facebook, Inc. All rights reserved. +// This source code is licensed under both the GPLv2 (found in the +// COPYING file in the root directory) and Apache 2.0 License +// (found in the LICENSE.Apache file in the root directory). + +#ifdef GFLAGS + +#pragma once + +#include + +#include +#include +#include + +#include "rocksdb/rocksdb_namespace.h" + +namespace ROCKSDB_NAMESPACE { +// `ExpectedValue` represents the expected value of a key used in db stress, +// which provides APIs to obtain various information e.g, value base, existence, +// pending operation status and APIs to edit expected value. +// +// 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_; +}; + +// `PendingExpectedValue` represents the expected value of a key undergoing a +// pending operation in db stress. +// +// This class is not thread-safe. +class PendingExpectedValue { + public: + explicit PendingExpectedValue(std::atomic* 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* const value_ptr_; + const ExpectedValue orig_value_; + const ExpectedValue final_value_; +}; + +// `ExpectedValueHelper` provides utils to parse `ExpectedValue` to obtain +// useful info about it in db stress +class ExpectedValueHelper { + public: + // Return whether the key associated with `pre_read_expected_value` and + // `post_read_expected_value` is expected not to exist from begining till the + // end of the read + // + // The negation of `MustHaveNotExisted()` is "may have not existed". + // To assert some key must have existsed, please use `MustHaveExisted()` + static bool MustHaveNotExisted(ExpectedValue pre_read_expected_value, + ExpectedValue post_read_expected_value); + + // Return whether the key associated with `pre_read_expected_value` and + // `post_read_expected_value` is expected to exist from begining till the end + // of the read. + // + // The negation of `MustHaveExisted()` is "may have existed". + // To assert some key must have not existsed, please use + // `MustHaveNotExisted()` + 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); +}; +} // namespace ROCKSDB_NAMESPACE + +#endif // GFLAGS diff --git a/src.mk b/src.mk index e1ab947a0..baaea29be 100644 --- a/src.mk +++ b/src.mk @@ -377,6 +377,7 @@ STRESS_LIB_SOURCES = \ 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/no_batched_ops_stress.cc \ db_stress_tool/multi_ops_txns_stress.cc \