From: Matt Benjamin Date: Thu, 30 Apr 2020 22:59:11 +0000 (-0400) Subject: rgw: introduce safe user-reset-stats X-Git-Tag: wip-pdonnell-testing-20200918.022351~401^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=25a82ed3795ecf6395e3d16fcbb4c29e478aa065;p=ceph-ci.git rgw: introduce safe user-reset-stats Defines cls_user_reset_stats2, a value-returning cls operation that sets new stats via progressive calls with an accumulator, avoiding risk of excessive call runtime. Fixes: https://tracker.ceph.com/issues/41080 Signed-off-by: Matt Benjamin --- diff --git a/src/cls/user/cls_user.cc b/src/cls/user/cls_user.cc index 20809daf0bc..1ab75d384f8 100644 --- a/src/cls/user/cls_user.cc +++ b/src/cls/user/cls_user.cc @@ -428,7 +428,75 @@ static int cls_user_reset_stats(cls_method_context_t hctx, CLS_LOG(20, "%s: updating header", __func__); return cls_cxx_map_write_header(hctx, &bl); -} +} /* legacy cls_user_reset_stats */ + +/// A method to reset the user.buckets header stats in accordance to +/// the values seen in the user.buckets omap keys. This is not be +/// equivalent to --sync-stats which also re-calculates the stats for +/// each bucket. +static int cls_user_reset_stats2(cls_method_context_t hctx, + buffer::list *in, buffer::list *out) +{ + cls_user_reset_stats2_op op; + + try { + auto bliter = in->cbegin(); + decode(op, bliter); + } catch (ceph::buffer::error& err) { + CLS_LOG(0, "ERROR: %s failed to decode op", __func__); + return -EINVAL; + } + + cls_user_header header; + string from_index{op.marker}, prefix; + cls_user_reset_stats2_ret ret; + + map keys; + int rc = cls_cxx_map_get_vals(hctx, from_index, prefix, MAX_ENTRIES, + &keys, &ret.truncated); + if (rc < 0) { + CLS_LOG(0, "ERROR: %s failed to retrieve omap key-values", __func__); + return rc; + } + CLS_LOG(20, "%s: read %lu key-values, truncated=%d", + __func__, keys.size(), ret.truncated); + + for (const auto& kv : keys) { + cls_user_bucket_entry e; + try { + auto& bl = kv.second; + auto bliter = bl.cbegin(); + decode(e, bliter); + } catch (ceph::buffer::error& err) { + CLS_LOG(0, "ERROR: %s failed to decode bucket entry for %s", + __func__, kv.first.c_str()); + return -EIO; + } + add_header_stats(&ret.acc_stats, e); + } + + /* try-update marker */ + if(!keys.empty()) + ret.marker = (--keys.cend())->first; + + if (! ret.truncated) { + buffer::list bl; + header.last_stats_update = op.time; + header.stats = ret.acc_stats; + encode(header, bl); + + CLS_LOG(20, "%s: updating header", __func__); + rc = cls_cxx_map_write_header(hctx, &bl); + + /* return final result */ + encode(ret, *out); + return rc; + } + + /* return partial result */ + encode(ret, *out); + return 0; +} /* cls_user_reset_stats2 */ CLS_INIT(user) { @@ -441,6 +509,7 @@ CLS_INIT(user) cls_method_handle_t h_user_list_buckets; cls_method_handle_t h_user_get_header; cls_method_handle_t h_user_reset_stats; + cls_method_handle_t h_user_reset_stats2; cls_register("user", &h_class); @@ -453,5 +522,7 @@ CLS_INIT(user) cls_register_cxx_method(h_class, "list_buckets", CLS_METHOD_RD, cls_user_list_buckets, &h_user_list_buckets); cls_register_cxx_method(h_class, "get_header", CLS_METHOD_RD, cls_user_get_header, &h_user_get_header); cls_register_cxx_method(h_class, "reset_user_stats", CLS_METHOD_RD | CLS_METHOD_WR, cls_user_reset_stats, &h_user_reset_stats); + cls_register_cxx_method(h_class, "reset_user_stats2", CLS_METHOD_RD | CLS_METHOD_WR, cls_user_reset_stats2, &h_user_reset_stats2); + return; } diff --git a/src/cls/user/cls_user_ops.h b/src/cls/user/cls_user_ops.h index 8b957a79f93..7edd1bc15ce 100644 --- a/src/cls/user/cls_user_ops.h +++ b/src/cls/user/cls_user_ops.h @@ -156,6 +156,69 @@ struct cls_user_reset_stats_op { }; WRITE_CLASS_ENCODER(cls_user_reset_stats_op); +struct cls_user_reset_stats2_op { + ceph::real_time time; + std::string marker; + cls_user_stats acc_stats; + + cls_user_reset_stats2_op() {} + + void encode(ceph::buffer::list& bl) const { + ENCODE_START(1, 1, bl); + encode(time, bl); + encode(marker, bl); + encode(acc_stats, bl); + ENCODE_FINISH(bl); + } + + void decode(ceph::buffer::list::const_iterator& bl) { + DECODE_START(1, bl); + decode(time, bl); + decode(marker, bl); + decode(acc_stats, bl); + DECODE_FINISH(bl); + } + + void dump(ceph::Formatter *f) const; + static void generate_test_instances(std::list& ls); +}; +WRITE_CLASS_ENCODER(cls_user_reset_stats2_op); + +struct cls_user_reset_stats2_ret { + std::string marker; + cls_user_stats acc_stats; /* 0-initialized */ + bool truncated; + + cls_user_reset_stats2_ret() + : truncated(false) {} + + void update_call(cls_user_reset_stats2_op& call) { + call.marker = marker; + call.acc_stats = acc_stats; + } + + void encode(ceph::buffer::list& bl) const { + ENCODE_START(1, 1, bl); + encode(marker, bl); + encode(acc_stats, bl); + encode(truncated, bl); + ENCODE_FINISH(bl); + } + + void decode(ceph::buffer::list::const_iterator& bl) { + DECODE_START(1, bl); + decode(marker, bl); + decode(acc_stats, bl); + decode(truncated, bl); + DECODE_FINISH(bl); + } + + void dump(ceph::Formatter *f) const; + static void generate_test_instances( + std::list& ls); +}; +WRITE_CLASS_ENCODER(cls_user_reset_stats2_ret); + struct cls_user_get_header_ret { cls_user_header header; diff --git a/src/rgw/rgw_tools.cc b/src/rgw/rgw_tools.cc index af5e05cca64..b264ae14dd7 100644 --- a/src/rgw/rgw_tools.cc +++ b/src/rgw/rgw_tools.cc @@ -248,7 +248,7 @@ thread_local bool is_asio_thread = false; int rgw_rados_operate(librados::IoCtx& ioctx, const std::string& oid, librados::ObjectReadOperation *op, bufferlist* pbl, - int flags, optional_yield y) + optional_yield y, int flags) { #ifdef HAVE_BOOST_CONTEXT // given a yield_context, call async_operate() to yield the coroutine instead @@ -257,7 +257,8 @@ int rgw_rados_operate(librados::IoCtx& ioctx, const std::string& oid, auto& context = y.get_io_context(); auto& yield = y.get_yield_context(); boost::system::error_code ec; - auto bl = librados::async_operate(context, ioctx, oid, op, flags, yield[ec]); + auto bl = librados::async_operate( + context, ioctx, oid, op, flags, yield[ec]); if (pbl) { *pbl = std::move(bl); } @@ -272,14 +273,8 @@ int rgw_rados_operate(librados::IoCtx& ioctx, const std::string& oid, } int rgw_rados_operate(librados::IoCtx& ioctx, const std::string& oid, - librados::ObjectReadOperation *op, bufferlist* pbl, - optional_yield y) -{ - return rgw_rados_operate(ioctx, oid, op, pbl, 0, y); -} - -int rgw_rados_operate(librados::IoCtx& ioctx, const std::string& oid, - librados::ObjectWriteOperation *op, int flags, optional_yield y) + librados::ObjectWriteOperation *op, optional_yield y, + int flags) { #ifdef HAVE_BOOST_CONTEXT if (y) { @@ -296,12 +291,6 @@ int rgw_rados_operate(librados::IoCtx& ioctx, const std::string& oid, return ioctx.operate(oid, op, flags); } -int rgw_rados_operate(librados::IoCtx& ioctx, const std::string& oid, - librados::ObjectWriteOperation *op, optional_yield y) -{ - return rgw_rados_operate(ioctx, oid, op, 0, y); -} - int rgw_rados_notify(librados::IoCtx& ioctx, const std::string& oid, bufferlist& bl, uint64_t timeout_ms, bufferlist* pbl, optional_yield y) diff --git a/src/rgw/rgw_tools.h b/src/rgw/rgw_tools.h index 1fca1365bf4..d49f39efa6e 100644 --- a/src/rgw/rgw_tools.h +++ b/src/rgw/rgw_tools.h @@ -94,14 +94,10 @@ extern thread_local bool is_asio_thread; /// perform the rados operation, using the yield context when given int rgw_rados_operate(librados::IoCtx& ioctx, const std::string& oid, librados::ObjectReadOperation *op, bufferlist* pbl, - optional_yield y); + optional_yield y, int flags = 0); int rgw_rados_operate(librados::IoCtx& ioctx, const std::string& oid, - librados::ObjectReadOperation *op, bufferlist* pbl, - int flags, optional_yield y); -int rgw_rados_operate(librados::IoCtx& ioctx, const std::string& oid, - librados::ObjectWriteOperation *op, optional_yield y); -int rgw_rados_operate(librados::IoCtx& ioctx, const std::string& oid, - librados::ObjectWriteOperation *op, int flags, optional_yield y); + librados::ObjectWriteOperation *op, optional_yield y, + int flags = 0); int rgw_rados_notify(librados::IoCtx& ioctx, const std::string& oid, bufferlist& bl, uint64_t timeout_ms, bufferlist* pbl, optional_yield y); diff --git a/src/rgw/services/svc_rados.cc b/src/rgw/services/svc_rados.cc index b0e8e02c914..6a330e94203 100644 --- a/src/rgw/services/svc_rados.cc +++ b/src/rgw/services/svc_rados.cc @@ -119,15 +119,15 @@ int RGWSI_RADOS::Obj::open() } int RGWSI_RADOS::Obj::operate(librados::ObjectWriteOperation *op, - optional_yield y) + optional_yield y, int flags) { - return rgw_rados_operate(ref.pool.ioctx(), ref.obj.oid, op, y); + return rgw_rados_operate(ref.pool.ioctx(), ref.obj.oid, op, y, flags); } -int RGWSI_RADOS::Obj::operate(librados::ObjectReadOperation *op, bufferlist *pbl, - optional_yield y) +int RGWSI_RADOS::Obj::operate(librados::ObjectReadOperation *op, + bufferlist *pbl, optional_yield y, int flags) { - return rgw_rados_operate(ref.pool.ioctx(), ref.obj.oid, op, pbl, y); + return rgw_rados_operate(ref.pool.ioctx(), ref.obj.oid, op, pbl, y, flags); } int RGWSI_RADOS::Obj::aio_operate(librados::AioCompletion *c, librados::ObjectWriteOperation *op) diff --git a/src/rgw/services/svc_rados.h b/src/rgw/services/svc_rados.h index 721a4bbe258..1d0941501f8 100644 --- a/src/rgw/services/svc_rados.h +++ b/src/rgw/services/svc_rados.h @@ -166,9 +166,10 @@ public: int open(); - int operate(librados::ObjectWriteOperation *op, optional_yield y); + int operate(librados::ObjectWriteOperation *op, optional_yield y, + int flags = 0); int operate(librados::ObjectReadOperation *op, bufferlist *pbl, - optional_yield y); + optional_yield y, int flags = 0); int aio_operate(librados::AioCompletion *c, librados::ObjectWriteOperation *op); int aio_operate(librados::AioCompletion *c, librados::ObjectReadOperation *op, bufferlist *pbl); diff --git a/src/rgw/services/svc_user_rados.cc b/src/rgw/services/svc_user_rados.cc index 04671bb3232..31d42588f68 100644 --- a/src/rgw/services/svc_user_rados.cc +++ b/src/rgw/services/svc_user_rados.cc @@ -798,13 +798,36 @@ int RGWSI_User_RADOS::cls_user_reset_stats(const rgw_user& user) { rgw_raw_obj obj = get_buckets_obj(user); auto rados_obj = svc.rados->obj(obj); - int r = rados_obj.open(); + int rval, r = rados_obj.open(); if (r < 0) { return r; } - librados::ObjectWriteOperation op; - ::cls_user_reset_stats(op); - return rados_obj.operate(&op, null_yield); + + cls_user_reset_stats2_op call; + cls_user_reset_stats2_ret ret; + + do { + buffer::list in, out; + librados::ObjectWriteOperation op; + + call.time = real_clock::now(); + ret.update_call(call); + + encode(call, in); + op.exec("user", "reset_user_stats2", in, &out, &rval); + r = rados_obj.operate(&op, null_yield, librados::OPERATION_RETURNVEC); + if (r < 0) { + return r; + } + try { + auto bliter = out.cbegin(); + decode(ret, bliter); + } catch (ceph::buffer::error& err) { + return -EINVAL; + } + } while (ret.truncated); + + return rval; } int RGWSI_User_RADOS::complete_flush_stats(RGWSI_MetaBackend::Context *ctx,