<turbo-stream action="append" target="posts_list"><template>    <div class="postbit" id="344956" data-post-id="344956">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="mekusigjinn" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  mekusigjinn
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>This new library has a future ahead and I’m all for a variety in this Kafka world.</p>
<p>Buut, the second best one, brod is good enough for our use case. And the main issue I have is not the producer side. It’s the consumption side.</p>
<p>Broadway is a single stage GenStage right? At work we use BroadwayKafka, and I mistakenly thought if I use Broadway I can connect another GenStage-esque Consumer that will do the dB and offer back pressure. Sadly my understanding was wrong.</p>
<p>Another issue is at the DB layer. Essentially I want to know if dB connection pool is full, and that will trigger a back pressure to Kafka consuming. That way, at the risk of increasing consumer lag - at least I protect the DB, and therefore data integrity.</p>
<p>But no matter how I search, the only answer is do something with the Ecto telemetries. Sure, the telemetries are awesome, and I have a gut feeling I can do something together with the telemetries and the erlang counter library, but I don’t know where to start.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="344956" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/klife-a-kafka-client-with-performance-gains-over-10x/67040/12">Post #11</a>
	                </div>
	            </div>
              <div id="likers-container-344956" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="344956"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #11"></div>
  </section>
</div>
    <div class="postbit" id="345039" data-post-id="345039">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="oliveiragahenrique" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/oliveiragahenrique/120/30594_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  oliveiragahenrique
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="mekusigjinn" data-post="12" data-topic="67040">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/m/ea5d25/48.png" class="avatar"> mekusigjinn:</div>
<blockquote>
<p>And the main issue I have is not the producer side. It’s the consumption side.</p>
</blockquote>
</aside>
<p>Yes, I’m totally aware of this. As I mentioned <a href="https://forum.elixirforum.com/t/klife-a-kafka-client-with-performance-gains-over-10x/67040/7" rel="nofollow">here</a>, I can only speak to plans for the consumer side right now, and I’m very interested in learning about the current issues the community faces with Kafka to try to create a better experience. Thanks for sharing it!</p>
<aside class="quote no-group" data-username="mekusigjinn" data-post="12" data-topic="67040">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/m/ea5d25/48.png" class="avatar"> mekusigjinn:</div>
<blockquote>
<p>I can connect another GenStage-esque Consumer that will do the dB and offer back pressure. Sadly my understanding was wrong.</p>
</blockquote>
</aside>
<p>The issue of “back pressure” keeps coming up, and I’m still working to fully understand it, especially since Kafka consumers inherently support back pressure by processing messages in pull mode. This means consumers should only request new records once they’ve fully handled the previous set, so if upstream services become slow, consumption naturally slows down as well.</p>
<p>However, given that back pressure still is a big pain, I believe that Kafka consumers shouldn’t merely slow down in proportion to upstream services; ideally, they would slow down even further to allow upstream services some breathing room to recover.</p>
<p>Several people have noted that bottlenecks often appear in upstream services like databases before Kafka consumption itself becomes an issue. With that in mind, here are a few approaches I’m considering:</p>
<ul>
<li>
<p>The auto-acking behavior in some Kafka libraries might not be ideal for these scenarios. If a database is slow or times out, the Kafka consumer should slow down rather than auto-acknowledging and risking overload.</p>
</li>
<li>
<p>Providing a “rate limiting” option for consumers could allow manual control over processing rates, helping to prevent Kafka consumers from overwhelming upstream services during production peaks.</p>
</li>
<li>
<p>Tracking consumption times for each consumer could enable an adaptive rate-limiting feature. For instance, if a consumer usually processes a batch of 10 records within 1 second but suddenly takes 5 seconds, we could reduce the consumption rate to give upstream services a chance to recover. Exposing these metrics to users could also be valuable.</p>
</li>
</ul>
<p>Let me know if I’m properly understanding your back pressure issues, and please feel free to share any others. I’d love to take them into account as I design the consumer features for Klife.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="345039" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/klife-a-kafka-client-with-performance-gains-over-10x/67040/13">Post #12</a>
	                </div>
	            </div>
              <div id="likers-container-345039" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="345039"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #12"></div>
  </section>
</div>
    <div class="postbit" id="345074" data-post-id="345074">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="tristan" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/tristan/120/12307_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  tristan
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Rebar3 Core Team</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>It’d be great to see <a href="https://github.com/silviucpp/erlkaf" class="inline-onebox" rel="noopener nofollow ugc">GitHub - silviucpp/erlkaf: Erlang kafka driver based on librdkafka · GitHub</a> compared against as well.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="345074" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/klife-a-kafka-client-with-performance-gains-over-10x/67040/14">Post #13</a>
	                </div>
	            </div>
              <div id="likers-container-345074" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="345074"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #13"></div>
  </section>
</div>
    <div class="postbit" id="345075" data-post-id="345075">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="oliveiragahenrique" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/oliveiragahenrique/120/30594_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  oliveiragahenrique
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Yes, I’d definitely like to include that! I didn’t add it initially because, as far as I know, erlkaf doesn’t offer an official synchronous produce mode, and comparing async implementations can be a bit trickier.</p>
<p>I plan to run an async benchmark, though, and will definitely include erlkaf in the list!</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="345075" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/klife-a-kafka-client-with-performance-gains-over-10x/67040/15">Post #14</a>
	                </div>
	            </div>
              <div id="likers-container-345075" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="345075"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #14"></div>
  </section>
</div>
    <div class="postbit" id="346723" data-post-id="346723">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="oliveiragahenrique" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/oliveiragahenrique/120/30594_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  oliveiragahenrique
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Hey <a class="mention" href="/u/tristan" rel="nofollow">@tristan</a>! Thanks to <a class="mention" href="/u/escobera" rel="nofollow">@escobera</a> we now have an async produce benchmark for Klife. The results are the following:</p>
<p></p><div class="lightbox-wrapper"><a class="lightbox" href="https://forum.elixirforum.com/uploads/default/original/3X/9/3/93b2c8e3e4167a24405cba4fef2549ea3b89149c.png" data-download-href="https://forum.elixirforum.com/uploads/default/93b2c8e3e4167a24405cba4fef2549ea3b89149c" title="image" rel="nofollow"><img src="https://forum.elixirforum.com/uploads/default/optimized/3X/9/3/93b2c8e3e4167a24405cba4fef2549ea3b89149c_2_690x403.png" alt="image" data-base62-sha1="l4BeqneV9HuiUc3acfrQmhsaFd2" width="690" height="403" srcset="https://forum.elixirforum.com/uploads/default/optimized/3X/9/3/93b2c8e3e4167a24405cba4fef2549ea3b89149c_2_690x403.png, https://forum.elixirforum.com/uploads/default/optimized/3X/9/3/93b2c8e3e4167a24405cba4fef2549ea3b89149c_2_1035x604.png 1.5x, https://forum.elixirforum.com/uploads/default/optimized/3X/9/3/93b2c8e3e4167a24405cba4fef2549ea3b89149c_2_1380x806.png 2x" data-dominant-color="DAE0E3"><div class="meta"><svg class="fa d-icon d-icon-far-image svg-icon" aria-hidden="true"><use href="#far-image"></use></svg><span class="filename">image</span><span class="informations">1693×991 50.3 KB</span><svg class="fa d-icon d-icon-discourse-expand svg-icon" aria-hidden="true"><use href="#discourse-expand"></use></svg></div></a></div><p></p>
<p>Given the nature of the async test (different semantics and guarantees between libraries, and the BEAM overload caused by tight producing loops), I believe it’s less conclusive than the synchronous benchmark—but still valuable to explore.</p>
<p>Let us know if you have any suggestions to improve this benchmark!</p>
<p>And huge thanks again to <a class="mention" href="/u/escobera" rel="nofollow">@escobera</a> for the work! <img src="https://forum.elixirforum.com/images/emoji/apple/purple_heart.png?v=15" title=":purple_heart:" class="emoji" alt=":purple_heart:" loading="lazy" width="20" height="20"></p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="346723" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/klife-a-kafka-client-with-performance-gains-over-10x/67040/16">Post #15</a>
	                </div>
	            </div>
              <div id="likers-container-346723" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="346723"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #15"></div>
  </section>
</div>
    <div class="postbit" id="367633" data-post-id="367633">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="oliveiragahenrique" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/oliveiragahenrique/120/30594_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  oliveiragahenrique
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Hi everyone!</p>
<p>Some time ago, I released the first version of Klife (<a href="https://forum.elixirforum.com/t/klife-a-kafka-client-with-performance-gains-over-10x/67040" class="inline-onebox" rel="nofollow">Klife - A Kafka client with performance gains over 10x</a>), a Kafka client written from scratch in Elixir. At that time, it only supported producing messages. Since then, I’ve been working on implementing consumer group support, and as it nears completion, I’d love to gather feedback from the community — especially around the design of the API and any Kafka-related pain points you’ve experienced, even if they’re not directly tied to Klife’s interface.</p>
<h2><a name="p-367633-why-this-post-1" class="anchor" href="#p-367633-why-this-post-1" aria-label="Heading link" rel="nofollow"></a>Why This Post?</h2>
<p>This post is a starting point for collaboration — a space to discuss Kafka consumer pain points and how Klife might help. I’m sharing the proposed API and internals to collect early feedback, but also to invite anyone who’s wrestled with Kafka on the BEAM to weigh in — whether it’s design suggestions, performance headaches or any small detail that’s made Kafka harder than it should be. No detail is too small.</p>
<h2><a name="p-367633-basic-usage-example-2" class="anchor" href="#p-367633-basic-usage-example-2" aria-label="Heading link" rel="nofollow"></a>Basic Usage Example</h2>
<p>Here’s a simple example of how a consumer group is defined in Klife:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule MyConsumerGroup do
  use Klife.Consumer.ConsumerGroup,
    client: MyClient,
    group_name: "my_group_name",
    topics: [
      [name: "my_consumer_topic"],
      [name: "my_consumer_topic_2"]
    ]

  @impl true
  def handle_record_batch(topic, partition, record_lists) do
    # Do some processing here!
  end
end
</code></pre>
<p>You define a module that implements the consumer group behaviour and pass configuration either via use (compile-time) or at start_link (runtime) depending on your needs. Then start it on your supervision tree.</p>
<h2><a name="p-367633-consumer-group-behaviour-3" class="anchor" href="#p-367633-consumer-group-behaviour-3" aria-label="Heading link" rel="nofollow"></a>Consumer Group Behaviour</h2>
<pre data-code-wrap="elixir"><code class="lang-elixir">@type action ::
        :commit | {:skip, String.t()} | {:move_to, String.t()} | :retry

@type callback_opts :: [
        {:handler_cooldown_ms, non_neg_integer()}
      ]

@callback handle_record_batch(topic :: String.t(), partition :: integer, list(Klife.Record.t())) ::
            action
            | {action, callback_opts}
            | list({action, Klife.Record.t()})
            | {list({action, Klife.Record.t()}), callback_opts}

@callback handle_consumer_start(topic :: String.t(), partition :: integer) :: :ok
@callback handle_consumer_stop(topic :: String.t(), partition :: integer, reason :: term) :: :ok

