From 503180ab32e9da1b357e096e438671a61bba931d Mon Sep 17 00:00:00 2001 From: Lari Hotari Date: Wed, 6 May 2026 01:20:15 +0300 Subject: [PATCH] [fix][broker] Skip backlog-quota eviction on fenced/closing topics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `BrokerService.monitorBacklogQuota` runs every `backlogQuotaCheckIntervalInSeconds` (default 60s, but reduced to 2s in some tests) and iterates every persistent topic on the broker. For each topic it calls `BacklogQuotaManager.handleExceededBacklogQuota`, which ultimately mutates managed-ledger cursor state via `slowestConsumer.skipEntries` and `markDeletePositionMoveForward`. Today this runs unconditionally, even on topics that have already been fenced (transient interceptor exception) or are being torn down (`isClosingOrDeleting`). When a namespace is being force-deleted under quota pressure, the checker contends with the delete path on the same managed ledger / cursor — slowing the deletion and causing intermittent `Awaitility.deleteNamespace` timeouts in tests like `BacklogQuotaManagerTest`. Fix: early-return at the top of `handleExceededBacklogQuota` if the topic reports `isFenced()` or `isClosingOrDeleting()`. The entries are about to be discarded by the close/delete path — running eviction is both wasted work and a source of contention. `isFenced` is auto-generated by Lombok `@Getter` on `AbstractTopic.isFenced`; `isClosingOrDeleting` is auto-generated on `PersistentTopic.isClosingOrDeleting`. No new accessors needed. --- .../pulsar/broker/service/BacklogQuotaManager.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/BacklogQuotaManager.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/BacklogQuotaManager.java index da0d7ccd4aec3..686e2244b560a 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/BacklogQuotaManager.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/BacklogQuotaManager.java @@ -94,6 +94,17 @@ public BacklogQuotaImpl getBacklogQuota(NamespaceName namespace, BacklogQuotaTyp */ public void handleExceededBacklogQuota(PersistentTopic persistentTopic, BacklogQuotaType backlogQuotaType, boolean preciseTimeBasedBacklogQuotaCheck) { + if (persistentTopic.isFenced() || persistentTopic.isClosingOrDeleting()) { + // Skip eviction work on a topic that is being torn down or transiently fenced. + // Mutating cursors here (skipEntries / markDeletePosition) contends with the + // delete path and can keep namespace force-delete from completing in time; + // the entries are about to be discarded anyway. + log.debug() + .attr("topic", persistentTopic.getName()) + .attr("backlogQuotaType", backlogQuotaType) + .log("Skipping backlog-quota eviction on fenced/closing topic"); + return; + } BacklogQuota quota = persistentTopic.getBacklogQuota(backlogQuotaType); BacklogQuotaMetrics topicBacklogQuotaMetrics = persistentTopic.getPersistentTopicMetrics().getBacklogQuotaMetrics();