Attribute handling no longer has special support in ContentsGenerator.
The most recent operation information is now stored in a special
attr rather than at the beginning of the object. ObjectDesc layers
include their own ContentsGenerators to allow more flexibility.
Also, writes truncate to the new object size rather than simply
causing reads to stop at that object size.
Signed-off-by: Samuel Just <sam.just@inktank.com>
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 pos = 0;
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) {
+ if (segment_length + pos > limit) {
segment_length = limit - pos;
}
if (include) {
}
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;
+ // make sure we write up to the limit
+ if (limit > 0 && (
+ out.empty() ||
+ (out.rbegin()->first + out.rbegin()->second < limit)))
+ out[limit-1] = 1;
}
ObjectDesc::iterator &ObjectDesc::iterator::advance(bool init) {
return *this;
}
while (pos == limit) {
- limit = *stack.begin();
+ cur_cont = stack.begin()->first;
+ limit = stack.begin()->second;
stack.pop_front();
- --cur_cont;
}
if (cur_cont == obj.layers.end()) {
}
interval_set<uint64_t> ranges;
- cont_gen->get_ranges(*cur_cont, ranges);
+ cur_cont->first->get_ranges(cur_cont->second, ranges);
while (!ranges.contains(pos)) {
- stack.push_front(limit);
+ stack.push_front(make_pair(cur_cont, limit));
+ uint64_t length = cur_cont->first->get_length(cur_cont->second);
uint64_t next;
- if (pos >= ranges.range_end()) {
+ if (pos >= length) {
next = limit;
+ cur_cont = obj.layers.end();
+ } else if (ranges.empty() || pos >= ranges.range_end()) {
+ next = length;
+ ++cur_cont;
} else {
next = ranges.start_after(pos);
+ ++cur_cont;
}
if (next < limit) {
limit = next;
}
- ++cur_cont;
if (cur_cont == obj.layers.end()) {
break;
}
ranges.clear();
- cont_gen->get_ranges(*cur_cont, ranges);
+ cur_cont->first->get_ranges(cur_cont->second, 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)));
+ if (!cont_iters.count(cur_cont->second)) {
+ cont_iters.insert(pair<ContDesc,ContentsGenerator::iterator>(
+ cur_cont->second,
+ cur_cont->first->get_iterator(cur_cont->second)));
}
- map<ContDesc,ContentsGenerator::iterator>::iterator j = cont_iters.find(*cur_cont);
+ map<ContDesc,ContentsGenerator::iterator>::iterator j = cont_iters.find(
+ cur_cont->second);
assert(j != cont_iters.end());
j->second.seek(pos);
return *this;
}
const ContDesc &ObjectDesc::most_recent() {
- return *layers.begin();
+ return layers.begin()->second;
}
-void ObjectDesc::update(const ContDesc &next) {
- layers.push_front(next);
+void ObjectDesc::update(ContentsGenerator *gen, const ContDesc &next) {
+ layers.push_front(make_pair(gen, 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) {
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+#include <tr1/memory>
#include "include/interval_set.h"
#include "include/buffer.h"
#include "include/encoding.h"
parent(parent), impl(impl) {}
};
- virtual bool read_header(bufferlist::iterator& input,
- ContDesc &output) = 0;
-
virtual uint64_t get_length(const ContDesc &in) = 0;
- virtual uint64_t get_attr_length(const ContDesc &in) = 0;
-
- virtual bufferlist gen_attribute(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;
}
};
-class VarLenGenerator : public ContentsGenerator {
+class RandGenerator : public ContentsGenerator {
public:
class RandWrap {
public:
uint64_t pos;
ContDesc cont;
RandWrap rand;
- bufferlist header;
- bufferlist::iterator header_pos;
- VarLenGenerator *cont_gen;
+ RandGenerator *cont_gen;
char current;
- iterator_impl(const ContDesc &cont, VarLenGenerator *cont_gen) :
+ iterator_impl(const ContDesc &cont, RandGenerator *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;
+ current = rand();
}
- virtual ContDesc get_cont() const { return cont; }
- virtual uint64_t get_pos() const { return pos; }
+ ContDesc get_cont() const { return cont; }
+ uint64_t get_pos() const { return pos; }
iterator_impl &operator++() {
pos++;
- if (header_pos.end()) {
- current = rand();
- } else {
- current = *header_pos;
- ++header_pos;
- }
+ current = rand();
return *this;
}
}
};
- void get_ranges(const ContDesc &cont, interval_set<uint64_t> &out);
+ virtual void get_ranges(const ContDesc &cont, interval_set<uint64_t> &out) = 0;
ContentsGenerator::iterator_impl *get_iterator_impl(const ContDesc &in) {
- VarLenGenerator::iterator_impl *i = new iterator_impl(in, this);
+ RandGenerator::iterator_impl *i = new iterator_impl(in, this);
return i;
}
delete in;
}
- ContentsGenerator::iterator_impl *dup_iterator_impl(const ContentsGenerator::iterator_impl *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();
+class VarLenGenerator : public RandGenerator {
+ uint64_t max_length;
+ uint64_t min_stride_size;
+ uint64_t max_stride_size;
+public:
+ VarLenGenerator(
+ uint64_t length, uint64_t min_stride_size, uint64_t max_stride_size) :
+ max_length(length),
+ min_stride_size(min_stride_size),
+ max_stride_size(max_stride_size) {}
+ void get_ranges(const ContDesc &cont, interval_set<uint64_t> &out);
+ uint64_t get_length(const ContDesc &in) {
+ RandWrap rand(in.seqnum);
+ return (rand() % max_length);
}
+};
+class AttrGenerator : public RandGenerator {
+ uint64_t max_len;
+public:
+ AttrGenerator(uint64_t max_len) : max_len(max_len) {}
+ void get_ranges(const ContDesc &cont, interval_set<uint64_t> &out) {
+ out.insert(0, get_length(cont));
+ }
uint64_t get_length(const ContDesc &in) {
RandWrap rand(in.seqnum);
- return (rand() % length) + get_header_length(in);
+ return (rand() % max_len);
}
-
- bufferlist gen_attribute(const ContDesc &in) {
- bufferlist header;
- write_header(in, header);
- ContentsGenerator::iterator iter = get_iterator(in);
- for (uint64_t to_write = get_attr_length(in); to_write > 0;
- --to_write) {
- header.append(*iter);
- ++iter;
+ bufferlist gen_bl(const ContDesc &in) {
+ bufferlist bl;
+ for (iterator i = get_iterator(in); !i.end(); ++i) {
+ bl.append(*i);
}
- return header;
+ assert(bl.length() < max_len);
+ return bl;
}
+};
-
- uint64_t get_attr_length(const ContDesc &in) {
+class AppendGenerator : public RandGenerator {
+ uint64_t off;
+ uint64_t max_len;
+ uint64_t max_intervals;
+public:
+ AppendGenerator(uint64_t off, uint64_t max_len, uint64_t max_intervals) :
+ off(off), max_len(max_len), max_intervals(max_intervals) {}
+ uint64_t get_length(const ContDesc &in) {
RandWrap rand(in.seqnum);
- return (rand() % attr_length) + get_header_length(in);
+ return off + (rand() % max_len);
+ }
+ void get_ranges(const ContDesc &cont, interval_set<uint64_t> &out) {
+ out.insert(off, get_length(cont));
}
-
- void write_header(const ContDesc &in, bufferlist &output);
-
- bool read_header(bufferlist::iterator &p, ContDesc &out);
- uint64_t length;
- uint64_t attr_length;
- uint64_t min_stride_size;
- uint64_t max_stride_size;
- VarLenGenerator(uint64_t length, uint64_t min_stride_size, uint64_t max_stride_size, uint64_t attr_length = 2000) :
- length(length), attr_length(attr_length),
- min_stride_size(min_stride_size), max_stride_size(max_stride_size) {}
};
class ObjectDesc {
public:
- ObjectDesc(ContentsGenerator *cont_gen)
+ ObjectDesc()
: exists(false), dirty(false),
- version(0), layers(), cont_gen(cont_gen) {}
+ version(0) {}
ObjectDesc(const ContDesc &init, ContentsGenerator *cont_gen)
: exists(false), dirty(false),
- version(0), layers(), cont_gen(cont_gen) {
- layers.push_front(init);
+ version(0) {
+ layers.push_front(make_pair(cont_gen, init));
}
class iterator {
public:
uint64_t pos;
ObjectDesc &obj;
- ContentsGenerator *cont_gen;
- list<uint64_t> stack;
+ list<pair<list<pair<std::tr1::shared_ptr<ContentsGenerator>,
+ ContDesc> >::iterator,
+ uint64_t> > stack;
map<ContDesc,ContentsGenerator::iterator> cont_iters;
uint64_t limit;
- list<ContDesc>::iterator cur_cont;
+ list<pair<std::tr1::shared_ptr<ContentsGenerator>,
+ 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());
+ iterator(ObjectDesc &obj) :
+ pos(0), obj(obj) {
+ limit = obj.layers.begin()->first->get_length(obj.layers.begin()->second);
cur_cont = obj.layers.begin();
advance(true);
}
if (cur_cont == obj.layers.end()) {
return '\0';
} else {
- map<ContDesc,ContentsGenerator::iterator>::iterator j = cont_iters.find(*cur_cont);
+ map<ContDesc,ContentsGenerator::iterator>::iterator j = cont_iters.find(
+ cur_cont->second);
assert(j != cont_iters.end());
return *(j->second);
}
}
bool end() {
- return pos >= cont_gen->get_length(*obj.layers.begin());
+ return pos >= obj.layers.begin()->first->get_length(
+ obj.layers.begin()->second);
}
void seek(uint64_t _pos) {
}
}
};
-
+
iterator begin() {
- return iterator(*this, this->cont_gen);
+ return iterator(*this);
}
bool deleted() {
return layers.size();
}
- void update(const ContDesc &next);
+ // takes ownership of gen
+ void update(ContentsGenerator *gen, const ContDesc &next);
bool check(bufferlist &to_check);
const ContDesc &most_recent();
+ ContentsGenerator *most_recent_gen() {
+ return layers.begin()->first.get();
+ }
map<string, ContDesc> attrs; // Both omap and xattrs
bufferlist header;
bool exists;
uint64_t version;
private:
- list<ContDesc> layers;
- ContentsGenerator *cont_gen;
- ObjectDesc();
+ list<pair<std::tr1::shared_ptr<ContentsGenerator>, ContDesc> > layers;
};
#endif
string prefix;
int errors;
int max_in_flight;
- ContentsGenerator &cont_gen;
int seq_num;
map<int,uint64_t> snaps;
uint64_t seq;
const char *rados_id;
bool initialized;
map<string, TestWatchContext*> watches;
-
+ const uint64_t max_size;
+ const uint64_t min_stride_size;
+ const uint64_t max_stride_size;
+ AttrGenerator attr_gen;
RadosTestContext(const string &pool_name,
int max_in_flight,
- ContentsGenerator &cont_gen,
+ uint64_t max_size,
+ uint64_t min_stride_size,
+ uint64_t max_stride_size,
const char *id = 0) :
state_lock("Context Lock"),
pool_obj_cont(),
next_oid(0),
errors(0),
max_in_flight(max_in_flight),
- cont_gen(cont_gen), seq_num(0), seq(0),
- rados_id(id), initialized(false)
+ seq_num(0), seq(0),
+ rados_id(id), initialized(false),
+ max_size(max_size),
+ min_stride_size(min_stride_size), max_stride_size(max_stride_size),
+ attr_gen(2000)
{
}
void rm_object_attrs(const string &oid, const set<string> &attrs)
{
- ObjectDesc new_obj(&cont_gen);
+ ObjectDesc new_obj;
for (map<int, map<string,ObjectDesc> >::reverse_iterator i =
pool_obj_cont.rbegin();
i != pool_obj_cont.rend();
void remove_object_header(const string &oid)
{
- ObjectDesc new_obj(&cont_gen);
+ ObjectDesc new_obj;
for (map<int, map<string,ObjectDesc> >::reverse_iterator i =
pool_obj_cont.rbegin();
i != pool_obj_cont.rend();
void update_object_header(const string &oid, const bufferlist &bl)
{
- ObjectDesc new_obj(&cont_gen);
+ ObjectDesc new_obj;
for (map<int, map<string,ObjectDesc> >::reverse_iterator i =
pool_obj_cont.rbegin();
i != pool_obj_cont.rend();
void update_object_attrs(const string &oid, const map<string, ContDesc> &attrs)
{
- ObjectDesc new_obj(&cont_gen);
+ ObjectDesc new_obj;
for (map<int, map<string,ObjectDesc> >::reverse_iterator i =
pool_obj_cont.rbegin();
i != pool_obj_cont.rend();
pool_obj_cont[current_snap].insert(pair<string,ObjectDesc>(oid, new_obj));
}
- void update_object(const string &oid, const ContDesc &contents)
+ void update_object(ContentsGenerator *cont_gen,
+ const string &oid, const ContDesc &contents)
{
- ObjectDesc new_obj(&cont_gen);
+ ObjectDesc new_obj;
for (map<int, map<string,ObjectDesc> >::reverse_iterator i =
pool_obj_cont.rbegin();
i != pool_obj_cont.rend();
}
}
new_obj.exists = true;
- new_obj.update(contents);
+ new_obj.update(cont_gen,
+ contents);
pool_obj_cont[current_snap].erase(oid);
pool_obj_cont[current_snap].insert(pair<string,ObjectDesc>(oid, new_obj));
}
void remove_object(const string &oid)
{
assert(!get_watch_context(oid));
- ObjectDesc new_obj(&cont_gen);
+ ObjectDesc new_obj;
pool_obj_cont[current_snap].erase(oid);
pool_obj_cont[current_snap].insert(pair<string,ObjectDesc>(oid, new_obj));
}
void roll_back(const string &oid, int snap)
{
assert(!get_watch_context(oid));
- ObjectDesc contents(&cont_gen);
+ ObjectDesc contents;
find_object(oid, &contents, snap);
pool_obj_cont.rbegin()->second.erase(oid);
pool_obj_cont.rbegin()->second.insert(pair<string,ObjectDesc>(oid, contents));
set<string> to_remove;
{
Mutex::Locker l(context->state_lock);
- ObjectDesc obj(&context->cont_gen);
+ ObjectDesc obj;
if (!context->find_object(oid, &obj)) {
context->kick();
done = true;
context->oid_not_in_use.erase(oid);
if (rand() % 30) {
- ContentsGenerator::iterator iter = context->cont_gen.get_iterator(cont);
+ ContentsGenerator::iterator iter = context->attr_gen.get_iterator(cont);
for (map<string, ContDesc>::iterator i = obj.attrs.begin();
i != obj.attrs.end();
++i, ++iter) {
map<string, bufferlist> omap_contents;
map<string, ContDesc> omap;
bufferlist header;
- ContentsGenerator::iterator keygen = context->cont_gen.get_iterator(cont);
+ ContentsGenerator::iterator keygen = context->attr_gen.get_iterator(cont);
op.create(false);
while (!*keygen) ++keygen;
while (*keygen) {
- header.append(*keygen);
+ if (*keygen != '_')
+ header.append(*keygen);
++keygen;
}
for (int i = 0; i < 20; ++i) {
++keygen;
}
ContDesc val(cont);
- val.seqnum += context->cont_gen.get_length(cont);
+ val.seqnum += (unsigned)(*keygen);
val.prefix = ("oid: " + oid);
omap[key] = val;
- bufferlist val_buffer = context->cont_gen.gen_attribute(val);
+ bufferlist val_buffer = context->attr_gen.gen_bl(val);
omap_contents[key] = val_buffer;
op.setxattr(key.c_str(), val_buffer);
}
uint64_t last_acked_tid;
librados::ObjectReadOperation read_op;
+ librados::ObjectWriteOperation write_op;
bufferlist rbuffer;
WriteOp(int n,
cont = ContDesc(context->seq_num, context->current_snap, context->seq_num, prefix);
- context->update_object(oid, cont);
+ ContentsGenerator *cont_gen = new VarLenGenerator(
+ context->max_size, context->min_stride_size, context->max_stride_size);
+ context->update_object(cont_gen, oid, cont);
context->oid_in_use.insert(oid);
context->oid_not_in_use.erase(oid);
interval_set<uint64_t> ranges;
- context->cont_gen.get_ranges(cont, ranges);
+
+ cont_gen->get_ranges(cont, ranges);
std::cout << num << ": seq_num " << context->seq_num << " ranges " << ranges << std::endl;
context->seq_num++;
context->state_lock.Unlock();
waiting_on = ranges.num_intervals();
//cout << " waiting_on = " << waiting_on << std::endl;
- ContentsGenerator::iterator gen_pos = context->cont_gen.get_iterator(cont);
+ ContentsGenerator::iterator gen_pos = cont_gen->get_iterator(cont);
uint64_t tid = 1;
for (interval_set<uint64_t>::iterator i = ranges.begin();
i != ranges.end();
to_write, i.get_len(), i.get_start());
}
+ bufferlist contbl;
+ ::encode(cont, contbl);
pair<TestOp*, TestOp::CallbackInfo*> *cb_arg =
new pair<TestOp*, TestOp::CallbackInfo*>(
this,
- new TestOp::CallbackInfo(tid));
+ new TestOp::CallbackInfo(++tid));
+ librados::AioCompletion *completion = context->rados.aio_create_completion(
+ (void*) cb_arg, &write_callback, NULL);
+ waiting.insert(completion);
+ waiting_on++;
+ write_op.setxattr("_header", contbl);
+ write_op.truncate(cont_gen->get_length(cont));
+ context->io_ctx.aio_operate(
+ context->prefix+oid, completion, &write_op);
+
+ cb_arg =
+ new pair<TestOp*, TestOp::CallbackInfo*>(
+ this,
+ new TestOp::CallbackInfo(++tid));
rcompletion = context->rados.aio_create_completion(
(void*) cb_arg, &write_callback, NULL);
waiting_on++;
return;
}
- ObjectDesc contents(&context->cont_gen);
+ ObjectDesc contents;
context->find_object(oid, &contents);
bool present = !contents.deleted();
: TestOp(n, context, stat),
completion(NULL),
oid(oid),
- old_value(&context->cont_gen),
snap(0),
retval(0),
attrretval(0)
op.read(0,
!old_value.has_contents() ? 0 :
- context->cont_gen.get_length(old_value.most_recent()),
+ old_value.most_recent_gen()->get_length(old_value.most_recent()),
&result,
&retval);
assert(0);
}
} else {
+ map<string, bufferlist>::iterator iter = xattrs.find("_header");
+ bufferlist headerbl;
+ if (iter == xattrs.end()) {
+ cerr << num << ": Error: did not find header attr, has_contents: "
+ << old_value.has_contents()
+ << std::endl;
+ assert(!old_value.has_contents());
+ } else {
+ headerbl = iter->second;
+ xattrs.erase(iter);
+ }
cout << num << ": expect " << old_value.most_recent() << std::endl;
assert(!old_value.deleted());
if (old_value.has_contents()) {
ContDesc to_check;
- bufferlist::iterator p = result.begin();
- if (!context->cont_gen.read_header(p, to_check)) {
- cerr << num << ": Unable to decode oid " << oid << " at snap " << context->current_snap << std::endl;
- context->errors++;
- }
+ bufferlist::iterator p = headerbl.begin();
+ ::decode(to_check, p);
if (to_check != old_value.most_recent()) {
cerr << num << ": oid " << oid << " found incorrect object contents " << to_check
<< ", expected " << old_value.most_recent() << std::endl;
++omap_iter) {
assert(old_value.attrs.count(omap_iter->first));
assert(xattrs.count(omap_iter->first));
- bufferlist bl = context->cont_gen.gen_attribute(
+ bufferlist bl = context->attr_gen.gen_bl(
old_value.attrs[omap_iter->first]);
assert(bl.length() == omap_iter->second.length());
assert(bl.length() == xattrs[omap_iter->first].length());
void _begin()
{
context->state_lock.Lock();
- ObjectDesc contents(&context->cont_gen);
+ ObjectDesc contents;
context->find_object(oid, &contents);
if (contents.deleted()) {
context->kick();
TestOpStat *stat)
: TestOp(n, context, stat),
oid(oid), oid_src(oid_src),
- src_value(&context->cont_gen),
comp(NULL), done(0), version(0), r(0)
{}
: TestOp(n, context, stat),
completion(NULL),
oid(oid),
- dirty(false),
- old_value(&context->cont_gen)
+ dirty(false)
{}
void _begin()
}
char *id = getenv("CEPH_CLIENT_ID");
- VarLenGenerator cont_gen(size, min_stride_size, max_stride_size);
- RadosTestContext context(pool_name, max_in_flight, cont_gen, id);
+ RadosTestContext context(
+ pool_name,
+ max_in_flight,
+ size,
+ min_stride_size,
+ max_stride_size,
+ id);
TestOpStat stats;
WeightedTestGenerator gen = WeightedTestGenerator(ops, objects,