@@ -299,6 +299,75 @@ def test_local_data_grows_in_tiered_cloud(self):
299299 f"expected near { replication * self .local_target_bytes } bytes"
300300 )
301301
302+ @cluster (num_nodes = 4 )
303+ def test_capacity_pressure_reclaims_tiered_cloud (self ):
304+ """
305+ Cluster-wide disk pressure (the disk_space_manager capacity path)
306+ reclaims a tiered_cloud partition's local log, not just the
307+ retention.local.target path.
308+
309+ The topic sets no local target and effectively infinite retention, so
310+ only the capacity path can bound it. On a build that excludes
311+ tiered_cloud partitions from disk_space_manager the local log grows
312+ unbounded and this fails; with them admitted, the eviction policy pins
313+ _cloud_gc_offset and ctp_stm trims to it, so the footprint converges
314+ near the capacity target.
315+
316+ Also asserts do_gc leaves the pin for ctp_stm (no "expected remote
317+ retention to be active" warning); stealing it would make trimming
318+ race-dependent.
319+ """
320+ assert self .redpanda is not None
321+
322+ # Node-wide log-data target, small enough that the produced topic must
323+ # be trimmed to meet it. disk_reservation_percent=0 and percent=100 so
324+ # the bytes target is the effective one.
325+ capacity_target = 16 * 1024 * 1024
326+ self .redpanda .set_cluster_config (
327+ {
328+ "retention_local_target_capacity_bytes" : capacity_target ,
329+ "retention_local_target_capacity_percent" : 100 ,
330+ "disk_reservation_percent" : 0 ,
331+ }
332+ )
333+
334+ # No retention.local.target.bytes: the local-target path stays inert
335+ # (only the 1-day default applies, and nothing is that old), so only
336+ # the capacity path can bound this topic. retention.bytes is
337+ # effectively unlimited, mirroring the field scenario that filled the
338+ # disk.
339+ rpk = RpkTool (self .redpanda )
340+ rpk .create_topic (
341+ topic = self .topic_name ,
342+ partitions = 1 ,
343+ replicas = 3 ,
344+ config = {
345+ TopicSpec .PROPERTY_STORAGE_MODE : (TopicSpec .STORAGE_MODE_TIERED_CLOUD ),
346+ "cleanup.policy" : TopicSpec .CLEANUP_DELETE ,
347+ "retention.bytes" : str (self .retention_bytes ),
348+ "segment.bytes" : str (self .segment_size ),
349+ },
350+ )
351+ self ._wait_for_partition_info ()
352+
353+ # Produce well past the capacity target so an untrimmed build grows far
354+ # beyond it.
355+ produce_bytes = 64 * 1024 * 1024
356+ self ._produce (produce_bytes )
357+ self .wait_until_reconciled (topic = self .topic_name , partition = 0 )
358+
359+ # The target is per node; the metric sums 3 replicas. Allow 3x the
360+ # target plus per-replica headroom (the never-evicted active segment,
361+ # internal logs, trim-cycle latency). Still far below the ~3x produce an
362+ # untrimmed build retains.
363+ replication = 3
364+ ceiling = replication * (capacity_target + 4 * self .segment_size )
365+ self ._wait_local_below (ceiling_bytes = ceiling , timeout_sec = 180 )
366+
367+ assert not self .redpanda .search_log_any (
368+ "expected remote retention to be active"
369+ ), "do_gc stole the space-management pin from ctp_stm"
370+
302371 @cluster (num_nodes = 4 )
303372 def test_compact_topic_evicts_via_l1_floor (self ):
304373 """
0 commit comments