Improve comment of ExpectedValue in db stress (#11456)
Summary: **Context/Summary:** https://github.com/facebook/rocksdb/pull/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: https://github.com/facebook/rocksdb/pull/11456 Test Plan: CI Reviewed By: jowlyzhang Differential Revision: D45965070 Pulled By: hx235 fbshipit-source-id: dcee690c13b00a3119757ea9d43b646f9644e1a9oxigraph-8.3.2
parent
50046869a4
commit
7263f51d50
@ -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 <atomic> |
||||||
|
|
||||||
|
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
|
@ -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 <stdint.h> |
||||||
|
|
||||||
|
#include <atomic> |
||||||
|
#include <cassert> |
||||||
|
#include <memory> |
||||||
|
|
||||||
|
#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<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_; |
||||||
|
}; |
||||||
|
|
||||||
|
// `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
|
Loading…
Reference in new issue