fluent-plugin-kafka 0.19.8 → 0.19.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f3e5a24c5a5c936b9d502631d6382faa9315a54f0813e5b636cf025ecb64212c
4
- data.tar.gz: 43c1b088840c92c330c70c456d92e3849d9de326b6b772c55bae63affea9f3e3
3
+ metadata.gz: 2eeb3b37a48db8cde6fda0eb9e413f507076c231abd06456a35540a10eed5667
4
+ data.tar.gz: 90b95493d3445829123ee569af66f581b109dcea7f91cf3d9aee6b2b6b9caeab
5
5
  SHA512:
6
- metadata.gz: 608800269cb2f306911e4a1a6a6aeea8b7e0e69fde4b34b5a165f67d4a306e82226de890151ffff813a9ba91ef4399d87aeb531243cb63cdf625a023cf92d3e7
7
- data.tar.gz: e818390d3f8946d0449a80d8c8ddd612e4a96632b2d6765fdcbf3beeb394c0cdc7c95e0cdce8c0780a05e66e39812328ddb48cd32135476cf5a85dac244485ef
6
+ metadata.gz: 3abf77244f8a7b239e92f63c798f0dc70b0d078a3b75de8f17ddc9e7cb17a8108088cc4f4bc3e44005496c68c89e960ca727def67cb7418f8e001bb713c938b4
7
+ data.tar.gz: 892d0c09e810b9fe78f87917a8468ac392b206991e99c4080f755e80953db79d8637e41717ef3ae0acbf13baa26e2fd1fe032bef7650ca70bc62df44b1dead7a
@@ -11,7 +11,7 @@ jobs:
11
11
  uses: ruby/actions/.github/workflows/ruby_versions.yml@master
12
12
  with:
13
13
  engine: cruby
14
- min_version: 3.0
14
+ min_version: 3.2
15
15
  build:
16
16
  needs: ruby-versions
17
17
  runs-on: ${{ matrix.os }}
@@ -29,25 +29,12 @@ jobs:
29
29
  - { min: '>= 0.14.0', max: '< 0.16.0' }
30
30
  - { min: '>= 0.16.0', max: '< 0.25.0' }
31
31
  - { min: '>= 0.25.0', max: '>= 0.25.0' }
32
- # rdkafka 0.25.0 supports Ruby 3.2+, so we exclude it for Ruby 3.0 and 3.1
33
32
  exclude:
34
33
  - ruby: head
35
- - ruby: '3.1'
36
- rdkafka_versions: { min: '>= 0.25.0', max: '>= 0.25.0' }
37
- - ruby: '3.0'
38
- rdkafka_versions: { min: '>= 0.25.0', max: '>= 0.25.0' }
39
- # rdkafka 0.15.2 is the last version which supports Ruby 2.7
40
- include:
41
- - ruby: '2.7'
42
- os: ubuntu-latest
43
- rdkafka_versions: { min: '>= 0.6.0', max: '< 0.12.0' }
44
- - ruby: '2.7'
45
- os: ubuntu-latest
46
- rdkafka_versions: { min: '>= 0.12.0', max: '= 0.15.2' }
47
34
  name: Ruby ${{ matrix.ruby }} unit testing on ${{ matrix.os }} with rdkafka gem version (min ${{ matrix.rdkafka_versions.min }} max ${{ matrix.rdkafka_versions.max }})
48
35
  steps:
49
36
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
50
- - uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
37
+ - uses: ruby/setup-ruby@14594264cd68ce8a2345dd349bc3d138a4ef85c8 # v1.327.0
51
38
  with:
52
39
  ruby-version: ${{ matrix.ruby }}
53
40
  - name: Install confluent-kafka
