1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
|
// 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 <stdio.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);
}
ExpectedValue() : expected_value_(DEL_MASK) {}
explicit ExpectedValue(uint32_t expected_value)
: expected_value_(expected_value) {}
bool Exists() const {
assert(!PendingWrite() && !PendingDelete());
return !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.
//
// After a `PendingExpectedValue` object is created, either `Rollback` or
// `Commit` should be called to close its pending state before it's destructed.
// In case no pending state was introduced while creating this
// `PendingExpectedValue` and user want to ignore the unclosed pending state,
// `PermitUnclosedPendingState` should be called explicitly.
// 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),
pending_state_closed_(false) {}
PendingExpectedValue(const PendingExpectedValue& other)
: value_ptr_(other.value_ptr_),
orig_value_(other.orig_value_),
final_value_(other.final_value_),
pending_state_closed_(false) {
other.ClosePendingState();
}
PendingExpectedValue(PendingExpectedValue&& other) noexcept
: value_ptr_(std::move(other.value_ptr_)),
orig_value_(std::move(other.orig_value_)),
final_value_(std::move(other.final_value_)),
pending_state_closed_(false) {
other.ClosePendingState();
}
PendingExpectedValue& operator=(const PendingExpectedValue& other) {
if (this != &other) {
other.ClosePendingState();
value_ptr_ = other.value_ptr_;
orig_value_ = other.orig_value_;
final_value_ = other.final_value_;
pending_state_closed_ = false;
}
return *this;
}
PendingExpectedValue& operator=(PendingExpectedValue&& other) {
if (this != &other) {
other.ClosePendingState();
value_ptr_ = std::move(other.value_ptr_);
orig_value_ = std::move(other.orig_value_);
final_value_ = std::move(other.final_value_);
pending_state_closed_ = false;
}
return *this;
}
~PendingExpectedValue() { assert(pending_state_closed_); }
void Commit() {
assert(!pending_state_closed_);
ClosePendingState();
// 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());
}
// Rollbacks the key to its original state.
// This rollbacks the pending state created in `ExpectedState::Precommit`,
// such as pending delete, pending put. If `ExpectedState::Precommit()` is not
// called before creating this `PendingExpectedValue`, this is a no-op.
void Rollback() {
assert(!pending_state_closed_);
ClosePendingState();
// 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(orig_value_.Read());
}
void PermitUnclosedPendingState() const {
assert(!pending_state_closed_);
ClosePendingState();
}
uint32_t GetFinalValueBase() { return final_value_.GetValueBase(); }
private:
inline void ClosePendingState() const { pending_state_closed_ = true; }
std::atomic<uint32_t>* value_ptr_;
ExpectedValue orig_value_;
ExpectedValue final_value_;
mutable bool pending_state_closed_;
};
// `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
|