]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw/rados: part support for RGWRados::Object::Read
authorCasey Bodley <cbodley@redhat.com>
Fri, 17 Feb 2023 13:47:05 +0000 (08:47 -0500)
committerCasey Bodley <cbodley@redhat.com>
Tue, 21 Nov 2023 16:09:23 +0000 (11:09 -0500)
Signed-off-by: Casey Bodley <cbodley@redhat.com>
src/rgw/driver/rados/rgw_rados.cc
src/rgw/driver/rados/rgw_rados.h
src/rgw/driver/rados/rgw_sal_rados.cc
src/rgw/rgw_op.cc
src/rgw/rgw_sal.h

index fc1d7b7a685527a91b579532e69773640ad08764..c5624ba0171ec10d49dd3509db627564e61365ed 100644 (file)
@@ -6527,15 +6527,105 @@ int RGWRados::set_attrs(const DoutPrefixProvider *dpp, RGWObjectCtx* octx, RGWBu
   return 0;
 }
 
+static int get_part_obj_state(const DoutPrefixProvider* dpp, optional_yield y,
+                              RGWRados* store, RGWBucketInfo& bucket_info,
+                              RGWObjectCtx* rctx, RGWObjManifest* manifest,
+                              int part_num, int* parts_count, bool prefetch,
+                              RGWObjState** pstate, RGWObjManifest** pmanifest)
+{
+  // navigate to the requested part in the manifest
+  RGWObjManifest::obj_iterator end = manifest->obj_end(dpp);
+  if (end.get_cur_part_id() == 0) { // not multipart
+    ldpp_dout(dpp, 20) << "object does not have a multipart manifest" << dendl;
+    return -ERR_INVALID_PART;
+  }
+  if (parts_count) {
+    *parts_count = end.get_cur_part_id() - 1;
+  }
+  ldpp_dout(dpp, 20) << "seeking to part #" << part_num
+      << " in the object manifest" << dendl;
+  RGWObjManifest::obj_iterator iter = manifest->obj_find_part(dpp, part_num);
+  if (iter == end) { // part number not found
+    ldpp_dout(dpp, 20) << "failed to find part #" << part_num
+        << " in the object manifest" << dendl;
+    return -ERR_INVALID_PART;
+  }
+  auto head_obj = iter.get_location().get_head_obj();
+  if (!head_obj) { // iterator points to a tail object
+    ldpp_dout(dpp, 20) << "object manifest for part #" << part_num
+        << " points to a tail object" << dendl;
+    return -ERR_INVALID_PART;
+  }
+  const auto part_offset = iter.get_ofs();
+
+  // read the part's head object
+  if (prefetch) {
+    rctx->set_prefetch_data(*head_obj);
+  }
+  RGWObjStateManifest* sm = nullptr;
+  constexpr bool follow_olh = false; // parts aren't versioned
+  int r = store->get_obj_state(dpp, rctx, bucket_info, *head_obj,
+                               &sm, follow_olh, y);
+  if (r < 0) {
+    return r;
+  }
+  *pstate = &sm->state;
+
+  // if the part has its own manifest, use it directly
+  if (sm->manifest) {
+    *pmanifest = &*sm->manifest;
+    return 0;
+  }
+
+  // create a new manifest for just this part
+  sm->manifest.emplace();
+  RGWObjManifest& part_manifest = *sm->manifest;
+  part_manifest.set_multipart_part_rule(iter.get_stripe_size(), part_num);
+
+  if (auto& prefix = iter.get_cur_override_prefix(); !prefix.empty()) {
+    // the part was reuploaded with a different prefix
+    part_manifest.set_prefix(prefix);
+  } else {
+    part_manifest.set_prefix(manifest->get_prefix());
+  }
+
+  RGWObjManifest::generator gen;
+  gen.create_begin(store->ctx(), &part_manifest,
+                   manifest->get_head_placement_rule(),
+                   &manifest->get_tail_placement().placement_rule,
+                   head_obj->bucket, *head_obj);
+
+  // copy each of the part's stripes into the new manifest. the final call to
+  // create_next() uses the starting offset of the next part
+  do {
+    ++iter;
+    gen.create_next(iter.get_ofs() - part_offset);
+  } while (iter.get_cur_part_id() == part_num);
+
+  // update the object size
+  sm->state.size = part_manifest.get_obj_size();
+
+  *pmanifest = &part_manifest;
+  return 0;
+}
+
 int RGWRados::Object::Read::prepare(optional_yield y, const DoutPrefixProvider *dpp)
 {
   RGWRados *store = source->get_store();
   CephContext *cct = store->ctx();
+  RGWObjectCtx& obj_ctx = source->get_ctx();
 
   bufferlist etag;
 
   map<string, bufferlist>::iterator iter;
 
+  bool part_prefetch = false;
+  if (params.part_num) {
+    // prefetch from the part's head object instead of the multipart head
+    auto sm = obj_ctx.get_state(source->get_obj());
+    part_prefetch = std::exchange(sm->state.prefetch_data, false);
+  }
+
   RGWObjState *astate;
   RGWObjManifest *manifest = nullptr;
   int r = source->get_state(dpp, &astate, &manifest, true, y);
@@ -6546,7 +6636,27 @@ int RGWRados::Object::Read::prepare(optional_yield y, const DoutPrefixProvider *
     return -ENOENT;
   }
 
-  const RGWBucketInfo& bucket_info = source->get_bucket_info();
+  RGWBucketInfo& bucket_info = source->get_bucket_info();
+
+  if (params.part_num) {
+    // use the manifest to redirect to the requested part number
+    if (!manifest) {
+      return -ERR_INVALID_PART;
+    }
+    r = get_part_obj_state(dpp, y, store, bucket_info, &source->get_ctx(),
+                           manifest, *params.part_num, params.parts_count,
+                           part_prefetch, &astate, &manifest);
+    if (r < 0) {
+      ldpp_dout(dpp, 4) << "failed to read part #" << *params.part_num
+          << ": " << cpp_strerror(r) << dendl;
+      return -ERR_INVALID_PART;
+    }
+    if (!astate->exists) {
+      ldpp_dout(dpp, 4) << "part #" << *params.part_num
+          << " does not exist" << dendl;
+      return -ERR_INVALID_PART;
+    }
+  }
 
   state.obj = astate->obj;
   store->obj_to_raw(bucket_info.placement_rule, state.obj, &state.head_obj);
index d8cba7b9b4edda4589aff9a9c9f3a4e77fa0816d..89e4576a4788eb3de3860af544c7707cf7b4bae3 100644 (file)
@@ -769,6 +769,8 @@ public:
         std::map<std::string, bufferlist> *attrs;
         rgw_obj *target_obj;
        uint64_t *epoch;
+        int* part_num = nullptr;
+        int* parts_count = nullptr;
 
         Params() : lastmod(nullptr), obj_size(nullptr), attrs(nullptr),
                   target_obj(nullptr), epoch(nullptr)
index ed6e2525ac2b818c311242bc676e7ff351d57e62..e619e968114812bead7cc6c1070a8f597d89987f 100644 (file)
@@ -1946,6 +1946,8 @@ int RadosObject::RadosReadOp::prepare(optional_yield y, const DoutPrefixProvider
   parent_op.conds.if_nomatch = params.if_nomatch;
   parent_op.params.lastmod = params.lastmod;
   parent_op.params.target_obj = params.target_obj;
+  parent_op.params.part_num = params.part_num;
+  parent_op.params.parts_count = params.parts_count;
   parent_op.params.obj_size = &obj_size;
   parent_op.params.attrs = &source->get_attrs();
 
index 3accf03e496896286155ab62f17760b53577d6c4..1a9cd11c6eff53698b88521e464dacaadfff6fc0 100644 (file)
@@ -2281,6 +2281,11 @@ void RGWGetObj::execute(optional_yield y)
   read_op->params.if_match = if_match;
   read_op->params.if_nomatch = if_nomatch;
   read_op->params.lastmod = &lastmod;
+  if (multipart_part_num) {
+    read_op->params.part_num = &*multipart_part_num;
+    multipart_parts_count.emplace(0);
+    read_op->params.parts_count = &*multipart_parts_count;
+  }
 
   op_ret = read_op->prepare(s->yield, this);
   if (op_ret < 0)
index 9b099be4ac2c6983c39b32ae685cac376ac05fd5..57c7c20d6e15fecaf7a251a45c459475447dfc92 100644 (file)
@@ -843,6 +843,12 @@ class Object {
         const char* if_nomatch{nullptr};
         ceph::real_time* lastmod{nullptr};
         rgw_obj* target_obj{nullptr}; // XXX dang remove?
+
+        /// If non-null, read data/attributes from the given multipart part.
+        int* part_num{nullptr};
+        /// If part_num is specified, the total number of multipart parts is
+        /// written to this output parameter.
+        int* parts_count{nullptr};
       } params;
 
       virtual ~ReadOp() = default;