kafka

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 28, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

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

type Decoder[T any] func([]byte) (T, error)

Decoder converts a raw Kafka message value to a typed Record value.

func JSONDecoder

func JSONDecoder[T any]() Decoder[T]

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

func NewSource[T any](cfg Config[T]) (*Source[T], error)

NewSource constructs a Kafka Source. The returned Source owns the underlying franz-go client; call Close to shut down cleanly.

func (*Source[T]) Close

func (s *Source[T]) Close() error

Close commits any outstanding marked offsets and shuts down the client.

func (*Source[T]) Name

func (s *Source[T]) Name() string

Name returns "kafka:<topic>".

func (*Source[T]) Read

func (s *Source[T]) Read(ctx context.Context, out chan<- source.Record[T]) error

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL