Skip to content

AxonOps — AI-Native Control Plane for Open Source Data Platforms

Kafka Client Throttling

Kafka implements throttling mechanisms to protect brokers from overload and ensure fair resource sharing among clients. This guide covers quota types, configuration, client-side backpressure, and quota monitoring.

Broker-side quotas and client-side backpressure controlsBroker-side quotas and client-side backpressure controlsThrottling MechanismsBroker-Side QuotasClient-Side BackpressureProduce Byte RateFetch Byte RateRequest RateController Mutation RateBuffer MemoryIn-Flight RequestsFetch Size

Quota TypeDescriptionUnit
producer_byte_rateUpper bound for producer throughputbytes/sec
consumer_byte_rateUpper bound for fetch throughputbytes/sec
request_percentageUpper bound of time spent processing requestspercentage
controller_mutation_rateRate limit for topic mutationsmutations/sec
connection_creation_rateUpper bound of connections accepted per IPconnections/sec
Quota resolution order from user and client ID down to defaultsQuota resolution order from user and client ID down to defaultsQuota Resolution Order1. User + Client ID2. User + Default Client3. User4. Default User + Client ID5. Default User + Default Client6. Default User7. Client ID8. Default Clientif not definedif not definedif not definedif not definedif not definedif not definedif not defined

Per-User Quota:

Terminal window
# Set quota for specific user
kafka-configs.sh --bootstrap-server localhost:9092 \
--alter --add-config 'producer_byte_rate=10485760,consumer_byte_rate=20971520' \
--entity-type users --entity-name alice

Per-Client-ID Quota:

Terminal window
# Set quota for specific client ID
kafka-configs.sh --bootstrap-server localhost:9092 \
--alter --add-config 'producer_byte_rate=5242880' \
--entity-type clients --entity-name batch-producer

User + Client-ID Quota:

Terminal window
# Set quota for specific user and client combination
kafka-configs.sh --bootstrap-server localhost:9092 \
--alter --add-config 'producer_byte_rate=52428800' \
--entity-type users --entity-name alice \
--entity-type clients --entity-name high-priority-producer

Default Quota:

Terminal window
# Set default quota for all users
kafka-configs.sh --bootstrap-server localhost:9092 \
--alter --add-config 'producer_byte_rate=1048576,consumer_byte_rate=2097152' \
--entity-type users --entity-default
# Set default quota for all clients
kafka-configs.sh --bootstrap-server localhost:9092 \
--alter --add-config 'request_percentage=25' \
--entity-type clients --entity-default

Broker returning a throttle time when a produce quota is exceededClientBrokerClientClientBrokerBrokerProduceRequest (1 MB)Track: user=alicebytes=1MB, time=100msQuota: 10 MB/sCurrent: 1 MB / 100 ms = 10 MB/sStatus: At limitProduceRequest (1 MB)Would exceed quotaProduceResponsethrottle_time_ms=100Client delays next requestby throttle_time_ms
throttle_time = (observed_rate - quota_limit) / quota_limit × window_size

Example:

  • Quota: 10 MB/s
  • Observed rate: 15 MB/s
  • Excess: 5 MB/s (50% over)
  • Throttle time: ~500ms to bring back to limit

Window size comes from the quota sampling configuration (quota.window.size.seconds and num.quota.samples).

Client state transitions during a throttle periodClient state transitions during a throttle periodSendingThrottledWaitingClient should not sendnew requests duringthrottle periodthrottle_time_ms > 0start timertimer expiresthrottle_time_ms = 0

# Percentage of I/O thread time
request_percentage=25

Calculation:

request_percentage is a percentage (double) representing the upper bound of time spent processing requests. Values can exceed 100 (for example, 200).

Limits admin operations to protect the controller:

Terminal window
# Set controller mutation rate quota
kafka-configs.sh --bootstrap-server localhost:9092 \
--alter --add-config 'controller_mutation_rate=10' \
--entity-type users --entity-name admin-user
OperationMutations
CreateTopics (1 topic, 10 partitions)10
DeleteTopics (1 topic)1
CreatePartitions (+5 partitions)5
AlterConfigsVariable

Producer configuration settings that apply backpressureProducer configuration settings that apply backpressureProducer Backpressure Points"buffer.memory" as BMBlocks send() whenbuffer exhausted"max.in.flight.requests.per.connection" as MIFLimits concurrentrequests per broker"max.block.ms" as MBMax time to blockon buffer/metadata

Configuration:

# Total buffer memory
buffer.memory=33554432 # 32 MB
# Max concurrent requests per connection
max.in.flight.requests.per.connection=5
# Max time to block on send()
max.block.ms=60000 # 60 seconds
Consumer flow control settingsConsumer flow control settingsConsumer Flow Control"fetch.max.bytes" as FMBMax bytes perfetch response"max.partition.fetch.bytes" as MPFBMax bytes perpartition per fetch"max.poll.records" as MPRMax recordsreturned by poll()"pause()/resume()" as PRProgrammaticflow control

Configuration:

# Max bytes per fetch response
fetch.max.bytes=52428800 # 50 MB
# Max bytes per partition
max.partition.fetch.bytes=1048576 # 1 MB
# Max records per poll
max.poll.records=500
// Implement backpressure with pause/resume
while (running) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
for (TopicPartition partition : records.partitions()) {
if (shouldPause(partition)) {
// Stop fetching from this partition
consumer.pause(Collections.singleton(partition));
pausedPartitions.add(partition);
} else {
processRecords(records.records(partition));
}
}
// Resume partitions that can accept more
for (TopicPartition partition : pausedPartitions) {
if (canResume(partition)) {
consumer.resume(Collections.singleton(partition));
pausedPartitions.remove(partition);
}
}
}

