{"record":{"id":"33140845f1420d05","repo":"apache/pulsar","slug":"split-service-unit-should-be-split-into-2-service","errorCode":null,"errorMessage":"Split service unit should be split into 2 service units.","messagePattern":"Split service unit should be split into 2 service units\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/models/Split.java","lineNumber":34,"sourceCode":" * specific language governing permissions and limitations\n * under the License.\n */\npackage org.apache.pulsar.broker.loadbalance.extensions.models;\n\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\n\n/**\n * Defines the information required for a service unit split(e.g. bundle split).\n */\npublic record Split(\n        String serviceUnit, String sourceBroker, Map<String, Optional<String>> splitServiceUnitToDestBroker) {\n\n    public Split {\n        Objects.requireNonNull(serviceUnit);\n        if (splitServiceUnitToDestBroker == null || splitServiceUnitToDestBroker.size() != 2) {\n            throw new IllegalArgumentException(\"Split service unit should be split into 2 service units.\");\n        }\n    }\n}\n","sourceCodeStart":16,"sourceCodeEnd":38,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker/src/main/java/org/apache/pulsar/broker/loadbalance/extensions/models/Split.java#L16-L38","documentation":"The Split record models a topic-bundle split into exactly two child service units. Its compact constructor validates that splitServiceUnitToDestBroker is non-null with exactly 2 entries and throws IllegalArgumentException otherwise. It exists so callers cannot construct a malformed split plan.","triggerScenarios":"Calling new Split(serviceUnit, sourceBroker, destinations) with a null map, or a map with 0, 1, or 3+ entries (split points must yield exactly two halves).","commonSituations":"Custom load-manager code computing split destinations produced one or more than two children, a migration/assignment tool wrote wrong data, or a unit test built a Split with a single destination.","solutions":["Compute exactly two child service units from the bundle's split point and map each to a destination broker before constructing Split.","Use the standard split logic (e.g. TopicRanges/HierarchyTopicPolicies bundle split) so the two halves are always produced.","Validate the destinations map size before constructing the record.","Fix tests to supply a two-entry map."],"exampleFix":"// before\nnew Split(bundle, src, Map.of(firstHalf, destA)); // 1 entry -> throws\n// after\nnew Split(bundle, src, Map.of(firstHalf, destA, secondHalf, destB)); // exactly 2 entries","handlingStrategy":"validation","validationCode":"if (dests == null || dests.size() != 2) {\n    throw new IllegalArgumentException(\"split must yield exactly 2 units, got \"\n        + (dests == null ? 0 : dests.size()));\n}\nSplit split = new Split(serviceUnit, sourceBroker, dests);","typeGuard":"boolean isValidSplit(Split s) {\n    return s != null && s.splitServiceUnitToDestBroker() != null\n        && s.splitServiceUnitToDestBroker().size() == 2;\n}","tryCatchPattern":"try {\n    Split split = new Split(serviceUnit, sourceBroker, dests);\n} catch (IllegalArgumentException e) {\n    log.error(\"Bad split plan: {}\", e.getMessage());\n    // skip/redo split computation\n}","preventionTips":["Compute both halves of the bundle before building the Split record.","Unit-test custom split-point logic to always produce two children.","Validate destinations map size in any tooling that writes split plans."],"tags":["load-manager","bundle-split","validation","illegal-argument"],"backgroundTag":"invalid-split-plan","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"}