What is ContainerProperties and which settings would you tune for reliability and rebalance stability?
answer
- ContainerProperties = container-level, not raw consumer props
- ackMode, pollTimeout, syncCommits, rebalanceListener, EOS
- 3 layers: consumer config / container props / factory
- rebalance stability = max.poll.interval.ms + records + CooperativeSticky
- commit-on-revoke minimizes redelivery
basics
~20 sContainerProperties is the configuration object on a listener container holding container-level settings: ack mode, poll timeout, ack time/count, consumer rebalance listener, sync/async commits, and the task executor. You tune it for commit behavior and rebalance stability.
solid answer
~40 sContainerProperties (obtained via factory.getContainerProperties() or a KafkaListenerEndpoint) carries settings that live on the Spring container rather than the KafkaConsumer. Key knobs: setAckMode(...) (RECORD/BATCH/MANUAL/TIME/COUNT/COUNT_TIME), setAckTime/setAckCount for the threshold modes, setPollTimeout (how long each poll blocks), setSyncCommits and setCommitCallback (sync vs async offset commits), setConsumerRebalanceListener (hook revoke/assign to commit-on-revoke), setIdleBetweenPolls / idle-event settings, setMicrometerEnabled/observationEnabled for metrics/tracing, and setEosMode / a KafkaAwareTransactionManager for exactly-once. For rebalance stability you don't tune ContainerProperties alone — you also set consumer props like max.poll.interval.ms, max.poll.records, session.timeout.ms, heartbeat.interval.ms, and choose CooperativeStickyAssignor. The container-side lever is committing offsets on partition revocation via a rebalance listener so redelivery is minimized. It sits alongside consumer-config (raw Kafka client props) and factory-level settings (concurrency, batch, error handler).
code
java · 26 lines@Bean
public ConcurrentKafkaListenerContainerFactory<String, String> reliableFactory(
ConsumerFactory<String, String> cf, KafkaTemplate<String, String> template) {
var factory = new ConcurrentKafkaListenerContainerFactory<String, String>();
factory.setConsumerFactory(cf);
ContainerProperties props = factory.getContainerProperties();
props.setAckMode(ContainerProperties.AckMode.MANUAL_IMMEDIATE);
props.setSyncCommits(true);
props.setPollTimeout(3000);
props.setConsumerRebalanceListener(new ConsumerAwareRebalanceListener() {
@Override
public void onPartitionsRevokedBeforeCommit(
Consumer<?, ?> consumer, Collection<TopicPartition> partitions) {
// commit current offsets before losing these partitions
}
});
// poison-record handling instead of wedging the partition
factory.setCommonErrorHandler(new DefaultErrorHandler(
new DeadLetterPublishingRecoverer(template),
new FixedBackOff(1000L, 3)));
return factory;
}
// Reliability also needs consumer props (in ConsumerFactory / spring.kafka.consumer):
// max.poll.interval.ms, max.poll.records, partition.assignment.strategy=CooperativeStickyAssignorgo deeper
Know ContainerProperties holds container settings like ack mode and poll timeout.
Distinguish container properties from raw consumer config and name a few settings (ackMode, syncCommits, pollTimeout).
Configure rebalance listeners, error handler/DLT, and reason about commit style vs throughput.
Architect rebalance-stable, reliable consumption across the three config layers — poll/records tuning, cooperative assignment, commit-on-revoke, and EOS trade-offs.
**What ContainerProperties is** A configuration bean attached to every listener container describing **container-level behavior** — the concerns Spring's container manages, as opposed to raw **`KafkaConsumer`** client properties (which live in the `ConsumerFactory` / `spring.kafka.consumer.*`). You reach it via `factory.getContainerProperties()` (for factory-built containers) or when constructing a container manually. **Notable settings** - **`setAckMode(AckMode)`** — commit timing (RECORD/BATCH/TIME/COUNT/COUNT_TIME/MANUAL/MANUAL_IMMEDIATE). - **`setAckTime(long)` / `setAckCount(int)`** — thresholds for TIME/COUNT/COUNT_TIME modes. - **`setPollTimeout(long)`** — how long each `consumer.poll()` blocks waiting for records. - **`setSyncCommits(boolean)` / `setCommitCallback(...)`** — synchronous vs asynchronous offset commits and a callback for async results. - **`setConsumerRebalanceListener(ConsumerRebalanceListener)`** — hook `onPartitionsRevoked` / `onPartitionsAssigned` to, e.g., commit offsets before losing a partition. - **`setIdleBetweenPolls(long)`**, idle/ no-messages event intervals (`setIdleEventInterval`) — emit `ListenerContainerIdleEvent` for monitoring. - **`setObservationEnabled(true)` / `setMicrometerEnabled(true)`** — metrics and tracing. - **`setEosMode(...)`** with a `KafkaAwareTransactionManager` — exactly-once (read-process-write) semantics. - **`setAuthExceptionRetryInterval`, `setShutdownTimeout`, `setStopContainerWhenFenced`**, and the task executor / listener thread naming. **Three configuration layers (know the distinction)** 1. **Consumer config** — raw Kafka client props (`bootstrap.servers`, `max.poll.records`, `max.poll.interval.ms`, `session.timeout.ms`, `heartbeat.interval.ms`, `partition.assignment.strategy`, `enable.auto.commit`). Set in the `ConsumerFactory`. 2. **ContainerProperties** — Spring container behavior (ack mode, poll timeout, rebalance listener, commit style). 3. **Factory-level** — `setConcurrency`, `setBatchListener`, `setCommonErrorHandler`, `setReplyTemplate`. **Tuning for reliability & rebalance stability** Rebalances (revoke + reassign partitions) happen on scaling, deploys, crashes, or when a consumer is deemed dead. To keep them rare and cheap: - **`max.poll.interval.ms`** (consumer prop): the max time allowed between polls; if your processing per batch exceeds it, the consumer is ejected → rebalance and redelivery. Raise it or reduce `max.poll.records` / speed up processing. - **`max.poll.records`**: cap batch size so a poll's records finish within `max.poll.interval.ms`. - **`session.timeout.ms` / `heartbeat.interval.ms`**: liveness detection; too-tight values cause spurious rebalances. - **`partition.assignment.strategy` = CooperativeStickyAssignor**: incremental cooperative rebalancing so consumers keep most partitions instead of a stop-the-world revoke-all. - **Commit on revoke**: via `setConsumerRebalanceListener`, commit current offsets in `onPartitionsRevoked` so a new owner resumes cleanly, minimizing duplicate reprocessing. - **AckMode + sync commits**: for stronger guarantees use RECORD or MANUAL_IMMEDIATE with `setSyncCommits(true)` (durable commit before proceeding) at some throughput cost. - **Error handling / DLT**: a `DefaultErrorHandler` with backoff + `DeadLetterPublishingRecoverer` so poison records don't wedge the partition. **Gotchas** - Confusing ContainerProperties with consumer config: e.g., `max.poll.records` is a **consumer** prop, not on ContainerProperties. - Slow listener + default `max.poll.interval.ms` → silent, repeated rebalances that look like duplicate processing. - Async commits (default in some paths) can lose the last commit on abrupt shutdown; use sync commits or commit-on-revoke for tighter guarantees. - Exactly-once via `setEosMode` requires a transactional producer and `KafkaAwareTransactionManager`; it's not just a flag.
- Is max.poll.records a ContainerProperties setting?No — it's a raw KafkaConsumer property set in the ConsumerFactory (spring.kafka.consumer.max-poll-records). ContainerProperties holds container-level concerns like ack mode, poll timeout, commit style, and the rebalance listener; the client props live one layer down.
- How does CooperativeStickyAssignor improve rebalance stability?It does incremental cooperative rebalancing: instead of revoking all partitions from every consumer (stop-the-world), consumers keep most of their partitions and only the minimal set moves, so processing pauses are shorter and fewer partitions get reprocessed.
- Why commit offsets in onPartitionsRevoked?When a partition is about to be reassigned, committing the current position means the next owner resumes exactly where you stopped, minimizing the number of already-processed records that get redelivered.
saying these in an interview costs you the question
- Putting max.poll.interval.ms / max.poll.records on ContainerProperties instead of consumer config
- Thinking setEosMode alone gives exactly-once without a transaction manager
- Ignoring slow-processing rebalance loops
- Assuming async commits are always safe on shutdown