]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
test/osd: TestSnaps now writes large, random objects with holes
authorSamuel Just <samuel.just@dreamhost.com>
Wed, 6 Jul 2011 18:24:41 +0000 (11:24 -0700)
committerSamuel Just <samuel.just@dreamhost.com>
Wed, 13 Jul 2011 22:12:31 +0000 (15:12 -0700)
In order to test clone recovery pathways, TestSnaps now writes to random
subsets of an object between snapshots.  Objects may now also be very
large.

Signed-off-by: Samuel Just <samuel.just@dreamhost.com>
src/Makefile.am
src/test/osd/Object.cc [new file with mode: 0644]
src/test/osd/Object.h [new file with mode: 0644]
src/test/osd/RadosModel.cc [new file with mode: 0644]
src/test/osd/RadosModel.h
src/test/osd/TestOpStat.cc [new file with mode: 0644]
src/test/osd/TestOpStat.h [new file with mode: 0644]
src/test/osd/TestSnaps.cc

index a3642605448225d4b97991f21e29c85f27a4bfe7..010f54c7b005e83c0829a91ef6757244983ab445 100644 (file)
@@ -172,7 +172,7 @@ test_trans_SOURCES = test_trans.cc
 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
 
diff --git a/src/test/osd/Object.cc b/src/test/osd/Object.cc
new file mode 100644 (file)
index 0000000..cbb5052
--- /dev/null
@@ -0,0 +1,175 @@
+// -*- 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;
+}
diff --git a/src/test/osd/Object.h b/src/test/osd/Object.h
new file mode 100644 (file)
index 0000000..59dd0c8
--- /dev/null
@@ -0,0 +1,288 @@
+// -*- 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
diff --git a/src/test/osd/RadosModel.cc b/src/test/osd/RadosModel.cc
new file mode 100644 (file)
index 0000000..79387c0
--- /dev/null
@@ -0,0 +1,26 @@
+// -*- 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();
+}
index feb4ac05ea9dec4b7237d6cdd85825f0d167bda1..52afee2a914e6264f4c6cacbde49195ba64d1476 100644 (file)
@@ -9,20 +9,22 @@
 #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();
   }
@@ -33,8 +35,8 @@ typename T::iterator rand_choose(T &cont)
   return retval;
 }
 
-struct TestOp
-{
+class TestOp {
+public:
   RadosTestContext *context;
   TestOpStat *stat;
   bool done;
@@ -59,120 +61,17 @@ struct TestOp
   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;
@@ -184,16 +83,20 @@ struct RadosTestContext
   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");
@@ -259,15 +162,33 @@ struct RadosTestContext
     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;
       }
     }
@@ -276,16 +197,16 @@ struct RadosTestContext
 
   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> &current = current_iter->second;
-      map<string,string> &next = next_iter->second;
-      for (map<string,string>::iterator i = current.begin();
+      map<string,ObjectDesc> &current = 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));
        }
       }
     }
@@ -300,24 +221,22 @@ struct RadosTestContext
     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,
@@ -330,42 +249,76 @@ struct WriteOp : public TestOp
   {
     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()
@@ -374,17 +327,18 @@ struct WriteOp : public TestOp
   }
 };
 
-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()
@@ -395,11 +349,11 @@ struct ReadOp : public TestOp
 
     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()
@@ -411,13 +365,20 @@ struct ReadOp : public TestOp
       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();
@@ -436,8 +397,8 @@ struct ReadOp : public TestOp
   }
 };
 
-struct SnapCreateOp : public TestOp
-{
+class SnapCreateOp : public TestOp {
+public:
   SnapCreateOp(RadosTestContext *context,
               TestOpStat *stat = 0) :
     TestOp(context, stat)
@@ -466,8 +427,8 @@ struct SnapCreateOp : public TestOp
   }
 };
 
-struct SnapRemoveOp : public TestOp
-{
+class SnapRemoveOp : public TestOp {
+public:
   int to_remove;
   SnapRemoveOp(RadosTestContext *context,
               int snap,
@@ -499,8 +460,8 @@ struct SnapRemoveOp : public TestOp
   }
 };
 
-struct RollbackOp : public TestOp
-{
+class RollbackOp : public TestOp {
+public:
   string oid;
   int roll_back_to;
   RollbackOp(RadosTestContext *context,
@@ -536,3 +497,5 @@ struct RollbackOp : public TestOp
     return "RollBackOp";
   }
 };
+
+#endif
diff --git a/src/test/osd/TestOpStat.cc b/src/test/osd/TestOpStat.cc
new file mode 100644 (file)
index 0000000..db6a82e
--- /dev/null
@@ -0,0 +1,61 @@
+// -*- 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;
+}
diff --git a/src/test/osd/TestOpStat.h b/src/test/osd/TestOpStat.h
new file mode 100644 (file)
index 0000000..e08fab9
--- /dev/null
@@ -0,0 +1,53 @@
+// -*- 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
index f978ddc5222209f5d72bfc5692fddedffb7f3bd6..540a50a11cd6f1c17366826e6c38df8f97822a5b 100644 (file)
@@ -85,6 +85,7 @@ int main(int argc, char **argv)
   int ops = 1000;
   int objects = 50;
   int max_in_flight = 16;
+  int size = 400000;
   if (argc > 1) {
     ops = atoi(argv[1]);
   }
@@ -97,6 +98,10 @@ int main(int argc, char **argv)
     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;
@@ -107,7 +112,8 @@ int main(int argc, char **argv)
   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);