From 562d7816ef3bab4bdd4a823184c8f5477b94b4d7 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 10 Oct 2013 11:51:16 -0700 Subject: librados: seek during object iteration Add ability to reset iterator to a specific hash position. For now, we just truncate this to the current PG. In the future, this may be more precise. Signed-off-by: Sage Weil --- src/include/rados/librados.h | 9 +++++ src/include/rados/librados.hpp | 7 ++++ src/librados/IoCtxImpl.cc | 8 ++++ src/librados/IoCtxImpl.h | 1 + src/librados/librados.cc | 25 ++++++++++++ src/osdc/Objecter.cc | 18 ++++++++- src/osdc/Objecter.h | 1 + src/test/librados/list.cc | 87 ++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 155 insertions(+), 1 deletion(-) diff --git a/src/include/rados/librados.h b/src/include/rados/librados.h index 449aeebe41a..eaf43ab6339 100644 --- a/src/include/rados/librados.h +++ b/src/include/rados/librados.h @@ -692,6 +692,15 @@ int rados_objects_list_open(rados_ioctx_t io, rados_list_ctx_t *ctx); */ uint32_t rados_objects_list_get_pg_hash_position(rados_list_ctx_t ctx); +/** + * Reposition object iterator to a different hash position + * + * @param ctx iterator marking where you are in the listing + * @param pos hash position to move to + * @returns actual (rounded) position we moved to + */ +uint32_t rados_objects_list_seek(rados_list_ctx_t ctx, uint32_t pos); + /** * Get the next object name and locator in the pool * diff --git a/src/include/rados/librados.hpp b/src/include/rados/librados.hpp index 46cedd578c5..d788e5ecf61 100644 --- a/src/include/rados/librados.hpp +++ b/src/include/rados/librados.hpp @@ -75,6 +75,9 @@ namespace librados /// get current hash position of the iterator, rounded to the current pg uint32_t get_pg_hash_position() const; + /// move the iterator to a given hash position. this may (will!) be rounded to the nearest pg. + uint32_t seek(uint32_t pos); + private: void get_next(); std::tr1::shared_ptr < ObjListCtx > ctx; @@ -588,7 +591,11 @@ namespace librados std::list *lockers); + /// Start enumerating objects for a pool ObjectIterator objects_begin(); + /// Start enumerating objects for a pool starting from a hash position + ObjectIterator objects_begin(uint32_t start_hash_position); + /// Iterator indicating the end of a pool const ObjectIterator& objects_end() const; uint64_t get_last_version(); diff --git a/src/librados/IoCtxImpl.cc b/src/librados/IoCtxImpl.cc index 6d35f6ba6f4..ae943f8bc92 100644 --- a/src/librados/IoCtxImpl.cc +++ b/src/librados/IoCtxImpl.cc @@ -388,6 +388,14 @@ int librados::IoCtxImpl::list(Objecter::ListContext *context, int max_entries) return r; } +uint32_t librados::IoCtxImpl::list_seek(Objecter::ListContext *context, + uint32_t pos) +{ + Mutex::Locker l(*lock); + context->list.clear(); + return objecter->list_objects_seek(context, pos); +} + int librados::IoCtxImpl::create(const object_t& oid, bool exclusive) { ::ObjectOperation op; diff --git a/src/librados/IoCtxImpl.h b/src/librados/IoCtxImpl.h index ccecd4e8184..6137ba91257 100644 --- a/src/librados/IoCtxImpl.h +++ b/src/librados/IoCtxImpl.h @@ -108,6 +108,7 @@ struct librados::IoCtxImpl { // io int list(Objecter::ListContext *context, int max_entries); + uint32_t list_seek(Objecter::ListContext *context, uint32_t pos); int create(const object_t& oid, bool exclusive); int create(const object_t& oid, bool exclusive, const std::string& category); int write(const object_t& oid, bufferlist& bl, size_t len, uint64_t off); diff --git a/src/librados/librados.cc b/src/librados/librados.cc index a916abcff26..f4a2a0c2786 100644 --- a/src/librados/librados.cc +++ b/src/librados/librados.cc @@ -488,6 +488,13 @@ librados::ObjectIterator librados::ObjectIterator::operator++(int) return ret; } +uint32_t librados::ObjectIterator::seek(uint32_t pos) +{ + uint32_t r = rados_objects_list_seek(ctx.get(), pos); + get_next(); + return r; +} + void librados::ObjectIterator::get_next() { const char *entry, *key; @@ -1136,6 +1143,16 @@ librados::ObjectIterator librados::IoCtx::objects_begin() return iter; } +librados::ObjectIterator librados::IoCtx::objects_begin(uint32_t pos) +{ + rados_list_ctx_t listh; + rados_objects_list_open(io_ctx_impl, &listh); + ObjectIterator iter((ObjListCtx*)listh); + iter.seek(pos); + iter.get_next(); + return iter; +} + const librados::ObjectIterator& librados::IoCtx::objects_end() const { return ObjectIterator::__EndObjectIterator; @@ -2534,6 +2551,14 @@ extern "C" void rados_objects_list_close(rados_list_ctx_t h) delete lh; } +extern "C" uint32_t rados_objects_list_seek(rados_list_ctx_t listctx, + uint32_t pos) +{ + librados::ObjListCtx *lh = (librados::ObjListCtx *)listctx; + uint32_t r = lh->ctx->list_seek(lh->lc, pos); + return r; +} + extern "C" uint32_t rados_objects_list_get_pg_hash_position( rados_list_ctx_t listctx) { diff --git a/src/osdc/Objecter.cc b/src/osdc/Objecter.cc index f767641a0f7..89f99f1a927 100644 --- a/src/osdc/Objecter.cc +++ b/src/osdc/Objecter.cc @@ -1714,8 +1714,24 @@ void Objecter::handle_osd_op_reply(MOSDOpReply *m) } -void Objecter::list_objects(ListContext *list_context, Context *onfinish) { +uint32_t Objecter::list_objects_seek(ListContext *list_context, + uint32_t pos) +{ + assert(client_lock.is_locked()); + pg_t actual = osdmap->raw_pg_to_pg(pg_t(pos, list_context->pool_id)); + ldout(cct, 10) << "list_objects_seek " << list_context + << " pos " << pos << " -> " << actual << dendl; + list_context->current_pg = actual.ps(); + list_context->cookie = collection_list_handle_t(); + list_context->at_end_of_pg = false; + list_context->at_end_of_pool = false; + list_context->current_pg_epoch = 0; + return list_context->current_pg; +} +void Objecter::list_objects(ListContext *list_context, Context *onfinish) +{ + assert(client_lock.is_locked()); ldout(cct, 10) << "list_objects" << dendl; ldout(cct, 20) << " pool_id " << list_context->pool_id << " pool_snap_seq " << list_context->pool_snap_seq diff --git a/src/osdc/Objecter.h b/src/osdc/Objecter.h index 4e6fa77e69b..20c12d8e48d 100644 --- a/src/osdc/Objecter.h +++ b/src/osdc/Objecter.h @@ -1812,6 +1812,7 @@ private: } void list_objects(ListContext *p, Context *onfinish); + uint32_t list_objects_seek(ListContext *p, uint32_t pos); // ------------------------- // pool ops diff --git a/src/test/librados/list.cc b/src/test/librados/list.cc index fca0ad40395..d2aeec25e96 100644 --- a/src/test/librados/list.cc +++ b/src/test/librados/list.cc @@ -3,6 +3,7 @@ #include "include/stringify.h" #include "test/librados/test.h" +#include "include/types.h" #include "gtest/gtest.h" #include #include @@ -214,3 +215,89 @@ TEST(LibRadosList, ListObjectsManyPP) { ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster)); } + + +TEST(LibRadosList, ListObjectsStart) { + rados_t cluster; + rados_ioctx_t ioctx; + std::string pool_name = get_temp_pool_name(); + ASSERT_EQ("", create_one_pool(pool_name, &cluster)); + rados_ioctx_create(cluster, pool_name.c_str(), &ioctx); + + char buf[128]; + memset(buf, 0xcc, sizeof(buf)); + + for (int i=0; i<16; ++i) { + string n = stringify(i); + ASSERT_EQ((int)sizeof(buf), rados_write(ioctx, n.c_str(), buf, sizeof(buf), 0)); + } + + rados_list_ctx_t ctx; + ASSERT_EQ(0, rados_objects_list_open(ioctx, &ctx)); + std::map > pg_to_obj; + const char *entry; + while (rados_objects_list_next(ctx, &entry, NULL) == 0) { + uint32_t pos = rados_objects_list_get_pg_hash_position(ctx); + std::cout << entry << " " << pos << std::endl; + pg_to_obj[pos].insert(entry); + } + rados_objects_list_close(ctx); + + std::map >::reverse_iterator p = + pg_to_obj.rbegin(); + ASSERT_EQ(0, rados_objects_list_open(ioctx, &ctx)); + while (p != pg_to_obj.rend()) { + ASSERT_EQ((uint32_t)p->first, rados_objects_list_seek(ctx, p->first)); + ASSERT_EQ(0, rados_objects_list_next(ctx, &entry, NULL)); + std::cout << "have " << entry << " expect one of " << p->second << std::endl; + ASSERT_TRUE(p->second.count(entry)); + + ++p; + if (p == pg_to_obj.rend()) + break; + } + rados_objects_list_close(ctx); + rados_ioctx_destroy(ioctx); + ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster)); +} + +TEST(LibRadosList, ListObjectsStartPP) { + std::string pool_name = get_temp_pool_name(); + Rados cluster; + ASSERT_EQ("", create_one_pool_pp(pool_name, cluster)); + IoCtx ioctx; + cluster.ioctx_create(pool_name.c_str(), ioctx); + + char buf[128]; + memset(buf, 0xcc, sizeof(buf)); + bufferlist bl; + bl.append(buf, sizeof(buf)); + + for (int i=0; i<16; ++i) { + ASSERT_EQ((int)sizeof(buf), ioctx.write(stringify(i), bl, bl.length(), 0)); + } + + librados::ObjectIterator it = ioctx.objects_begin(); + std::map > pg_to_obj; + for (; it != ioctx.objects_end(); ++it) { + std::cout << it->first << " " << it.get_pg_hash_position() << std::endl; + pg_to_obj[it.get_pg_hash_position()].insert(it->first); + } + + std::map >::reverse_iterator p = + pg_to_obj.rbegin(); + it = ioctx.objects_begin(p->first); + while (p != pg_to_obj.rend()) { + std::cout << "have " << it->first << " expect one of " << p->second << std::endl; + ASSERT_TRUE(p->second.count(it->first)); + + ++p; + if (p == pg_to_obj.rend()) + break; + ASSERT_EQ((uint32_t)p->first, it.seek(p->first)); + } + + ioctx.close(); + ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster)); +} + -- cgit v1.2.1