From 2f02a8a567dcef47c9f5a720da4aa39191bd0c8c Mon Sep 17 00:00:00 2001 From: Joao Eduardo Luis Date: Wed, 11 Apr 2012 21:42:16 +0100 Subject: [PATCH] DeterministicOpSequence: Generate a reproducible sequence of operations. Signed-off-by: Joao Eduardo Luis --- .../filestore_test/DeterministicOpSequence.cc | 408 ++++++++++++++++++ .../filestore_test/DeterministicOpSequence.h | 85 ++++ 2 files changed, 493 insertions(+) create mode 100644 src/test/filestore_test/DeterministicOpSequence.cc create mode 100644 src/test/filestore_test/DeterministicOpSequence.h diff --git a/src/test/filestore_test/DeterministicOpSequence.cc b/src/test/filestore_test/DeterministicOpSequence.cc new file mode 100644 index 0000000000000..9b5619b0b8122 --- /dev/null +++ b/src/test/filestore_test/DeterministicOpSequence.cc @@ -0,0 +1,408 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* +* Ceph - scalable distributed file system +* +* Copyright (C) 2012 New Dream Network +* +* This is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License version 2.1, as published by the Free Software +* Foundation. See file COPYING. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "os/FileStore.h" +#include "common/ceph_argparse.h" +#include "global/global_init.h" +#include "common/debug.h" +#include +#include + +#include "DeterministicOpSequence.h" + +#include "common/config.h" + +#define dout_subsys ceph_subsys_filestore +#undef dout_prefix +#define dout_prefix *_dout << "deterministic_seq " + +DeterministicOpSequence::DeterministicOpSequence(FileStore *store, + std::string status) : TestFileStoreState(store), m_osr("OSR") { + if (!status.empty()) + m_status.open(status.c_str()); +} + +DeterministicOpSequence::~DeterministicOpSequence() { + // TODO Auto-generated destructor stub +} + +void DeterministicOpSequence::run_one_op(int op, rngen_t& gen) { + switch (op) { + case DSOP_TOUCH: + do_touch(gen); + break; + case DSOP_WRITE: + do_write(gen); + break; + case DSOP_COLL_MOVE: + do_coll_move(gen); + break; + case DSOP_COLL_ADD: + do_coll_add(gen); + break; + case DSOP_CLONE: + do_clone(gen); + break; + case DSOP_CLONE_RANGE: + do_clone_range(gen); + break; + } +} + +void DeterministicOpSequence::generate(int seed, int num_txs) +{ + std::ostringstream ss; + ss << "generate run " << num_txs << " --seed " << seed; + + if (m_status.is_open()) { + m_status << ss.str() << std::endl; + m_status.flush(); + } + + dout(0) << ss.str() << dendl; + + rngen_t gen(seed); + boost::uniform_int<> op_rng(DSOP_FIRST, DSOP_LAST); + + for (int i = 1; i <= num_txs; i++) { + int op = op_rng(gen); + _print_status(i, op); + dout(0) << "generate seq " << i << " op " << op << dendl; + run_one_op(op, gen); + } +} + +void DeterministicOpSequence::_print_status(int seq, int op) { + if (!m_status.is_open()) + return; + m_status << seq << " " << op << std::endl; + m_status.flush(); +} + +int DeterministicOpSequence::_gen_coll_id(rngen_t& gen) { + boost::uniform_int<> coll_rng(0, m_collections.size()-1); + return coll_rng(gen); +} + +int DeterministicOpSequence::_gen_obj_id(rngen_t& gen) { + boost::uniform_int<> obj_rng(0, m_num_objs_per_coll-1); + return obj_rng(gen); +} + +void DeterministicOpSequence::do_touch(rngen_t& gen) +{ + int coll_id = _gen_coll_id(gen); + int obj_id = _gen_obj_id(gen); + + coll_entry_t *entry = get_coll_at(coll_id); + hobject_t *obj = entry->touch_obj(obj_id); + + dout(0) << "do_touch " << entry->m_coll.to_str() << "/" << obj->oid.name + << dendl; + + _do_touch(entry->m_coll, *obj); +} + +void DeterministicOpSequence::_gen_random(rngen_t& gen, + size_t size, bufferlist& bl) { + + static const char alphanum[] = "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; + + boost::uniform_int<> char_rng(0, sizeof(alphanum)); + bufferptr bp(size); + for (unsigned int i = 0; i < size - 1; i++) { + bp[i] = alphanum[char_rng(gen)]; + } + bp[size - 1] = '\0'; + bl.append(bp); +} + +void DeterministicOpSequence::do_write(rngen_t& gen) +{ + int coll_id = _gen_coll_id(gen); + int obj_id = _gen_obj_id(gen); + + coll_entry_t *entry = get_coll_at(coll_id); + if (!entry) { + dout(0) << "do_write coll id " << coll_id << " non-existent" << dendl; + return; + } + hobject_t *obj = entry->touch_obj(obj_id); + if (!obj) { + dout(0) << "do_write " << entry->m_coll.to_str() + << " no object #" << obj_id << dendl; + return; + } + + boost::uniform_int<> size_rng(100, (2 << 19)); + size_t size = (size_t) size_rng(gen); + bufferlist bl; + _gen_random(gen, size, bl); + + dout(0) << "do_write " << entry->m_coll.to_str() << "/" << obj->oid.name + << " 0~" << size << dendl; + + _do_write(entry->m_coll, *obj, 0, bl.length(), bl); +} + +bool DeterministicOpSequence::_prepare_clone(rngen_t& gen, + coll_t& coll_ret, hobject_t& orig_obj_ret, hobject_t& new_obj_ret) +{ + int coll_id = _gen_coll_id(gen); + + coll_entry_t *entry = get_coll_at(coll_id); + if (!entry) { + dout(0) << "_prepare_clone coll id " << coll_id << " non-existent" << dendl; + return false; + } + + if (entry->m_objects.size() == 0) { + dout(0) << "_prepare_clone coll " << entry->m_coll.to_str() + << " has no objects to clone" << dendl; + return false; + } + + hobject_t *orig_obj = NULL; + boost::uniform_int<> orig_obj_rng(0, entry->m_objects.size()-1); + int orig_obj_pos = orig_obj_rng(gen); + map::iterator it = entry->m_objects.begin(); + for (int i = 0; it != entry->m_objects.end(); it++, i++) { + if (i == orig_obj_pos) { + orig_obj = it->second; + break; + } + } + + if (!orig_obj) { + dout(0) << "_prepare_clone coll " << entry->m_coll.to_str() + << " has no object in pos #" << orig_obj_pos << dendl; + return false; + } + + int new_obj_id = -1, i = 0; + do { + i++; + // we'll spin 10 times looking for a free object id. + int id = _gen_obj_id(gen); + map::iterator it = entry->m_objects.find(new_obj_id); + if (it == entry->m_objects.end()) { + new_obj_id = id; + break; + } + } while (i <= 10); + + hobject_t *new_obj = entry->touch_obj(new_obj_id); + + if (!orig_obj || !new_obj) { + dout(0) << "_prepare_clone coll " << entry->m_coll.to_str() + << " has no object (orig: " << orig_obj << " pos #" << orig_obj_pos + << " ; new: " << new_obj << " #" << new_obj_id << ")" << dendl; + return false; + } + + coll_ret = entry->m_coll; + orig_obj_ret = *orig_obj; + new_obj_ret = *new_obj; + + return true; +} + +void DeterministicOpSequence::do_clone(rngen_t& gen) +{ + coll_t coll; + hobject_t orig_obj, new_obj; + if (!_prepare_clone(gen, coll, orig_obj, new_obj)) { + return; + } + + dout(0) << "do_clone " << coll.to_str() << "/" << orig_obj.oid.name + << " => " << coll.to_str() << "/" << new_obj.oid.name << dendl; + + _do_clone(coll, orig_obj, new_obj); +} + +void DeterministicOpSequence::do_clone_range(rngen_t& gen) +{ + coll_t coll; + hobject_t orig_obj, new_obj; + if (!_prepare_clone(gen, coll, orig_obj, new_obj)) { + return; + } + + /* Whenever we have to make a clone_range() operation, just write to the + * object first, so we know we have something to clone in the said range. + * This may not be the best solution ever, but currently we're not keeping + * track of the written-to objects, and until we know for sure we really + * need to, let's just focus on the task at hand. + */ + + boost::uniform_int<> write_size_rng(100, (2 << 19)); + size_t size = (size_t) write_size_rng(gen); + bufferlist bl; + _gen_random(gen, size, bl); + _do_write(coll, orig_obj, 0, bl.length(), bl); + + boost::uniform_int<> clone_len(1, bl.length()); + size = (size_t) clone_len(gen); + + dout(0) << "do_clone_range " << coll.to_str() << "/" << orig_obj.oid.name + << " (0~" << size << ")" + << " => " << coll.to_str() << "/" << new_obj.oid.name + << " (0)" << dendl; + _do_clone_range(coll, orig_obj, new_obj, 0, size, 0); +} + +bool DeterministicOpSequence::_prepare_colls(rngen_t& gen, + coll_entry_t* &orig_coll, coll_entry_t* &new_coll) +{ + int orig_coll_id = _gen_coll_id(gen); + int new_coll_id = orig_coll_id; + do { + new_coll_id = _gen_coll_id(gen); + } while (new_coll_id == orig_coll_id); + + dout(0) << "_prepare_colls from coll id " << orig_coll_id + << " to coll id " << new_coll_id << dendl; + + orig_coll = get_coll_at(orig_coll_id); + new_coll = get_coll_at(new_coll_id); + + if (!orig_coll || !new_coll) { + dout(0) << "_prepare_colls " << " no such collection " + << "(orig: " << orig_coll << " id " << orig_coll_id + << " ; new: " << new_coll << " id" << new_coll_id << ")" << dendl; + return false; + } + + if (!orig_coll->m_objects.size()) { + dout(0) << "_prepare_colls coll " << orig_coll->m_coll.to_str() + << " has no objects to use" << dendl; + return false; + } + + return true; +} + + +void DeterministicOpSequence::do_coll_move(rngen_t& gen) +{ + coll_entry_t *orig_coll = NULL, *new_coll = NULL; + if (!_prepare_colls(gen, orig_coll, new_coll)) + return; + + if (!orig_coll || !new_coll) + return; + + boost::uniform_int<> obj_rng(0, orig_coll->m_objects.size()-1); + int obj_pos = obj_rng(gen); + int obj_key = -1; + hobject_t *obj = orig_coll->remove_obj_at(obj_pos, &obj_key); + + hobject_t *new_coll_old_obj = new_coll->replace_obj(obj_key, obj); + dout(0) << "do_coll_move " << orig_coll->m_coll.to_str() << "/" << obj->oid.name + << " => " << new_coll->m_coll.to_str() << "/" << obj->oid.name << dendl; + if (new_coll_old_obj != NULL) { + dout(0) << "do_coll_move replacing obj " << new_coll->m_coll.to_str() + << "/" << new_coll_old_obj->oid.name + << " with " << obj->oid.name << dendl; + delete new_coll_old_obj; + } + + _do_coll_move(new_coll->m_coll, orig_coll->m_coll, *obj); +} + +void DeterministicOpSequence::do_coll_rename(rngen_t& gen) { + +} + +void DeterministicOpSequence::do_coll_add(rngen_t& gen) +{ + coll_entry_t *orig_coll = NULL, *new_coll = NULL; + if (!_prepare_colls(gen, orig_coll, new_coll)) + return; + + if (!orig_coll || !new_coll) + return; + + boost::uniform_int<> obj_rng(0, orig_coll->m_objects.size()-1); + int obj_pos = obj_rng(gen); + int obj_key = -1; + hobject_t *obj = orig_coll->get_obj_at(obj_pos, &obj_key); + if (!obj) { + dout(0) << "do_coll_add coll " << orig_coll->m_coll.to_str() + << " has no object as pos #" << obj_pos << " (key " << obj_key << ")" + << dendl; + return; + } + dout(0) << "do_coll_add " << orig_coll->m_coll.to_str() << "/" << obj->oid.name + << " => " << new_coll->m_coll.to_str() << "/" << obj->oid.name << dendl; + + _do_coll_add(orig_coll->m_coll, new_coll->m_coll, *obj); +} + +void DeterministicOpSequence::_do_touch(coll_t coll, hobject_t& obj) { + ObjectStore::Transaction t; + t.touch(coll, obj); + m_store->apply_transaction(t); +} + +void DeterministicOpSequence::_do_write(coll_t coll, hobject_t& obj, + uint64_t off, uint64_t len, const bufferlist& data) +{ + ObjectStore::Transaction t; + t.write(coll, obj, off, len, data); + m_store->apply_transaction(t); +} + +void DeterministicOpSequence::_do_clone(coll_t coll, hobject_t& orig_obj, + hobject_t& new_obj) +{ + ObjectStore::Transaction t; + t.clone(coll, orig_obj, new_obj); + m_store->apply_transaction(t); +} + +void DeterministicOpSequence::_do_clone_range(coll_t coll, + hobject_t& orig_obj, hobject_t& new_obj, uint64_t srcoff, + uint64_t srclen, uint64_t dstoff) +{ + ObjectStore::Transaction t; + t.clone_range(coll, orig_obj, new_obj, srcoff, srclen, dstoff); + m_store->apply_transaction(t); +} + +void DeterministicOpSequence::_do_coll_move(coll_t new_coll, + coll_t old_coll, hobject_t& obj) +{ + ObjectStore::Transaction t; + t.collection_move(new_coll, old_coll, obj); + m_store->apply_transaction(t); +} + +void DeterministicOpSequence::_do_coll_add(coll_t orig_coll, coll_t new_coll, + hobject_t& obj) +{ + ObjectStore::Transaction t; + t.collection_add(orig_coll, new_coll, obj); + m_store->apply_transaction(t); +} diff --git a/src/test/filestore_test/DeterministicOpSequence.h b/src/test/filestore_test/DeterministicOpSequence.h new file mode 100644 index 0000000000000..b0e930b336861 --- /dev/null +++ b/src/test/filestore_test/DeterministicOpSequence.h @@ -0,0 +1,85 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* +* Ceph - scalable distributed file system +* +* Copyright (C) 2012 New Dream Network +* +* This is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License version 2.1, as published by the Free Software +* Foundation. See file COPYING. +*/ +#ifndef FILESTORE_DTRMNSTC_SEQ_OPS_H_ +#define FILESTORE_DTRMNSTC_SEQ_OPS_H_ + +#include +#include +#include +#include "os/FileStore.h" +#include +#include +#include + +#include "TestFileStoreState.h" + +typedef boost::mt11213b rngen_t; + +class DeterministicOpSequence : public TestFileStoreState { + + public: + DeterministicOpSequence(FileStore *store, std::string status = std::string()); + virtual ~DeterministicOpSequence(); + + virtual void generate(int seed, int num_txs); + + protected: + enum { + DSOP_TOUCH = 0, + DSOP_WRITE = 1, + DSOP_CLONE = 2, + DSOP_CLONE_RANGE = 3, + DSOP_OBJ_REMOVE = 4, + DSOP_COLL_MOVE = 5, + DSOP_COLL_RENAME = 6, + DSOP_COLL_ADD = 7, + + DSOP_FIRST = DSOP_TOUCH, + DSOP_LAST = DSOP_COLL_ADD, + }; + + ObjectStore::Sequencer m_osr; + std::ofstream m_status; + + void run_one_op(int op, rngen_t& gen); + void do_touch(rngen_t& gen); + void do_write(rngen_t& gen); + void do_clone(rngen_t& gen); + void do_clone_range(rngen_t& gen); + void do_coll_move(rngen_t& gen); + void do_coll_rename(rngen_t& gen); + void do_coll_add(rngen_t& gen); + + virtual void _do_touch(coll_t coll, hobject_t& obj); + virtual void _do_write(coll_t coll, hobject_t& obj, uint64_t off, + uint64_t len, const bufferlist& data); + virtual void _do_clone(coll_t coll, hobject_t& orig_obj, hobject_t& new_obj); + virtual void _do_clone_range(coll_t coll, hobject_t& orig_obj, + hobject_t& new_obj, uint64_t srcoff, uint64_t srclen, uint64_t dstoff); + virtual void _do_coll_move(coll_t new_coll, coll_t old_coll, hobject_t& obj); + virtual void _do_coll_add(coll_t orig_coll, coll_t new_coll, hobject_t& obj); + + int _gen_coll_id(rngen_t& gen); + int _gen_obj_id(rngen_t& gen); + void _gen_random(rngen_t& gen, size_t size, bufferlist& bl); + void _print_status(int seq, int op); + + private: + bool _prepare_clone(rngen_t& gen, coll_t& coll_ret, + hobject_t& orig_obj_ret, hobject_t& new_obj_ret); + bool _prepare_colls(rngen_t& gen, + coll_entry_t* &orig_coll, coll_entry_t* &new_coll); +}; + + +#endif /* FILESTORE_DTRMNSTC_SEQ_OPS_H_ */ -- 2.39.5