Ziyun Fu created KAFKA-20843:
--------------------------------

             Summary: Custom consumer group assignors may silently use 
duplicate or built-in assignor names        
                 Key: KAFKA-20843
                 URL: https://issues.apache.org/jira/browse/KAFKA-20843
             Project: Kafka
          Issue Type: Bug
            Reporter: Ziyun Fu


Server-side consumer group assignors are configured through 
`group.consumer.assignors` as a list of either built-in assignor names or 
fully-qualified class names.

Each entry is resolved against the built-in name map first:

* `uniform` → `UniformAssignor`
* `range` → `RangeAssignor`

An entry is only treated as a class name if that lookup misses 
(`GroupCoordinatorConfig#consumerGroupAssignors`, 
`GroupCoordinatorConfig.java:861-891`).

Assignors are then addressed *by name* everywhere downstream:

* `GroupMetadataManager` builds a `name -> assignor` map with 
`Collectors.toMap(ConsumerGroupPartitionAssignor::name, Function.identity())` 
(`GroupMetadataManager.java:573-576`).
* `GroupCoordinatorService` validates 
`ConsumerGroupHeartbeatRequest.serverAssignor` against the set of configured 
assignor names (`GroupCoordinatorService.java:508`).
* Target assignment resolves the group’s preferred assignor with 
`consumerGroupAssignors.get(preferredServerAssignor)` 
(`GroupMetadataManager.java:4217`), where the name comes from the members’ 
persisted assignor preference.

### 1. Two assignors configured with the same `name()`

`verifyConsumerGroupConfigs()` only validates timeouts and intervals, and the 
`ConfigDef.ValidList.anyNonDuplicateValues` validator only de-duplicates the 
raw config entries. As a result, distinct entries that produce the same 
`name()` pass configuration parsing and the broker starts normally.

The failure surfaces much later: the `Collectors.toMap` call above throws 
`IllegalStateException: Duplicate key <name>` when a `GroupMetadataManager` is 
constructed. That happens per coordinator shard while a `__consumer_offsets` 
partition is loaded (`GroupCoordinatorShard.java:267`, reached from the 
transition to `LOADING` in `CoordinatorRuntime.java:533-545`).

So the broker comes up healthy, but then fails to load group coordinator shards 
with an error message that does not point at the misconfiguration.

This also covers configuring one built-in assignor twice under two spellings, 
for example:

`group.consumer.assignors=uniform,org.apache.kafka.coordinator.group.assignor.UniformAssignor`

The two entries differ as strings, so the duplicate check in `ConfigDef` does 
not catch them.

Because a name has to unambiguously identify one assignor, these configurations 
should be rejected but currently are not.

### 2. A custom assignor whose `name()` equals a built-in assignor name

A custom assignor returning `"uniform"` or `"range"` from `name()` is accepted, 
even when the built-in assignor it collides with is not configured at all.

This silently re-points the built-in name to the custom class:

* every client that sets `group.remote.assignor=uniform`
* every existing group whose persisted preferred assignor is `uniform`

would then be served by the custom implementation with no warning.

Conversely, because config entries are resolved against the built-in name map 
first, the short name `uniform` can never select the custom class. So the same 
name means two different things depending on how it is spelled in the config.

The client-side assignors for the classic protocol already guard against this: 
`ConsumerPartitionAssignor#getAssignorInstances` throws a `KafkaException` when 
two configured assignors report the same `name()` 
(`ConsumerPartitionAssignor.java:439-444`).

The server side has no equivalent check.

``` 



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to