]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: introduce safe user-reset-stats 34869/head
authorMatt Benjamin <mbenjamin@redhat.com>
Thu, 30 Apr 2020 22:59:11 +0000 (18:59 -0400)
committerMatt Benjamin <mbenjamin@redhat.com>
Thu, 2 Jul 2020 14:00:30 +0000 (10:00 -0400)
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 <mbenjamin@redhat.com>
src/cls/user/cls_user.cc
src/cls/user/cls_user_ops.h
src/rgw/rgw_tools.cc
src/rgw/rgw_tools.h
src/rgw/services/svc_rados.cc
src/rgw/services/svc_rados.h
src/rgw/services/svc_user_rados.cc

index 20809daf0bc0f8ade6b3c22ca5f7afe92240cf42..1ab75d384f80a7ca3c1c9449d492966a2cb7075e 100644 (file)
@@ -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<string, buffer::list> 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;
 }
index 8b957a79f93d573362c7214ebbbdf17a138c1dd6..7edd1bc15cef213bccfc993da158b7d0784c49b9 100644 (file)
@@ -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<cls_user_reset_stats2_op*>& 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<cls_user_reset_stats2_ret*>& ls);
+};
+WRITE_CLASS_ENCODER(cls_user_reset_stats2_ret);
+
 struct cls_user_get_header_ret {
   cls_user_header header;
 
index af5e05cca64af9218e5336acef0c6713f82139ee..b264ae14dd75b8be1f26aaf44daf76bf69947332 100644 (file)
@@ -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)
index 1fca1365bf4c2f9216db21ffcd05434df9bb414f..d49f39efa6eb9558fefad0c7f831482931bf4964 100644 (file)
@@ -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);
index b0e8e02c9149fc49af7ff0dc03bc7b760291deec..6a330e94203a4b3522734c6bbc75f86cd7070f31 100644 (file)
@@ -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)
index 721a4bbe258180011695138b00c977a02437935b..1d0941501f82f4c98bedb34e67aac99964806268 100644 (file)
@@ -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);
index 04671bb323250bc832c196e16f13ae28eaaa0708..31d42588f683ff849892da4acb298fafc8b3f00c 100644 (file)
@@ -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,