Skip to content

Commit 95a0bcd

Browse files
estolfojsvd
andauthored
Add drop_error_types config option to not retry after certain error types (#1228)
* Add drop_error_types option to not retry after certain error types * Add code example to docs * Use variable for error type in specs * Update lib/logstash/plugin_mixins/elasticsearch/api_configs.rb Co-authored-by: João Duarte <[email protected]> * Update docs/index.asciidoc Co-authored-by: João Duarte <[email protected]> * Update docs/index.asciidoc Co-authored-by: João Duarte <[email protected]> * Update lib/logstash/plugin_mixins/elasticsearch/common.rb Co-authored-by: João Duarte <[email protected]> * Add drop_error_types to the table of options * Refactor specs after code review * Test more specific code paths * Update CHANGELOG and bump version in gemspec * Update CHANGELOG.md Co-authored-by: João Duarte <[email protected]> * Update logstash-output-elasticsearch.gemspec Co-authored-by: João Duarte <[email protected]> --------- Co-authored-by: João Duarte <[email protected]>
1 parent efb4137 commit 95a0bcd

File tree

6 files changed

+105
-3
lines changed

6 files changed

+105
-3
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 12.1.0
2+
- Add drop_error_types config option to not retry after certain error types [#1228](https://github.com/logstash-plugins/logstash-output-elasticsearch/pull/1228)
3+
14
## 12.0.7
25
- Support both, encoded and non encoded api-key formats on plugin configuration [#1223](https://github.com/logstash-plugins/logstash-output-elasticsearch/pull/1223)
36

docs/index.asciidoc

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,7 @@ Please check out <<plugins-{type}s-{plugin}-obsolete-options>> for details.
373373
| <<plugins-{type}s-{plugin}-doc_as_upsert>> |<<boolean,boolean>>|No
374374
| <<plugins-{type}s-{plugin}-document_id>> |<<string,string>>|No
375375
| <<plugins-{type}s-{plugin}-document_type>> |<<string,string>>|No
376+
| <<plugins-{type}s-{plugin}-drop_error_types>> |<<array,array>>|No
376377
| <<plugins-{type}s-{plugin}-ecs_compatibility>> | <<string,string>>|No
377378
| <<plugins-{type}s-{plugin}-failure_type_logging_whitelist>> |<<array,array>>|No
378379
| <<plugins-{type}s-{plugin}-healthcheck_path>> |<<string,string>>|No
@@ -644,6 +645,24 @@ If you don't set a value for this option:
644645
- for elasticsearch clusters 8.x: no value will be used;
645646
- for elasticsearch clusters 7.x: the value of '_doc' will be used.
646647

648+
[id="plugins-{type}s-{plugin}-drop_error_types"]
649+
===== `drop_error_types`
650+
651+
* Value type is <<array,array>>
652+
* Default value is `[]`
653+
654+
Lists the set of error types for which individual bulk request actions will not be retried. Unless an individual - document level - action returns 409 or an error from this list, failures will be retried indefinitely.
655+
A warning message will be logged indicating that the action failed, unless the error type is
656+
listed in the <<plugins-{type}s-{plugin}-silence_errors_in_log>> config option.
657+
Note that the events are not added to the Dead Letter Queue (DLQ), regardless of whether it is enabled.
658+
659+
[source,ruby]
660+
output {
661+
elasticsearch {
662+
drop_error_types => ["index_closed_exception"]
663+
}
664+
}
665+
647666
[id="plugins-{type}s-{plugin}-ecs_compatibility"]
648667
===== `ecs_compatibility`
649668

lib/logstash/plugin_mixins/elasticsearch/api_configs.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,10 @@ module APIConfigs
204204
# if enabled, failed index name interpolation events go into dead letter queue.
205205
:dlq_on_failed_indexname_interpolation => { :validate => :boolean, :default => true },
206206

207+
# Failures on actions from a bulk request will not be retried for these error types; the events will be dropped.
208+
# The events won't be added to the DLQ either.
209+
:drop_error_types => { :validate => :string, :list => true, :default => [] },
210+
207211
# Obsolete Settings
208212
:ssl => { :obsolete => "Set 'ssl_enabled' instead." },
209213
:ssl_certificate_verification => { :obsolete => "Set 'ssl_verification_mode' instead." },

lib/logstash/plugin_mixins/elasticsearch/common.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,16 +278,18 @@ def submit(actions)
278278

279279
status = action_props["status"]
280280
error = action_props["error"]
281+
type = error["type"] if error
281282
action = actions[idx]
282283

283-
# Retry logic: If it is success, we move on. If it is a failure, we have 3 paths:
284+
# Retry logic: If it is success, we move on. If it is a failure, we have the following paths:
284285
# - For 409, we log and drop. there is nothing we can do
286+
# - For any error types set in the 'drop_error_types' config, log and drop.
285287
# - For a mapping error, we send to dead letter queue for a human to intervene at a later point.
286288
# - For everything else there's mastercard. Yep, and we retry indefinitely. This should fix #572 and other transient network issues
287289
if DOC_SUCCESS_CODES.include?(status)
288290
@document_level_metrics.increment(:successes)
289291
next
290-
elsif DOC_CONFLICT_CODE == status
292+
elsif DOC_CONFLICT_CODE == status || @drop_error_types.include?(type)
291293
@document_level_metrics.increment(:non_retryable_failures)
292294
@logger.warn "Failed action", status: status, action: action, response: response if log_failure_type?(error)
293295
next

logstash-output-elasticsearch.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Gem::Specification.new do |s|
22
s.name = 'logstash-output-elasticsearch'
3-
s.version = '12.0.7'
3+
s.version = '12.1.0'
44
s.licenses = ['apache-2.0']
55
s.summary = "Stores logs in Elasticsearch"
66
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"

spec/unit/outputs/elasticsearch_spec.rb

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1499,6 +1499,80 @@
14991499
end
15001500
end
15011501

1502+
describe 'drop_error_types' do
1503+
1504+
let(:error_type) { 'index_closed_exception' }
1505+
1506+
let(:options) { super().merge('drop_error_types' => [error_type]) }
1507+
1508+
let(:events) { [ LogStash::Event.new("foo" => "bar") ] }
1509+
1510+
let(:dlq_writer) { subject.instance_variable_get(:@dlq_writer) }
1511+
1512+
let(:error_code) { 403 }
1513+
1514+
let(:event_action_tuples) { subject.map_events(events) }
1515+
1516+
let(:bulk_response) do
1517+
{
1518+
"took"=>1, "ingest_took"=>11, "errors"=>true, "items"=>
1519+
[{
1520+
"index"=>{"_index"=>"bar", "_type"=>"_doc", "_id"=>'bar', "status" => error_code,
1521+
"error"=>{"type" => error_type, "reason" => "TEST" }
1522+
}
1523+
}]
1524+
}
1525+
end
1526+
1527+
before(:each) do
1528+
allow(subject.client).to receive(:bulk_send).and_return(bulk_response)
1529+
end
1530+
1531+
context 'DLQ is enabled' do
1532+
1533+
let(:options) { super().merge("dlq_custom_codes" => [403]) }
1534+
1535+
it 'does not write the event to the DLQ' do
1536+
expect(dlq_writer).not_to receive(:write)
1537+
subject.send(:submit, event_action_tuples)
1538+
end
1539+
end
1540+
1541+
context 'DLQ is not enabled' do
1542+
1543+
before(:each) do
1544+
allow(subject).to receive(:dlq_enabled?).and_return(false)
1545+
end
1546+
1547+
it 'does not retry indexing the event' do
1548+
expect(subject).to receive(:submit).with(event_action_tuples).once.and_call_original
1549+
subject.send(:retrying_submit, event_action_tuples)
1550+
end
1551+
end
1552+
1553+
context 'the error type is not in `silence_errors_in_log`' do
1554+
1555+
it 'logs the error' do
1556+
expect(subject.logger).to receive(:warn).with(a_string_including("Failed action"), anything)
1557+
subject.send(:submit, event_action_tuples)
1558+
end
1559+
end
1560+
1561+
context 'the error type is in `silence_errors_in_log`' do
1562+
1563+
let(:options) { super().merge('silence_errors_in_log' => [error_type]) }
1564+
1565+
before(:each) do
1566+
# ensure that neither warn nor info is called on the logger by using a test double
1567+
subject.instance_variable_set("@logger", double('logger'))
1568+
end
1569+
1570+
it 'does not log the error' do
1571+
subject.send(:submit, event_action_tuples)
1572+
end
1573+
end
1574+
end
1575+
15021576
describe "custom headers" do
15031577
let(:manticore_options) { subject.client.pool.adapter.manticore.instance_variable_get(:@options) }
15041578

0 commit comments

Comments
 (0)