Documentation
¶
Overview ¶
Package kafka provides a Kafka-backed implementation of source.Source via the franz-go client. Suitable for both Apache Kafka and Amazon MSK.
Semantics: at-least-once with manual offset marking. Each record's Ack callback marks the underlying Kafka record for commit; AutoCommitMarks then periodically advances the committed offset. Records that are not Ack'd before consumer-group rebalance or process death are re-delivered to the next consumer — pipelines must dedup by EventID.
EventID is "<topic>:<partition>:<offset>", globally unique within a Kafka cluster.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config[T any] struct { // Brokers is the seed broker list, e.g. {"localhost:9092"} or MSK bootstrap servers. Brokers []string // Topic is the Kafka topic to consume from. Topic string // ConsumerGroup is the Kafka consumer-group ID. All workers sharing this ID // cooperatively partition the topic between them. ConsumerGroup string // Decode converts a raw message value to T. Use JSONDecoder[T]() for JSON-encoded // records, or supply your own for Avro / Protobuf / etc. Decode Decoder[T] // OnDecodeError, if non-nil, is called for every message whose Decode returned // an error. The default behavior is to drop the record silently and advance — // fine for development but dangerous in production. Wire this to a DLQ producer // or a metrics.Recorder.RecordError to surface poison pills. OnDecodeError func(raw []byte, partition int32, offset int64, err error) // EventID, if non-nil, is called on every decoded record to compute the // at-least-once dedup key. The default is "<topic>:<partition>:<offset>" // — globally unique within the cluster's history but only deduplicates // Kafka-side redeliveries (rebalance after a crash before commit). For // CDC pipelines where the same logical change can be produced twice // (Debezium retransmits, dual-write fixers), set this to extract the // upstream identifier (e.g. Mongo `_id`, Postgres LSN) from the record. EventID func(T) string // OnFetchError, if non-nil, is called for partition-level fetch errors that the // client is going to retry internally (broker bounce, leader change, etc). // Default: drop. Wire to logging / metrics to surface persistent failures. OnFetchError func(topic string, partition int32, err error) // Concurrency controls per-partition decode parallelism. When N > 1, the // Source spawns N decoder goroutines plus one fetcher; each partition is // pinned to worker (partition mod N), so per-partition order is preserved // while decode-heavy formats (Protobuf with schema lookups, encrypted // payloads) can saturate multiple cores. Default 1 — the single-goroutine // path is identical to the historical behavior. There is no benefit to // setting Concurrency above the partition count assigned to this consumer. Concurrency int // PartitionQueueSize is the per-worker channel depth used when Concurrency // > 1. Each fetch batch's per-partition slice is enqueued onto the // matching worker's channel; if a slow downstream backs up, the fetcher // blocks. Defaults to 256. PartitionQueueSize int // Extra lets callers append additional franz-go options (TLS, SASL, etc). Extra []kgo.Opt }
Config configures a Kafka Source.
type DLQConfig ¶ added in v0.2.0
type DLQConfig struct {
// Brokers is the seed broker list for the DLQ cluster — typically the
// same as the source cluster, but not required to be.
Brokers []string
// Topic is the dead-letter topic. Must exist (auto-create is disabled by
// default on most production clusters). Pre-provision with retention
// suited to your operational workflow.
Topic string
// Source is a short caller-supplied identifier (usually the source
// pipeline / topic name) that downstream DLQ consumers use to attribute
// records back to their origin.
Source string
// Extra lets callers append additional franz-go producer options (TLS,
// SASL, RequiredAcks=all, idempotent producer, custom partitioner, …).
Extra []kgo.Opt
}
DLQConfig configures a DLQProducer.
type DLQProducer ¶ added in v0.2.0
type DLQProducer struct {
// contains filtered or unexported fields
}
DLQProducer ships poison-pill records — those that Decode rejected — to a Kafka dead-letter topic so they can be inspected, replayed, or simply counted. It is a thin convenience over a franz-go producer wired into the Source's OnDecodeError callback shape.
The Source itself never knows about the DLQ: callers compose this producer with their Config[T] via the OnDecodeError field:
dlq, err := kafka.NewDLQProducer(kafka.DLQConfig{
Brokers: brokers,
Topic: "page_views.dlq",
Source: "page_views",
})
if err != nil { ... }
defer dlq.Close(ctx)
src, _ := kafka.NewSource(kafka.Config[PageView]{
Brokers: brokers,
Topic: "page_views",
ConsumerGroup: "page_views_worker",
Decode: kafka.JSONDecoder[PageView](),
OnDecodeError: dlq.OnDecodeError,
OnFetchError: dlq.OnFetchError, // optional — fetch errors are usually transient
})
Records are produced asynchronously. Close calls Flush so in-flight publishes are awaited; a returned error wraps the first publish that failed irreversibly. The producer does not retry forever — kgo's defaults apply (RecordPartitioner, RequiredAcks=leader, idempotent producer disabled). Override via Extra if you need different semantics.
Wire-format: the payload is the raw bytes the source observed; headers carry diagnostic context:
x-murmur-source-topic — originating topic (string) x-murmur-source-partition — partition number (string-formatted int32) x-murmur-source-offset — Kafka offset (string-formatted int64) x-murmur-source-name — caller-supplied source identifier (DLQConfig.Source) x-murmur-error — error message from Decode / fetch x-murmur-error-kind — "decode" | "fetch"
Downstream consumers (DLQ tail processes, dashboards) can filter on the kind header to separate poison records from transient fetch failures.
func NewDLQProducer ¶ added in v0.2.0
func NewDLQProducer(cfg DLQConfig) (*DLQProducer, error)
NewDLQProducer constructs a DLQProducer.
func (*DLQProducer) Close ¶ added in v0.2.0
func (d *DLQProducer) Close(ctx context.Context) error
Close flushes any in-flight publishes and shuts down the producer. Returns the first irrecoverable publish error observed during the producer's lifetime, if any.
func (*DLQProducer) OnDecodeError ¶ added in v0.2.0
func (d *DLQProducer) OnDecodeError(raw []byte, partition int32, offset int64, err error)
OnDecodeError is the callback shape consumed by Config.OnDecodeError. Publishes the raw bytes plus diagnostic headers to the DLQ topic asynchronously.
func (*DLQProducer) OnFetchError ¶ added in v0.2.0
func (d *DLQProducer) OnFetchError(topic string, partition int32, err error)
OnFetchError is the callback shape consumed by Config.OnFetchError. Publishes a zero-byte marker record carrying the topic/partition/error in headers — useful for catching repeated fetch failures (a broken broker, a missing topic) in a single stream alongside poison-pill content.
Most fetch errors are transient and the franz-go client retries them internally, so this can be noisy on real clusters; wire it only if you want to surface every retry.
type Decoder ¶
Decoder converts a raw Kafka message value to a typed Record value.
func JSONDecoder ¶
JSONDecoder returns a Decoder that unmarshals JSON into T.
type Source ¶
type Source[T any] struct { // contains filtered or unexported fields }
Source reads from a Kafka topic and yields source.Records.
func NewSource ¶
NewSource constructs a Kafka Source. The returned Source owns the underlying franz-go client; call Close to shut down cleanly.
func (*Source[T]) Read ¶
Read polls the consumer group and yields decoded records into out until ctx is canceled. Returns nil on graceful shutdown; non-nil only on a fatal client error.
When Config.Concurrency > 1, decode work fans out across N goroutines, with each partition pinned to worker (partition mod N) so per-partition order is preserved. The single-goroutine path (the default) is unchanged.