Irbisa · Cheatsheet
Message Brokers: Kafka & RabbitMQ
Partitions, consumer groups and offsets, replication and acks, delivery guarantees, idempotent producers, retention and compaction, RabbitMQ exchanges and acks, event schemas
Explain Kafka's topic, partition, consumer group and offset, and how Kafka differs from RabbitMQ.
What exchange types does RabbitMQ have, and how do bindings and routing keys decide which queues receive a message?
func setup(ch *amqp.Channel) error { // durable: the exchange, the queue and the binding survive a broker restart if err := ch.ExchangeDeclare("orders", amqp.ExchangeTopic, true, false, false, false, nil); err != nil { return err } q, err := ch.QueueDeclare("billing", true, false, false, false, nil)…What did ZooKeeper do for Kafka, and what changed when Kafka 4.0 switched to KRaft?
Why is Kafka so fast on ordinary disks, and what do the append-only log, the page cache, batching and zero-copy each contribute?
How long does Kafka keep messages, and what is the difference between time- or size-based retention and log compaction?
Where does Kafka keep a consumer group's committed offsets, what does
auto.offset.resetdo, and how do you replay a topic from a given time?// A group reader: StartOffset applies only to partitions with no committed offset.func newBillingReader(brokers []string) *kafka.Reader { return kafka.NewReader(kafka.ReaderConfig{ Brokers: brokers, GroupID: "billing", Topic: "orders",…Events for one order arrive out of order, and growing the consumer group from 12 to 24 members did not reduce lag. 12 partitions, messages sent without a key. Why both?
// wrong: &kafka.Writer{Addr: ..., Topic: "orders"} with keyless messages;// the default balancer is RoundRobin, so one order's events scatter. // right: key by the entity whose order matters, with a balancer that hashes itvar w = &kafka.Writer{ Addr: kafka.TCP("kafka-1:9092", "kafka-2:9092"),…A broker died and some acknowledged messages are gone. How do
acks, the replication factor,min.insync.replicasand the ISR decide whether a write survives?// Topic: three copies, and writes are refused while fewer than two are in sync.func createPayments(ctx context.Context, c *kafka.Client) error { res, err := c.CreateTopics(ctx, &kafka.CreateTopicsRequest{Topics: []kafka.TopicConfig{{ Topic: "payments", NumPartitions: 12, ReplicationFactor: 3,…What does
enable.idempotenceon a Kafka producer prevent, how does it keep order across retries, and which duplicates does it not stop?Each HTTP request publishes one event through this
kafka.Writerand takes just over a second. Why, and what doBatchTimeout,BatchSizeandAsynctrade off?var w = &kafka.Writer{ Addr: kafka.TCP("kafka-1:9092", "kafka-2:9092"), Topic: "orders", Balancer: &kafka.Hash{},} // Called once per POST /orders, after the order is saved.func publishCreated(ctx context.Context, o Order) error { return w.WriteMessages(ctx, kafka.Message{ Key: []byte(o.ID), Value: o.JSON(), })}Which Go Kafka client would you choose (
franz-go,segmentio/kafka-go,confluent-kafka-go,sarama), and what does each one cost you?This consumer commits each offset, then hands the message to a worker pool. After a pod eviction, hundreds of events were silently lost. Where did they go?
func consume(ctx context.Context, r *kafka.Reader, jobs chan<- kafka.Message) error { for { m, err := r.FetchMessage(ctx) if err != nil { return err } if err := r.CommitMessages(ctx, m); err != nil { return err } jobs <- m // 16 goroutines read jobs and call handle(ctx, m) }}After a broker failover the payments consumer replays a minute of messages and some customers pay twice. Product wants exactly-once delivery. What do you build?
func handle(ctx context.Context, db *sql.DB, r *kafka.Reader, m kafka.Message) error { tx, err := db.BeginTx(ctx, nil) if err != nil { return err } defer tx.Rollback() // no-op after Commit…One malformed message makes the consumer fail and retry forever, and its whole partition stops moving. How do you handle poison messages?
func process(ctx context.Context, r *kafka.Reader, dlq *kafka.Writer, m kafka.Message) error { var err error for attempt := 0; attempt < 3; attempt++ { if err = handle(ctx, m); err == nil { return r.CommitMessages(ctx, m) }…What must be true for a RabbitMQ message to survive a broker restart, and what do publisher confirms and quorum queues add?
// Once at startup: a replicated queue and a channel in confirm mode.func setup(ch *amqp.Channel) error { _, err := ch.QueueDeclare("payments", true, false, false, false, amqp.Table{ amqp.QueueTypeArg: amqp.QueueTypeQuorum, // replicated with Raft, always durable }) if err != nil {…Find the bug: when a worker pod is killed mid-job, the RabbitMQ messages it was processing are gone, although the queue is durable and the messages persistent.
func work(ctx context.Context, ch *amqp.Channel) error { msgs, err := ch.Consume("thumbnails", "", true, false, false, false, nil) if err != nil { return err } for d := range msgs { if err := render(ctx, d.Body); err != nil { log.Printf("render failed: %v", err) } } return nil}Several consumers read one RabbitMQ queue. Who gets which message, what happens to ordering, and when do you need a single active consumer?
Should an
OrderUpdatedevent carry the whole order or only its id, and what does each choice cost the consumers?type OrderUpdated struct { OrderID string `json:"order_id"` Version int64 `json:"version"` // grows with every change at the source Status string `json:"status"` TotalCents int64 `json:"total_cents"`}…Producers and consumers of an event deploy on their own schedules. How do you change the event's schema safely, and what does a schema registry check?