Skip to content

Commit 6d034be

Browse files
orangecolaclaude
andcommitted
address PR review comments
- Move check_crcs and auto_create_topics to build_common_props (both supported by KafkaShareConsumer) - Add CONSUMER_GROUP_ONLY_OPTIONS + validate_consumer_group_config! to raise on share_group_acknowledgement_on_error with consumer_group mode - Release unprocessed records on shutdown in share_group_thread_runner instead of blocking on queue - Replace ternary with if/else for @share_group_ack_error_type assignment - Update consumer_threads docs with clearer partition/thread guidance per mode - Update share group acknowledgement docs to clarify pipeline queue visibility boundary - Update share_group_acknowledgement_on_error docs to frame around pipeline semantics - Add note about broker/group-level equivalents for incompatible share_group options Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent a17e163 commit 6d034be

2 files changed

Lines changed: 53 additions & 32 deletions

File tree

docs/input-kafka.asciidoc

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,11 @@ you could run multiple Logstash instances with the same `group_id` to spread the
5858
physical machines. Messages in a topic will be distributed to all Logstash instances with
5959
the same `group_id`.
6060

61-
In `consumer_group` mode, you should ideally have as many threads as the number of partitions
62-
for a perfect balance -- more threads than partitions means that some threads will be idle.
63-
This does not apply in `share_group` mode, where any thread can fetch from any partition, so
64-
the number of threads is not bounded by the partition count.
61+
The number of consumer threads this input should spawn for fetching events and pushing them into the pipeline's queue.
62+
63+
When configured with `consumer_mode => consumer_group`, each partition in the topic can only be assigned to a single consumer at a time, so if the _total_ number of consumer threads across all consumers in the group exceeds the number of partitions, some of those threads will be idle.
64+
65+
This constraint doesn't apply when configured with `consumer_mode => share_group` which is not partition-oriented.
6566

6667
[id="plugins-{type}s-{plugin}-share-groups"]
6768
==== Kafka Share Groups (queue semantics)
@@ -76,10 +77,9 @@ Requirements:
7677
* Kafka brokers 4.0 or later with `group.share.enable=true`
7778
* The `group_id` setting identifies the share group name
7879

79-
Share group consumers must acknowledge each record. Successful records are always acknowledged
80-
as `ACCEPT`. The <<plugins-{type}s-{plugin}-share_group_acknowledgement_on_error>> option
81-
controls acknowledgement for records that fail processing (`release` for redelivery or `reject`
82-
to permanently discard).
80+
Share group consumers must acknowledge each record.
81+
After an event is accepted by the pipeline's queue, it is acknowledged as `ACCEPT` to ensure that no other consumer in the share group receives it.
82+
When an event is either _not_ accepted by the pipeline's queue or an error occurs before it is offered to the queue, the <<plugins-{type}s-{plugin}-share_group_acknowledgement_on_error>> option controls how the event is acknowledged (`release` for redelivery or `reject` to permanently discard).
8383

8484
Example configuration:
8585

@@ -350,7 +350,13 @@ Setting any of the following options in the pipeline config raises a configurati
350350
`share_group` mode, since they do not apply to the Share Group protocol: `group_protocol`,
351351
`group_instance_id`, `partition_assignment_strategy`, `heartbeat_interval_ms`,
352352
`session_timeout_ms`, `isolation_level`, `enable_auto_commit`, `auto_commit_interval_ms`,
353-
`auto_offset_reset`, `check_crcs`, `exclude_internal_topics`, `schema_registry_url`.
353+
`auto_offset_reset`, `exclude_internal_topics`, `schema_registry_url`.
354+
355+
Some of these have broker/group-level equivalents for share groups that can be configured
356+
directly on the broker (for example, `auto.offset.reset` → `share.auto.offset.reset`,
357+
`heartbeat.interval.ms` → `share.heartbeat.interval.ms`,
358+
`session.timeout.ms` → `share.session.timeout.ms`,
359+
`isolation.level` → `share.isolation.level`).
354360
====
355361

356362
[id="plugins-{type}s-{plugin}-consumer_threads"]
@@ -910,16 +916,10 @@ and a rebalance operation is triggered for the group identified by `group_id`
910916
* Default value is `release`
911917
* Only applicable when <<plugins-{type}s-{plugin}-consumer_mode>> is `share_group`
912918

913-
Controls how a record is acknowledged when an error occurs while processing it.
914-
915-
`release` (default): The record is released back to the share group for redelivery to any
916-
available consumer. Use this when processing errors are transient and the record should
917-
be retried.
918-
919-
`reject`: The record is permanently discarded and will not be redelivered. Use this when
920-
the record is malformed and retrying would never succeed.
919+
Controls how a record is acknowledged when the event is either not accepted by the pipeline's queue or an error occurs before it is offered.
921920

922-
Successfully processed records are always acknowledged as ACCEPT regardless of this setting.
921+
* `release` (default): The record is released back to the share group for redelivery to any available consumer. This strategy favors consistency over availability, which can allow a single malformed event to be retried indefinitely.
922+
* `reject`: The record is permanently discarded and will not be redelivered. This strategy favors availability over consistency and risks data loss.
923923

924924
[id="plugins-{type}s-{plugin}-ssl_endpoint_identification_algorithm"]
925925
===== `ssl_endpoint_identification_algorithm`

lib/logstash/inputs/kafka.rb

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ class LogStash::Inputs::Kafka < LogStash::Inputs::Base
301301
# error if set in the pipeline config: `group_protocol`, `group_instance_id`,
302302
# `partition_assignment_strategy`, `heartbeat_interval_ms`, `session_timeout_ms`,
303303
# `isolation_level`, `enable_auto_commit`, `auto_commit_interval_ms`, `auto_offset_reset`,
304-
# `check_crcs`, `exclude_internal_topics`, `schema_registry_url`.
304+
# `exclude_internal_topics`, `schema_registry_url`.
305305
config :consumer_mode, :validate => ["consumer_group", "share_group"], :default => "consumer_group"
306306

