1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2012 New Dream Network
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
21 #include "os/ObjectStore.h"
22 #include "common/ceph_argparse.h"
23 #include "global/global_init.h"
24 #include "common/debug.h"
25 #include <boost/scoped_ptr.hpp>
26 #include <boost/lexical_cast.hpp>
28 #include "DeterministicOpSequence.h"
29 #include "common/config.h"
30 #include "include/assert.h"
32 #define dout_context g_ceph_context
33 #define dout_subsys ceph_subsys_filestore
35 #define dout_prefix *_dout << "deterministic_seq "
37 DeterministicOpSequence::DeterministicOpSequence(ObjectStore
*store
,
39 : TestObjectStoreState(store
),
43 txn_object
= hobject_t(sobject_t("txn", CEPH_NOSNAP
));
46 m_status
.open(status
.c_str());
49 DeterministicOpSequence::~DeterministicOpSequence()
51 // TODO Auto-generated destructor stub
54 bool DeterministicOpSequence::run_one_op(int op
, rngen_t
& gen
)
67 case DSOP_CLONE_RANGE
:
68 ok
= do_clone_range(gen
);
74 ok
= do_coll_move(gen
);
77 ok
= do_set_attrs(gen
);
79 case DSOP_COLL_CREATE
:
80 ok
= do_coll_create(gen
);
84 assert(0 == "bad op");
89 void DeterministicOpSequence::generate(int seed
, int num_txs
)
91 std::ostringstream ss
;
92 ss
<< "generate run " << num_txs
<< " --seed " << seed
;
94 if (m_status
.is_open()) {
95 m_status
<< ss
.str() << std::endl
;
99 dout(0) << ss
.str() << dendl
;
102 boost::uniform_int
<> op_rng(DSOP_FIRST
, DSOP_LAST
);
104 for (txn
= 1; txn
<= num_txs
; ) {
105 int op
= op_rng(gen
);
106 _print_status(txn
, op
);
107 dout(0) << "generate seq " << txn
<< " op " << op
<< dendl
;
108 if (run_one_op(op
, gen
))
113 void DeterministicOpSequence::_print_status(int seq
, int op
)
115 if (!m_status
.is_open())
117 m_status
<< seq
<< " " << op
<< std::endl
;
121 int DeterministicOpSequence::_gen_coll_id(rngen_t
& gen
)
123 boost::uniform_int
<> coll_rng(0, m_collections_ids
.size()-1);
124 return coll_rng(gen
);
127 int DeterministicOpSequence::_gen_obj_id(rngen_t
& gen
)
129 boost::uniform_int
<> obj_rng(0, m_num_objects
- 1);
133 void DeterministicOpSequence::note_txn(ObjectStore::Transaction
*t
)
137 t
->truncate(txn_coll
, ghobject_t(txn_object
), 0);
138 t
->write(txn_coll
, ghobject_t(txn_object
), 0, bl
.length(), bl
);
139 dout(10) << __func__
<< " " << txn
<< dendl
;
142 bool DeterministicOpSequence::do_touch(rngen_t
& gen
)
144 int coll_id
= _gen_coll_id(gen
);
145 int obj_id
= _gen_obj_id(gen
);
147 coll_entry_t
*entry
= get_coll_at(coll_id
);
148 ceph_assert(entry
!= NULL
);
150 // Don't care about other collections if already exists
151 if (!entry
->check_for_obj(obj_id
)) {
152 bool other_found
= false;
153 map
<int, coll_entry_t
*>::iterator it
= m_collections
.begin();
154 for (; it
!= m_collections
.end(); ++it
) {
155 if (it
->second
->check_for_obj(obj_id
)) {
156 ceph_assert(it
->first
!= coll_id
);
161 dout(0) << "do_touch new object in collection and exists in another" << dendl
;
165 hobject_t
*obj
= entry
->touch_obj(obj_id
);
167 dout(0) << "do_touch " << entry
->m_coll
.to_str() << "/" << obj
->oid
.name
<< dendl
;
169 _do_touch(entry
->m_coll
, *obj
);
173 bool DeterministicOpSequence::do_remove(rngen_t
& gen
)
175 int coll_id
= _gen_coll_id(gen
);
177 coll_entry_t
*entry
= get_coll_at(coll_id
);
178 ceph_assert(entry
!= NULL
);
180 if (entry
->m_objects
.size() == 0) {
181 dout(0) << "do_remove no objects in collection" << dendl
;
184 int obj_id
= entry
->get_random_obj_id(gen
);
185 hobject_t
*obj
= entry
->touch_obj(obj_id
);
188 dout(0) << "do_remove " << entry
->m_coll
.to_str() << "/" << obj
->oid
.name
<< dendl
;
190 _do_remove(entry
->m_coll
, *obj
);
191 hobject_t
*rmobj
= entry
->remove_obj(obj_id
);
197 static void _gen_random(rngen_t
& gen
,
198 size_t size
, bufferlist
& bl
) {
200 static const char alphanum
[] = "0123456789"
201 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
202 "abcdefghijklmnopqrstuvwxyz";
204 boost::uniform_int
<> char_rng(0, sizeof(alphanum
));
206 for (unsigned int i
= 0; i
< size
- 1; i
++) {
207 bp
[i
] = alphanum
[char_rng(gen
)];
213 static void gen_attrs(rngen_t
&gen
,
214 map
<string
, bufferlist
> *out
) {
215 boost::uniform_int
<> num_rng(10, 30);
216 boost::uniform_int
<> key_size_rng(5, 10);
217 boost::uniform_int
<> val_size_rng(100, 1000);
218 size_t num_attrs
= static_cast<size_t>(num_rng(gen
));
219 for (size_t i
= 0; i
< num_attrs
; ++i
) {
220 size_t key_size
= static_cast<size_t>(num_rng(gen
));
221 size_t val_size
= static_cast<size_t>(num_rng(gen
));
223 _gen_random(gen
, key_size
, keybl
);
224 string
key(keybl
.c_str(), keybl
.length());
225 _gen_random(gen
, val_size
, (*out
)[key
]);
229 bool DeterministicOpSequence::do_set_attrs(rngen_t
& gen
)
231 int coll_id
= _gen_coll_id(gen
);
233 coll_entry_t
*entry
= get_coll_at(coll_id
);
234 ceph_assert(entry
!= NULL
);
236 if (entry
->m_objects
.size() == 0) {
237 dout(0) << "do_set_attrs no objects in collection" << dendl
;
240 int obj_id
= entry
->get_random_obj_id(gen
);
241 hobject_t
*obj
= entry
->touch_obj(obj_id
);
244 map
<string
, bufferlist
> out
;
245 gen_attrs(gen
, &out
);
247 dout(0) << "do_set_attrs " << out
.size() << " entries" << dendl
;
248 _do_set_attrs(entry
->m_coll
, *obj
, out
);
252 bool DeterministicOpSequence::do_write(rngen_t
& gen
)
254 int coll_id
= _gen_coll_id(gen
);
256 coll_entry_t
*entry
= get_coll_at(coll_id
);
257 ceph_assert(entry
!= NULL
);
259 if (entry
->m_objects
.size() == 0) {
260 dout(0) << "do_write no objects in collection" << dendl
;
263 int obj_id
= entry
->get_random_obj_id(gen
);
264 hobject_t
*obj
= entry
->touch_obj(obj_id
);
267 boost::uniform_int
<> size_rng(100, (2 << 19));
268 size_t size
= (size_t) size_rng(gen
);
270 _gen_random(gen
, size
, bl
);
272 dout(0) << "do_write " << entry
->m_coll
.to_str() << "/" << obj
->oid
.name
273 << " 0~" << size
<< dendl
;
275 _do_write(entry
->m_coll
, *obj
, 0, bl
.length(), bl
);
279 bool DeterministicOpSequence::_prepare_clone(rngen_t
& gen
,
280 coll_t
& coll_ret
, hobject_t
& orig_obj_ret
, hobject_t
& new_obj_ret
)
282 int coll_id
= _gen_coll_id(gen
);
284 coll_entry_t
*entry
= get_coll_at(coll_id
);
285 ceph_assert(entry
!= NULL
);
287 if (entry
->m_objects
.size() >= 2) {
288 dout(0) << "_prepare_clone coll " << entry
->m_coll
.to_str()
289 << " doesn't have 2 or more objects" << dendl
;
293 int orig_obj_id
= entry
->get_random_obj_id(gen
);
294 hobject_t
*orig_obj
= entry
->touch_obj(orig_obj_id
);
295 ceph_assert(orig_obj
);
299 id
= entry
->get_random_obj_id(gen
);
300 } while (id
== orig_obj_id
);
301 hobject_t
*new_obj
= entry
->touch_obj(id
);
302 ceph_assert(new_obj
);
304 coll_ret
= entry
->m_coll
;
305 orig_obj_ret
= *orig_obj
;
306 new_obj_ret
= *new_obj
;
311 bool DeterministicOpSequence::do_clone(rngen_t
& gen
)
314 hobject_t orig_obj
, new_obj
;
315 if (!_prepare_clone(gen
, coll
, orig_obj
, new_obj
)) {
319 dout(0) << "do_clone " << coll
.to_str() << "/" << orig_obj
.oid
.name
320 << " => " << coll
.to_str() << "/" << new_obj
.oid
.name
<< dendl
;
322 _do_clone(coll
, orig_obj
, new_obj
);
326 bool DeterministicOpSequence::do_clone_range(rngen_t
& gen
)
329 hobject_t orig_obj
, new_obj
;
330 if (!_prepare_clone(gen
, coll
, orig_obj
, new_obj
)) {
334 /* Whenever we have to make a clone_range() operation, just write to the
335 * object first, so we know we have something to clone in the said range.
336 * This may not be the best solution ever, but currently we're not keeping
337 * track of the written-to objects, and until we know for sure we really
338 * need to, let's just focus on the task at hand.
341 boost::uniform_int
<> write_size_rng(100, (2 << 19));
342 size_t size
= (size_t) write_size_rng(gen
);
344 _gen_random(gen
, size
, bl
);
346 boost::uniform_int
<> clone_len(1, bl
.length());
347 size
= (size_t) clone_len(gen
);
349 dout(0) << "do_clone_range " << coll
.to_str() << "/" << orig_obj
.oid
.name
350 << " (0~" << size
<< ")"
351 << " => " << coll
.to_str() << "/" << new_obj
.oid
.name
353 _do_write_and_clone_range(coll
, orig_obj
, new_obj
, 0, size
, 0, bl
);
357 bool DeterministicOpSequence::_prepare_colls(rngen_t
& gen
,
358 coll_entry_t
* &orig_coll
, coll_entry_t
* &new_coll
)
360 ceph_assert(m_collections_ids
.size() > 1);
361 int orig_coll_id
= _gen_coll_id(gen
);
364 new_coll_id
= _gen_coll_id(gen
);
365 } while (new_coll_id
== orig_coll_id
);
367 dout(0) << "_prepare_colls from coll id " << orig_coll_id
368 << " to coll id " << new_coll_id
<< dendl
;
370 orig_coll
= get_coll_at(orig_coll_id
);
371 ceph_assert(orig_coll
!= NULL
);
372 new_coll
= get_coll_at(new_coll_id
);
373 ceph_assert(new_coll
!= NULL
);
375 if (!orig_coll
->m_objects
.size()) {
376 dout(0) << "_prepare_colls coll " << orig_coll
->m_coll
.to_str()
377 << " has no objects to use" << dendl
;
385 bool DeterministicOpSequence::do_coll_move(rngen_t
& gen
)
387 coll_entry_t
*orig_coll
= NULL
, *new_coll
= NULL
;
388 if (!_prepare_colls(gen
, orig_coll
, new_coll
))
391 ceph_assert(orig_coll
&& new_coll
);
393 boost::uniform_int
<> obj_rng(0, orig_coll
->m_objects
.size()-1);
394 int obj_pos
= obj_rng(gen
);
396 hobject_t
*obj
= orig_coll
->get_obj_at(obj_pos
, &obj_key
);
398 dout(0) << "do_coll_move coll " << orig_coll
->m_coll
.to_str()
399 << " has no object as pos #" << obj_pos
<< " (key " << obj_key
<< ")"
403 if (new_coll
->check_for_obj(obj_key
)) {
404 dout(0) << "do_coll_move coll " << orig_coll
->m_coll
.to_str()
405 << " already has object as pos #" << obj_pos
<< " (key " << obj_key
<< ")"
409 dout(0) << "do_coll_move " << orig_coll
->m_coll
.to_str() << "/" << obj
->oid
.name
410 << " => " << new_coll
->m_coll
.to_str() << "/" << obj
->oid
.name
<< dendl
;
411 new_coll
->touch_obj(obj_key
);
413 orig_coll
->remove_obj(obj_key
);
415 _do_coll_move(orig_coll
->m_coll
, new_coll
->m_coll
, *obj
);
420 bool DeterministicOpSequence::do_coll_create(rngen_t
& gen
)
422 boost::uniform_int
<> pg_num_range(0, 512);
423 int pg_num
= pg_num_range(gen
);
425 // Assume there is 7 OSDs in total, the PGs are evenly distributed across those OSDs
426 int pgs
= pg_num
/ 7;
428 boost::uniform_int
<> num_objs_range(1, 1024);
429 int num_objs
= num_objs_range(gen
);
431 int pool_id
= get_next_pool_id();
432 std::set
<int> pg_created
;
433 for (int i
= 0; i
< pgs
; i
++) {
434 boost::uniform_int
<> pg_range(0, pg_num
- 1);
435 int pg_id
= pg_range(gen
);
436 if (pg_created
.count(pg_id
) > 0)
438 _do_coll_create(coll_t(spg_t(pg_t(pg_id
,pool_id
),shard_id_t::NO_SHARD
)),
439 (uint32_t) pg_num
, (uint64_t) num_objs
);
440 pg_created
.insert(pg_id
);
445 void DeterministicOpSequence::_do_coll_create(coll_t cid
, uint32_t pg_num
, uint64_t num_objs
)
447 ObjectStore::Transaction t
;
449 t
.create_collection(cid
, 32);
451 ::encode(pg_num
, hint
);
452 ::encode(num_objs
, hint
);
453 t
.collection_hint(cid
, ObjectStore::Transaction::COLL_HINT_EXPECTED_NUM_OBJECTS
, hint
);
454 dout(0) << "Give collection: " << cid
<< " a hint, pg_num is: " << pg_num
<< ", num_objs is: "
455 << num_objs
<< dendl
;
457 m_store
->apply_transaction(&m_osr
, std::move(t
));
460 void DeterministicOpSequence::_do_touch(coll_t coll
, hobject_t
& obj
)
462 ObjectStore::Transaction t
;
464 t
.touch(coll
, ghobject_t(obj
));
465 m_store
->apply_transaction(&m_osr
, std::move(t
));
468 void DeterministicOpSequence::_do_remove(coll_t coll
, hobject_t
& obj
)
470 ObjectStore::Transaction t
;
472 t
.remove(coll
, ghobject_t(obj
));
473 m_store
->apply_transaction(&m_osr
, std::move(t
));
476 void DeterministicOpSequence::_do_set_attrs(coll_t coll
,
478 const map
<string
, bufferlist
> &attrs
)
480 ObjectStore::Transaction t
;
482 t
.omap_setkeys(coll
, ghobject_t(obj
), attrs
);
483 m_store
->apply_transaction(&m_osr
, std::move(t
));
486 void DeterministicOpSequence::_do_write(coll_t coll
, hobject_t
& obj
,
487 uint64_t off
, uint64_t len
, const bufferlist
& data
)
489 ObjectStore::Transaction t
;
491 t
.write(coll
, ghobject_t(obj
), off
, len
, data
);
492 m_store
->apply_transaction(&m_osr
, std::move(t
));
495 void DeterministicOpSequence::_do_clone(coll_t coll
, hobject_t
& orig_obj
,
498 ObjectStore::Transaction t
;
500 t
.clone(coll
, ghobject_t(orig_obj
), ghobject_t(new_obj
));
501 m_store
->apply_transaction(&m_osr
, std::move(t
));
504 void DeterministicOpSequence::_do_clone_range(coll_t coll
,
505 hobject_t
& orig_obj
, hobject_t
& new_obj
, uint64_t srcoff
,
506 uint64_t srclen
, uint64_t dstoff
)
508 ObjectStore::Transaction t
;
510 t
.clone_range(coll
, ghobject_t(orig_obj
), ghobject_t(new_obj
),
511 srcoff
, srclen
, dstoff
);
512 m_store
->apply_transaction(&m_osr
, std::move(t
));
515 void DeterministicOpSequence::_do_write_and_clone_range(coll_t coll
,
523 ObjectStore::Transaction t
;
525 t
.write(coll
, ghobject_t(orig_obj
), srcoff
, bl
.length(), bl
);
526 t
.clone_range(coll
, ghobject_t(orig_obj
), ghobject_t(new_obj
),
527 srcoff
, srclen
, dstoff
);
528 m_store
->apply_transaction(&m_osr
, std::move(t
));
531 void DeterministicOpSequence::_do_coll_move(coll_t orig_coll
, coll_t new_coll
,
534 ObjectStore::Transaction t
;
536 t
.remove(new_coll
, ghobject_t(obj
));
537 t
.collection_move_rename(orig_coll
, ghobject_t(obj
), new_coll
, ghobject_t(obj
));
538 m_store
->apply_transaction(&m_osr
, std::move(t
));