logstash-integration-rabbitmq 7.4.0-java → 7.4.1-java

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: f8b1b48521b0094de4590f515bf7a0e5b100c31437dfcfd6441054e663a862c3
4
- data.tar.gz: cface3e3fa3835787b3f9ca5d9e5447e65d8c2a0473445a41a7984beab07ac56
3
+ metadata.gz: 6307e3495481416481bb5e9caa66e1b6cfcae0c4b03dc4389f16c82820cc4043
4
+ data.tar.gz: d1bad3275ba3698ee2233449bd18eae7e9ce2973234a682e759d6e533a2201f8
5
5
  SHA512:
6
- metadata.gz: f94cf03c38e5a6668898cf90cb76f94a56b97c49a150c54a1ed058bd1e8ebd0c6df1061bf4629bb6c93347698a373285d3c28a5ec6acfa263656e44920ecd7ac
7
- data.tar.gz: e63a13e0164a381817198b8adf84b149fc278ce7b57b00ba7ed0dc7388b6dbff1a0b9fc5151e1cc488af19eb4ad3e2eb0593ee051157edc175678148f2027521
6
+ metadata.gz: 7410e75bc589079185f944952fdeecb10ad16bf2f7c68e4bd5071bf89715d22e65cb1d99d09b56677459e5837fdacc672e63040a5a953402a5baba20bb142c14
7
+ data.tar.gz: 2e02696d7c21c9ac2acdb834c462bc34833fc62af33f4babaf8031b10c6cf879cb8f5edaa97b4f133bd534d4d389f21e11c80bbb5f04d447429563326f15feff
data/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ ## 7.4.1
2
+ - Improve thread safety to avoid race conditions during shutdown and integration tests. [#67](https://github.com/logstash-plugins/logstash-integration-rabbitmq/pull/66)
3
+
1
4
  ## 7.4.0
2
5
  - Removed obsolete `verify_ssl` and `debug` options [#60](https://github.com/logstash-plugins/logstash-integration-rabbitmq/pull/60)
3
6
 
@@ -59,6 +59,7 @@ module LogStash
59
59
  class RabbitMQ < LogStash::Inputs::Threadable
60
60
 
61
61
  java_import java.util.concurrent.TimeUnit
62
+ java_import java.util.concurrent.CountDownLatch
62
63
 
63
64
  include ::LogStash::PluginMixins::RabbitMQConnection
64
65
 
@@ -189,6 +190,7 @@ module LogStash
189
190
  connect!
190
191
  declare_queue!
191
192
  bind_exchange!
193
+ @poison_latch = CountDownLatch.new(1)
192
194
  @hare_info.channel.prefetch = @prefetch_count
193
195
  rescue => e
194
196
  # when encountering an exception during shut-down,
@@ -268,7 +270,11 @@ module LogStash
268
270
  next
269
271
  end
270
272
 
271
- break if payload == INTERNAL_QUEUE_POISON
273
+ if payload == INTERNAL_QUEUE_POISON
274
+ @logger.info("RabbitMQ consumer thread received shutdown signal, exiting")
275
+ @poison_latch.countDown
276
+ break
277
+ end
272
278
 
273
279
  metadata, data = payload
274
280
  @codec.decode(data) do |event|
@@ -302,11 +308,22 @@ module LogStash
302
308
  end
303
309
 
304
310
  def stop
305
- @internal_queue.put(INTERNAL_QUEUE_POISON)
311
+ stop_consumer_thread
306
312
  shutdown_consumer
307
313
  close_connection
308
314
  end
309
315
 
316
+ def stop_consumer_thread
317
+ # After sending the poison pill, we need to wait for the consumer thread to actually exit.
318
+ # Closing channel right away may lead to messages not being acked properly or a race condition
319
+ # where the consumer thread tries to ack a message on a closed channel.
320
+ @internal_queue.put(INTERNAL_QUEUE_POISON)
321
+ timed_out = !@poison_latch.await(2, TimeUnit::SECONDS)
322
+ if timed_out
323
+ @logger.warn("Timeout waiting for RabbitMQ consumer thread to terminate")
324
+ end
325
+ end
326
+
310
327
  def shutdown_consumer
311
328
  # There are two possible flows to shutdown consumers. When the plugin is the one shutting down, it should send a channel
312
329
  # cancellation message by invoking channel.basic_cancel(consumer_tag) and waiting for the consumer to terminate
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'logstash-integration-rabbitmq'
3
- s.version = '7.4.0'
3
+ s.version = '7.4.1'
4
4
  s.licenses = ['Apache License (2.0)']
5
5
  s.summary = "Integration with RabbitMQ - input and output plugins"
6
6
  s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline "+
@@ -33,7 +33,6 @@ describe LogStash::Inputs::RabbitMQ do
33
33
  let(:connection) { double("MarchHare Connection") }
34
34
  let(:channel) { double("Channel") }
35
35
  let(:exchange) { double("Exchange") }
36
- let(:channel) { double("Channel") }
37
36
  let(:queue) { double("queue") }
38
37
 
39
38
  # Doing this in a before block doesn't give us enough control over scope
@@ -76,6 +75,12 @@ describe LogStash::Inputs::RabbitMQ do
76
75
  allow(consumer).to receive(:consumer_tag).and_return(consumer_tag)
77
76
  end
78
77
 
78
+ after do
79
+ # This unit tests don't initialize consumer thread, so sync signal should be manually sent.
80
+ instance.instance_variable_get(:@poison_latch).countDown
81
+ instance.stop
82
+ end
83
+
79
84
  context "with a cancelled consumer" do
80
85
  before do
81
86
  allow(consumer).to receive(:cancelled?).and_return(true)
@@ -84,7 +89,6 @@ describe LogStash::Inputs::RabbitMQ do
84
89
 
85
90
  it "should not call basic_cancel" do
86
91
  expect(channel).to_not receive(:basic_cancel)
87
- instance.stop
88
92
  end
89
93
  end
90
94
 
@@ -96,7 +100,6 @@ describe LogStash::Inputs::RabbitMQ do
96
100
 
97
101
  it "should not call basic_cancel" do
98
102
  expect(channel).to_not receive(:basic_cancel)
99
- instance.stop
100
103
  end
101
104
  end
102
105
 
@@ -108,13 +111,11 @@ describe LogStash::Inputs::RabbitMQ do
108
111
 
109
112
  it "should call basic_cancel" do
110
113
  expect(channel).to receive(:basic_cancel).with(consumer_tag)
111
- instance.stop
112
114
  end
113
115
 
114
116
  it "should log terminating info" do
115
117
  allow(channel).to receive(:basic_cancel).with(consumer_tag)
116
118
  expect(instance.logger).to receive(:info).with(/Waiting for RabbitMQ consumer to terminate before stopping/, anything)
117
- instance.stop
118
119
  end
119
120
  end
120
121
  end
@@ -384,24 +385,30 @@ describe "LogStash::Inputs::RabbitMQ with a live server", :integration => true d
384
385
  let(:hare_info) { instance.instance_variable_get(:@hare_info) }
385
386
  let(:output_queue) { Queue.new }
386
387
 
387
- # Spawn a connection in the bg and wait up (n) seconds
388
+ # Spawn a connection and waits for it to be ready.
388
389
  def spawn_and_wait(instance)
389
390
  instance.register
390
391
 
391
392
  output_queue # materialize in this thread
392
393
 
393
- Thread.new {
394
+ @consumer_thread = Thread.new {
394
395
  instance.run(output_queue)
395
396
  }
396
397
 
397
- 20.times do
398
- instance.send(:connection_open?) ? break : sleep(0.1)
398
+ # Ensure that the connection and channel are fully started.
399
+ Timeout.timeout(2, Timeout::Error, "Timeout waiting for connection to open and channel to be available") do
400
+ until instance.send(:connection_open?) && instance.instance_variable_get(:@hare_info)&.channel
401
+ sleep 0.05
402
+ end
399
403
  end
400
404
 
401
- # Extra time to make sure the consumer can attach
402
- # Without this there's a chance the shutdown code will execute
403
- # before consumption begins. This is tricky to do more elegantly
404
- sleep 4
405
+ # Ensure that the consumer is fully started.
406
+ hare_info = instance.instance_variable_get(:@hare_info)
407
+ Timeout.timeout(10, Timeout::Error, "Timeout waiting for channel to have consumers.") do
408
+ until hare_info.channel.consumers && !hare_info.channel.consumers.empty?
409
+ sleep 0.05
410
+ end
411
+ end
405
412
  end
406
413
 
407
414
  let(:test_connection) { MarchHare.connect(instance.send(:rabbitmq_settings)) }
@@ -418,6 +425,11 @@ describe "LogStash::Inputs::RabbitMQ with a live server", :integration => true d
418
425
 
419
426
  after do
420
427
  instance.stop()
428
+ # Stop the thread gracefully before tearing down connections.
429
+ @consumer_thread.join
430
+ # Exchange deletion needs to be placed after thread is finished,
431
+ # as the exchange may be dynamically created during the test context initialization.
432
+ exchange&.delete if defined?(exchange)
421
433
  test_channel.close
422
434
  test_connection.close
423
435
  end
@@ -440,10 +452,6 @@ describe "LogStash::Inputs::RabbitMQ with a live server", :integration => true d
440
452
  #let(:queue) { test_channel.queue(queue_name, :auto_delete => true) }
441
453
  let(:queue_name) { "logstash-input-rabbitmq-#{rand(0xFFFFFFFF)}" }
442
454
 
443
- after do
444
- exchange.delete
445
- end
446
-
447
455
  context "when the message has a payload but no message headers" do
448
456
  before do
449
457
  exchange.publish(message)
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-integration-rabbitmq
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.4.0
4
+ version: 7.4.1
5
5
  platform: java
6
6
  authors:
7
7
  - Elastic
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-09-16 00:00:00.000000000 Z
10
+ date: 2025-11-04 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
13
+ name: logstash-core-plugin-api
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
16
  - - ">="
@@ -19,7 +19,6 @@ dependencies:
19
19
  - - "<="
20
20
  - !ruby/object:Gem::Version
21
21
  version: '2.99'
22
- name: logstash-core-plugin-api
23
22
  type: :runtime
24
23
  prerelease: false
25
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -31,12 +30,12 @@ dependencies:
31
30
  - !ruby/object:Gem::Version
32
31
  version: '2.99'
33
32
  - !ruby/object:Gem::Dependency
33
+ name: logstash-core
34
34
  requirement: !ruby/object:Gem::Requirement
35
35
  requirements:
36
36
  - - ">="
37
37
  - !ruby/object:Gem::Version
38
38
  version: 6.5.0
39
- name: logstash-core
40
39
  type: :runtime
41
40
  prerelease: false
42
41
  version_requirements: !ruby/object:Gem::Requirement
@@ -45,12 +44,12 @@ dependencies:
45
44
  - !ruby/object:Gem::Version
46
45
  version: 6.5.0
47
46
  - !ruby/object:Gem::Dependency
47
+ name: logstash-codec-json
48
48
  requirement: !ruby/object:Gem::Requirement
49
49
  requirements:
50
50
  - - ">="
51
51
  - !ruby/object:Gem::Version
52
52
  version: '0'
53
- name: logstash-codec-json
54
53
  type: :runtime
55
54
  prerelease: false
56
55
  version_requirements: !ruby/object:Gem::Requirement
@@ -59,12 +58,12 @@ dependencies:
59
58
  - !ruby/object:Gem::Version
60
59
  version: '0'
61
60
  - !ruby/object:Gem::Dependency
61
+ name: march_hare
62
62
  requirement: !ruby/object:Gem::Requirement
63
63
  requirements:
64
64
  - - "~>"
65
65
  - !ruby/object:Gem::Version
66
66
  version: '4.0'
67
- name: march_hare
68
67
  type: :runtime
69
68
  prerelease: false
70
69
  version_requirements: !ruby/object:Gem::Requirement
@@ -73,12 +72,12 @@ dependencies:
73
72
  - !ruby/object:Gem::Version
74
73
  version: '4.0'
75
74
  - !ruby/object:Gem::Dependency
75
+ name: stud
76
76
  requirement: !ruby/object:Gem::Requirement
77
77
  requirements:
78
78
  - - "~>"
79
79
  - !ruby/object:Gem::Version
80
80
  version: 0.0.22
81
- name: stud
82
81
  type: :runtime
83
82
  prerelease: false
84
83
  version_requirements: !ruby/object:Gem::Requirement
@@ -87,12 +86,12 @@ dependencies:
87
86
  - !ruby/object:Gem::Version
88
87
  version: 0.0.22
89
88
  - !ruby/object:Gem::Dependency
89
+ name: back_pressure
90
90
  requirement: !ruby/object:Gem::Requirement
91
91
  requirements:
92
92
  - - "~>"
93
93
  - !ruby/object:Gem::Version
94
94
  version: '1.0'
95
- name: back_pressure
96
95
  type: :runtime
97
96
  prerelease: false
98
97
  version_requirements: !ruby/object:Gem::Requirement
@@ -101,12 +100,12 @@ dependencies:
101
100
  - !ruby/object:Gem::Version
102
101
  version: '1.0'
103
102
  - !ruby/object:Gem::Dependency
103
+ name: logstash-devutils
104
104
  requirement: !ruby/object:Gem::Requirement
105
105
  requirements:
106
106
  - - "~>"
107
107
  - !ruby/object:Gem::Version
108
108
  version: '2.0'
109
- name: logstash-devutils
110
109
  type: :development
111
110
  prerelease: false
112
111
  version_requirements: !ruby/object:Gem::Requirement
@@ -115,12 +114,12 @@ dependencies:
115
114
  - !ruby/object:Gem::Version
116
115
  version: '2.0'
117
116
  - !ruby/object:Gem::Dependency
117
+ name: logstash-input-generator
118
118
  requirement: !ruby/object:Gem::Requirement
119
119
  requirements:
120
120
  - - ">="
121
121
  - !ruby/object:Gem::Version
122
122
  version: '0'
123
- name: logstash-input-generator
124
123
  type: :development
125
124
  prerelease: false
126
125
  version_requirements: !ruby/object:Gem::Requirement
@@ -129,12 +128,12 @@ dependencies:
129
128
  - !ruby/object:Gem::Version
130
129
  version: '0'
131
130
  - !ruby/object:Gem::Dependency
131
+ name: logstash-codec-plain
132
132
  requirement: !ruby/object:Gem::Requirement
133
133
  requirements:
134
134
  - - ">="
135
135
  - !ruby/object:Gem::Version
136
136
  version: '0'
137
- name: logstash-codec-plain
138
137
  type: :development
139
138
  prerelease: false
140
139
  version_requirements: !ruby/object:Gem::Requirement
@@ -202,7 +201,6 @@ metadata:
202
201
  logstash_plugin: 'true'
203
202
  logstash_group: integration
204
203
  integration_plugins: logstash-input-rabbitmq,logstash-output-rabbitmq
205
- post_install_message:
206
204
  rdoc_options: []
207
205
  require_paths:
208
206
  - lib
@@ -217,8 +215,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
217
215
  - !ruby/object:Gem::Version
218
216
  version: '0'
219
217
  requirements: []
220
- rubygems_version: 3.3.26
221
- signing_key:
218
+ rubygems_version: 3.6.3
222
219
  specification_version: 4
223
220
  summary: Integration with RabbitMQ - input and output plugins
224
221
  test_files: