Skip to content

Commit

Permalink
rgw: enhance functionality of radosgw-admin object reindex ...
Browse files Browse the repository at this point in the history
Adds ability to handle versioned buckets to reindexing. Also, it
completes the addition of the index entries without the subsequent
need to list the bucket.

Signed-off-by: J. Eric Ivancich <[email protected]>
  • Loading branch information
ivancich committed Aug 9, 2023
1 parent 535b87f commit f731570
Show file tree
Hide file tree
Showing 4 changed files with 282 additions and 27 deletions.
3 changes: 3 additions & 0 deletions doc/man/8/radosgw-admin.rst
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,9 @@ which are as follows:
:command:`object rewrite`
Rewrite the specified object.

:command:`object reindex`
Add an object to its bucket's index. Used rarely for emergency repairs.

:command:`objects expire`
Run expired objects cleanup.

Expand Down
272 changes: 253 additions & 19 deletions src/rgw/driver/rados/rgw_rados.cc
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,32 @@ static string default_storage_extra_pool_suffix = "rgw.buckets.non-ec";
static RGWObjCategory main_category = RGWObjCategory::Main;
#define RGW_USAGE_OBJ_PREFIX "usage."

// reads attribute as std::string
static inline void read_attr(std::map<std::string, bufferlist>& attrs,
const std::string& attr_name,
std::string& dest,
bool* found = nullptr) {
auto i = attrs.find(attr_name);
if (i != attrs.end()) {
dest = rgw_bl_str(i->second);
}
if (found) *found = i != attrs.end();
}

// reads attribute as bufferlist
static inline void read_attr(std::map<std::string, bufferlist>& attrs,
const std::string& attr_name,
bufferlist& dest,
bool* found = nullptr) {
auto i = attrs.find(attr_name);
if (i != attrs.cend()) {
dest = i->second; // copy
}
if (found) {
*found = i != attrs.end();
}
}

