]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: introduce safe user-reset-stats 36655/head
authorMatt Benjamin <mbenjamin@redhat.com>
Thu, 30 Apr 2020 22:59:11 +0000 (18:59 -0400)
committerNathan Cutler <ncutler@suse.com>
Sat, 15 Aug 2020 13:13:53 +0000 (15:13 +0200)
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>
(cherry picked from commit 25a82ed3795ecf6395e3d16fcbb4c29e478aa065)

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 e80e6e231c27046608cd863cec413aa540c5b728..64018fa1b40f757a37ef1f47fad2aa62e2cff9bc 100644 (file)
@@ -422,7 +422,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)
 {
@@ -435,6 +503,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);
 
@@ -447,6 +516,8 @@ 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 fa4b1f31b67b848d2e100aad25f32b9de28c2df8..b64c9aebfe39e0604e52d097688efcd496f818f9 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,