[{"data":1,"prerenderedAt":2499},["ShallowReactive",2],{"blog-post-\u002Fblog\u002Fkafka-golang-clients":3},{"id":4,"title":5,"body":6,"date":2485,"description":2486,"extension":2487,"meta":2488,"navigation":1130,"ogImage":2489,"path":2490,"pinned":2491,"seo":2492,"sitemap":2493,"stem":2494,"tags":2495,"__hash__":2498},"blog\u002Fblog\u002Fkafka-golang-clients.md","Kafka Go Clients: franz-go vs kafka-go vs Sarama",{"type":7,"value":8,"toc":2444},"minimark",[9,35,38,43,149,162,166,181,191,229,237,241,247,519,522,541,544,557,562,654,660,662,678,682,761,772,775,786,790,864,869,871,885,889,1008,1013,1017,1020,1064,1070,1073,1079,1082,1102,1207,1256,1259,1263,1445,1454,1458,1505,1509,1512,1516,1594,1598,1735,1744,1748,1915,1922,1926,2012,2015,2019,2041,2044,2048,2059,2070,2074,2080,2087,2096,2100,2119,2125,2131,2135,2143,2146,2166,2174,2178,2185,2193,2201,2208,2212,2216,2229,2233,2239,2246,2257,2261,2264,2299,2302,2306,2322,2326,2329,2386,2389,2393,2396,2400,2406,2412,2416,2422,2426,2434,2437,2440],[10,11,12,13,17,18,21,22,25,26,29,30,34],"p",{},"The Go ecosystem has four major Kafka clients that do not share a common implementation. ",[14,15,16],"strong",{},"Sarama",", ",[14,19,20],{},"kafka-go",", and ",[14,23,24],{},"franz-go"," each implement the Kafka protocol in pure Go, while ",[14,27,28],{},"confluent-kafka-go"," wraps Confluent's ",[31,32,33],"code",{},"librdkafka"," C library via cgo. The differences go beyond API style: Kafka feature coverage, consumer-group behavior, build requirements, and default partitioning all vary from client to client.",[10,36,37],{},"This guide compares the four across deployment, consumer-group protocols (including KIP-848), authentication, default partitioners, and Schema Registry support. Capabilities are evaluated against specific releases, and a GitHub snapshot compares maintenance activity. Finally, it outlines when to stay on Sarama or kafka-go and how to check whether a new client routes keyed records to different partitions than the old one.",[39,40,42],"h2",{"id":41},"quick-decision","Quick decision",[44,45,46,62],"table",{},[47,48,49],"thead",{},[50,51,52,56,59],"tr",{},[53,54,55],"th",{},"Situation",[53,57,58],{},"Best starting point",[53,60,61],{},"What to verify",[63,64,65,82,115,133],"tbody",{},[50,66,67,73,75],{},[68,69,70],"td",{},[14,71,72],{},"New pure Go service, or a build that must avoid cgo",[68,74,24],{},[68,76,77,78,81],{},"Schema Registry ",[31,79,80],{},"pkg\u002Fsr"," requires application-provided codecs; one account handled all recorded PR merges in the 12-month snapshot.",[50,83,84,96,98],{},[68,85,86],{},[14,87,88,91,92,95],{},[31,89,90],{},"Reader"," \u002F ",[31,93,94],{},"Writer"," API for basic produce and consume",[68,97,20],{},[68,99,100,106,107,114],{},[14,101,102,103,105],{},"The default ",[31,104,94],{}," ignores record keys and uses round-robin."," There is no high-level transactional producer API, cooperative-sticky assignor, or KIP-848 consumer-group support. Confluent Cloud ",[108,109,113],"a",{"href":110,"rel":111},"https:\u002F\u002Fdocs.confluent.io\u002Fcloud\u002Fcurrent\u002Fclient-apps\u002Fdeprecate-how-to.html",[112],"nofollow","flags its consumer as noncompliant",".",[50,116,117,122,124],{},[68,118,119],{},[14,120,121],{},"Confluent's Schema Registry serializers or commercial support, and cgo is acceptable",[68,123,28],{},[68,125,126,127,130,131,114],{},"Alpine\u002Fmusl builds require ",[31,128,129],{},"-tags musl","; GSSAPI requires a separately installed ",[31,132,33],{},[50,134,135,140,142],{},[68,136,137],{},[14,138,139],{},"Migrating from Sarama or kafka-go for broader protocol support without cgo",[68,141,24],{},[68,143,144,145,114],{},"Default partitioners differ, so keyed records can move to other partitions. See ",[108,146,148],{"href":147},"#keep-keyed-records-on-the-same-partitions","Keep keyed records on the same partitions",[150,151,152],"blockquote",{},[10,153,154,157,158,114],{},[14,155,156],{},"Tip:"," These clients provide Kafka messaging, not a Kafka Streams equivalent. For Kafka-backed tables, messaging frameworks, or in-process stream pipelines, see ",[108,159,161],{"href":160},"\u002Fblog\u002Fkafka-streams-in-golang","Goka vs Watermill vs go-streams",[39,163,165],{"id":164},"pure-go-or-cgo","Pure Go or cgo?",[10,167,168,169,172,173,176,177,180],{},"Sarama, kafka-go, and franz-go build with ",[31,170,171],{},"CGO_ENABLED=0",". For Linux targets, a standard Go cross-build can produce a statically linked binary without a target C toolchain. This simplifies deployment in ",[31,174,175],{},"scratch"," or ",[31,178,179],{},"distroless"," images, although TLS connections still need the required CA certificates. Pure Go client code also remains visible in Go stack traces and CPU profiles.",[10,182,183,187,188,190],{},[108,184,28],{"href":185,"rel":186},"https:\u002F\u002Fgithub.com\u002Fconfluentinc\u002Fconfluent-kafka-go",[112]," wraps ",[31,189,33],{}," through cgo. That trade-off is often worthwhile when you need Confluent's first-party Schema Registry serializers or commercial support. The build and deployment requirements are:",[192,193,194,205,217],"ul",{},[195,196,197,200,201,204],"li",{},[14,198,199],{},"Build:"," ",[31,202,203],{},"CGO_ENABLED=1"," and a C compiler are required. Cross-compiling needs a C toolchain for the target architecture.",[195,206,207,210,211,213,214,216],{},[14,208,209],{},"Containers:"," The bundled ",[31,212,33],{}," archive does not make the final binary fully static: a glibc build still depends on the target system's libc. Alpine\u002Fmusl builds require ",[31,215,129],{},"; a fully static musl build also needs a musl C compiler and external-linker flags. Test the binary inside the production container image.",[195,218,219,222,223,225,226,114],{},[14,220,221],{},"GSSAPI\u002FKerberos:"," The bundled library excludes GSSAPI. Enabling it requires a separately installed ",[31,224,33],{}," and ",[31,227,228],{},"-tags dynamic",[10,230,231,232,114],{},"See Confluent's ",[108,233,236],{"href":234,"rel":235},"https:\u002F\u002Fgithub.com\u002Fconfluentinc\u002Fconfluent-kafka-go\u002Fblob\u002Fv2.15.1\u002FREADME.md#librdkafka",[112],"build instructions",[39,238,240],{"id":239},"kafka-go-clients-compared","Kafka Go clients compared",[10,242,243,246],{},[14,244,245],{},"Capabilities verified against official documentation and package artifacts:"," 26 Sep 2026",[44,248,249,284],{},[47,250,251],{},[50,252,253,256,263,270,277],{},[53,254,255],{},"Area",[53,257,258],{},[108,259,262],{"href":260,"rel":261},"https:\u002F\u002Fpkg.go.dev\u002Fgithub.com\u002Ftwmb\u002Ffranz-go@v1.22.0\u002Fpkg\u002Fkgo",[112],"franz-go v1.22.0",[53,264,265],{},[108,266,269],{"href":267,"rel":268},"https:\u002F\u002Fgithub.com\u002Fsegmentio\u002Fkafka-go\u002Freleases\u002Ftag\u002Fv0.4.51",[112],"kafka-go v0.4.51",[53,271,272],{},[108,273,276],{"href":274,"rel":275},"https:\u002F\u002Fgithub.com\u002FIBM\u002Fsarama\u002Freleases\u002Ftag\u002Fv1.61.0",[112],"Sarama v1.61.0",[53,278,279],{},[108,280,283],{"href":281,"rel":282},"https:\u002F\u002Fgithub.com\u002Fconfluentinc\u002Fconfluent-kafka-go\u002Freleases\u002Ftag\u002Fv2.15.1",[112],"confluent-kafka-go v2.15.1",[63,285,286,323,373,396,417,447,468,495],{},[50,287,288,293,298,304,315],{},[68,289,290],{},[14,291,292],{},"API",[68,294,295],{},[31,296,297],{},"kgo.Client",[68,299,300,17,302],{},[31,301,94],{},[31,303,90],{},[68,305,306,17,309,17,312],{},[31,307,308],{},"SyncProducer",[31,310,311],{},"AsyncProducer",[31,313,314],{},"ConsumerGroup",[68,316,317,17,320],{},[31,318,319],{},"Producer",[31,321,322],{},"Consumer",[50,324,325,330,342,356,362],{},[68,326,327],{},[14,328,329],{},"Consumer model",[68,331,332,334,335,91,338,341],{},[31,333,297],{}," with ",[31,336,337],{},"PollFetches",[31,339,340],{},"PollRecords","; configurable commits",[68,343,344,334,346,349,350,225,353],{},[31,345,90],{},[31,347,348],{},"GroupID","; ",[31,351,352],{},"FetchMessage",[31,354,355],{},"CommitMessages",[68,357,358,359],{},"Claim handlers in ",[31,360,361],{},"ConsumerGroup.Consume",[68,363,364,225,367,91,370,341],{},[31,365,366],{},"Consumer.SubscribeTopics",[31,368,369],{},"Poll",[31,371,372],{},"ReadMessage",[50,374,375,380,385,388,391],{},[68,376,377],{},[14,378,379],{},"Admin API",[68,381,382],{},[31,383,384],{},"kadm",[68,386,387],{},"Admin helpers in the same module",[68,389,390],{},"Cluster admin types in the same module",[68,392,393],{},[31,394,395],{},"AdminClient",[50,397,398,403,406,409,412],{},[68,399,400],{},[14,401,402],{},"Transactions \u002F EOS",[68,404,405],{},"Transactions and documented EOS flow",[68,407,408],{},"No high-level transactional producer or consume-transform-produce API",[68,410,411],{},"Producer transactions",[68,413,414,416],{},[31,415,33],{}," transactions",[50,418,419,424,431,434,441],{},[68,420,421],{},[14,422,423],{},"Consumer-group protocols",[68,425,426,427,430],{},"Cooperative-sticky; KIP-848 via ",[31,428,429],{},"ServerSideBalancer()"," in v1.22.0 (Kafka 4.0+, 4.3+ recommended)",[68,432,433],{},"No built-in cooperative assignor; no documented KIP-848 consumer membership",[68,435,436,437,440],{},"Cooperative-sticky from v1.60.1; KIP-848 ",[31,438,439],{},"ConsumerGroupDescribe"," API only, no consumer membership",[68,442,443,446],{},[31,444,445],{},"cooperative-sticky","; KIP-848 GA from 2.12.0 (Kafka 4.0+; opt-in)",[50,448,449,454,457,460,463],{},[68,450,451],{},[14,452,453],{},"Authentication",[68,455,456],{},"TLS; SASL PLAIN, SCRAM, OAUTHBEARER, GSSAPI, AWS_MSK_IAM",[68,458,459],{},"TLS; SASL PLAIN and SCRAM; separate AWS MSK IAM module",[68,461,462],{},"TLS; SASL PLAIN, SCRAM, OAUTHBEARER, GSSAPI",[68,464,465,466],{},"TLS; SASL PLAIN, SCRAM, OAUTHBEARER; GSSAPI with dynamic ",[31,467,33],{},[50,469,470,475,478,485,488],{},[68,471,472],{},[14,473,474],{},"Default partitioner",[68,476,477],{},"Keyed: Murmur2; unkeyed: adaptive sticky batches",[68,479,480,481,484],{},"Keyed and unkeyed: round-robin; keys ignored unless ",[31,482,483],{},"Balancer"," is set",[68,486,487],{},"Keyed: FNV-1a; unkeyed: random",[68,489,490,491,494],{},"Keyed: CRC32 (",[31,492,493],{},"consistent_random","); unkeyed: sticky random",[50,496,497,502,507,510,512],{},[68,498,499],{},[14,500,501],{},"Schema Registry",[68,503,504,506],{},[31,505,80],{}," client and Serde helper; provide codecs",[68,508,509],{},"Separate integration",[68,511,509],{},[68,513,514,515,518],{},"First-party ",[31,516,517],{},"schemaregistry"," serializers",[10,520,521],{},"A successful produce does not prove another service can deserialize the record. Schema Registry framing, headers, and compression still have to match across clients.",[10,523,524,525,17,530,21,535,540],{},"Cooperative rebalancing under the classic group protocol (KIP-429) is separate from KIP-848's broker-side assignment. The default partitioner row describes the high-level producer defaults. See the ",[108,526,529],{"href":527,"rel":528},"https:\u002F\u002Fgithub.com\u002Fsegmentio\u002Fkafka-go\u002Fblob\u002Fv0.4.51\u002FREADME.md#compatibility-with-other-clients",[112],"kafka-go partitioner guide",[108,531,534],{"href":532,"rel":533},"https:\u002F\u002Fpkg.go.dev\u002Fgithub.com\u002Ftwmb\u002Ffranz-go@v1.22.0\u002Fpkg\u002Fkgo#SaramaCompatHasher",[112],"franz-go partitioner documentation",[108,536,539],{"href":537,"rel":538},"https:\u002F\u002Fgithub.com\u002Fconfluentinc\u002Flibrdkafka\u002Fblob\u002Fv2.15.1\u002FCONFIGURATION.md",[112],"librdkafka configuration"," before changing a keyed producer.",[542,543,24],"h3",{"id":24},[10,545,546,550,551,554,555,114],{},[108,547,24],{"href":548,"rel":549},"https:\u002F\u002Fgithub.com\u002Ftwmb\u002Ffranz-go",[112]," is a pure Go client built around a single ",[31,552,553],{},"*kgo.Client"," for producing, consuming, and issuing Kafka requests. The project aims to support every client-facing Kafka feature from Kafka 0.8.0 onward. Admin helpers live in ",[31,556,384],{},[10,558,559],{},[14,560,561],{},"Capabilities and limitations",[192,563,564,577,588,602,619,625,643],{},[195,565,566,569,570,572,573,176,575,114],{},[14,567,568],{},"API:"," A single ",[31,571,297],{}," handles both producing and consuming; there are no separate producer and consumer types. Consumers retrieve records through ",[31,574,337],{},[31,576,340],{},[195,578,579,582,583,114],{},[14,580,581],{},"Protocol coverage:"," v1.22.0 includes idempotent and transactional production and a documented consume-transform-produce EOS flow. See the project's ",[108,584,587],{"href":585,"rel":586},"https:\u002F\u002Fgithub.com\u002Ftwmb\u002Ffranz-go\u002Fblob\u002Fv1.22.0\u002Fdocs\u002Ftransactions.md",[112],"transaction guide",[195,589,590,593,594,597,598,601],{},[14,591,592],{},"Consumer commits:"," Consumer groups automatically commit offsets by default, including offsets for records returned by a poll that may still be processing. Use ",[31,595,596],{},"AutoCommitMarks()",", disable auto-commit and commit after processing, or use ",[31,599,600],{},"BlockRebalanceOnPoll()"," with its rebalance-timeout trade-off when processing must finish first.",[195,603,604,607,608,614,615,618],{},[14,605,606],{},"Consumer groups:"," Cooperative-sticky assignment is supported under the classic protocol. KIP-848 is available through ",[108,609,612],{"href":610,"rel":611},"https:\u002F\u002Fpkg.go.dev\u002Fgithub.com\u002Ftwmb\u002Ffranz-go@v1.22.0\u002Fpkg\u002Fkgo#ServerSideBalancer",[112],[31,613,429],{}," in v1.22.0. It requires Kafka 4.0+ and a range or sticky balancer; the project recommends Kafka 4.3+ because earlier brokers can return ",[31,616,617],{},"STALE_MEMBER_EPOCH"," during normal offset commits. The classic protocol remains the default.",[195,620,621,624],{},[14,622,623],{},"Default partitioner:"," Keyed records use Murmur2, matching the Java client's default. Unkeyed records stay on a partition until the default byte threshold is reached, then choose the next partition adaptively.",[195,626,627,630,631,638,639,642],{},[14,628,629],{},"Schema Registry:"," The separately versioned ",[108,632,635,637],{"href":633,"rel":634},"https:\u002F\u002Fpkg.go.dev\u002Fgithub.com\u002Ftwmb\u002Ffranz-go\u002Fpkg\u002Fsr@v1.8.0",[112],[31,636,80],{}," module"," provides a Registry API client and a ",[31,640,641],{},"Serde"," for Confluent's wire format. It does not include Avro, Protobuf, or JSON Schema codecs; applications register their own encode and decode functions.",[195,644,645,648,649,653],{},[14,646,647],{},"Maintenance:"," The ",[108,650,652],{"href":651},"#github-repository-metrics","repository snapshot"," shows the most commits and merged PRs of the four clients, with a median merge time of 2.1 hours. One account handled all recorded merges in the preceding year.",[10,655,656,659],{},[14,657,658],{},"When to choose:"," Choose franz-go for a new cgo-free service that needs broad Kafka protocol coverage, transactional consume-transform-produce flows, or KIP-848. Prefer confluent-kafka-go when Confluent's first-party serializers or commercial support are required and cgo is acceptable.",[542,661,20],{"id":20},[10,663,664,668,669,225,671,673,674,677],{},[108,665,20],{"href":666,"rel":667},"https:\u002F\u002Fgithub.com\u002Fsegmentio\u002Fkafka-go",[112]," is Segment's pure Go client. It provides context-aware high-level ",[31,670,94],{},[31,672,90],{}," types, plus the lower-level ",[31,675,676],{},"Conn"," API for direct broker operations.",[10,679,680],{},[14,681,561],{},[192,683,684,694,713,726,742,751,756],{},[195,685,686,688,689,225,691,693],{},[14,687,568],{}," The high-level ",[31,690,90],{},[31,692,94],{}," types avoid Sarama's handler and channel model, reducing setup code for basic produce and consume.",[195,695,696,698,699,701,702,705,706,708,709,712],{},[14,697,623],{}," The default ",[31,700,94],{}," balancer is ",[14,703,704],{},"round-robin and ignores record keys",". Set ",[31,707,483],{}," explicitly, for example to ",[31,710,711],{},"&kafka.Hash{}",", if a key must stay on one partition.",[195,714,715,717,718,720,721,334,723,725],{},[14,716,592],{}," With a consumer group, ",[31,719,372],{}," automatically commits offsets and may do so before processing finishes. Use ",[31,722,352],{},[31,724,355],{}," for explicit control. Committing a later message also commits every earlier offset in that partition, so concurrent processing must preserve commit order.",[195,727,728,730,731,736,737,739,740,114],{},[14,729,581],{}," The project ",[108,732,735],{"href":733,"rel":734},"https:\u002F\u002Fgithub.com\u002Fsegmentio\u002Fkafka-go\u002Fblob\u002Fv0.4.51\u002FREADME.md#kafka-versions",[112],"documents testing"," against Kafka 0.10.1.0 through 2.7.1; later brokers may work, but newer APIs may be missing. v0.4.51 has no high-level transactional producer or consume-transform-produce API. Administrative operations use ",[31,738,676],{}," methods and protocol request types rather than a dedicated ",[31,741,395],{},[195,743,744,746,747,114],{},[14,745,606],{}," No built-in cooperative-sticky assignor; no documented KIP-848 consumer-group membership. Confluent Cloud ",[108,748,750],{"href":110,"rel":749},[112],"lists the latest kafka-go consumer as noncompliant",[195,752,753,755],{},[14,754,629],{}," kafka-go does not include a Schema Registry client or serializers; schema encoding requires a separate integration.",[195,757,758,760],{},[14,759,647],{}," The 25 Sep 2026 snapshot shows no default-branch commits or merged PRs in 90 days, with 83 open PRs at a median age of 2.6 years. Check the release history and issue backlog for requirements you expect to evolve.",[10,762,763,765,766,768,769,771],{},[14,764,658],{}," Choose kafka-go when the ",[31,767,90],{},"\u002F",[31,770,94],{}," API is the priority, v0.4.51 covers the required broker and consumer-group behavior, and its maintenance pace is acceptable for the service. For a new service that needs broader protocol coverage, start with franz-go.",[542,773,16],{"id":774},"sarama",[10,776,777,781,782,785],{},[108,778,16],{"href":779,"rel":780},"https:\u002F\u002Fgithub.com\u002FIBM\u002Fsarama",[112]," is the oldest of the four clients: a pure Go library created at Shopify in 2013 and now hosted by IBM under the import path ",[31,783,784],{},"github.com\u002FIBM\u002Fsarama",". It provides synchronous and asynchronous producers, consumer groups, and admin APIs.",[10,787,788],{},[14,789,561],{},[192,791,792,798,815,834,842,852,857],{},[195,793,794,797],{},[14,795,796],{},"Compatibility:"," Sarama documents a \"2 releases + 2 months\" policy for Kafka and Go: it supports the two latest stable releases, with a two-month grace period for older ones. Pin the module version and review the changelog when upgrading Kafka or Go.",[195,799,800,803,804,807,808,810,811,814],{},[14,801,802],{},"API surface:"," Sarama separates synchronous and asynchronous producers, consumer groups, and admin operations. Consumer groups require a ",[31,805,806],{},"ConsumerGroupHandler",". Applications using ",[31,809,311],{}," must drain the error channel and, when ",[31,812,813],{},"Producer.Return.Successes"," is enabled, the success channel. This explicit lifecycle provides control but adds coordination and boilerplate.",[195,816,817,200,819,225,822,825,826,829,830,833],{},[14,818,592],{},[31,820,821],{},"MarkMessage",[31,823,824],{},"MarkOffset"," only mark an offset as processed. With auto-commit enabled by default, Sarama commits marked offsets in the background every second; offsets advance only as far as the handler marks them. Call ",[31,827,828],{},"session.Commit()"," for a synchronous commit. Marked offsets not yet committed can be lost in a crash, so processing should tolerate redelivery. A new group starts from the newest offset by default (",[31,831,832],{},"Consumer.Offsets.Initial = OffsetNewest",").",[195,835,836,838,839,841],{},[14,837,606],{}," Cooperative-sticky rebalancing under the classic group protocol is supported from v1.60.1. v1.61.0 adds the KIP-848 ",[31,840,439],{}," API only; KIP-848 consumer-group membership is not supported.",[195,843,844,200,846,849,850,114],{},[14,845,623],{},[31,847,848],{},"NewHashPartitioner"," hashes non-nil keys with FNV-1a, not the Java client's Murmur2, and sends records with nil keys to a random partition. Check key placement before another producer writes the same keys; see ",[108,851,148],{"href":147},[195,853,854,856],{},[14,855,629],{}," Sarama does not include a Schema Registry client or serializers, so schema encoding requires a separate integration.",[195,858,859,648,861,863],{},[14,860,647],{},[108,862,652],{"href":651}," shows recent commits and merged PRs, but one account handled 98% of recorded PR merges in the preceding year.",[10,865,866,868],{},[14,867,658],{}," Keep Sarama when an existing application meets its requirements. For a new pure Go service, start with franz-go unless compatibility with existing Sarama code and tooling, or the team's experience with its API, provides a concrete advantage.",[542,870,28],{"id":28},[10,872,873,877,878,880,881,884],{},[108,874,28],{"href":875,"rel":876},"https:\u002F\u002Fdocs.confluent.io\u002Fkafka-clients\u002Fgo\u002Fcurrent\u002Foverview.html",[112]," is Confluent's Go client. Producer, consumer, and admin APIs wrap ",[31,879,33],{}," through cgo. See ",[108,882,165],{"href":883},"#pure-go-or-cgo"," for build and container requirements.",[10,886,887],{},[14,888,561],{},[192,890,891,920,927,959,978,996,1001],{},[195,892,893,895,896,899,900,903,904,907,908,910,911,176,913,915,916,919],{},[14,894,568],{}," Configuration uses ",[31,897,898],{},"ConfigMap"," with librdkafka property names rather than typed Go option functions. ",[31,901,902],{},"Producer.Produce()"," queues records asynchronously; final delivery reports arrive through ",[31,905,906],{},"Events()"," or a message-specific delivery channel. ",[31,909,322],{}," uses timeout-based polling (",[31,912,369],{},[31,914,372],{},") rather than ",[31,917,918],{},"context.Context","-driven cancellation.",[195,921,922,924,925,114],{},[14,923,581],{}," Most broker protocol behavior comes from the bundled librdkafka version. v2.15.1 includes idempotent and transactional producers and broad admin operations through ",[31,926,395],{},[195,928,929,200,931,17,934,21,937,940,941,944,945,176,948,951,952,176,955,958],{},[14,930,592],{},[31,932,933],{},"enable.auto.commit=true",[31,935,936],{},"auto.commit.interval.ms=5000",[31,938,939],{},"enable.auto.offset.store=true"," by default. For at-least-once processing, set ",[31,942,943],{},"enable.auto.offset.store=false",", call ",[31,946,947],{},"StoreMessage",[31,949,950],{},"StoreOffsets"," after processing, and leave auto-commit enabled. Alternatively, disable auto-commit and call ",[31,953,954],{},"CommitMessage",[31,956,957],{},"CommitOffsets"," explicitly.",[195,960,961,963,964,967,968,971,972,977],{},[14,962,606],{}," Cooperative-sticky assignment is available through ",[31,965,966],{},"partition.assignment.strategy",". KIP-848 is GA from v2.12.0 with Kafka 4.0+ brokers; enable it with ",[31,969,970],{},"group.protocol=consumer",". See Confluent's versioned ",[108,973,976],{"href":974,"rel":975},"https:\u002F\u002Fgithub.com\u002Fconfluentinc\u002Fconfluent-kafka-go\u002Fblob\u002Fv2.15.1\u002Fkafka\u002Fkip-848-migration-guide.md",[112],"migration guide",". The classic protocol remains the default.",[195,979,980,698,982,984,985,988,989,992,993,114],{},[14,981,623],{},[31,983,493],{}," partitioner hashes keyed records with ",[14,986,987],{},"CRC32, not Murmur2",". This does not match the Java client's default or franz-go. To match Java partitioning, set ",[31,990,991],{},"partitioner"," to ",[31,994,995],{},"murmur2_random",[195,997,998,1000],{},[14,999,629],{}," The module includes first-party serializers for Avro, Protobuf, and JSON Schema that handle schema registration, caching, and wire framing. These integrations and Confluent commercial support are the primary reasons to accept cgo.",[195,1002,1003,648,1005,1007],{},[14,1004,647],{},[108,1006,652],{"href":651}," shows recent development with merges spread across more accounts than the other clients (top account: 39%), alongside an older open PR backlog (74 open, median age 2.4 years).",[10,1009,1010,1012],{},[14,1011,658],{}," Choose confluent-kafka-go when Confluent's serializers or commercial support are required and every deployment target supports its cgo build requirements. Choose a pure Go client when the build must avoid a C toolchain.",[39,1014,1016],{"id":1015},"migrating-from-sarama-or-kafka-go","Migrating from Sarama or kafka-go",[10,1018,1019],{},"When a missing feature or deployment constraint justifies changing clients, test runtime behavior as well as compilation:",[192,1021,1022,1028,1034,1052,1058],{},[195,1023,1024,1027],{},[14,1025,1026],{},"Producer:"," Compare partitioning for the same keys, batching, delivery errors, and idempotence or transaction settings. A different partitioner can change per-key ordering across the migration.",[195,1029,1030,1033],{},[14,1031,1032],{},"Consumer:"," Compare the starting offset, commit timing, retry behavior after a failed record, and partition handoff during rebalances.",[195,1035,1036,1039,1040,1047,1048,114],{},[14,1037,1038],{},"Consumer-group protocol:"," Under the classic protocol, every active member must advertise at least one common assignor. Moving from an eager assignor to cooperative-sticky requires two staged rollouts: first add cooperative-sticky alongside the old eager assignor, then remove the eager assignor after every member has been upgraded. Treat KIP-848 as a separate migration; Kafka 4.0+ supports rolling classic-to-consumer upgrades, but assignment, timeout, and rebalance callback behavior changes. See the franz-go ",[108,1041,1044],{"href":1042,"rel":1043},"https:\u002F\u002Fpkg.go.dev\u002Fgithub.com\u002Ftwmb\u002Ffranz-go@v1.22.0\u002Fpkg\u002Fkgo#Balancers",[112],[31,1045,1046],{},"Balancers"," documentation and Confluent's ",[108,1049,1051],{"href":974,"rel":1050},[112],"KIP-848 migration guide",[195,1053,1054,1057],{},[14,1055,1056],{},"Serialization:"," Preserve key and value encoding, headers, Schema Registry framing, and subject naming so existing consumers can read the new producer's records.",[195,1059,1060,1063],{},[14,1061,1062],{},"Deployment:"," If the destination is confluent-kafka-go, build and run it on every target OS, CPU architecture, and libc combination, including the production container image.",[10,1065,1066,1067,114],{},"Run the old and new consumers against a test topic with separate group IDs initialized to the same offsets. Compare processed records and commit positions before moving production traffic. If production will use a new group ID, initialize its offsets explicitly rather than relying on ",[31,1068,1069],{},"auto.offset.reset",[542,1071,148],{"id":1072},"keep-keyed-records-on-the-same-partitions",[10,1074,1075,1076,1078],{},"The defaults in the comparison table are not interchangeable. Sarama hashes keys with FNV-1a, kafka-go's default ",[31,1077,94],{}," ignores keys and uses round-robin, franz-go uses Murmur2, and confluent-kafka-go uses CRC32. Changing the producer can send the same key to a different partition and break per-key ordering across the cutover.",[10,1080,1081],{},"First identify the old producer's actual partitioner, including custom settings, and keep the topic's partition count fixed during the test.",[192,1083,1084],{},[195,1085,1086,1089,1090,1093,1094,1097,1098,1101],{},[14,1087,1088],{},"Sarama to franz-go:"," Use ",[31,1091,1092],{},"kgo.SaramaCompatHasher"," with a 32-bit FNV-1a hash function. The similarly named ",[31,1095,1096],{},"SaramaHasher"," does ",[14,1099,1100],{},"not"," exactly match Sarama's default signed-hash behavior.",[1103,1104,1109],"pre",{"className":1105,"code":1106,"language":1107,"meta":1108,"style":1108},"language-go shiki shiki-themes kafma-dark kafma-light","import (\n    \"hash\u002Ffnv\"\n\n    \"github.com\u002Ftwmb\u002Ffranz-go\u002Fpkg\u002Fkgo\"\n)\n\nfnv32a := func(key []byte) uint32 {\n    hasher := fnv.New32a()\n    hasher.Write(key)\n    return hasher.Sum32()\n}\n\nclient, err := kgo.NewClient(\n    kgo.SeedBrokers(brokers...),\n    kgo.RecordPartitioner(kgo.StickyKeyPartitioner(kgo.SaramaCompatHasher(fnv32a))),\n)\n","go","",[31,1110,1111,1119,1125,1132,1138,1144,1149,1155,1161,1167,1173,1179,1184,1190,1196,1202],{"__ignoreMap":1108},[1112,1113,1116],"span",{"class":1114,"line":1115},"line",1,[1112,1117,1118],{},"import (\n",[1112,1120,1122],{"class":1114,"line":1121},2,[1112,1123,1124],{},"    \"hash\u002Ffnv\"\n",[1112,1126,1128],{"class":1114,"line":1127},3,[1112,1129,1131],{"emptyLinePlaceholder":1130},true,"\n",[1112,1133,1135],{"class":1114,"line":1134},4,[1112,1136,1137],{},"    \"github.com\u002Ftwmb\u002Ffranz-go\u002Fpkg\u002Fkgo\"\n",[1112,1139,1141],{"class":1114,"line":1140},5,[1112,1142,1143],{},")\n",[1112,1145,1147],{"class":1114,"line":1146},6,[1112,1148,1131],{"emptyLinePlaceholder":1130},[1112,1150,1152],{"class":1114,"line":1151},7,[1112,1153,1154],{},"fnv32a := func(key []byte) uint32 {\n",[1112,1156,1158],{"class":1114,"line":1157},8,[1112,1159,1160],{},"    hasher := fnv.New32a()\n",[1112,1162,1164],{"class":1114,"line":1163},9,[1112,1165,1166],{},"    hasher.Write(key)\n",[1112,1168,1170],{"class":1114,"line":1169},10,[1112,1171,1172],{},"    return hasher.Sum32()\n",[1112,1174,1176],{"class":1114,"line":1175},11,[1112,1177,1178],{},"}\n",[1112,1180,1182],{"class":1114,"line":1181},12,[1112,1183,1131],{"emptyLinePlaceholder":1130},[1112,1185,1187],{"class":1114,"line":1186},13,[1112,1188,1189],{},"client, err := kgo.NewClient(\n",[1112,1191,1193],{"class":1114,"line":1192},14,[1112,1194,1195],{},"    kgo.SeedBrokers(brokers...),\n",[1112,1197,1199],{"class":1114,"line":1198},15,[1112,1200,1201],{},"    kgo.RecordPartitioner(kgo.StickyKeyPartitioner(kgo.SaramaCompatHasher(fnv32a))),\n",[1112,1203,1205],{"class":1114,"line":1204},16,[1112,1206,1143],{},[192,1208,1209,1230],{},[195,1210,1211,1214,1215,992,1218,1220,1221,1224,1225,1229],{},[14,1212,1213],{},"Sarama to kafka-go:"," Set ",[31,1216,1217],{},"Writer.Balancer",[31,1219,711],{},"; use ",[31,1222,1223],{},"&kafka.ReferenceHash{}"," only if the old producer used Sarama's reference hash partitioner. The versioned ",[108,1226,1228],{"href":527,"rel":1227},[112],"kafka-go compatibility guide"," documents both mappings.",[195,1231,1232,1214,1235,1238,1239,1242,1243,1245,1246,1251,1252,1255],{},[14,1233,1234],{},"To confluent-kafka-go:",[31,1236,1237],{},"partitioner=murmur2_random"," for Java-compatible Murmur2 placement used by keyed franz-go records, or ",[31,1240,1241],{},"partitioner=fnv1a_random"," for Sarama's default ",[31,1244,848],{}," (",[108,1247,1250],{"href":1248,"rel":1249},"https:\u002F\u002Fgithub.com\u002Fconfluentinc\u002Flibrdkafka\u002Fblob\u002Fv2.15.1\u002Fsrc\u002Frdfnv1a.c",[112],"librdkafka implementation","); it does not match ",[31,1253,1254],{},"NewReferenceHashPartitioner",". kafka-go's default round-robin placement has no stable key mapping to reproduce.",[10,1257,1258],{},"Produce representative keys, including empty and nil keys where applicable, to a test topic with the same partition count from both clients. Compare the resulting partitions before switching production traffic. Matching a hash function alone is insufficient if the clients treat empty keys or the partition count differently.",[542,1260,1262],{"id":1261},"sarama-to-franz-go","Sarama to franz-go",[44,1264,1265,1279],{},[47,1266,1267],{},[50,1268,1269,1272,1274,1276],{},[53,1270,1271],{},"Task",[53,1273,16],{},[53,1275,24],{},[53,1277,1278],{},"Migration check",[63,1280,1281,1300,1324,1349,1380,1409,1427],{},[50,1282,1283,1286,1292,1297],{},[68,1284,1285],{},"Configuration",[68,1287,1288,1291],{},[31,1289,1290],{},"sarama.NewConfig()"," and client or producer constructors",[68,1293,1294],{},[31,1295,1296],{},"kgo.NewClient(opts...)",[68,1298,1299],{},"Map broker version, TLS\u002FSASL, retries, batching, and timeouts explicitly.",[50,1301,1302,1305,1313,1321],{},[68,1303,1304],{},"Produce",[68,1306,1307,176,1310],{},[31,1308,1309],{},"SyncProducer.SendMessage",[31,1311,1312],{},"AsyncProducer.Input()",[68,1314,1315,176,1318,1320],{},[31,1316,1317],{},"ProduceSync",[31,1319,1304],{}," with a completion callback",[68,1322,1323],{},"Compare delivery errors, flush and shutdown behavior, and the partitioner.",[50,1325,1326,1329,1334,1346],{},[68,1327,1328],{},"Consume in a group",[68,1330,1331,1333],{},[31,1332,361],{}," with a handler for each claim",[68,1335,1336,225,1338,1341,1342,176,1344],{},[31,1337,314],{},[31,1339,1340],{},"ConsumeTopics"," options, then ",[31,1343,337],{},[31,1345,340],{},[68,1347,1348],{},"Preserve per-partition processing order and the intended start offset.",[50,1350,1351,1354,1363,1377],{},[68,1352,1353],{},"Commit offsets",[68,1355,1356,225,1359,1362],{},[31,1357,1358],{},"session.MarkMessage",[31,1360,1361],{},"session.Commit"," or auto-commit",[68,1364,1365,334,1367,1370,1371,334,1374],{},[31,1366,596],{},[31,1368,1369],{},"MarkCommitRecords",", or ",[31,1372,1373],{},"DisableAutoCommit()",[31,1375,1376],{},"CommitRecords",[68,1378,1379],{},"Commit only after the work the old service considered complete.",[50,1381,1382,1385,1395,1406],{},[68,1383,1384],{},"Handle rebalances",[68,1386,1387,1388,91,1391,1394],{},"Handler ",[31,1389,1390],{},"Setup",[31,1392,1393],{},"Cleanup"," and claim lifecycle",[68,1396,1397,17,1400,17,1403],{},[31,1398,1399],{},"OnPartitionsAssigned",[31,1401,1402],{},"OnPartitionsRevoked",[31,1404,1405],{},"OnPartitionsLost",[68,1407,1408],{},"Flush in-flight work before a partition moves; check callback concurrency.",[50,1410,1411,1414,1417,1424],{},[68,1412,1413],{},"Handle errors",[68,1415,1416],{},"Producer error channel, returned errors, and consumer errors",[68,1418,1419,1420,1423],{},"Produce callback or result, ",[31,1421,1422],{},"Fetches.Errors()",", and returned errors",[68,1425,1426],{},"Keep failures visible and preserve retry policy.",[50,1428,1429,1432,1437,1442],{},[68,1430,1431],{},"Admin",[68,1433,1434],{},[31,1435,1436],{},"ClusterAdmin",[68,1438,1439],{},[31,1440,1441],{},"kadm.NewClient",[68,1443,1444],{},"Map only the operations your service actually uses.",[10,1446,1447,1448,1453],{},"franz-go's default group auto-commit timing may differ from a Sarama handler that marks records after processing. Set the commit strategy deliberately, and use the ",[108,1449,1452],{"href":1450,"rel":1451},"https:\u002F\u002Fpkg.go.dev\u002Fgithub.com\u002Ftwmb\u002Ffranz-go@v1.22.0\u002Fpkg\u002Fkgo#CommitRecords",[112],"franz-go group documentation"," when translating a handler. Test partition revocation while work is in flight; a successful compile does not prove equivalent processing guarantees.",[542,1455,1457],{"id":1456},"kafka-go-to-franz-go","kafka-go to franz-go",[192,1459,1460,1475,1499],{},[195,1461,1462,1465,1466,334,1469,1471,1472,1474],{},[14,1463,1464],{},"Produce:"," Replace ",[31,1467,1468],{},"Writer.WriteMessages",[31,1470,1317],{}," or asynchronous ",[31,1473,1304],{},". If consumers relied on kafka-go's round-robin distribution, franz-go's default partitioner will change where keyed records land. If the old writer set a balancer explicitly, map and test that algorithm before the cutover.",[195,1476,1477,1480,1481,1484,1485,176,1487,1489,1490,1492,1493,1495,1496,1498],{},[14,1478,1479],{},"Consume and commit:"," Replace a ",[31,1482,1483],{},"Reader.FetchMessage"," loop with ",[31,1486,337],{},[31,1488,340],{},". To preserve explicit commits after processing, configure ",[31,1491,1373],{}," and call ",[31,1494,1376],{},"; franz-go otherwise auto-commits records from previous polls on an interval. A kafka-go service using ",[31,1497,372],{}," already has automatic commits, but the timing still needs to be tested.",[195,1500,1501,1504],{},[14,1502,1503],{},"Rebalancing:"," kafka-go does not provide cooperative-sticky assignment, while franz-go uses it by default under the classic protocol. If both clients temporarily share a group ID, use the staged assignor rollout described above. A separate group ID avoids a mixed group but has independent committed offsets.",[39,1506,1508],{"id":1507},"github-repository-metrics","GitHub repository metrics",[10,1510,1511],{},"The tables below compare the four clients' GitHub activity, maintenance, and public usage using the same measurement windows and assessment rules as the Python client comparison. Snapshot: 25 Sep 2026.",[542,1513,1515],{"id":1514},"project-overview","Project overview",[44,1517,1518,1545],{},[47,1519,1520],{},[50,1521,1522,1525,1530,1535,1540],{},[53,1523,1524],{},"Attribute",[53,1526,1527],{},[108,1528,24],{"href":548,"rel":1529},[112],[53,1531,1532],{},[108,1533,20],{"href":666,"rel":1534},[112],[53,1536,1537],{},[108,1538,16],{"href":779,"rel":1539},[112],[53,1541,1542],{},[108,1543,28],{"href":185,"rel":1544},[112],[63,1546,1547,1564,1580],{},[50,1548,1549,1552,1555,1558,1561],{},[68,1550,1551],{},"Repository created",[68,1553,1554],{},"25 Mar 2019",[68,1556,1557],{},"9 May 2017",[68,1559,1560],{},"5 Jul 2013",[68,1562,1563],{},"12 Jul 2016",[50,1565,1566,1569,1572,1575,1577],{},[68,1567,1568],{},"License",[68,1570,1571],{},"BSD-3-Clause",[68,1573,1574],{},"MIT",[68,1576,1574],{},[68,1578,1579],{},"Apache-2.0",[50,1581,1582,1585,1588,1590,1592],{},[68,1583,1584],{},"Repository status",[68,1586,1587],{},"Not archived · original",[68,1589,1587],{},[68,1591,1587],{},[68,1593,1587],{},[542,1595,1597],{"id":1596},"activity","Activity",[44,1599,1600,1615],{},[47,1601,1602],{},[50,1603,1604,1607,1609,1611,1613],{},[53,1605,1606],{},"Metric",[53,1608,24],{},[53,1610,20],{},[53,1612,16],{},[53,1614,28],{},[63,1616,1617,1633,1650,1667,1684,1701,1718],{},[50,1618,1619,1622,1625,1628,1631],{},[68,1620,1621],{},"Latest default-branch commit",[68,1623,1624],{},"25 Sep 2026",[68,1626,1627],{},"23 Apr 2026",[68,1629,1630],{},"24 Sep 2026",[68,1632,1630],{},[50,1634,1635,1638,1641,1644,1647],{},[68,1636,1637],{},"Latest GitHub Release",[68,1639,1640],{},"None listed",[68,1642,1643],{},"v0.4.51 (23 Apr 2026)",[68,1645,1646],{},"v1.61.0 (22 Sep 2026)",[68,1648,1649],{},"v2.15.1 (10 Sep 2026)",[50,1651,1652,1655,1658,1661,1664],{},[68,1653,1654],{},"GitHub Releases (12 mo)",[68,1656,1657],{},"0",[68,1659,1660],{},"2",[68,1662,1663],{},"15",[68,1665,1666],{},"9",[50,1668,1669,1672,1675,1678,1681],{},[68,1670,1671],{},"Commits",[68,1673,1674],{},"386 (90 d) · 1,102 (12 mo)",[68,1676,1677],{},"0 (90 d) · 2 (12 mo)",[68,1679,1680],{},"86 (90 d) · 385 (12 mo)",[68,1682,1683],{},"55 (90 d) · 156 (12 mo)",[50,1685,1686,1689,1692,1695,1698],{},[68,1687,1688],{},"Issue flow (90 d)",[68,1690,1691],{},"13 opened · 15 closed",[68,1693,1694],{},"2 opened · 1 closed",[68,1696,1697],{},"12 opened · 13 closed",[68,1699,1700],{},"3 opened · 3 closed",[50,1702,1703,1706,1709,1712,1715],{},[68,1704,1705],{},"PR flow (90 d)",[68,1707,1708],{},"111 opened · 103 merged",[68,1710,1711],{},"12 opened · 0 merged",[68,1713,1714],{},"123 opened · 74 merged",[68,1716,1717],{},"42 opened · 29 merged",[50,1719,1720,1723,1726,1729,1732],{},[68,1721,1722],{},"Activity assessment",[68,1724,1725],{},"🟢 386 commits and 103 merged PRs in 90 days.",[68,1727,1728],{},"🔴 No commits or merged PRs in 90 days.",[68,1730,1731],{},"🟢 86 commits and 74 merged PRs in 90 days.",[68,1733,1734],{},"🟢 55 commits and 29 merged PRs in 90 days.",[10,1736,1737,1738,1743],{},"Release figures count GitHub Releases, not Go module tags. franz-go has ",[108,1739,1742],{"href":1740,"rel":1741},"https:\u002F\u002Fgithub.com\u002Ftwmb\u002Ffranz-go\u002Ftags",[112],"recent version tags"," despite having no GitHub Release entries in this snapshot.",[542,1745,1747],{"id":1746},"maintenance","Maintenance",[44,1749,1750,1764],{},[47,1751,1752],{},[50,1753,1754,1756,1758,1760,1762],{},[53,1755,1606],{},[53,1757,24],{},[53,1759,20],{},[53,1761,16],{},[53,1763,28],{},[63,1765,1766,1783,1800,1817,1834,1851,1868,1881,1898],{},[50,1767,1768,1771,1774,1777,1780],{},[68,1769,1770],{},"Active commit authors (12 mo)",[68,1772,1773],{},"45",[68,1775,1776],{},"1",[68,1778,1779],{},"26",[68,1781,1782],{},"27",[50,1784,1785,1788,1791,1794,1797],{},[68,1786,1787],{},"PR merge distribution (12 mo)",[68,1789,1790],{},"1 person · 100% of merges",[68,1792,1793],{},"2 people · Top 1: 50% · Top 2: 100%",[68,1795,1796],{},"2 people · Top 1: 98% · Top 2: 100%",[68,1798,1799],{},"17 people · Top 1: 39% · Top 2: 56%",[50,1801,1802,1805,1808,1811,1814],{},[68,1803,1804],{},"Issue backlog",[68,1806,1807],{},"0 open issues",[68,1809,1810],{},"182 open · median age 3.2 y",[68,1812,1813],{},"14 open · median age 1.8 y",[68,1815,1816],{},"202 open · median age 3.6 y",[50,1818,1819,1822,1825,1828,1831],{},[68,1820,1821],{},"Issue closure rate",[68,1823,1824],{},"19\u002F20 closed within 30 d and 90 d",[68,1826,1827],{},"0\u002F1 within 30 d or 90 d (small sample)",[68,1829,1830],{},"6\u002F7 closed within 30 d · 7\u002F7 within 90 d",[68,1832,1833],{},"3\u002F3 closed within 30 d and 90 d (small sample)",[50,1835,1836,1839,1842,1845,1848],{},[68,1837,1838],{},"PR backlog",[68,1840,1841],{},"0 open PRs",[68,1843,1844],{},"83 open · median age 2.6 y",[68,1846,1847],{},"34 open · median age 1 d",[68,1849,1850],{},"74 open · median age 2.4 y",[50,1852,1853,1856,1859,1862,1865],{},[68,1854,1855],{},"Median PR merge time (90 d)",[68,1857,1858],{},"2.1 h (n=103)",[68,1860,1861],{},"N\u002FA — no merged PRs",[68,1863,1864],{},"1.3 d (n=74)",[68,1866,1867],{},"4.2 d (n=29)",[50,1869,1870,1873,1875,1877,1879],{},[68,1871,1872],{},"Published GitHub security advisories",[68,1874,1657],{},[68,1876,1657],{},[68,1878,1657],{},[68,1880,1657],{},[50,1882,1883,1886,1889,1892,1895],{},[68,1884,1885],{},"Responsiveness assessment",[68,1887,1888],{},"🟢 Median merge time 2.1 h; 19 of 20 cohort issues closed within 90 d.",[68,1890,1891],{},"🔴 No PRs merged in 90 d; 83 open PRs have a median age of 2.6 y.",[68,1893,1894],{},"🟢 Median merge time 1.3 d; 7 of 7 cohort issues closed within 90 d.",[68,1896,1897],{},"🟡 Median merge time 4.2 d; 74 open PRs have a median age of 2.4 y.",[50,1899,1900,1903,1906,1909,1912],{},[68,1901,1902],{},"PR merge concentration assessment",[68,1904,1905],{},"🔴 One account handled all recorded merges.",[68,1907,1908],{},"🟡 The largest account handled 50% of merges.",[68,1910,1911],{},"🟡 One account handled 98% of merges.",[68,1913,1914],{},"🟢 The largest account handled 39% of merges.",[10,1916,1917,1918,1921],{},"PR merge distribution counts non-bot ",[31,1919,1920],{},"mergedBy"," accounts, so automated merges may undercount human reviewers. Issue closure rates use issues opened 90–180 days before the snapshot, giving each issue a full 90-day window.",[542,1923,1925],{"id":1924},"public-usage-and-interest","Public usage and interest",[44,1927,1928,1942],{},[47,1929,1930],{},[50,1931,1932,1934,1936,1938,1940],{},[53,1933,1606],{},[53,1935,24],{},[53,1937,20],{},[53,1939,16],{},[53,1941,28],{},[63,1943,1944,1961,1978,1995],{},[50,1945,1946,1949,1952,1955,1958],{},[68,1947,1948],{},"Stars",[68,1950,1951],{},"3,072",[68,1953,1954],{},"8,632",[68,1956,1957],{},"12,518",[68,1959,1960],{},"5,168",[50,1962,1963,1966,1969,1972,1975],{},[68,1964,1965],{},"Forks",[68,1967,1968],{},"305",[68,1970,1971],{},"860",[68,1973,1974],{},"1,869",[68,1976,1977],{},"703",[50,1979,1980,1983,1986,1989,1992],{},[68,1981,1982],{},"GitHub dependents (Used by)",[68,1984,1985],{},"1,990",[68,1987,1988],{},"10,989",[68,1990,1991],{},"7,162",[68,1993,1994],{},"7,144",[50,1996,1997,2000,2003,2006,2009],{},[68,1998,1999],{},"Public usage assessment",[68,2001,2002],{},"🟢 1,990 dependents, 305 forks, and 3,072 stars.",[68,2004,2005],{},"🟢 10,989 dependents, 860 forks, and 8,632 stars.",[68,2007,2008],{},"🟢 7,162 dependents, 1,869 forks, and 12,518 stars.",[68,2010,2011],{},"🟢 7,144 dependents, 703 forks, and 5,168 stars.",[10,2013,2014],{},"GitHub dependents are approximate public-repository counts. Stars, forks, and dependents indicate visible usage and interest, not whether a library fits your application's Kafka requirements.",[542,2016,2018],{"id":2017},"overall-repository-signals","Overall repository signals",[192,2020,2021,2026,2031,2036],{},[195,2022,2023,2025],{},[14,2024,24],{}," has the most recent commits and merged PRs, with fast recorded merge times; one account handled all recorded PR merges in the past year.",[195,2027,2028,2030],{},[14,2029,20],{}," has the highest public dependent count of the four, but no default-branch commits or merged PRs in the last 90 days; its open issue and PR backlogs are old.",[195,2032,2033,2035],{},[14,2034,16],{}," has broad public usage and recent development activity, but one account handled 98% of recorded PR merges in the past year.",[195,2037,2038,2040],{},[14,2039,28],{}," has recent activity and broad public usage, with PR merges spread across more accounts than the other three; its open PR backlog is old.",[10,2042,2043],{},"These are maintenance and adoption signals, not evidence of runtime quality or performance.",[39,2045,2047],{"id":2046},"test-go-kafka-clients-with-kafma","Test Go Kafka clients with Kafma",[10,2049,2050,2051,2054,2055,2058],{},"A clean ",[31,2052,2053],{},"go build"," does not show which partition a key reached, what bytes a producer wrote, or what a consumer does with a record it cannot decode. Go Kafka clients expose keys and values as ",[31,2056,2057],{},"[]byte"," in their consumer APIs, so deserialization and tombstone handling live in application code or a serializer layer. Retry and commit defaults still differ by client.",[10,2060,2061,2064,2065,2069],{},[108,2062,2063],{"href":768},"Kafma"," is a desktop Kafka UI that runs alongside your Go service. Its ",[108,2066,2068],{"href":2067},"\u002Ffeatures\u002Fkafka-console","Kafka console"," provides one client-independent place to inspect producer output, send controlled records, and watch the consumer group's committed position.",[542,2071,2073],{"id":2072},"compare-partitions-for-the-same-keys","Compare partitions for the same keys",[10,2075,2076,2077,2079],{},"Produce the same keys from the old and new producers to a test topic with the same partition count. In Kafma, compare each record's key and partition. Look for two problems: one key spread across partitions by a single producer, as kafka-go's default round-robin ",[31,2078,94],{}," does, and one key placed differently by two producers, such as Sarama's FNV-1a and franz-go's Murmur2.",[10,2081,2082,2083,2086],{},"The example below sends ",[31,2084,2085],{},"customer_42"," to a three-partition topic. kafka-go spreads three records across P0, P1, and P2, while Sarama sends the key to P2 and franz-go sends it to P0. The expanded franz-go record also shows the test headers and decoded JSON value.",[10,2088,2089],{},[2090,2091],"img",{"alt":2092,"height":2093,"src":2094,"width":2095},"Kafma comparing the partitions selected by kafka-go, Sarama, and franz-go for the same record key",1520,"https:\u002F\u002Fmedia.kafma.app\u002Fblog\u002Fkafka-golang-clients\u002Fgo-client-partitions.png",2160,[542,2097,2099],{"id":2098},"check-schema-registry-framing","Check Schema Registry framing",[10,2101,2102,2103,2105,2106,2109,2110,2113,2114,2118],{},"Sarama and kafka-go rely on a separate library for Schema Registry framing, and franz-go's ",[31,2104,80],{}," needs codecs you supply. When a consumer cannot decode a record, open it in Kafma. With Schema Registry configured, ",[14,2107,2108],{},"Decoded"," shows the value resolved through its schema ID, and ",[14,2111,2112],{},"Raw"," shows the original bytes as hexadecimal. Browse subjects and versions in Kafma's ",[108,2115,2117],{"href":2116},"\u002Ffeatures\u002Fkafka-schema-registry","Schema Registry UI",", then compare their schema IDs with the record.",[10,2120,2121,2122,2124],{},"In the default payload-prefix format, the raw value starts with a zero magic byte and a 4-byte schema ID. If ",[14,2123,2112],{}," starts with plain JSON or text, the prefix is absent; check whether the producer intentionally uses header-based schema identification before treating the record as unframed.",[10,2126,2127],{},[2090,2128],{"alt":2129,"height":2093,"src":2130,"width":2095},"Kafma showing an Avro record decoded through schema ID 7, with the Decoded and Raw views","https:\u002F\u002Fmedia.kafma.app\u002Fblog\u002Fkafka-javascript-clients\u002Fconsumer.png",[542,2132,2134],{"id":2133},"send-bad-records-to-a-running-consumer","Send bad records to a running consumer",[10,2136,2137,2138,2142],{},"Keep the consumer running and use the ",[108,2139,2141],{"href":2140},"\u002Fdocs\u002Fconsole\u002Fproducing-messages","producer panel"," to send, to one partition: a valid record, a payload the decoder rejects, a tombstone with a null value, and another valid record. Check whether each bad record is retried, skipped, or stops the partition, and whether the final record is processed and committed.",[10,2144,2145],{},"An unrecovered panic, such as a nil dereference while handling a tombstone, exits the process and stops consumption on every partition.",[10,2147,2148,2149,2152,2153,2156,2157,2161,2162,114],{},"To test shutdown, use ",[14,2150,2151],{},"Loop"," to keep records arriving, then send the service ",[31,2154,2155],{},"SIGTERM",". Open ",[108,2158,2160],{"href":2159},"\u002Fdocs\u002Fconsole\u002Fconsuming-messages#live-and-watch-group-modes","Watch Group"," and confirm that the member leaves, assignments settle, and the committed position matches the last record the application completed. Kafma reads this state without joining the group, triggering a rebalance, or committing offsets. To follow lag and partition assignments across every member, open the group in Kafma's ",[108,2163,2165],{"href":2164},"\u002Ffeatures\u002Fkafka-consumer-groups","consumer group UI",[10,2167,2168],{},[2090,2169],{"alt":2170,"height":2171,"src":2172,"width":2173},"Kafma watching pending and consumed records around a consumer group's committed offsets",1368,"https:\u002F\u002Fmedia.kafma.app\u002Fdocs\u002Fconsole\u002Fconsuming-messages\u002Fwatch-group.png",1562,[542,2175,2177],{"id":2176},"clone-data-for-migration-tests","Clone data for migration tests",[10,2179,2180,2184],{},[108,2181,2183],{"href":2182},"\u002Ffeatures\u002Fkafka-data-clone","Kafma Data Clone"," copies a selected range of real records to a test topic. Use it to exercise older schema versions, missing headers, tombstones, and other cases that hand-written fixtures miss. It can copy keys, values, timestamps, and headers, register required schemas, and mask selected fields.",[10,2186,2187,2188,2192],{},"Use the clone to check decoding and consumer behavior. Copied records receive new offsets and may land on different partitions, so compare partitions with records produced directly by the old and new clients instead. See the ",[108,2189,2191],{"href":2190},"\u002Fdocs\u002Fdata-clone\u002Foverview","Data Clone guide"," for copy behavior.",[10,2194,2195],{},[2090,2196],{"alt":2197,"height":2198,"src":2199,"width":2200},"Kafma cloning selected topics and masked records into a test cluster",1564,"https:\u002F\u002Fmedia.kafma.app\u002Fchangelog\u002Fv1.0.0\u002Fhomepage-data-clone.png",2740,[10,2202,2203,2207],{},[108,2204,2206],{"href":2205},"\u002Fdownload","Download Kafma"," and connect to the same test cluster as the Go service.",[39,2209,2211],{"id":2210},"frequently-asked-questions","Frequently asked questions",[542,2213,2215],{"id":2214},"is-there-an-official-apache-kafka-client-for-go","Is there an official Apache Kafka client for Go?",[10,2217,2218,2219,2222,2223,2225,2226,2228],{},"No. Apache Kafka's first-party client is Java (",[31,2220,2221],{},"kafka-clients","). In Go, Confluent maintains ",[31,2224,28],{}," on ",[31,2227,33],{},". Sarama, kafka-go, and franz-go are independent pure Go implementations.",[542,2230,2232],{"id":2231},"is-confluent-kafka-go-faster-than-franz-go","Is confluent-kafka-go faster than franz-go?",[10,2234,2235,2236,2238],{},"There is no reliable general winner. ",[31,2237,33],{}," can perform well, but cgo alone does not make confluent-kafka-go faster than franz-go. Throughput and latency depend on message size, batching, compression, acks, broker limits, and commit behavior. Benchmark the same workload and delivery guarantees on the same brokers before using performance to choose.",[542,2240,2242,2243,2245],{"id":2241},"can-i-build-confluent-kafka-go-with-cgo_enabled0","Can I build confluent-kafka-go with ",[31,2244,171],{},"?",[10,2247,2248,2249,2251,2252,2254,2255,114],{},"No. confluent-kafka-go wraps ",[31,2250,33],{}," and requires cgo. If the build must use ",[31,2253,171],{},", choose Sarama, kafka-go, or franz-go. See ",[108,2256,165],{"href":883},[542,2258,2260],{"id":2259},"do-go-kafka-clients-partition-keys-the-same-way-as-java","Do Go Kafka clients partition keys the same way as Java?",[10,2262,2263],{},"For records with non-null keys, franz-go uses Java-compatible Murmur2 by default. The other clients need configuration:",[192,2265,2266,2282,2293],{},[195,2267,2268,1214,2271,992,2273,2276,2277,2281],{},[14,2269,2270],{},"kafka-go:",[31,2272,1217],{},[31,2274,2275],{},"&kafka.Murmur2Balancer{}",", which the ",[108,2278,2280],{"href":527,"rel":2279},[112],"kafka-go documentation"," describes as compatible with the Java default partitioner.",[195,2283,2284,1214,2287,2289,2290,114],{},[14,2285,2286],{},"confluent-kafka-go:",[31,2288,1237],{},"; see the ",[108,2291,539],{"href":537,"rel":2292},[112],[195,2294,2295,2298],{},[14,2296,2297],{},"Sarama:"," Implement a custom partitioner that matches Java's Murmur2 hash, including how Java converts the hash to a positive value before selecting a partition.",[10,2300,2301],{},"Records without keys follow each client's own round-robin, sticky, or random strategy, so Java compatibility applies only to keyed records. Verify placement with representative keys before switching producers.",[542,2303,2305],{"id":2304},"which-go-kafka-clients-support-kip-848","Which Go Kafka clients support KIP-848?",[10,2307,2308,2309,2314,2315,2317,2318,2321],{},"franz-go and confluent-kafka-go support KIP-848 as an opt-in on Kafka 4.0+. franz-go v1.22.0 enables it through ",[108,2310,2312],{"href":610,"rel":2311},[112],[31,2313,429],{}," and recommends Kafka 4.3+; confluent-kafka-go v2.15.1 uses ",[31,2316,970],{},". Sarama v1.61.0 adds only the KIP-848 ",[14,2319,2320],{},"ConsumerGroupDescribe API",", not group membership, and kafka-go v0.4.51 does not document KIP-848 group membership. Cooperative-sticky rebalancing under the classic protocol is a different feature.",[542,2323,2325],{"id":2324},"which-go-kafka-client-works-with-aws-msk-iam","Which Go Kafka client works with AWS MSK IAM?",[10,2327,2328],{},"All four can connect, through different mechanisms:",[192,2330,2331,2349,2363,2374],{},[195,2332,2333,2336,2337,2344,2345,2348],{},[14,2334,2335],{},"franz-go:"," The built-in ",[108,2338,2341],{"href":2339,"rel":2340},"https:\u002F\u002Fpkg.go.dev\u002Fgithub.com\u002Ftwmb\u002Ffranz-go@v1.22.0\u002Fpkg\u002Fsasl\u002Faws",[112],[31,2342,2343],{},"pkg\u002Fsasl\u002Faws"," package implements ",[31,2346,2347],{},"AWS_MSK_IAM"," directly.",[195,2350,2351,2353,2354,2344,2361,114],{},[14,2352,2270],{}," The separate ",[108,2355,2358],{"href":2356,"rel":2357},"https:\u002F\u002Fpkg.go.dev\u002Fgithub.com\u002Fsegmentio\u002Fkafka-go\u002Fsasl\u002Faws_msk_iam_v2@v0.1.0",[112],[31,2359,2360],{},"aws_msk_iam_v2",[31,2362,2347],{},[195,2364,2365,2367,2368,2373],{},[14,2366,2297],{}," Generate SASL\u002FOAUTHBEARER tokens with ",[108,2369,2372],{"href":2370,"rel":2371},"https:\u002F\u002Fgithub.com\u002Faws\u002Faws-msk-iam-sasl-signer-go",[112],"AWS's Go signer",", which includes a Sarama example.",[195,2375,2376,2378,2379,2382,2383,114],{},[14,2377,2286],{}," Handle ",[31,2380,2381],{},"OAuthBearerTokenRefresh"," events, generate tokens with the AWS signer, and pass them to ",[31,2384,2385],{},"SetOAuthBearerToken()",[10,2387,2388],{},"Configure TLS and test credential refresh against your MSK cluster.",[542,2390,2392],{"id":2391},"is-kafka-go-still-maintained","Is kafka-go still maintained?",[10,2394,2395],{},"The repository is not archived, and v0.4.51 was released on 23 Apr 2026. However, the 25 Sep 2026 snapshot found no default-branch commits or merged PRs in the preceding 90 days, two commits in 12 months, and 83 open PRs with a median age of 2.6 years. That indicates a slower recent maintenance pace; it does not establish that the library is abandoned or unsuitable for an existing service. Check whether the released version supports your brokers and required features before starting a new service on it.",[542,2397,2399],{"id":2398},"can-producers-and-consumers-use-different-go-kafka-libraries","Can producers and consumers use different Go Kafka libraries?",[10,2401,2402,2403,2405],{},"Yes. They must agree on key and value serialization, headers, compression, and Schema Registry framing. Consumers that share a group also need compatible group protocols. Mixing ",[31,2404,970],{}," with classic-protocol members in one group needs a planned upgrade.",[10,2407,2408,2409,114],{},"If producers use different libraries for the same keyed records, align their partitioners first. Otherwise, records with the same key can land on different partitions and lose per-key ordering. See ",[108,2410,2073],{"href":2411},"#compare-partitions-for-the-same-keys",[542,2413,2415],{"id":2414},"does-go-have-kafka-streams","Does Go have Kafka Streams?",[10,2417,2418,2419,2421],{},"Not as Apache Kafka Streams. For Kafka-backed tables, messaging frameworks, and in-process pipelines, see ",[108,2420,161],{"href":160},". Most services only need a client.",[39,2423,2425],{"id":2424},"conclusion","Conclusion",[10,2427,2428,2429,768,2431,2433],{},"For a new cgo-free Go service, start with franz-go when protocol coverage and recent development activity matter. Consider kafka-go when the ",[31,2430,90],{},[31,2432,94],{}," API is the priority and its current release, broker compatibility, and maintenance pace meet your needs. Use confluent-kafka-go when Confluent's serializers or support are required and cgo is acceptable. Keep Sarama when it already works.",[10,2435,2436],{},"Whichever client you choose, compare where keyed records land before replacing an existing producer or running both producers side by side; all four clients use different default partitioners.",[10,2438,2439],{},"This guide is maintained by the team behind Kafma.",[2441,2442,2443],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}",{"title":1108,"searchDepth":1127,"depth":1127,"links":2445},[2446,2447,2448,2454,2459,2466,2472,2484],{"id":41,"depth":1121,"text":42},{"id":164,"depth":1121,"text":165},{"id":239,"depth":1121,"text":240,"children":2449},[2450,2451,2452,2453],{"id":24,"depth":1127,"text":24},{"id":20,"depth":1127,"text":20},{"id":774,"depth":1127,"text":16},{"id":28,"depth":1127,"text":28},{"id":1015,"depth":1121,"text":1016,"children":2455},[2456,2457,2458],{"id":1072,"depth":1127,"text":148},{"id":1261,"depth":1127,"text":1262},{"id":1456,"depth":1127,"text":1457},{"id":1507,"depth":1121,"text":1508,"children":2460},[2461,2462,2463,2464,2465],{"id":1514,"depth":1127,"text":1515},{"id":1596,"depth":1127,"text":1597},{"id":1746,"depth":1127,"text":1747},{"id":1924,"depth":1127,"text":1925},{"id":2017,"depth":1127,"text":2018},{"id":2046,"depth":1121,"text":2047,"children":2467},[2468,2469,2470,2471],{"id":2072,"depth":1127,"text":2073},{"id":2098,"depth":1127,"text":2099},{"id":2133,"depth":1127,"text":2134},{"id":2176,"depth":1127,"text":2177},{"id":2210,"depth":1121,"text":2211,"children":2473},[2474,2475,2476,2478,2479,2480,2481,2482,2483],{"id":2214,"depth":1127,"text":2215},{"id":2231,"depth":1127,"text":2232},{"id":2241,"depth":1127,"text":2477},"Can I build confluent-kafka-go with CGO_ENABLED=0?",{"id":2259,"depth":1127,"text":2260},{"id":2304,"depth":1127,"text":2305},{"id":2324,"depth":1127,"text":2325},{"id":2391,"depth":1127,"text":2392},{"id":2398,"depth":1127,"text":2399},{"id":2414,"depth":1127,"text":2415},{"id":2424,"depth":1121,"text":2425},"2026-09-26","Compare Golang Kafka clients: franz-go, kafka-go, Sarama, and confluent-kafka-go. See API, cgo, and Schema Registry trade-offs.","md",{},null,"\u002Fblog\u002Fkafka-golang-clients",false,{"title":5,"description":2486},{"loc":2490},"blog\u002Fkafka-golang-clients",[2496,2497],"Kafka Client Libraries","Golang","kppE7TCBOHrivO0Mu2ORkO_ZcXFLwNaduOFqbbS1YUc",1790518188425]