Apache Kafka — Interview Q&A Matrix
Parent stack: Messaging & Integration Technology: Apache Kafka Levels: Junior · Senior · Architect Questions: 6
Each entry contains a Question, the Expected Depth, an Ideal Answer, and Red Flags.
Junior
Q1 — Kafka producers, consumers, and the log model
Question: Explain Kafka's main concepts: topic, partition, record, offset, producer, consumer, and consumer group.
Expected Depth:
- Describes a topic as a partitioned, append-only log.
- Explains that ordering is guaranteed only inside one partition.
- Distinguishes the current consumer position from the committed offset.
- Explains consumer groups and the one-active-consumer-per-partition rule within a group.
- Knows that retention is based on time or size, not on whether a consumer has read a record.
- Can show basic producer and
@KafkaListenerusage.
Ideal Answer:
- A topic is a named stream of records. Kafka splits a topic into partitions, and each partition is an ordered, append-only log.
- A record contains a key, value, timestamp, and headers. Kafka gives each record an offset, which is its position inside one partition.
- Kafka guarantees order only within a partition. It does not provide one global order across all partitions in a topic.
- A consumer has a current position and may also store a committed offset. The committed offset normally identifies the next record to read after a restart.
- Consumers with the same
group.idshare the partitions. One partition is assigned to at most one active consumer in that group. Different groups can read the same records independently. - Producers send records to Kafka:
kafkaTemplate.send("orders", order.customerId(), order);- A Spring Kafka consumer can process records with
@KafkaListener:
@KafkaListener(topics = "orders", groupId = "billing")
public void onOrder(OrderEvent event) {
billing.charge(event);
}- Reading a record does not delete it. Kafka removes old data according to the topic's retention or compaction policy.
Red Flags:
- Claims that Kafka guarantees global ordering across a topic.
- Says that an offset is global across all partitions.
- Thinks that reading a record removes it from Kafka.
- Says that several consumers in one group can read the same partition at the same time.
- Confuses a consumer's current position with its committed offset.
Q2 — Message keys and partition assignment
Question: Why would you set a key on a Kafka record, and what happens when the key is null?
Expected Depth:
- Explains that the producer partitioner uses the serialized key to choose a partition.
- Connects the key to per-entity ordering and data locality.
- Explains sticky batching for records with a null key.
- Understands that the partition count limits active consumer parallelism in one group.
- Recognizes hot partitions, low-cardinality keys, skew, and the effect of increasing the partition count.
Ideal Answer:
- By default, the producer hashes the serialized key to select a partition. Records with the same key usually go to the same partition while the partition count and partitioner remain unchanged.
- Use a stable business key, such as
accountId, when events for one entity must stay in order:
var record = new ProducerRecord<String, AccountEvent>(
"account-events",
event.accountId(),
event);
producer.send(record);- If the key is
null, the default producer uses a sticky partition for a batch and later moves to another partition. This improves batching and throughput, but it does not keep records for one entity together. - The number of partitions limits the number of active consumers in one group. If a topic has six partitions, at most six consumers in that group can receive assignments.
- A poor key can create a hot partition. For example, using
countryas the key may send most traffic to one partition if most users are in one country. - Increasing the partition count can map an existing key to a different partition. If strict long-term ordering is required, plan partition expansion and key strategy carefully.
Red Flags:
- Describes the partition rule as a permanent public contract such as
hash(key) % partitions. - Expects per-entity ordering while sending null keys.
- Uses a low-cardinality or highly skewed key without discussing hot partitions.
- Claims that more consumers than partitions increase parallelism in one group.
- Increases the partition count without considering key remapping and ordering.
Senior
Q3 — Delivery semantics: at-least-once, at-most-once, exactly-once
Question: Compare Kafka's delivery guarantees and how you achieve idempotent/exactly-once processing in a consumer.
Expected Depth:
- Compares at-most-once, at-least-once, and Kafka exactly-once semantics.
- Explains how offset commit timing changes loss and duplicate risks.
- Distinguishes an idempotent producer from a transactional producer.
- Knows that transactional consumers should use
isolation.level=read_committed. - Explains why database writes and external API calls still need idempotency.
- Uses a unique event ID and one database transaction for inbox deduplication and business changes.
Ideal Answer:
- At-most-once: commit the offset before processing. A crash after the commit can lose the record.
- At-least-once: process first and commit afterwards. A crash after the side effect but before the commit can cause the record to be processed again. The handler must be idempotent.
- Kafka exactly-once semantics: use an idempotent producer and Kafka transactions. In a consume-transform-produce flow, the output records and consumed offsets can commit in one Kafka transaction.
props.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true);
props.put(ProducerConfig.TRANSACTIONAL_ID_CONFIG, "billing-tx");
producer.initTransactions();
producer.beginTransaction();
try {
producer.send(outputRecord);
producer.sendOffsetsToTransaction(offsets, consumerGroupMetadata);
producer.commitTransaction();
} catch (RuntimeException error) {
producer.abortTransaction();
throw error;
}- Consumers that must ignore aborted transactional records use:
isolation.level=read_committed- Kafka transactions do not make a database write or payment API call exactly-once. For a database, insert the event ID into an inbox table with a unique key and apply the business update in the same database transaction:
BEGIN;
WITH accepted AS (
INSERT INTO consumer_inbox (consumer_name, event_id)
VALUES ('billing', :event_id)
ON CONFLICT DO NOTHING
RETURNING 1
)
UPDATE invoices
SET status = 'PAID'
WHERE id = :invoice_id
AND EXISTS (SELECT 1 FROM accepted);
COMMIT;- Commit the Kafka offset only after the database transaction succeeds. If the service crashes after the database commit but before the offset commit, Kafka delivers the record again and the inbox key prevents a second update.
- For an external API, send a stable idempotency key if the API supports one.
Red Flags:
- Claims that Kafka transactions make database or HTTP side effects exactly-once.
- Commits offsets before processing without explaining the possible data loss.
- Uses at-least-once delivery without an idempotency plan.
- Checks whether an event exists and inserts it in separate, non-atomic steps.
- Reads transactional topics with
read_uncommittedand assumes aborted records are hidden. - Uses the same
transactional.idfor several producer instances at the same time without understanding producer fencing.
Q4 — Consumer group rebalancing and poison records
Question: Consumers rebalance often, and one invalid record blocks a partition. Explain the causes and how you would handle both problems.
Expected Depth:
- Explains rebalance triggers: membership changes, missed heartbeats,
max.poll.interval.ms, and partition-count changes. - Distinguishes
session.timeout.msfrommax.poll.interval.ms. - Explains eager versus cooperative rebalancing and static membership.
- Handles processing failures with bounded retries, backoff, and a dead-letter topic.
- Handles deserialization failures that occur before the listener method runs.
- Preserves per-partition ordering and commits offsets only after successful processing or recovery.
Ideal Answer:
- A rebalance can happen when a consumer joins or leaves, misses its session heartbeats, exceeds
max.poll.interval.ms, changes its subscription, or when the topic gains partitions. session.timeout.mscontrols failure detection through heartbeats.max.poll.interval.mslimits the time between calls topoll(). Long record processing often breaks the second limit.- Reduce unnecessary disruption with the cooperative sticky assignor, which moves partitions in steps instead of revoking all of them at once. Static membership can also reduce rebalances during short restarts.
- Keep the poll loop healthy by lowering
max.poll.records, making processing faster, or increasingmax.poll.interval.msbased on measured worst-case processing time. Sending work to another executor requires careful offset and ordering control; it is not a safe default. - A poison record always fails because its data is invalid or the handler cannot process it. Infinite retries block all later records in that partition.
- Use bounded retries with backoff, then publish the failed record and error details to a dead-letter topic:
@Bean
DefaultErrorHandler errorHandler(KafkaTemplate<?, ?> template) {
var recoverer = new DeadLetterPublishingRecoverer(template);
var backoff = new ExponentialBackOffWithMaxRetries(3);
backoff.setInitialInterval(1_000L);
backoff.setMultiplier(2.0);
backoff.setMaxInterval(10_000L);
return new DefaultErrorHandler(recoverer, backoff);
}- A malformed payload may fail during deserialization before
@KafkaListenerruns. Configure Spring Kafka'sErrorHandlingDeserializerso the error handler can recover the record:
spring.kafka.consumer.value-deserializer=\
org.springframework.kafka.support.serializer.ErrorHandlingDeserializer
spring.kafka.consumer.properties.spring.deserializer.value.delegate.class=\
org.springframework.kafka.support.serializer.JsonDeserializer- Monitor rebalance count, consumer lag, processing time, retry count, and dead-letter-topic volume. A dead-letter topic needs ownership, alerts, retention, and a safe replay process.
Red Flags:
- Retries an invalid record forever and blocks the partition.
- Adds a dead-letter topic but has no alert, owner, or replay process.
- Increases
max.poll.interval.mswithout checking why processing is slow. - Sends records to a worker pool and commits offsets before the workers finish.
- Ignores deserialization errors because the listener method never receives them.
- Claims that cooperative rebalancing removes all rebalance pauses.
Architect
Q5 — Saga vs Outbox: distributed transactions across services
Question: An order flow spans Order, Payment, and Inventory services with separate databases. There's no distributed 2PC. As architect, design reliable cross-service consistency.
Expected Depth:
- Explains the dual-write problem and why cross-service two-phase commit is rarely a good default.
- Uses a transactional outbox to store business data and an event in one local transaction.
- Knows that the outbox relay is normally at-least-once, so consumers must be idempotent.
- Compares saga choreography with saga orchestration.
- Designs compensating actions, timeouts, retries, and a durable saga state.
- Accepts eventual consistency and defines states that users and operators can understand.
Ideal Answer:
- The dual-write problem appears when a service updates its database and publishes to Kafka as two separate operations. If it crashes between them, the database and Kafka can disagree.
- Distributed two-phase commit adds strong coupling, longer lock times, and lower availability. Many brokers, databases, and cloud services do not support one shared transaction.
- Use the transactional outbox pattern. Store the business change and an outbox row in the same local database transaction:
BEGIN;
INSERT INTO orders (id, status)
VALUES (:order_id, 'PENDING');
INSERT INTO outbox (event_id, aggregate_id, event_type, payload)
VALUES (:event_id, :order_id, 'OrderCreated', :payload);
COMMIT;- A polling relay or change-data-capture tool such as Debezium publishes outbox rows to Kafka. The relay may publish the same event more than once after a failure, so every event needs a stable ID and consumers must be idempotent.
- Model the full workflow as a saga of local transactions:
- Choreography: each service reacts to events and publishes the next event. It has less central coupling, but the workflow is harder to see and change.
- Orchestration: a coordinator sends commands and records each step. The flow is easier to monitor, but the coordinator becomes important infrastructure.
- A compensation is a new business action, not a database rollback. For example, if inventory reservation fails after payment, issue a refund. The refund must also be idempotent because it may be retried.
- Store saga state such as
PENDING_PAYMENT,PENDING_INVENTORY,COMPLETED, orCOMPENSATING. Add timeouts for steps that never reply, and provide operator tools for stuck sagas. - Tell API clients that the operation is asynchronous. A
202 Acceptedresponse with a status endpoint is often clearer than pretending that all services committed immediately.
Red Flags:
- Uses cross-service XA or two-phase commit as the default without discussing availability and lock time.
- Writes to the database and Kafka separately without an outbox or another consistency plan.
- Assumes that an outbox relay publishes each event exactly once.
- Calls compensation a rollback and ignores that the compensating action can also fail.
- Has no idempotency, timeout, durable saga state, or plan for stuck workflows.
- Uses choreography for a large workflow without tracing, ownership, or documentation.
Q6 — Event-driven architecture, schema evolution, and governance
Question: Events are becoming the platform's integration backbone across many teams. Design for schema evolution, compatibility, and governance so producers and consumers can evolve independently.
Expected Depth:
- Uses a Schema Registry with Avro, Protobuf, or JSON Schema.
- Explains backward, forward, full, and transitive compatibility at a practical level.
- Prefers additive schema changes and knows that schema compatibility does not protect business meaning.
- Compares notification events with event-carried state transfer.
- Defines topic ownership, naming, retention, compaction, security, and replay rules.
- Tests producer and consumer compatibility in CI before deployment.
Ideal Answer:
- Use a Schema Registry and reject incompatible schema changes in CI. Choose the compatibility mode from the upgrade order:
- Backward compatibility: new consumers can read data written with the previous schema.
- Forward compatibility: old consumers can read data written with the new schema.
- Full compatibility: both directions are supported.
- Transitive compatibility: the check covers all registered versions, not only the latest version.
- Prefer additive changes. With Avro, a new field normally needs a default so new readers can read old records:
{
"name": "discountCode",
"type": ["null", "string"],
"default": null
}- For Protobuf, never reuse a removed field number. Reserve it so a future field cannot be mistaken for old data:
message Payment {
reserved 4;
string payment_id = 1;
int64 amount_minor = 2;
string currency = 3;
}- Schema compatibility checks structure, not meaning. Changing
amountfrom major units to minor units may pass a schema check but still break consumers. Use a new field or event version for a semantic change. - Treat events as public contracts owned by a named team. Prefer:
- A notification event when consumers can safely fetch current data and the extra request is acceptable.
- Event-carried state transfer when consumers need independence and can accept a larger event and duplicated data.
- Define retention from replay and audit needs. Use log compaction for latest-state topics only when keys and tombstone handling are correct.
- Protect personal and financial data with access controls, encryption, and a clear deletion policy. Kafka retention can make accidental sensitive data difficult to remove.
- Test replay in a separate consumer group, make handlers idempotent, and limit replay speed so old traffic does not overload databases or APIs.
Red Flags:
- Stores shared schemas only inside producer code and has no compatibility check.
- Renames, removes, or changes the meaning of a field without a migration plan.
- Reuses a removed Protobuf field number.
- Assumes that Schema Registry detects business-semantic changes.
- Has no topic owner, retention rule, access policy, or replay process.
- Replays a large topic directly into production dependencies without rate limits or idempotency.
Scoring Rubric
| Level | Pass bar |
|---|---|
| Junior | Explains Kafka's basic model and uses producers and consumers correctly. |
| Senior | Explains delivery trade-offs and handles failures without losing correctness. |
| Architect | Defines reliable event contracts, workflows, governance, and operational controls. |