{"record":{"id":"5dbc4d8e5e64eb76","repo":"apache/pulsar","slug":"retention-quota-must-exceed-configured-backlog-quo","errorCode":null,"errorMessage":"Retention Quota must exceed configured backlog quota for namespace.","messagePattern":"Retention Quota must exceed configured backlog quota for namespace\\.","errorType":"http","errorClass":"RestException","httpStatus":412,"severity":"error","filePath":"pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java","lineNumber":1913,"sourceCode":"            policies.backlog_quota_map.put(quotaType, quota);\n            return policies;\n        });\n    }\n\n    protected void internalSetRetention(RetentionPolicies retention) {\n        validateRetentionPolicies(retention);\n        validateNamespacePolicyOperation(namespaceName, PolicyName.RETENTION, PolicyOperation.WRITE);\n        validatePoliciesReadOnlyAccess();\n\n        try {\n            Policies policies = namespaceResources().getPolicies(namespaceName)\n                    .orElseThrow(() -> new RestException(Status.NOT_FOUND,\n                    \"Namespace policies does not exist\"));\n            if (!checkQuotas(policies, retention)) {\n                log.warn()\n                        .attr(\"namespace\", namespaceName)\n                        .log(\"Failed to update retention configuration for namespace: conflicts with backlog quota\");\n                throw new RestException(Status.PRECONDITION_FAILED,\n                        \"Retention Quota must exceed configured backlog quota for namespace.\");\n            }\n            policies.retention_policies = retention;\n            namespaceResources().setPolicies(namespaceName, p -> policies);\n            log.info()\n                    .attr(\"namespace\", namespaceName)\n                    .attr(\"retention\", objectWriter().writeValueAsString(retention))\n                    .log(\"Successfully updated retention configuration\");\n        } catch (RestException pfe) {\n            throw pfe;\n        } catch (Exception e) {\n            log.error()\n                    .attr(\"namespace\", namespaceName)\n                    .exception(e)\n                    .log(\"Failed to update retention configuration for namespace\");\n            throw new RestException(e);\n        }\n    }","sourceCodeStart":1895,"sourceCodeEnd":1931,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java#L1895-L1931","documentation":"Thrown by setRetention for a namespace: the broker validates that the retention quota being set is strictly greater than the namespace's configured backlog quota (checkQuotas). If not, the update is rejected with HTTP 412 PRECONDITION_FAILED, because a backlog quota larger than retention would create contradictory data-retention semantics.","triggerScenarios":"POST /namespaces/{ns}/retention with a retention size/time smaller than (or equal to a conflicting) backlog quota already configured via setBacklogQuota; lowering retention on a namespace that has a large destination_storage backlog quota.","commonSituations":"Reducing retention to save storage while an old large backlog quota remains; migrating namespaces between tenants with different quota defaults; automation that sets retention before clearing backlog quotas.","solutions":["Increase the retention policies values so the retention quota exceeds the configured backlog quota","First lower or remove the backlog quota (setBacklogQuota with null or smaller value), then set retention","Inspect current policies (getBacklogQuotaMap and getRetention) and pick consistent values","Apply quota changes in order: backlog quota down before retention down"],"exampleFix":"// before\nadmin.namespaces().setBacklogQuota(\"my-tenant/my-ns\", new BacklogQuota(1_000_000_000L, ...));\nadmin.namespaces().setRetention(\"my-tenant/my-ns\", new RetentionPolicies(500_000_000L, 30)); // 412\n// after\nadmin.namespaces().setRetention(\"my-tenant/my-ns\", new RetentionPolicies(2_000_000_000L, 120)); // retention > backlog","handlingStrategy":"validation","validationCode":"Map<BacklogQuotaType, BacklogQuota> quotas = admin.namespaces().getBacklogQuotaMap(ns);\nlong maxBacklog = quotas.values().stream().mapToLong(BacklogQuota::getLimitSize).max().orElse(0);\nif (retention.getRetentionSizeInMB() * 1024L * 1024L <= maxBacklog) {\n    throw new IllegalArgumentException(\"Retention must exceed configured backlog quota; lower backlog first\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    admin.namespaces().setRetention(ns, retention);\n} catch (PulsarAdminException.PreconditionFailedException e) {\n    admin.namespaces().setBacklogQuota(ns, smallerQuota, BacklogQuotaType.destination_storage);\n    admin.namespaces().setRetention(ns, retention);\n}","preventionTips":["Lower backlog quotas before lowering retention","Validate retention vs backlog in your deployment tooling","Document the invariant retention quota > backlog quota for the team"],"tags":["http-412","retention","backlog-quota","admin-api"],"backgroundTag":"retention-quota-conflict","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}