]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd: default to sparse-reads for any IO operation over 64K 20208/head
authorJason Dillaman <dillaman@redhat.com>
Thu, 19 Oct 2017 17:52:56 +0000 (13:52 -0400)
committerJason Dillaman <dillaman@redhat.com>
Wed, 31 Jan 2018 16:31:38 +0000 (11:31 -0500)
Testing BlueStore against both HDDs and OSDs with fully allocated
and sparse-allocated objects shows a performance improvement with
sparse-read between 32K and 64K.

Fixes: http://tracker.ceph.com/issues/21849
Signed-off-by: Jason Dillaman <dillaman@redhat.com>
(cherry picked from commit 251658471eab6d8cd968d678922bab437f72a9c7)

src/common/options.cc
src/librbd/ImageCtx.cc
src/librbd/ImageCtx.h
src/librbd/io/ImageRequest.cc
src/librbd/io/ObjectRequest.cc
src/librbd/io/ObjectRequest.h
src/test/librbd/CMakeLists.txt
src/test/librbd/io/test_mock_ImageRequest.cc
src/test/librbd/io/test_mock_ObjectRequest.cc [new file with mode: 0644]

index 0469939ab737a982c636db64b5a4d9eb613cf452..00720d5c603136d053c10629d82d9620c49ab00e 100644 (file)
@@ -5658,6 +5658,18 @@ static std::vector<Option> get_rbd_options() {
     .set_default(false)
     .set_description("localize parent requests to closest OSD"),
 
+    Option("rbd_sparse_read_threshold_bytes", Option::TYPE_UINT,
+           Option::LEVEL_ADVANCED)
+    .set_default(64_K)
+    .set_description("threshold for issuing a sparse-read")
+    .set_long_description("minimum number of sequential bytes to read against "
+                          "an object before issuing a sparse-read request to "
+                          "the cluster. 0 implies it must be a full object read"
+                          "to issue a sparse-read, 1 implies always use "
+                          "sparse-read, and any value larger than the maximum "
+                          "object size will disable sparse-read for all "
+                          "requests"),
+
     Option("rbd_readahead_trigger_requests", Option::TYPE_INT, Option::LEVEL_ADVANCED)
     .set_default(10)
     .set_description("number of sequential requests necessary to trigger readahead"),
index 3918a768181904417293e75228114bcaba08510d..411d34c110c88b042487c03a8230bb20cfa0bcbc 100644 (file)
@@ -981,6 +981,7 @@ struct C_InvalidateCache : public Context {
         "rbd_localize_snap_reads", false)(
         "rbd_balance_parent_reads", false)(
         "rbd_localize_parent_reads", false)(
+        "rbd_sparse_read_threshold_bytes", false)(
         "rbd_readahead_trigger_requests", false)(
         "rbd_readahead_max_bytes", false)(
         "rbd_readahead_disable_after_bytes", false)(
@@ -1039,6 +1040,7 @@ struct C_InvalidateCache : public Context {
     ASSIGN_OPTION(localize_snap_reads, bool);
     ASSIGN_OPTION(balance_parent_reads, bool);
     ASSIGN_OPTION(localize_parent_reads, bool);
+    ASSIGN_OPTION(sparse_read_threshold_bytes, uint64_t);
     ASSIGN_OPTION(readahead_trigger_requests, int64_t);
     ASSIGN_OPTION(readahead_max_bytes, int64_t);
     ASSIGN_OPTION(readahead_disable_after_bytes, int64_t);
@@ -1063,6 +1065,10 @@ struct C_InvalidateCache : public Context {
     if (thread_safe) {
       ASSIGN_OPTION(journal_pool, std::string);
     }
+
+    if (sparse_read_threshold_bytes == 0) {
+      sparse_read_threshold_bytes = get_object_size();
+    }
   }
 
   ExclusiveLock<ImageCtx> *ImageCtx::create_exclusive_lock() {
index 9479bae30a9a566ce0e7f7a42bdb72ec5b039927..625da49cd9836a77a3e5f5197b14f4bd584a98ee 100644 (file)
@@ -176,6 +176,7 @@ namespace librbd {
     bool localize_snap_reads;
     bool balance_parent_reads;
     bool localize_parent_reads;
+    uint64_t sparse_read_threshold_bytes;
     uint32_t readahead_trigger_requests;
     uint64_t readahead_max_bytes;
     uint64_t readahead_disable_after_bytes;
index 434c208eefd1b56679988f9f1a1505251d063124..871280689f8e9ecd44d9eac2507ff62b4a88d798 100644 (file)
@@ -355,7 +355,7 @@ void ImageReadRequest<I>::send_request() {
         aio_comp);
       ObjectReadRequest<I> *req = ObjectReadRequest<I>::create(
         &image_ctx, extent.oid.name, extent.objectno, extent.offset,
-        extent.length, extent.buffer_extents, snap_id, true, m_op_flags,
+        extent.length, extent.buffer_extents, snap_id, m_op_flags,
        this->m_trace, req_comp);
       req_comp->request = req;
 
index 16e8aabf20d1382671f20c726a5e90cacd865fb3..3c55ccd07d9423b44d554743dac8d10b9ddc2db3 100644 (file)
@@ -205,14 +205,13 @@ template <typename I>
 ObjectReadRequest<I>::ObjectReadRequest(I *ictx, const std::string &oid,
                                         uint64_t objectno, uint64_t offset,
                                         uint64_t len, Extents& be,
-                                        librados::snap_t snap_id, bool sparse,
-                                       int op_flags,
+                                        librados::snap_t snap_id, int op_flags,
                                        const ZTracer::Trace &parent_trace,
                                         Context *completion)
   : ObjectRequest<I>(ictx, oid, objectno, offset, len, snap_id, false, "read",
                      parent_trace, completion),
-    m_buffer_extents(be), m_tried_parent(false), m_sparse(sparse),
-    m_op_flags(op_flags), m_state(LIBRBD_AIO_READ_FLAT) {
+    m_buffer_extents(be), m_tried_parent(false), m_op_flags(op_flags),
+    m_state(LIBRBD_AIO_READ_FLAT) {
   guard_read();
 }
 
@@ -326,7 +325,7 @@ void ObjectReadRequest<I>::send() {
 
   librados::ObjectReadOperation op;
   int flags = image_ctx->get_read_flags(this->m_snap_id);
-  if (m_sparse) {
+  if (this->m_object_len >= image_ctx->sparse_read_threshold_bytes) {
     op.sparse_read(this->m_object_off, this->m_object_len, &m_ext_map,
                    &m_read_data, nullptr);
   } else {
index 5ab43d336c3aab2b9a159450cd169117ffba58b7..703bb4e0dd20b9096892983bb672e2f375dcf9ff 100644 (file)
@@ -151,20 +151,19 @@ public:
   static ObjectReadRequest* create(ImageCtxT *ictx, const std::string &oid,
                                    uint64_t objectno, uint64_t offset,
                                    uint64_t len, Extents &buffer_extents,
-                                   librados::snap_t snap_id, bool sparse,
-                                  int op_flags,
+                                   librados::snap_t snap_id, int op_flags,
                                   const ZTracer::Trace &parent_trace,
                                    Context *completion) {
     return new ObjectReadRequest(ictx, oid, objectno, offset, len,
-                                 buffer_extents, snap_id, sparse, op_flags,
+                                 buffer_extents, snap_id, op_flags,
                                 parent_trace, completion);
   }
 
   ObjectReadRequest(ImageCtxT *ictx, const std::string &oid,
                     uint64_t objectno, uint64_t offset, uint64_t len,
                     Extents& buffer_extents, librados::snap_t snap_id,
-                    bool sparse, int op_flags,
-                   const ZTracer::Trace &parent_trace, Context *completion);
+                    int op_flags, const ZTracer::Trace &parent_trace,
+                    Context *completion);
 
   bool should_complete(int r) override;
   void send() override;
@@ -197,7 +196,6 @@ public:
 private:
   Extents m_buffer_extents;
   bool m_tried_parent;
-  bool m_sparse;
   int m_op_flags;
   ceph::bufferlist m_read_data;
   ExtentMap m_ext_map;
index 7c095f482db3662e028ac9b3ee8393c71f74dd5c..3ec3d3df84a0beb1fb178329349a58ca0c15cf2a 100644 (file)
@@ -37,6 +37,7 @@ set(unittest_librbd_srcs
   image/test_mock_RemoveRequest.cc
   io/test_mock_ImageRequest.cc
   io/test_mock_ImageRequestWQ.cc
+  io/test_mock_ObjectRequest.cc
   journal/test_mock_OpenRequest.cc
   journal/test_mock_PromoteRequest.cc
   journal/test_mock_Replay.cc
index 4aca8a8d9ce69c99119fb937355b3e6ff56cea4a..7925ce928f45c08279423c32d4f11e684bb256d4 100644 (file)
@@ -136,8 +136,7 @@ struct ObjectReadRequest<librbd::MockTestImageCtx> : public ObjectRequest<librbd
                                    const std::string &oid,
                                    uint64_t objectno, uint64_t offset,
                                    uint64_t len, Extents &buffer_extents,
-                                   librados::snap_t snap_id, bool sparse,
-                                   int op_flags,
+                                   librados::snap_t snap_id, int op_flags,
                                    const ZTracer::Trace &parent_trace,
                                    Context *completion) {
     assert(s_instance != nullptr);
diff --git a/src/test/librbd/io/test_mock_ObjectRequest.cc b/src/test/librbd/io/test_mock_ObjectRequest.cc
new file mode 100644 (file)
index 0000000..616dbcf
--- /dev/null
@@ -0,0 +1,189 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockObjectMap.h"
+#include "test/librbd/mock/cache/MockImageCache.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "librbd/io/CopyupRequest.h"
+#include "librbd/io/ImageRequest.h"
+#include "librbd/io/ObjectRequest.h"
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+  MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+  }
+};
+
+} // anonymous namespace
+
+namespace util {
+
+inline ImageCtx *get_image_ctx(MockImageCtx *image_ctx) {
+  return image_ctx->image_ctx;
+}
+
+} // namespace util
+
+namespace io {
+
+template <>
+struct CopyupRequest<librbd::MockImageCtx> {
+  static CopyupRequest* create(librbd::MockImageCtx *ictx,
+                               const std::string &oid, uint64_t objectno,
+                               Extents &&image_extents,
+                               const ZTracer::Trace &parent_trace) {
+    return nullptr;
+  }
+
+  MOCK_METHOD0(send, void());
+};
+
+template <>
+struct ImageRequest<librbd::MockImageCtx> {
+  static void aio_read(librbd::MockImageCtx *ictx, AioCompletion *c,
+                       Extents &&image_extents, ReadResult &&read_result,
+                       int op_flags, const ZTracer::Trace &parent_trace) {
+  }
+};
+
+} // namespace io
+} // namespace librbd
+
+#include "librbd/io/ObjectRequest.cc"
+
+namespace librbd {
+namespace io {
+
+using ::testing::_;
+using ::testing::DoDefault;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::WithArg;
+
+struct TestMockIoObjectRequest : public TestMockFixture {
+  typedef ObjectRequest<librbd::MockImageCtx> MockObjectRequest;
+  typedef ObjectReadRequest<librbd::MockImageCtx> MockObjectReadRequest;
+
+  void expect_object_may_exist(MockTestImageCtx &mock_image_ctx,
+                               uint64_t object_no, bool exists) {
+    if (mock_image_ctx.object_map != nullptr) {
+      EXPECT_CALL(*mock_image_ctx.object_map, object_may_exist(object_no))
+        .WillOnce(Return(exists));
+    }
+  }
+
+  void expect_get_parent_overlap(MockTestImageCtx &mock_image_ctx,
+                                 librados::snap_t snap_id, uint64_t overlap,
+                                 int r) {
+    EXPECT_CALL(mock_image_ctx, get_parent_overlap(snap_id, _))
+      .WillOnce(WithArg<1>(Invoke([overlap, r](uint64_t *o) {
+                             *o = overlap;
+                             return r;
+                           })));
+  }
+
+  void expect_prune_parent_extents(MockTestImageCtx &mock_image_ctx,
+                                   const MockObjectRequest::Extents& extents,
+                                   uint64_t overlap, uint64_t object_overlap) {
+    EXPECT_CALL(mock_image_ctx, prune_parent_extents(_, overlap))
+      .WillOnce(WithArg<0>(Invoke([extents, object_overlap](MockObjectRequest::Extents& e) {
+                             e = extents;
+                             return object_overlap;
+                           })));
+  }
+
+  void expect_get_read_flags(MockTestImageCtx &mock_image_ctx,
+                             librados::snap_t snap_id, int flags) {
+    EXPECT_CALL(mock_image_ctx, get_read_flags(snap_id))
+      .WillOnce(Return(flags));
+  }
+
+  void expect_read(MockTestImageCtx &mock_image_ctx,
+                   const std::string& oid, uint64_t off, uint64_t len,
+                   int r) {
+    auto& expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.data_ctx),
+                               read(oid, len, off, _));
+    if (r < 0) {
+      expect.WillOnce(Return(r));
+    } else {
+      expect.WillOnce(DoDefault());
+    }
+  }
+
+  void expect_sparse_read(MockTestImageCtx &mock_image_ctx,
+                          const std::string& oid, uint64_t off, uint64_t len,
+                          int r) {
+    auto& expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.data_ctx),
+                               sparse_read(oid, off, len, _, _));
+    if (r < 0) {
+      expect.WillOnce(Return(r));
+    } else {
+      expect.WillOnce(DoDefault());
+    }
+  }
+};
+
+TEST_F(TestMockIoObjectRequest, Read) {
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+  ictx->sparse_read_threshold_bytes = 8096;
+
+  MockTestImageCtx mock_image_ctx(*ictx);
+  MockObjectMap mock_object_map;
+  if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+    mock_image_ctx.object_map = &mock_object_map;
+  }
+
+  InSequence seq;
+  expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 0, 0);
+  expect_prune_parent_extents(mock_image_ctx, {}, 0, 0);
+  expect_object_may_exist(mock_image_ctx, 0, true);
+  expect_get_read_flags(mock_image_ctx, CEPH_NOSNAP, 0);
+  expect_read(mock_image_ctx, "object0", 0, 4096, 0);
+
+  C_SaferCond ctx;
+  MockObjectReadRequest::Extents extents{{0, 4096}};
+  auto req = MockObjectReadRequest::create(
+    &mock_image_ctx, "object0", 0, 0, 4096, extents, CEPH_NOSNAP, 0, {}, &ctx);
+  req->send();
+  ASSERT_EQ(-ENOENT, ctx.wait());
+}
+
+TEST_F(TestMockIoObjectRequest, SparseReadThreshold) {
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+  ictx->sparse_read_threshold_bytes = ictx->get_object_size();
+
+  MockTestImageCtx mock_image_ctx(*ictx);
+  MockObjectMap mock_object_map;
+  if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+    mock_image_ctx.object_map = &mock_object_map;
+  }
+
+  InSequence seq;
+  expect_get_parent_overlap(mock_image_ctx, CEPH_NOSNAP, 0, 0);
+  expect_prune_parent_extents(mock_image_ctx, {}, 0, 0);
+  expect_object_may_exist(mock_image_ctx, 0, true);
+  expect_get_read_flags(mock_image_ctx, CEPH_NOSNAP, 0);
+  expect_sparse_read(mock_image_ctx, "object0", 0,
+                     ictx->sparse_read_threshold_bytes, 0);
+
+  C_SaferCond ctx;
+  MockObjectReadRequest::Extents extents{
+    {0, ictx->sparse_read_threshold_bytes}};
+  auto req = MockObjectReadRequest::create(
+    &mock_image_ctx, "object0", 0, 0, ictx->sparse_read_threshold_bytes,
+    extents, CEPH_NOSNAP, 0, {}, &ctx);
+  req->send();
+  ASSERT_EQ(-ENOENT, ctx.wait());
+}
+
+} // namespace io
+} // namespace librbd