@optional_callbacks [handle_consumer_start: 2, handle_consumer_stop: 3]
</code></pre>
<p>The main callback is <code>handle_record_batch/3</code>, where the actual record processing happens. The records are guaranteed to be ordered, and the return value tells Klife what to do with each one:</p>
<ul>
<li>
<p><code>:commit</code> - everything went fine, commit the record</p>
</li>
<li>
<p><code>{:skip, reason}</code> - commit the record but note the reason for skipping it on commit metadata</p>
</li>
<li>
<p><code>{:move_to, topic}</code> - commit the record and send it to another topic (for retries or DLQs), also note it on commit metadata</p>
</li>
<li>
<p><code>:retry</code> - do <strong>NOT</strong> commit; requeue the record on the internal queue for another processing cycle with bumped attempt count</p>
</li>
</ul>
<p>Response example:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">[
  {:commit, rec1},
  {{:skip, "validation failed"}, rec2},
  {{:move_to, "my_dlq_topic"}, rec3},
  {:commit, rec4},
  {:retry, rec5}
]
</code></pre>
<p>If you return a single action (<code>:commit</code>, <code>{:skip, reason}</code>, etc), it will be applied to all records in the batch (i.e., it’s shorthand for the full list).</p>
<p>There are some edge cases to handle, such as:</p>
<ul>
<li>
<p>What should happen if the user returns a list shorter than the number of records?</p>
</li>
<li>
<p>What if the list contains an invalid order (e.g., retrying a record before committing one with a higher offset)?</p>
</li>
</ul>
<p>The current plan is to raise on these cases to avoid inconsistent consumer group state.</p>
<p>You can also return <code>callback_opts</code> to control things like cooldown timing (more on that below).</p>
<h2><a name="p-367633-consumer-group-config-4" class="anchor" href="#p-367633-consumer-group-config-4" aria-label="Heading link" rel="nofollow"></a>Consumer Group Config</h2>
<p>A consumer group is responsible for maintaining heartbeat, reacting to rebalances, and managing consumer lifecycles. Here’s a summary of its configuration:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">[
  client: [
    type: :atom,
    required: true,
    doc: "The name of the klife client to be used by the consumer group"
  ],
  topics: [
    type: {:list, {:keyword_list, TopicConfig.get_opts()}},
    required: true,
    doc: "List of topic configurations that will be handled by the consumer group"
  ],
  group_name: [
    type: :string,
    required: true,
    doc: "Name of the consumer group"
  ],
  instance_id: [
    type: :string,
    doc: "Value to identify the consumer across restarts (static membership). See KIP-345"
  ],
  rebalance_timeout_ms: [
    type: :non_neg_integer,
    default: 30_000,
    doc:
      "The maximum time in milliseconds that the kafka broker coordinator will wait on the member to revoke it's partitions"
  ],
  fetcher_name: [
    type: :atom,
    doc:
      "Fetcher name to be used by the consumers of the group. Defaults to client's default fetcher"
  ],
  committers_count: [
    type: :pos_integer,
    default: 1,
    doc: "How many committer processes will be started for the consumer group"
  ],
  isolation_level: [
    type: {:in, [:read_committed, :read_uncommitted]},
    default: :read_committed,
    doc:
      "Define if the consumers of the consumer group will receive uncommitted transactional records"
  ]
]
</code></pre>
<p>A few Klife-specific highlights:</p>
<ul>
<li>
<p><code>:fetcher_name</code> - lets you group fetches to brokers under a named fetcher, enabling custom batching behavior. Similar to the current <code>:producer_name</code> options on the producer feature.</p>
</li>
<li>
<p><code>:committers_count</code> - since commit requests can not be grouped across groups, each consumer group has it’s specific commiter process which may be a bottleneck when consuming many partitions, with this option you can share the load among more committer processes</p>
</li>
</ul>
<h2><a name="p-367633-per-topic-consumer-options-5" class="anchor" href="#p-367633-per-topic-consumer-options-5" aria-label="Heading link" rel="nofollow"></a>Per-Topic Consumer Options</h2>
<p>Each consumer group manages a set of consumers—one per assigned partition. Each consumer runs its own processing loop, backed by an internal queue, and performs asynchronous fetch and commit operations to maximize throughput.</p>
<p>While some configuration is inherited from the consumer group, most settings are defined in the <code>TopicConfig</code>, which is passed via the <code>topics</code> option in the consumer group. These options include:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">[
  name: [
    type: :string,
    required: true,
    doc: "Name of the topic the consumer group will subscribe to"
  ],
  fetcher_name: [
    type: {:or, [:atom, :string]},
    doc:
      "Fetcher name to be used by the consumers of this topic. Overrides the one defined on the consumer group."
  ],
  isolation_level: [
    type: {:in, [:read_committed, :read_uncommitted]},
    doc: "May override the isolation level defined on the consumer group"
  ],
  offset_reset_policy: [
    type: {:in, [:latest, :earliest, :error]},
    default: :latest,
    doc:
      "Define from which offset the consumer will start processing records when no previous committed offset is found."
  ],
  fetch_max_bytes: [
    type: :non_neg_integer,
    default: 50_000,
    doc:
      "The maximum amount of bytes to fetch in a single request. Must be lower than fetcher config `max_bytes_per_request`"
  ],
  fetch_interval_ms: [
    type: :non_neg_integer,
    default: 5000,
    doc: """
    Time in milliseconds that the consumer will wait before trying to fetch new data from the broker after it runs out of records to process.

    The consumer always tries to optimize fetch requests wait times by issuing requests before it's internal queue is empty. Therefore
    this option is only used for the wait time after a fetch request returns empty.

    TODO: Add backoff description
    """
  ],
  handler_cooldown_ms: [
    type: :non_neg_integer,
    default: 0,
    doc: """
    Time in milliseconds that the consumer will wait before handling new records. Can be overrided for one cycle by the handler return value.
    """
  ],
  handler_max_commits_in_flight: [
    type: :non_neg_integer,
    default: 0,
    doc: """
    Controls how many commit messages can be waiting for confirmation before the consumer stops processing new records.

    When this limit is reached, processing pauses until confirmations are received. Set to 0 to process records one batch at a time - each batch must be fully confirmed before starting the next.
    """
  ],
  handler_max_batch_size: [
    type: :pos_integer,
    default: 10,
    doc:
      "The maximum amount of records that will be delivered to the handler in each processing cycle."
  ]
]
</code></pre>
<p>Notable options:</p>
<ul>
<li>
<p><code>:fetch_max_bytes</code> - Controls the size of each fetch request (not the full queue size). Actual memory use may exceed this due to async fetch prefetching.</p>
</li>
<li>
<p><code>:fetch_interval_ms</code> - This setting only applies when a fetch request returns no records. On busy topics, the consumer fetches on demand as soon as the internal queue drops below a threshold. But if a fetch returns empty, the consumer enters a progressive linear backoff, gradually increasing the wait time until it reaches <code>:fetch_interval_ms</code>, after which it waits that full interval between retries until new data becomes available.</p>
</li>
<li>
<p><code>:handler_cooldown_ms</code> - Adds a post-commit cooldown between batches, helping throttle consumption without blocking useful work. Aimed to address issues like the ones reported on the original post when .</p>
</li>
<li>
<p><code>:handler_max_commits_in_flight</code> - Allows processing new batches while waiting for previous commits to complete. A performance vs consistency tradeoff — useful when strict ordering isn’t needed. I’m also planning to add an ETS-based temporary offset store (optionally replicated cluster-wide) to reduce the risk of duplicate processing on crashes.</p>
</li>
</ul>
<h2><a name="p-367633-caveats-6" class="anchor" href="#p-367633-caveats-6" aria-label="Heading link" rel="nofollow"></a>Caveats</h2>
<ul>
<li>
<p>KIP-848 Only: The current implementation is built around the new rebalance protocol introduced in <a href="https://cwiki.apache.org/confluence/display/KAFKA/KIP-848%3A+The+Next+Generation+of+the+Consumer+Rebalance+Protocol" rel="noopener nofollow ugc">KIP-848</a>, which became <a href="https://kafka.apache.org/blog#apache_kafka_400_release_announcement" rel="noopener nofollow ugc">general available on Kafka 4.0</a>. This gives us access to modern consumer features and address ont of the biggest pain points afaik (costly rebalances), but may limit compatibility with older clusters.</p>
</li>
<li>
<p>Still a Work in Progress: The commit logic is still being finalized, but partition assignment, rebalancing, and record handling are already working well. You can check out the current implementation here: <a href="https://github.com/oliveigah/klife" class="inline-onebox" rel="noopener nofollow ugc">GitHub - oliveigah/klife: Kafka client for elixir · GitHub</a></p>
</li>
</ul>
<h2><a name="p-367633-wrap-up-7" class="anchor" href="#p-367633-wrap-up-7" aria-label="Heading link" rel="nofollow"></a>Wrap-up</h2>
<p>Thank you for reading this far!</p>
<p>Again, if you’ve struggled with Kafka in Elixir before, I’d love to hear from you — whether it’s feedback on the proposed interface, missing features in existing clients, or design tradeoffs you’d like to see better addressed. Even non-technical frustrations are valuable at this stage!</p>
<p>Let’s use this thread as an open forum for discussing Elixir + Kafka. Your insights will directly help shape Klife’s future direction.</p>
<p>Thanks in advance! <img src="https://forum.elixirforum.com/images/emoji/apple/purple_heart.png?v=15" title=":purple_heart:" class="emoji" alt=":purple_heart:" loading="lazy" width="20" height="20"></p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="367633" data-batch-url="/posts/batch_likers">
                        9
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/klife-a-kafka-client-with-performance-gains-over-10x/67040/17">Post #16</a>
	                </div>
	            </div>
              <div id="likers-container-367633" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="367633"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #16"></div>
  </section>
