TEST_OP_CACHE_TRY_FLUSH,
TEST_OP_CACHE_EVICT,
TEST_OP_APPEND,
- TEST_OP_APPEND_EXCL
+ TEST_OP_APPEND_EXCL,
+ TEST_OP_SET_REDIRECT,
+ TEST_OP_UNSET_REDIRECT
};
class TestWatchContext : public librados::WatchCtx2 {
set<string> oid_not_in_use;
set<string> oid_flushing;
set<string> oid_not_flushing;
+ set<string> oid_redirect_not_in_use;
+ set<string> oid_redirect_in_use;
SharedPtrRegistry<int, int> snaps_in_use;
int current_snap;
string pool_name;
bool pool_snaps;
bool write_fadvise_dontneed;
int snapname_num;
+ map<string,string > redirect_objs;
RadosTestContext(const string &pool_name,
int max_in_flight,
return false;
}
+ void update_object_redirect_target(const string &oid, const string &target)
+ {
+ redirect_objs[oid] = target;
+ }
+
bool object_existed_at(const string &oid, int snap = -1) const
{
ObjectDesc contents;
}
};
+class SetRedirectOp : public TestOp {
+public:
+ string oid, oid_tgt, tgt_pool_name;
+ ObjectDesc src_value, tgt_value;
+ librados::ObjectWriteOperation op;
+ librados::ObjectReadOperation rd_op;
+ librados::AioCompletion *comp;
+ ceph::shared_ptr<int> in_use;
+ int done;
+ int r;
+ SetRedirectOp(int n,
+ RadosTestContext *context,
+ const string &oid,
+ const string &oid_tgt,
+ const string &tgt_pool_name,
+ TestOpStat *stat = 0)
+ : TestOp(n, context, stat),
+ oid(oid), oid_tgt(oid_tgt), tgt_pool_name(tgt_pool_name),
+ comp(NULL), done(0),
+ r(0)
+ {}
+
+ void _begin() override
+ {
+ Mutex::Locker l(context->state_lock);
+ context->oid_in_use.insert(oid);
+ context->oid_not_in_use.erase(oid);
+ context->oid_redirect_in_use.insert(oid_tgt);
+ context->oid_redirect_not_in_use.erase(oid_tgt);
+
+ context->find_object(oid, &src_value);
+ if(!context->redirect_objs[oid].empty()) {
+ /* update target's user_version */
+ rd_op.stat(NULL, NULL, NULL);
+ comp = context->rados.aio_create_completion();
+ context->io_ctx.aio_operate(context->prefix+oid_tgt, comp, &rd_op,
+ librados::OPERATION_ORDER_READS_WRITES,
+ NULL);
+ comp->wait_for_safe();
+ context->update_object_version(oid_tgt, comp->get_version64());
+ comp->release();
+
+ /* unset redirect target */
+ comp = context->rados.aio_create_completion();
+ bool present = !src_value.deleted();
+ context->remove_object(oid);
+ op.remove();
+ context->io_ctx.aio_operate(context->prefix+oid, comp, &op,
+ librados::OPERATION_ORDER_READS_WRITES |
+ librados::OPERATION_IGNORE_REDIRECT);
+ comp->wait_for_safe();
+ if ((r = comp->get_return_value())) {
+ if (!(r == -ENOENT && !present)) {
+ cerr << "r is " << r << " while deleting " << oid << " and present is " << present << std::endl;
+ ceph_abort();
+ }
+ }
+ comp->release();
+
+ context->oid_redirect_not_in_use.insert(context->redirect_objs[oid]);
+ context->oid_redirect_in_use.erase(context->redirect_objs[oid]);
+
+ /* copy_from oid_tgt --> oid */
+ comp = context->rados.aio_create_completion();
+ context->find_object(oid_tgt, &tgt_value);
+ string src = context->prefix+oid_tgt;
+ op.copy_from(src.c_str(), context->io_ctx, tgt_value.version);
+ context->io_ctx.aio_operate(context->prefix+oid, comp, &op,
+ librados::OPERATION_ORDER_READS_WRITES);
+ comp->wait_for_safe();
+ if ((r = comp->get_return_value())) {
+ cerr << "Error: oid " << oid << " copy_from " << oid_tgt << " returned error code "
+ << r << std::endl;
+ ceph_abort();
+ }
+ context->update_object_full(oid, tgt_value);
+ context->update_object_version(oid, comp->get_version64());
+ comp->release();
+ }
+
+ comp = context->rados.aio_create_completion();
+ rd_op.stat(NULL, NULL, NULL);
+ context->io_ctx.aio_operate(context->prefix+oid, comp, &rd_op,
+ librados::OPERATION_ORDER_READS_WRITES |
+ librados::OPERATION_IGNORE_REDIRECT,
+ NULL);
+ comp->wait_for_safe();
+ if ((r = comp->get_return_value()) && !src_value.deleted()) {
+ cerr << "Error: oid " << oid << " stat returned error code "
+ << r << std::endl;
+ ceph_abort();
+ }
+ context->update_object_version(oid, comp->get_version64());
+ comp->release();
+
+ context->find_object(oid, &src_value);
+ context->find_object(oid_tgt, &tgt_value);
+
+ if (!src_value.deleted() && !tgt_value.deleted())
+ context->update_object_full(oid, tgt_value);
+
+ if (src_value.version != 0 && !src_value.deleted())
+ op.assert_version(src_value.version);
+ op.set_redirect(context->prefix+oid_tgt, context->io_ctx, tgt_value.version);
+
+ pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
+ new pair<TestOp*, TestOp::CallbackInfo*>(this,
+ new TestOp::CallbackInfo(0));
+ comp = context->rados.aio_create_completion((void*) cb_arg, NULL,
+ &write_callback);
+ context->io_ctx.aio_operate(context->prefix+oid, comp, &op,
+ librados::OPERATION_ORDER_READS_WRITES);
+ }
+
+ void _finish(CallbackInfo *info) override
+ {
+ Mutex::Locker l(context->state_lock);
+
+ if (info->id == 0) {
+ assert(comp->is_complete());
+ cout << num << ": finishing set_redirect to oid " << oid << std::endl;
+ if ((r = comp->get_return_value())) {
+ if (r == -ENOENT && src_value.deleted()) {
+ cout << num << ": got expected ENOENT (src dne)" << std::endl;
+ } else {
+ cerr << "Error: oid " << oid << " set_redirect " << oid_tgt << " returned error code "
+ << r << std::endl;
+ ceph_abort();
+ }
+ } else {
+ context->update_object_redirect_target(oid, oid_tgt);
+ context->update_object_version(oid, comp->get_version64());
+ }
+ }
+
+ if (++done == 1) {
+ context->oid_in_use.erase(oid);
+ context->oid_not_in_use.insert(oid);
+ context->kick();
+ }
+ }
+
+ bool finished() override
+ {
+ return done == 1;
+ }
+
+ string getType() override
+ {
+ return "SetRedirectOp";
+ }
+};
+
+class UnsetRedirectOp : public TestOp {
+public:
+ string oid;
+ librados::ObjectWriteOperation op;
+ librados::AioCompletion *completion;
+ librados::AioCompletion *comp;
+
+ UnsetRedirectOp(int n,
+ RadosTestContext *context,
+ const string &oid,
+ TestOpStat *stat = 0)
+ : TestOp(n, context, stat), oid(oid)
+ {}
+
+ void _begin() override
+ {
+ context->state_lock.Lock();
+ if (context->get_watch_context(oid)) {
+ context->kick();
+ context->state_lock.Unlock();
+ return;
+ }
+
+ ObjectDesc contents;
+ context->find_object(oid, &contents);
+ bool present = !contents.deleted();
+
+ context->oid_in_use.insert(oid);
+ context->oid_not_in_use.erase(oid);
+ context->seq_num++;
+
+ context->remove_object(oid);
+
+ context->state_lock.Unlock();
+
+ comp = context->rados.aio_create_completion();
+ op.remove();
+ context->io_ctx.aio_operate(context->prefix+oid, comp, &op,
+ librados::OPERATION_ORDER_READS_WRITES |
+ librados::OPERATION_IGNORE_REDIRECT);
+ comp->wait_for_safe();
+ int r = comp->get_return_value();
+ if (r && !(r == -ENOENT && !present)) {
+ cerr << "r is " << r << " while deleting " << oid << " and present is " << present << std::endl;
+ ceph_abort();
+ }
+
+ context->state_lock.Lock();
+ context->oid_in_use.erase(oid);
+ context->oid_not_in_use.insert(oid);
+ if(!context->redirect_objs[oid].empty()) {
+ context->oid_redirect_not_in_use.insert(context->redirect_objs[oid]);
+ context->oid_redirect_in_use.erase(context->redirect_objs[oid]);
+ context->update_object_redirect_target(oid, string());
+ }
+ context->kick();
+ context->state_lock.Unlock();
+ }
+
+ string getType() override
+ {
+ return "UnsetRedirectOp";
+ }
+};
+
class HitSetListOp : public TestOp {
librados::AioCompletion *comp1, *comp2;
uint32_t hash;
TestOpStat *stats,
int max_seconds,
bool ec_pool,
- bool balance_reads) :
+ bool balance_reads,
+ bool set_redirect) :
m_nextop(NULL), m_op(0), m_ops(ops), m_seconds(max_seconds),
m_objects(objects), m_stats(stats),
m_total_weight(0),
m_ec_pool(ec_pool),
- m_balance_reads(balance_reads)
+ m_balance_reads(balance_reads),
+ m_set_redirect(set_redirect)
{
m_start = time(0);
for (map<TestOpType, unsigned int>::const_iterator it = op_weights.begin();
m_weight_sums.insert(pair<TestOpType, unsigned int>(it->first,
m_total_weight));
}
+ if (m_set_redirect) {
+ /* create redirect objects + set-redirect*/
+ m_redirect_objects = objects*2; // for copy_from + set-redirect test
+ m_initial_redirected_objects = objects;
+ m_ops = ops+m_redirect_objects+m_initial_redirected_objects;
+ }
}
TestOp *next(RadosTestContext &context) override
} else if (m_op >= m_ops) {
return NULL;
}
+
+ if (m_set_redirect) {
+ /*
+ * set-redirect test
+ * 1. create objects (copy from)
+ * 2. set-redirect
+ */
+ int create_objects_end = m_objects + m_redirect_objects;
+ int set_redirect_end = create_objects_end + m_initial_redirected_objects;
+
+ if (m_op <= create_objects_end) {
+ stringstream oid;
+ int _oid = m_op;
+ oid << _oid;
+ if ((_oid) % 2) {
+ oid << " " << string(300, 'o');
+ }
+ stringstream oid2;
+ int _oid2 = _oid - m_objects;
+ oid2 << _oid2;
+ if ((_oid2) % 2) {
+ oid2 << " " << string(300, 'o');
+ }
+ cout << m_op << ": " << "(create redirect oid) copy_from oid " << oid.str()
+ << " from oid " << oid2.str() << std::endl;
+ return new CopyFromOp(m_op, &context, oid.str(), oid2.str(), m_stats);
+ } else if (m_op <= set_redirect_end) {
+ stringstream oid;
+ int _oid = m_op-create_objects_end;
+ oid << _oid;
+ if ((_oid) % 2) {
+ oid << " " << string(300, 'o');
+ }
+ stringstream oid2;
+ int _oid2 = _oid + m_objects;
+ oid2 << _oid2;
+ if ((_oid2) % 2) {
+ oid2 << " " << string(300, 'o');
+ }
+ cout << m_op << ": " << "set_redirect oid " << oid.str() << " target oid "
+ << oid2.str() << std::endl;
+ return new SetRedirectOp(m_op, &context, oid.str(), oid2.str(), context.pool_name);
+ }
+
+ if (!context.oid_redirect_not_in_use.size() && m_op == set_redirect_end+1) {
+ for (int t_op = m_objects+1; t_op <= create_objects_end; t_op++) {
+ stringstream oid;
+ oid << t_op;
+ if (t_op % 2) {
+ oid << " " << string(300, 'o');
+ }
+ context.oid_not_flushing.erase(oid.str());
+ context.oid_not_in_use.erase(oid.str());
+ context.oid_in_use.erase(oid.str());
+ if (t_op > m_objects + m_initial_redirected_objects) {
+ context.oid_redirect_not_in_use.insert(oid.str());
+ }
+ }
+ }
+ }
if (m_nextop) {
retval = m_nextop;
<< context.current_snap << std::endl;
return new WriteOp(m_op, &context, oid, true, true, m_stats);
+ case TEST_OP_SET_REDIRECT:
+ oid = *(rand_choose(context.oid_not_in_use));
+ oid2 = *(rand_choose(context.oid_redirect_not_in_use));
+ cout << m_op << ": " << "set_redirect oid " << oid << " target oid " << oid2 << std::endl;
+ return new SetRedirectOp(m_op, &context, oid, oid2, context.pool_name, m_stats);
+
+ case TEST_OP_UNSET_REDIRECT:
+ oid = *(rand_choose(context.oid_not_in_use));
+ cout << m_op << ": " << "unset_redirect oid " << oid << std::endl;
+ return new UnsetRedirectOp(m_op, &context, oid, m_stats);
+
default:
cerr << m_op << ": Invalid op type " << type << std::endl;
ceph_abort();
unsigned int m_total_weight;
bool m_ec_pool;
bool m_balance_reads;
+ bool m_set_redirect;
+ int m_redirect_objects;
+ int m_initial_redirected_objects;
};
int main(int argc, char **argv)
{ TEST_OP_CACHE_EVICT, "cache_evict", true },
{ TEST_OP_APPEND, "append", true },
{ TEST_OP_APPEND_EXCL, "append_excl", true },
+ { TEST_OP_SET_REDIRECT, "set_redirect", true },
+ { TEST_OP_UNSET_REDIRECT, "unset_redirect", true },
{ TEST_OP_READ /* grr */, NULL },
};
bool no_omap = false;
bool no_sparse = false;
bool balance_reads = false;
+ bool set_redirect = false;
for (int i = 1; i < argc; ++i) {
if (strcmp(argv[i], "--max-ops") == 0)
cout << "adding op weight " << op_types[j].name << " -> " << weight << std::endl;
op_weights.insert(pair<TestOpType, unsigned int>(op_types[j].op, weight));
}
+ } else if (strcmp(argv[i], "--set_redirect") == 0) {
+ set_redirect = true;
} else {
cerr << "unknown arg " << argv[i] << std::endl;
//usage();
WeightedTestGenerator gen = WeightedTestGenerator(
ops, objects,
op_weights, &stats, max_seconds,
- ec_pool, balance_reads);
+ ec_pool, balance_reads, set_redirect);
int r = context.init();
if (r < 0) {
cerr << "Error initializing rados test context: "