From 60ba07ae5c62f4880bbf0dc4b14dbe82d414eb7f Mon Sep 17 00:00:00 2001 From: Casey Bodley Date: Tue, 22 Jul 2025 14:25:37 -0400 Subject: [PATCH] rgw: RGWDeleteMultiObj spawns a coroutine on null_yield if called with null_yield, RGWDeleteMultiObj::execute() creates an io_context and spawns a coroutine to run on it. this ensures that spawn_throttle always gets a valid yield context Signed-off-by: Casey Bodley --- src/rgw/rgw_op.cc | 41 +++++++++++++++++++++++++++++++++-------- src/rgw/rgw_op.h | 3 +++ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/rgw/rgw_op.cc b/src/rgw/rgw_op.cc index 4965625c9324..41ca7ad2c437 100644 --- a/src/rgw/rgw_op.cc +++ b/src/rgw/rgw_op.cc @@ -7642,6 +7642,22 @@ void RGWDeleteMultiObj::handle_individual_object(const RGWMultiDelObject& object send_partial_response(o, del_op->result.delete_marker, del_op->result.version_id, op_ret); } +void RGWDeleteMultiObj::handle_objects(const std::vector& objects, + uint32_t max_aio, + boost::asio::yield_context yield) +{ + auto group = ceph::async::spawn_throttle{yield, max_aio}; + + for (const auto& object : objects) { + group.spawn([this, &object] (boost::asio::yield_context yield) { + handle_individual_object(object, yield); + }); + + rgw_flush_formatter(s, s->formatter); + } + group.wait(); +} + void RGWDeleteMultiObj::execute(optional_yield y) { const char* buf = data.c_str(); @@ -7710,16 +7726,25 @@ void RGWDeleteMultiObj::execute(optional_yield y) // process up to max_aio object deletes in parallel const uint32_t max_aio = std::max(1, s->cct->_conf->rgw_multi_obj_del_max_aio); - auto group = ceph::async::spawn_throttle{y, max_aio}; - for (const auto& object : multi_delete->objects) { - group.spawn([this, &object] (boost::asio::yield_context yield) { - handle_individual_object(object, yield); - }); - - rgw_flush_formatter(s, s->formatter); + // if we're not already running in a coroutine, spawn one + if (!y) { + auto& objects = multi_delete->objects; + + boost::asio::io_context context; + boost::asio::spawn(context, + [this, &objects, max_aio] (boost::asio::yield_context yield) { + handle_objects(objects, max_aio, yield); + }, + [] (std::exception_ptr eptr) { + if (eptr) std::rethrow_exception(eptr); + }); + context.run(); + } else { + // use the existing coroutine's yield context + handle_objects(multi_delete->objects, max_aio, + y.get_yield_context()); } - group.wait(); /* set the return code to zero, errors at this point will be dumped to the response */ diff --git a/src/rgw/rgw_op.h b/src/rgw/rgw_op.h index eb79bb9b8a60..35d7fb637eed 100644 --- a/src/rgw/rgw_op.h +++ b/src/rgw/rgw_op.h @@ -2178,6 +2178,9 @@ class RGWDeleteMultiObj : public RGWOp { */ void handle_individual_object(const RGWMultiDelObject& object, optional_yield y); + void handle_objects(const std::vector& objects, + uint32_t max_aio, boost::asio::yield_context yield); + protected: std::vector ops_log_entries; bufferlist data; -- 2.47.3