Skip to content

Commit e342927

Browse files
authored
Merge pull request #50148 from cbodley/wip-58750
rgw: Head/GetObject support partNumber Reviewed-by: Daniel Gryniewicz <dang@redhat.com>
2 parents ac7e285 + 338440a commit e342927

11 files changed

Lines changed: 257 additions & 38 deletions

PendingReleaseNotes

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@
6464
of POOL_APP_NOT_ENABLED health warning for that pool.
6565
The user might temporarily mute this warning using
6666
``ceph health mute POOL_APP_NOT_ENABLED``.
67+
CephFS: Disallow delegating preallocated inode ranges to clients. Config
68+
`mds_client_delegate_inos_pct` defaults to 0 which disables async dirops
69+
in the kclient.
70+
* S3 Get/HeadObject now support query parameter `partNumber` to read a specific
71+
part of a completed multipart upload.
6772

6873
>=18.0.0
6974

src/rgw/driver/rados/rgw_obj_manifest.cc

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,27 @@ bool RGWObjManifest::get_rule(uint64_t ofs, RGWObjManifestRule *rule)
197197
return true;
198198
}
199199

200+
auto RGWObjManifest::obj_find_part(const DoutPrefixProvider *dpp,
201+
int part_num) const
202+
-> obj_iterator
203+
{
204+
const obj_iterator end = obj_end(dpp);
205+
if (end.get_cur_part_id() == 0) { // not mulitipart
206+
return end;
207+
}
208+
209+
// linear search over parts/stripes
210+
for (obj_iterator i = obj_begin(dpp); i != end; ++i) {
211+
if (i.get_cur_part_id() == part_num) {
212+
return i;
213+
}
214+
if (i.get_cur_part_id() > part_num) {
215+
return end;
216+
}
217+
}
218+
return end;
219+
}
220+
200221
int RGWObjManifest::generator::create_begin(CephContext *cct, RGWObjManifest *_m,
201222
const rgw_placement_rule& head_placement_rule,
202223
const rgw_placement_rule *tail_placement_rule,

src/rgw/driver/rados/rgw_obj_manifest.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#pragma once
2222

23+
#include <optional>
2324
#include "rgw_zone_types.h"
2425
#include "rgw_bucket_types.h"
2526
#include "rgw_obj_types.h"
@@ -57,6 +58,14 @@ class rgw_obj_select {
5758
}
5859
}
5960

61+
std::optional<rgw_obj> get_head_obj() const {
62+
if (is_raw) {
63+
return std::nullopt;
64+
} else {
65+
return obj;
66+
}
67+
}
68+
6069
rgw_raw_obj get_raw_obj(const RGWZoneGroup& zonegroup, const RGWZoneParams& zone_params) const;
6170
rgw_raw_obj get_raw_obj(RGWRados* store) const;
6271

@@ -547,6 +556,10 @@ class RGWObjManifest {
547556
return ofs;
548557
}
549558

559+
const std::string& get_cur_override_prefix() const {
560+
return cur_override_prefix;
561+
}
562+
550563
int get_cur_part_id() const {
551564
return cur_part_id;
552565
}
@@ -582,6 +595,8 @@ class RGWObjManifest {
582595
obj_iterator obj_find(const DoutPrefixProvider *dpp, uint64_t ofs) const {
583596
return obj_iterator{dpp, this, std::min(ofs, obj_size)};
584597
}
598+
// return an iterator to the beginning of the given part number
599+
obj_iterator obj_find_part(const DoutPrefixProvider *dpp, int part_num) const;
585600

586601
/*
587602
* simple object generator. Using a simple single rule manifest.

src/rgw/driver/rados/rgw_putobj_processor.cc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -595,9 +595,10 @@ int AppendObjectProcessor::process_first_chunk(bufferlist &&data, rgw::sal::Data
595595

596596
int AppendObjectProcessor::prepare(optional_yield y)
597597
{
598-
RGWObjState *astate;
598+
RGWObjState *astate = nullptr;
599+
constexpr bool follow_olh = true;
599600
int r = store->get_obj_state(dpp, &obj_ctx, bucket_info, head_obj,
600-
&astate, &cur_manifest, y);
601+
&astate, &cur_manifest, follow_olh, y);
601602
if (r < 0) {
602603
return r;
603604
}

src/rgw/driver/rados/rgw_rados.cc

Lines changed: 162 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4658,7 +4658,9 @@ int RGWRados::copy_obj(RGWObjectCtx& obj_ctx,
46584658
RGWObjState *astate = NULL;
46594659
RGWObjManifest *amanifest = nullptr;
46604660

4661-
ret = get_obj_state(dpp, &obj_ctx, src_bucket_info, src_obj, &astate, &amanifest, y);
4661+
constexpr bool follow_olh = true;
4662+
ret = get_obj_state(dpp, &obj_ctx, src_bucket_info, src_obj,
4663+
&astate, &amanifest, follow_olh, y);
46624664
if (ret < 0) {
46634665
return ret;
46644666
}
@@ -5885,48 +5887,38 @@ static bool has_olh_tag(map<string, bufferlist>& attrs)
58855887
int RGWRados::get_olh_target_state(const DoutPrefixProvider *dpp, RGWObjectCtx&
58865888
obj_ctx, RGWBucketInfo& bucket_info,
58875889
const rgw_obj& obj, RGWObjState *olh_state,
5888-
RGWObjState **target_state,
5889-
RGWObjManifest **target_manifest, optional_yield y)
5890+
RGWObjStateManifest **psm, optional_yield y)
58905891
{
58915892
ceph_assert(olh_state->is_olh);
58925893

58935894
rgw_obj target;
5894-
int r = RGWRados::follow_olh(dpp, bucket_info, obj_ctx, olh_state, obj, &target, y); /* might return -EAGAIN */
5895+
int r = RGWRados::follow_olh(dpp, bucket_info, obj_ctx, olh_state,
5896+
obj, &target, y); /* might return -EAGAIN */
58955897
if (r < 0) {
58965898
return r;
58975899
}
58985900

5899-
r = get_obj_state(dpp, &obj_ctx, bucket_info, target, target_state,
5900-
target_manifest, false, y);
5901-
if (r < 0) {
5902-
return r;
5903-
}
5904-
5905-
return 0;
5901+
return get_obj_state(dpp, &obj_ctx, bucket_info, target, psm, false, y);
59065902
}
59075903

