]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rbd: include persistent cache metrics in "rbd status" report
authorIlya Dryomov <idryomov@gmail.com>
Sat, 9 Apr 2022 15:48:17 +0000 (17:48 +0200)
committerIlya Dryomov <idryomov@gmail.com>
Wed, 13 Apr 2022 10:51:21 +0000 (12:51 +0200)
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
(cherry picked from commit e996fd80601ec8c309c1517f33171e88a2f31cad)

doc/rbd/rbd-persistent-write-back-cache.rst
src/tools/rbd/action/Status.cc

index 604bf46e040a026b0dbdbf6553d9df3898751808..6bf65f1a0f717a681fd79ea109c1be876571fe4a 100644 (file)
@@ -74,15 +74,29 @@ users may use the command ``rbd status``.  ::
         rbd status {pool-name}/{image-name}
 
 The status of the cache is shown, including present, clean, cache size and the
-location. Currently the status is updated only at the time the cache is opened
-and closed and therefore may appear to be out of date (e.g. show that the cache
-is clean when it is actually dirty).
+location as well as some basic metrics.
 
 For example::
 
         $ rbd status rbd/foo
-        Watchers: none
-        Image cache state: {"present":"true","empty":"false","clean":"true","cache_type":"ssd","pwl_host":"sceph9","pwl_path":"/tmp/rbd-pwl.rbd.abcdef123456.pool","pwl_size":1073741824}
+        Watchers:
+                watcher=10.10.0.102:0/1061883624 client.25496 cookie=140338056493088
+        Persistent cache state:
+                host: sceph9
+                path: /mnt/nvme0/rbd-pwl.rbd.101e5824ad9a.pool
+                size: 1 GiB
+                mode: ssd
+                stats_timestamp: Sun Apr 10 13:26:32 2022
+                present: true   empty: false    clean: false
+                allocated: 509 MiB
+                cached: 501 MiB
+                dirty: 338 MiB
+                free: 515 MiB
+                hits_full: 1450 / 61%
+                hits_partial: 0 / 0%
+                misses: 924
+                hit_bytes: 192 MiB / 66%
+                miss_bytes: 97 MiB
 
 Discard Cache
 -------------
index b63c455dd7b054611f9d19912a6039131380958c..4cc01c628213adabec547b0d29b138a8b98dca36 100644 (file)
@@ -3,6 +3,7 @@
 
 #include "common/errno.h"
 #include "common/Formatter.h"
+#include "json_spirit/json_spirit.h"
 #include "tools/rbd/ArgumentTypes.h"
 #include "tools/rbd/Shell.h"
 #include "tools/rbd/Utils.h"
@@ -103,14 +104,83 @@ static int do_show_status(librados::IoCtx& io_ctx, const std::string &image_name
     }
   }
 
-  std::string image_cache_str;
+  struct {
+    // decoded
+    std::string host;
+    std::string path;
+    uint64_t size;
+    std::string mode;
+    std::string stats_timestamp;
+    bool present;
+    bool empty;
+    bool clean;
+    uint64_t allocated_bytes;
+    uint64_t cached_bytes;
+    uint64_t dirty_bytes;
+    uint64_t free_bytes;
+    uint64_t hits_full;
+    uint64_t hits_partial;
+    uint64_t misses;
+    uint64_t hit_bytes;
+    uint64_t miss_bytes;
+
+    // calculated
+    uint64_t total_read_ops;
+    uint64_t total_read_bytes;
+    int hits_full_percent;
+    int hits_partial_percent;
+    int hit_bytes_percent;
+  } cache_state;
+  std::string cache_str;
   if (features & RBD_FEATURE_DIRTY_CACHE) {
-    r = image.metadata_get(IMAGE_CACHE_STATE, &image_cache_str);
+    r = image.metadata_get(IMAGE_CACHE_STATE, &cache_str);
     if (r < 0) {
-      std::cerr << "rbd: getting image cache state failed: " << cpp_strerror(r)
+      std::cerr << "rbd: getting persistent cache state failed: " << cpp_strerror(r)
                 << std::endl;
       // not fatal
     }
+    json_spirit::mValue json_root;
+    if (!json_spirit::read(cache_str.c_str(), json_root)) {
+      std::cerr << "rbd: parsing persistent cache state failed" << std::endl;
+      cache_str.clear();
+    } else {
+      try {
+        auto& o = json_root.get_obj();
+        cache_state.host = o["host"].get_str();
+        cache_state.path = o["path"].get_str();
+        cache_state.size = o["size"].get_uint64();
+        cache_state.mode = o["mode"].get_str();
+        time_t stats_timestamp_sec = o["stats_timestamp"].get_uint64();
+        cache_state.stats_timestamp = ctime(&stats_timestamp_sec);
+        cache_state.stats_timestamp.pop_back();
+        cache_state.present = o["present"].get_bool();
+        cache_state.empty = o["empty"].get_bool();
+        cache_state.clean = o["clean"].get_bool();
+        cache_state.allocated_bytes = o["allocated_bytes"].get_uint64();
+        cache_state.cached_bytes = o["cached_bytes"].get_uint64();
+        cache_state.dirty_bytes = o["dirty_bytes"].get_uint64();
+        cache_state.free_bytes = o["free_bytes"].get_uint64();
+        cache_state.hits_full = o["hits_full"].get_uint64();
+        cache_state.hits_partial = o["hits_partial"].get_uint64();
+        cache_state.misses = o["misses"].get_uint64();
+        cache_state.hit_bytes = o["hit_bytes"].get_uint64();
+        cache_state.miss_bytes = o["miss_bytes"].get_uint64();
+      } catch (std::runtime_error &e) {
+        std::cerr << "rbd: parsing persistent cache state failed: " << e.what()
+                  << std::endl;
+        cache_str.clear();
+      }
+      cache_state.total_read_ops = cache_state.hits_full +
+          cache_state.hits_partial + cache_state.misses;
+      cache_state.total_read_bytes = cache_state.hit_bytes +
+          cache_state.miss_bytes;
+      cache_state.hits_full_percent = utils::get_percentage(
+          cache_state.hits_full, cache_state.total_read_ops);
+      cache_state.hits_partial_percent = utils::get_percentage(
+          cache_state.hits_partial, cache_state.total_read_ops);
+      cache_state.hit_bytes_percent = utils::get_percentage(
+          cache_state.hit_bytes, cache_state.total_read_bytes);
+    }
   }
 
   if (f)
