logstash-output-elasticsearch 11.14.1-java → 11.15.1-java

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c6405648edffa1a591049d280196f4e7d0e4812f029daf782346e76e202f40b0
4
- data.tar.gz: 2a8c1ae4c5cf07839aba4a7416305bae027b5461602c8518770f716683643e42
3
+ metadata.gz: 6ad6b80e4c6db7a54c8b7a830f6a922a7057b2e7585820db8a6c093b57db3800
4
+ data.tar.gz: 82cd893394fc7ac08ae7a79b2cd953f80fbf258ec2faad225300a2ce516ab208
5
5
  SHA512:
6
- metadata.gz: 9d5f80600ff713cd5429b7c1fe3dde0c665918e1ff1dbf69e8ca2e5a71a6c5e083031ad8951eeb561c0007e305bf2e808884ffdc97e9cd13aaa068df8cdd3e77
7
- data.tar.gz: 1f5e4fe699a2c7189d84215a5631a42b20f608600ad10a97e464e19429586b5949d0f1570d67535e748e2f2e24a43bd323418b4fa114b8896eb568894ee35f82
6
+ metadata.gz: b861d195377c3eeadf4489d21780be6627597aa0b93992bd7378b7cf680f6f99ac120ae342a4b48739ee6ef2652aa9238ee9fc08838cbed0f6dae0c887b06daa
7
+ data.tar.gz: 16091be406132dbb590ad76a0ba2e83cb0729717d8229b410bccbcabc660a7a26146fd2c7aaae6976f954bd17f1f763c6353552699ffd67173efb9917b913658
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 11.15.1
2
+ - Move async finish_register to bottom of register to avoid race condition [#1125](https://github.com/logstash-plugins/logstash-output-elasticsearch/pull/1125)
3
+
4
+ ## 11.15.0
5
+ - Added the ability to negatively acknowledge the batch under processing if the plugin is blocked in a retry-error-loop and a shutdown is requested. [#1119](https://github.com/logstash-plugins/logstash-output-elasticsearch/pull/1119)
6
+
1
7
  ## 11.14.1
2
8
  - [DOC] Fixed incorrect pull request link on the CHANGELOG `11.14.0` entry [#1122](https://github.com/logstash-plugins/logstash-output-elasticsearch/pull/1122)
3
9
 
@@ -313,6 +313,27 @@ class LogStash::Outputs::ElasticSearch < LogStash::Outputs::Base
313
313
  data_stream_enabled = data_stream_config?
314
314
 
315
315
  setup_template_manager_defaults(data_stream_enabled)
316
+ # To support BWC, we check if DLQ exists in core (< 5.4). If it doesn't, we use nil to resort to previous behavior.
317
+ @dlq_writer = dlq_enabled? ? execution_context.dlq_writer : nil
318
+
319
+ @dlq_codes = DOC_DLQ_CODES.to_set
320
+
321
+ if dlq_enabled?
322
+ check_dlq_custom_codes
323
+ @dlq_codes.merge(dlq_custom_codes)
324
+ else
325
+ raise LogStash::ConfigurationError, "DLQ feature (dlq_custom_codes) is configured while DLQ is not enabled" unless dlq_custom_codes.empty?
326
+ end
327
+
328
+ setup_mapper_and_target(data_stream_enabled)
329
+
330
+ @bulk_request_metrics = metric.namespace(:bulk_requests)
331
+ @document_level_metrics = metric.namespace(:documents)
332
+
333
+ if ecs_compatibility == :v8
334
+ @logger.warn("Elasticsearch Output configured with `ecs_compatibility => v8`, which resolved to an UNRELEASED preview of version 8.0.0 of the Elastic Common Schema. " +
335
+ "Once ECS v8 and an updated release of this plugin are publicly available, you will need to update this plugin to resolve this warning.")
336
+ end
316
337
 
317
338
  @after_successful_connection_thread = after_successful_connection do
318
339
  begin
@@ -326,18 +347,9 @@ class LogStash::Outputs::ElasticSearch < LogStash::Outputs::Base
326
347
  end
327
348
  end
328
349
 
329
- # To support BWC, we check if DLQ exists in core (< 5.4). If it doesn't, we use nil to resort to previous behavior.
330
- @dlq_writer = dlq_enabled? ? execution_context.dlq_writer : nil
331
-
332
- @dlq_codes = DOC_DLQ_CODES.to_set
333
-
334
- if dlq_enabled?
335
- check_dlq_custom_codes
336
- @dlq_codes.merge(dlq_custom_codes)
337
- else
338
- raise LogStash::ConfigurationError, "DLQ feature (dlq_custom_codes) is configured while DLQ is not enabled" unless dlq_custom_codes.empty?
339
- end
350
+ end
340
351
 
352
+ def setup_mapper_and_target(data_stream_enabled)
341
353
  if data_stream_enabled
342
354
  @event_mapper = -> (e) { data_stream_event_action_tuple(e) }
343
355
  @event_target = -> (e) { data_stream_name(e) }
@@ -346,14 +358,6 @@ class LogStash::Outputs::ElasticSearch < LogStash::Outputs::Base
346
358
  @event_mapper = -> (e) { event_action_tuple(e) }
347
359
  @event_target = -> (e) { e.sprintf(@index) }
348
360
  end
349
-
350
- @bulk_request_metrics = metric.namespace(:bulk_requests)
351
- @document_level_metrics = metric.namespace(:documents)
352
-
353
- if ecs_compatibility == :v8
354
- @logger.warn("Elasticsearch Output configured with `ecs_compatibility => v8`, which resolved to an UNRELEASED preview of version 8.0.0 of the Elastic Common Schema. " +
355
- "Once ECS v8 and an updated release of this plugin are publicly available, you will need to update this plugin to resolve this warning.")
356
- end
357
361
  end
358
362
 
359
363
  # @override post-register when ES connection established
@@ -431,7 +435,12 @@ class LogStash::Outputs::ElasticSearch < LogStash::Outputs::Base
431
435
  def wait_for_successful_connection
432
436
  after_successful_connection_done = @after_successful_connection_done
433
437
  return unless after_successful_connection_done
434
- stoppable_sleep 1 until after_successful_connection_done.true?
438
+ stoppable_sleep 1 until (after_successful_connection_done.true? || pipeline_shutdown_requested?)
439
+
440
+ if pipeline_shutdown_requested?
441
+ logger.info "Aborting the batch due to shutdown request while waiting for connections to become live"
442
+ abort_batch_if_available!
443
+ end
435
444
 
436
445
  status = @after_successful_connection_thread && @after_successful_connection_thread.value
437
446
  if status.is_a?(Exception) # check if thread 'halted' with an error
@@ -159,6 +159,8 @@ module LogStash; module PluginMixins; module ElasticSearch
159
159
  def after_successful_connection(&block)
160
160
  Thread.new do
161
161
  sleep_interval = @retry_initial_interval
162
+ # in case of a pipeline's shutdown_requested?, the method #close shutdown also this thread
163
+ # so no need to explicitly handle it here and return an AbortedBatchException.
162
164
  until successful_connection? || @stopping.true?
163
165
  @logger.debug("Waiting for connectivity to Elasticsearch cluster, retrying in #{sleep_interval}s")
164
166
  sleep_interval = sleep_for_interval(sleep_interval)
@@ -191,8 +193,14 @@ module LogStash; module PluginMixins; module ElasticSearch
191
193
  @logger.info("Retrying individual bulk actions that failed or were rejected by the previous bulk request", count: submit_actions.size)
192
194
  end
193
195
  rescue => e
194
- @logger.error("Encountered an unexpected error submitting a bulk request, will retry",
195
- message: e.message, exception: e.class, backtrace: e.backtrace)
196
+ if abort_batch_present? && e.instance_of?(org.logstash.execution.AbortedBatchException)
197
+ # if Logstash support abort of a batch and the batch is aborting,
198
+ # bubble up the exception so that the pipeline can handle it
199
+ raise e
200
+ else
201
+ @logger.error("Encountered an unexpected error submitting a bulk request, will retry",
202
+ message: e.message, exception: e.class, backtrace: e.backtrace)
203
+ end
196
204
  end
197
205
 
198
206
  # Everything was a success!
@@ -331,6 +339,11 @@ module LogStash; module PluginMixins; module ElasticSearch
331
339
 
332
340
  sleep_interval = sleep_for_interval(sleep_interval)
333
341
  @bulk_request_metrics.increment(:failures)
342
+ if pipeline_shutdown_requested?
343
+ # when any connection is available and a shutdown is requested
344
+ # the batch can be aborted, eventually for future retry.
345
+ abort_batch_if_available!
346
+ end
334
347
  retry unless @stopping.true?
335
348
  rescue ::LogStash::Outputs::ElasticSearch::HttpClient::Pool::BadResponseCodeError => e
336
349
  @bulk_request_metrics.increment(:failures)
@@ -349,6 +362,11 @@ module LogStash; module PluginMixins; module ElasticSearch
349
362
  end
350
363
 
351
364
  sleep_interval = sleep_for_interval(sleep_interval)
365
+ if pipeline_shutdown_requested?
366
+ # In case ES side changes access credentials and a pipeline reload is triggered
367
+ # this error becomes a retry on restart
368
+ abort_batch_if_available!
369
+ end
352
370
  retry
353
371
  rescue => e # Stuff that should never happen - print out full connection issues
354
372
  @logger.error(
@@ -363,6 +381,19 @@ module LogStash; module PluginMixins; module ElasticSearch
363
381
  end
364
382
  end
365
383
 
384
+ def pipeline_shutdown_requested?
385
+ return super if defined?(super) # since LS 8.1.0
386
+ execution_context&.pipeline&.shutdown_requested
387
+ end
388
+
389
+ def abort_batch_if_available!
390
+ raise org.logstash.execution.AbortedBatchException.new if abort_batch_present?
391
+ end
392
+
393
+ def abort_batch_present?
394
+ ::Gem::Version.create(LOGSTASH_VERSION) >= ::Gem::Version.create('8.8.0')
395
+ end
396
+
366
397
  def dlq_enabled?
367
398
  # TODO there should be a better way to query if DLQ is enabled
368
399
  # See more in: https://github.com/elastic/logstash/issues/8064
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'logstash-output-elasticsearch'
3
- s.version = '11.14.1'
3
+ s.version = '11.15.1'
4
4
  s.licenses = ['apache-2.0']
5
5
  s.summary = "Stores logs in Elasticsearch"
6
6
  s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
@@ -50,6 +50,104 @@ describe LogStash::Outputs::ElasticSearch do
50
50
  subject.close
51
51
  end
52
52
 
53
+ context "check aborting of a batch" do
54
+ context "on an unreachable ES instance" do
55
+ let(:events) { [ ::LogStash::Event.new("foo" => "bar1"), ::LogStash::Event.new("foo" => "bar2") ] }
56
+
57
+ let(:shutdown_value) { true }
58
+
59
+ let(:logger) { double("logger") }
60
+
61
+ before(:each) do
62
+ allow(subject).to receive(:logger).and_return(logger)
63
+ allow(logger).to receive(:info)
64
+
65
+ allow(subject).to receive(:pipeline_shutdown_requested?) do
66
+ shutdown_value
67
+ end
68
+ end
69
+
70
+ it "the #multi_receive abort while waiting on unreachable and a shutdown is requested" do
71
+ expect { subject.multi_receive(events) }.to raise_error(org.logstash.execution.AbortedBatchException)
72
+ expect(logger).to have_received(:info).with(/Aborting the batch due to shutdown request while waiting for connections to become live/i)
73
+ end
74
+ end
75
+
76
+ context "when a connected ES becomes unreachable" do
77
+ # let(:error) do
78
+ # ::LogStash::Outputs::ElasticSearch::HttpClient::Pool::BadResponseCodeError.new(
79
+ # 429, double("url").as_null_object, request_body, double("response body")
80
+ # )
81
+ # end
82
+
83
+ shared_examples 'raise an abort error' do
84
+ let(:options) {
85
+ {
86
+ "index" => "my-index",
87
+ "hosts" => ["localhost","localhost:9202"],
88
+ "path" => "some-path",
89
+ "manage_template" => false
90
+ }
91
+ }
92
+
93
+ let(:manticore_urls) { subject.client.pool.urls }
94
+ let(:manticore_url) { manticore_urls.first }
95
+
96
+ let(:stub_http_client_pool!) do
97
+ [:start_resurrectionist, :start_sniffer, :healthcheck!].each do |method|
98
+ allow_any_instance_of(LogStash::Outputs::ElasticSearch::HttpClient::Pool).to receive(method)
99
+ end
100
+ end
101
+
102
+ let(:event) { ::LogStash::Event.new("foo" => "bar") }
103
+
104
+ let(:logger) { double("logger").as_null_object }
105
+ let(:response) { { :errors => [], :items => [] } }
106
+
107
+ let(:request_body) { double(:request_body, :bytesize => 1023) }
108
+
109
+ before(:each) do
110
+ bulk_param = [["index", anything, event.to_hash]]
111
+
112
+ allow(subject).to receive(:logger).and_return(logger)
113
+
114
+ # fail consistently for ever
115
+ allow(subject.client).to receive(:bulk).with(bulk_param).and_raise(error)
116
+ end
117
+
118
+ it "should exit the retry with an abort exception if shutdown is requested" do
119
+ # execute in another thread because it blocks in a retry loop until the shutdown is triggered
120
+ th = Thread.new do
121
+ subject.multi_receive([event])
122
+ rescue org.logstash.execution.AbortedBatchException => e
123
+ # return exception's class so that it can be verified when retrieving the thread's value
124
+ e.class
125
+ end
126
+
127
+ # trigger the shutdown signal
128
+ allow(subject).to receive(:pipeline_shutdown_requested?) { true }
129
+
130
+ expect(th.value).to eq(org.logstash.execution.AbortedBatchException)
131
+ end
132
+ end
133
+
134
+ context "with 429 error" do
135
+ let(:error) do
136
+ ::LogStash::Outputs::ElasticSearch::HttpClient::Pool::BadResponseCodeError.new(
137
+ 429, double("url").as_null_object, request_body, double("response body")
138
+ )
139
+ end
140
+
141
+ it_behaves_like 'raise an abort error'
142
+ end
143
+
144
+ context "with 'no connections' error" do
145
+ let(:error) { ::LogStash::Outputs::ElasticSearch::HttpClient::Pool::NoConnectionAvailableError.new }
146
+
147
+ it_behaves_like 'raise an abort error'
148
+ end
149
+ end
150
+ end if LOGSTASH_VERSION >= '8.8'
53
151
 
54
152
  context "with an active instance" do
55
153
  let(:options) {
@@ -1358,6 +1456,42 @@ describe LogStash::Outputs::ElasticSearch do
1358
1456
  end
1359
1457
 
1360
1458
  end
1459
+
1460
+ context 'during register/finish_register' do
1461
+
1462
+ let(:options) { { 'hosts' => '127.0.0.1:9999', 'data_stream' => 'true' } }
1463
+ let(:es_version) { '8.7.0' } # DS default on LS 8.x
1464
+
1465
+ before do
1466
+ allow(subject).to receive(:discover_cluster_uuid)
1467
+ allow(subject).to receive(:maybe_create_rollover_alias)
1468
+ allow(subject).to receive(:maybe_create_ilm_policy)
1469
+ allow(subject).to receive(:build_client)
1470
+ end
1471
+
1472
+ # this test could not have been done using latches as the fix to the race
1473
+ # condition alters the order of the instructions in elasticsearch.rb
1474
+ #
1475
+ # the race condition happened when the @index was set to the datastream
1476
+ # after `ilm_in_use?` is called but before `setup_ilm`
1477
+ it 'doesn\'t have a race condition leading to resetting back to ILM' do
1478
+ ilm_in_use = subject.method(:ilm_in_use?)
1479
+ expect(subject).to receive(:ilm_in_use?) do |params|
1480
+ ret = ilm_in_use.call
1481
+ sleep 3
1482
+ ret
1483
+ end
1484
+ setup_mapper_and_target = subject.method(:setup_mapper_and_target)
1485
+ expect(subject).to receive(:setup_mapper_and_target).once do |params|
1486
+ sleep 1
1487
+ setup_mapper_and_target.call(params)
1488
+ end
1489
+ subject.register
1490
+ sleep 6
1491
+ expect(subject.index).to eq("logs-generic-default")
1492
+ end
1493
+
1494
+ end
1361
1495
  end
1362
1496
 
1363
1497
  @private
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-output-elasticsearch
3
3
  version: !ruby/object:Gem::Version
4
- version: 11.14.1
4
+ version: 11.15.1
5
5
  platform: java
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-03-22 00:00:00.000000000 Z
11
+ date: 2023-04-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -356,7 +356,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
356
356
  - !ruby/object:Gem::Version
357
357
  version: '0'
358
358
  requirements: []
359
- rubygems_version: 3.1.6
359
+ rubygems_version: 3.2.29
360
360
  signing_key:
361
361
  specification_version: 4
362
362
  summary: Stores logs in Elasticsearch