Added optional "-b"/"balanced" flag to the end of read/read2/read3 operations in interactive mode, to make them balanced reads.
Balanced read percentage is not used in interactive mode.
Add command line argument to specify percentage of read ops that should use the balanced reads flag. Default is 100%.
Signed-off-by: Matty Williams <Matty.Williams@ibm.com>
Signed-off-by: Alex Ainscow <aainscow@uk.ibm.com>
Signed-off-by: Jon Bailey <jonathan.bailey1@ibm.com>
}
}
+template <OpType opType, int numIOs>
+ceph::io_exerciser::ReadWriteOp<opType, numIOs>::ReadWriteOp(
+ std::array<uint64_t, numIOs>&& offset,
+ std::array<uint64_t, numIOs>&& length,
+ std::optional<bool> balanced_read)
+ : ReadWriteOp<opType, numIOs>(std::move(offset), std::move(length)) {
+ this->balanced_read = balanced_read;
+ }
+
ConsistencyOp::ConsistencyOp() : TestOp<OpType::Consistency>() {}
std::unique_ptr<ConsistencyOp> ConsistencyOp::generate() {
}
}
-SingleReadOp::SingleReadOp(uint64_t offset, uint64_t length)
- : ReadWriteOp<OpType::Read, 1>({offset}, {length}) {}
+SingleReadOp::SingleReadOp(uint64_t offset, uint64_t length, std::optional<bool> balanced_read)
+ : ReadWriteOp<OpType::Read, 1>({offset}, {length}, balanced_read) {}
std::unique_ptr<SingleReadOp> SingleReadOp::generate(uint64_t offset,
uint64_t length) {
- return std::make_unique<SingleReadOp>(offset, length);
+ return std::make_unique<SingleReadOp>(offset, length, std::nullopt);
+}
+
+std::unique_ptr<SingleReadOp> SingleReadOp::generate(uint64_t offset,
+ uint64_t length, bool balanced_read) {
+ return std::make_unique<SingleReadOp>(offset, length, balanced_read);
}
DoubleReadOp::DoubleReadOp(uint64_t offset1, uint64_t length1, uint64_t offset2,
- uint64_t length2)
- : ReadWriteOp<OpType::Read2, 2>({offset1, offset2}, {length1, length2}) {}
+ uint64_t length2, std::optional<bool> balanced_read)
+ : ReadWriteOp<OpType::Read2, 2>({offset1, offset2}, {length1, length2}, balanced_read) {}
std::unique_ptr<DoubleReadOp> DoubleReadOp::generate(uint64_t offset1,
uint64_t length1,
uint64_t offset2,
uint64_t length2) {
- return std::make_unique<DoubleReadOp>(offset1, length1, offset2, length2);
+ return std::make_unique<DoubleReadOp>(offset1, length1, offset2, length2, std::nullopt);
+}
+
+std::unique_ptr<DoubleReadOp> DoubleReadOp::generate(uint64_t offset1,
+ uint64_t length1,
+ uint64_t offset2,
+ uint64_t length2,
+ bool balanced_read) {
+ return std::make_unique<DoubleReadOp>(offset1, length1, offset2, length2, balanced_read);
}
TripleReadOp::TripleReadOp(uint64_t offset1, uint64_t length1, uint64_t offset2,
- uint64_t length2, uint64_t offset3, uint64_t length3)
+ uint64_t length2, uint64_t offset3, uint64_t length3,
+ std::optional<bool> balanced_read)
: ReadWriteOp<OpType::Read3, 3>({offset1, offset2, offset3},
- {length1, length2, length3}) {}
+ {length1, length2, length3},
+ balanced_read) {}
std::unique_ptr<TripleReadOp> TripleReadOp::generate(
uint64_t offset1, uint64_t length1, uint64_t offset2, uint64_t length2,
uint64_t offset3, uint64_t length3) {
return std::make_unique<TripleReadOp>(offset1, length1, offset2, length2,
- offset3, length3);
+ offset3, length3, std::nullopt);
+}
+
+std::unique_ptr<TripleReadOp> TripleReadOp::generate(
+ uint64_t offset1, uint64_t length1, uint64_t offset2, uint64_t length2,
+ uint64_t offset3, uint64_t length3, bool balanced_read) {
+ return std::make_unique<TripleReadOp>(offset1, length1, offset2, length2,
+ offset3, length3, balanced_read);
}
SingleWriteOp::SingleWriteOp(uint64_t offset, uint64_t length)
public:
std::array<uint64_t, numIOs> offset;
std::array<uint64_t, numIOs> length;
+ std::optional<bool> balanced_read = std::nullopt;
protected:
ReadWriteOp(std::array<uint64_t, numIOs>&& offset,
std::array<uint64_t, numIOs>&& length);
+ ReadWriteOp(std::array<uint64_t, numIOs>&& offset,
+ std::array<uint64_t, numIOs>&& length,
+ std::optional<bool> balanced_read);
std::string to_string(uint64_t block_size) const override;
};
class SingleReadOp : public ReadWriteOp<OpType::Read, 1> {
public:
- SingleReadOp(uint64_t offset, uint64_t length);
+ SingleReadOp(uint64_t offset, uint64_t length, std::optional<bool> balanced_read);
static std::unique_ptr<SingleReadOp> generate(uint64_t offset,
uint64_t length);
+ static std::unique_ptr<SingleReadOp> generate(uint64_t offset,
+ uint64_t length, bool balanced_read);
};
class DoubleReadOp : public ReadWriteOp<OpType::Read2, 2> {
public:
DoubleReadOp(uint64_t offset1, uint64_t length1, uint64_t offset2,
- uint64_t length2);
+ uint64_t length2, std::optional<bool> balanced_read);
static std::unique_ptr<DoubleReadOp> generate(uint64_t offset1,
uint64_t length1,
uint64_t offset2,
uint64_t length2);
+ static std::unique_ptr<DoubleReadOp> generate(uint64_t offset1,
+ uint64_t length1,
+ uint64_t offset2,
+ uint64_t length2,
+ bool balanced_read);
};
class TripleReadOp : public ReadWriteOp<OpType::Read3, 3> {
public:
TripleReadOp(uint64_t offset1, uint64_t length1, uint64_t offset2,
- uint64_t length2, uint64_t offset3, uint64_t length3);
+ uint64_t length2, uint64_t offset3, uint64_t length3,
+ std::optional<bool> balanced_read);
static std::unique_ptr<TripleReadOp> generate(
uint64_t offset1, uint64_t length1, uint64_t offset2, uint64_t length2,
uint64_t offset3, uint64_t length3);
+ static std::unique_ptr<TripleReadOp> generate(
+ uint64_t offset1, uint64_t length1, uint64_t offset2, uint64_t length2,
+ uint64_t offset3, uint64_t length3, bool balanced_read);
};
class SingleWriteOp : public ReadWriteOp<OpType::Write, 1> {
const std::string& pool, const std::string& primary_oid, const std::string& secondary_oid,
uint64_t block_size, int seed, int threads, ceph::mutex& lock,
ceph::condition_variable& cond, bool is_replicated_pool,
- bool ec_optimizations, GenerationType data_generation_type,
+ bool ec_optimizations, int balanced_read_percentage,
+ GenerationType data_generation_type,
std::shared_ptr<ceph::io_exerciser::IoSequence> seq, bool delete_objects)
: Model(primary_oid, secondary_oid, block_size, delete_objects),
rados(rados),
lock(lock),
cond(cond),
outstanding_io(0),
- seq(seq) {
+ seq(seq),
+ rng(seed),
+ balanced_read_percentage(balanced_read_percentage) {
int rc;
rc = rados.ioctx_create(pool.c_str(), io);
ceph_assert(rc == 0);
}
finish_io();
};
+
+ int flags = 0;
+ if (readOp.balanced_read.has_value()) {
+ if (*readOp.balanced_read) {
+ flags = librados::OPERATION_BALANCE_READS;
+ }
+ // Else: keep flags == 0
+ } else {
+ ceph_assert(balanced_read_percentage >= 0);
+ ceph_assert(balanced_read_percentage <= 100);
+ uint64_t range = 100;
+ uint64_t rand_value = rng();
+ int index = rand_value % range;
+ if (index <= balanced_read_percentage) {
+ flags = librados::OPERATION_BALANCE_READS;
+ }
+ }
librados::async_operate(asio.get_executor(), io, primary_oid,
- std::move(rop), 0, nullptr, read_cb);
+ std::move(rop), flags, nullptr, read_cb);
num_io++;
};
#include "librados/AioCompletionImpl.h"
#include "common/ceph_mutex.h"
+#include <random>
+
namespace boost::asio { class io_context; }
/* Overview
librados::IoCtx io;
int outstanding_io;
std::shared_ptr<ceph::io_exerciser::IoSequence> seq;
+ std::mt19937_64 rng;
+ int balanced_read_percentage;
void start_io();
void finish_io();
const std::string& pool, const std::string& primary_oid, const std::string& secondary_oid,
uint64_t block_size, int seed, int threads, ceph::mutex& lock,
ceph::condition_variable& cond, bool is_replicated_pool,
- bool ec_optimizations, ceph::io_exerciser::data_generation::GenerationType data_generation_type,
+ bool ec_optimizations, int balanced_read_percentage,
+ ceph::io_exerciser::data_generation::GenerationType data_generation_type,
std::shared_ptr<ceph::io_exerciser::IoSequence> seq = nullptr, bool delete_objects = true);
~RadosIo();
"\t\t remove",
"\t\t swap",
"\t\t copy",
- "\t\t read|write|failedwrite <off> <len>",
- "\t\t read2|write2|failedwrite2 <off> <len> <off> <len>",
- "\t\t read3|write3|failedwrite3 <off> <len> <off> <len> <off> <len>",
+ "\t\t read <off> <len> [balanced]",
+ "\t\t write|failedwrite <off> <len>",
+ "\t\t read2 <off> <len> <off> <len> [balanced]",
+ "\t\t write2|failedwrite2 <off> <len> <off> <len>",
+ "\t\t read3 <off> <len> <off> <len> <off> <len> [balanced]",
+ "\t\t write3|failedwrite3 <off> <len> <off> <len> <off> <len>",
"\t\t append",
"\t\t truncate",
"\t\t injecterror <io_type> <shard> <type> <good_count> <fail_count>",
"dont_delete_objects",
"Stops the IO exerciser from deleting the object it was running the test "
"against once the test finishes. Does not affect interactive mode")(
+ "balanced_read_percentage", po::value<int>(),
+ "The percentage of read operations that should be performed with "
+ "balanced reads enabled. 100 by default. Doesn't affect interactive mode.")(
"data_generation_type", po::value<std::string>(),
"Data generation type to use for write IOs. Default is HeaderedSeededRandom");
"the specified plugin type may not "
"support them");
}
+
+ ceph_assert(rc == 0);
}
if (allow_pool_ec_overwrites) {
SelectObjectSize& sos, SelectNumThreads& snt, SelectSeqRange& ssr,
std::mt19937_64& rng, ceph::mutex& lock,
ceph::condition_variable& cond, bool dryrun, bool verbose,
- std::optional<int> seqseed, bool testrecovery, bool checkconsistency,
- bool delete_objects, GenerationType data_generation_type)
- : rng(rng), verbose(verbose), seqseed(seqseed), testrecovery(testrecovery),
- checkconsistency(checkconsistency), delete_objects(delete_objects),
+ std::optional<int> seqseed, bool testrecovery, bool checkconsistency, bool delete_objects,
+ int balanced_read_percentage, GenerationType data_generation_type)
+ : rng(rng), verbose(verbose), seqseed(seqseed), testrecovery(testrecovery), checkconsistency(checkconsistency),
+ delete_objects(delete_objects), balanced_read_percentage(balanced_read_percentage),
data_generation_type(data_generation_type) {
if (dryrun) {
int model_seed = rng();
exerciser_model = std::make_unique<ceph::io_exerciser::RadosIo>(
rados, asio, pool, primary_oid, secondary_oid, sbs.select(), model_seed,
threads, lock, cond, spo.is_replicated_pool(),
- spo.get_allow_pool_ec_optimizations(), data_generation_type, seq, delete_objects);
+ spo.get_allow_pool_ec_optimizations(), balanced_read_percentage,
+ data_generation_type, seq, delete_objects);
dout(0) << "= " << primary_oid << " pool=" << pool << " threads=" << threads
<< " blocksize=" << exerciser_model->get_block_size() << " ="
<< dendl;
allow_pool_deep_scrubbing = vm.contains("allow_pool_deep_scrubbing");
allow_pool_scrubbing = vm.contains("allow_pool_scrubbing");
+ if (vm.contains("balanced_read_percentage")) {
+ balanced_read_percentage = vm["balanced_read_percentage"].as<int>();
+ if (balanced_read_percentage > 100) {
+ balanced_read_percentage = 100;
+ } else if (balanced_read_percentage < 0) {
+ balanced_read_percentage = 0;
+ }
+ if (interactive) {
+ dout(0) << "Balanced read percentage cannot be specified in interactive mode. "
+ << "Use the \"-b\" or \"balanced\" flag to make a balanced read operation in the interactive terminal." << dendl;
+ }
+ } else {
+ balanced_read_percentage = 100;
+ }
+
if (testrecovery && (num_object_pairs > 1)) {
throw std::invalid_argument("testrecovery option not allowed if parallel is"
" specified, except when parallel=1 is used");
rados, asio, pool, primary_object_name, secondary_object_name, sbs.select(), model_seed,
1, // 1 thread
lock, cond, spo.is_replicated_pool(),
- spo.get_allow_pool_ec_optimizations(),
+ spo.get_allow_pool_ec_optimizations(), balanced_read_percentage,
data_generation_type);
}
} else if (op == "read") {
uint64_t offset = get_numeric_token();
uint64_t length = get_numeric_token();
- ioop = ceph::io_exerciser::SingleReadOp::generate(offset, length);
+ std::optional<std::string> token = get_optional_token();
+ if (token.has_value() && (*token == "-b" || *token == "balanced")) {
+ ioop = ceph::io_exerciser::SingleReadOp::generate(offset, length, true);
+ } else {
+ ioop = ceph::io_exerciser::SingleReadOp::generate(offset, length, false);
+ }
} else if (op == "read2") {
uint64_t offset1 = get_numeric_token();
uint64_t length1 = get_numeric_token();
uint64_t offset2 = get_numeric_token();
uint64_t length2 = get_numeric_token();
- ioop = DoubleReadOp::generate(offset1, length1, offset2, length2);
+ std::optional<std::string> token = get_optional_token();
+ if (token.has_value() && (*token == "-b" || *token == "balanced")) {
+ ioop = DoubleReadOp::generate(offset1, length1, offset2, length2, true);
+ } else {
+ ioop = DoubleReadOp::generate(offset1, length1, offset2, length2, false);
+ }
} else if (op == "read3") {
uint64_t offset1 = get_numeric_token();
uint64_t length1 = get_numeric_token();
uint64_t length2 = get_numeric_token();
uint64_t offset3 = get_numeric_token();
uint64_t length3 = get_numeric_token();
- ioop = TripleReadOp::generate(offset1, length1, offset2, length2, offset3,
- length3);
+ std::optional<std::string> token = get_optional_token();
+ if (token.has_value() && (*token == "-b" || *token == "balanced")) {
+ ioop = TripleReadOp::generate(offset1, length1, offset2, length2, offset3,
+ length3, true);
+ } else {
+ ioop = TripleReadOp::generate(offset1, length1, offset2, length2, offset3,
+ length3, false);
+ }
} else if (op == "write") {
uint64_t offset = get_numeric_token();
uint64_t length = get_numeric_token();
test_objects.push_back(
std::make_shared<ceph::io_sequence::tester::TestObject>(
primary_name, secondary_name, rados, asio, sbs, spo, sos, snt, ssr, rng, lock, cond,
- dryrun, verbose, seqseed, testrecovery, checkconsistency,
- delete_objects, data_generation_type));
+ dryrun, verbose, seqseed, testrecovery, checkconsistency, delete_objects,
+ balanced_read_percentage, data_generation_type));
}
catch (const std::runtime_error &e) {
std::cerr << "Error: " << e.what() << std::endl;
bool testRecovery,
bool checkConsistency,
bool delete_objects,
+ int balanced_read_percentage,
GenerationType data_generation_type);
int get_num_io();
bool testrecovery;
bool checkconsistency;
bool delete_objects;
+ int balanced_read_percentage;
GenerationType data_generation_type;
};
ceph::split split = ceph::split("");
ceph::spliterator tokens;
+ int balanced_read_percentage;
+
void clear_tokens();
std::string get_token(bool allow_eof = false);
std::optional<std::string> get_optional_token();