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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2f43be09f1996c0e4effd6914055fa643549925433ab4512f4e3a0ed1fa1933c
4
- data.tar.gz: 346cb42a27eaaa3f51e7cf1ba3d52fb332f278dfe8cecf7d1aa200af42ba4f5b
3
+ metadata.gz: b74796568bc1df12ae5015cfcf38aeb43971b3bd3eda2dcc668104decc69b6a2
4
+ data.tar.gz: ae3a1f82f7354798692933a2426977ea2e17f7335a74d02eb09a376e4421af6b
5
5
  SHA512:
6
- metadata.gz: 3a75a4a4165e04178384a66740de3bce2981c2d019a8bd2192519a725bfe88d5ddf7469cf2a2aebda12b1b48b2117817171da2f459d283c3e3e16d86422c4c06
7
- data.tar.gz: ae15ead4d5efd52cc67bf58ad1d994bde11443bbc0e083d56f695ddc7163be44454ee1db1a95b81ec884c51a790f2e7582733df4f5faf6b9d4715a0685a31c92
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 :schedule_frequency, :loader_schedule
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
- def post_initialize
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
- unless @loader_schedule.is_a?(String)
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,8 +3,6 @@ require_relative "single_load_runner"
3
3
 
4
4
  module LogStash module Filters module Jdbc
5
5
  class RepeatingLoadRunner < SingleLoadRunner
6
- # info - attr_reader :local, :loaders, :preloaders
7
-
8
6
  def repeated_load
9
7
  local.repopulate_all(loaders)
10
8
  @reload_counter.increment
@@ -26,10 +26,6 @@ module LogStash module Filters module Jdbc
26
26
  def repeated_load
27
27
  end
28
28
 
29
- def call
30
- repeated_load
31
- end
32
-
33
29
  def reload_count
34
30
  @reload_counter.value
35
31
  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
- cronline = Jdbc::LoaderSchedule.new(@loader_schedule)
198
- cronline.to_log_string.tap do |msg|
199
- logger.info("Scheduler operations: #{msg}") unless msg.empty?
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
@@ -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(LogStash::PluginMixins::Jdbc::ValueTracking.build_last_value_tracker(self))
263
- set_statement_logger(LogStash::PluginMixins::Jdbc::CheckedCountLogger.new(@logger))
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 set_statement_logger(instance)
287
- @statement_handler = LogStash::PluginMixins::Jdbc::StatementHandler.build_statement_handler(self, instance)
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.new(
299
- :max_work_threads => 1, :thread_name => "[#{id}]<jdbc__scheduler"
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)
@@ -4,7 +4,6 @@ require "logstash/config/mixin"
4
4
  require "time"
5
5
  require "date"
6
6
  require_relative "value_tracking"
7
- require_relative "checked_count_logger"
8
7
  require_relative "statement_handler"
9
8
 
10
9
  java_import java.util.concurrent.locks.ReentrantLock
@@ -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, logger)
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, logger)
19
+ klass.new(plugin)
20
20
  end
21
21
 
22
- attr_reader :statement, :parameters, :statement_logger
22
+ attr_reader :statement, :parameters
23
23
 
24
- def initialize(plugin, statement_logger)
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
- query = db[statement, parameters]
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
- attr_reader :jdbc_page_size
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
- def post_init(plugin)
91
- super(plugin)
92
- @jdbc_page_size = plugin.jdbc_page_size
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: jdbc_page_size).each do |row|
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 == jdbc_page_size
111
- offset += jdbc_page_size
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.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
- s.add_runtime_dependency 'rufus-scheduler', '~> 3.0.9'
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'
@@ -17,7 +17,7 @@ module LogStash module Filters module Jdbc
17
17
  expect(local_db).to receive(:populate_all).once.with(loaders)
18
18
  expect(local_db).to receive(:repopulate_all).once.with(loaders)
19
19
  runner.initial_load
20
- subject.call
20
+ subject.repeated_load
21
21
  end
22
22
  end
23
23
  end
@@ -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
@@ -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.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-01-19 00:00:00.000000000 Z
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: 3.0.9
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: 3.0.9
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