307307
# Controls how records are acknowledged when `consumer_mode` is set to `share_group`.
@@ -335,11 +335,14 @@ def initialize(params = {})
335335
enable_auto_commit
336336
auto_commit_interval_ms
337337
auto_offset_reset
338-
check_crcs
339338
exclude_internal_topics
340339
schema_registry_url
341340
].freeze
342341

342+
CONSUMER_GROUP_ONLY_OPTIONS = %w[
343+
share_group_acknowledgement_on_error
344+
].freeze
345+
343346
public
344347
def register
345348
@runner_threads = []
@@ -350,9 +353,14 @@ def register
350353
if @consumer_mode == "share_group"
351354
validate_share_group_config!
352355
ack_type_class = org.apache.kafka.clients.consumer.AcknowledgeType
353-
@share_group_ack_error_type = @share_group_acknowledgement_on_error == "reject" ?
354-
ack_type_class::REJECT : ack_type_class::RELEASE
356+
@share_group_ack_error_type =
357+
if @share_group_acknowledgement_on_error == "reject"
358+
ack_type_class::REJECT
359+
else
360+
ack_type_class::RELEASE
361+
end
355362
else
363+
validate_consumer_group_config!
356364
check_schema_registry_parameters
357365
set_group_protocol!
358366
end
@@ -532,6 +540,8 @@ def build_common_props(client_id)
532540
props.put(kafka::RETRY_BACKOFF_MS_CONFIG, retry_backoff_ms.to_s) unless retry_backoff_ms.nil?
533541
props.put(kafka::SEND_BUFFER_CONFIG, send_buffer_bytes.to_s) unless send_buffer_bytes.nil?
534542
props.put(kafka::VALUE_DESERIALIZER_CLASS_CONFIG, value_deserializer_class)
543+
props.put(kafka::ALLOW_AUTO_CREATE_TOPICS_CONFIG, auto_create_topics) unless auto_create_topics.nil?
544+
props.put(kafka::CHECK_CRCS_CONFIG, check_crcs.to_s) unless check_crcs.nil?
535545
props.put(kafka::CLIENT_RACK_CONFIG, client_rack) unless client_rack.nil?
536546

537547
props.put("security.protocol", security_protocol) unless security_protocol.nil?
@@ -554,8 +564,6 @@ def create_consumer(client_id, group_instance_id)
554564

555565
props.put(kafka::AUTO_COMMIT_INTERVAL_MS_CONFIG, auto_commit_interval_ms.to_s) unless auto_commit_interval_ms.nil?
556566
props.put(kafka::AUTO_OFFSET_RESET_CONFIG, auto_offset_reset) unless auto_offset_reset.nil?
557-
props.put(kafka::ALLOW_AUTO_CREATE_TOPICS_CONFIG, auto_create_topics) unless auto_create_topics.nil?
558-
props.put(kafka::CHECK_CRCS_CONFIG, check_crcs.to_s) unless check_crcs.nil?
559567
props.put(kafka::ENABLE_AUTO_COMMIT_CONFIG, enable_auto_commit.to_s)
560568
props.put(kafka::EXCLUDE_INTERNAL_TOPICS_CONFIG, exclude_internal_topics) unless exclude_internal_topics.nil?
561569
props.put(kafka::GROUP_INSTANCE_ID_CONFIG, group_instance_id) unless group_instance_id.nil?
@@ -650,6 +658,14 @@ def validate_share_group_config!
650658
end
651659
end
652660

661+
def validate_consumer_group_config!
662+
incompatible = original_params.keys & CONSUMER_GROUP_ONLY_OPTIONS
663+
unless incompatible.empty?
664+
raise LogStash::ConfigurationError,
665+
"The following options require consumer_mode => 'share_group' and cannot be used with consumer_group mode: #{incompatible.join(', ')}"
666+
end
667+
end
668+
653669
def create_share_consumer(client_id)
654670
begin
655671
props = build_common_props(client_id)
@@ -674,18 +690,23 @@ def share_group_thread_runner(logstash_queue, consumer, name)
674690
begin
675691
codec_instance = @codec.clone
676692
ack_accept = org.apache.kafka.clients.consumer.AcknowledgeType::ACCEPT
693+
ack_release = org.apache.kafka.clients.consumer.AcknowledgeType::RELEASE
677694
until stop?
678695
records = do_poll(consumer)
679696
unless records.empty?
680697
records.each do |record|
681-
begin
682-
handle_record(record, codec_instance, logstash_queue)
683-
consumer.acknowledge(record, ack_accept)
684-
rescue => e
685-
logger.error("Error processing record in share group mode, acknowledging with #{@share_group_acknowledgement_on_error}",
686-
:kafka_error_message => e,
687-
:cause => e.respond_to?(:getCause) ? e.getCause() : nil)
688-
consumer.acknowledge(record, @share_group_ack_error_type)
698+
if stop?
699+
consumer.acknowledge(record, ack_release)
700+
else
701+
begin
702+
handle_record(record, codec_instance, logstash_queue)
703+
consumer.acknowledge(record, ack_accept)
704+
rescue => e
705+
logger.error("Error processing record in share group mode, acknowledging with #{@share_group_acknowledgement_on_error}",
706+
:kafka_error_message => e,
707+
:cause => e.respond_to?(:getCause) ? e.getCause() : nil)
708+
consumer.acknowledge(record, @share_group_ack_error_type)
709+
end
689710
end
690711
end
691712
commit_share_acknowledgements(consumer)

0 commit comments

Comments
 (0)