MetricDescription
kafka.server:type=Fetch,name=queue-sizeThrottled fetch queue size
kafka.server:type=Produce,name=queue-sizeThrottled produce queue size
kafka.server:type=Request,name=queue-sizeThrottled request queue size
kafka.server:type=Fetch,name=throttle-timeAverage fetch throttle time
kafka.server:type=Produce,name=throttle-timeAverage produce throttle time
kafka.server:type=Request,name=throttle-timeAverage request throttle time
kafka.server:type=ControllerMutation,name=throttle-timeAverage controller mutation throttle time
MetricDescription
produce-throttle-time-avgAverage produce throttle time
produce-throttle-time-maxMax produce throttle time
fetch-throttle-time-avgAverage fetch throttle time
fetch-throttle-time-maxMax fetch throttle time
Terminal window
# List all quotas
kafka-configs.sh --bootstrap-server localhost:9092 \
--describe --entity-type users
# Describe specific user quota
kafka-configs.sh --bootstrap-server localhost:9092 \
--describe --entity-type users --entity-name alice
# Describe specific client quota
kafka-configs.sh --bootstrap-server localhost:9092 \
--describe --entity-type clients --entity-name my-producer

Quota Planning ExampleQuota Planning ExampleCluster CapacityAllocation3 Brokers × 100 MB/s = 300 MB/sCritical Apps: 60% = 180 MB/sBatch Jobs: 30% = 90 MB/sDevelopment: 10% = 30 MB/s
Client TypeProducer QuotaConsumer QuotaRequest %
Critical real-time50-100 MB/s100-200 MB/s50%
Standard production10-50 MB/s50-100 MB/s25%
Batch processing5-20 MB/s20-50 MB/s10%
Development1-5 MB/s5-10 MB/s5%

Terminal window
# Increase quota without restart
kafka-configs.sh --bootstrap-server localhost:9092 \
--alter --add-config 'producer_byte_rate=52428800' \
--entity-type users --entity-name alice
# Remove quota (fall back to default)
kafka-configs.sh --bootstrap-server localhost:9092 \
--alter --delete-config 'producer_byte_rate' \
--entity-type users --entity-name alice
Quota configuration change propagating to all brokersAdminBroker 1ZK.KRaftBroker 2Broker 3AdminAdminBroker 1Broker 1ZK/KRaftZK/KRaftBroker 2Broker 2Broker 3Broker 3AlterClientQuotasWrite quota configAckConfig change notificationConfig change notificationUpdate quota cacheUpdate quota cacheQuotas effective within secondsNo client restart needed

// Monitor throttle metrics
producer.metrics().forEach((name, metric) -> {
if (name.name().contains("throttle")) {
double value = (double) metric.metricValue();
if (value > 0) {
log.warn("Producer throttled: {} = {}", name, value);
}
}
});
// Check for throttling in consumer
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
// Get metrics
Metric throttleMetric = consumer.metrics().get(
new MetricName("fetch-throttle-time-avg", "consumer-fetch-manager-metrics",
"", Collections.emptyMap()));
if (throttleMetric != null) {
double throttleTime = (double) throttleMetric.metricValue();
if (throttleTime > 100) {
log.warn("Consumer being throttled: {}ms average", throttleTime);
}
}
public class AdaptiveProducer {
private final RateLimiter rateLimiter;
private double currentRate;
public void send(ProducerRecord<String, String> record) {
rateLimiter.acquire(); // Client-side rate limiting
producer.send(record, (metadata, exception) -> {
if (metadata != null && metadata.throttleTimeMs() > 0) {
// Reduce rate when throttled
adjustRate(metadata.throttleTimeMs());
}
});
}
private void adjustRate(long throttleTimeMs) {
if (throttleTimeMs > 100) {
currentRate *= 0.8; // Reduce by 20%
rateLimiter.setRate(currentRate);
}
}
}

Throttle replication traffic during reassignment:

Terminal window
# Set replication throttle
kafka-configs.sh --bootstrap-server localhost:9092 \
--alter --add-config 'leader.replication.throttled.rate=50000000,follower.replication.throttled.rate=50000000' \
--entity-type brokers --entity-name 1
Terminal window
# Reassignment with throttle
kafka-reassign-partitions.sh --bootstrap-server localhost:9092 \
--reassignment-json-file reassignment.json \
--throttle 50000000 \
--execute
# Verify and remove throttle after completion
kafka-reassign-partitions.sh --bootstrap-server localhost:9092 \
--reassignment-json-file reassignment.json \
--verify

SymptomPossible CauseSolution
High throttle timeQuota too lowIncrease quota
Inconsistent throttlingMultiple client IDsConsolidate or set per-client quotas
Sudden throttlingBurst trafficImplement client-side rate limiting
All clients throttledDefault quota too lowIncrease default or set specific quotas
Terminal window
# Check current quotas
kafka-configs.sh --bootstrap-server localhost:9092 \
--describe --entity-type users --entity-type clients
# Check broker metrics
kafka-run-class.sh kafka.tools.JmxTool \
--object-name 'kafka.server:type=ClientQuotaManager,user=*,client-id=*' \
--jmx-url service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi

FeatureMinimum Version
Byte rate quotas0.9.0
Request percentage quota0.11.0
User quotas0.10.1
User + Client-ID quotas0.10.1
Controller mutation quota2.7.0

PracticeRationale
Set default quotasPrevent unknown clients from overloading
Use user quotas with authenticationAccurate tracking per user
Leave headroomAllow for traffic bursts
Monitor throttle metricsDetect quota issues early
PracticeRationale
Implement client-side rate limitingSmooth traffic patterns
Handle throttle responseAvoid wasted requests
Use pause/resume for consumersControl processing rate
Monitor throttle metricsAdapt to quota changes