The 30 second answer
Nakadi, Zalando's open-source event bus, exposes Kafka-style partitioned event types over HTTP. Each partition is an ordered log, and consumers track position with cursors. With the subscription API, Nakadi assigns partitions to consumers, streams batches, and expects cursor commits, giving at-least-once delivery. Because position is a cursor, a poison event forces a choice: stop the partition until it is fixed, or skip it and park it somewhere for repair.
Nakadi is Zalando's open-source event broker. Its manual describes it as a RESTful API over a partitioned log similar to Kafka, with event types, schemas and per-event-type authorization. It is a good public example to reason about event delivery in a catalog pipeline.
Ordering and partitions. An event type is split into partitions. The manual says each partition is a fully ordered log and "there is no global ordering across partitions". Producers choose a partition strategy; with the hash strategy and partition_key_fields, all events for the same key (an order number, or a SKU in a catalog) land in the same partition, so they are consumed in order.
Consuming with subscriptions. With the subscription API, Nakadi manages consumption. Clients open a stream; Nakadi assigns partitions automatically, and "the balancing unit is the partition", so a subscription cannot usefully have more clients than partitions. Clients receive batches with cursors and commit them by posting to the subscription's cursors resource. The manual describes the guarantee as at-least-once: if a commit fails, a client can see the same event again, but events that were committed successfully are not resent. Consumers should therefore be idempotent.
Flow control. Two stream parameters matter in a design discussion. max_uncommitted_events (default 10) is the number of uncommitted events Nakadi will stream before pausing; a commit resumes the stream. commit_timeout (at most 60 seconds) is how long Nakadi waits for a commit after sending a batch; if it does not arrive, the stream is terminated and its partitions are reassigned to other consumers. A consumer that processes slowly must commit in time or it will churn.
The poison event. Nakadi's own comparison with SQS explains the trade-off well. In SQS, each message is acknowledged individually, so only the unprocessable message is delayed and eventually moved to a dead-letter queue. With cursor semantics, as in Nakadi, Kinesis or Kafka, the client "has to decide: either stop all further message processing until the problem is fixed or skip the message and move the cursor". In a search ingestion pipeline:
- Stopping preserves order for that partition but blocks every product behind it, so search goes stale for a slice of the catalog.
- Skipping keeps the pipeline moving but loses the event unless you write it to a parking store with enough context to replay it after the fix.
A practical rule: validate against the schema at the edge, retry transient failures with backoff, and send events that fail deterministically to a parking store, alerting on its size. Because later events for the same SKU usually carry the full current state, a skipped event is often superseded, but a periodic reconciliation or reindex should still catch gaps.
Compaction and retention. An event type can use the compact cleanup policy to keep only the latest event per key, useful for a "current state of every product" stream that a new consumer can bootstrap from. Retention for normal event types is an operational setting: the open-source project's default is two days, and the manual warns that each installation may differ.
Alternate interview scenarios
"Why put an HTTP API in front of Kafka instead of letting every team use Kafka clients?"
The manual's reasons are organisational: many languages, protection of the cluster from misbehaving clients, schema enforcement, and authorization per event type. The cost is an extra hop and fewer client-side features, so it is a platform decision rather than a performance one.
"Consumer lag grows on one partition only. What do you suspect?"
A hot key that sends most events to that partition, a poison event that is being retried in a loop, or a slow downstream call for a subset of products. Per-partition lag and processing-time metrics, plus the offset of the last committed cursor, tell them apart. The Kafka-side forms of this are Why can one hot partition stall a consumer group, and how do you fix it? and How do you handle growing consumer lag when producers outpace consumers?.
The same cursor-log trade-offs, written against Apache Kafka rather than Nakadi, are answered in more depth on the Kafka interview page: at-least-once versus exactly-once, idempotent processing when the same event arrives twice, per-partition ordering, a crash after processing but before the offset is committed, a poison message that blocks the partition, retry, backoff or a dead-letter topic, and schema compatibility while older consumers are still running. Nakadi's HTTP API and cursor commits are the platform-specific part; those Kafka answers cover the log semantics underneath.
Sources (2)
- Nakadi manualnakadi.io
- Apache Kafka: Introduction (partitions and ordering)kafka.apache.org