@@ -51,7 +51,10 @@ batcher<Clock>::batcher(
5151 , _rtc(_as)
5252 , _logger(cd_log, _rtc)
5353 , _stage(std::move(stage))
54- , _probe(config::shard_local_cfg().disable_metrics()) {}
54+ , _probe(config::shard_local_cfg().disable_metrics())
55+ , _upload_sem(
56+ config::shard_local_cfg ().cloud_storage_max_connections(), "l0/batcher") {
57+ }
5558
5659template <class Clock >
5760ss::future<> batcher<Clock>::start() {
@@ -254,29 +257,56 @@ ss::future<> batcher<Clock>::bg_controller_loop() {
254257 co_return ;
255258 }
256259
260+ // Acquire semaphore units to limit concurrent background fibers.
261+ // This blocks until a slot is available.
262+ auto units_fut = co_await ss::coroutine::as_future (
263+ ss::get_units (_upload_sem, 1 , _as));
264+
257265 auto list = _stage.pull_write_requests (
258- 10_MiB); // TODO: use configuration parameter
266+ config::shard_local_cfg ()
267+ .cloud_topics_produce_batching_size_threshold (),
268+ config::shard_local_cfg ()
269+ .cloud_topics_produce_cardinality_threshold ());
270+
271+ bool complete = list.complete ;
272+
273+ if (units_fut.failed ()) {
274+ vlog (
275+ _logger.info ,
276+ " Batcher upload loop is shutting down: {}" ,
277+ units_fut.get_exception ());
278+ co_return ;
279+ }
280+ auto units = std::move (units_fut.get ());
259281
260282 // We can spawn the work in the background without worrying about memory
261- // usage because the pipeline tracks the memory usage for us and will
262- // stop accepting new write requests if we go over the limit.
263- ssx::spawn_with_gate (_gate, [this , list = std::move (list)]() mutable {
264- return run_once (std::move (list))
265- .then ([this ](std::expected<std::monostate, errc> res) {
266- if (!res.has_value ()) {
267- if (res.error () == errc::shutting_down) {
268- vlog (
269- _logger.info ,
270- " Batcher upload loop is shutting down" );
271- } else {
272- vlog (
273- _logger.info ,
274- " Batcher upload loop error: {}" ,
275- res.error ());
276- }
277- }
278- });
279- });
283+ // usage because the background fibers is holding units acquired above.
284+ ssx::spawn_with_gate (
285+ _gate,
286+ [this , list = std::move (list), units = std::move (units)]() mutable {
287+ return run_once (std::move (list))
288+ .then ([this ](std::expected<std::monostate, errc> res) {
289+ if (!res.has_value ()) {
290+ if (res.error () == errc::shutting_down) {
291+ vlog (
292+ _logger.info ,
293+ " Batcher upload loop is shutting down" );
294+ } else {
295+ vlog (
296+ _logger.info ,
297+ " Batcher upload loop error: {}" ,
298+ res.error ());
299+ }
300+ }
301+ })
302+ .finally ([u = std::move (units)] {});
303+ });
304+
305+ // The work is spawned in the background so we can grab data for the
306+ // next L0 object. If complete==true, all pending requests were pulled,
307+ // so wait for more. If complete==false, there are more pending
308+ // requests.
309+ more_work = !complete;
280310 }
281311}
282312
0 commit comments