skip to content

What is ContainerProperties and which settings would you tune for reliability and rebalance stability?

level: principalimportance: should knowfreq 45%

answer

  1. ContainerProperties = container-level, not raw consumer props
  2. ackMode, pollTimeout, syncCommits, rebalanceListener, EOS
  3. 3 layers: consumer config / container props / factory
  4. rebalance stability = max.poll.interval.ms + records + CooperativeSticky
  5. commit-on-revoke minimizes redelivery

basics

~20 s

ContainerProperties 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 s

ContainerProperties (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
java
@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=CooperativeStickyAssignor

go deeper

for a junior

Know ContainerProperties holds container settings like ack mode and poll timeout.

for a middle

Distinguish container properties from raw consumer config and name a few settings (ackMode, syncCommits, pollTimeout).

for a senior

Configure rebalance listeners, error handler/DLT, and reason about commit style vs throughput.

for a principal

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

context