rgw_raw_obj rgw_obj_select::get_raw_obj(RGWRados* store) const
{
if (!is_raw) {
Expand Down Expand Up @@ -3620,33 +3646,235 @@ int RGWRados::rewrite_obj(RGWBucketInfo& dest_bucket_info, const rgw_obj& obj, c
attrset, 0, real_time(), NULL, dpp, y);
}

int RGWRados::reindex_obj(const RGWBucketInfo& bucket_info,
const rgw_obj& obj,

int RGWRados::reindex_obj(rgw::sal::Driver* driver,
RGWBucketInfo& bucket_info,
const rgw_obj& head_obj,
const DoutPrefixProvider* dpp,
optional_yield y)
{
if (bucket_info.versioned()) {
ldpp_dout(dpp, 10) << "WARNING: " << __func__ <<
": cannot process versioned bucket \"" <<
bucket_info.bucket.get_key() << "\"" <<
dendl;
return -ENOTSUP;
// used for trimming pending entries; max value means all versions trimmed
const uint64_t max_ver = std::numeric_limits<uint64_t>::max();
// used for linking an olh
const std::string empty_op_tag = "";

int ret;
RGWObjectCtx obj_ctx(driver);

// aids in printing out name of bucket/object
auto p = [](const rgw_obj& o) -> std::string {
std::stringstream ss;
ss << o.bucket.name << ':' << o.key;
return ss.str();
};

// since the code for linking a versioned object and adding a delete
// marker is so similar, we bring the common OLH-handling code into
// this lambda
auto link_helper = [&](const bool is_delete_marker,
rgw_bucket_dir_entry_meta& meta,
const std::string& log_tag) -> int {
int ret = 0;

// convert the head object name into the OLH object by removing
// the instance info
rgw_obj olh_obj = head_obj;
olh_obj.key.instance.clear();

RGWObjState* olh_state { nullptr };
RGWObjManifest* olh_manifest { nullptr }; // we don't use, but must send in
ret = get_obj_state(dpp, &obj_ctx, bucket_info, olh_obj,
&olh_state, &olh_manifest,
false, // don't follow olh
y);
if (ret < 0) {
ldpp_dout(dpp, 0) << "ERROR: " << __func__ <<
": during " << log_tag << " get_obj_state on OLH object " <<
olh_obj.key << " returned: " << cpp_strerror(-ret) << dendl;
return ret;
}

// In order to update the data in the OLH object we're calling
// bucket_index_link_olh followed by bucket_index_trim_olh_log
// since that churns metadata less than a call to set_olh
// would. bucket_index_link_olh does leave entries in the OLH
// object's pending log since normally OLH updates are paired with
// other ops, but we remove such entries below.
ret = bucket_index_link_olh(dpp,
bucket_info,
*olh_state,
head_obj,
is_delete_marker,
empty_op_tag,
&meta,
0, // zero olh_epoch means calculated in CLS
ceph::real_clock::zero(), // unmod_since
true, // high_precision_time
y,
nullptr, // zones trace
false); // log data change
if (ret < 0) {
ldpp_dout(dpp, 0) << "ERROR: " << __func__ <<
": during " << log_tag << " set_index_link_olh returned: " <<
cpp_strerror(-ret) << dendl;
return ret;
}

// bucket_)index_link_olh leaves a pending_log entry in the OLH;
// this trims it out
ret = bucket_index_trim_olh_log(dpp,
bucket_info,
*olh_state,
head_obj,
max_ver,
y);
if (ret < 0) {
ldpp_dout(dpp, 0) << "ERROR: " << __func__ <<
": during " << log_tag <<
" bucket_index_trim_olh_log returned: " <<
cpp_strerror(-ret) << dendl;
return ret;
}

return 0;
}; // link_helper lambda

librados::IoCtx head_obj_ctx;
ret = get_obj_head_ioctx(dpp, bucket_info, head_obj, &head_obj_ctx);
if (ret < 0) {
ldpp_dout(dpp, 0) << "ERROR: " << __func__ <<
": get_obj_head_ioctx for " << p(head_obj) << " returned: " <<
cpp_strerror(-ret) << dendl;
return ret;
}

Bucket target(this, bucket_info);
RGWRados::Bucket::UpdateIndex update_idx(&target, obj);
const std::string* no_write_tag = nullptr;
const int64_t pool_id = head_obj_ctx.get_id();
const bool is_versioned = bucket_info.versioned();
const bool has_instance = ! head_obj.key.instance.empty();

ldpp_dout(dpp, 20) << "INFO: " << __func__ << ": reindexing " <<
p(head_obj) << dendl;

RGWObjState *head_state { nullptr };
RGWObjManifest *head_manifest { nullptr };

int ret = update_idx.prepare(dpp, RGWModifyOp::CLS_RGW_OP_ADD, no_write_tag, y);
// if head_obj does not exist does not return -ENOENT but instead
// sets head_state->exists to false
ret = get_obj_state(dpp, &obj_ctx, bucket_info, head_obj,
&head_state, &head_manifest,
false, // don't follow olh
y);
if (ret < 0) {
ldpp_dout(dpp, 0) << "ERROR: " << __func__ <<
": update index prepare for \"" << obj << "\" returned: " <<
": get_obj_state on " << p(head_obj) << " returned: " <<
cpp_strerror(-ret) << dendl;
return ret;
}

return 0;
}
if (! head_state->exists && is_versioned && has_instance) {
// head object does not exist if it's a delete marker; handle here
// and return
ldpp_dout(dpp, 20) << "INFO: " << __func__ << ": indexing " <<
p(head_obj) << " as delete marker" << dendl;

// empty metadata object is fine for delete marker
rgw_bucket_dir_entry_meta meta;

return link_helper(true, meta, "set delete marker");
} else if (ret < 0) {
ldpp_dout(dpp, 0) << "ERROR: " << __func__ <<
": unable to complete stat of " << p(head_obj) << "; returned: " <<
cpp_strerror(-ret) << dendl;
return ret;
}

// data we'll pull from head object xattrs
std::string etag;
std::string content_type;
std::string storage_class;
bufferlist acl_bl;
bool found_olh_info { false };
bufferlist olh_info_bl;
bool appendable { false };
bufferlist part_num_bl;

rgw::sal::Attrs& attr_set = head_state->attrset;
read_attr(attr_set, RGW_ATTR_ETAG, etag);
read_attr(attr_set, RGW_ATTR_CONTENT_TYPE, content_type);
read_attr(attr_set, RGW_ATTR_STORAGE_CLASS, storage_class);
read_attr(attr_set, RGW_ATTR_ACL, acl_bl);
read_attr(attr_set, RGW_ATTR_OLH_INFO, olh_info_bl, &found_olh_info);
read_attr(attr_set, RGW_ATTR_APPEND_PART_NUM, part_num_bl, &appendable);

// check for a pure OLH object and if so exit early
if (found_olh_info) {
try {
auto iter = olh_info_bl.cbegin();
RGWOLHInfo info;
decode(info, iter);
if (! info.target.key.instance.empty()) {
// since there is a listed instance this appears to be a pure
// OLH (i.e., no data); we won't index as we index actual
// objects with data and set the OLH then
ldpp_dout(dpp, 20) << "INFO: " << __func__ << ": " <<
p(head_obj) << " appears to be a pure OLH object; ignoring" << dendl;
return 0;
}
} catch (buffer::error& err) {
ldpp_dout(dpp, 0) << "ERROR: " << __func__ <<
": unable to decode OLH info for " << p(head_obj) << dendl;
return -EIO;
}
}

Bucket bkt(this, bucket_info);
RGWRados::Bucket::UpdateIndex update_idx(&bkt, head_obj);

// note: we can skip calling prepare() since there's no transaction
// and we don't specify a write tag (i.e., transaction tag)
ret = update_idx.complete(dpp,
pool_id,
0, // bucket index epoch
head_state->size,
head_state->accounted_size,
head_state->mtime,
etag,
content_type,
storage_class,
&acl_bl,
RGWObjCategory::Main, // RGWObjCategory category,
nullptr, // remove_objs list
y,
nullptr, // user data string
appendable);
if (ret < 0) {
ldpp_dout(dpp, 0) << "ERROR: " << __func__ <<
": update index complete for " << p(head_obj) << " returned: " <<
cpp_strerror(-ret) << dendl;
return ret;
}

if (bucket_info.versioned()) {
ldpp_dout(dpp, 20) << "INFO: " << __func__ << ": since " <<
bucket_info.bucket << " appears to be versioned, setting OLH for " <<
p(head_obj) << dendl;

// write OLH and instance entries
rgw_bucket_dir_entry_meta meta;
meta.category = RGWObjCategory::Main;
meta.mtime = head_state->mtime;
meta.size = head_state->size;
meta.accounted_size = head_state->accounted_size;
meta.etag = etag;
meta.content_type = content_type;
meta.appendable = appendable;

ret = link_helper(false, meta, "linking version");
} // if bucket is versioned

return ret;
} // RGWRados::reindex_obj


struct obj_time_weight {
real_time mtime;
Expand Down Expand Up @@ -7487,7 +7715,7 @@ int RGWRados::repair_olh(const DoutPrefixProvider *dpp, RGWObjState* state, cons
return r;
}
return 0;
}
} // RGWRados::repair_olh

int RGWRados::bucket_index_trim_olh_log(const DoutPrefixProvider *dpp,
RGWBucketInfo& bucket_info,
Expand Down Expand Up @@ -7703,7 +7931,7 @@ int RGWRados::apply_olh_log(const DoutPrefixProvider *dpp,
/* update olh object */
r = rgw_rados_operate(dpp, ref.pool.ioctx(), ref.obj.oid, &op, y);
if (r < 0) {
ldpp_dout(dpp, 0) << "ERROR: could not apply olh update, r=" << r << dendl;
ldpp_dout(dpp, 0) << "ERROR: " << __func__ << ": could not apply olh update to oid \"" << ref.obj.oid << "\", r=" << r << dendl;
return r;
}

Expand Down Expand Up @@ -7822,7 +8050,8 @@ int RGWRados::set_olh(const DoutPrefixProvider *dpp, RGWObjectCtx& obj_ctx,
const rgw_obj& target_obj, bool delete_marker,
rgw_bucket_dir_entry_meta *meta,
uint64_t olh_epoch, real_time unmod_since, bool high_precision_time,
optional_yield y, rgw_zone_set *zones_trace, bool log_data_change)
optional_yield y, rgw_zone_set *zones_trace, bool log_data_change,
bool skip_olh_obj_update)
{
string op_tag;

Expand Down Expand Up @@ -7884,6 +8113,11 @@ int RGWRados::set_olh(const DoutPrefixProvider *dpp, RGWObjectCtx& obj_ctx,
return -EIO;
}

// exit early if we're skipping the olh update and just updating the index
if (skip_olh_obj_update) {
return 0;
}

ret = update_olh(dpp, obj_ctx, state, bucket_info, olh_obj, y);
if (ret == -ECANCELED) { /* already did what we needed, no need to retry, raced with another user */
ret = 0;
Expand Down Expand Up @@ -8053,7 +8287,7 @@ int RGWRados::remove_olh_pending_entries(const DoutPrefixProvider *dpp, const RG
return 0;
}
if (r < 0) {
ldpp_dout(dpp, 0) << "ERROR: could not apply olh update, r=" << r << dendl;
ldpp_dout(dpp, 0) << "ERROR: " << __func__ << ": could not apply olh update to oid \"" << ref.obj.oid << "\", r=" << r << dendl;
return r;
}
}
Expand Down
22 changes: 17 additions & 5 deletions src/rgw/driver/rados/rgw_rados.h
Original file line number Diff line number Diff line change
Expand Up @@ -1091,7 +1091,8 @@ class RGWRados
D3nDataCache* d3n_data_cache{nullptr};

int rewrite_obj(RGWBucketInfo& dest_bucket_info, const rgw_obj& obj, const DoutPrefixProvider *dpp, optional_yield y);
int reindex_obj(const RGWBucketInfo& dest_bucket_info,
int reindex_obj(rgw::sal::Driver* driver,
RGWBucketInfo& dest_bucket_info,
const rgw_obj& obj,
const DoutPrefixProvider* dpp,
optional_yield y);
Expand Down Expand Up @@ -1339,17 +1340,28 @@ class RGWRados
int apply_olh_log(const DoutPrefixProvider *dpp, RGWObjectCtx& obj_ctx, RGWObjState& obj_state, RGWBucketInfo& bucket_info, const rgw_obj& obj,
bufferlist& obj_tag, std::map<uint64_t, std::vector<rgw_bucket_olh_log_entry> >& log,
uint64_t *plast_ver, optional_yield y, rgw_zone_set *zones_trace = nullptr);
int update_olh(const DoutPrefixProvider *dpp, RGWObjectCtx& obj_ctx, RGWObjState *state, RGWBucketInfo& bucket_info, const rgw_obj& obj, optional_yield y, rgw_zone_set *zones_trace = nullptr);
int update_olh(const DoutPrefixProvider *dpp, RGWObjectCtx& obj_ctx, RGWObjState *state, RGWBucketInfo& bucket_info, const rgw_obj& obj, optional_yield y,
rgw_zone_set *zones_trace = nullptr);
int clear_olh(const DoutPrefixProvider *dpp,
RGWObjectCtx& obj_ctx,
const rgw_obj& obj,
RGWBucketInfo& bucket_info,
const std::string& tag,
const uint64_t ver,
optional_yield y);
int set_olh(const DoutPrefixProvider *dpp, RGWObjectCtx& obj_ctx, RGWBucketInfo& bucket_info, const rgw_obj& target_obj, bool delete_marker, rgw_bucket_dir_entry_meta *meta,
uint64_t olh_epoch, ceph::real_time unmod_since, bool high_precision_time,
optional_yield y, rgw_zone_set *zones_trace = nullptr, bool log_data_change = false);
int set_olh(const DoutPrefixProvider *dpp,
RGWObjectCtx& obj_ctx,
RGWBucketInfo& bucket_info,
const rgw_obj& target_obj,
bool delete_marker,
rgw_bucket_dir_entry_meta *meta,
uint64_t olh_epoch,
ceph::real_time unmod_since,
bool high_precision_time,
optional_yield y,
rgw_zone_set *zones_trace = nullptr,
bool log_data_change = false,
bool skip_olh_obj_update = false); // can skip the OLH object update if, for example, repairing index
int repair_olh(const DoutPrefixProvider *dpp, RGWObjState* state, const RGWBucketInfo& bucket_info,
const rgw_obj& obj, optional_yield y);
int unlink_obj_instance(const DoutPrefixProvider *dpp, RGWObjectCtx& obj_ctx, RGWBucketInfo& bucket_info, const rgw_obj& target_obj,
Expand Down
Loading

0 comments on commit f731570

Please sign in to comment.