</div>
    <div class="postbit" id="367062" data-post-id="367062">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="oliveiragahenrique" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/oliveiragahenrique/120/30594_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  oliveiragahenrique
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>cc: <a class="mention" href="/u/jakemorrison" rel="nofollow">@jakemorrison</a> <a class="mention" href="/u/mindreader" rel="nofollow">@mindreader</a> <a class="mention" href="/u/mekusigjinn" rel="nofollow">@mekusigjinn</a></p>
<p>You mentioned some Kafka pain points in the original announcement post, would love to hear your thoughts here as well. Thanks!</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="367062" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/klife-a-kafka-client-with-performance-gains-over-10x/67040/18">Post #17</a>
	                </div>
	            </div>
              <div id="likers-container-367062" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="367062"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #17"></div>
  </section>
</div>
    <div class="postbit" id="367085" data-post-id="367085">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="mindreader" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/mindreader/120/19278_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  mindreader
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>It looks like you’ve done a lot of good work. I’ve been mostly trying to keep kafka out of my companies because it tends to be the wiring between services and can actually cause the company to stop using elixir in favor of mainstream languages.</p>
<p>That said it looks like you’ve put a lot of thought into it, and if I can no longer avoid it I will definitely take it for a spin. I know that writing fully feature kafka client is a rough job (due to the way kafka works).</p>
<p>One thing that helps is adding telemetry. Seeing information about telemetry in for example <a href="https://hexdocs.pm/gnat/readme.html" class="inline-onebox" rel="noopener nofollow ugc">README — gnat v1.15.1</a> give me some assurance that the author is thinking about production use.</p>
<p>I like the async produce function. Hopefully you can send a pid along so that upon receipt, it can send a message back to the original process.</p>
<p>I worry that using the phash2 function for partitioning is a problem. Kafka has a default partition algorithm and if your algorithm by default doesn’t match how the official bindings hash, it will select a different place to send messages to than the official bindings would, which believe it or not, could cause issues. Not something you would notice immediately, but I’ve written applications where it would matter (production from different languages consistently into partitions).</p>
<p>I dig the testing stuff. That was always lacking when I used to use it. Running an instance of kafka for testing or dev work is absolutely the worst.</p>
<p>Actually one thing I ended up having to do, is in order to simulate kafka without actually running kafka in dev was to basically just write a genserver that would run on all nodes and distribute messages the way they would be distributed in actual kafka. If that came in your package it would be a life saver, and its pretty easy to do in elixir.</p>
<p>Good luck.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="367085" data-batch-url="/posts/batch_likers">
                        4
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/klife-a-kafka-client-with-performance-gains-over-10x/67040/19">Post #18</a>
	                </div>
	            </div>
              <div id="likers-container-367085" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="367085"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #18"></div>
  </section>
