{"record":{"id":"d76a1c15415c41b8","repo":"apache/seatunnel","slug":"topicpartition-matched-by-multiple-table-conf","errorCode":null,"errorMessage":"TopicPartition '{}' matched by multiple table configs. Keeping '{}' and ignoring '{}'.","messagePattern":"TopicPartition '(.+?)' matched by multiple table configs\\. Keeping '(.+?)' and ignoring '(.+?)'\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"seatunnel-connectors-v2/connector-pulsar/src/main/java/org/apache/seatunnel/connectors/seatunnel/pulsar/source/enumerator/discoverer/MultiTablePartitionDiscoverer.java","lineNumber":67,"sourceCode":"\n    private final List<TableDiscovererPair> discovererPairs;\n    private final Map<TopicPartition, TablePath> partitionToTablePath = new HashMap<>();\n\n    public MultiTablePartitionDiscoverer(List<TableDiscovererPair> discovererPairs) {\n        this.discovererPairs = discovererPairs;\n    }\n\n    @Override\n    public Set<TopicPartition> getSubscribedTopicPartitions(PulsarAdmin admin) {\n        Set<TopicPartition> allPartitions = new HashSet<>();\n        partitionToTablePath.clear();\n\n        for (TableDiscovererPair pair : discovererPairs) {\n            Set<TopicPartition> partitions = pair.discoverer.getSubscribedTopicPartitions(admin);\n            for (TopicPartition tp : partitions) {\n                TablePath existing = partitionToTablePath.putIfAbsent(tp, pair.tablePath);\n                if (existing != null && !existing.equals(pair.tablePath)) {\n                    LOG.warn(\n                            \"TopicPartition '{}' matched by multiple table configs. Keeping '{}' and ignoring '{}'.\",\n                            tp,\n                            existing,\n                            pair.tablePath);\n                }\n            }\n            allPartitions.addAll(partitions);\n        }\n        return allPartitions;\n    }\n\n    public TablePath getTablePath(TopicPartition partition) {\n        return partitionToTablePath.get(partition);\n    }\n\n    public boolean hasTopicPattern() {\n        return discovererPairs.stream().anyMatch(TableDiscovererPair::isTopicPattern);\n    }","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/apache/seatunnel/blob/cf67b549a7a6c35fa0beb12d83c62892427ea919/seatunnel-connectors-v2/connector-pulsar/src/main/java/org/apache/seatunnel/connectors/seatunnel/pulsar/source/enumerator/discoverer/MultiTablePartitionDiscoverer.java#L49-L85","documentation":"In the multi-table Pulsar source, the same Pulsar TopicPartition was matched by more than one table's config (table path patterns overlap). The discoverer keeps the first table that claimed the partition (existing mapping) and ignores the later one, logging this warning so data misrouting from overlapping patterns is visible.","triggerScenarios":"getSubscribedTopicPartitions iterates discovererPairs and finds a TopicPartition already mapped to a different TablePath via putIfAbsent — i.e., two table configs (e.g. overlapping topic patterns) both match the same topic partition.","commonSituations":"Two table configs with overlapping topic name-space patterns (e.g. 'app.*' and 'app.orders'); copy-pasted table configs pointing at the same topic; wildcard patterns accidentally covering a specific table's topic.","solutions":["Make table topic patterns non-overlapping so each topic partition matches exactly one table","Remove the duplicate/overlapping table config entry","Verify which table was kept ('{}' first value) and confirm that is the intended owner"],"exampleFix":"// before (overlapping)\ntables:\n  - table_a: {topic_pattern: \"app.*\"}\n  - table_b: {topic_pattern: \"app.orders\"}\n// after\n  - table_a: {topic_pattern: \"app.users\"}\n  - table_b: {topic_pattern: \"app.orders\"}","handlingStrategy":"validation","validationCode":"// assert no topic matches two table patterns before submitting\nSet<String> overlaps = matchAllTopics(patterns).entrySet().stream().filter(e -> e.getValue().size() > 1).map(Map.Entry::getKey).collect(toSet()); if (!overlaps.isEmpty()) throw new IllegalStateException(\"overlapping patterns for \" + overlaps);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Design table topic patterns to be mutually exclusive","Test pattern expansion against real topic list before deploy","Keep specific patterns out of wildcard reach","Review warnings in logs after any table config change"],"tags":["config","pulsar","overlap"],"backgroundTag":"conflicting-config-options","analyzedSha":"cf67b549a7a6c35fa0beb12d83c62892427ea919","analyzedAt":"2026-09-10T21:44:55.265Z","contentChangedAt":"2026-09-10T21:44:55.265Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}