logstash-integration-jdbc 5.2.2 → 5.2.5
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 +4 -4
- data/CHANGELOG.md +9 -0
- data/Gemfile +2 -0
- data/lib/logstash/filters/jdbc/loader_schedule.rb +11 -45
- data/lib/logstash/filters/jdbc/repeating_load_runner.rb +0 -2
- data/lib/logstash/filters/jdbc/single_load_runner.rb +0 -4
- data/lib/logstash/filters/jdbc_static.rb +7 -8
- data/lib/logstash/inputs/jdbc.rb +6 -11
- data/lib/logstash/plugin_mixins/jdbc/jdbc.rb +0 -1
- data/lib/logstash/plugin_mixins/jdbc/scheduler.rb +66 -2
- data/lib/logstash/plugin_mixins/jdbc/statement_handler.rb +51 -45
- data/lib/logstash/plugin_mixins/jdbc/value_tracking.rb +0 -3
- data/logstash-integration-jdbc.gemspec +4 -2
- data/spec/filters/jdbc/repeating_load_runner_spec.rb +1 -1
- data/spec/inputs/integration/integ_spec.rb +25 -0
- data/spec/inputs/jdbc_spec.rb +0 -48
- data/spec/plugin_mixins/jdbc/scheduler_spec.rb +26 -0
- metadata +6 -7
- data/lib/logstash/plugin_mixins/jdbc/checked_count_logger.rb +0 -43
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b74796568bc1df12ae5015cfcf38aeb43971b3bd3eda2dcc668104decc69b6a2
|
4
|
+
data.tar.gz: ae3a1f82f7354798692933a2426977ea2e17f7335a74d02eb09a376e4421af6b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d198c08e74dc4702dae5de1ca4661d68d10298ff497e44739e7cc775d575bfc9fe825154a9de6f2497e60fb3c694f06d36d8971ecf9935e13592c959d6c13c53
|
7
|
+
data.tar.gz: ccd725393201ec5282644f003880fa0449a027942442698fe6e1758f25335ff585efb73d0f52716c9698eb19990ef67a15b21fef90255473b39683339520796a
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
## 5.2.5
|
2
|
+
- Fix: do not execute more queries with debug logging [#109](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/109)
|
3
|
+
|
4
|
+
## 5.2.4
|
5
|
+
- Fix: compatibility with all (>= 3.0) rufus-scheduler versions [#97](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/97)
|
6
|
+
|
7
|
+
## 5.2.3
|
8
|
+
- Performance: avoid contention on scheduler execution [#103](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/103)
|
9
|
+
|
1
10
|
## 5.2.2
|
2
11
|
- Feat: name scheduler threads + redirect error logging [#102](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/102)
|
3
12
|
|
data/Gemfile
CHANGED
@@ -9,3 +9,5 @@ if Dir.exist?(logstash_path) && use_logstash_source
|
|
9
9
|
gem 'logstash-core', :path => "#{logstash_path}/logstash-core"
|
10
10
|
gem 'logstash-core-plugin-api', :path => "#{logstash_path}/logstash-core-plugin-api"
|
11
11
|
end
|
12
|
+
|
13
|
+
gem 'rufus-scheduler', ENV['RUFUS_SCHEDULER_VERSION'] if ENV['RUFUS_SCHEDULER_VERSION']
|
@@ -4,60 +4,26 @@ require "rufus/scheduler"
|
|
4
4
|
|
5
5
|
module LogStash module Filters module Jdbc
|
6
6
|
class LoaderSchedule < Validatable
|
7
|
-
attr_reader :
|
8
|
-
|
9
|
-
def to_log_string
|
10
|
-
message = ""
|
11
|
-
message.concat "these months in the year [#{@cronline.months.to_a.join(", ")}];" unless @cronline.months.nil?
|
12
|
-
message.concat "these days in the month [#{@cronline.days.to_a.join(", ")}];" unless @cronline.days.nil?
|
13
|
-
message.concat "these hours in the day [#{@cronline.hours.to_a.join(", ")}];" unless @cronline.hours.nil?
|
14
|
-
message.concat "these minutes in the hour [#{@cronline.minutes.to_a.join(", ")}];" unless @cronline.minutes.nil?
|
15
|
-
message.concat "these seconds in the minute [#{@cronline.seconds.to_a.join(", ")}]" unless @cronline.seconds.nil?
|
16
|
-
if !message.empty?
|
17
|
-
message.prepend "Scheduled for: "
|
18
|
-
end
|
19
|
-
message
|
20
|
-
end
|
7
|
+
attr_reader :loader_schedule
|
21
8
|
|
22
9
|
private
|
23
10
|
|
24
|
-
|
25
|
-
if valid?
|
26
|
-
# From the Rufus::Scheduler docs:
|
27
|
-
# By default, rufus-scheduler sleeps 0.300 second between every step.
|
28
|
-
# At each step it checks for jobs to trigger and so on.
|
29
|
-
# set the frequency to 2.5 seconds if we are not reloading in the seconds timeframe
|
30
|
-
# rufus scheduler thread should respond to stop quickly enough.
|
31
|
-
if only_seconds_set?
|
32
|
-
@schedule_frequency = 0.3
|
33
|
-
else
|
34
|
-
@schedule_frequency = 2.5
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
|
40
|
-
def only_seconds_set?
|
41
|
-
@cronline.seconds &&
|
42
|
-
@cronline.minutes.nil? &&
|
43
|
-
@cronline.hours.nil? &&
|
44
|
-
@cronline.days.nil? &&
|
45
|
-
@cronline.months.nil?
|
46
|
-
end
|
47
|
-
|
11
|
+
# @overload
|
48
12
|
def parse_options
|
49
13
|
@loader_schedule = @options
|
50
14
|
|
51
|
-
|
15
|
+
if @loader_schedule.is_a?(String)
|
16
|
+
begin
|
17
|
+
# Rufus::Scheduler 3.0 - 3.6 methods signature: parse_cron(o, opts)
|
18
|
+
# since Rufus::Scheduler 3.7 methods signature: parse_cron(o, opts={})
|
19
|
+
@cronline = Rufus::Scheduler.parse_cron(@loader_schedule, {})
|
20
|
+
rescue => e
|
21
|
+
@option_errors << "The loader_schedule option is invalid: #{e.message}"
|
22
|
+
end
|
23
|
+
else
|
52
24
|
@option_errors << "The loader_schedule option must be a string"
|
53
25
|
end
|
54
26
|
|
55
|
-
begin
|
56
|
-
@cronline = Rufus::Scheduler::CronLine.new(@loader_schedule)
|
57
|
-
rescue => e
|
58
|
-
@option_errors << "The loader_schedule option is invalid: #{e.message}"
|
59
|
-
end
|
60
|
-
|
61
27
|
@valid = @option_errors.empty?
|
62
28
|
end
|
63
29
|
end
|
@@ -3,6 +3,7 @@ require "logstash-integration-jdbc_jars"
|
|
3
3
|
require "logstash/filters/base"
|
4
4
|
require "logstash/namespace"
|
5
5
|
require "logstash/plugin_mixins/ecs_compatibility_support"
|
6
|
+
require "logstash/plugin_mixins/jdbc/scheduler"
|
6
7
|
require_relative "jdbc/loader"
|
7
8
|
require_relative "jdbc/loader_schedule"
|
8
9
|
require_relative "jdbc/repeating_load_runner"
|
@@ -191,17 +192,15 @@ module LogStash module Filters class JdbcStatic < LogStash::Filters::Base
|
|
191
192
|
@processor = Jdbc::LookupProcessor.new(@local_lookups, global_lookup_options)
|
192
193
|
runner_args.unshift(@processor.local)
|
193
194
|
if @loader_schedule
|
194
|
-
args = []
|
195
195
|
@loader_runner = Jdbc::RepeatingLoadRunner.new(*runner_args)
|
196
196
|
@loader_runner.initial_load
|
197
|
-
|
198
|
-
|
199
|
-
|
197
|
+
@scheduler = LogStash::PluginMixins::Jdbc::Scheduler.
|
198
|
+
start_cron_scheduler(@loader_schedule, thread_name: "[#{id}]-jdbc_static__scheduler") { @loader_runner.repeated_load }
|
199
|
+
cron_job = @scheduler.cron_jobs.first
|
200
|
+
if cron_job
|
201
|
+
frequency = cron_job.respond_to?(:rough_frequency) ? cron_job.rough_frequency : cron_job.frequency
|
202
|
+
logger.info("Loaders will execute every #{frequency} seconds", loader_schedule: @loader_schedule)
|
200
203
|
end
|
201
|
-
logger.info("Scheduler scan for work frequency is: #{cronline.schedule_frequency}")
|
202
|
-
rufus_args = {:max_work_threads => 1, :frequency => cronline.schedule_frequency}
|
203
|
-
@scheduler = Rufus::Scheduler.new(rufus_args)
|
204
|
-
@scheduler.cron(cronline.loader_schedule, @loader_runner)
|
205
204
|
else
|
206
205
|
@loader_runner = Jdbc::SingleLoadRunner.new(*runner_args)
|
207
206
|
@loader_runner.initial_load
|
data/lib/logstash/inputs/jdbc.rb
CHANGED
@@ -259,8 +259,8 @@ module LogStash module Inputs class Jdbc < LogStash::Inputs::Base
|
|
259
259
|
end
|
260
260
|
end
|
261
261
|
|
262
|
-
set_value_tracker
|
263
|
-
|
262
|
+
set_value_tracker LogStash::PluginMixins::Jdbc::ValueTracking.build_last_value_tracker(self)
|
263
|
+
set_statement_handler LogStash::PluginMixins::Jdbc::StatementHandler.build_statement_handler(self)
|
264
264
|
|
265
265
|
@enable_encoding = !@charset.nil? || !@columns_charset.empty?
|
266
266
|
|
@@ -283,8 +283,8 @@ module LogStash module Inputs class Jdbc < LogStash::Inputs::Base
|
|
283
283
|
end # def register
|
284
284
|
|
285
285
|
# test injection points
|
286
|
-
def
|
287
|
-
@statement_handler =
|
286
|
+
def set_statement_handler(handler)
|
287
|
+
@statement_handler = handler
|
288
288
|
end
|
289
289
|
|
290
290
|
def set_value_tracker(instance)
|
@@ -295,13 +295,8 @@ module LogStash module Inputs class Jdbc < LogStash::Inputs::Base
|
|
295
295
|
load_driver
|
296
296
|
if @schedule
|
297
297
|
# input thread (Java) name example "[my-oracle]<jdbc"
|
298
|
-
@scheduler = LogStash::PluginMixins::Jdbc::Scheduler.
|
299
|
-
|
300
|
-
)
|
301
|
-
@scheduler.schedule_cron @schedule do
|
302
|
-
execute_query(queue)
|
303
|
-
end
|
304
|
-
|
298
|
+
@scheduler = LogStash::PluginMixins::Jdbc::Scheduler.
|
299
|
+
start_cron_scheduler(@schedule, thread_name: "[#{id}]<jdbc__scheduler") { execute_query(queue) }
|
305
300
|
@scheduler.join
|
306
301
|
else
|
307
302
|
execute_query(queue)
|
@@ -12,6 +12,70 @@ module LogStash module PluginMixins module Jdbc
|
|
12
12
|
TimeImpl = defined?(Rufus::Scheduler::EoTime) ? Rufus::Scheduler::EoTime :
|
13
13
|
(defined?(Rufus::Scheduler::ZoTime) ? Rufus::Scheduler::ZoTime : ::Time)
|
14
14
|
|
15
|
+
# @param cron [String] cron-line
|
16
|
+
# @param opts [Hash] scheduler options
|
17
|
+
# @return scheduler instance
|
18
|
+
def self.start_cron_scheduler(cron, opts = {}, &block)
|
19
|
+
unless block_given?
|
20
|
+
raise ArgumentError, 'missing (cron scheduler) block - worker task to execute'
|
21
|
+
end
|
22
|
+
scheduler = new_scheduler(opts)
|
23
|
+
scheduler.schedule_cron(cron, &block)
|
24
|
+
scheduler
|
25
|
+
end
|
26
|
+
|
27
|
+
# @param opts [Hash] scheduler options
|
28
|
+
# @return scheduler instance
|
29
|
+
def self.new_scheduler(opts)
|
30
|
+
unless opts.key?(:thread_name)
|
31
|
+
raise ArgumentError, 'thread_name: option is required to be able to distinguish multiple scheduler threads'
|
32
|
+
end
|
33
|
+
opts[:max_work_threads] ||= 1
|
34
|
+
# amount the scheduler thread sleeps between checking whether jobs
|
35
|
+
# should trigger, default is 0.3 which is a bit too often ...
|
36
|
+
# in theory the cron expression '* * * * * *' supports running jobs
|
37
|
+
# every second but this is very rare, we could potentially go higher
|
38
|
+
opts[:frequency] ||= 1.0
|
39
|
+
|
40
|
+
new(opts)
|
41
|
+
end
|
42
|
+
|
43
|
+
# @overload
|
44
|
+
def timeout_jobs
|
45
|
+
# Rufus relies on `Thread.list` which is a blocking operation and with many schedulers
|
46
|
+
# (and threads) within LS will have a negative impact on performance as scheduler
|
47
|
+
# threads will end up waiting to obtain the `Thread.list` lock.
|
48
|
+
#
|
49
|
+
# However, this isn't necessary we can easily detect whether there are any jobs
|
50
|
+
# that might need to timeout: only when `@opts[:timeout]` is set causes worker thread(s)
|
51
|
+
# to have a `Thread.current[:rufus_scheduler_timeout]` that is not nil
|
52
|
+
return unless @opts[:timeout]
|
53
|
+
super
|
54
|
+
end
|
55
|
+
|
56
|
+
# @overload
|
57
|
+
def work_threads(query = :all)
|
58
|
+
if query == :__all_no_cache__ # special case from JobDecorator#start_work_thread
|
59
|
+
@_work_threads = nil # when a new worker thread is being added reset
|
60
|
+
return super(:all)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Gets executed every time a job is triggered, we're going to cache the
|
64
|
+
# worker threads for this scheduler (to avoid `Thread.list`) - they only
|
65
|
+
# change when a new thread is being started from #start_work_thread ...
|
66
|
+
work_threads = @_work_threads
|
67
|
+
if work_threads.nil?
|
68
|
+
work_threads = threads.select { |t| t[:rufus_scheduler_work_thread] }
|
69
|
+
@_work_threads = work_threads
|
70
|
+
end
|
71
|
+
|
72
|
+
case query
|
73
|
+
when :active then work_threads.select { |t| t[:rufus_scheduler_job] }
|
74
|
+
when :vacant then work_threads.reject { |t| t[:rufus_scheduler_job] }
|
75
|
+
else work_threads
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
15
79
|
# @overload
|
16
80
|
def on_error(job, err)
|
17
81
|
details = { exception: err.class, message: err.message, backtrace: err.backtrace }
|
@@ -76,10 +140,10 @@ module LogStash module PluginMixins module Jdbc
|
|
76
140
|
|
77
141
|
ret = super() # does not return Thread instance in 3.0
|
78
142
|
|
79
|
-
work_threads = @scheduler.work_threads
|
143
|
+
work_threads = @scheduler.work_threads(:__all_no_cache__)
|
80
144
|
while prev_thread_count == work_threads.size # very unlikely
|
81
145
|
Thread.pass
|
82
|
-
work_threads = @scheduler.work_threads
|
146
|
+
work_threads = @scheduler.work_threads(:__all_no_cache__)
|
83
147
|
end
|
84
148
|
|
85
149
|
work_thread_name_prefix = @scheduler.work_thread_name_prefix
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module LogStash module PluginMixins module Jdbc
|
4
4
|
class StatementHandler
|
5
|
-
def self.build_statement_handler(plugin
|
5
|
+
def self.build_statement_handler(plugin)
|
6
6
|
if plugin.use_prepared_statements
|
7
7
|
klass = PreparedStatementHandler
|
8
8
|
else
|
@@ -16,27 +16,39 @@ module LogStash module PluginMixins module Jdbc
|
|
16
16
|
klass = NormalStatementHandler
|
17
17
|
end
|
18
18
|
end
|
19
|
-
klass.new(plugin
|
19
|
+
klass.new(plugin)
|
20
20
|
end
|
21
21
|
|
22
|
-
attr_reader :statement, :parameters
|
22
|
+
attr_reader :statement, :parameters
|
23
23
|
|
24
|
-
def initialize(plugin
|
24
|
+
def initialize(plugin)
|
25
25
|
@statement = plugin.statement
|
26
|
-
@statement_logger = statement_logger
|
27
|
-
post_init(plugin)
|
28
26
|
end
|
29
27
|
|
30
28
|
def build_query(db, sql_last_value)
|
31
|
-
# override in subclass
|
29
|
+
fail NotImplementedError # override in subclass
|
32
30
|
end
|
33
31
|
|
34
|
-
def post_init(plugin)
|
35
|
-
# override in subclass, if needed
|
36
|
-
end
|
37
32
|
end
|
38
33
|
|
39
34
|
class NormalStatementHandler < StatementHandler
|
35
|
+
|
36
|
+
attr_reader :parameters
|
37
|
+
|
38
|
+
def initialize(plugin)
|
39
|
+
super(plugin)
|
40
|
+
@parameter_keys = ["sql_last_value"] + plugin.parameters.keys
|
41
|
+
@parameters = plugin.parameters.inject({}) do |hash,(k,v)|
|
42
|
+
case v
|
43
|
+
when LogStash::Timestamp
|
44
|
+
hash[k.to_sym] = v.time
|
45
|
+
else
|
46
|
+
hash[k.to_sym] = v
|
47
|
+
end
|
48
|
+
hash
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
40
52
|
# Performs the query, yielding once per row of data
|
41
53
|
# @param db [Sequel::Database]
|
42
54
|
# @param sql_last_value [Integer|DateTime|Time]
|
@@ -52,27 +64,18 @@ module LogStash module PluginMixins module Jdbc
|
|
52
64
|
|
53
65
|
def build_query(db, sql_last_value)
|
54
66
|
parameters[:sql_last_value] = sql_last_value
|
55
|
-
|
56
|
-
statement_logger.log_statement_parameters(statement, parameters, query)
|
57
|
-
query
|
67
|
+
db[statement, parameters]
|
58
68
|
end
|
59
69
|
|
60
|
-
def post_init(plugin)
|
61
|
-
@parameter_keys = ["sql_last_value"] + plugin.parameters.keys
|
62
|
-
@parameters = plugin.parameters.inject({}) do |hash,(k,v)|
|
63
|
-
case v
|
64
|
-
when LogStash::Timestamp
|
65
|
-
hash[k.to_sym] = v.time
|
66
|
-
else
|
67
|
-
hash[k.to_sym] = v
|
68
|
-
end
|
69
|
-
hash
|
70
|
-
end
|
71
|
-
end
|
72
70
|
end
|
73
71
|
|
74
72
|
class PagedNormalStatementHandler < NormalStatementHandler
|
75
|
-
|
73
|
+
|
74
|
+
def initialize(plugin)
|
75
|
+
super(plugin)
|
76
|
+
@jdbc_page_size = plugin.jdbc_page_size
|
77
|
+
@logger = plugin.logger
|
78
|
+
end
|
76
79
|
|
77
80
|
# Performs the query, respecting our pagination settings, yielding once per row of data
|
78
81
|
# @param db [Sequel::Database]
|
@@ -81,16 +84,22 @@ module LogStash module PluginMixins module Jdbc
|
|
81
84
|
def perform_query(db, sql_last_value)
|
82
85
|
query = build_query(db, sql_last_value)
|
83
86
|
query.each_page(@jdbc_page_size) do |paged_dataset|
|
87
|
+
log_dataset_page(paged_dataset) if @logger.debug?
|
84
88
|
paged_dataset.each do |row|
|
85
89
|
yield row
|
86
90
|
end
|
87
91
|
end
|
88
92
|
end
|
89
93
|
|
90
|
-
|
91
|
-
|
92
|
-
|
94
|
+
private
|
95
|
+
|
96
|
+
# @param paged_dataset [Sequel::Dataset::Pagination] like object
|
97
|
+
def log_dataset_page(paged_dataset)
|
98
|
+
@logger.debug "fetching paged dataset", current_page: paged_dataset.current_page,
|
99
|
+
record_count: paged_dataset.current_page_record_count,
|
100
|
+
total_record_count: paged_dataset.pagination_record_count
|
93
101
|
end
|
102
|
+
|
94
103
|
end
|
95
104
|
|
96
105
|
class ExplicitPagingModeStatementHandler < PagedNormalStatementHandler
|
@@ -101,20 +110,29 @@ module LogStash module PluginMixins module Jdbc
|
|
101
110
|
def perform_query(db, sql_last_value)
|
102
111
|
query = build_query(db, sql_last_value)
|
103
112
|
offset = 0
|
113
|
+
page_size = @jdbc_page_size
|
104
114
|
loop do
|
105
115
|
rows_in_page = 0
|
106
|
-
query.with_sql(query.sql, offset: offset, size:
|
116
|
+
query.with_sql(query.sql, offset: offset, size: page_size).each do |row|
|
107
117
|
yield row
|
108
118
|
rows_in_page += 1
|
109
119
|
end
|
110
|
-
break unless rows_in_page ==
|
111
|
-
offset +=
|
120
|
+
break unless rows_in_page == page_size
|
121
|
+
offset += page_size
|
112
122
|
end
|
113
123
|
end
|
114
124
|
end
|
115
125
|
|
116
126
|
class PreparedStatementHandler < StatementHandler
|
117
|
-
attr_reader :name, :bind_values_array, :statement_prepared, :prepared
|
127
|
+
attr_reader :name, :bind_values_array, :statement_prepared, :prepared, :parameters
|
128
|
+
|
129
|
+
def initialize(plugin)
|
130
|
+
super(plugin)
|
131
|
+
@name = plugin.prepared_statement_name.to_sym
|
132
|
+
@bind_values_array = plugin.prepared_statement_bind_values
|
133
|
+
@parameters = plugin.parameters
|
134
|
+
@statement_prepared = Concurrent::AtomicBoolean.new(false)
|
135
|
+
end
|
118
136
|
|
119
137
|
# Performs the query, ignoring our pagination settings, yielding once per row of data
|
120
138
|
# @param db [Sequel::Database]
|
@@ -142,7 +160,6 @@ module LogStash module PluginMixins module Jdbc
|
|
142
160
|
db.set_prepared_statement(name, prepared)
|
143
161
|
end
|
144
162
|
bind_value_sql_last_value(sql_last_value)
|
145
|
-
statement_logger.log_statement_parameters(statement, parameters, nil)
|
146
163
|
begin
|
147
164
|
db.call(name, parameters)
|
148
165
|
rescue => e
|
@@ -153,17 +170,6 @@ module LogStash module PluginMixins module Jdbc
|
|
153
170
|
end
|
154
171
|
end
|
155
172
|
|
156
|
-
def post_init(plugin)
|
157
|
-
# don't log statement count when using prepared statements for now...
|
158
|
-
# needs enhancement to allow user to supply a bindable count prepared statement in settings.
|
159
|
-
@statement_logger.disable_count
|
160
|
-
|
161
|
-
@name = plugin.prepared_statement_name.to_sym
|
162
|
-
@bind_values_array = plugin.prepared_statement_bind_values
|
163
|
-
@parameters = plugin.parameters
|
164
|
-
@statement_prepared = Concurrent::AtomicBoolean.new(false)
|
165
|
-
end
|
166
|
-
|
167
173
|
def create_bind_values_hash
|
168
174
|
hash = {}
|
169
175
|
bind_values_array.each_with_index {|v,i| hash[:"p#{i}"] = v}
|
@@ -6,9 +6,6 @@ module LogStash module PluginMixins module Jdbc
|
|
6
6
|
|
7
7
|
def self.build_last_value_tracker(plugin)
|
8
8
|
handler = plugin.record_last_run ? FileHandler.new(plugin.last_run_metadata_path) : NullFileHandler.new(plugin.last_run_metadata_path)
|
9
|
-
if plugin.record_last_run
|
10
|
-
handler = FileHandler.new(plugin.last_run_metadata_path)
|
11
|
-
end
|
12
9
|
if plugin.clean_run
|
13
10
|
handler.clean
|
14
11
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'logstash-integration-jdbc'
|
3
|
-
s.version = '5.2.
|
3
|
+
s.version = '5.2.5'
|
4
4
|
s.licenses = ['Apache License (2.0)']
|
5
5
|
s.summary = "Integration with JDBC - input and filter plugins"
|
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"
|
@@ -34,7 +34,9 @@ Gem::Specification.new do |s|
|
|
34
34
|
|
35
35
|
s.add_runtime_dependency 'tzinfo'
|
36
36
|
s.add_runtime_dependency 'tzinfo-data'
|
37
|
-
|
37
|
+
# plugin maintains compatibility with < 3.5 (3.0.9)
|
38
|
+
# but works with newer rufus-scheduler >= 3.5 as well
|
39
|
+
s.add_runtime_dependency 'rufus-scheduler'
|
38
40
|
s.add_runtime_dependency 'logstash-mixin-ecs_compatibility_support', '~>1.3'
|
39
41
|
s.add_runtime_dependency "logstash-mixin-validator_support", '~> 1.0'
|
40
42
|
s.add_runtime_dependency "logstash-mixin-event_support", '~> 1.0'
|
@@ -41,6 +41,31 @@ describe LogStash::Inputs::Jdbc, :integration => true do
|
|
41
41
|
expect(event.get('first_name')).to eq("Mark")
|
42
42
|
expect(event.get('last_name')).to eq("Guckenheimer")
|
43
43
|
end
|
44
|
+
|
45
|
+
context 'with paging' do
|
46
|
+
let(:settings) do
|
47
|
+
super().merge 'jdbc_paging_enabled' => true, 'jdbc_page_size' => 1,
|
48
|
+
"statement" => 'SELECT * FROM "employee" WHERE EMP_NO >= :p1 ORDER BY EMP_NO',
|
49
|
+
'parameters' => { 'p1' => 0 }
|
50
|
+
end
|
51
|
+
|
52
|
+
before do # change plugin logger level to debug - to exercise logging
|
53
|
+
logger = plugin.class.name.gsub('::', '.').downcase
|
54
|
+
logger = org.apache.logging.log4j.LogManager.getLogger(logger)
|
55
|
+
@prev_logger_level = [ logger.getName, logger.getLevel ]
|
56
|
+
org.apache.logging.log4j.core.config.Configurator.setLevel logger.getName, org.apache.logging.log4j.Level::DEBUG
|
57
|
+
end
|
58
|
+
|
59
|
+
after do
|
60
|
+
org.apache.logging.log4j.core.config.Configurator.setLevel *@prev_logger_level
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should populate the event with database entries" do
|
64
|
+
plugin.run(queue)
|
65
|
+
event = queue.pop
|
66
|
+
expect(event.get('first_name')).to eq('David')
|
67
|
+
end
|
68
|
+
end
|
44
69
|
end
|
45
70
|
|
46
71
|
context "when supplying a non-existent library" do
|
data/spec/inputs/jdbc_spec.rb
CHANGED
@@ -1432,54 +1432,6 @@ describe LogStash::Inputs::Jdbc do
|
|
1432
1432
|
end
|
1433
1433
|
end
|
1434
1434
|
|
1435
|
-
context "when debug logging and a count query raises a count related error" do
|
1436
|
-
let(:settings) do
|
1437
|
-
{ "statement" => "SELECT * from types_table" }
|
1438
|
-
end
|
1439
|
-
let(:logger) { double("logger", :debug? => true) }
|
1440
|
-
let(:statement_logger) { LogStash::PluginMixins::Jdbc::CheckedCountLogger.new(logger) }
|
1441
|
-
let(:value_tracker) { double("value tracker", :set_value => nil, :write => nil) }
|
1442
|
-
let(:msg) { 'Java::JavaSql::SQLSyntaxErrorException: Dynamic SQL Error; SQL error code = -104; Token unknown - line 1, column 105; LIMIT [SQLState:42000, ISC error code:335544634]' }
|
1443
|
-
let(:error_args) do
|
1444
|
-
{"exception" => msg}
|
1445
|
-
end
|
1446
|
-
|
1447
|
-
before do
|
1448
|
-
db << "INSERT INTO types_table (num, string, started_at, custom_time, ranking) VALUES (1, 'A test', '1999-12-31', '1999-12-31 23:59:59', 95.67)"
|
1449
|
-
plugin.register
|
1450
|
-
plugin.set_statement_logger(statement_logger)
|
1451
|
-
plugin.set_value_tracker(value_tracker)
|
1452
|
-
allow(value_tracker).to receive(:value).and_return("bar")
|
1453
|
-
allow(statement_logger).to receive(:execute_count).once.and_raise(StandardError.new(msg))
|
1454
|
-
end
|
1455
|
-
|
1456
|
-
after do
|
1457
|
-
plugin.stop
|
1458
|
-
end
|
1459
|
-
|
1460
|
-
context "if the count query raises an error" do
|
1461
|
-
it "should log a debug line without a count key as its unknown whether a count works at this stage" do
|
1462
|
-
expect(logger).to receive(:warn).once.with("Attempting a count query raised an error, the generated count statement is most likely incorrect but check networking, authentication or your statement syntax", error_args)
|
1463
|
-
expect(logger).to receive(:warn).once.with("Ongoing count statement generation is being prevented")
|
1464
|
-
expect(logger).to receive(:debug).once.with("Executing JDBC query", :statement => settings["statement"], :parameters => {:sql_last_value=>"bar"})
|
1465
|
-
plugin.run(queue)
|
1466
|
-
queue.pop
|
1467
|
-
end
|
1468
|
-
|
1469
|
-
it "should create an event normally" do
|
1470
|
-
allow(logger).to receive(:warn)
|
1471
|
-
allow(logger).to receive(:debug)
|
1472
|
-
plugin.run(queue)
|
1473
|
-
event = queue.pop
|
1474
|
-
expect(event.get("num")).to eq(1)
|
1475
|
-
expect(event.get("string")).to eq("A test")
|
1476
|
-
expect(event.get("started_at")).to be_a_logstash_timestamp_equivalent_to("1999-12-31T00:00:00.000Z")
|
1477
|
-
expect(event.get("custom_time")).to be_a_logstash_timestamp_equivalent_to("1999-12-31T23:59:59.000Z")
|
1478
|
-
expect(event.get("ranking").to_f).to eq(95.67)
|
1479
|
-
end
|
1480
|
-
end
|
1481
|
-
end
|
1482
|
-
|
1483
1435
|
context "when an unreadable jdbc_driver_path entry is present" do
|
1484
1436
|
let(:driver_jar_path) do
|
1485
1437
|
jar_file = $CLASSPATH.find { |name| name.index(Jdbc::Derby.driver_jar) }
|
@@ -49,4 +49,30 @@ describe LogStash::PluginMixins::Jdbc::Scheduler do
|
|
49
49
|
|
50
50
|
end
|
51
51
|
|
52
|
+
context 'work threads' do
|
53
|
+
|
54
|
+
let(:opts) { super().merge :max_work_threads => 3 }
|
55
|
+
|
56
|
+
let(:counter) { java.util.concurrent.atomic.AtomicLong.new(0) }
|
57
|
+
|
58
|
+
before do
|
59
|
+
scheduler.schedule_cron('* * * * * *') { counter.increment_and_get; sleep 3.25 } # every second
|
60
|
+
end
|
61
|
+
|
62
|
+
it "are working" do
|
63
|
+
sleep(0.05) while counter.get == 0
|
64
|
+
expect( scheduler.work_threads.size ).to eql 1
|
65
|
+
sleep(0.05) while counter.get == 1
|
66
|
+
expect( scheduler.work_threads.size ).to eql 2
|
67
|
+
sleep(0.05) while counter.get == 2
|
68
|
+
expect( scheduler.work_threads.size ).to eql 3
|
69
|
+
|
70
|
+
sleep 1.25
|
71
|
+
expect( scheduler.work_threads.size ).to eql 3
|
72
|
+
sleep 1.25
|
73
|
+
expect( scheduler.work_threads.size ).to eql 3
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
52
78
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: logstash-integration-jdbc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.2.
|
4
|
+
version: 5.2.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Elastic
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-05-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
@@ -131,17 +131,17 @@ dependencies:
|
|
131
131
|
- !ruby/object:Gem::Dependency
|
132
132
|
requirement: !ruby/object:Gem::Requirement
|
133
133
|
requirements:
|
134
|
-
- - "
|
134
|
+
- - ">="
|
135
135
|
- !ruby/object:Gem::Version
|
136
|
-
version:
|
136
|
+
version: '0'
|
137
137
|
name: rufus-scheduler
|
138
138
|
prerelease: false
|
139
139
|
type: :runtime
|
140
140
|
version_requirements: !ruby/object:Gem::Requirement
|
141
141
|
requirements:
|
142
|
-
- - "
|
142
|
+
- - ">="
|
143
143
|
- !ruby/object:Gem::Version
|
144
|
-
version:
|
144
|
+
version: '0'
|
145
145
|
- !ruby/object:Gem::Dependency
|
146
146
|
requirement: !ruby/object:Gem::Requirement
|
147
147
|
requirements:
|
@@ -275,7 +275,6 @@ files:
|
|
275
275
|
- lib/logstash/filters/jdbc_streaming.rb
|
276
276
|
- lib/logstash/inputs/jdbc.rb
|
277
277
|
- lib/logstash/inputs/tzinfo_jruby_patch.rb
|
278
|
-
- lib/logstash/plugin_mixins/jdbc/checked_count_logger.rb
|
279
278
|
- lib/logstash/plugin_mixins/jdbc/common.rb
|
280
279
|
- lib/logstash/plugin_mixins/jdbc/jdbc.rb
|
281
280
|
- lib/logstash/plugin_mixins/jdbc/scheduler.rb
|
@@ -1,43 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module LogStash module PluginMixins module Jdbc
|
4
|
-
class CheckedCountLogger
|
5
|
-
def initialize(logger)
|
6
|
-
@logger = logger
|
7
|
-
@needs_check = true
|
8
|
-
@count_is_supported = false
|
9
|
-
@in_debug = @logger.debug?
|
10
|
-
end
|
11
|
-
|
12
|
-
def disable_count
|
13
|
-
@needs_check = false
|
14
|
-
@count_is_supported = false
|
15
|
-
end
|
16
|
-
|
17
|
-
def log_statement_parameters(statement, parameters, query)
|
18
|
-
return unless @in_debug
|
19
|
-
check_count_query(query) if @needs_check && query
|
20
|
-
if @count_is_supported
|
21
|
-
@logger.debug("Executing JDBC query", :statement => statement, :parameters => parameters, :count => execute_count(query))
|
22
|
-
else
|
23
|
-
@logger.debug("Executing JDBC query", :statement => statement, :parameters => parameters)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def check_count_query(query)
|
28
|
-
@needs_check = false
|
29
|
-
begin
|
30
|
-
execute_count(query)
|
31
|
-
@count_is_supported = true
|
32
|
-
rescue Exception => e
|
33
|
-
@logger.warn("Attempting a count query raised an error, the generated count statement is most likely incorrect but check networking, authentication or your statement syntax", "exception" => e.message)
|
34
|
-
@logger.warn("Ongoing count statement generation is being prevented")
|
35
|
-
@count_is_supported = false
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def execute_count(query)
|
40
|
-
query.count
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end end end
|