@@ -146,8 +216,29 @@ static int do_show_status(librados::IoCtx& io_ctx, const std::string &image_name
       f->dump_string("state_description", migration_status.state_description);
       f->close_section(); // migration
     }
-    if (!image_cache_str.empty()) {
-      f->dump_string("image_cache_state", image_cache_str);
+    if (!cache_str.empty()) {
+      f->open_object_section("persistent_cache");
+      f->dump_string("host", cache_state.host);
+      f->dump_string("path", cache_state.path);
+      f->dump_unsigned("size", cache_state.size);
+      f->dump_string("mode", cache_state.mode);
+      f->dump_string("stats_timestamp", cache_state.stats_timestamp);
+      f->dump_bool("present", cache_state.present);
+      f->dump_bool("empty", cache_state.empty);
+      f->dump_bool("clean", cache_state.clean);
+      f->dump_unsigned("allocated_bytes", cache_state.allocated_bytes);
+      f->dump_unsigned("cached_bytes", cache_state.cached_bytes);
+      f->dump_unsigned("dirty_bytes", cache_state.dirty_bytes);
+      f->dump_unsigned("free_bytes", cache_state.free_bytes);
+      f->dump_unsigned("hits_full", cache_state.hits_full);
+      f->dump_int("hits_full_percent", cache_state.hits_full_percent);
+      f->dump_unsigned("hits_partial", cache_state.hits_partial);
+      f->dump_int("hits_partial_percent", cache_state.hits_partial_percent);
+      f->dump_unsigned("misses", cache_state.misses);
+      f->dump_unsigned("hit_bytes", cache_state.hit_bytes);
+      f->dump_int("hit_bytes_percent", cache_state.hit_bytes_percent);
+      f->dump_unsigned("miss_bytes", cache_state.miss_bytes);
+      f->close_section(); // persistent_cache
     }
   } else {
     if (watchers.size()) {
@@ -188,9 +279,33 @@ static int do_show_status(librados::IoCtx& io_ctx, const std::string &image_name
       }
       std::cout << std::endl;
     }
-
-    if (!image_cache_str.empty()) {
-        std::cout << "Image cache state: " << image_cache_str << std::endl;
+    if (!cache_str.empty()) {
+      std::cout << "Persistent cache state:" << std::endl;
+      std::cout << "\thost: " << cache_state.host << std::endl;
+      std::cout << "\tpath: " << cache_state.path << std::endl;
+      std::cout << "\tsize: " << byte_u_t(cache_state.size) << std::endl;
+      std::cout << "\tmode: " << cache_state.mode << std::endl;
+      std::cout << "\tstats_timestamp: " << cache_state.stats_timestamp
+                << std::endl;
+      std::cout << "\tpresent: " << (cache_state.present ? "true" : "false")
+                << "\tempty: " << (cache_state.empty ? "true" : "false")
+                << "\tclean: " << (cache_state.clean ? "true" : "false")
+                << std::endl;
+      std::cout << "\tallocated: " << byte_u_t(cache_state.allocated_bytes)
+                << std::endl;
+      std::cout << "\tcached: " << byte_u_t(cache_state.cached_bytes)
+                << std::endl;
+      std::cout << "\tdirty: " << byte_u_t(cache_state.dirty_bytes) << std::endl;
+      std::cout << "\tfree: " << byte_u_t(cache_state.free_bytes) << std::endl;
+      std::cout << "\thits_full: " << cache_state.hits_full << " / "
+                << cache_state.hits_full_percent << "%" << std::endl;
+      std::cout << "\thits_partial: " << cache_state.hits_partial << " / "
+                << cache_state.hits_partial_percent << "%" << std::endl;
+      std::cout << "\tmisses: " << cache_state.misses << std::endl;
+      std::cout << "\thit_bytes: " << byte_u_t(cache_state.hit_bytes) << " / "
+                << cache_state.hit_bytes_percent << "%" << std::endl;
+      std::cout << "\tmiss_bytes: " << byte_u_t(cache_state.miss_bytes)
+                << std::endl;
     }
   }