using namespace std;
-int retry_on_busy(optional_yield y, const DoutPrefixProvider *dpp,
+int retry_on_transient_error(optional_yield y, const DoutPrefixProvider *dpp,
CephContext *cct, const char *op_name,
std::function<int()> op)
{
int ret = 0;
for (int i = 0; i < max_attempts; i++) {
ret = op();
- if (ret != -EBUSY) return ret;
+ // 503 -> -EBUSY and 500 -> -ERR_INTERNAL_ERROR are transient on S3 backends
+ if (ret != -EBUSY && ret != -ERR_INTERNAL_ERROR) return ret;
if (i == max_attempts - 1) {
ldpp_dout(dpp, 0) << op_name << ": exhausted " << max_attempts
- << " -EBUSY retries, giving up" << dendl;
+ << " transient-error retries (ret=" << ret << "), giving up" << dendl;
return ret;
}
int64_t base = std::min(initial_ms << std::min(i, 30), max_ms);
int delay_ms = base - ceph::util::generate_random_number<int>(0, base / 10);
- ldpp_dout(dpp, 1) << op_name << ": -EBUSY, attempt " << (i + 1) << "/"
- << max_attempts << "; retrying after " << delay_ms
- << "ms" << dendl;
+ ldpp_dout(dpp, 1) << op_name << ": transient error (ret=" << ret << "), attempt "
+ << (i + 1) << "/" << max_attempts << "; retrying after "
+ << delay_ms << "ms" << dendl;
if (y) {
auto& yc = y.get_yield_context();
rgw_obj dest_obj(dest_bucket, rgw_obj_key(target_obj_name));
- ret = retry_on_busy(tier_ctx.y, tier_ctx.dpp, tier_ctx.cct, __func__, [&]() -> int {
+ ret = retry_on_transient_error(tier_ctx.y, tier_ctx.dpp, tier_ctx.cct, __func__, [&]() -> int {
if (!in_progress) { // first time. Send RESTORE req.
ret = cloud_tier_restore(tier_ctx.dpp, tier_ctx.conn, dest_obj, days, glacier_params, tier_ctx.y);
ldpp_dout(tier_ctx.dpp, 20) << __func__ << "Restoring object=" << target_obj_name << "returned ret = " << ret << dendl;
return 0;
}
- ret = retry_on_busy(tier_ctx.y, tier_ctx.dpp, tier_ctx.cct, __func__, [&]() {
+ ret = retry_on_transient_error(tier_ctx.y, tier_ctx.dpp, tier_ctx.cct, __func__, [&]() {
return rgw_cloud_tier_get_object(tier_ctx, false, headers, pset_mtime,
etag, accounted_size, attrs, cb);
});
std::shared_ptr<RGWLCCloudStreamPut> writef;
// per-part retry: the outer retry restarts from part 1 since upload state
// doesn't persist which parts have been sent
- ret = retry_on_busy(tier_ctx.y, tier_ctx.dpp, tier_ctx.cct, __func__, [&]() {
+ ret = retry_on_transient_error(tier_ctx.y, tier_ctx.dpp, tier_ctx.cct, __func__, [&]() {
auto rf = std::make_shared<RGWLCStreamRead>(tier_ctx.cct, tier_ctx.dpp,
tier_ctx.obj, tier_ctx.o.meta.mtime, tier_ctx.y);
auto wf = std::make_shared<RGWLCCloudStreamPut>(tier_ctx.dpp,
const std::string& upload_id) {
int ret;
- ret = retry_on_busy(tier_ctx.y, tier_ctx.dpp, tier_ctx.cct, __func__, [&]() {
+ ret = retry_on_transient_error(tier_ctx.y, tier_ctx.dpp, tier_ctx.cct, __func__, [&]() {
return cloud_tier_abort_multipart(tier_ctx.dpp, tier_ctx.conn, dest_obj, upload_id, tier_ctx.y);
});
ret = tier_ctx.conn.send_resource(tier_ctx.dpp, "PUT", resource, nullptr, nullptr,
out_bl, &bl, nullptr, tier_ctx.y);
- if (ret == -EBUSY) return ret;
+ /*
+ * Transient remote errors (503 -> -EBUSY, 500 -> -ERR_INTERNAL_ERROR) are
+ * retried by the caller's retry_on_transient_error wrapper, so surface them
+ * as-is rather than flattening to -EIO.
+ */
+ if (ret == -EBUSY || ret == -ERR_INTERNAL_ERROR) {
+ return ret;
+ }
if (ret < 0) {
ldpp_dout(tier_ctx.dpp, 0) << "create target bucket : " << tier_ctx.target_bucket_name << " returned ret:" << ret << dendl;
}
if (result.code != "BucketAlreadyOwnedByYou" && result.code != "BucketAlreadyExists") {
ldpp_dout(tier_ctx.dpp, 0) << "ERROR: Creating target bucket failed with error: " << result.code << dendl;
- return -EIO;
+ return (ret < 0) ? ret : -EIO;
}
+ // benign "already owned/exists" response
+ return 0;
}
- return 0;
+ // no response body: propagate any transport error instead of masking as success
+ return ret;
}
static int do_cloud_tier_transfer_object(RGWLCCloudTierCtx& tier_ctx, std::set<std::string>& cloud_targets) {
}
int rgw_cloud_tier_transfer_object(RGWLCCloudTierCtx& tier_ctx, std::set<std::string>& cloud_targets) {
- return retry_on_busy(tier_ctx.y, tier_ctx.dpp, tier_ctx.cct, __func__,
+ return retry_on_transient_error(tier_ctx.y, tier_ctx.dpp, tier_ctx.cct, __func__,
[&]() { return do_cloud_tier_transfer_object(tier_ctx, cloud_targets); });
}