</div>
    <div class="postbit" id="367159" data-post-id="367159">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="oliveiragahenrique" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/oliveiragahenrique/120/30594_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  oliveiragahenrique
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Thank you so much for your feedback! (:</p>
<aside class="quote no-group" data-username="mindreader" data-post="19" data-topic="67040">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/mindreader/48/19278_2.png" class="avatar"> mindreader:</div>
<blockquote>
<p>One thing that helps is adding telemetry.</p>
</blockquote>
</aside>
<p>Yes! I will add some telemetry before the first oficial 1.0 for sure! I’m not sure exactly about which ones yet, but I will work on it!</p>
<aside class="quote no-group" data-username="mindreader" data-post="19" data-topic="67040">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/mindreader/48/19278_2.png" class="avatar"> mindreader:</div>
<blockquote>
<p>I like the async produce function. Hopefully you can send a pid along so that upon receipt, it can send a message back to the original process.</p>
</blockquote>
</aside>
<p>This is straightforward to support in <code>produce_async/2</code>, but not in <code>produce_batch_async/2</code>. By design, each record in a batch results in a separate message sent to the target PID. Ideally, we would deliver the entire batch as a single message, but that would require an intermediary process to collect and forward the full list.</p>
<p>To keep the interfaces consistent for now, this behavior is not currently supported. But I would like to support if I can find a good way to do it.</p>
<aside class="quote no-group" data-username="mindreader" data-post="19" data-topic="67040">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/mindreader/48/19278_2.png" class="avatar"> mindreader:</div>
<blockquote>
<p>I worry that using the phash2 function for partitioning is a problem.</p>
</blockquote>
</aside>
<p>Great catch! I had not considered how using a different default partitioning strategy could cause issues in multi-clients environments. I will update the default in version 1.0, thanks for pointing it out!</p>
<p>That said, it’s still easy to support custom behavior by plugging in a custom partitioner.</p>
<aside class="quote no-group" data-username="mindreader" data-post="19" data-topic="67040">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/mindreader/48/19278_2.png" class="avatar"> mindreader:</div>
<blockquote>
<p>I dig the testing stuff. That was always lacking when I used to use it.</p>
</blockquote>
</aside>
<p>Yes! Right now, tests require a local Kafka instance and support assertions on produced records. Some folks have mentioned they are not too comfortable using it in development (personally, I do not think it is that bad, though I might just be too deep into it haha). That said, I definitely plan to add support for testing without a local Kafka. Just need to think through the best way to implement it.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="367159" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/klife-a-kafka-client-with-performance-gains-over-10x/67040/20">Post #19</a>
	                </div>
	            </div>
              <div id="likers-container-367159" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="367159"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #19"></div>
  </section>
</div>
    <div class="postbit" id="379677" data-post-id="379677">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="Schultzer" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/Schultzer/120/4339_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  Schultzer
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I’ll be giving this library a go, I’ll let you know if I experience any issues. Great to see performance being prioritized, which is essential for a well engineered piece of software!</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="379677" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/klife-a-kafka-client-with-performance-gains-over-10x/67040/21">Post #20</a>
	                </div>
	            </div>
              <div id="likers-container-379677" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="379677"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #20"></div>
  </section>
</div>
</template></turbo-stream><turbo-stream action="replace" target="load-more-container"><template><div id="load-more-container" class="load-more-container">
    <a class="load-more-button" data-turbo-stream="true" href="/topics/67040/load_more?page=3">Load more posts (11 remaining)</a>
</div></template></turbo-stream>