59085904
int RGWRados::get_obj_state_impl(const DoutPrefixProvider *dpp, RGWObjectCtx *octx,
5909-
RGWBucketInfo& bucket_info, const rgw_obj& obj,
5910-
RGWObjState **state, RGWObjManifest** manifest,
5911-
bool follow_olh, optional_yield y, bool assume_noent)
5905+
RGWBucketInfo& bucket_info, const rgw_obj& obj,
5906+
RGWObjStateManifest** psm, bool follow_olh,
5907+
optional_yield y, bool assume_noent)
59125908
{
59135909
if (obj.empty()) {
59145910
return -EINVAL;
59155911
}
59165912

59175913
bool need_follow_olh = follow_olh && obj.key.instance.empty();
5918-
*manifest = nullptr;
59195914

59205915
RGWObjStateManifest *sm = octx->get_state(obj);
59215916
RGWObjState *s = &(sm->state);
59225917
ldpp_dout(dpp, 20) << "get_obj_state: octx=" << (void *)octx << " obj=" << obj << " state=" << (void *)s << " s->prefetch_data=" << s->prefetch_data << dendl;
5923-
*state = s;
5924-
if (sm->manifest) {
5925-
*manifest = &(*sm->manifest);
5926-
}
5918+
*psm = sm;
59275919
if (s->has_attrs) {
59285920
if (s->is_olh && need_follow_olh) {
5929-
return get_olh_target_state(dpp, *octx, bucket_info, obj, s, state, manifest, y);
5921+
return get_olh_target_state(dpp, *octx, bucket_info, obj, s, psm, y);
59305922
}
59315923
return 0;
59325924
}
@@ -6018,7 +6010,6 @@ int RGWRados::get_obj_state_impl(const DoutPrefixProvider *dpp, RGWObjectCtx *oc
60186010
ldpp_dout(dpp, 0) << "ERROR: couldn't decode manifest" << dendl;
60196011
return -EIO;
60206012
}
6021-
*manifest = &(*sm->manifest);
60226013
ldpp_dout(dpp, 10) << "manifest: total_size = " << sm->manifest->get_obj_size() << dendl;
60236014
if (cct->_conf->subsys.should_gather<ceph_subsys_rgw, 20>() && \
60246015
sm->manifest->has_explicit_objs()) {
@@ -6078,7 +6069,7 @@ int RGWRados::get_obj_state_impl(const DoutPrefixProvider *dpp, RGWObjectCtx *oc
60786069
ldpp_dout(dpp, 20) << __func__ << ": setting s->olh_tag to " << string(s->olh_tag.c_str(), s->olh_tag.length()) << dendl;
60796070

60806071
if (need_follow_olh) {
6081-
return get_olh_target_state(dpp, *octx, bucket_info, obj, s, state, manifest, y);
6072+
return get_olh_target_state(dpp, *octx, bucket_info, obj, s, psm, y);
60826073
} else if (obj.key.have_null_instance() && !sm->manifest) {
60836074
// read null version, and the head object only have olh info
60846075
s->exists = false;
@@ -6089,18 +6080,45 @@ int RGWRados::get_obj_state_impl(const DoutPrefixProvider *dpp, RGWObjectCtx *oc
60896080
return 0;
60906081
}
60916082

6092-
int RGWRados::get_obj_state(const DoutPrefixProvider *dpp, RGWObjectCtx *octx, RGWBucketInfo& bucket_info, const rgw_obj& obj, RGWObjState **state, RGWObjManifest** manifest,
6093-
bool follow_olh, optional_yield y, bool assume_noent)
6083+
int RGWRados::get_obj_state(const DoutPrefixProvider *dpp, RGWObjectCtx *octx,
6084+
RGWBucketInfo& bucket_info, const rgw_obj& obj,
6085+
RGWObjStateManifest** psm, bool follow_olh,
6086+
optional_yield y, bool assume_noent)
60946087
{
60956088
int ret;
60966089

60976090
do {
6098-
ret = get_obj_state_impl(dpp, octx, bucket_info, obj, state, manifest, follow_olh, y, assume_noent);
6091+
ret = get_obj_state_impl(dpp, octx, bucket_info, obj, psm,
6092+
follow_olh, y, assume_noent);
60996093
} while (ret == -EAGAIN);
61006094

61016095
return ret;
61026096
}
61036097

6098+
int RGWRados::get_obj_state(const DoutPrefixProvider *dpp, RGWObjectCtx *rctx,
6099+
RGWBucketInfo& bucket_info, const rgw_obj& obj,
6100+
RGWObjState** pstate, RGWObjManifest** pmanifest,
6101+
bool follow_olh, optional_yield y, bool assume_noent)
6102+
{
6103+
RGWObjStateManifest* sm = nullptr;
6104+
int r = get_obj_state(dpp, rctx, bucket_info, obj, &sm,
6105+
follow_olh, y, assume_noent);
6106+
if (r < 0) {
6107+
return r;
6108+
}
6109+
if (pstate) {
6110+
*pstate = &sm->state;
6111+
}
6112+
if (pmanifest) {
6113+
if (sm->manifest) {
6114+
*pmanifest = &(*sm->manifest);
6115+
} else {
6116+
*pmanifest = nullptr;
6117+
}
6118+
}
6119+
return 0;
6120+
}
6121+
61046122
int RGWRados::Object::get_manifest(const DoutPrefixProvider *dpp, RGWObjManifest **pmanifest, optional_yield y)
61056123
{
61066124
RGWObjState *astate;
@@ -6509,15 +6527,108 @@ int RGWRados::set_attrs(const DoutPrefixProvider *dpp, RGWObjectCtx* octx, RGWBu
65096527
return 0;
65106528
}
65116529

6530+
static int get_part_obj_state(const DoutPrefixProvider* dpp, optional_yield y,
6531+
RGWRados* store, RGWBucketInfo& bucket_info,
6532+
RGWObjectCtx* rctx, RGWObjManifest* manifest,
6533+
int part_num, int* parts_count, bool prefetch,
6534+
RGWObjState** pstate, RGWObjManifest** pmanifest)
6535+
{
6536+
if (!manifest) {
6537+
return -ERR_INVALID_PART;
6538+
}
6539+
// navigate to the requested part in the manifest
6540+
RGWObjManifest::obj_iterator end = manifest->obj_end(dpp);
6541+
if (end.get_cur_part_id() == 0) { // not multipart
6542+
ldpp_dout(dpp, 20) << "object does not have a multipart manifest" << dendl;
6543+
return -ERR_INVALID_PART;
6544+
}
6545+
if (parts_count) {
6546+
*parts_count = end.get_cur_part_id() - 1;
6547+
}
6548+
ldpp_dout(dpp, 20) << "seeking to part #" << part_num
6549+
<< " in the object manifest" << dendl;
6550+
RGWObjManifest::obj_iterator iter = manifest->obj_find_part(dpp, part_num);
6551+
if (iter == end) { // part number not found
6552+
ldpp_dout(dpp, 20) << "failed to find part #" << part_num
6553+
<< " in the object manifest" << dendl;
6554+
return -ERR_INVALID_PART;
6555+
}
6556+
auto head_obj = iter.get_location().get_head_obj();
6557+
if (!head_obj) { // iterator points to a tail object
6558+
ldpp_dout(dpp, 20) << "object manifest for part #" << part_num
6559+
<< " points to a tail object" << dendl;
6560+
return -ERR_INVALID_PART;
6561+
}
6562+
const auto part_offset = iter.get_ofs();
6563+
6564+
// read the part's head object
6565+
if (prefetch) {
6566+
rctx->set_prefetch_data(*head_obj);
6567+
}
6568+
RGWObjStateManifest* sm = nullptr;
6569+
constexpr bool follow_olh = false; // parts aren't versioned
6570+
int r = store->get_obj_state(dpp, rctx, bucket_info, *head_obj,
6571+
&sm, follow_olh, y);
6572+
if (r < 0) {
6573+
return r;
6574+
}
6575+
*pstate = &sm->state;
6576+
6577+
// if the part has its own manifest, use it directly
6578+
if (sm->manifest) {
6579+
*pmanifest = &*sm->manifest;
6580+
return 0;
6581+
}
6582+
6583+
// create a new manifest for just this part
6584+
sm->manifest.emplace();
6585+
RGWObjManifest& part_manifest = *sm->manifest;
6586+
part_manifest.set_multipart_part_rule(iter.get_stripe_size(), part_num);
6587+
6588+
if (auto& prefix = iter.get_cur_override_prefix(); !prefix.empty()) {
6589+
// the part was reuploaded with a different prefix
6590+
part_manifest.set_prefix(prefix);
6591+
} else {
6592+
part_manifest.set_prefix(manifest->get_prefix());
6593+
}
6594+
6595+
RGWObjManifest::generator gen;
6596+
gen.create_begin(store->ctx(), &part_manifest,
6597+
manifest->get_head_placement_rule(),
6598+
&manifest->get_tail_placement().placement_rule,
6599+
head_obj->bucket, *head_obj);
6600+
6601+
// copy each of the part's stripes into the new manifest. the final call to
6602+
// create_next() uses the starting offset of the next part
6603+
do {
6604+
++iter;
6605+
gen.create_next(iter.get_ofs() - part_offset);
6606+
} while (iter.get_cur_part_id() == part_num);
6607+
6608+
// update the object size
6609+
sm->state.size = part_manifest.get_obj_size();
6610+
6611+
*pmanifest = &part_manifest;
6612+
return 0;
6613+
}
6614+
65126615
int RGWRados::Object::Read::prepare(optional_yield y, const DoutPrefixProvider *dpp)
65136616
{
65146617
RGWRados *store = source->get_store();
65156618
CephContext *cct = store->ctx();
6619+
RGWObjectCtx& obj_ctx = source->get_ctx();
65166620

65176621
bufferlist etag;
65186622

65196623
map<string, bufferlist>::iterator iter;
65206624

6625+
bool part_prefetch = false;
6626+
if (params.part_num) {
6627+
// prefetch from the part's head object instead of the multipart head
6628+
auto sm = obj_ctx.get_state(source->get_obj());
6629+
part_prefetch = std::exchange(sm->state.prefetch_data, false);
6630+
}
6631+
65216632
RGWObjState *astate;
65226633
RGWObjManifest *manifest = nullptr;
65236634
int r = source->get_state(dpp, &astate, &manifest, true, y);
@@ -6528,7 +6639,32 @@ int RGWRados::Object::Read::prepare(optional_yield y, const DoutPrefixProvider *
65286639
return -ENOENT;
65296640
}
65306641

6531-
const RGWBucketInfo& bucket_info = source->get_bucket_info();
6642+
RGWBucketInfo& bucket_info = source->get_bucket_info();
6643+
6644+
if (params.part_num) {
6645+
int parts_count = 0;
6646+
// use the manifest to redirect to the requested part number
6647+
r = get_part_obj_state(dpp, y, store, bucket_info, &source->get_ctx(),
6648+
manifest, *params.part_num, &parts_count,
6649+
part_prefetch, &astate, &manifest);
6650+
if (r == -ERR_INVALID_PART && *params.part_num == 1) {
6651+
// for non-multipart uploads, treat requests for the first part as a
6652+
// request for the entire range. this behavior is expected by the java
6653+
// sdk's TransferManager.download()
6654+
ldpp_dout(dpp, 4) << "requested part #" << *params.part_num
6655+
<< ": " << cpp_strerror(r) << dendl;
6656+
} else if (r < 0) {
6657+
ldpp_dout(dpp, 4) << "failed to read part #" << *params.part_num
6658+
<< ": " << cpp_strerror(r) << dendl;
6659+
return -ERR_INVALID_PART;
6660+
} else if (!astate->exists) {
6661+
ldpp_dout(dpp, 4) << "part #" << *params.part_num
6662+
<< " does not exist" << dendl;
6663+
return -ERR_INVALID_PART;
6664+
} else {
6665+
params.parts_count = parts_count;
6666+
}
6667+
}
65326668

65336669
state.obj = astate->obj;
65346670
store->obj_to_raw(bucket_info.placement_rule, state.obj, &state.head_obj);

0 commit comments

Comments
 (0)