test_trans_LDADD = libos.la $(LIBGLOBAL_LDA)
bin_DEBUGPROGRAMS += test_trans
-testsnaps_SOURCES = test/osd/TestSnaps.cc
+testsnaps_SOURCES = test/osd/TestSnaps.cc test/osd/TestOpStat.cc test/osd/Object.cc test/osd/RadosModel.cc
testsnaps_LDADD = librados.la $(LIBGLOBAL_LDA)
bin_DEBUGPROGRAMS += testsnaps
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+#include "include/interval_set.h"
+#include "include/buffer.h"
+#include <list>
+#include <map>
+#include <set>
+
+#include "Object.h"
+
+ostream &operator<<(ostream &out, const ContDesc &rhs)
+{
+ return out << "ObjNum: " << rhs.objnum
+ << " snap: " << rhs.cursnap
+ << " seqnum: " << rhs.seqnum
+ << " prefix: " << rhs.prefix;
+}
+
+void VarLenGenerator::get_ranges(const ContDesc &cont, interval_set<uint64_t> &out) {
+ RandWrap rand(cont.seqnum);
+ uint64_t pos = get_header_length(cont);
+ uint64_t limit = get_length(cont);
+ out.insert(0, pos);
+ bool include = false;
+ while (pos < limit) {
+ uint64_t segment_length = (rand() % (max_stride_size - min_stride_size)) + min_stride_size;
+ assert(segment_length < max_stride_size);
+ assert(segment_length >= min_stride_size);
+ if (segment_length + pos >= limit) {
+ segment_length = limit - pos;
+ }
+ if (include) {
+ out.insert(pos, segment_length);
+ include = false;
+ } else {
+ include = true;
+ }
+ pos += segment_length;
+ }
+}
+
+void VarLenGenerator::write_header(const ContDesc &in, bufferlist &output) {
+ int data[6];
+ data[0] = 0xDEADBEEF;
+ data[1] = in.objnum;
+ data[2] = in.cursnap;
+ data[3] = (int)in.seqnum;
+ data[4] = in.prefix.size();
+ data[5] = 0xDEADBEEF;
+ output.append((char *)data, sizeof(data));
+ output.append(in.prefix.c_str(), in.prefix.size());
+ output.append((char *)data, sizeof(data[0]));
+}
+
+bool VarLenGenerator::read_header(bufferlist::iterator &p, ContDesc &out) {
+ try {
+ int data[6];
+ p.copy(sizeof(data), (char *)data);
+ if ((unsigned)data[0] != 0xDEADBEEF || (unsigned)data[5] != 0xDEADBEEF) return false;
+ out.objnum = data[1];
+ out.cursnap = data[2];
+ out.seqnum = (unsigned) data[3];
+ int prefix_size = data[4];
+ if (prefix_size >= 1000 || prefix_size <= 0) {
+ std::cerr << "prefix size is " << prefix_size << std::endl;
+ return false;
+ }
+ char buffer[1000];
+ p.copy(prefix_size, buffer);
+ buffer[prefix_size] = 0;
+ out.prefix = buffer;
+ unsigned test;
+ p.copy(sizeof(test), (char *)&test);
+ if (test != 0xDEADBEEF) return false;
+ } catch (ceph::buffer::end_of_buffer e) {
+ std::cerr << "end_of_buffer" << endl;
+ return false;
+ }
+ return true;
+}
+
+ObjectDesc::iterator &ObjectDesc::iterator::advance(bool init) {
+ assert(pos < limit);
+ assert(!end());
+ if (!init) {
+ pos++;
+ }
+ if (end()) {
+ return *this;
+ }
+ while (pos == limit) {
+ limit = *stack.begin();
+ stack.pop_front();
+ cur_cont--;
+ }
+
+ if (cur_cont == obj.layers.end()) {
+ return *this;
+ }
+
+ interval_set<uint64_t> ranges;
+ cont_gen->get_ranges(*cur_cont, ranges);
+ while (!ranges.contains(pos)) {
+ stack.push_front(limit);
+ uint64_t next;
+ if (pos >= ranges.range_end()) {
+ next = limit;
+ } else {
+ next = ranges.start_after(pos);
+ }
+ if (next < limit) {
+ limit = next;
+ }
+ cur_cont++;
+ if (cur_cont == obj.layers.end()) {
+ break;
+ }
+
+ ranges.clear();
+ cont_gen->get_ranges(*cur_cont, ranges);
+ }
+
+ if (cur_cont == obj.layers.end()) {
+ return *this;
+ }
+
+ if (!cont_iters.count(*cur_cont)) {
+ cont_iters.insert(pair<ContDesc,ContentsGenerator::iterator>(*cur_cont,
+ cont_gen->get_iterator(*cur_cont)));
+ }
+ map<ContDesc,ContentsGenerator::iterator>::iterator j = cont_iters.find(*cur_cont);
+ assert(j != cont_iters.end());
+ j->second.seek(pos);
+ return *this;
+}
+
+const ContDesc &ObjectDesc::most_recent() {
+ return *layers.begin();
+}
+
+void ObjectDesc::update(const ContDesc &next) {
+ layers.push_front(next);
+ return;
+ interval_set<uint64_t> fall_through;
+ fall_through.insert(0, cont_gen->get_length(next));
+ for (list<ContDesc>::iterator i = layers.begin();
+ i != layers.end();
+ ) {
+ interval_set<uint64_t> valid;
+ cont_gen->get_ranges(*i, valid);
+ valid.intersection_of(fall_through);
+ if (valid.empty()) {
+ layers.erase(i++);
+ continue;
+ }
+ fall_through.subtract(valid);
+ ++i;
+ }
+}
+
+bool ObjectDesc::check(bufferlist &to_check) {
+ iterator i = begin();
+ for (bufferlist::iterator p = to_check.begin();
+ !p.end();
+ ++p, ++i) {
+ if (i.end()) {
+ std::cout << "reached end of iterator first" << std::endl;
+ return false;
+ }
+ if (*i != *p) {
+ std::cout << "incorrect buffer num layers: " << layers.size() << std::endl;
+ return false;
+ }
+ }
+ return true;
+}
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+#include "include/interval_set.h"
+#include "include/buffer.h"
+#include <list>
+#include <map>
+#include <set>
+
+#ifndef OBJECT_H
+#define OBJECT_H
+
+class ContDesc {
+public:
+ int objnum;
+ int cursnap;
+ unsigned seqnum;
+ string prefix;
+ string oid;
+
+ ContDesc() :
+ objnum(0), cursnap(0),
+ seqnum(0), prefix("") {}
+
+ ContDesc(int objnum,
+ int cursnap,
+ unsigned seqnum,
+ const string &prefix) :
+ objnum(objnum), cursnap(cursnap),
+ seqnum(seqnum), prefix(prefix) {}
+
+ bool operator==(const ContDesc &rhs) {
+ return (rhs.objnum == objnum &&
+ rhs.cursnap == cursnap &&
+ rhs.seqnum == seqnum &&
+ rhs.prefix == prefix &&
+ rhs.oid == oid);
+ }
+
+ bool operator<(const ContDesc &rhs) const {
+ return seqnum < rhs.seqnum;
+ }
+
+ bool operator!=(const ContDesc &rhs) {
+ return !((*this) == rhs);
+ }
+};
+
+ostream &operator<<(ostream &out, const ContDesc &rhs);
+
+class ContentsGenerator {
+public:
+
+ class iterator_impl {
+ public:
+ virtual char operator*() = 0;
+ virtual iterator_impl &operator++() = 0;
+ virtual void seek(uint64_t pos) = 0;
+ virtual bool end() = 0;
+ virtual ContDesc get_cont() const = 0;
+ virtual uint64_t get_pos() const = 0;
+ virtual ~iterator_impl() {};
+ };
+
+ class iterator {
+ public:
+ ContentsGenerator *parent;
+ iterator_impl *impl;
+ char operator *() { return **impl; }
+ iterator &operator++() { ++(*impl); return *this; };
+ void seek(uint64_t pos) { impl->seek(pos); }
+ bool end() { return impl->end(); }
+ ~iterator() { parent->put_iterator_impl(impl); }
+ iterator(const iterator &rhs) : parent(rhs.parent) {
+ impl = parent->dup_iterator_impl(rhs.impl);
+ }
+ iterator &operator=(const iterator &rhs) {
+ iterator new_iter(rhs);
+ swap(new_iter);
+ return *this;
+ }
+ void swap(iterator &other) {
+ ContentsGenerator *otherparent = other.parent;
+ other.parent = parent;
+ parent = otherparent;
+
+ iterator_impl *otherimpl = other.impl;
+ other.impl = impl;
+ impl = otherimpl;
+ }
+ iterator(ContentsGenerator *parent, iterator_impl *impl) :
+ parent(parent), impl(impl) {}
+ };
+
+ virtual bool read_header(bufferlist::iterator& input,
+ ContDesc &output) = 0;
+
+ virtual uint64_t get_length(const ContDesc &in) = 0;
+
+ virtual void get_ranges(const ContDesc &in, interval_set<uint64_t> &ranges) = 0;
+
+ virtual iterator_impl *get_iterator_impl(const ContDesc &in) = 0;
+
+ virtual iterator_impl *dup_iterator_impl(const iterator_impl *in) = 0;
+
+ virtual void put_iterator_impl(iterator_impl *in) = 0;
+
+ virtual ~ContentsGenerator() {};
+
+ iterator get_iterator(const ContDesc &in) {
+ return iterator(this, get_iterator_impl(in));
+ }
+};
+
+class VarLenGenerator : public ContentsGenerator {
+public:
+ class RandWrap {
+ public:
+ unsigned int state;
+ RandWrap(unsigned int seed)
+ {
+ state = seed;
+ }
+
+ int operator()()
+ {
+ return rand_r(&state);
+ }
+ };
+
+ class iterator_impl : public ContentsGenerator::iterator_impl {
+ public:
+ uint64_t pos;
+ ContDesc cont;
+ RandWrap rand;
+ bufferlist header;
+ bufferlist::iterator header_pos;
+ VarLenGenerator *cont_gen;
+ char current;
+ iterator_impl(const ContDesc &cont, VarLenGenerator *cont_gen) :
+ pos(0), cont(cont), rand(cont.seqnum), cont_gen(cont_gen) {
+ cont_gen->write_header(cont, header);
+ header_pos = header.begin();
+ current = *header_pos;
+ ++header_pos;
+ }
+
+ virtual ContDesc get_cont() const { return cont; }
+ virtual uint64_t get_pos() const { return pos; }
+
+ iterator_impl &operator++() {
+ assert(!end());
+ pos++;
+ if (end()) {
+ return *this;
+ }
+ if (header_pos.end()) {
+ current = rand();
+ } else {
+ current = *header_pos;
+ ++header_pos;
+ }
+ return *this;
+ }
+
+ char operator*() {
+ return current;
+ }
+
+ void seek(uint64_t _pos) {
+ if (_pos < pos) {
+ iterator_impl begin = iterator_impl(cont, cont_gen);
+ begin.seek(_pos);
+ *this = begin;
+ }
+ while (pos < _pos) {
+ ++(*this);
+ }
+ }
+
+ bool end() {
+ return pos >= cont_gen->get_length(cont);
+ }
+ };
+
+ void get_ranges(const ContDesc &cont, interval_set<uint64_t> &out);
+
+ ContentsGenerator::iterator_impl *get_iterator_impl(const ContDesc &in) {
+ VarLenGenerator::iterator_impl *i = new iterator_impl(in, this);
+ return i;
+ }
+
+ void put_iterator_impl(ContentsGenerator::iterator_impl *in) {
+ delete in;
+ }
+
+ ContentsGenerator::iterator_impl *dup_iterator_impl(const ContentsGenerator::iterator_impl *in) {
+ ContentsGenerator::iterator_impl *retval = get_iterator_impl(in->get_cont());
+ retval->seek(in->get_pos());
+ return retval;
+ }
+
+ int get_header_length(const ContDesc &in) {
+ return 7*sizeof(int) + in.prefix.size();
+ }
+
+ uint64_t get_length(const ContDesc &in) {
+ RandWrap rand(in.seqnum);
+ return (rand() % length) + get_header_length(in);
+ }
+
+ void write_header(const ContDesc &in, bufferlist &output);
+
+ bool read_header(bufferlist::iterator &p, ContDesc &out);
+ uint64_t length;
+ uint64_t min_stride_size;
+ uint64_t max_stride_size;
+ VarLenGenerator(int length) :
+ length(length), min_stride_size(length/10), max_stride_size(length/5) {}
+};
+
+class ObjectDesc {
+public:
+ ObjectDesc(ContentsGenerator *cont_gen) :
+ layers(), cont_gen(cont_gen) {};
+ ObjectDesc(const ContDesc &init, ContentsGenerator *cont_gen) :
+ layers(), cont_gen(cont_gen) {
+ layers.push_front(init);
+ };
+
+ class iterator {
+ public:
+ uint64_t pos;
+ ObjectDesc &obj;
+ ContentsGenerator *cont_gen;
+ list<uint64_t> stack;
+ map<ContDesc,ContentsGenerator::iterator> cont_iters;
+ uint64_t limit;
+ list<ContDesc>::iterator cur_cont;
+
+ iterator(ObjectDesc &obj, ContentsGenerator *cont_gen) :
+ pos(0), obj(obj), cont_gen(cont_gen) {
+ limit = cont_gen->get_length(*obj.layers.begin());
+ cur_cont = obj.layers.begin();
+ advance(true);
+ }
+
+ iterator &advance(bool init);
+ iterator &operator++() {
+ return advance(false);
+ }
+
+ char operator*() {
+ if (cur_cont == obj.layers.end()) {
+ return '\0';
+ } else {
+ map<ContDesc,ContentsGenerator::iterator>::iterator j = cont_iters.find(*cur_cont);
+ assert(j != cont_iters.end());
+ return *(j->second);
+ }
+ }
+
+ bool end() {
+ return pos == cont_gen->get_length(*obj.layers.begin());
+ }
+
+ void seek(uint64_t _pos) {
+ if (_pos < pos) {
+ assert(0);
+ }
+ while (pos < _pos) {
+ ++(*this);
+ }
+ }
+ };
+
+ iterator begin() {
+ return iterator(*this, this->cont_gen);
+ }
+
+ void update(const ContDesc &next);
+ bool check(bufferlist &to_check);
+ const ContDesc &most_recent();
+private:
+ list<ContDesc> layers;
+ ContentsGenerator *cont_gen;
+ ObjectDesc();
+};
+
+#endif
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+#include "include/interval_set.h"
+#include "include/buffer.h"
+#include <list>
+#include <map>
+#include <set>
+#include "RadosModel.h"
+#include "TestOpStat.h"
+
+
+void TestOp::begin()
+{
+ //if (stat) stat->begin(this);
+ _begin();
+}
+
+void TestOp::finish()
+{
+ _finish();
+ //if (stat && finished()) stat->end(this);
+}
+
+void callback(librados::completion_t cb, void *arg) {
+ TestOp *op = (TestOp *) arg;
+ op->finish();
+}
#include <set>
#include <list>
#include <string>
+#include <string.h>
#include <stdlib.h>
#include <time.h>
+#include "Object.h"
+#include "TestOpStat.h"
-using namespace std;
+#ifndef RADOSMODEL_H
+#define RADOSMODEL_H
-/* Snap creation/removal tester
- */
+using namespace std;
-struct RadosTestContext;
-struct TestOpStat;
+class RadosTestContext;
+class TestOpStat;
template <typename T>
-typename T::iterator rand_choose(T &cont)
-{
+typename T::iterator rand_choose(T &cont) {
if (cont.size() == 0) {
return cont.end();
}
return retval;
}
-struct TestOp
-{
+class TestOp {
+public:
RadosTestContext *context;
TestOpStat *stat;
bool done;
void finish();
};
-struct TestOpStat
-{
- Mutex stat_lock;
-
- TestOpStat() : stat_lock("TestOpStat lock") {}
-
- static uint64_t gettime()
- {
- timeval t;
- gettimeofday(&t,0);
- return (1000000*t.tv_sec) + t.tv_usec;
- }
-
- struct TypeStatus
- {
- map<TestOp*,uint64_t> inflight;
- multiset<uint64_t> latencies;
- void begin(TestOp *in)
- {
- assert(!inflight.count(in));
- inflight[in] = gettime();
- }
-
- void end(TestOp *in)
- {
- assert(inflight.count(in));
- uint64_t curtime = gettime();
- latencies.insert(curtime - inflight[in]);
- inflight.erase(in);
- }
-
- void export_latencies(map<double,uint64_t> &in) const
- {
- map<double,uint64_t>::iterator i = in.begin();
- multiset<uint64_t>::iterator j = latencies.begin();
- int count = 0;
- while (j != latencies.end() && i != in.end()) {
- count++;
- if ((((double)count)/((double)latencies.size())) * 100 >= i->first) {
- i->second = *j;
- ++i;
- }
- ++j;
- }
- }
- };
- map<string,TypeStatus> stats;
-
- void begin(TestOp *in)
- {
- stat_lock.Lock();
- stats[in->getType()].begin(in);
- stat_lock.Unlock();
- }
-
- void end(TestOp *in)
- {
- stat_lock.Lock();
- stats[in->getType()].end(in);
- stat_lock.Unlock();
- }
-
- friend std::ostream & operator<<(std::ostream &, TestOpStat&);
-};
-
-std::ostream & operator<<(std::ostream &out, TestOpStat &rhs)
-{
- rhs.stat_lock.Lock();
- for (map<string,TestOpStat::TypeStatus>::iterator i = rhs.stats.begin();
- i != rhs.stats.end();
- ++i) {
- map<double,uint64_t> latency;
- latency[10] = 0;
- latency[50] = 0;
- latency[90] = 0;
- latency[99] = 0;
- i->second.export_latencies(latency);
-
- out << i->first << " latency: " << std::endl;
- for (map<double,uint64_t>::iterator j = latency.begin();
- j != latency.end();
- ++j) {
- if (j->second == 0) break;
- out << "\t" << j->first << "th percentile: "
- << j->second / 1000 << "ms" << std::endl;
- }
- }
- rhs.stat_lock.Unlock();
- return out;
-}
-
-void TestOp::begin()
-{
- if (stat) stat->begin(this);
- _begin();
-}
-
-void TestOp::finish()
-{
- if (stat) stat->end(this);
- _finish();
-}
-
-struct TestOpGenerator
-{
+class TestOpGenerator {
+public:
virtual ~TestOpGenerator();
virtual TestOp *next(RadosTestContext &context) = 0;
};
-struct RadosTestContext
-{
+class RadosTestContext {
+public:
Mutex state_lock;
Cond wait_cond;
- map<int, map<string,string> > pool_obj_cont;
+ map<int, map<string,ObjectDesc> > pool_obj_cont;
set<int> snaps;
set<string> oid_in_use;
set<string> oid_not_in_use;
string prefix;
int errors;
int max_in_flight;
+ ContentsGenerator &cont_gen;
+ int seq_num;
RadosTestContext(const string &pool_name,
int max_in_flight,
+ ContentsGenerator &cont_gen,
const char *id = 0) :
state_lock("Context Lock"),
pool_obj_cont(),
current_snap(0),
pool_name(pool_name),
errors(0),
- max_in_flight(max_in_flight)
+ max_in_flight(max_in_flight),
+ cont_gen(cont_gen), seq_num(0)
{
rados.init(id);
rados.conf_read_file("ceph.conf");
wait_cond.Signal();
}
- bool find_object(string oid, string &contents, int snap = -1) const
+ void update_object(const string &oid, const ContDesc &contents)
{
- for (map<int, map<string,string> >::const_reverse_iterator i =
+ ObjectDesc new_obj(&cont_gen);
+ for (map<int, map<string,ObjectDesc> >::reverse_iterator i =
+ pool_obj_cont.rbegin();
+ i != pool_obj_cont.rend();
+ ++i) {
+ map<string,ObjectDesc>::iterator j = i->second.find(oid);
+ if (j != i->second.end()) {
+ new_obj = j->second;
+ break;
+ }
+ }
+ new_obj.update(contents);
+ pool_obj_cont[current_snap].erase(oid);
+ pool_obj_cont[current_snap].insert(pair<string,ObjectDesc>(oid, new_obj));
+ }
+
+ bool find_object(const string &oid, ObjectDesc *contents, int snap = -1) const
+ {
+ for (map<int, map<string,ObjectDesc> >::const_reverse_iterator i =
pool_obj_cont.rbegin();
i != pool_obj_cont.rend();
++i) {
if (snap != -1 && snap < i->first) continue;
if (i->second.count(oid) != 0) {
- contents = i->second.find(oid)->second;
+ *contents = i->second.find(oid)->second;
return true;
}
}
void remove_snap(int snap)
{
- map<int, map<string,string> >::iterator next_iter = pool_obj_cont.find(snap);
- map<int, map<string,string> >::iterator current_iter = next_iter++;
+ map<int, map<string,ObjectDesc> >::iterator next_iter = pool_obj_cont.find(snap);
+ map<int, map<string,ObjectDesc> >::iterator current_iter = next_iter++;
if (next_iter != pool_obj_cont.end()) {
- map<string,string> ¤t = current_iter->second;
- map<string,string> &next = next_iter->second;
- for (map<string,string>::iterator i = current.begin();
+ map<string,ObjectDesc> ¤t = current_iter->second;
+ map<string,ObjectDesc> &next = next_iter->second;
+ for (map<string,ObjectDesc>::iterator i = current.begin();
i != current.end();
++i) {
if (next.count(i->first) == 0) {
- next[i->first] = i->second;
+ next.insert(pair<string,ObjectDesc>(i->first, i->second));
}
}
}
snaps.insert(current_snap - 1);
}
- void roll_back(string oid, int snap)
+ void roll_back(const string &oid, int snap)
{
- string contents;
- find_object(oid, contents, snap);
- pool_obj_cont.rbegin()->second[oid] = contents;
+ ObjectDesc contents(&cont_gen);
+ find_object(oid, &contents, snap);
+ pool_obj_cont.rbegin()->second.erase(oid);
+ pool_obj_cont.rbegin()->second.insert(pair<string,ObjectDesc>(oid, contents));
}
};
-void callback(librados::completion_t cb, void *arg) {
- TestOp *op = (TestOp *) arg;
- op->finish();
-}
+void callback(librados::completion_t cb, void *arg);
-struct WriteOp : public TestOp
-{
+class WriteOp : public TestOp {
+public:
string oid;
- string written;
- librados::AioCompletion *completion;
+ ContDesc cont;
+ set<librados::AioCompletion *> waiting;
WriteOp(RadosTestContext *context,
const string &oid,
{
context->state_lock.Lock();
done = 0;
- completion = context->rados.aio_create_completion((void *) this, &callback, 0);
- stringstream to_write;
- to_write << context->prefix << "OID: " << oid << " snap " << context->current_snap << std::endl;
- written = to_write.str();
- context->pool_obj_cont[context->current_snap][oid] = written;
+ stringstream acc;
+ acc << context->prefix << "OID: " << oid << " snap " << context->current_snap << std::endl;
+ string prefix = acc.str();
+
+ cont = ContDesc(context->seq_num, context->current_snap, context->seq_num, prefix);
+
+ context->update_object(oid, cont);
context->oid_in_use.insert(oid);
if (context->oid_not_in_use.count(oid) != 0) {
context->oid_not_in_use.erase(oid);
}
- bufferlist write_buffer;
- write_buffer.append(to_write.str());
+ context->seq_num++;
context->state_lock.Unlock();
- context->io_ctx.aio_write(context->prefix+oid, completion,
- write_buffer, to_write.str().length(), 0);
+ interval_set<uint64_t> ranges;
+ context->cont_gen.get_ranges(cont, ranges);
+ ContentsGenerator::iterator gen_pos = context->cont_gen.get_iterator(cont);
+ for (interval_set<uint64_t>::iterator i = ranges.begin();
+ i != ranges.end();
+ ++i) {
+ librados::AioCompletion *completion =
+ context->rados.aio_create_completion((void *) this, &callback, &callback);
+ bufferlist to_write;
+ gen_pos.seek(i.get_start());
+ for (uint64_t k = 0; k != i.get_len(); ++k, ++gen_pos) {
+ to_write.append(*gen_pos);
+ }
+ assert(to_write.length() == i.get_len());
+ assert(to_write.length() > 0);
+ std::cout << "Writing " << context->prefix+oid << " from " << i.get_start()
+ << " to " << i.get_len() + i.get_start() << " ranges are "
+ << ranges << std::endl;
+ context->io_ctx.aio_write(context->prefix+oid, completion,
+ to_write, i.get_len(), i.get_start());
+ waiting.insert(completion);
+ }
}
void _finish()
{
context->state_lock.Lock();
- context->oid_in_use.erase(oid);
- context->oid_not_in_use.insert(oid);
- if (int err = completion->get_return_value()) {
- cerr << "Error: oid " << oid << " write returned error code "
- << err << std::endl;
+ for (set<librados::AioCompletion *>::iterator i = waiting.begin();
+ i != waiting.end();
+ ) {
+ if ((*i)->is_complete() && (*i)->is_safe()) {
+ if (int err = (*i)->get_return_value()) {
+ cerr << "Error: oid " << oid << " write returned error code "
+ << err << std::endl;
+ }
+ waiting.erase(i++);
+ } else {
+ ++i;
+ }
+ }
+
+ if (waiting.empty()) {
+ context->oid_in_use.erase(oid);
+ context->oid_not_in_use.insert(oid);
+ context->kick();
+ done = true;
}
- context->kick();
- done = true;
context->state_lock.Unlock();
+
}
bool finished()
{
- return done && completion->is_complete();
+ return waiting.empty();
}
string getType()
}
};
-struct ReadOp : public TestOp
-{
+class ReadOp : public TestOp {
+public:
librados::AioCompletion *completion;
string oid;
bufferlist result;
- string old_value;
+ ObjectDesc old_value;
ReadOp(RadosTestContext *context,
const string &oid,
TestOpStat *stat = 0) :
TestOp(context, stat),
- oid(oid)
+ oid(oid),
+ old_value(&context->cont_gen)
{}
void _begin()
context->oid_in_use.insert(oid);
context->oid_not_in_use.erase(oid);
- context->find_object(oid, old_value);
+ context->find_object(oid, &old_value);
context->state_lock.Unlock();
context->io_ctx.aio_read(context->prefix+oid, completion,
- &result, old_value.length(), 0);
+ &result, context->cont_gen.get_length(old_value.most_recent()), 0);
}
void _finish()
cerr << "Error: oid " << oid << " read returned error code "
<< err << std::endl;
} else {
- string to_check;
- result.copy(0, result.length(), to_check);
- if (to_check != old_value) {
+ ContDesc to_check;
+ bufferlist::iterator p = result.begin();
+ if (!context->cont_gen.read_header(p, to_check)) {
+ cerr << "Unable to decode oid " << oid << " at snap " << context->current_snap << std::endl;
+ context->errors++;
+ }
+ if (to_check != old_value.most_recent()) {
+ cerr << "Found incorrect object contents " << to_check
+ << ", expected " << old_value.most_recent() << " oid " << oid << std::endl;
+ context->errors++;
+ }
+ if (!old_value.check(result)) {
+ cerr << "Object " << oid << " contents " << to_check << " corrupt" << std::endl;
context->errors++;
- cerr << "Error: oid " << oid << " read returned \n"
- << to_check << "\nShould have returned\n"
- << old_value << "\nCurrent snap is " << context->current_snap << std::endl;
}
}
context->kick();
}
};
-struct SnapCreateOp : public TestOp
-{
+class SnapCreateOp : public TestOp {
+public:
SnapCreateOp(RadosTestContext *context,
TestOpStat *stat = 0) :
TestOp(context, stat)
}
};
-struct SnapRemoveOp : public TestOp
-{
+class SnapRemoveOp : public TestOp {
+public:
int to_remove;
SnapRemoveOp(RadosTestContext *context,
int snap,
}
};
-struct RollbackOp : public TestOp
-{
+class RollbackOp : public TestOp {
+public:
string oid;
int roll_back_to;
RollbackOp(RadosTestContext *context,
return "RollBackOp";
}
};
+
+#endif
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+#include "include/interval_set.h"
+#include "include/buffer.h"
+#include <list>
+#include <map>
+#include <set>
+#include "RadosModel.h"
+#include "TestOpStat.h"
+
+void TestOpStat::begin(TestOp *in) {
+ stat_lock.Lock();
+ stats[in->getType()].begin(in);
+ stat_lock.Unlock();
+}
+
+void TestOpStat::end(TestOp *in) {
+ stat_lock.Lock();
+ stats[in->getType()].end(in);
+ stat_lock.Unlock();
+}
+
+void TestOpStat::TypeStatus::export_latencies(map<double,uint64_t> &in) const
+{
+ map<double,uint64_t>::iterator i = in.begin();
+ multiset<uint64_t>::iterator j = latencies.begin();
+ int count = 0;
+ while (j != latencies.end() && i != in.end()) {
+ count++;
+ if ((((double)count)/((double)latencies.size())) * 100 >= i->first) {
+ i->second = *j;
+ ++i;
+ }
+ ++j;
+ }
+}
+
+std::ostream & operator<<(std::ostream &out, TestOpStat &rhs)
+{
+ rhs.stat_lock.Lock();
+ for (map<string,TestOpStat::TypeStatus>::iterator i = rhs.stats.begin();
+ i != rhs.stats.end();
+ ++i) {
+ map<double,uint64_t> latency;
+ latency[10] = 0;
+ latency[50] = 0;
+ latency[90] = 0;
+ latency[99] = 0;
+ i->second.export_latencies(latency);
+
+ out << i->first << " latency: " << std::endl;
+ for (map<double,uint64_t>::iterator j = latency.begin();
+ j != latency.end();
+ ++j) {
+ if (j->second == 0) break;
+ out << "\t" << j->first << "th percentile: "
+ << j->second / 1000 << "ms" << std::endl;
+ }
+ }
+ rhs.stat_lock.Unlock();
+ return out;
+}
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+#include "common/Mutex.h"
+#include "common/Cond.h"
+#include "include/rados/librados.hpp"
+
+#ifndef TESTOPSTAT_H
+#define TESTOPSTAT_H
+
+struct TestOp;
+
+class TestOpStat {
+public:
+ Mutex stat_lock;
+
+ TestOpStat() : stat_lock("TestOpStat lock") {}
+
+ static uint64_t gettime()
+ {
+ timeval t;
+ gettimeofday(&t,0);
+ return (1000000*t.tv_sec) + t.tv_usec;
+ }
+
+ class TypeStatus {
+ public:
+ map<TestOp*,uint64_t> inflight;
+ multiset<uint64_t> latencies;
+ void begin(TestOp *in)
+ {
+ assert(!inflight.count(in));
+ inflight[in] = gettime();
+ }
+
+ void end(TestOp *in)
+ {
+ assert(inflight.count(in));
+ uint64_t curtime = gettime();
+ latencies.insert(curtime - inflight[in]);
+ inflight.erase(in);
+ }
+
+ void export_latencies(map<double,uint64_t> &in) const;
+ };
+ map<string,TypeStatus> stats;
+
+ void begin(TestOp *in);
+ void end(TestOp *in);
+ friend std::ostream & operator<<(std::ostream &, TestOpStat&);
+};
+
+std::ostream & operator<<(std::ostream &out, TestOpStat &rhs);
+
+#endif
int ops = 1000;
int objects = 50;
int max_in_flight = 16;
+ int size = 400000;
if (argc > 1) {
ops = atoi(argv[1]);
}
max_in_flight = atoi(argv[3]);
}
+ if (argc > 4) {
+ size = atoi(argv[4]);
+ }
+
if (max_in_flight > objects) {
cerr << "Error: max_in_flight must be greater than the number of objects"
<< std::endl;
if (id) cerr << "Client id is: " << id << std::endl;
string pool_name = "data";
- RadosTestContext context(pool_name, max_in_flight, id);
+ VarLenGenerator cont_gen(size);
+ RadosTestContext context(pool_name, max_in_flight, cont_gen, id);
TestOpStat stats;
SnapTestGenerator gen = SnapTestGenerator(ops, objects, &stats);