data/ChangeLog CHANGED
@@ -1,3 +1,25 @@
1
+ Release 0.19.9 - 2026/10/30
2
+ * out_kafka2: Fix duplicated records and failing writes with
3
+ `share_producer true`. All flush threads used one ruby-kafka producer
4
+ at the same time, although it is not thread safe. NOTE: only one flush
5
+ thread at a time now uses the shared producer, so chunks are no longer
6
+ sent in parallel even if `flush_thread_count` is 2 or more. Keep
7
+ `share_producer false` to send them in parallel. (#589)
8
+ * out_kafka2: Fix later writes failing until restart after a delivery
9
+ error. Since 0.18.0, the messages of a failed delivery stayed in the
10
+ producer, so later writes could keep failing with
11
+ `Kafka::MessageSizeTooLarge`. The producer is now discarded after a
12
+ failure and created again on the next write. (#588, #589)
13
+ * in_kafka: Fix endless re-fetching of a batch with a record larger than
14
+ `chunk_limit_size`. The offset did not advance, and the records that
15
+ did fit were emitted again on every `interval`. NOTE: such a record is
16
+ now dropped with a warning. Use `<label @ERROR>` to keep it. (#584)
17
+ * in_kafka_group: Fix endless consumer restarts on a record larger than
18
+ `chunk_limit_size`. The consumer restarted without committing offsets,
19
+ so the same batch was fetched forever, and the records that did fit
20
+ were emitted again each time. NOTE: such a record is now dropped with a
21
+ warning. Use `<label @ERROR>` to keep it. (#583)
22
+
1
23
  Release 0.19.8 - 2026/09/04
2
24
  * in_kafka: Emit each record with its own tag when `tag_source record` is
3
25
  set. The whole fetch batch used to be emitted with the tag of the
data/README.md CHANGED
@@ -263,6 +263,8 @@ The `<formatter name>` in `<format>` uses fluentd's formatter plugins. See [form
263
263
 
264
264
  **Note:** Java based Kafka client uses `murmur2` as partitioner function by default. If you want to use same partitioning behavior with fluent-plugin-kafka, change it to `murmur2` instead of `crc32`. Note that for using `murmur2` hash partitioner function, you must install `digest-murmurhash` gem.
265
265
 
266
+ **Note:** `share_producer true` makes all flush threads use one ruby-kafka producer. This reduces the connections to the brokers. However, the ruby-kafka producer is not thread safe, so the flush threads send chunks one at a time. To send chunks in parallel with `flush_thread_count`, keep `share_producer` false. `rdkafka2` can share its producer without this limit, because librdkafka is thread safe.
267
+
266
268
  ruby-kafka sometimes returns `Kafka::DeliveryFailed` error without good information.
267
269
  In this case, `get_kafka_client_log` is useful for identifying the error cause.
268
270
  ruby-kafka's log is routed to fluentd log so you can see ruby-kafka's log in fluentd logs.
@@ -273,7 +275,7 @@ Supports following ruby-kafka's producer options.
273
275
  - required_acks - default: -1 - The number of acks required per request. If you need flush performance, set lower value, e.g. 1, 2.
274
276
  - ack_timeout - default: nil - How long the producer waits for acks. The unit is seconds.
275
277
  - compression_codec - default: nil - The codec the producer uses to compress messages.
276
- - max_send_limit_bytes - default: nil - Max byte size to send message to avoid MessageSizeTooLarge. For example, if you set 1000000(message.max.bytes in kafka), Message more than 1000000 byes will be dropped.
278
+ - max_send_limit_bytes - default: nil - Max byte size of one record after formatting. A larger record is dropped with a warning, and the other records in the chunk are sent. For example, if you set 1000000, a record larger than 1000000 bytes is dropped. This does not limit the size of a record batch, so it does not prevent MessageSizeTooLarge by itself. See [FAQ](#why-does-fluentd-fail-with-kafkamessagesizetoolarge-or-msg_size_too_large).
277
279
  - discard_kafka_delivery_failed - default: false - discard the record where [Kafka::DeliveryFailed](http://www.rubydoc.info/gems/ruby-kafka/Kafka/DeliveryFailed) occurred
278
280
 
279
281
  If you want to know about detail of monitoring, see also https://github.com/zendesk/ruby-kafka#monitoring
@@ -451,7 +453,7 @@ Support of fluentd v0.12 has ended. `kafka_buffered` will be an alias of `kafka2
451
453
  - required_acks - default: -1 - The number of acks required per request. If you need flush performance, set lower value, e.g. 1, 2.
452
454
  - ack_timeout - default: nil - How long the producer waits for acks. The unit is seconds.
453
455
  - compression_codec - default: nil - The codec the producer uses to compress messages.
454
- - max_send_limit_bytes - default: nil - Max byte size to send message to avoid MessageSizeTooLarge. For example, if you set 1000000(message.max.bytes in kafka), Message more than 1000000 byes will be dropped.
456
+ - max_send_limit_bytes - default: nil - Max byte size of one record after formatting. A larger record is dropped with a warning, and the other records in the chunk are sent. For example, if you set 1000000, a record larger than 1000000 bytes is dropped. This does not limit the size of a record batch, so it does not prevent MessageSizeTooLarge by itself. See [FAQ](#why-does-fluentd-fail-with-kafkamessagesizetoolarge-or-msg_size_too_large).
455
457
  - discard_kafka_delivery_failed - default: false - discard the record where [Kafka::DeliveryFailed](http://www.rubydoc.info/gems/ruby-kafka/Kafka/DeliveryFailed) occurred
456
458
  - monitoring_list - default: [] - library to be used to monitor. statsd and datadog are supported
457
459
 
@@ -564,7 +566,8 @@ You need to install rdkafka gem.
564
566
  share_producer (bool) :default => false
565
567
  # Timeout for polling message wait. If 0, no wait.
566
568
  rdkafka_delivery_handle_poll_timeout (integer) :default => 30
567
- # If the record size is larger than this value, such records are ignored. Default is no limit
569
+ # Max byte size of one record after formatting. Larger records are dropped.
570
+ # This does not limit the size of a record batch. Default is no limit
568
571
  max_send_limit_bytes (integer) :default => nil
569
572
  # The maximum number of enqueueing bytes per second. It can reduce the
570
573
  # load of both Fluentd and Kafka when excessive messages are attempted
@@ -608,6 +611,40 @@ To avoid the problem, there are 2 approaches:
608
611
  - Upgrade your kafka cluster to latest version. This is better because recent version is faster and robust.
609
612
  - Downgrade ruby-kafka/fluent-plugin-kafka to work with your older kafka.
610
613
 
614
+ ### Why does Fluentd fail with Kafka::MessageSizeTooLarge (or msg_size_too_large)?
615
+
616
+ Kafka rejects a record batch that is larger than the size limit of the topic. The limit is `max.message.bytes` of the topic if it is set, and `message.max.bytes` of the broker otherwise. The topic value can be larger or smaller than the broker value. Below, "the topic limit" means this value. If one output writes to several topics, use the smallest one.
617
+
618
+ `kafka2` sends all records of a buffer chunk at once, and the records for the same partition go into one record batch. In the worst case, the whole chunk becomes one record batch. To make the batches smaller, lower `chunk_limit_size` in `<buffer>`.
619
+
620
+ `chunk_limit_size` is the size of a chunk in the buffer, where records are kept as msgpack. The records sent to Kafka can be larger, because of `<format>`, `<inject>` and the overhead of each Kafka record. Start with about half of the topic limit, and lower it if the error continues. If `<buffer>` has `compress gzip` or `compress zstd`, `chunk_limit_size` limits the compressed size, and a chunk can hold several times more data. In that case, divide it further by the compression ratio of your data. `compression_codec` works the other way. Kafka checks the size of a record batch after compression, so compressed batches leave more room. How much depends on how well your data compresses.
621
+
622
+ `max_send_limit_bytes` does not make the batch smaller. It checks only the formatted value of each record, and drops the records larger than this setting. The message key, headers and the overhead of the batch are not counted. To drop a single record that Kafka would reject, set it below the topic limit with some room for them.
623
+
624
+ The following example assumes a topic limit of 1MB.
625
+
626
+ <match app.**>
627
+ @type kafka2
628
+ brokers <broker1_host>:<broker1_port>
629
+ default_topic app
630
+
631
+ max_send_limit_bytes 1000000
632
+
633
+ <format>
634
+ @type json
635
+ </format>
636
+
637
+ <buffer topic>
638
+ chunk_limit_size 512k
639
+ </buffer>
640
+ </match>
641
+
642
+ Fluentd may log `chunk bytes limit exceeds for an emitted event stream`. This warning comes from the Fluentd buffer. It means that one event stream emitted to the output was larger than `chunk_limit_size`, and Fluentd splits it into several chunks. The number in the warning is the size of that event stream in the buffer, not the growth by formatting. If a single record is larger than `chunk_limit_size`, that record cannot be stored, and Fluentd raises `BufferChunkOverflowError`.
643
+
644
+ `kafka_buffered` splits a chunk into smaller deliveries by `kafka_agg_max_bytes` (default: 4096) and `kafka_agg_max_messages`, so `chunk_limit_size` does not decide its batch size.
645
+
646
+ `rdkafka2` passes records to librdkafka one by one, and librdkafka builds the record batches within its own `batch.size` and `message.max.bytes`. You can set them in `rdkafka_options`. Keep them below the topic limit. `msg_size_too_large` is in the default of `unrecoverable_error_codes`, so Fluentd does not retry a chunk that hits it.
647
+
611
648
  ## Contributing
612
649
 
613
650
  1. Fork it
@@ -13,7 +13,7 @@ Gem::Specification.new do |gem|
13
13
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
14
  gem.name = "fluent-plugin-kafka"
15
15
  gem.require_paths = ["lib"]
16
- gem.version = '0.19.8'
16
+ gem.version = '0.19.9'
17
17
  gem.required_ruby_version = ">= 2.1.0"
18
18
 
19
19
  gem.add_dependency "fluentd", [">= 0.10.58", "< 2"]
@@ -62,6 +62,12 @@ class Fluent::KafkaInput < Fluent::Input
62
62
  include Fluent::KafkaPluginUtil::SSLSettings
63
63
  include Fluent::KafkaPluginUtil::SaslSettings
64
64
 
65
+ BufferChunkOverflowError = if defined?(Fluent::Plugin::Buffer::BufferChunkOverflowError)
66
+ Fluent::Plugin::Buffer::BufferChunkOverflowError
67
+ else
68
+ Fluent::BufferChunkLimitError
69
+ end
70
+
65
71
  unless method_defined?(:router)
66
72
  define_method("router") { Fluent::Engine }
67
73
  end
@@ -348,7 +354,11 @@ class Fluent::KafkaInput < Fluent::Input
348
354
  offset = messages.last.offset + 1
349
355
 
350
356
  event_streams.each { |tag, es|
351
- @router.emit_stream(tag, es)
357
+ begin
358
+ @router.emit_stream(tag, es)
359
+ rescue BufferChunkOverflowError => e
360
+ $log.warn "Skipped records larger than the buffer chunk limit size in #{@topic_entry.topic}/#{@topic_entry.partition}", :tag => tag, :error => e.to_s
361
+ end
352
362
  }
353
363
 
354
364
  if @offset_manager
@@ -85,6 +85,12 @@ class Fluent::KafkaGroupInput < Fluent::Input
85
85
  Fluent::BufferQueueLimitError
86
86
  end
87
87
 
88
+ BufferChunkOverflowError = if defined?(Fluent::Plugin::Buffer::BufferChunkOverflowError)
89
+ Fluent::Plugin::Buffer::BufferChunkOverflowError
90
+ else
91
+ Fluent::BufferChunkLimitError
92
+ end
93
+
88
94
  unless method_defined?(:router)
89
95
  define_method("router") { Fluent::Engine }
90
96
  end
@@ -380,6 +386,8 @@ class Fluent::KafkaGroupInput < Fluent::Input
380
386
  retries = 0
381
387
  begin
382
388
  router.emit_stream(tag, es)
389
+ rescue BufferChunkOverflowError => e
390
+ log.warn "Skipped records larger than the buffer chunk limit size", :tag => tag, :error => e.to_s
383
391
  rescue BufferError
384
392
  raise ForShutdown if @consumer.nil?
385
393
 
@@ -198,6 +198,10 @@ DESC
198
198
  end
199
199
  end
200
200
 
201
+ if @share_producer && @buffer_config.flush_thread_count > 1
202
+ log.warn "share_producer sends one chunk at a time even with flush_thread_count #{@buffer_config.flush_thread_count}. Set share_producer false to send chunks in parallel"
203
+ end
204
+
201
205
  @producer_opts = {max_retries: @max_send_retries, required_acks: @required_acks, idempotent: @idempotent}
202
206
  @producer_opts[:ack_timeout] = @ack_timeout if @ack_timeout
203
207
  @producer_opts[:compression_codec] = @compression_codec.to_sym if @compression_codec
@@ -240,6 +244,7 @@ DESC
240
244
 
241
245
  if @share_producer
242
246
  @shared_producer = create_producer
247
+ @shared_producer_mutex = Mutex.new
243
248
  else
244
249
  @producers = {}
245
250
  @producers_mutex = Mutex.new
@@ -274,8 +279,10 @@ DESC
274
279
 
275
280
  def shutdown_producers
276
281
  if @share_producer
277
- @shared_producer.shutdown
278
- @shared_producer = nil
282
+ @shared_producer_mutex.synchronize {
283
+ @shared_producer.shutdown if @shared_producer
284
+ @shared_producer = nil
285
+ }
279
286
  else
280
287
  @producers_mutex.synchronize {
281
288
  shutdown_threads = @producers.map { |key, producer|
@@ -293,7 +300,7 @@ DESC
293
300
 
294
301
  def get_producer
295
302
  if @share_producer
296
- @shared_producer
303
+ @shared_producer ||= create_producer
297
304
  else
298
305
  @producers_mutex.synchronize {
299
306
  producer = @producers[Thread.current.object_id]
@@ -306,6 +313,17 @@ DESC
306
313
  end
307
314
  end
308
315
 
316
+ def discard_producer
317
+ if @share_producer
318
+ producer, @shared_producer = @shared_producer, nil
319
+ else
320
+ producer = @producers_mutex.synchronize { @producers.delete(Thread.current.object_id) }
321
+ end
322
+ producer.shutdown if producer
323
+ rescue => e
324
+ log.warn "failed to shut down the discarded producer:", :error => e.to_s, :error_class => e.class.to_s
325
+ end
326
+
309
327
  def setup_formatter(conf)
310
328
  type = conf['@type']
311
329
  case type
@@ -327,10 +345,20 @@ DESC
327
345
  end
328
346
  end
329
347
 
330
- # TODO: optimize write performance
331
348
  def write(chunk)
332
349
  @writing_threads_mutex.synchronize { @writing_threads.add(Thread.current) }
333
350
 
351
+ if @share_producer
352
+ @shared_producer_mutex.synchronize { write_chunk(chunk) }
353
+ else
354
+ write_chunk(chunk)
355
+ end
356
+ ensure
357
+ @writing_threads_mutex.synchronize { @writing_threads.delete(Thread.current) }
358
+ end
359
+
360
+ # TODO: optimize write performance
361
+ def write_chunk(chunk)
334
362
  tag = chunk.metadata.tag
335
363
  topic = if @topic
336
364
  extract_placeholders(@topic, chunk)
@@ -418,12 +446,11 @@ DESC
418
446
  log.warn "Exception ignored in tag : #{tag}" if ignore
419
447
  # For safety, refresh client and its producers
420
448
  refresh_client(false)
449
+ discard_producer
421
450
  # raise UnrecoverableError for backup ignored exception chunk
422
451
  raise Fluent::UnrecoverableError if ignore && exception_backup
423
452
  # Raise exception to retry sendind messages
424
453
  raise e unless ignore
425
- ensure
426
- @writing_threads_mutex.synchronize { @writing_threads.delete(Thread.current) }
427
454
  end
428
455
  end
429
456
  end
@@ -52,6 +52,18 @@ class KafkaInputTest < Test::Unit::TestCase
52
52
  end
53
53
  end
54
54
 
55
+ class OverflowRouter < FakeRouter
56
+ def initialize(overflow_tag)
57
+ super()
58
+ @overflow_tag = overflow_tag
59
+ end
60
+
61
+ def emit_stream(tag, es)
62
+ raise Fluent::KafkaInput::BufferChunkOverflowError, "too large" if tag == @overflow_tag
63
+ super
64
+ end
65
+ end
66
+
55
67
  def create_topic_watcher(messages, router, tag_source: :record, add_prefix: nil, add_suffix: nil)
56
68
  kafka = Object.new
57
69
  kafka.define_singleton_method(:fetch_messages) { |**args| messages }
@@ -159,6 +171,20 @@ class KafkaInputTest < Test::Unit::TestCase
159
171
  assert_equal([[TOPIC_NAME, ['record 1', 'record 2']]],
160
172
  router.emitted.map { |tag, records| [tag, records.map { |r| r['message'] }] })
161
173
  end
174
+
175
+ def test_consume_skips_buffer_chunk_overflow
176
+ messages = [
177
+ tagged_message('app.large', 'record 1', 0),
178
+ tagged_message('app.small', 'record 2', 1),
179
+ ]
180
+ router = OverflowRouter.new('app.large')
181
+ watcher = create_topic_watcher(messages, router)
182
+ watcher.consume
183
+
184
+ assert_equal([['app.small', ['record 2']]],
185
+ router.emitted.map { |tag, records| [tag, records.map { |r| r['message'] }] })
186
+ assert_equal(2, watcher.instance_variable_get(:@next_offset))
187
+ end
162
188
  end
163
189
 
164
190
  class ConsumeTest < self
@@ -36,6 +36,19 @@ class KafkaGroupInputTest < Test::Unit::TestCase
36
36
  assert_true d.instance.multi_workers_ready?
37
37
  end
38
38
 
39
+ def test_emit_events_with_buffer_chunk_overflow
40
+ d = create_driver
41
+ es = Fluent::MultiEventStream.new
42
+ router = Object.new
43
+ mock(router).emit_stream("test", es).once { raise Fluent::Plugin::Buffer::BufferChunkOverflowError, "too large" }
44
+ stub(d.instance).router { router }
45
+
46
+ assert_nothing_raised do
47
+ d.instance.emit_events("test", es)
48
+ end
49
+ assert_true d.logs.any? { |log| log.include?("Skipped records larger than the buffer chunk limit size") && log.include?("too large") }
50
+ end
51
+
39
52
  class ConsumeTest < self
40
53
  def setup
41
54
  @kafka = Kafka.new(["localhost:9092"], client_id: 'kafka')
@@ -144,6 +144,79 @@ class Kafka2OutputTest < Test::Unit::TestCase
144
144
  assert_equal [10], producer.produced.collect { |message| message[:partition] }
145
145
  end
146
146
 
147
+ data("per thread producer" => false,
148
+ "shared producer" => true)
149
+ def test_write_failure_replaces_producer(share_producer)
150
+ d = create_driver(base_config + config_element('ROOT', '', {"default_topic" => "kitagawakeiko",
151
+ "brokers" => "localhost:1",
152
+ "share_producer" => share_producer}))
153
+ d.instance_start
154
+ producer = d.instance.get_producer
155
+ mock.proxy(producer).shutdown
156
+
157
+ assert_raise(Kafka::ConnectionError) {
158
+ d.instance.write(create_chunk([{"a" => "b"}]))
159
+ }
160
+
161
+ assert_not_same producer, d.instance.get_producer
162
+ ensure
163
+ d.instance_shutdown if d
164
+ end
165
+
166
+ data("per thread producer" => false,
167
+ "shared producer" => true)
168
+ def test_write_failure_keeps_error_when_producer_shutdown_fails(share_producer)
169
+ d = create_driver(base_config + config_element('ROOT', '', {"default_topic" => "kitagawakeiko",
170
+ "brokers" => "localhost:1",
171
+ "share_producer" => share_producer}))
172
+ d.instance_start
173
+ producer = d.instance.get_producer
174
+ stub(producer).shutdown { raise IOError }
175
+
176
+ assert_raise(Kafka::ConnectionError) {
177
+ d.instance.write(create_chunk([{"a" => "b"}]))
178
+ }
179
+ ensure
180
+ d.instance_shutdown if d
181
+ end
182
+
183
+ class SlowProducer < DummyProducer
184
+ attr_reader :max_concurrent_deliveries
185
+
186
+ def initialize
187
+ @mutex = Mutex.new
188
+ @deliveries = 0
189
+ @max_concurrent_deliveries = 0
190
+ end
191
+
192
+ def deliver_messages
193
+ @mutex.synchronize {
194
+ @deliveries += 1
195
+ @max_concurrent_deliveries = [@max_concurrent_deliveries, @deliveries].max
196
+ }
197
+ sleep 0.1
198
+ @mutex.synchronize { @deliveries -= 1 }
199
+ end
200
+
201
+ def shutdown
202
+ end
203
+ end
204
+
205
+ def test_shared_producer_delivers_one_chunk_at_a_time
206
+ d = create_driver(config + config_element('ROOT', '', {"share_producer" => true}))
207
+ producer = SlowProducer.new
208
+ stub(d.instance).create_producer { producer }
209
+ d.instance_start
210
+
211
+ 2.times.map {
212
+ Thread.new { d.instance.write(create_chunk([{"a" => "b"}])) }
213
+ }.each(&:join)
214
+
215
+ assert_equal 1, producer.max_concurrent_deliveries
216
+ ensure
217
+ d.instance_shutdown if d
218
+ end
219
+
147
220
  data("crc32" => "crc32",
148
221
  "murmur2" => "murmur2")
149
222
  def test_partitioner_hash_function(data)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-kafka
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.19.8
4
+ version: 0.19.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hidemasa Togashi
@@ -218,7 +218,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
218
218
  - !ruby/object:Gem::Version
219
219
  version: '0'
220
220
  requirements: []
221
- rubygems_version: 4.0.18
221
+ rubygems_version: 4.0.20
222
222
  specification_version: 4
223
223
  summary: Fluentd plugin for Apache Kafka > 0.8
224
224
  test_files: