Enhance transaction_test_util with delays (#4970)
Summary: Enhance ::Insert and ::Verify test functions to add artificial delay between prepare and commit, and take snapshot and reads respectively. A future PR will make use of these to improve stress tests to test against long-running transactions as well as long-running backup jobs. Also randomly sets set_snapshot to false for inserters to skip setting the snapshot in the initialization phase and let the snapshot be taken later explicitly. Pull Request resolved: https://github.com/facebook/rocksdb/pull/4970 Differential Revision: D14031342 Pulled By: maysamyabandeh fbshipit-source-id: b52b453751f0b25b81b23c48892bc1d152464cab
This commit is contained in:
parent
576d2d6c60
commit
d6b9b3b884
@ -21,6 +21,10 @@
|
|||||||
#include "rocksdb/utilities/optimistic_transaction_db.h"
|
#include "rocksdb/utilities/optimistic_transaction_db.h"
|
||||||
#include "rocksdb/utilities/transaction.h"
|
#include "rocksdb/utilities/transaction.h"
|
||||||
#include "rocksdb/utilities/transaction_db.h"
|
#include "rocksdb/utilities/transaction_db.h"
|
||||||
|
|
||||||
|
#include "db/dbformat.h"
|
||||||
|
#include "db/snapshot_impl.h"
|
||||||
|
#include "util/logging.h"
|
||||||
#include "util/random.h"
|
#include "util/random.h"
|
||||||
#include "util/string_util.h"
|
#include "util/string_util.h"
|
||||||
|
|
||||||
@ -28,13 +32,15 @@ namespace rocksdb {
|
|||||||
|
|
||||||
RandomTransactionInserter::RandomTransactionInserter(
|
RandomTransactionInserter::RandomTransactionInserter(
|
||||||
Random64* rand, const WriteOptions& write_options,
|
Random64* rand, const WriteOptions& write_options,
|
||||||
const ReadOptions& read_options, uint64_t num_keys, uint16_t num_sets)
|
const ReadOptions& read_options, uint64_t num_keys, uint16_t num_sets,
|
||||||
|
const uint64_t cmt_delay_ms, const uint64_t first_id)
|
||||||
: rand_(rand),
|
: rand_(rand),
|
||||||
write_options_(write_options),
|
write_options_(write_options),
|
||||||
read_options_(read_options),
|
read_options_(read_options),
|
||||||
num_keys_(num_keys),
|
num_keys_(num_keys),
|
||||||
num_sets_(num_sets),
|
num_sets_(num_sets),
|
||||||
txn_id_(0) {}
|
txn_id_(first_id),
|
||||||
|
cmt_delay_ms_(cmt_delay_ms) {}
|
||||||
|
|
||||||
RandomTransactionInserter::~RandomTransactionInserter() {
|
RandomTransactionInserter::~RandomTransactionInserter() {
|
||||||
if (txn_ != nullptr) {
|
if (txn_ != nullptr) {
|
||||||
@ -51,17 +57,18 @@ bool RandomTransactionInserter::TransactionDBInsert(
|
|||||||
|
|
||||||
std::hash<std::thread::id> hasher;
|
std::hash<std::thread::id> hasher;
|
||||||
char name[64];
|
char name[64];
|
||||||
snprintf(name, 64, "txn%" ROCKSDB_PRIszt "-%d",
|
snprintf(name, 64, "txn%" ROCKSDB_PRIszt "-%" PRIu64,
|
||||||
hasher(std::this_thread::get_id()), txn_id_++);
|
hasher(std::this_thread::get_id()), txn_id_++);
|
||||||
assert(strlen(name) < 64 - 1);
|
assert(strlen(name) < 64 - 1);
|
||||||
txn_->SetName(name);
|
assert(txn_->SetName(name).ok());
|
||||||
|
|
||||||
bool take_snapshot = rand_->OneIn(2);
|
// Take a snapshot if set_snapshot was not set or with 50% change otherwise
|
||||||
|
bool take_snapshot = txn_->GetSnapshot() == nullptr || rand_->OneIn(2);
|
||||||
if (take_snapshot) {
|
if (take_snapshot) {
|
||||||
txn_->SetSnapshot();
|
txn_->SetSnapshot();
|
||||||
read_options_.snapshot = txn_->GetSnapshot();
|
read_options_.snapshot = txn_->GetSnapshot();
|
||||||
}
|
}
|
||||||
auto res = DoInsert(nullptr, txn_, false);
|
auto res = DoInsert(db, txn_, false);
|
||||||
if (take_snapshot) {
|
if (take_snapshot) {
|
||||||
read_options_.snapshot = nullptr;
|
read_options_.snapshot = nullptr;
|
||||||
}
|
}
|
||||||
@ -74,7 +81,7 @@ bool RandomTransactionInserter::OptimisticTransactionDBInsert(
|
|||||||
optimistic_txn_ =
|
optimistic_txn_ =
|
||||||
db->BeginTransaction(write_options_, txn_options, optimistic_txn_);
|
db->BeginTransaction(write_options_, txn_options, optimistic_txn_);
|
||||||
|
|
||||||
return DoInsert(nullptr, optimistic_txn_, true);
|
return DoInsert(db, optimistic_txn_, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RandomTransactionInserter::DBInsert(DB* db) {
|
bool RandomTransactionInserter::DBInsert(DB* db) {
|
||||||
@ -178,20 +185,39 @@ bool RandomTransactionInserter::DoInsert(DB* db, Transaction* txn,
|
|||||||
}
|
}
|
||||||
bytes_inserted_ += key.size() + sum.size();
|
bytes_inserted_ += key.size() + sum.size();
|
||||||
}
|
}
|
||||||
|
ROCKS_LOG_DEBUG(db->GetDBOptions().info_log,
|
||||||
|
"Insert (%s) %s snap: %" PRIu64 " key:%s value: %" PRIu64
|
||||||
|
"+%" PRIu64 "=%" PRIu64,
|
||||||
|
txn->GetName().c_str(), s.ToString().c_str(),
|
||||||
|
txn->GetSnapshot()->GetSequenceNumber(), full_key.c_str(),
|
||||||
|
int_value, incr, int_value + incr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s.ok()) {
|
if (s.ok()) {
|
||||||
if (txn != nullptr) {
|
if (txn != nullptr) {
|
||||||
if (!is_optimistic && !rand_->OneIn(10)) {
|
bool with_prepare = !is_optimistic && !rand_->OneIn(10);
|
||||||
// also try commit without prpare
|
if (with_prepare) {
|
||||||
|
// Also try commit without prepare
|
||||||
s = txn->Prepare();
|
s = txn->Prepare();
|
||||||
assert(s.ok());
|
assert(s.ok());
|
||||||
|
ROCKS_LOG_DEBUG(db->GetDBOptions().info_log,
|
||||||
|
"Prepare of %" PRIu64 " %s (%s)", txn->GetId(),
|
||||||
|
s.ToString().c_str(), txn->GetName().c_str());
|
||||||
|
db->GetDBOptions().env->SleepForMicroseconds(
|
||||||
|
static_cast<int>(cmt_delay_ms_ * 1000));
|
||||||
}
|
}
|
||||||
if (!rand_->OneIn(20)) {
|
if (!rand_->OneIn(20)) {
|
||||||
s = txn->Commit();
|
s = txn->Commit();
|
||||||
|
assert(!with_prepare || s.ok());
|
||||||
|
ROCKS_LOG_DEBUG(db->GetDBOptions().info_log,
|
||||||
|
"Commit of %" PRIu64 " %s (%s)", txn->GetId(),
|
||||||
|
s.ToString().c_str(), txn->GetName().c_str());
|
||||||
} else {
|
} else {
|
||||||
// Also try 5% rollback
|
// Also try 5% rollback
|
||||||
s = txn->Rollback();
|
s = txn->Rollback();
|
||||||
|
ROCKS_LOG_DEBUG(db->GetDBOptions().info_log,
|
||||||
|
"Rollback %" PRIu64 " %s %s", txn->GetId(),
|
||||||
|
txn->GetName().c_str(), s.ToString().c_str());
|
||||||
assert(s.ok());
|
assert(s.ok());
|
||||||
}
|
}
|
||||||
assert(is_optimistic || s.ok());
|
assert(is_optimistic || s.ok());
|
||||||
@ -226,6 +252,8 @@ bool RandomTransactionInserter::DoInsert(DB* db, Transaction* txn,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
ROCKS_LOG_DEBUG(db->GetDBOptions().info_log, "Error %s for txn %s",
|
||||||
|
s.ToString().c_str(), txn->GetName().c_str());
|
||||||
if (txn != nullptr) {
|
if (txn != nullptr) {
|
||||||
assert(txn->Rollback().ok());
|
assert(txn->Rollback().ok());
|
||||||
}
|
}
|
||||||
@ -246,7 +274,11 @@ bool RandomTransactionInserter::DoInsert(DB* db, Transaction* txn,
|
|||||||
// Verify that the sum of the keys in each set are equal
|
// Verify that the sum of the keys in each set are equal
|
||||||
Status RandomTransactionInserter::Verify(DB* db, uint16_t num_sets,
|
Status RandomTransactionInserter::Verify(DB* db, uint16_t num_sets,
|
||||||
uint64_t num_keys_per_set,
|
uint64_t num_keys_per_set,
|
||||||
bool take_snapshot, Random64* rand) {
|
bool take_snapshot, Random64* rand,
|
||||||
|
uint64_t delay_ms) {
|
||||||
|
// delay_ms is the delay between taking a snapshot and doing the reads. It
|
||||||
|
// emulates reads from a long-running backup job.
|
||||||
|
assert(delay_ms == 0 || take_snapshot);
|
||||||
uint64_t prev_total = 0;
|
uint64_t prev_total = 0;
|
||||||
uint32_t prev_i = 0;
|
uint32_t prev_i = 0;
|
||||||
bool prev_assigned = false;
|
bool prev_assigned = false;
|
||||||
@ -254,6 +286,8 @@ Status RandomTransactionInserter::Verify(DB* db, uint16_t num_sets,
|
|||||||
ReadOptions roptions;
|
ReadOptions roptions;
|
||||||
if (take_snapshot) {
|
if (take_snapshot) {
|
||||||
roptions.snapshot = db->GetSnapshot();
|
roptions.snapshot = db->GetSnapshot();
|
||||||
|
db->GetDBOptions().env->SleepForMicroseconds(
|
||||||
|
static_cast<int>(delay_ms * 1000));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint16_t> set_vec(num_sets);
|
std::vector<uint16_t> set_vec(num_sets);
|
||||||
@ -271,7 +305,9 @@ Status RandomTransactionInserter::Verify(DB* db, uint16_t num_sets,
|
|||||||
|
|
||||||
// Use either point lookup or iterator. Point lookups are slower so we use
|
// Use either point lookup or iterator. Point lookups are slower so we use
|
||||||
// it less often.
|
// it less often.
|
||||||
if (num_keys_per_set != 0 && rand && rand->OneIn(10)) { // use point lookup
|
const bool use_point_lookup =
|
||||||
|
num_keys_per_set != 0 && rand && rand->OneIn(10);
|
||||||
|
if (use_point_lookup) {
|
||||||
ReadOptions read_options;
|
ReadOptions read_options;
|
||||||
for (uint64_t k = 0; k < num_keys_per_set; k++) {
|
for (uint64_t k = 0; k < num_keys_per_set; k++) {
|
||||||
std::string dont_care;
|
std::string dont_care;
|
||||||
@ -299,17 +335,37 @@ Status RandomTransactionInserter::Verify(DB* db, uint16_t num_sets,
|
|||||||
value.ToString().c_str());
|
value.ToString().c_str());
|
||||||
return Status::Corruption();
|
return Status::Corruption();
|
||||||
}
|
}
|
||||||
|
ROCKS_LOG_DEBUG(
|
||||||
|
db->GetDBOptions().info_log,
|
||||||
|
"VerifyRead at %" PRIu64 " (%" PRIu64 "): %.*s value: %" PRIu64,
|
||||||
|
roptions.snapshot ? roptions.snapshot->GetSequenceNumber() : 0ul,
|
||||||
|
roptions.snapshot
|
||||||
|
? ((SnapshotImpl*)roptions.snapshot)->min_uncommitted_
|
||||||
|
: 0ul,
|
||||||
|
key.size(), key.data(), int_value);
|
||||||
total += int_value;
|
total += int_value;
|
||||||
}
|
}
|
||||||
delete iter;
|
delete iter;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prev_assigned && total != prev_total) {
|
if (prev_assigned && total != prev_total) {
|
||||||
|
db->GetDBOptions().info_log->Flush();
|
||||||
fprintf(stdout,
|
fprintf(stdout,
|
||||||
"RandomTransactionVerify found inconsistent totals. "
|
"RandomTransactionVerify found inconsistent totals using "
|
||||||
"Set[%" PRIu32 "]: %" PRIu64 ", Set[%" PRIu32 "]: %" PRIu64 " \n",
|
"pointlookup? %d "
|
||||||
prev_i, prev_total, set_i, total);
|
"Set[%" PRIu32 "]: %" PRIu64 ", Set[%" PRIu32 "]: %" PRIu64
|
||||||
|
" at snapshot %" PRIu64 "\n",
|
||||||
|
use_point_lookup, prev_i, prev_total, set_i, total,
|
||||||
|
roptions.snapshot ? roptions.snapshot->GetSequenceNumber() : 0ul);
|
||||||
|
fflush(stdout);
|
||||||
return Status::Corruption();
|
return Status::Corruption();
|
||||||
|
} else {
|
||||||
|
ROCKS_LOG_DEBUG(
|
||||||
|
db->GetDBOptions().info_log,
|
||||||
|
"RandomTransactionVerify pass pointlookup? %d total: %" PRIu64
|
||||||
|
" snap: %" PRIu64,
|
||||||
|
use_point_lookup, total,
|
||||||
|
roptions.snapshot ? roptions.snapshot->GetSequenceNumber() : 0ul);
|
||||||
}
|
}
|
||||||
prev_total = total;
|
prev_total = total;
|
||||||
prev_i = set_i;
|
prev_i = set_i;
|
||||||
|
@ -35,10 +35,13 @@ class RandomTransactionInserter {
|
|||||||
public:
|
public:
|
||||||
// num_keys is the number of keys in each set.
|
// num_keys is the number of keys in each set.
|
||||||
// num_sets is the number of sets of keys.
|
// num_sets is the number of sets of keys.
|
||||||
|
// cmt_delay_ms is the delay between prepare (if there is any) and commit
|
||||||
|
// first_id is the id of the first transaction
|
||||||
explicit RandomTransactionInserter(
|
explicit RandomTransactionInserter(
|
||||||
Random64* rand, const WriteOptions& write_options = WriteOptions(),
|
Random64* rand, const WriteOptions& write_options = WriteOptions(),
|
||||||
const ReadOptions& read_options = ReadOptions(), uint64_t num_keys = 1000,
|
const ReadOptions& read_options = ReadOptions(), uint64_t num_keys = 1000,
|
||||||
uint16_t num_sets = 3);
|
uint16_t num_sets = 3, const uint64_t cmt_delay_ms = 0,
|
||||||
|
const uint64_t first_id = 0);
|
||||||
|
|
||||||
~RandomTransactionInserter();
|
~RandomTransactionInserter();
|
||||||
|
|
||||||
@ -76,7 +79,8 @@ class RandomTransactionInserter {
|
|||||||
|
|
||||||
// Returns OK if Invariant is true.
|
// Returns OK if Invariant is true.
|
||||||
static Status Verify(DB* db, uint16_t num_sets, uint64_t num_keys_per_set = 0,
|
static Status Verify(DB* db, uint16_t num_sets, uint64_t num_keys_per_set = 0,
|
||||||
bool take_snapshot = false, Random64* rand = nullptr);
|
bool take_snapshot = false, Random64* rand = nullptr,
|
||||||
|
uint64_t delay_ms = 0);
|
||||||
|
|
||||||
// Returns the status of the previous Insert operation
|
// Returns the status of the previous Insert operation
|
||||||
Status GetLastStatus() { return last_status_; }
|
Status GetLastStatus() { return last_status_; }
|
||||||
@ -116,7 +120,9 @@ class RandomTransactionInserter {
|
|||||||
Transaction* txn_ = nullptr;
|
Transaction* txn_ = nullptr;
|
||||||
Transaction* optimistic_txn_ = nullptr;
|
Transaction* optimistic_txn_ = nullptr;
|
||||||
|
|
||||||
std::atomic<int> txn_id_;
|
uint64_t txn_id_;
|
||||||
|
// The delay between ::Prepare and ::Commit
|
||||||
|
const uint64_t cmt_delay_ms_;
|
||||||
|
|
||||||
bool DoInsert(DB* db, Transaction* txn, bool is_optimistic);
|
bool DoInsert(DB* db, Transaction* txn, bool is_optimistic);
|
||||||
};
|
};
|
||||||
|
@ -4999,7 +4999,9 @@ Status TransactionStressTestInserter(TransactionDB* db,
|
|||||||
WriteOptions write_options;
|
WriteOptions write_options;
|
||||||
ReadOptions read_options;
|
ReadOptions read_options;
|
||||||
TransactionOptions txn_options;
|
TransactionOptions txn_options;
|
||||||
txn_options.set_snapshot = true;
|
// Inside the inserter we might also retake the snapshot. We do both since two
|
||||||
|
// separte functions are engaged for each.
|
||||||
|
txn_options.set_snapshot = _rand.OneIn(2);
|
||||||
|
|
||||||
RandomTransactionInserter inserter(&_rand, write_options, read_options,
|
RandomTransactionInserter inserter(&_rand, write_options, read_options,
|
||||||
num_keys_per_set,
|
num_keys_per_set,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user