1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 #include "include/int_types.h"
5 #include "common/ceph_mutex.h"
6 #include "include/rados/librados.hpp"
20 #include "TestOpStat.h"
21 #include "test/librados/test.h"
22 #include "common/sharedptr_registry.hpp"
23 #include "common/errno.h"
24 #include "osd/HitSet.h"
31 class RadosTestContext
;
35 typename
T::iterator
rand_choose(T
&cont
) {
36 if (std::empty(cont
)) {
37 return std::end(cont
);
39 return std::next(std::begin(cont
), rand() % cont
.size());
59 TEST_OP_CACHE_TRY_FLUSH
,
64 TEST_OP_UNSET_REDIRECT
,
70 class TestWatchContext
: public librados::WatchCtx2
{
71 TestWatchContext(const TestWatchContext
&);
73 ceph::condition_variable cond
;
76 ceph::mutex lock
= ceph::make_mutex("watch lock");
77 TestWatchContext() = default;
78 void handle_notify(uint64_t notify_id
, uint64_t cookie
,
80 bufferlist
&bl
) override
{
81 std::lock_guard l
{lock
};
85 void handle_error(uint64_t cookie
, int err
) override
{
86 std::lock_guard l
{lock
};
87 cout
<< "watch handle_error " << err
<< std::endl
;
90 std::lock_guard l
{lock
};
94 std::unique_lock l
{lock
};
95 cond
.wait(l
, [this] { return !waiting
; });
97 uint64_t &get_handle() {
105 RadosTestContext
*context
;
108 TestOp(int n
, RadosTestContext
*context
,
109 TestOpStat
*stat
= 0)
115 virtual ~TestOp() {};
118 * This struct holds data to be passed by a callback
119 * to a TestOp::finish method.
121 struct CallbackInfo
{
123 explicit CallbackInfo(uint64_t id
) : id(id
) {}
124 virtual ~CallbackInfo() {};
127 virtual void _begin() = 0;
130 * Called when the operation completes.
131 * This should be overridden by asynchronous operations.
133 * @param info information stored by a callback, or NULL -
134 * useful for multi-operation TestOps
136 virtual void _finish(CallbackInfo
*info
)
140 virtual string
getType() = 0;
141 virtual bool finished()
147 void finish(CallbackInfo
*info
);
148 virtual bool must_quiesce_other_ops() { return false; }
151 class TestOpGenerator
{
153 virtual ~TestOpGenerator() {};
154 virtual TestOp
*next(RadosTestContext
&context
) = 0;
157 class RadosTestContext
{
159 ceph::mutex state_lock
= ceph::make_mutex("Context Lock");
160 ceph::condition_variable wait_cond
;
161 // snap => {oid => desc}
162 map
<int, map
<string
,ObjectDesc
> > pool_obj_cont
;
163 set
<string
> oid_in_use
;
164 set
<string
> oid_not_in_use
;
165 set
<string
> oid_flushing
;
166 set
<string
> oid_not_flushing
;
167 set
<string
> oid_redirect_not_in_use
;
168 set
<string
> oid_redirect_in_use
;
169 SharedPtrRegistry
<int, int> snaps_in_use
;
172 librados::IoCtx io_ctx
;
173 librados::Rados rados
;
179 map
<int,uint64_t> snaps
;
181 const char *rados_id
;
183 map
<string
, TestWatchContext
*> watches
;
184 const uint64_t max_size
;
185 const uint64_t min_stride_size
;
186 const uint64_t max_stride_size
;
187 AttrGenerator attr_gen
;
189 const bool no_sparse
;
191 bool write_fadvise_dontneed
;
192 string low_tier_pool_name
;
193 librados::IoCtx low_tier_io_ctx
;
195 map
<string
,string
> redirect_objs
;
198 RadosTestContext(const string
&pool_name
,
201 uint64_t min_stride_size
,
202 uint64_t max_stride_size
,
206 bool write_fadvise_dontneed
,
207 const string
&low_tier_pool_name
,
209 const char *id
= 0) :
212 pool_name(pool_name
),
215 max_in_flight(max_in_flight
),
217 rados_id(id
), initialized(false),
219 min_stride_size(min_stride_size
), max_stride_size(max_stride_size
),
220 attr_gen(2000, 20000),
222 no_sparse(no_sparse
),
223 pool_snaps(pool_snaps
),
224 write_fadvise_dontneed(write_fadvise_dontneed
),
225 low_tier_pool_name(low_tier_pool_name
),
227 enable_dedup(enable_dedup
)
233 int r
= rados
.init(rados_id
);
236 r
= rados
.conf_read_file(NULL
);
239 r
= rados
.conf_parse_env(NULL
);
245 r
= rados
.ioctx_create(pool_name
.c_str(), io_ctx
);
250 if (!low_tier_pool_name
.empty()) {
251 r
= rados
.ioctx_create(low_tier_pool_name
.c_str(), low_tier_io_ctx
);
258 r
= rados
.mon_command(
259 "{\"prefix\": \"osd pool set\", \"pool\": \"" + pool_name
+
260 "\", \"var\": \"write_fadvise_dontneed\", \"val\": \"" + (write_fadvise_dontneed
? "true" : "false") + "\"}",
267 r
= rados
.mon_command(
268 "{\"prefix\": \"osd pool set\", \"pool\": \"" + pool_name
+
269 "\", \"var\": \"fingerprint_algorithm\", \"val\": \"" + "sha256" + "\"}",
277 char hostname_cstr
[100];
278 gethostname(hostname_cstr
, 100);
279 stringstream hostpid
;
280 hostpid
<< hostname_cstr
<< getpid() << "-";
281 prefix
= hostpid
.str();
282 ceph_assert(!initialized
);
294 void loop(TestOpGenerator
*gen
)
296 ceph_assert(initialized
);
297 list
<TestOp
*> inflight
;
298 std::unique_lock state_locker
{state_lock
};
300 TestOp
*next
= gen
->next(*this);
301 TestOp
*waiting
= NULL
;
303 while (next
|| !inflight
.empty()) {
304 if (next
&& next
->must_quiesce_other_ops() && !inflight
.empty()) {
306 next
= NULL
; // Force to wait for inflight to drain
309 inflight
.push_back(next
);
313 (*inflight
.rbegin())->begin();
317 for (list
<TestOp
*>::iterator i
= inflight
.begin();
318 i
!= inflight
.end();) {
319 if ((*i
)->finished()) {
320 cout
<< (*i
)->num
<< ": done (" << (inflight
.size()-1) << " left)" << std::endl
;
328 if (inflight
.size() >= (unsigned) max_in_flight
|| (!next
&& !inflight
.empty())) {
329 cout
<< " waiting on " << inflight
.size() << std::endl
;
330 wait_cond
.wait(state_locker
);
339 next
= gen
->next(*this);
346 wait_cond
.notify_all();
349 TestWatchContext
*get_watch_context(const string
&oid
) {
350 return watches
.count(oid
) ? watches
[oid
] : 0;
353 TestWatchContext
*watch(const string
&oid
) {
354 ceph_assert(!watches
.count(oid
));
355 return (watches
[oid
] = new TestWatchContext
);
358 void unwatch(const string
&oid
) {
359 ceph_assert(watches
.count(oid
));
364 ObjectDesc
get_most_recent(const string
&oid
) {
366 for (map
<int, map
<string
,ObjectDesc
> >::reverse_iterator i
=
367 pool_obj_cont
.rbegin();
368 i
!= pool_obj_cont
.rend();
370 map
<string
,ObjectDesc
>::iterator j
= i
->second
.find(oid
);
371 if (j
!= i
->second
.end()) {
379 void rm_object_attrs(const string
&oid
, const set
<string
> &attrs
)
381 ObjectDesc new_obj
= get_most_recent(oid
);
382 for (set
<string
>::const_iterator i
= attrs
.begin();
385 new_obj
.attrs
.erase(*i
);
387 new_obj
.dirty
= true;
388 pool_obj_cont
[current_snap
].insert_or_assign(oid
, new_obj
);
391 void remove_object_header(const string
&oid
)
393 ObjectDesc new_obj
= get_most_recent(oid
);
394 new_obj
.header
= bufferlist();
395 new_obj
.dirty
= true;
396 pool_obj_cont
[current_snap
].insert_or_assign(oid
, new_obj
);
400 void update_object_header(const string
&oid
, const bufferlist
&bl
)
402 ObjectDesc new_obj
= get_most_recent(oid
);
404 new_obj
.exists
= true;
405 new_obj
.dirty
= true;
406 pool_obj_cont
[current_snap
].insert_or_assign(oid
, new_obj
);
409 void update_object_attrs(const string
&oid
, const map
<string
, ContDesc
> &attrs
)
411 ObjectDesc new_obj
= get_most_recent(oid
);
412 for (map
<string
, ContDesc
>::const_iterator i
= attrs
.begin();
415 new_obj
.attrs
[i
->first
] = i
->second
;
417 new_obj
.exists
= true;
418 new_obj
.dirty
= true;
419 pool_obj_cont
[current_snap
].insert_or_assign(oid
, new_obj
);
422 void update_object(ContentsGenerator
*cont_gen
,
423 const string
&oid
, const ContDesc
&contents
)
425 ObjectDesc new_obj
= get_most_recent(oid
);
426 new_obj
.exists
= true;
427 new_obj
.dirty
= true;
428 new_obj
.update(cont_gen
,
430 pool_obj_cont
[current_snap
].insert_or_assign(oid
, new_obj
);
433 void update_object_full(const string
&oid
, const ObjectDesc
&contents
)
435 pool_obj_cont
[current_snap
].insert_or_assign(oid
, contents
);
436 pool_obj_cont
[current_snap
][oid
].dirty
= true;
439 void update_object_undirty(const string
&oid
)
441 ObjectDesc new_obj
= get_most_recent(oid
);
442 new_obj
.dirty
= false;
443 pool_obj_cont
[current_snap
].insert_or_assign(oid
, new_obj
);
446 void update_object_version(const string
&oid
, uint64_t version
,
449 for (map
<int, map
<string
,ObjectDesc
> >::reverse_iterator i
=
450 pool_obj_cont
.rbegin();
451 i
!= pool_obj_cont
.rend();
453 if (snap
!= -1 && snap
< i
->first
)
455 map
<string
,ObjectDesc
>::iterator j
= i
->second
.find(oid
);
456 if (j
!= i
->second
.end()) {
458 j
->second
.version
= version
;
459 cout
<< __func__
<< " oid " << oid
460 << " v " << version
<< " " << j
->second
.most_recent()
461 << " " << (j
->second
.dirty
? "dirty" : "clean")
462 << " " << (j
->second
.exists
? "exists" : "dne")
469 void remove_object(const string
&oid
)
471 ceph_assert(!get_watch_context(oid
));
473 pool_obj_cont
[current_snap
].insert_or_assign(oid
, new_obj
);
476 bool find_object(const string
&oid
, ObjectDesc
*contents
, int snap
= -1) const
478 for (map
<int, map
<string
,ObjectDesc
> >::const_reverse_iterator i
=
479 pool_obj_cont
.rbegin();
480 i
!= pool_obj_cont
.rend();
482 if (snap
!= -1 && snap
< i
->first
) continue;
483 if (i
->second
.count(oid
) != 0) {
484 *contents
= i
->second
.find(oid
)->second
;
491 void update_object_redirect_target(const string
&oid
, const string
&target
)
493 redirect_objs
[oid
] = target
;
496 void update_object_chunk_target(const string
&oid
, uint64_t offset
, const ChunkDesc
&info
)
498 for (map
<int, map
<string
,ObjectDesc
> >::const_reverse_iterator i
=
499 pool_obj_cont
.rbegin();
500 i
!= pool_obj_cont
.rend();
502 if (i
->second
.count(oid
) != 0) {
503 ObjectDesc obj_desc
= i
->second
.find(oid
)->second
;
504 obj_desc
.chunk_info
[offset
] = info
;
505 update_object_full(oid
, obj_desc
);
512 bool object_existed_at(const string
&oid
, int snap
= -1) const
515 bool found
= find_object(oid
, &contents
, snap
);
516 return found
&& contents
.exists
;
519 void remove_snap(int snap
)
521 map
<int, map
<string
,ObjectDesc
> >::iterator next_iter
= pool_obj_cont
.find(snap
);
522 ceph_assert(next_iter
!= pool_obj_cont
.end());
523 map
<int, map
<string
,ObjectDesc
> >::iterator current_iter
= next_iter
++;
524 ceph_assert(current_iter
!= pool_obj_cont
.end());
525 map
<string
,ObjectDesc
> ¤t
= current_iter
->second
;
526 map
<string
,ObjectDesc
> &next
= next_iter
->second
;
527 for (map
<string
,ObjectDesc
>::iterator i
= current
.begin();
530 if (next
.count(i
->first
) == 0) {
531 next
.insert(pair
<string
,ObjectDesc
>(i
->first
, i
->second
));
534 pool_obj_cont
.erase(current_iter
);
538 void add_snap(uint64_t snap
)
540 snaps
[current_snap
] = snap
;
542 pool_obj_cont
[current_snap
];
546 void roll_back(const string
&oid
, int snap
)
548 ceph_assert(!get_watch_context(oid
));
550 find_object(oid
, &contents
, snap
);
551 contents
.dirty
= true;
552 pool_obj_cont
.rbegin()->second
.insert_or_assign(oid
, contents
);
556 void read_callback(librados::completion_t comp
, void *arg
);
557 void write_callback(librados::completion_t comp
, void *arg
);
559 /// remove random xattrs from given object, and optionally remove omap
560 /// entries if @c no_omap is not specified in context
561 class RemoveAttrsOp
: public TestOp
{
564 librados::ObjectWriteOperation op
;
565 librados::AioCompletion
*comp
;
566 RemoveAttrsOp(int n
, RadosTestContext
*context
,
569 : TestOp(n
, context
, stat
), oid(oid
), comp(NULL
)
572 void _begin() override
575 set
<string
> to_remove
;
577 std::lock_guard l
{context
->state_lock
};
579 if (!context
->find_object(oid
, &obj
)) {
584 cont
= ContDesc(context
->seq_num
, context
->current_snap
,
585 context
->seq_num
, "");
586 context
->oid_in_use
.insert(oid
);
587 context
->oid_not_in_use
.erase(oid
);
590 ContentsGenerator::iterator iter
= context
->attr_gen
.get_iterator(cont
);
591 for (map
<string
, ContDesc
>::iterator i
= obj
.attrs
.begin();
592 i
!= obj
.attrs
.end();
595 to_remove
.insert(i
->first
);
596 op
.rmxattr(i
->first
.c_str());
599 if (to_remove
.empty()) {
601 context
->oid_in_use
.erase(oid
);
602 context
->oid_not_in_use
.insert(oid
);
606 if (!context
->no_omap
) {
607 op
.omap_rm_keys(to_remove
);
610 if (!context
->no_omap
) {
613 for (map
<string
, ContDesc
>::iterator i
= obj
.attrs
.begin();
614 i
!= obj
.attrs
.end();
616 op
.rmxattr(i
->first
.c_str());
617 to_remove
.insert(i
->first
);
619 context
->remove_object_header(oid
);
621 context
->rm_object_attrs(oid
, to_remove
);
624 pair
<TestOp
*, TestOp::CallbackInfo
*> *cb_arg
=
625 new pair
<TestOp
*, TestOp::CallbackInfo
*>(this,
626 new TestOp::CallbackInfo(0));
627 comp
= context
->rados
.aio_create_completion((void*) cb_arg
,
629 context
->io_ctx
.aio_operate(context
->prefix
+oid
, comp
, &op
);
632 void _finish(CallbackInfo
*info
) override
634 std::lock_guard l
{context
->state_lock
};
636 context
->update_object_version(oid
, comp
->get_version64());
637 context
->oid_in_use
.erase(oid
);
638 context
->oid_not_in_use
.insert(oid
);
642 bool finished() override
647 string
getType() override
649 return "RemoveAttrsOp";
653 /// add random xattrs to given object, and optionally add omap
654 /// entries if @c no_omap is not specified in context
655 class SetAttrsOp
: public TestOp
{
658 librados::ObjectWriteOperation op
;
659 librados::AioCompletion
*comp
;
661 RadosTestContext
*context
,
664 : TestOp(n
, context
, stat
),
668 void _begin() override
672 std::lock_guard l
{context
->state_lock
};
673 cont
= ContDesc(context
->seq_num
, context
->current_snap
,
674 context
->seq_num
, "");
675 context
->oid_in_use
.insert(oid
);
676 context
->oid_not_in_use
.erase(oid
);
679 map
<string
, bufferlist
> omap_contents
;
680 map
<string
, ContDesc
> omap
;
682 ContentsGenerator::iterator keygen
= context
->attr_gen
.get_iterator(cont
);
684 while (!*keygen
) ++keygen
;
687 header
.append(*keygen
);
690 for (int i
= 0; i
< 20; ++i
) {
692 while (!*keygen
) ++keygen
;
693 while (*keygen
&& key
.size() < 40) {
694 key
.push_back((*keygen
% 20) + 'a');
698 val
.seqnum
+= (unsigned)(*keygen
);
699 val
.prefix
= ("oid: " + oid
);
701 bufferlist val_buffer
= context
->attr_gen
.gen_bl(val
);
702 omap_contents
[key
] = val_buffer
;
703 op
.setxattr(key
.c_str(), val_buffer
);
705 if (!context
->no_omap
) {
706 op
.omap_set_header(header
);
707 op
.omap_set(omap_contents
);
711 std::lock_guard l
{context
->state_lock
};
712 context
->update_object_header(oid
, header
);
713 context
->update_object_attrs(oid
, omap
);
716 pair
<TestOp
*, TestOp::CallbackInfo
*> *cb_arg
=
717 new pair
<TestOp
*, TestOp::CallbackInfo
*>(this,
718 new TestOp::CallbackInfo(0));
719 comp
= context
->rados
.aio_create_completion((void*) cb_arg
, &write_callback
);
720 context
->io_ctx
.aio_operate(context
->prefix
+oid
, comp
, &op
);
723 void _finish(CallbackInfo
*info
) override
725 std::lock_guard l
{context
->state_lock
};
727 if ((r
= comp
->get_return_value())) {
728 cerr
<< "err " << r
<< std::endl
;
732 context
->update_object_version(oid
, comp
->get_version64());
733 context
->oid_in_use
.erase(oid
);
734 context
->oid_not_in_use
.insert(oid
);
738 bool finished() override
743 string
getType() override
749 class WriteOp
: public TestOp
{
753 set
<librados::AioCompletion
*> waiting
;
754 librados::AioCompletion
*rcompletion
= nullptr;
755 // numbers of async ops submitted
756 uint64_t waiting_on
= 0;
757 uint64_t last_acked_tid
= 0;
759 librados::ObjectReadOperation read_op
;
760 librados::ObjectWriteOperation write_op
;
763 const bool do_append
;
767 RadosTestContext
*context
,
771 TestOpStat
*stat
= 0)
772 : TestOp(n
, context
, stat
),
774 do_append(do_append
),
778 void _begin() override
782 std::lock_guard state_locker
{context
->state_lock
};
783 acc
<< context
->prefix
<< "OID: " << oid
<< " snap " << context
->current_snap
<< std::endl
;
784 string prefix
= acc
.str();
786 cont
= ContDesc(context
->seq_num
, context
->current_snap
, context
->seq_num
, prefix
);
788 ContentsGenerator
*cont_gen
;
790 ObjectDesc old_value
;
791 bool found
= context
->find_object(oid
, &old_value
);
792 uint64_t prev_length
= found
&& old_value
.has_contents() ?
793 old_value
.most_recent_gen()->get_length(old_value
.most_recent()) :
795 bool requires_alignment
;
796 int r
= context
->io_ctx
.pool_requires_alignment2(&requires_alignment
);
798 uint64_t alignment
= 0;
799 if (requires_alignment
) {
800 r
= context
->io_ctx
.pool_required_alignment2(&alignment
);
802 ceph_assert(alignment
!= 0);
804 cont_gen
= new AppendGenerator(
807 context
->min_stride_size
,
808 context
->max_stride_size
,
811 cont_gen
= new VarLenGenerator(
812 context
->max_size
, context
->min_stride_size
, context
->max_stride_size
);
814 context
->update_object(cont_gen
, oid
, cont
);
816 context
->oid_in_use
.insert(oid
);
817 context
->oid_not_in_use
.erase(oid
);
819 map
<uint64_t, uint64_t> ranges
;
821 cont_gen
->get_ranges_map(cont
, ranges
);
822 std::cout
<< num
<< ": seq_num " << context
->seq_num
<< " ranges " << ranges
<< std::endl
;
825 waiting_on
= ranges
.size();
826 ContentsGenerator::iterator gen_pos
= cont_gen
->get_iterator(cont
);
827 // assure that tid is greater than last_acked_tid
828 uint64_t tid
= last_acked_tid
+ 1;
829 for (auto [offset
, len
] : ranges
) {
830 gen_pos
.seek(offset
);
831 bufferlist to_write
= gen_pos
.gen_bl_advance(len
);
832 ceph_assert(to_write
.length() == len
);
833 ceph_assert(to_write
.length() > 0);
834 std::cout
<< num
<< ": writing " << context
->prefix
+oid
835 << " from " << offset
836 << " to " << len
+ offset
<< " tid " << tid
<< std::endl
;
838 new pair
<TestOp
*, TestOp::CallbackInfo
*>(this,
839 new TestOp::CallbackInfo(tid
++));
840 librados::AioCompletion
*completion
=
841 context
->rados
.aio_create_completion((void*) cb_arg
, &write_callback
);
842 waiting
.insert(completion
);
843 librados::ObjectWriteOperation op
;
847 op
.write(offset
, to_write
);
849 if (do_excl
&& cb_arg
->second
->id
== last_acked_tid
+ 1)
851 context
->io_ctx
.aio_operate(
852 context
->prefix
+oid
, completion
,
857 encode(cont
, contbl
);
858 pair
<TestOp
*, TestOp::CallbackInfo
*> *cb_arg
=
859 new pair
<TestOp
*, TestOp::CallbackInfo
*>(
861 new TestOp::CallbackInfo(tid
++));
862 librados::AioCompletion
*completion
= context
->rados
.aio_create_completion(
863 (void*) cb_arg
, &write_callback
);
864 waiting
.insert(completion
);
866 write_op
.setxattr("_header", contbl
);
868 write_op
.truncate(cont_gen
->get_length(cont
));
870 context
->io_ctx
.aio_operate(
871 context
->prefix
+oid
, completion
, &write_op
);
874 new pair
<TestOp
*, TestOp::CallbackInfo
*>(
876 new TestOp::CallbackInfo(tid
++));
877 rcompletion
= context
->rados
.aio_create_completion(
878 (void*) cb_arg
, &write_callback
);
880 read_op
.read(0, 1, &rbuffer
, 0);
881 context
->io_ctx
.aio_operate(
882 context
->prefix
+oid
, rcompletion
,
884 librados::OPERATION_ORDER_READS_WRITES
, // order wrt previous write/update
888 void _finish(CallbackInfo
*info
) override
891 std::lock_guard state_locker
{context
->state_lock
};
892 uint64_t tid
= info
->id
;
894 cout
<< num
<< ": finishing write tid " << tid
<< " to " << context
->prefix
+ oid
<< std::endl
;
896 if (tid
<= last_acked_tid
) {
897 cerr
<< "Error: finished tid " << tid
898 << " when last_acked_tid was " << last_acked_tid
<< std::endl
;
901 last_acked_tid
= tid
;
905 if (waiting_on
== 0) {
906 uint64_t version
= 0;
907 for (set
<librados::AioCompletion
*>::iterator i
= waiting
.begin();
910 ceph_assert((*i
)->is_complete());
911 if (int err
= (*i
)->get_return_value()) {
912 cerr
<< "Error: oid " << oid
<< " write returned error code "
915 if ((*i
)->get_version64() > version
)
916 version
= (*i
)->get_version64();
921 context
->update_object_version(oid
, version
);
922 if (rcompletion
->get_version64() != version
) {
923 cerr
<< "Error: racing read on " << oid
<< " returned version "
924 << rcompletion
->get_version64() << " rather than version "
925 << version
<< std::endl
;
926 ceph_abort_msg("racing read got wrong version");
930 ObjectDesc old_value
;
931 ceph_assert(context
->find_object(oid
, &old_value
, -1));
932 if (old_value
.deleted())
933 std::cout
<< num
<< ": left oid " << oid
<< " deleted" << std::endl
;
935 std::cout
<< num
<< ": left oid " << oid
<< " "
936 << old_value
.most_recent() << std::endl
;
939 rcompletion
->release();
940 context
->oid_in_use
.erase(oid
);
941 context
->oid_not_in_use
.insert(oid
);
947 bool finished() override
952 string
getType() override
958 class WriteSameOp
: public TestOp
{
962 set
<librados::AioCompletion
*> waiting
;
963 librados::AioCompletion
*rcompletion
;
965 uint64_t last_acked_tid
;
967 librados::ObjectReadOperation read_op
;
968 librados::ObjectWriteOperation write_op
;
972 RadosTestContext
*context
,
974 TestOpStat
*stat
= 0)
975 : TestOp(n
, context
, stat
),
976 oid(oid
), rcompletion(NULL
), waiting_on(0),
980 void _begin() override
982 std::lock_guard state_locker
{context
->state_lock
};
985 acc
<< context
->prefix
<< "OID: " << oid
<< " snap " << context
->current_snap
<< std::endl
;
986 string prefix
= acc
.str();
988 cont
= ContDesc(context
->seq_num
, context
->current_snap
, context
->seq_num
, prefix
);
990 ContentsGenerator
*cont_gen
;
991 cont_gen
= new VarLenGenerator(
992 context
->max_size
, context
->min_stride_size
, context
->max_stride_size
);
993 context
->update_object(cont_gen
, oid
, cont
);
995 context
->oid_in_use
.insert(oid
);
996 context
->oid_not_in_use
.erase(oid
);
998 map
<uint64_t, uint64_t> ranges
;
1000 cont_gen
->get_ranges_map(cont
, ranges
);
1001 std::cout
<< num
<< ": seq_num " << context
->seq_num
<< " ranges " << ranges
<< std::endl
;
1004 waiting_on
= ranges
.size();
1005 ContentsGenerator::iterator gen_pos
= cont_gen
->get_iterator(cont
);
1006 // assure that tid is greater than last_acked_tid
1007 uint64_t tid
= last_acked_tid
+ 1;
1008 for (auto [offset
, len
] : ranges
) {
1009 gen_pos
.seek(offset
);
1010 bufferlist to_write
= gen_pos
.gen_bl_advance(len
);
1011 ceph_assert(to_write
.length() == len
);
1012 ceph_assert(to_write
.length() > 0);
1013 std::cout
<< num
<< ": writing " << context
->prefix
+oid
1014 << " from " << offset
1015 << " to " << offset
+ len
<< " tid " << tid
<< std::endl
;
1017 new pair
<TestOp
*, TestOp::CallbackInfo
*>(this,
1018 new TestOp::CallbackInfo(tid
++));
1019 librados::AioCompletion
*completion
=
1020 context
->rados
.aio_create_completion((void*) cb_arg
,
1022 waiting
.insert(completion
);
1023 librados::ObjectWriteOperation op
;
1024 /* no writesame multiplication factor for now */
1025 op
.writesame(offset
, to_write
.length(), to_write
);
1027 context
->io_ctx
.aio_operate(
1028 context
->prefix
+oid
, completion
,
1033 encode(cont
, contbl
);
1034 pair
<TestOp
*, TestOp::CallbackInfo
*> *cb_arg
=
1035 new pair
<TestOp
*, TestOp::CallbackInfo
*>(
1037 new TestOp::CallbackInfo(tid
++));
1038 librados::AioCompletion
*completion
= context
->rados
.aio_create_completion(
1039 (void*) cb_arg
, &write_callback
);
1040 waiting
.insert(completion
);
1042 write_op
.setxattr("_header", contbl
);
1043 write_op
.truncate(cont_gen
->get_length(cont
));
1044 context
->io_ctx
.aio_operate(
1045 context
->prefix
+oid
, completion
, &write_op
);
1048 new pair
<TestOp
*, TestOp::CallbackInfo
*>(
1050 new TestOp::CallbackInfo(tid
++));
1051 rcompletion
= context
->rados
.aio_create_completion(
1052 (void*) cb_arg
, &write_callback
);
1054 read_op
.read(0, 1, &rbuffer
, 0);
1055 context
->io_ctx
.aio_operate(
1056 context
->prefix
+oid
, rcompletion
,
1058 librados::OPERATION_ORDER_READS_WRITES
, // order wrt previous write/update
1062 void _finish(CallbackInfo
*info
) override
1065 std::lock_guard state_locker
{context
->state_lock
};
1066 uint64_t tid
= info
->id
;
1068 cout
<< num
<< ": finishing writesame tid " << tid
<< " to " << context
->prefix
+ oid
<< std::endl
;
1070 if (tid
<= last_acked_tid
) {
1071 cerr
<< "Error: finished tid " << tid
1072 << " when last_acked_tid was " << last_acked_tid
<< std::endl
;
1075 last_acked_tid
= tid
;
1079 if (waiting_on
== 0) {
1080 uint64_t version
= 0;
1081 for (set
<librados::AioCompletion
*>::iterator i
= waiting
.begin();
1084 ceph_assert((*i
)->is_complete());
1085 if (int err
= (*i
)->get_return_value()) {
1086 cerr
<< "Error: oid " << oid
<< " writesame returned error code "
1087 << err
<< std::endl
;
1089 if ((*i
)->get_version64() > version
)
1090 version
= (*i
)->get_version64();
1095 context
->update_object_version(oid
, version
);
1096 ceph_assert(rcompletion
->is_complete());
1097 ceph_assert(rcompletion
->get_return_value() == 1);
1098 if (rcompletion
->get_version64() != version
) {
1099 cerr
<< "Error: racing read on " << oid
<< " returned version "
1100 << rcompletion
->get_version64() << " rather than version "
1101 << version
<< std::endl
;
1102 ceph_abort_msg("racing read got wrong version");
1104 rcompletion
->release();
1107 ObjectDesc old_value
;
1108 ceph_assert(context
->find_object(oid
, &old_value
, -1));
1109 if (old_value
.deleted())
1110 std::cout
<< num
<< ": left oid " << oid
<< " deleted" << std::endl
;
1112 std::cout
<< num
<< ": left oid " << oid
<< " "
1113 << old_value
.most_recent() << std::endl
;
1116 context
->oid_in_use
.erase(oid
);
1117 context
->oid_not_in_use
.insert(oid
);
1123 bool finished() override
1128 string
getType() override
1130 return "WriteSameOp";
1134 class DeleteOp
: public TestOp
{
1139 RadosTestContext
*context
,
1141 TestOpStat
*stat
= 0)
1142 : TestOp(n
, context
, stat
), oid(oid
)
1145 void _begin() override
1147 std::unique_lock state_locker
{context
->state_lock
};
1148 if (context
->get_watch_context(oid
)) {
1153 ObjectDesc contents
;
1154 context
->find_object(oid
, &contents
);
1155 bool present
= !contents
.deleted();
1157 context
->oid_in_use
.insert(oid
);
1158 context
->oid_not_in_use
.erase(oid
);
1161 context
->remove_object(oid
);
1163 interval_set
<uint64_t> ranges
;
1164 state_locker
.unlock();
1168 librados::ObjectWriteOperation op
;
1171 r
= context
->io_ctx
.operate(context
->prefix
+oid
, &op
);
1173 r
= context
->io_ctx
.remove(context
->prefix
+oid
);
1175 if (r
&& !(r
== -ENOENT
&& !present
)) {
1176 cerr
<< "r is " << r
<< " while deleting " << oid
<< " and present is " << present
<< std::endl
;
1180 state_locker
.lock();
1181 context
->oid_in_use
.erase(oid
);
1182 context
->oid_not_in_use
.insert(oid
);
1186 string
getType() override
1192 class ReadOp
: public TestOp
{
1194 vector
<librados::AioCompletion
*> completions
;
1195 librados::ObjectReadOperation op
;
1197 ObjectDesc old_value
;
1200 bool localize_reads
;
1202 std::shared_ptr
<int> in_use
;
1204 vector
<bufferlist
> results
;
1205 vector
<int> retvals
;
1206 vector
<std::map
<uint64_t, uint64_t>> extent_results
;
1207 vector
<bool> is_sparse_read
;
1208 uint64_t waiting_on
;
1210 vector
<bufferlist
> checksums
;
1211 vector
<int> checksum_retvals
;
1213 map
<string
, bufferlist
> attrs
;
1216 set
<string
> omap_requested_keys
;
1217 map
<string
, bufferlist
> omap_returned_values
;
1218 set
<string
> omap_keys
;
1219 map
<string
, bufferlist
> omap
;
1222 map
<string
, bufferlist
> xattrs
;
1224 RadosTestContext
*context
,
1227 bool localize_reads
,
1228 TestOpStat
*stat
= 0)
1229 : TestOp(n
, context
, stat
),
1233 balance_reads(balance_reads
),
1234 localize_reads(localize_reads
),
1238 is_sparse_read(3, false),
1241 checksum_retvals(3),
1245 void _do_read(librados::ObjectReadOperation
& read_op
, int index
) {
1247 if (old_value
.has_contents())
1248 len
= old_value
.most_recent_gen()->get_length(old_value
.most_recent());
1249 if (context
->no_sparse
|| rand() % 2) {
1250 is_sparse_read
[index
] = false;
1255 bufferlist init_value_bl
;
1256 encode(static_cast<uint32_t>(-1), init_value_bl
);
1257 read_op
.checksum(LIBRADOS_CHECKSUM_TYPE_CRC32C
, init_value_bl
, 0, len
,
1258 0, &checksums
[index
], &checksum_retvals
[index
]);
1260 is_sparse_read
[index
] = true;
1261 read_op
.sparse_read(0,
1263 &extent_results
[index
],
1269 void _begin() override
1271 std::unique_lock state_locker
{context
->state_lock
};
1272 if (!(rand() % 4) && !context
->snaps
.empty()) {
1273 snap
= rand_choose(context
->snaps
)->first
;
1274 in_use
= context
->snaps_in_use
.lookup_or_create(snap
, snap
);
1278 std::cout
<< num
<< ": read oid " << oid
<< " snap " << snap
<< std::endl
;
1280 for (uint32_t i
= 0; i
< 3; i
++) {
1281 completions
[i
] = context
->rados
.aio_create_completion((void *) this, &read_callback
);
1284 context
->oid_in_use
.insert(oid
);
1285 context
->oid_not_in_use
.erase(oid
);
1286 ceph_assert(context
->find_object(oid
, &old_value
, snap
));
1287 if (old_value
.deleted())
1288 std::cout
<< num
<< ": expect deleted" << std::endl
;
1290 std::cout
<< num
<< ": expect " << old_value
.most_recent() << std::endl
;
1292 TestWatchContext
*ctx
= context
->get_watch_context(oid
);
1293 state_locker
.unlock();
1295 ceph_assert(old_value
.exists
);
1297 std::cerr
<< num
<< ": about to start" << std::endl
;
1299 std::cerr
<< num
<< ": started" << std::endl
;
1301 context
->io_ctx
.set_notify_timeout(600);
1302 int r
= context
->io_ctx
.notify2(context
->prefix
+oid
, bl
, 0, NULL
);
1304 std::cerr
<< "r is " << r
<< std::endl
;
1307 std::cerr
<< num
<< ": notified, waiting" << std::endl
;
1310 state_locker
.lock();
1312 context
->io_ctx
.snap_set_read(context
->snaps
[snap
]);
1315 for (map
<string
, ContDesc
>::iterator i
= old_value
.attrs
.begin();
1316 i
!= old_value
.attrs
.end();
1319 string key
= i
->first
;
1321 key
.push_back((rand() % 26) + 'a');
1322 omap_requested_keys
.insert(key
);
1325 if (!context
->no_omap
) {
1326 op
.omap_get_vals_by_keys(omap_requested_keys
, &omap_returned_values
, 0);
1327 // NOTE: we're ignore pmore here, which assumes the OSD limit is high
1329 op
.omap_get_keys2("", -1, &omap_keys
, nullptr, nullptr);
1330 op
.omap_get_vals2("", -1, &omap
, nullptr, nullptr);
1331 op
.omap_get_header(&header
, 0);
1333 op
.getxattrs(&xattrs
, 0);
1337 flags
|= librados::OPERATION_BALANCE_READS
;
1339 flags
|= librados::OPERATION_LOCALIZE_READS
;
1341 ceph_assert(!context
->io_ctx
.aio_operate(context
->prefix
+oid
, completions
[0], &op
,
1345 // send 2 pipelined reads on the same object/snap. This can help testing
1346 // OSD's read behavior in some scenarios
1347 for (uint32_t i
= 1; i
< 3; ++i
) {
1348 librados::ObjectReadOperation pipeline_op
;
1349 _do_read(pipeline_op
, i
);
1350 ceph_assert(!context
->io_ctx
.aio_operate(context
->prefix
+oid
, completions
[i
], &pipeline_op
, 0));
1355 context
->io_ctx
.snap_set_read(0);
1359 void _finish(CallbackInfo
*info
) override
1361 std::unique_lock state_locker
{context
->state_lock
};
1363 ceph_assert(waiting_on
> 0);
1368 context
->oid_in_use
.erase(oid
);
1369 context
->oid_not_in_use
.insert(oid
);
1370 int retval
= completions
[0]->get_return_value();
1371 for (vector
<librados::AioCompletion
*>::iterator it
= completions
.begin();
1372 it
!= completions
.end(); ++it
) {
1373 ceph_assert((*it
)->is_complete());
1374 uint64_t version
= (*it
)->get_version64();
1375 int err
= (*it
)->get_return_value();
1376 if (err
!= retval
) {
1377 cerr
<< num
<< ": Error: oid " << oid
<< " read returned different error codes: "
1378 << retval
<< " and " << err
<< std::endl
;
1382 if (!(err
== -ENOENT
&& old_value
.deleted())) {
1383 cerr
<< num
<< ": Error: oid " << oid
<< " read returned error code "
1384 << err
<< std::endl
;
1387 } else if (version
!= old_value
.version
) {
1388 cerr
<< num
<< ": oid " << oid
<< " version is " << version
1389 << " and expected " << old_value
.version
<< std::endl
;
1390 ceph_assert(version
== old_value
.version
);
1394 map
<string
, bufferlist
>::iterator iter
= xattrs
.find("_header");
1395 bufferlist headerbl
;
1396 if (iter
== xattrs
.end()) {
1397 if (old_value
.has_contents()) {
1398 cerr
<< num
<< ": Error: did not find header attr, has_contents: "
1399 << old_value
.has_contents()
1401 ceph_assert(!old_value
.has_contents());
1404 headerbl
= iter
->second
;
1407 if (old_value
.deleted()) {
1408 std::cout
<< num
<< ": expect deleted" << std::endl
;
1409 ceph_abort_msg("expected deleted");
1411 std::cout
<< num
<< ": expect " << old_value
.most_recent() << std::endl
;
1413 if (old_value
.has_contents()) {
1415 auto p
= headerbl
.cbegin();
1416 decode(to_check
, p
);
1417 if (to_check
!= old_value
.most_recent()) {
1418 cerr
<< num
<< ": oid " << oid
<< " found incorrect object contents " << to_check
1419 << ", expected " << old_value
.most_recent() << std::endl
;
1422 for (unsigned i
= 0; i
< results
.size(); i
++) {
1423 if (is_sparse_read
[i
]) {
1424 if (!old_value
.check_sparse(extent_results
[i
], results
[i
])) {
1425 cerr
<< num
<< ": oid " << oid
<< " contents " << to_check
<< " corrupt" << std::endl
;
1429 if (!old_value
.check(results
[i
])) {
1430 cerr
<< num
<< ": oid " << oid
<< " contents " << to_check
<< " corrupt" << std::endl
;
1434 uint32_t checksum
= 0;
1435 if (checksum_retvals
[i
] == 0) {
1437 auto bl_it
= checksums
[i
].cbegin();
1438 uint32_t csum_count
;
1439 decode(csum_count
, bl_it
);
1440 decode(checksum
, bl_it
);
1441 } catch (const buffer::error
&err
) {
1442 checksum_retvals
[i
] = -EBADMSG
;
1445 if (checksum_retvals
[i
] != 0 || checksum
!= results
[i
].crc32c(-1)) {
1446 cerr
<< num
<< ": oid " << oid
<< " checksum " << checksums
[i
]
1447 << " incorrect, expecting " << results
[i
].crc32c(-1)
1453 if (context
->errors
) ceph_abort();
1457 if (!context
->no_omap
) {
1458 if (!(old_value
.header
== header
)) {
1459 cerr
<< num
<< ": oid " << oid
<< " header does not match, old size: "
1460 << old_value
.header
.length() << " new size " << header
.length()
1462 ceph_assert(old_value
.header
== header
);
1464 if (omap
.size() != old_value
.attrs
.size()) {
1465 cerr
<< num
<< ": oid " << oid
<< " omap.size() is " << omap
.size()
1466 << " and old is " << old_value
.attrs
.size() << std::endl
;
1467 ceph_assert(omap
.size() == old_value
.attrs
.size());
1469 if (omap_keys
.size() != old_value
.attrs
.size()) {
1470 cerr
<< num
<< ": oid " << oid
<< " omap.size() is " << omap_keys
.size()
1471 << " and old is " << old_value
.attrs
.size() << std::endl
;
1472 ceph_assert(omap_keys
.size() == old_value
.attrs
.size());
1475 if (xattrs
.size() != old_value
.attrs
.size()) {
1476 cerr
<< num
<< ": oid " << oid
<< " xattrs.size() is " << xattrs
.size()
1477 << " and old is " << old_value
.attrs
.size() << std::endl
;
1478 ceph_assert(xattrs
.size() == old_value
.attrs
.size());
1480 for (map
<string
, ContDesc
>::iterator iter
= old_value
.attrs
.begin();
1481 iter
!= old_value
.attrs
.end();
1483 bufferlist bl
= context
->attr_gen
.gen_bl(
1485 if (!context
->no_omap
) {
1486 map
<string
, bufferlist
>::iterator omap_iter
= omap
.find(iter
->first
);
1487 ceph_assert(omap_iter
!= omap
.end());
1488 ceph_assert(bl
.length() == omap_iter
->second
.length());
1489 bufferlist::iterator k
= bl
.begin();
1490 for(bufferlist::iterator l
= omap_iter
->second
.begin();
1491 !k
.end() && !l
.end();
1493 ceph_assert(*l
== *k
);
1496 map
<string
, bufferlist
>::iterator xattr_iter
= xattrs
.find(iter
->first
);
1497 ceph_assert(xattr_iter
!= xattrs
.end());
1498 ceph_assert(bl
.length() == xattr_iter
->second
.length());
1499 bufferlist::iterator k
= bl
.begin();
1500 for (bufferlist::iterator j
= xattr_iter
->second
.begin();
1501 !k
.end() && !j
.end();
1503 ceph_assert(*j
== *k
);
1506 if (!context
->no_omap
) {
1507 for (set
<string
>::iterator i
= omap_requested_keys
.begin();
1508 i
!= omap_requested_keys
.end();
1510 if (!omap_returned_values
.count(*i
))
1511 ceph_assert(!old_value
.attrs
.count(*i
));
1512 if (!old_value
.attrs
.count(*i
))
1513 ceph_assert(!omap_returned_values
.count(*i
));
1515 for (map
<string
, bufferlist
>::iterator i
= omap_returned_values
.begin();
1516 i
!= omap_returned_values
.end();
1518 ceph_assert(omap_requested_keys
.count(i
->first
));
1519 ceph_assert(omap
.count(i
->first
));
1520 ceph_assert(old_value
.attrs
.count(i
->first
));
1521 ceph_assert(i
->second
== omap
[i
->first
]);
1525 for (vector
<librados::AioCompletion
*>::iterator it
= completions
.begin();
1526 it
!= completions
.end(); ++it
) {
1533 bool finished() override
1538 string
getType() override
1544 class SnapCreateOp
: public TestOp
{
1547 RadosTestContext
*context
,
1548 TestOpStat
*stat
= 0)
1549 : TestOp(n
, context
, stat
)
1552 void _begin() override
1557 if (context
->pool_snaps
) {
1560 ss
<< context
->prefix
<< "snap" << ++context
->snapname_num
;
1561 snapname
= ss
.str();
1563 int ret
= context
->io_ctx
.snap_create(snapname
.c_str());
1565 cerr
<< "snap_create returned " << ret
<< std::endl
;
1568 ceph_assert(!context
->io_ctx
.snap_lookup(snapname
.c_str(), &snap
));
1571 ceph_assert(!context
->io_ctx
.selfmanaged_snap_create(&snap
));
1574 std::unique_lock state_locker
{context
->state_lock
};
1575 context
->add_snap(snap
);
1577 if (!context
->pool_snaps
) {
1578 vector
<uint64_t> snapset(context
->snaps
.size());
1581 for (map
<int,uint64_t>::reverse_iterator i
= context
->snaps
.rbegin();
1582 i
!= context
->snaps
.rend();
1584 snapset
[j
] = i
->second
;
1587 state_locker
.unlock();
1589 int r
= context
->io_ctx
.selfmanaged_snap_set_write_ctx(context
->seq
, snapset
);
1591 cerr
<< "r is " << r
<< " snapset is " << snapset
<< " seq is " << context
->seq
<< std::endl
;
1597 string
getType() override
1599 return "SnapCreateOp";
1601 bool must_quiesce_other_ops() override
{ return context
->pool_snaps
; }
1604 class SnapRemoveOp
: public TestOp
{
1607 SnapRemoveOp(int n
, RadosTestContext
*context
,
1609 TestOpStat
*stat
= 0)
1610 : TestOp(n
, context
, stat
),
1614 void _begin() override
1616 std::unique_lock state_locker
{context
->state_lock
};
1617 uint64_t snap
= context
->snaps
[to_remove
];
1618 context
->remove_snap(to_remove
);
1620 if (context
->pool_snaps
) {
1623 ceph_assert(!context
->io_ctx
.snap_get_name(snap
, &snapname
));
1624 ceph_assert(!context
->io_ctx
.snap_remove(snapname
.c_str()));
1626 ceph_assert(!context
->io_ctx
.selfmanaged_snap_remove(snap
));
1628 vector
<uint64_t> snapset(context
->snaps
.size());
1630 for (map
<int,uint64_t>::reverse_iterator i
= context
->snaps
.rbegin();
1631 i
!= context
->snaps
.rend();
1633 snapset
[j
] = i
->second
;
1636 int r
= context
->io_ctx
.selfmanaged_snap_set_write_ctx(context
->seq
, snapset
);
1638 cerr
<< "r is " << r
<< " snapset is " << snapset
<< " seq is " << context
->seq
<< std::endl
;
1644 string
getType() override
1646 return "SnapRemoveOp";
1650 class WatchOp
: public TestOp
{
1654 RadosTestContext
*context
,
1656 TestOpStat
*stat
= 0)
1657 : TestOp(n
, context
, stat
),
1661 void _begin() override
1663 std::unique_lock state_locker
{context
->state_lock
};
1664 ObjectDesc contents
;
1665 context
->find_object(oid
, &contents
);
1666 if (contents
.deleted()) {
1670 context
->oid_in_use
.insert(oid
);
1671 context
->oid_not_in_use
.erase(oid
);
1673 TestWatchContext
*ctx
= context
->get_watch_context(oid
);
1674 state_locker
.unlock();
1678 std::lock_guard l
{context
->state_lock
};
1679 ctx
= context
->watch(oid
);
1682 r
= context
->io_ctx
.watch2(context
->prefix
+oid
,
1686 r
= context
->io_ctx
.unwatch2(ctx
->get_handle());
1688 std::lock_guard l
{context
->state_lock
};
1689 context
->unwatch(oid
);
1694 cerr
<< "r is " << r
<< std::endl
;
1699 std::lock_guard l
{context
->state_lock
};
1700 context
->oid_in_use
.erase(oid
);
1701 context
->oid_not_in_use
.insert(oid
);
1705 string
getType() override
1711 class RollbackOp
: public TestOp
{
1715 librados::ObjectWriteOperation zero_write_op1
;
1716 librados::ObjectWriteOperation zero_write_op2
;
1717 librados::ObjectWriteOperation op
;
1718 vector
<librados::AioCompletion
*> comps
;
1719 std::shared_ptr
<int> in_use
;
1724 RadosTestContext
*context
,
1726 TestOpStat
*stat
= 0)
1727 : TestOp(n
, context
, stat
),
1728 oid(_oid
), roll_back_to(-1),
1730 last_finished(-1), outstanding(3)
1733 void _begin() override
1735 context
->state_lock
.lock();
1736 if (context
->get_watch_context(oid
)) {
1738 context
->state_lock
.unlock();
1742 if (context
->snaps
.empty()) {
1744 context
->state_lock
.unlock();
1749 context
->oid_in_use
.insert(oid
);
1750 context
->oid_not_in_use
.erase(oid
);
1752 roll_back_to
= rand_choose(context
->snaps
)->first
;
1753 in_use
= context
->snaps_in_use
.lookup_or_create(
1758 cout
<< "rollback oid " << oid
<< " to " << roll_back_to
<< std::endl
;
1760 bool existed_before
= context
->object_existed_at(oid
);
1761 bool existed_after
= context
->object_existed_at(oid
, roll_back_to
);
1763 context
->roll_back(oid
, roll_back_to
);
1764 uint64_t snap
= context
->snaps
[roll_back_to
];
1766 outstanding
-= (!existed_before
) + (!existed_after
);
1768 context
->state_lock
.unlock();
1771 zero_write_op1
.append(bl
);
1772 zero_write_op2
.append(bl2
);
1774 if (context
->pool_snaps
) {
1775 op
.snap_rollback(snap
);
1777 op
.selfmanaged_snap_rollback(snap
);
1780 if (existed_before
) {
1781 pair
<TestOp
*, TestOp::CallbackInfo
*> *cb_arg
=
1782 new pair
<TestOp
*, TestOp::CallbackInfo
*>(this,
1783 new TestOp::CallbackInfo(0));
1785 context
->rados
.aio_create_completion((void*) cb_arg
,
1787 context
->io_ctx
.aio_operate(
1788 context
->prefix
+oid
, comps
[0], &zero_write_op1
);
1791 pair
<TestOp
*, TestOp::CallbackInfo
*> *cb_arg
=
1792 new pair
<TestOp
*, TestOp::CallbackInfo
*>(this,
1793 new TestOp::CallbackInfo(1));
1795 context
->rados
.aio_create_completion((void*) cb_arg
,
1797 context
->io_ctx
.aio_operate(
1798 context
->prefix
+oid
, comps
[1], &op
);
1800 if (existed_after
) {
1801 pair
<TestOp
*, TestOp::CallbackInfo
*> *cb_arg
=
1802 new pair
<TestOp
*, TestOp::CallbackInfo
*>(this,
1803 new TestOp::CallbackInfo(2));
1805 context
->rados
.aio_create_completion((void*) cb_arg
,
1807 context
->io_ctx
.aio_operate(
1808 context
->prefix
+oid
, comps
[2], &zero_write_op2
);
1812 void _finish(CallbackInfo
*info
) override
1814 std::lock_guard l
{context
->state_lock
};
1815 uint64_t tid
= info
->id
;
1816 cout
<< num
<< ": finishing rollback tid " << tid
1817 << " to " << context
->prefix
+ oid
<< std::endl
;
1818 ceph_assert((int)(info
->id
) > last_finished
);
1819 last_finished
= info
->id
;
1822 if ((r
= comps
[last_finished
]->get_return_value()) != 0) {
1823 cerr
<< "err " << r
<< std::endl
;
1826 if (--outstanding
== 0) {
1828 context
->update_object_version(oid
, comps
[tid
]->get_version64());
1829 context
->oid_in_use
.erase(oid
);
1830 context
->oid_not_in_use
.insert(oid
);
1831 in_use
= std::shared_ptr
<int>();
1836 bool finished() override
1841 string
getType() override
1843 return "RollBackOp";
1847 class CopyFromOp
: public TestOp
{
1849 string oid
, oid_src
;
1850 ObjectDesc src_value
;
1851 librados::ObjectWriteOperation op
;
1852 librados::ObjectReadOperation rd_op
;
1853 librados::AioCompletion
*comp
;
1854 librados::AioCompletion
*comp_racing_read
= nullptr;
1855 std::shared_ptr
<int> in_use
;
1861 RadosTestContext
*context
,
1863 const string
&oid_src
,
1865 : TestOp(n
, context
, stat
),
1866 oid(oid
), oid_src(oid_src
),
1867 comp(NULL
), snap(-1), done(0),
1871 void _begin() override
1875 std::lock_guard l
{context
->state_lock
};
1876 cont
= ContDesc(context
->seq_num
, context
->current_snap
,
1877 context
->seq_num
, "");
1878 context
->oid_in_use
.insert(oid
);
1879 context
->oid_not_in_use
.erase(oid
);
1880 context
->oid_in_use
.insert(oid_src
);
1881 context
->oid_not_in_use
.erase(oid_src
);
1883 // choose source snap
1884 if (0 && !(rand() % 4) && !context
->snaps
.empty()) {
1885 snap
= rand_choose(context
->snaps
)->first
;
1886 in_use
= context
->snaps_in_use
.lookup_or_create(snap
, snap
);
1890 context
->find_object(oid_src
, &src_value
, snap
);
1891 if (!src_value
.deleted())
1892 context
->update_object_full(oid
, src_value
);
1895 string src
= context
->prefix
+oid_src
;
1896 op
.copy_from(src
.c_str(), context
->io_ctx
, src_value
.version
, 0);
1898 pair
<TestOp
*, TestOp::CallbackInfo
*> *cb_arg
=
1899 new pair
<TestOp
*, TestOp::CallbackInfo
*>(this,
1900 new TestOp::CallbackInfo(0));
1901 comp
= context
->rados
.aio_create_completion((void*) cb_arg
,
1903 context
->io_ctx
.aio_operate(context
->prefix
+oid
, comp
, &op
);
1905 // queue up a racing read, too.
1906 pair
<TestOp
*, TestOp::CallbackInfo
*> *read_cb_arg
=
1907 new pair
<TestOp
*, TestOp::CallbackInfo
*>(this,
1908 new TestOp::CallbackInfo(1));
1909 comp_racing_read
= context
->rados
.aio_create_completion((void*) read_cb_arg
, &write_callback
);
1910 rd_op
.stat(NULL
, NULL
, NULL
);
1911 context
->io_ctx
.aio_operate(context
->prefix
+oid
, comp_racing_read
, &rd_op
,
1912 librados::OPERATION_ORDER_READS_WRITES
, // order wrt previous write/update
1917 void _finish(CallbackInfo
*info
) override
1919 std::lock_guard l
{context
->state_lock
};
1921 // note that the read can (and atm will) come back before the
1922 // write reply, but will reflect the update and the versions will
1925 if (info
->id
== 0) {
1927 ceph_assert(comp
->is_complete());
1928 cout
<< num
<< ": finishing copy_from to " << context
->prefix
+ oid
<< std::endl
;
1929 if ((r
= comp
->get_return_value())) {
1930 if (r
== -ENOENT
&& src_value
.deleted()) {
1931 cout
<< num
<< ": got expected ENOENT (src dne)" << std::endl
;
1933 cerr
<< "Error: oid " << oid
<< " copy_from " << oid_src
<< " returned error code "
1938 ceph_assert(!version
|| comp
->get_version64() == version
);
1939 version
= comp
->get_version64();
1940 context
->update_object_version(oid
, comp
->get_version64());
1942 } else if (info
->id
== 1) {
1944 ceph_assert(comp_racing_read
->is_complete());
1945 cout
<< num
<< ": finishing copy_from racing read to " << context
->prefix
+ oid
<< std::endl
;
1946 if ((r
= comp_racing_read
->get_return_value())) {
1947 if (!(r
== -ENOENT
&& src_value
.deleted())) {
1948 cerr
<< "Error: oid " << oid
<< " copy_from " << oid_src
<< " returned error code "
1952 ceph_assert(comp_racing_read
->get_return_value() == 0);
1953 ceph_assert(!version
|| comp_racing_read
->get_version64() == version
);
1954 version
= comp_racing_read
->get_version64();
1958 context
->oid_in_use
.erase(oid
);
1959 context
->oid_not_in_use
.insert(oid
);
1960 context
->oid_in_use
.erase(oid_src
);
1961 context
->oid_not_in_use
.insert(oid_src
);
1966 bool finished() override
1971 string
getType() override
1973 return "CopyFromOp";
1977 class ChunkReadOp
: public TestOp
{
1979 vector
<librados::AioCompletion
*> completions
;
1980 librados::ObjectReadOperation op
;
1982 ObjectDesc old_value
;
1983 ObjectDesc tgt_value
;
1986 bool localize_reads
;
1988 std::shared_ptr
<int> in_use
;
1990 vector
<bufferlist
> results
;
1991 vector
<int> retvals
;
1992 vector
<bool> is_sparse_read
;
1993 uint64_t waiting_on
;
1995 vector
<bufferlist
> checksums
;
1996 vector
<int> checksum_retvals
;
1997 uint32_t offset
= 0;
1998 uint32_t length
= 0;
2000 string tgt_pool_name
;
2001 uint32_t tgt_offset
= 0;
2004 RadosTestContext
*context
,
2006 const string
&tgt_pool_name
,
2008 bool localize_reads
,
2009 TestOpStat
*stat
= 0)
2010 : TestOp(n
, context
, stat
),
2014 balance_reads(balance_reads
),
2015 localize_reads(localize_reads
),
2020 checksum_retvals(2),
2021 tgt_pool_name(tgt_pool_name
)
2024 void _do_read(librados::ObjectReadOperation
& read_op
, uint32_t offset
, uint32_t length
, int index
) {
2025 read_op
.read(offset
,
2030 bufferlist init_value_bl
;
2031 encode(static_cast<uint32_t>(-1), init_value_bl
);
2032 read_op
.checksum(LIBRADOS_CHECKSUM_TYPE_CRC32C
, init_value_bl
, offset
, length
,
2033 0, &checksums
[index
], &checksum_retvals
[index
]);
2038 void _begin() override
2040 context
->state_lock
.lock();
2041 std::cout
<< num
<< ": chunk read oid " << oid
<< " snap " << snap
<< std::endl
;
2043 for (uint32_t i
= 0; i
< 2; i
++) {
2044 completions
[i
] = context
->rados
.aio_create_completion((void *) this, &read_callback
);
2047 context
->find_object(oid
, &old_value
);
2049 if (old_value
.chunk_info
.size() == 0) {
2050 std::cout
<< ": no chunks" << std::endl
;
2052 context
->state_lock
.unlock();
2057 context
->oid_in_use
.insert(oid
);
2058 context
->oid_not_in_use
.erase(oid
);
2059 if (old_value
.deleted()) {
2060 std::cout
<< num
<< ": expect deleted" << std::endl
;
2062 std::cout
<< num
<< ": expect " << old_value
.most_recent() << std::endl
;
2065 int rand_index
= rand() % old_value
.chunk_info
.size();
2066 auto iter
= old_value
.chunk_info
.begin();
2067 for (int i
= 0; i
< rand_index
; i
++) {
2070 offset
= iter
->first
;
2071 offset
+= (rand() % iter
->second
.length
)/2;
2072 uint32_t t_length
= rand() % iter
->second
.length
;
2073 while (t_length
+ offset
> iter
->first
+ iter
->second
.length
) {
2074 t_length
= rand() % iter
->second
.length
;
2077 tgt_offset
= iter
->second
.offset
+ offset
- iter
->first
;
2078 tgt_oid
= iter
->second
.oid
;
2080 std::cout
<< num
<< ": ori offset " << iter
->first
<< " req offset " << offset
2081 << " ori length " << iter
->second
.length
<< " req length " << length
2082 << " ori tgt_offset " << iter
->second
.offset
<< " req tgt_offset " << tgt_offset
2083 << " tgt_oid " << tgt_oid
<< std::endl
;
2085 TestWatchContext
*ctx
= context
->get_watch_context(oid
);
2086 context
->state_lock
.unlock();
2088 ceph_assert(old_value
.exists
);
2090 std::cerr
<< num
<< ": about to start" << std::endl
;
2092 std::cerr
<< num
<< ": started" << std::endl
;
2094 context
->io_ctx
.set_notify_timeout(600);
2095 int r
= context
->io_ctx
.notify2(context
->prefix
+oid
, bl
, 0, NULL
);
2097 std::cerr
<< "r is " << r
<< std::endl
;
2100 std::cerr
<< num
<< ": notified, waiting" << std::endl
;
2103 std::lock_guard state_locker
{context
->state_lock
};
2105 _do_read(op
, offset
, length
, 0);
2109 flags
|= librados::OPERATION_BALANCE_READS
;
2111 flags
|= librados::OPERATION_LOCALIZE_READS
;
2113 ceph_assert(!context
->io_ctx
.aio_operate(context
->prefix
+oid
, completions
[0], &op
,
2117 _do_read(op
, tgt_offset
, length
, 1);
2118 ceph_assert(!context
->io_ctx
.aio_operate(context
->prefix
+tgt_oid
, completions
[1], &op
,
2124 void _finish(CallbackInfo
*info
) override
2126 std::lock_guard l
{context
->state_lock
};
2128 ceph_assert(waiting_on
> 0);
2133 context
->oid_in_use
.erase(oid
);
2134 context
->oid_not_in_use
.insert(oid
);
2135 int retval
= completions
[0]->get_return_value();
2136 std::cout
<< ": finish!! ret: " << retval
<< std::endl
;
2137 context
->find_object(tgt_oid
, &tgt_value
);
2139 for (int i
= 0; i
< 2; i
++) {
2140 ceph_assert(completions
[i
]->is_complete());
2141 int err
= completions
[i
]->get_return_value();
2142 if (err
!= retval
) {
2143 cerr
<< num
<< ": Error: oid " << oid
<< " read returned different error codes: "
2144 << retval
<< " and " << err
<< std::endl
;
2148 if (!(err
== -ENOENT
&& old_value
.deleted())) {
2149 cerr
<< num
<< ": Error: oid " << oid
<< " read returned error code "
2150 << err
<< std::endl
;
2157 if (old_value
.deleted()) {
2158 std::cout
<< num
<< ": expect deleted" << std::endl
;
2159 ceph_abort_msg("expected deleted");
2161 std::cout
<< num
<< ": expect " << old_value
.most_recent() << std::endl
;
2163 if (tgt_value
.has_contents()) {
2164 uint32_t checksum
[2] = {0};
2165 if (checksum_retvals
[1] == 0) {
2167 auto bl_it
= checksums
[1].cbegin();
2168 uint32_t csum_count
;
2169 decode(csum_count
, bl_it
);
2170 decode(checksum
[1], bl_it
);
2171 } catch (const buffer::error
&err
) {
2172 checksum_retvals
[1] = -EBADMSG
;
2176 if (checksum_retvals
[1] != 0) {
2177 cerr
<< num
<< ": oid " << oid
<< " checksum retvals " << checksums
[0]
2178 << " error " << std::endl
;
2182 checksum
[0] = results
[0].crc32c(-1);
2184 if (checksum
[0] != checksum
[1]) {
2185 cerr
<< num
<< ": oid " << oid
<< " checksum src " << checksum
[0]
2186 << " chunksum tgt " << checksum
[1] << " incorrect, expecting "
2187 << results
[0].crc32c(-1)
2191 if (context
->errors
) ceph_abort();
2194 for (vector
<librados::AioCompletion
*>::iterator it
= completions
.begin();
2195 it
!= completions
.end(); ++it
) {
2202 bool finished() override
2207 string
getType() override
2209 return "ChunkReadOp";
2213 class CopyOp
: public TestOp
{
2215 string oid
, oid_src
, tgt_pool_name
;
2216 librados::ObjectWriteOperation op
;
2217 librados::ObjectReadOperation rd_op
;
2218 librados::AioCompletion
*comp
;
2219 ObjectDesc src_value
, tgt_value
;
2223 RadosTestContext
*context
,
2224 const string
&oid_src
,
2226 const string
&tgt_pool_name
,
2227 TestOpStat
*stat
= 0)
2228 : TestOp(n
, context
, stat
),
2229 oid(oid
), oid_src(oid_src
), tgt_pool_name(tgt_pool_name
),
2230 comp(NULL
), done(0), r(0)
2233 void _begin() override
2235 std::lock_guard l
{context
->state_lock
};
2236 context
->oid_in_use
.insert(oid_src
);
2237 context
->oid_not_in_use
.erase(oid_src
);
2239 string src
= context
->prefix
+oid_src
;
2240 context
->find_object(oid_src
, &src_value
);
2241 op
.copy_from(src
.c_str(), context
->io_ctx
, src_value
.version
, 0);
2243 cout
<< "copy op oid " << oid_src
<< " to " << oid
<< " tgt_pool_name " << tgt_pool_name
<< std::endl
;
2245 pair
<TestOp
*, TestOp::CallbackInfo
*> *cb_arg
=
2246 new pair
<TestOp
*, TestOp::CallbackInfo
*>(this,
2247 new TestOp::CallbackInfo(0));
2248 comp
= context
->rados
.aio_create_completion((void*) cb_arg
, &write_callback
);
2249 if (tgt_pool_name
== context
->low_tier_pool_name
) {
2250 context
->low_tier_io_ctx
.aio_operate(context
->prefix
+oid
, comp
, &op
);
2252 context
->io_ctx
.aio_operate(context
->prefix
+oid
, comp
, &op
);
2256 void _finish(CallbackInfo
*info
) override
2258 std::lock_guard l
{context
->state_lock
};
2260 if (info
->id
== 0) {
2261 ceph_assert(comp
->is_complete());
2262 cout
<< num
<< ": finishing copy op to oid " << oid
<< std::endl
;
2263 if ((r
= comp
->get_return_value())) {
2264 cerr
<< "Error: oid " << oid
<< " write returned error code "
2271 context
->oid_in_use
.erase(oid_src
);
2272 context
->oid_not_in_use
.insert(oid_src
);
2277 bool finished() override
2282 string
getType() override
2288 class SetChunkOp
: public TestOp
{
2290 string oid
, oid_tgt
, tgt_pool_name
;
2291 ObjectDesc src_value
, tgt_value
;
2292 librados::ObjectReadOperation op
;
2293 librados::ObjectReadOperation rd_op
;
2294 librados::AioCompletion
*comp
;
2295 std::shared_ptr
<int> in_use
;
2300 uint64_t tgt_offset
;
2302 RadosTestContext
*context
,
2306 const string
&oid_tgt
,
2307 const string
&tgt_pool_name
,
2308 uint64_t tgt_offset
,
2309 TestOpStat
*stat
= 0)
2310 : TestOp(n
, context
, stat
),
2311 oid(oid
), oid_tgt(oid_tgt
), tgt_pool_name(tgt_pool_name
),
2312 comp(NULL
), done(0),
2313 r(0), offset(offset
), length(length
),
2314 tgt_offset(tgt_offset
)
2317 void _begin() override
2319 std::lock_guard l
{context
->state_lock
};
2320 context
->oid_in_use
.insert(oid
);
2321 context
->oid_not_in_use
.erase(oid
);
2323 if (tgt_pool_name
.empty()) ceph_abort();
2325 context
->find_object(oid
, &src_value
);
2326 context
->find_object(oid_tgt
, &tgt_value
);
2328 if (src_value
.version
!= 0 && !src_value
.deleted())
2329 op
.assert_version(src_value
.version
);
2330 op
.set_chunk(offset
, length
, context
->low_tier_io_ctx
,
2331 context
->prefix
+oid_tgt
, tgt_offset
, CEPH_OSD_OP_FLAG_WITH_REFERENCE
);
2333 pair
<TestOp
*, TestOp::CallbackInfo
*> *cb_arg
=
2334 new pair
<TestOp
*, TestOp::CallbackInfo
*>(this,
2335 new TestOp::CallbackInfo(0));
2336 comp
= context
->rados
.aio_create_completion((void*) cb_arg
,
2338 context
->io_ctx
.aio_operate(context
->prefix
+oid
, comp
, &op
,
2339 librados::OPERATION_ORDER_READS_WRITES
, NULL
);
2342 void _finish(CallbackInfo
*info
) override
2344 std::lock_guard l
{context
->state_lock
};
2346 if (info
->id
== 0) {
2347 ceph_assert(comp
->is_complete());
2348 cout
<< num
<< ": finishing set_chunk to oid " << oid
<< std::endl
;
2349 if ((r
= comp
->get_return_value())) {
2350 if (r
== -ENOENT
&& src_value
.deleted()) {
2351 cout
<< num
<< ": got expected ENOENT (src dne)" << std::endl
;
2352 } else if (r
== -EOPNOTSUPP
) {
2353 bool is_overlapped
= false;
2354 for (auto &p
: src_value
.chunk_info
) {
2355 if ((p
.first
<= offset
&& p
.first
+ p
.second
.length
> offset
) ||
2356 (p
.first
> offset
&& p
.first
<= offset
+ length
)) {
2357 cout
<< " range is overlapped offset: " << offset
<< " length: " << length
2358 << " chunk_info offset: " << p
.second
.offset
<< " length "
2359 << p
.second
.length
<< std::endl
;
2360 is_overlapped
= true;
2361 context
->update_object_version(oid
, comp
->get_version64());
2364 if (!is_overlapped
) {
2365 cerr
<< "Error: oid " << oid
<< " set_chunk " << oid_tgt
<< " returned error code "
2366 << r
<< " offset: " << offset
<< " length: " << length
<< std::endl
;
2370 cerr
<< "Error: oid " << oid
<< " set_chunk " << oid_tgt
<< " returned error code "
2376 info
.offset
= tgt_offset
;
2377 info
.length
= length
;
2379 context
->update_object_chunk_target(oid
, offset
, info
);
2380 context
->update_object_version(oid
, comp
->get_version64());
2385 context
->oid_in_use
.erase(oid
);
2386 context
->oid_not_in_use
.insert(oid
);
2391 bool finished() override
2396 string
getType() override
2398 return "SetChunkOp";
2402 class SetRedirectOp
: public TestOp
{
2404 string oid
, oid_tgt
, tgt_pool_name
;
2405 ObjectDesc src_value
, tgt_value
;
2406 librados::ObjectWriteOperation op
;
2407 librados::ObjectReadOperation rd_op
;
2408 librados::AioCompletion
*comp
;
2409 std::shared_ptr
<int> in_use
;
2412 SetRedirectOp(int n
,
2413 RadosTestContext
*context
,
2415 const string
&oid_tgt
,
2416 const string
&tgt_pool_name
,
2417 TestOpStat
*stat
= 0)
2418 : TestOp(n
, context
, stat
),
2419 oid(oid
), oid_tgt(oid_tgt
), tgt_pool_name(tgt_pool_name
),
2420 comp(NULL
), done(0),
2424 void _begin() override
2426 std::lock_guard l
{context
->state_lock
};
2427 context
->oid_in_use
.insert(oid
);
2428 context
->oid_not_in_use
.erase(oid
);
2429 context
->oid_redirect_in_use
.insert(oid_tgt
);
2430 context
->oid_redirect_not_in_use
.erase(oid_tgt
);
2432 if (tgt_pool_name
.empty()) ceph_abort();
2434 context
->find_object(oid
, &src_value
);
2435 if(!context
->redirect_objs
[oid
].empty()) {
2436 /* copy_from oid --> oid_tgt */
2437 comp
= context
->rados
.aio_create_completion();
2438 string src
= context
->prefix
+oid
;
2439 op
.copy_from(src
.c_str(), context
->io_ctx
, src_value
.version
, 0);
2440 context
->low_tier_io_ctx
.aio_operate(context
->prefix
+oid_tgt
, comp
, &op
,
2441 librados::OPERATION_ORDER_READS_WRITES
);
2442 comp
->wait_for_complete();
2443 if ((r
= comp
->get_return_value())) {
2444 cerr
<< "Error: oid " << oid
<< " copy_from " << oid_tgt
<< " returned error code "
2450 /* unset redirect target */
2451 comp
= context
->rados
.aio_create_completion();
2452 bool present
= !src_value
.deleted();
2453 op
.unset_manifest();
2454 context
->io_ctx
.aio_operate(context
->prefix
+oid
, comp
, &op
,
2455 librados::OPERATION_ORDER_READS_WRITES
|
2456 librados::OPERATION_IGNORE_REDIRECT
);
2457 comp
->wait_for_complete();
2458 if ((r
= comp
->get_return_value())) {
2459 if (!(r
== -ENOENT
&& !present
) && r
!= -EOPNOTSUPP
) {
2460 cerr
<< "r is " << r
<< " while deleting " << oid
<< " and present is " << present
<< std::endl
;
2466 context
->oid_redirect_not_in_use
.insert(context
->redirect_objs
[oid
]);
2467 context
->oid_redirect_in_use
.erase(context
->redirect_objs
[oid
]);
2470 comp
= context
->rados
.aio_create_completion();
2471 rd_op
.stat(NULL
, NULL
, NULL
);
2472 context
->io_ctx
.aio_operate(context
->prefix
+oid
, comp
, &rd_op
,
2473 librados::OPERATION_ORDER_READS_WRITES
|
2474 librados::OPERATION_IGNORE_REDIRECT
,
2476 comp
->wait_for_complete();
2477 if ((r
= comp
->get_return_value()) && !src_value
.deleted()) {
2478 cerr
<< "Error: oid " << oid
<< " stat returned error code "
2482 context
->update_object_version(oid
, comp
->get_version64());
2485 comp
= context
->rados
.aio_create_completion();
2486 rd_op
.stat(NULL
, NULL
, NULL
);
2487 context
->low_tier_io_ctx
.aio_operate(context
->prefix
+oid_tgt
, comp
, &rd_op
,
2488 librados::OPERATION_ORDER_READS_WRITES
|
2489 librados::OPERATION_IGNORE_REDIRECT
,
2491 comp
->wait_for_complete();
2492 if ((r
= comp
->get_return_value())) {
2493 cerr
<< "Error: oid " << oid_tgt
<< " stat returned error code "
2497 uint64_t tgt_version
= comp
->get_version64();
2501 context
->find_object(oid
, &src_value
);
2503 if (src_value
.version
!= 0 && !src_value
.deleted())
2504 op
.assert_version(src_value
.version
);
2505 op
.set_redirect(context
->prefix
+oid_tgt
, context
->low_tier_io_ctx
, tgt_version
);
2507 pair
<TestOp
*, TestOp::CallbackInfo
*> *cb_arg
=
2508 new pair
<TestOp
*, TestOp::CallbackInfo
*>(this,
2509 new TestOp::CallbackInfo(0));
2510 comp
= context
->rados
.aio_create_completion((void*) cb_arg
, &write_callback
);
2511 context
->io_ctx
.aio_operate(context
->prefix
+oid
, comp
, &op
,
2512 librados::OPERATION_ORDER_READS_WRITES
);
2515 void _finish(CallbackInfo
*info
) override
2517 std::lock_guard l
{context
->state_lock
};
2519 if (info
->id
== 0) {
2520 ceph_assert(comp
->is_complete());
2521 cout
<< num
<< ": finishing set_redirect to oid " << oid
<< std::endl
;
2522 if ((r
= comp
->get_return_value())) {
2523 if (r
== -ENOENT
&& src_value
.deleted()) {
2524 cout
<< num
<< ": got expected ENOENT (src dne)" << std::endl
;
2526 cerr
<< "Error: oid " << oid
<< " set_redirect " << oid_tgt
<< " returned error code "
2531 context
->update_object_redirect_target(oid
, oid_tgt
);
2532 context
->update_object_version(oid
, comp
->get_version64());
2537 context
->oid_in_use
.erase(oid
);
2538 context
->oid_not_in_use
.insert(oid
);
2543 bool finished() override
2548 string
getType() override
2550 return "SetRedirectOp";
2554 class UnsetRedirectOp
: public TestOp
{
2557 librados::ObjectWriteOperation op
;
2558 librados::AioCompletion
*comp
= nullptr;
2560 UnsetRedirectOp(int n
,
2561 RadosTestContext
*context
,
2563 TestOpStat
*stat
= 0)
2564 : TestOp(n
, context
, stat
), oid(oid
)
2567 void _begin() override
2569 std::unique_lock state_locker
{context
->state_lock
};
2570 if (context
->get_watch_context(oid
)) {
2575 ObjectDesc contents
;
2576 context
->find_object(oid
, &contents
);
2577 bool present
= !contents
.deleted();
2579 context
->oid_in_use
.insert(oid
);
2580 context
->oid_not_in_use
.erase(oid
);
2583 context
->remove_object(oid
);
2585 state_locker
.unlock();
2587 comp
= context
->rados
.aio_create_completion();
2589 context
->io_ctx
.aio_operate(context
->prefix
+oid
, comp
, &op
,
2590 librados::OPERATION_ORDER_READS_WRITES
|
2591 librados::OPERATION_IGNORE_REDIRECT
);
2592 comp
->wait_for_complete();
2593 int r
= comp
->get_return_value();
2594 if (r
&& !(r
== -ENOENT
&& !present
)) {
2595 cerr
<< "r is " << r
<< " while deleting " << oid
<< " and present is " << present
<< std::endl
;
2598 state_locker
.lock();
2599 context
->oid_in_use
.erase(oid
);
2600 context
->oid_not_in_use
.insert(oid
);
2601 if(!context
->redirect_objs
[oid
].empty()) {
2602 context
->oid_redirect_not_in_use
.insert(context
->redirect_objs
[oid
]);
2603 context
->oid_redirect_in_use
.erase(context
->redirect_objs
[oid
]);
2604 context
->update_object_redirect_target(oid
, string());
2609 string
getType() override
2611 return "UnsetRedirectOp";
2615 class TierPromoteOp
: public TestOp
{
2617 librados::AioCompletion
*completion
;
2618 librados::ObjectWriteOperation op
;
2620 std::shared_ptr
<int> in_use
;
2622 TierPromoteOp(int n
,
2623 RadosTestContext
*context
,
2626 : TestOp(n
, context
, stat
),
2631 void _begin() override
2633 context
->state_lock
.lock();
2635 context
->oid_in_use
.insert(oid
);
2636 context
->oid_not_in_use
.erase(oid
);
2638 pair
<TestOp
*, TestOp::CallbackInfo
*> *cb_arg
=
2639 new pair
<TestOp
*, TestOp::CallbackInfo
*>(this,
2640 new TestOp::CallbackInfo(0));
2641 completion
= context
->rados
.aio_create_completion((void *) cb_arg
,
2643 context
->state_lock
.unlock();
2646 int r
= context
->io_ctx
.aio_operate(context
->prefix
+oid
, completion
,
2651 void _finish(CallbackInfo
*info
) override
2653 std::lock_guard l
{context
->state_lock
};
2655 ceph_assert(completion
->is_complete());
2657 ObjectDesc oid_value
;
2658 context
->find_object(oid
, &oid_value
);
2659 int r
= completion
->get_return_value();
2660 cout
<< num
<< ": got " << cpp_strerror(r
) << std::endl
;
2664 ceph_abort_msg("shouldn't happen");
2666 context
->update_object_version(oid
, completion
->get_version64());
2667 context
->find_object(oid
, &oid_value
);
2668 context
->oid_in_use
.erase(oid
);
2669 context
->oid_not_in_use
.insert(oid
);
2674 bool finished() override
2679 string
getType() override
2681 return "TierPromoteOp";
2685 class TierFlushOp
: public TestOp
{
2687 librados::AioCompletion
*completion
;
2688 librados::ObjectReadOperation op
;
2690 std::shared_ptr
<int> in_use
;
2693 RadosTestContext
*context
,
2696 : TestOp(n
, context
, stat
),
2701 void _begin() override
2703 context
->state_lock
.lock();
2705 context
->oid_in_use
.insert(oid
);
2706 context
->oid_not_in_use
.erase(oid
);
2708 pair
<TestOp
*, TestOp::CallbackInfo
*> *cb_arg
=
2709 new pair
<TestOp
*, TestOp::CallbackInfo
*>(this,
2710 new TestOp::CallbackInfo(0));
2711 completion
= context
->rados
.aio_create_completion((void *) cb_arg
,
2713 context
->state_lock
.unlock();
2716 unsigned flags
= librados::OPERATION_IGNORE_CACHE
;
2717 int r
= context
->io_ctx
.aio_operate(context
->prefix
+oid
, completion
,
2722 void _finish(CallbackInfo
*info
) override
2724 context
->state_lock
.lock();
2726 ceph_assert(completion
->is_complete());
2728 int r
= completion
->get_return_value();
2729 cout
<< num
<< ": got " << cpp_strerror(r
) << std::endl
;
2733 ceph_abort_msg("shouldn't happen");
2735 context
->update_object_version(oid
, completion
->get_version64());
2736 context
->oid_in_use
.erase(oid
);
2737 context
->oid_not_in_use
.insert(oid
);
2740 context
->state_lock
.unlock();
2743 bool finished() override
2748 string
getType() override
2750 return "TierFlushOp";
2754 class HitSetListOp
: public TestOp
{
2755 librados::AioCompletion
*comp1
, *comp2
;
2757 std::list
< std::pair
<time_t, time_t> > ls
;
2762 RadosTestContext
*context
,
2764 TestOpStat
*stat
= 0)
2765 : TestOp(n
, context
, stat
),
2766 comp1(NULL
), comp2(NULL
),
2770 void _begin() override
2772 pair
<TestOp
*, TestOp::CallbackInfo
*> *cb_arg
=
2773 new pair
<TestOp
*, TestOp::CallbackInfo
*>(this,
2774 new TestOp::CallbackInfo(0));
2775 comp1
= context
->rados
.aio_create_completion((void*) cb_arg
,
2777 int r
= context
->io_ctx
.hit_set_list(hash
, comp1
, &ls
);
2778 ceph_assert(r
== 0);
2781 void _finish(CallbackInfo
*info
) override
{
2782 std::lock_guard l
{context
->state_lock
};
2785 cerr
<< num
<< ": no hitsets" << std::endl
;
2788 cerr
<< num
<< ": hitsets are " << ls
<< std::endl
;
2789 int r
= rand() % ls
.size();
2790 std::list
<pair
<time_t,time_t> >::iterator p
= ls
.begin();
2793 pair
<TestOp
*, TestOp::CallbackInfo
*> *cb_arg
=
2794 new pair
<TestOp
*, TestOp::CallbackInfo
*>(this,
2795 new TestOp::CallbackInfo(0));
2796 comp2
= context
->rados
.aio_create_completion((void*) cb_arg
, &write_callback
);
2797 r
= context
->io_ctx
.hit_set_get(hash
, comp2
, p
->second
, &bl
);
2798 ceph_assert(r
== 0);
2801 int r
= comp2
->get_return_value();
2804 auto p
= bl
.cbegin();
2806 cout
<< num
<< ": got hitset of type " << hitset
.get_type_name()
2807 << " size " << bl
.length()
2810 // FIXME: we could verify that we did in fact race with a trim...
2811 ceph_assert(r
== -ENOENT
);
2819 bool finished() override
{
2823 string
getType() override
{
2824 return "HitSetListOp";
2828 class UndirtyOp
: public TestOp
{
2830 librados::AioCompletion
*completion
;
2831 librados::ObjectWriteOperation op
;
2835 RadosTestContext
*context
,
2837 TestOpStat
*stat
= 0)
2838 : TestOp(n
, context
, stat
),
2843 void _begin() override
2845 context
->state_lock
.lock();
2846 pair
<TestOp
*, TestOp::CallbackInfo
*> *cb_arg
=
2847 new pair
<TestOp
*, TestOp::CallbackInfo
*>(this,
2848 new TestOp::CallbackInfo(0));
2849 completion
= context
->rados
.aio_create_completion((void *) cb_arg
,
2852 context
->oid_in_use
.insert(oid
);
2853 context
->oid_not_in_use
.erase(oid
);
2854 context
->update_object_undirty(oid
);
2855 context
->state_lock
.unlock();
2858 int r
= context
->io_ctx
.aio_operate(context
->prefix
+oid
, completion
,
2863 void _finish(CallbackInfo
*info
) override
2865 std::lock_guard state_locker
{context
->state_lock
};
2867 ceph_assert(completion
->is_complete());
2868 context
->oid_in_use
.erase(oid
);
2869 context
->oid_not_in_use
.insert(oid
);
2870 context
->update_object_version(oid
, completion
->get_version64());
2875 bool finished() override
2880 string
getType() override
2886 class IsDirtyOp
: public TestOp
{
2888 librados::AioCompletion
*completion
;
2889 librados::ObjectReadOperation op
;
2892 ObjectDesc old_value
;
2894 std::shared_ptr
<int> in_use
;
2897 RadosTestContext
*context
,
2899 TestOpStat
*stat
= 0)
2900 : TestOp(n
, context
, stat
),
2906 void _begin() override
2908 context
->state_lock
.lock();
2910 if (!(rand() % 4) && !context
->snaps
.empty()) {
2911 snap
= rand_choose(context
->snaps
)->first
;
2912 in_use
= context
->snaps_in_use
.lookup_or_create(snap
, snap
);
2916 std::cout
<< num
<< ": is_dirty oid " << oid
<< " snap " << snap
2919 pair
<TestOp
*, TestOp::CallbackInfo
*> *cb_arg
=
2920 new pair
<TestOp
*, TestOp::CallbackInfo
*>(this,
2921 new TestOp::CallbackInfo(0));
2922 completion
= context
->rados
.aio_create_completion((void *) cb_arg
,
2925 context
->oid_in_use
.insert(oid
);
2926 context
->oid_not_in_use
.erase(oid
);
2927 context
->state_lock
.unlock();
2930 context
->io_ctx
.snap_set_read(context
->snaps
[snap
]);
2933 op
.is_dirty(&dirty
, NULL
);
2934 int r
= context
->io_ctx
.aio_operate(context
->prefix
+oid
, completion
,
2939 context
->io_ctx
.snap_set_read(0);
2943 void _finish(CallbackInfo
*info
) override
2945 std::lock_guard state_locker
{context
->state_lock
};
2947 ceph_assert(completion
->is_complete());
2948 context
->oid_in_use
.erase(oid
);
2949 context
->oid_not_in_use
.insert(oid
);
2951 ceph_assert(context
->find_object(oid
, &old_value
, snap
));
2953 int r
= completion
->get_return_value();
2955 cout
<< num
<< ": " << (dirty
? "dirty" : "clean") << std::endl
;
2956 ceph_assert(!old_value
.deleted());
2957 ceph_assert(dirty
== old_value
.dirty
);
2959 cout
<< num
<< ": got " << r
<< std::endl
;
2960 ceph_assert(r
== -ENOENT
);
2961 ceph_assert(old_value
.deleted());
2967 bool finished() override
2972 string
getType() override
2980 class CacheFlushOp
: public TestOp
{
2982 librados::AioCompletion
*completion
;
2983 librados::ObjectReadOperation op
;
2988 std::shared_ptr
<int> in_use
;
2991 RadosTestContext
*context
,
2995 : TestOp(n
, context
, stat
),
3003 void _begin() override
3005 context
->state_lock
.lock();
3007 if (!(rand() % 4) && !context
->snaps
.empty()) {
3008 snap
= rand_choose(context
->snaps
)->first
;
3009 in_use
= context
->snaps_in_use
.lookup_or_create(snap
, snap
);
3013 // not being particularly specific here about knowing which
3014 // flushes are on the oldest clean snap and which ones are not.
3015 can_fail
= !blocking
|| !context
->snaps
.empty();
3016 // FIXME: we could fail if we've ever removed a snap due to
3017 // the async snap trimming.
3019 cout
<< num
<< ": " << (blocking
? "cache_flush" : "cache_try_flush")
3020 << " oid " << oid
<< " snap " << snap
<< std::endl
;
3023 context
->io_ctx
.snap_set_read(context
->snaps
[snap
]);
3026 pair
<TestOp
*, TestOp::CallbackInfo
*> *cb_arg
=
3027 new pair
<TestOp
*, TestOp::CallbackInfo
*>(this,
3028 new TestOp::CallbackInfo(0));
3029 completion
= context
->rados
.aio_create_completion((void *) cb_arg
,
3031 context
->oid_flushing
.insert(oid
);
3032 context
->oid_not_flushing
.erase(oid
);
3033 context
->state_lock
.unlock();
3035 unsigned flags
= librados::OPERATION_IGNORE_CACHE
;
3039 op
.cache_try_flush();
3040 flags
= librados::OPERATION_SKIPRWLOCKS
;
3042 int r
= context
->io_ctx
.aio_operate(context
->prefix
+oid
, completion
,
3047 context
->io_ctx
.snap_set_read(0);
3051 void _finish(CallbackInfo
*info
) override
3053 std::lock_guard state_locker
{context
->state_lock
};
3055 ceph_assert(completion
->is_complete());
3056 context
->oid_flushing
.erase(oid
);
3057 context
->oid_not_flushing
.insert(oid
);
3058 int r
= completion
->get_return_value();
3059 cout
<< num
<< ": got " << cpp_strerror(r
) << std::endl
;
3061 context
->update_object_version(oid
, 0, snap
);
3062 } else if (r
== -EBUSY
) {
3063 ceph_assert(can_fail
);
3064 } else if (r
== -EINVAL
) {
3065 // caching not enabled?
3066 } else if (r
== -ENOENT
) {
3067 // may have raced with a remove?
3069 ceph_abort_msg("shouldn't happen");
3075 bool finished() override
3080 string
getType() override
3082 return "CacheFlushOp";
3086 class CacheEvictOp
: public TestOp
{
3088 librados::AioCompletion
*completion
;
3089 librados::ObjectReadOperation op
;
3091 std::shared_ptr
<int> in_use
;
3094 RadosTestContext
*context
,
3097 : TestOp(n
, context
, stat
),
3102 void _begin() override
3104 context
->state_lock
.lock();
3107 if (!(rand() % 4) && !context
->snaps
.empty()) {
3108 snap
= rand_choose(context
->snaps
)->first
;
3109 in_use
= context
->snaps_in_use
.lookup_or_create(snap
, snap
);
3113 cout
<< num
<< ": cache_evict oid " << oid
<< " snap " << snap
<< std::endl
;
3116 context
->io_ctx
.snap_set_read(context
->snaps
[snap
]);
3119 pair
<TestOp
*, TestOp::CallbackInfo
*> *cb_arg
=
3120 new pair
<TestOp
*, TestOp::CallbackInfo
*>(this,
3121 new TestOp::CallbackInfo(0));
3122 completion
= context
->rados
.aio_create_completion((void *) cb_arg
,
3124 context
->state_lock
.unlock();
3127 int r
= context
->io_ctx
.aio_operate(context
->prefix
+oid
, completion
,
3128 &op
, librados::OPERATION_IGNORE_CACHE
,
3133 context
->io_ctx
.snap_set_read(0);
3137 void _finish(CallbackInfo
*info
) override
3139 std::lock_guard state_locker
{context
->state_lock
};
3141 ceph_assert(completion
->is_complete());
3143 int r
= completion
->get_return_value();
3144 cout
<< num
<< ": got " << cpp_strerror(r
) << std::endl
;
3147 } else if (r
== -EBUSY
) {
3148 // raced with something that dirtied the object
3149 } else if (r
== -EINVAL
) {
3150 // caching not enabled?
3151 } else if (r
== -ENOENT
) {
3152 // may have raced with a remove?
3154 ceph_abort_msg("shouldn't happen");
3160 bool finished() override
3165 string
getType() override
3167 return "CacheEvictOp";