fix(deps): update module github.com/twmb/franz-go to v1.22.0 - #47
Open
renovate[bot] wants to merge 1 commit into
Open
renovate[bot] wants to merge 1 commit into
renovate[bot] wants to merge 1 commit into
Conversation
Author
ℹ️ Artifact update noticeFile name: go.modIn order to perform the update(s) described in the table above, Renovate ran the
Details:
|
renovate
Bot
force-pushed
the
renovate/github.com-twmb-franz-go-1.x
branch
from
August 12, 2026 17:42
b7af748 to
81088f7
Compare
renovate
Bot
force-pushed
the
renovate/github.com-twmb-franz-go-1.x
branch
from
August 30, 2026 22:06
81088f7 to
1ede8b4
Compare
renovate
Bot
force-pushed
the
renovate/github.com-twmb-franz-go-1.x
branch
2 times, most recently
from
September 2, 2026 02:41
1ede8b4 to
c32673d
Compare
renovate
Bot
force-pushed
the
renovate/github.com-twmb-franz-go-1.x
branch
from
September 15, 2026 01:42
c32673d to
895f146
Compare
renovate
Bot
force-pushed
the
renovate/github.com-twmb-franz-go-1.x
branch
from
September 18, 2026 07:06
895f146 to
8c350de
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains the following updates:
v1.3.1→v1.22.0Warning
Some dependencies could not be looked up. Check the Dependency Dashboard for more information.
Release Notes
twmb/franz-go (github.com/twmb/franz-go)
v1.22.0Compare Source
===
This release supports Kafka 4.3 and 4.4, has a few new APIs, and has a few
big internal improvements. In particular, I recommend checking out the new
StreamingCompressionoption, as well as evaluating if you'd like to useRackAwarePartitioning. There are some behavior changes that you should readabout below. The "next gen" rebalancer is now usable via the new
ServerSideBalanceroption. It's had a few releases to shake out bugsinternally (via integration tests and LLM audits), but if you do experience a
bug, please open an issue straightaway.
Some minor bug fixes (that were never reported) were found during the
implementation that are not worth mentioning.
kfake has also been significantly extended and I recommend checking out the
new APIs, in particular:
My
kclCLI has been significantly expanded as well and is worth checkingout. It supports essentially everything you can do with a cluster, and now
allows you to run a full broker locally via
kcl fake(in memory or a dumbdisk backed localhost broker) - as well as setup the fake broker with fault
injection. I've been running LLM audits and extensions to
kclin particularto try to shape it up to a "finalized" CLI shape. If you use it and have ideas
for improvements, please open an issue.
Behavior changes
Rack aware group partition assignment (KIP-881) now requires
BalanceRacks.v1.21.0 enabled group balancers to assign partitions based on the rack that
members were in if you used the range or sticky/cooperative-sticky balancers.
Well,
Rackis also used to opt into preferred read replica assignmentwhen fetching by the broker itself. These two decisions conflict with each
other. Now,
BalanceRacks()is required to opt into group balancers usingthe rack while balancing. The client warns when balancing if
BalanceRacksis on and the brokers have preferred read replicas enabled.
ConsumeResetOffsetdefaults toRewindOffset(time.Minute)ratherthan
NewOffset().AtStart(). Setting onlyConsumeStartOffsetno longersets
ConsumeResetOffset. I introducedConsumeStartOffseta while backbecause it was really weird IMO to use a reset offset for both how a consumer
starts and for how it recovers in the event of data loss or falling behind.
They were bidirectional since introduction, but since start is newer and much
less commonly used and you often don't want to recover from the start, I've
removed the start -> reset mapping when you only set the start. I recommend
reading the docs on both options for an updated understanding of when and
how they apply. As well, I've introduced
RewindOffset(d)which is onlyrelevant to the reset offset (rewind by
dduration from the last consumedoffset on data loss we cannot exactly recover from) and
LookbackOffset(d)which is relevant to both options but more useful for the start offset
(start consuming
dbefore the newest record; before Kafka 3.0 it isdbefore the current time). If a committed offset has fallen below the log
start, the first fetch answers
OFFSET_OUT_OF_RANGEand the reset offsetdecides where to resume. Before, a start offset of
AtEndwas copied intothe reset offset, so the consumer skipped to the end. Now, with the
defaults, it resumes at the log start.
ConsumeResetOffset's new default isRewindOffset(time.Minute).Topic recreation is now a hard failure. The client always
produces to and consumes from the first instance of a topic. If you delete
and recreate a topic, the client refuses the new version: buffered records
fail with
UNKNOWN_TOPIC_ID, fetches stop, offsets from the old topic cannotbe committed to the new one, and transactions on the old topic fail. This
needs a broker that reports topic IDs (Kafka 2.8+). Previously, some things
in the client continued to accidentally work, and the behavior was
unreliable and usually not good. If you want your application to stay alive
across topic recreations, you can
PurgeTopicsFromClientand, forconsumers,
AddConsumeTopics. More details about topic recreation are now ina new section in the README.
MaxDecompressBatchBytesnow blocks decompression if a batch woulddecompress too large (default 1GiB). Fetches when consuming can only
specify to the broker "give me X bytes of batches", but they cannot control
how large those batches decompress into. A hostile or buggy batch could OOM
your program. Now, a batch over the limit causes the partition to enter
a fatal state and return
ErrDecompressTooLargeonce from polling.The application can recover by manually skipping the batch with
SetOffsets(with the fields in the error; see the docs), or by restarting the client
with a higher limit. This option does not apply to custom decompressors,
but, custom decompressors can still return
ErrMaxDecompressto stopthe partition. This option is also closely related to streaming compression,
which is described below.
Improvements
gzip now uses klauspost/compress (same format). Its default level is
1.7x faster than stdlib's with a slightly better ratio; klauspost's default
maps to its level 5 where stdlib's mapped to 6, and level for level it is
1.1x to 1.2x faster.
WithLevel(n)now selects klauspost's leveln, sothe bytes a given level produces differ from before.
The sticky balancers are now exactly optimal on balance, then rack
placement (with
BalanceRacks), then stickiness. Balancing was alreadyload optimal but had some very niche edge cases where maximal stickiness
was not preserved, especially if balancing used racks. Rack placement
outranks stickiness: turning
BalanceRackson in a running groupreassigns, at its next rebalance, every partition held by a member in a
different zone from the partition's leader.
Sticky balancing is much faster, most of all on rejoins and on groups whose
members subscribe to different topics. Against v1.21.7: a rejoin of 100
members over 1600 topics of 100 partitions goes from 351ms to 24ms; a regex
shaped group of 500 members over 20,000 topics from 176ms and 810MB to 12ms
and 9MB; 2001 members over 500 topics of 2000 partitions with one narrow
subscriber from 3.4s to 0.3s. Fresh uniform balances are unchanged.
Features
Streaming compression
StreamingCompressionis an opt-in producer option that compresses apartition's backlog of batches together, bounded by their compressed size.
By default a batch is cut at
ProducerBatchMaxBytesmeasured on uncompressedrecords. Streaming compression will help reduce traffic to the broker and
increase how effective compression actually is (by pulling more data in at
once). A custom compressor makes this option a no-op.
The client is implemented such that each compression codec's worst case
overhead is tracked internally, which should avoid a compressed batch ever
exceeding
ProducerBatchMaxBytes. If this ever does happen, the clientdiscards the merge, logs a warning, disables streaming compression for the
client going forward (records are still compressed batch by batch), and asks
you to file an issue.
The client has a new option
MaxDecompressBatchBytesto bound both (a) howmuch the producer can stuff into a merged batch (i.e. how much it will
decompress into), and (b) the maximum size a consumer will decompress a batch
to; the consumer never decompresses past the bound (preventing a zip bomb).
The default is 1GiB.
Rack aware producer partitioning (KIP-1123)
RackAwarePartitioningsends unkeyed records to partitions whose leader is inthe client's
Rack(which must also be set), falling back to all partitionswhen no leader is. Keyed records are never affected. Unlike the Java client,
this works with any partitioner, since the eligible-broker filtering happens
before your partitioner is consulted. Note that this option skews which
partitions receive records if your producers are not spread across racks in
proportion to partition leaders.
ServerSideBalancer (KIP-848)
ServerSideBalanceropts into KIP-848 "next-gen" consumer groups, where thebroker's group coordinator assigns partitions rather than the client. This
requires Kafka 4.0+ and either a range or sticky / cooperative-sticky
balancer. This replaces the hidden
opt_in_kafka_next_gen_balancer_betacontext key from v1.19.0; the key still works in this release and is removed
in the next. The default remains the classic protocol, matching the Java
client. I still think the classic client side balancers are better (and this
client's implementation is way faster than the Java client), but if you want
to use server side balancing, it is strongly recommended to only use it if
your cluster is Kafka 4.3+. Before 4.3 (before KIP-1251), an offset commit
that races with a heartbeat epoch bump can fail with
STALE_MEMBER_EPOCH,which the client cannot detect nor handle.
BalanceInfo for custom balancers
A balancer that implements
GroupMemberBalancerInforeceives aBalanceInfobefore balancing: the group, generation, leader member ID, and lazily built
topic and broker metadata.
ConsumerBalancerimplements it, so balancersbuilt on
NewConsumerBalancercan callInfo(). This allows, for example, abalancer that assigns every partition to the leader with the other members as
hot standbys. Thanks @michaelwilner!
API additions
Relevant commits
There are many commits, but some of the more notable ones:
27d11286feature kversion: FeatureLevelDescription73358f62feature kversion: supported and finalized feature levels per release7be0be16behavior change kgo: add MaxDecompressedBatchBytesb37f1041feature kgo: add ServerSideBalancer to opt into KIP-848033a46c7improvement kgo: begin ApiVersions at the max a broker told us, for an hour46a9b2adbehavior change kgo: use ConsumeResetOffset when the broker loses data we cannot locate8b33e43dimprovement kgo: speed up compression on both the legacy and the merge path9de0fa36feature kgo: add StreamingCompression, compressed-size-bound batch mergingde7327e6feature kgo: detect misrouted connections (KIP-1242)8ad36ec7feature kgo: support TxnOffsetCommit v6e4f7bc43feature kgo: add rack-aware producer partitioning (KIP-1123)123f2ffaimprovement kgo: repair the sticky plan to the best balance, rack, and stickiness23ab9a0ebehavior change kgo: add BalanceRacks, gate rack aware balancing behind itd4f6db2fimprovement kgo: drop reassigned partitions in one pass in AdjustCooperative35efafc8behavior change kgo: fail records for a recreated topic instead of producing by name4f10346afeature kgo: expose BalanceInfo for custom balancer implementations (thanks @michaelwilner!)cd7f9b4efeature kgo: add NewRecordAttrs constructor (thanks @pracucci!)v1.21.7Compare Source
===
A handful of bug fixes and improvements found by users and while working on
v1.22. Rather than enumerating the relevant commits, you can check the git log
between v1.21.6 and this release - there are many minor commits. As well, kfake
has been improved significantly and has more API surface to aid in writing
tests.
A rare, very niche panic while producing has been fixed. Thanks
@PumpkinDemo for the report, see
#1385 for more details.
If retention deleted the segment a consumer was reading, the
OffsetOutOfRange reset listed by the last consumed timestamp and could skip
surviving records, or jump to the log end and skip everything. The reset
now resumes at the log start when below it.
Improved KIP-951 handling (the broker returning where a partition should move
with the produce response if the partition changed leadership). Previously, a
broker could return NotLeaderForPartition and hint the leader the client
was already using, at the same or an older epoch. These hints are now
ignored and the client backs off, rather than spinning. Thanks
@3AceShowHand for the report and
@jjj-n for a fix, see
#1412.
Rack aware balancers ignored rack for any topic the group leader did not
itself consume. A client now loads the rack for all partitions in the
group, even if the leader does not consume some of the topics.
The metadata cache has been improved (there were a few cases where it was
emptied erroneously).
Regex consuming now consistently never matches internal topics such as
__consumer_offsets. As well, the regex log no longer reports an excludedtopic as both added and skipped (thanks @lahsivjar).
Decompression allocates less (thanks @scunningham).
If you use pools, slices are now reliably returned if decompression errors.
The client now starts at a random seed broker rather than always the
first, so many clients starting at once no longer all hit the same seed.
Thanks @chailuecha!
A producer that receives RequestTimedOut or NotEnoughReplicasAfterAppend now
retries after the produce backoff rather than waiting for a metadata refresh.
A few other minor improvements and bug fixes.
v1.21.6Compare Source
===
Some bug fixes (mostly minor - hence the delay for the release) found by users
and further Claude audits. I am gearing up for a 1.22 release but some of the
features I am planning for are more complicated to review, so it may take a bit
of time. Anyway:
Previously, rollback from a cooperative group to an eager group was
deliberately not supported and there was a data race condition if this
happened. It is now technically supported, although you will experience
duplicate data. If you want a safe non-duplicate-causing rollback, you need
to turn off the entire group, remove the cooperative consumer, and swap the
whole group to eager rebalancing.
Fixed a
panic: close of closed channelon anacks=0produce connectionin a specific edge case (a broker connection dying before the connection
was fully established caused the panic).
If
EndTransactionfailed with an unconfirmed outcome (a transport error,exhausted retries, or
UNKNOWN_SERVER_ERROR), the documented abort retrywas a wire no-op and the next transaction could silently commit the prior
"failed" transaction's records under KIP-890 part 2. The producer ID is now
flagged for reload, which fence-aborts anything still ongoing broker-side.
GroupTransactSession.Endcould hang forever, ignoring its context, if thegroup had never joined (e.g. the consumed topic did not exist yet) and the
transaction committed no offsets.
Previously, if a broker replied to ApiVersions with an error, we ignored it
and you would eventually see an unclear error (usually a bare io.EOF, since
anything that rejects ApiVersions hangs up right after replying). These
errors are now handled correctly.
Some niche edge case bugs that are only worth reading about if you're super
interested were found in repeated Claude audits and were fixed (check the PR
/ git history). This includes further KIP-848 "next gen consumer group" fixes.
Relevant commits
582e0f21bugfix kgo: surface error codes in ApiVersions responses67ef4c61bugfix kgo: fix double close of a connection's deadCh on acks=0 produce3ac2fff1bugfix kgo: revoke everything when the group protocol downgrades from cooperative to eager795d5b61improvement kgo: flatten topic/partition maps in group rebalance logs (thanks @constanca-m!)70addc1eimprovement kgo: classify retired broker reads as broker dead (thanks @tomplarge!)18f9a10fimprovement deps: replace golang.org/x/crypto/pbkdf2 with stdlib crypto/pbkdf2 (thanks @macdewee!)6ecd2f9fbugfix kgo: recover when an attempted EndTxn outcome is unconfirmed821f879ebugfix kgo: fix GroupTransactSession.End hanging when the group never joinedv1.21.5Compare Source
===
Three bug fixes:
Fixed a nil-pointer panic when building a group
OffsetFetch: if the groupwas assigned a topic that was no longer in the client's tracked set --
reachable when a topic is purged from consuming while still assigned, for
example
PurgeTopicsFromConsumingoverlappingAddConsumeTopics, or theautomatic regex missing-topic purge --
loadTopicreturned nil anddereferencing it for the topic ID crashed the client. The topic ID is now
only set when the topic is known. Thanks @iwittkau!
Fixed a data race on a coordinator's cached node ID. When a broker
disconnected while a
FindCoordinatorload for that broker was still inflight,
deleteStaleCoordinatorsByNodecould read the in-flight load'snodefield before the loading goroutine published it (via closing theload's wait channel), which
go test -raceflagged. The node read nowhappens only after the load has been observed as complete. Thanks
@nikolauspschuetz!
A share partition that was listed in a ShareFetch only to carry a
piggybacked acknowledgement -- for a cursor that was revoked, paused, or
migrated to a new leader after its records were drained -- was added to the
broker's share session but never tracked client-side, so it could never be
forgotten. The broker would re-acquire and redeliver that partition's
records indefinitely while the client discarded them ("broker returned
partition ... we did not ask for"), spinning the share fetch loop. The
client now tracks every partition it sends, matching the broker's session
bookkeeping.
Relevant commits
ab185e42bugfix kgo: add nil check when loading topics in groupConsumer (thanks @iwittkau!)aca084edbugfix kgo: fix data race on coordinatorLoad.node (thanks @nikolauspschuetz!)754bc349bugfix kgo: forget piggyback-only partitions from the share sessionv1.21.4Compare Source
===
This release is a "large" (many commits) release that has many small or
hard to encounter bugs fixed. I pointed Claude's Fable at this repo and
ran some audit rounds while available and thankfully got through the highest
value audit rounds before Fable was removed.
For once, I will not be describing every bug fixed nor calling out every
relevant commit. Instead, if you are curious, look at
#1348. Some worthwhile
description is below.
Three important bug fixes to call out:
In transactional exactly-once consuming, a
SetOffsetsseek (which happensduring
GroupTransactSession.Endafter an aborted transaction) could beundone by a concurrent offset load (via a background list or epoch load) that
completed slightly later. This could happen when the client discovers a
partition leader moved while you are aborting, which could result in missed
records.
Consuming with
read_committedagainst a broker that returns a partition'saborted-transaction list out of offset order could surface aborted,
rolled-back records as if they were committed. Apache Kafka always returns
them in order so this was never observed there, but Redpanda does not (when
an aborted transaction is still in memory and an earlier one is already on
disk). The list is now sorted client-side, matching the Java client,
librdkafka, and Sarama.
GroupTransactSession.Endno longer reports a successful commit when thebroker answers
EndTxnwithUNKNOWN_SERVER_ERROR(seen from Redpanda insome older versions). Previously the consumer's offsets were advanced past a
transaction that may have aborted; now the commit is reported as failing
and the session rewinds for reprocessing.
Beyond those, by area:
Many transaction-path fixes for coordinator churn and KIP-890 part 2 that
would have resulted in not-working (hard client fail) or hung transactions:
InitProducerIDretriesCONCURRENT_TRANSACTIONSwhen taking over acrashed producer's transaction, retriable producer-id load failures are no
longer treated as fatal, KIP-890p2 is opted into only when the negotiated
versions actually support it (fixing spurious
INVALID_TXN_STATEon 4.0+clusters running older semantics), a transaction whose every produce failed
now aborts instead of hanging until the transaction timeout, and a failed
AddPartitionsToTxnno longer drops partitions added by an earlier request.GzipCompression().WithLevel(...)was completely broken and would panic.More KIP-848 (next-gen consumer group) robustness fixes under coordinator
and leader churn.
Stale consumer-group member rejoining fixes: a member that rejoins claiming a
partition at an old generation no longer panics the group leader or causes
two members to consume the same partition. Malformed member metadata,
duplicate member ids, and negative claimed partitions in a join are now
rejected or sanitized rather than mis-balancing or panicking the leader.
Metadata and topic recreation: a stale per-broker metadata view that
momentarily omits a just-added partition (the window right after
CreatePartitions) no longer fails buffered producer records or leaves anewly assigned consumer / share partition silently unconsumed; both heal
once metadata catches up.
Share consumer: fetch errors are now classified like the classic consumer
(retriable errors stripped, a metadata refresh triggered to heal a leader
move, top-level errors backed off) instead of stalling for up to
MetadataMaxAgeor hot-looping, and leader-move migrations are tracked sothat leaving or closing cannot strand un-acked records.
SASL: KIP-368 re-authentication no longer races the connection's other
reader, which could corrupt pipelined traffic on brokers that set a session
lifetime (e.g. AWS MSK IAM); requests now park and replay across a re-auth.
The Azure Event Hubs ApiVersions reset retry no longer leaks the abandoned
connection or silently downgrades it to v0.
KIP-714 client telemetry: the terminating push is now actually delivered on
Close, the.rateand.avgrollups are computed correctly (they wereconstant / wrong before), and an unsupported user-metric attribute no longer
corrupts the OTLP payload (which had disabled metrics for the rest of the
client's life).
Smaller consumer fixes: overlapping manual
CommitOffsetsno longer reopenautocommit early (which could rewind the committed offset), a conformant
UNDEFINED_EPOCH_OFFSETepoch response no longer raises a falseErrDataLoss, andFetches.EachTopicnow preservesTopicIDacrossmulti-broker responses (it was zero whenever more than one broker replied,
i.e. normally).
RecordReader/RecordFormatterno longer panic on truncated or malformedlayouts, accept
\xNNescapes for bytes above 0x7f, and reject layouts thatwould read nothing and loop forever.
WithPools: decompression no longer produces garbage when a pool hands backa non-zero-length sized slice, and pooled slices are no longer leaked for
batches that keep no records (e.g. aborted-transaction data under
read_committed).Other producer fixes:
EnsureProduceConnectionIsOpendials the right brokerfor filtered ids and no longer breaks an
acks=0connection, the adaptiveLeastBackupPartitionernow actually picks the least-backed-up partition,and producing during or after
Closefails cleanly instead of hanging alater
Flush.A broad set of guards against malformed or hostile broker responses that
could previously panic the fetcher, hot-loop, or mis-consume: negative or
oversized record counts and batch lengths, decompression bombs, duplicate or
omitted partitions, negative offsets, and unexpected top-level fetch errors.
The client is also more resilient when its own API contracts are violated
(e.g.
AllowRebalancecalled while a poll is in flight).v1.21.3Compare Source
===
This patch release contains a few bug fixes and a few internal improvements.
PollRecords/PollFetchescould permanently hang since v1.21.0 if aconsumer session stopped (usually via metadata updates) while
fetches to more than four brokers were pending and no poll was in
flight. This could only affect users that deliberately set
MaxConcurrentFetches(0),or that were using
ShareMaxRecordsStrict.Producing to a topic whose partitions ALL have a retriable load error
(e.g. a rolling restart of an RF=1 broker briefly leaving every
partition leaderless) no longer fails records up front with "unable to
partition record due to no usable partitions". Instead, the records
remain buffered and retried as metadata reloads.
Classic consumer groups now rejoin immediately when an offset commit
returns
UNKNOWN_MEMBER_IDorILLEGAL_GENERATION(the broker lostthe member, e.g. a session expired during a network blip), rather than
consuming as a zombie until the heartbeat loop notices the dead session.
DescribeShareGroupOffsets,AlterShareGroupOffsets, andDeleteShareGroupOffsetsare now routed to the group coordinatorrather than the share coordinator (which would reject the requests
for being misrouted).
The client-internal metadata cache now deeply clones the cached response
before putting it into the cache and before returning it via
RequestCachedMetadata(which is now used by default in kadm), eliminatingdata race possibilities.
Various next-gen rebalancer session improvements.
Relevant commits
f8842170improvement kgo: fall back to all partitions when no partition is writable (thanks @ericsg666!)824e34d2improvement kgo: rejoin a classic group when a commit returns a fatal member error (thanks @v14dis14v!)f520e820bugfix kgo: do not exit manageFetchConcurrency while sources are pending in wantFetch (thanks @SLoeuillet!)8d9c836bbugfix kgo: isolate metadata cache from broker response19f7dbb2bugfix kgo,kfake: route share group offset RPCs to the group coordinatorv1.21.2Compare Source
===
This patch release contains two narrow bug fixes and one small feature.
Deps are also bumped so that you are force-pinned to a klauspost/compress
version that has a stack-splitting bugfix that sometimes affected franz-go.
PurgeTopicsFromConsumingnow correctly persists deleted topics ifyou also had specific topics paused. Previously, when a topic had
partition-level pauses, unpausing the topic itself (while keeping
specific partitions paused) was bugged and the topic was stuck in
an "all paused" state (thanks @gorakdev!).
PollFetchesno longer surfaces a spuriousUNSTABLE_OFFSET_COMMITfetch error when an OffsetFetch retry is canceled mid-wait by a
rebalance or client close. Observed flaking
TestTxnEtl/sticky/848on KIP-848 consumer groups under transactional load.
The MSK IAM SASL mechanism now honors
AWS_REGIONwhen the brokerhostname does not match the standard MSK URL format, allowing
connections through custom DNS names (e.g. private link endpoints)
(thanks @janmoritzmeyer0210!).
Relevant commits
22a17320bugfix kgo: do not inject fake fetch error when OffsetFetch retry is canceled95d74ab3bugfix kgo: fix delTopics not writing back modified pausedPartitions struct (thanks @gorakdev!)40e5a0e5feature sasl/aws: support custom AWS MSK DNS names (thanks @janmoritzmeyer0210!)v1.21.1Compare Source
===
This patch release contains a few bug fixes in the new share consumer and a
few internal improvements. The highlights are noted below; if you are
interested in all the improvements, check the commits.
Share consuming no longer panics on empty batches from the broker.
The share acking previously did not work in tandem with Record.Recycle;
that has been fixed. Records that are recycled without being ack'd are
now also auto-accepted.
PurgeFetchTopicsno longer panics on KIP-848 consumer groups.ListGroupsandListTransactionsfan out to every broker andmerge the responses. During a group-coordinator or
transaction state leader migration, the same group or
transactional ID can transiently appear in both the old and new
coordinator's response, producing duplicates in the merged result.
The merges now dedupe by key (first shard response wins).
Two narrow producer logic races have been fixed; the code previously
auto-fixed after some time and there were no correctness issues, but
now the logic race has been eliminated.
Azure Event Hubs (and any other broker that advertises high
Produce / OffsetFetch / OffsetCommit versions while capping Metadata
below v10) is now handled correctly. Previously, OffsetFetch v8/v9
responses were spuriously stamped with
UNKNOWN_TOPIC_OR_PARTITIONclient-side, and Produce v13 / OffsetFetch v10+ / OffsetCommit v10+
could be sent with zero TopicIDs on the wire. The Produce path now
caps at v12 if any partition lacks a TopicID, and OffsetFetch /
OffsetCommit now pin to v9 in the same situation. Fixes #1312.
Relevant commits
3c1d0d2eb7463fd87bb7ccbcimprovement kgo: add guards across Produce / OffsetFetch / OffsetCommit for brokers that advertise high request versions while capping Metadata below v10 (Azure Event Hubs); fixes #1312c606410ebugfix (and other stuff) kgo: simplify share-consumer ack tracking, fix pool-reuse race2ff493a1kgo: consumer-path audit fixes1623dabakgo: produce path audit fixese7bed5fcimprovement kgo: dedupe groups and txns in fan-out sharders96a14015improvement kgo: consume-path alloc reductions and log-level gatesv1.21.0Compare Source
===
This is a relatively "major" minor release. It adds support for Kafka 4.2,
adds full support for KIP-932 share groups, adds
KIP-881 rack-aware partition assignment, adds a handful of other
features / options, and fixes several niche bugs.
The companion
kfakepackage has also been significantly extended; it nowsupports everything except delegation tokens, streams APIs, and broker
internal APIs. kfake can be used as a dumb localhost broker; it has an option
to persist to disk to tolerate restarts (and it even handles quick restarts
without interrupting any client state). See the run_tests.sh script and the
main.go file in pkg/kfake if you want to see about bootstrapping this yourself.
I may create some tiny 'dumbkafka' binary that supports running on localhost
with a few options. Regardless, kfake is quite neat.
The
kadmpackage has been extended with new share APIs. See theincoming
kadmtag for full details.As a meta note, this was a significant time investment (>4w most evenings and
weekends for KIP-932 alone). I hope future releases require less work; 932 is
the last major feature this library has been missing for a while, and of
upcoming KIPs, only transactional support for 932 looks to maybe be some
effort. That said, if you get a lot of value from this and have a spare
quarter, please consider sponsoring.
API additions
Share groups (KIP-932)
franz-go now fully supports KIP-932 share groups for consuming.
Share groups are the "queue-like" alternative to consumer groups: many
consumers can share a single partition, records are individually
acknowledged, and unacknowledged records are automatically redelivered.
The new share group API mirrors the existing consumer group shape; see
full documentation on pkg.go.dev:
Two new
RecordFormatterverbs were added alongside this:%D- share group delivery count%A- share group acquisition deadline (timestamp; supports the samestrftime/Go formatting as
%d)Rack-aware partition assignment (KIP-881)
Both the range and sticky balancers now understand consumer racks. If you
set
kgo.Rack, the leader will preferentially assign you partitions whoseleader is in the same rack, preserving the existing priority of balance
over locality over stickiness. I was originally not planning to support this
in franz-go since the next generation rebalancer was releasing at a similar
time, but I suspect it's worth it to keep the client-driven rebalancing for
a while.
Custom balancers that use the consumer protocol can use the new
(*ConsumerBalancer).PartitionRacks()method to access the computedpartition-rack map.
Other new kgo APIs
AllowIdempotentProduceCancellationpermits cancellation of in-flightidempotent records, at the cost of breaking idempotency's duplicate
guarantee. When a record is in-flight, the client cannot tell "never
written" from "written but reply lost". Cancelling leaves the client's
sequence window inconsistent with the broker: the next produce either
silently gap-accepts (broker wrote the cancelled records) or hits
OUT_OF_ORDER_SEQUENCEand forces a producer ID reload (broker didnot). Any application-level retry of a cancelled record that the
broker actually stored will duplicate on the broker - idempotent
dedupe cannot help because the window has reset. By default the
client refuses to cancel in this state and waits for the record's
outcome. Use this when time-bounded delivery matters more than
duplicate-avoidance. Incompatible with a transactional id.
ProducerBatchMaxBytesFntakes a topic name and returns the max batchsize for that topic, following the
RetryTimeout/RetryTimeoutFnpattern. Useful when you produce to multiple topics with different
broker-side
max.message.bytes.AlwaysRetryEOFkeeps retrying EOF errors indefinitely for users whoseinfrastructure considers EOF always transient. This option is actually
generally recommended for all users, BUT you really have to ensure your
SASL and TLS is setup correctly when using this option. Invalid SASL or
TLS is only visible as an EOF error, so the client by default uses
heuristics to hard fail requests without retrying if the first write triggers
an EOF. This has bit some users over time due to an EOF ALSO being seen
during restarts; this new option allows you to say "trust me, I know
my configuration is correct: keep retrying".
HookPollStartfires at the start of eachPollFetches/PollRecordscall. Thanks @rarguellof91!Misc additions
kadmgained aRequireStableoption for offset fetching.kfake.VirtualNetworkplus a newkgoxsync package enable fulltesting/synctestsupport against an in-process kfake cluster. Seeexamples/testing_with_kfake_and_synctest. Thanks@cupcicm!
Behavior changes
MaxConcurrentFetches(0)previously meant "unbounded"; it now means"no background fetches: a single fetch is only issued while you are
polling". This was a deliberate change to support
ShareMaxRecordsStrict, where no buffering is recommended because thebroker-side acquisition timer starts as soon as records are
returned. Use a negative value (e.g.
-1) for the prior unboundedbehavior (or just don't use the option; the default is unbounded).
This also means you can now use 0 to disable pre-fetching entirely,
even outside of share groups.
ApiVersionsis now sent on every new broker connection, not justthe first one per broker. The broker uses the ApiVersions request's
ClientSoftwareName/ClientSoftwareVersionfields to scope KIP-714metric subscriptions; caching and skipping ApiVersions meant later
connections (fetch, group, etc.) registered as
software=unknown, andone
kgo.Clientappeared as two software entities to the broker.Versions are still cached for request-version selection, so this is
only a one-extra-request-per-connection cost.
Group consumer
OffsetFetchnow hardcodesRequireStable=true. Theprior behavior could return stale committed offsets during a
rebalance, and in the worst case, result in duplicate messages.
You no longer need to use the
RequireStableFetchOffsetsoption.ErrRecordTimeoutnow wraps the last retry error seen while waitingon metadata in
waitUnknownTopic.errors.Iscontinues to work forErrRecordTimeout; you can now alsoerrors.Isthe underlying cause(e.g. SASL auth errors that previously disappeared).
BlockRebalanceOnPollno longer gates the assign-side callback if theuser did not register
OnPartitionsAssigned. Assign only addspartitions, so a user's in-flight commit cannot reference anything
they don't still own; the gate only existed to serialize user
callbacks with poll.
MessageTooLargeerrors now include the uncompressed and compressedmessage sizes. Thanks @anubhav21sharma!
Bug fixes
Fixed a KIP-951 bug where
ensureBrokerscould destroy unrelatedbroker objects when the broker set changed. This would manifest to you
as "the broker chosen for the request is dead" (or does not exist).
listOffsetshad two related bugs: a nil cursor panic in the drainpath, and a race where a metadata signal could be lost. These were
possible to encounter on extremely fast setups (the bugs were years
old, and I only encountered via localhost kfake testing).
The KIP-848 heartbeat path had several bugs that were shaken out by
long spin loops against the new kfake share/848 support (that said,
I am still not opting into KIP-848 by default, and will only allow
an option-based opt-in once KIP-1251 is released in Kafka 4.3).
fetchOffsetsnow validatesOffsetFetchresponses against what wasrequested (brokers have been seen to omit partitions) and correctly
handles the group-level error code.
Fixed a TOCTOU race between
producerIDandcreateReqthat couldconstruct a produce request with a stale producer ID.
failDialnow actually clears the stale coordinator / controllercache, so a single bad dial does not poison subsequent discovery.
Various smaller fixes: retrying when
broker.gowrote 0 bytes (treatas "didn't try"), transient dial errors now retry for the full
configured retry budget rather than the hard-coded ~1.5s cap (which
previously caused failures across 5-30s rolling restarts).
Improvements
batchPromisesis now backpressured: the ring is a dynamically-sizedcircular buffer that can block pushes at
max(maxBufferedRecords, 8192). Previously it could grow unboundedly and OOM the client whenrecords failed faster than
finishPromisescould process them.Two rounds of allocation reductions in hot paths: the produce blocking
path no longer forces its closure onto the heap,
recBuf's lingertimer is reused,
brokerCxn's 4-byte read buffer is reused,headerless records no longer allocate an empty header slice, and some
deprecated APIs were swapped out.
InitProducerIDfailures caused by transient broker errors (dialrefused, EOF across a broker restart, etc.) no longer surface as a
fatal "unrecoverable producer ID" error. The client now marks the
producer ID for reload on these errors so the next produce or begin
re-runs
InitProducerIDagainst the (probably now-available) broker.Relevant commits
8854973cimprovement kgo: recover producer ID from transient broker errors in maybeRecoverProducerIDc9a91d11feature kgo: add xsync package + kfake: add VirtualNetwork for synctest (thanks @manuc-conf!)a5a7c6f2kgo: send ApiVersions on every new broker connection14346ddcfeature kgo: add rack-aware partition assignment (KIP-881)40bf3e52feature kgo: add HookPollStart hook (thanks @rarguellof91!)68c85147behavior change kgo: skip BlockRebalanceOnPoll gate on assign with no OnPartitionsAssigned6d9a0188feature kgo: add %D (delivery count) and %A (acquisition deadline) to RecordFormatter0f0bca22feature kgo: add support for share groups (KIP-932)8762d567kgo: retry if we failed at 0 bytes written3e2f61a8kgo: retry transient dial errors for the configured retry budget857ed6dcbehavior change kgo: wrap last retry error into ErrRecordTimeoutd922b883kgo: fix failDial to actually clear stale coordinator/controller cacheaf5abf66bugfix kgo: fix KIP-951 ensureBrokers destroying unrelated broker objects6c7aab54bugfix kgo: fix listOrEpoch drain and nil cursor panic in listOffsets416e8269kgo: do not cancel prior in-flight offset commits2f666797kgo: simplify STALE_MEMBER_EPOCH retry in offset commit9dde6ad8behavior change kgo: hardcode RequireStable for group consumer OffsetFetch8a9400dfbugfix kgo: fix listOrEpoch race where metadata signal is lost0338467dfeature kgo: add AllowIdempotentProduceCancellation optionbc46151afeature kgo: add ProducerBatchMaxBytesFn for per-topic batch size limitsde2dff52bugfix kgo: handle group-level error code in fetchOffsets45ec9cfbbugfix kgo: fix TOCTOU race between producerID and createReq63cf8fa6kgo: add backpressure to batchPromises ring buffer9f15841bfeature kgo: add AlwaysRetryEOF option1de163c3include uncompressed/compressed message size in MessageTooLarge error (thanks @anubhav21sharma!)v1.20.7Compare Source
===
This patch release fixes numerous niche bugs - some user reported, some found
while investigating other things - contains a few behavior improvements,
extensive kfake additions, and many test additions / improvements.
There have been extensive additions to kfake over the course of the past
month; kfake now supports transactions, the next generation consumer group
protocol, and more Kafka APIs. franz-go integration tests now run and pass
against kfake in CI.
Testing has further been extensively improved: integration tests now run
against the latest patch version of Kafka for all major Kafka versions going
back to 0.11.0. The existing test suite has been extended to run while opting
into the next generation consumer group. An integration test against Kerberos
has been added. For ~roughly the past year (maybe half year), all bugs found
have had regression tests added in kfake. This release massively extends the
kfake test suite -- both with behavior tests that were ported via Claude
directly to franz-go (with license attribution!) and with behavior tests that
Claude generated specifically for kfake.
The "next generation" (KIP-848) consumer group code in franz-go itself has some
improvements. These improvements were found after adding 848 code to kfake,
which allowed for much faster integration test looping. This looping was still
very slow for what it's worth; towards the end, integration tests would pass
~40+ times with race mode over the course of two hours before failing once.
Claude was instrumental with adding appropriate log lines and tracing logs for
diagnosing extremely niche failures; things also got slower when a few specific
log lines that would've helped weren't added the first time...
Anyway,
Bug fixes
Returns from PollRecords / PollFetches that contained ONLY an error (context
cancellation or something) previously did not block rebalances, even if you
opted into BlockRebalanceOnPoll.
If, while idempotently producing, the client encountered
TIMED_OUTwhileproducing (retryable), the client considered this a "we definitively did not
produce" state, and allowed you to cancel the records. Well, maybe the records
actually did get produced broker side eventually, and now you re-produce new
records - the NEW records could be "deduplicated" due to how idempotency works.
This one is a bit niche, if you're interested, you should read #1217 and the
two PRs that address it.
My original implementation of how Kerberos handled authentication was
correct... for the time. I missed how it should have been touched up years
ago and now 4.0 hard deprecates the old auth flow. So, that's been found,
reported, and now fixed.
If a partition returned a retryable error in a metadata request (odd behavior
already) the first time the client is discovering the partition (i.e. on
startup), the client would not retry loading the partition right away (it
would, but much later on standard metadata refresh).
There was a very subtle, basically un-encounterable data race while
consuming. That was fixed in
5caaa1e0. It's so niche it's not worthwriting more about here.
RequestCachedMetadata, broken for a few releases, has been fixed. I plan toswitch kadm back to using it in the next kadm release.
There was a data race on client shutdown while leaving the group if you canceled
the client context.
Improvements
ConnIdleTimeoutis now obeyed more exactly, allowing you to more reliably reasonabout the maximum idle time. Thanks @carsonip!
At the end of a group transact session, I force a heartbeat to kinda "force
detect" the group is still alive (with some timing bounds). If the heartbeat
detected any error - including
REBALANCE_IN_PROGRESS- the session wouldabort no matter what. This has been changed to actually allow a commit (if
that's what you're doing) in certain scenarios (notably: the client detects
KIP-447 support on the broker and you are using RequireStable).
SetOffsets now does not let you set offsets for partitions that are not being
consumed. Previously, you could, but it'd just create entries in a map that were
never used. Those entries are no longer created.
ProduceSync now automatically un-lingers any partition that is produced to,
causing more immediate flushes. This should resolve lag that was introduced
from v1.20.0 where linger was set to 10ms by default. There are many usages of
franz-go I've seen in the wild where ProduceSync is used in random places to
produce one message before going back to other things.
764eb29dimprovement kgo: unlingerConfiguration
📅 Schedule: (UTC)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.