logstash-integration-jdbc 5.2.4 → 5.4.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -0
  3. data/docs/filter-jdbc_static.asciidoc +14 -2
  4. data/docs/filter-jdbc_streaming.asciidoc +1 -1
  5. data/docs/input-jdbc.asciidoc +41 -4
  6. data/lib/logstash/filters/jdbc/basic_database.rb +1 -1
  7. data/lib/logstash/filters/jdbc/read_only_database.rb +2 -2
  8. data/lib/logstash/filters/jdbc_static.rb +19 -10
  9. data/lib/logstash/inputs/jdbc.rb +69 -20
  10. data/lib/logstash/plugin_mixins/jdbc/common.rb +2 -1
  11. data/lib/logstash/plugin_mixins/jdbc/jdbc.rb +22 -17
  12. data/lib/logstash/plugin_mixins/jdbc/sequel_bootstrap.rb +21 -0
  13. data/lib/logstash/plugin_mixins/jdbc/statement_handler.rb +51 -45
  14. data/lib/logstash/plugin_mixins/jdbc/timezone_proxy.rb +61 -0
  15. data/lib/logstash/plugin_mixins/jdbc/value_tracking.rb +16 -3
  16. data/lib/logstash-integration-jdbc_jars.rb +4 -2
  17. data/logstash-integration-jdbc.gemspec +6 -6
  18. data/spec/filters/jdbc_static_spec.rb +10 -0
  19. data/spec/filters/jdbc_streaming_spec.rb +7 -10
  20. data/spec/inputs/integration/integ_spec.rb +28 -9
  21. data/spec/inputs/jdbc_spec.rb +202 -59
  22. data/spec/plugin_mixins/jdbc/timezone_proxy_spec.rb +68 -0
  23. data/spec/plugin_mixins/jdbc/value_tracking_spec.rb +113 -0
  24. data/vendor/jar-dependencies/org/apache/derby/derby/10.15.2.1/derby-10.15.2.1.jar +0 -0
  25. data/vendor/jar-dependencies/org/apache/derby/derbyclient/10.15.2.1/derbyclient-10.15.2.1.jar +0 -0
  26. data/vendor/jar-dependencies/org/apache/derby/derbyshared/10.15.2.1/derbyshared-10.15.2.1.jar +0 -0
  27. data/vendor/jar-dependencies/org/apache/derby/derbytools/10.15.2.1/derbytools-10.15.2.1.jar +0 -0
  28. metadata +39 -49
  29. data/lib/logstash/plugin_mixins/jdbc/checked_count_logger.rb +0 -43
  30. data/lib/logstash/plugin_mixins/jdbc/scheduler.rb +0 -175
  31. data/spec/plugin_mixins/jdbc/scheduler_spec.rb +0 -78
  32. data/vendor/jar-dependencies/org/apache/derby/derby/10.14.1.0/derby-10.14.1.0.jar +0 -0
  33. data/vendor/jar-dependencies/org/apache/derby/derbyclient/10.14.1.0/derbyclient-10.14.1.0.jar +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ad77fc92ab07e1da7e4c6285075b6583f9f293c073199b614e3852cd22bbc6a9
4
- data.tar.gz: 9e7b2c0f0c16930903bdee1f22b2651db7cf9f80d01a12f212b7a40155d4ebea
3
+ metadata.gz: 7604bb8967c9d63f585d14622ab18b70bc0d9e9b34e43bca6b6c59c9d05a6b76
4
+ data.tar.gz: 2e9d142376d12bbd1313142e79174d540d53e098e481330262e784c8177e4898
5
5
  SHA512:
6
- metadata.gz: 2928a01bbe1ef418f36d6ddc7847416fc6d43278b20242926037bef02e8a429ba1d4587f1bd88f8d52de2ec1fef26e749aeb358de82a8bf69c36e5c3c9d42f8e
7
- data.tar.gz: 8aae596826ec1d9155e0f8bdfad9a882e92fa2be610f89d7c67a7b3f44f0e6937bcbafd78900457d92b20a5a28188163f7b4f25040d8deb229f505ab6faf2c22
6
+ metadata.gz: 165749e33fed7d98b9635f6a1d74cdb05623563a3942d34c093ef761cd8e4f4784e6eb467a699909e5420ceef8fb4779c00e1b154845838052aa278a592ddc2a
7
+ data.tar.gz: b9a4e7822ea60c3dde25edd6a1a6e72837069a993046a050a05aa32e92172d4689624df12e85c31b79f62c40fd0f45f98700e07a678a7ea2656d9730f2f14a8e
data/CHANGELOG.md CHANGED
@@ -1,3 +1,53 @@
1
+ ## 5.4.11
2
+ - Fixes an issue in which any one instance of a JDBC input plugin using `jdbc_default_timezone` changes the behaviour of plugin instances that do _not_ use `jdbc_default_timezone`, ensuring that timezone offsets remain consistent for each instance of the plugin _as configured_ [#151](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/151)
3
+ - Fixes an exception that could occur while reloading `jdbc_static` databases when the underlying connection to the remote has been broken [#165](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/165)
4
+
5
+ ## 5.4.10
6
+ - Adds retry mechanism when checkout Derby from SVN repository [#158](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/158)
7
+ - [DOC] add known limitations and settings for connection issue [#167](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/167)
8
+
9
+ ## 5.4.9
10
+ - Fix Derby missed driver classes when built locally for version 10.15 [#160](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/160)
11
+
12
+ ## 5.4.8
13
+ - Update Derby with locally built 10.15.2.1 version [#155](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/155)
14
+
15
+ ## 5.4.7
16
+ - Update sequel version to >= 5.74.0. It fixes the generic jdbc adapter to properly handle disconnect errors [#153](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/153)
17
+
18
+ ## 5.4.6
19
+ - Update sequel version to >= 5.73.0. The ibmdb and jdbc/db2 adapters were fixed to properly handle disconnect errors, removing the related connection from the pool [#144](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/144)
20
+
21
+ ## 5.4.5
22
+ - Pin sequel to < 5.72.0 due to ruby/bigdecimal#169 [#141](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/141)
23
+
24
+ ## 5.4.4
25
+ - Fix: adaptations for JRuby 9.4 [#125](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/125)
26
+
27
+ ## 5.4.3
28
+ - Fix crash when metadata file can't be deleted after moving under path.data [#136](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/136)
29
+
30
+ ## 5.4.2
31
+ - Doc: described default_hash and tag_on_default_use interaction filter plugin [#122](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/122)
32
+ - Added new settings `statement_retry_attempts` and `statement_retry_attempts_wait_time` for retry of failed sql statement execution [#123](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/123)
33
+
34
+ ## 5.4.1
35
+ - Bugfix leak which happened in creating a new Database pool for every query. The pool is now crated on registration and closed on plugin's `stop` [#119](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/119)
36
+
37
+ ## 5.4.0
38
+ - Ambiguous Timestamp Support [#92](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/92)
39
+ - FIX: when encountering an ambiguous timestamp, the JDBC Input no longer crashes
40
+ - Added support for disambiguating timestamps in daylight saving time (DST) overlap periods
41
+
42
+ ## 5.3.0
43
+ - Refactor: start using scheduler mixin [#110](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/110)
44
+
45
+ ## 5.2.6
46
+ - Fix: change default path of 'last_run_metadata_path' to be rooted in the LS data.path folder and not in $HOME [#106](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/106)
47
+
48
+ ## 5.2.5
49
+ - Fix: do not execute more queries with debug logging [#109](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/109)
50
+
1
51
  ## 5.2.4
2
52
  - Fix: compatibility with all (>= 3.0) rufus-scheduler versions [#97](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/97)
3
53
 
@@ -111,10 +111,14 @@ filter {
111
111
  query => "SELECT firstname, lastname FROM users WHERE userid = ? AND country = ?"
112
112
  prepared_parameters => ["[loggedin_userid]", "[user_nation]"] <4>
113
113
  target => "user" <5>
114
+ default_hash => { <6>
115
+ firstname => nil
116
+ lastname => nil
117
+ }
114
118
  }
115
119
  ]
116
120
  # using add_field here to add & rename values to the event root
117
- add_field => { server_name => "%{[server][0][description]}" } <6>
121
+ add_field => { server_name => "%{[server][0][description]}" } <7>
118
122
  add_field => { user_firstname => "%{[user][0][firstname]}" }
119
123
  add_field => { user_lastname => "%{[user][0][lastname]}" }
120
124
  remove_field => ["server", "user"]
@@ -127,6 +131,13 @@ filter {
127
131
  jdbc_connection_string => "jdbc:postgresql://remotedb:5432/ls_test_2"
128
132
  }
129
133
  }
134
+
135
+ output {
136
+ if "_jdbcstaticdefaultsused" in [tags] {
137
+ # Print all the not found users
138
+ stdout { }
139
+ }
140
+ }
130
141
  -----
131
142
  <1> Queries an external database to fetch the dataset that will be cached
132
143
  locally.
@@ -139,7 +150,8 @@ See <<plugins-{type}s-{plugin}-object_order>>.
139
150
  follow the positional ordering.
140
151
  <5> Specifies the event field that will store the looked-up data. If the lookup
141
152
  returns multiple columns, the data is stored as a JSON object within the field.
142
- <6> Takes data from the JSON object and stores it in top-level event fields for
153
+ <6> When the user is not found in the database, an event is created using data from the <<plugins-{type}s-{plugin}-local_lookups>> `default hash` setting, and the event is tagged with the list set in <<plugins-{type}s-{plugin}-tag_on_default_use>>.
154
+ <7> Takes data from the JSON object and stores it in top-level event fields for
143
155
  easier analysis in Kibana.
144
156
 
145
157
  Here's a full example:
@@ -21,7 +21,7 @@ include::{include_path}/plugin_header-integration.asciidoc[]
21
21
 
22
22
  ==== Description
23
23
 
24
- This filter executes a SQL query and store the result set in the field
24
+ This filter executes a SQL query and stores the result set in the field
25
25
  specified as `target`.
26
26
  It will cache the results locally in an LRU cache with expiry.
27
27
 
@@ -176,6 +176,29 @@ input {
176
176
  }
177
177
  ---------------------------------------------------------------------------------------------------
178
178
 
179
+ ==== Database-specific considerations
180
+
181
+ The JDBC input plugin leverages the https://github.com/jeremyevans/sequel[sequel] library to query databases through their JDBC drivers.
182
+ The implementation of drivers will vary, however, potentially leading to unexpected behavior.
183
+
184
+ ===== Unable to reuse connections
185
+
186
+ Some databases - such as Sybase or SQL Anywhere - may have issues with stale connections, timing out between scheduled runs and never reconnecting.
187
+
188
+ To ensure connections are valid before queries are executed, enable <<plugins-{type}s-{plugin}-jdbc_validate_connection>> and set <<plugins-{type}s-{plugin}-jdbc_validation_timeout>> to a shorter interval than the <<plugins-{type}s-{plugin}-schedule>>.
189
+
190
+ [source,ruby]
191
+ ---------------------------------------------------------------------------------------------------
192
+ input {
193
+ jdbc {
194
+ schedule => "* * * * *" # run every minute
195
+ jdbc_validate_connection => true
196
+ jdbc_validation_timeout => 50 # 50 seconds
197
+ }
198
+ }
199
+ ---------------------------------------------------------------------------------------------------
200
+
201
+
179
202
 
180
203
  [id="plugins-{type}s-{plugin}-options"]
181
204
  ==== Jdbc Input Configuration Options
@@ -286,10 +309,14 @@ JDBC connection string
286
309
  ===== `jdbc_default_timezone`
287
310
 
288
311
  * Value type is <<string,string>>
312
+ ** Value should be a canonical timezone or offset, such as `Europe/Paris` or `Etc/GMT+3`
313
+ ** Value _may_ include square-bracketed extensions, such as `America/Denver[dst_enabled_on_overlap:true]`
289
314
  * There is no default value for this setting.
290
315
 
291
- Timezone conversion.
292
- Logstash (and Elasticsearch) expects that timestamps are expressed in UTC terms.
316
+ [id="plugins-{type}s-{plugin}-jdbc_timezone_conv"]
317
+ ====== Timezone conversion
318
+
319
+ Logstash and Elasticsearch expect timestamps to be expressed in UTC terms.
293
320
  If your database has recorded timestamps that are relative to another timezone,
294
321
  the database timezone if you will, then set this setting to be the timezone that
295
322
  the database is using. However, as SQL does not allow for timezone data in
@@ -299,7 +326,15 @@ in relative UTC time in ISO8601 format.
299
326
 
300
327
  Using this setting will manually assign a specified timezone offset, instead
301
328
  of using the timezone setting of the local machine. You must use a canonical
302
- timezone, *America/Denver*, for example.
329
+ timezone, `America/Denver`, for example.
330
+
331
+ [id="plugins-{type}s-{plugin}-jdbc_ambiguous_timestamps"]
332
+ ===== Ambiguous timestamps
333
+
334
+ While it is common to store local times in SQL's timestamp column type, many timezones change their offset during the course of a calendar year and therefore cannot be used with SQL's timestamp type to represent an ordered, continuous timeline.
335
+ For example in the `America/Chicago` zone when daylight saving time (DST) ends in the autumn, the clock rolls from `01:59:59` back to `01:00:00`, making any timestamp in the 2-hour period between `01:00:00CDT` and `02:00:00CST` on that day ambiguous.
336
+
337
+ When encountering an ambiguous timestamp caused by a DST transition, the query will fail unless the timezone specified here includes a square-bracketed instruction for how to handle overlapping periods (such as: `America/Chicago[dst_enabled_on_overlap:true]` or `Australia/Melbourne[dst_enabled_on_overlap:false]`).
303
338
 
304
339
  [id="plugins-{type}s-{plugin}-plugin_timezone"]
305
340
  ===== `plugin_timezone`
@@ -479,10 +514,12 @@ How often to validate a connection (in seconds)
479
514
  ===== `last_run_metadata_path`
480
515
 
481
516
  * Value type is <<string,string>>
482
- * Default value is `"$HOME/.logstash_jdbc_last_run"`
517
+ * Default value is `"<path.data>/plugins/inputs/jdbc/logstash_jdbc_last_run"`
483
518
 
484
519
  Path to file with last run time
485
520
 
521
+ In versions prior to 5.2.6 the metadata file was written to `$HOME/.logstash_jdbc_last_run`. If during a Logstash upgrade the file is found in "$HOME" it will be moved to the default location under "path.data". If the path is defined by the user then no automatic move is performed.
522
+
486
523
  [id="plugins-{type}s-{plugin}-lowercase_column_names"]
487
524
  ===== `lowercase_column_names`
488
525
 
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
  require "fileutils"
3
- require "sequel"
3
+ require "logstash/plugin_mixins/jdbc/sequel_bootstrap"
4
4
  require "sequel/adapters/jdbc"
5
5
  require "java"
6
6
  require "logstash/util/loggable"
@@ -12,7 +12,7 @@ module LogStash module Filters module Jdbc
12
12
  if connected?
13
13
  result = @db[statement].count
14
14
  else
15
- debug_log_messages.concat("and there is no connection to the remote db at this time")
15
+ debug_log_messages << "and there is no connection to the remote db at this time"
16
16
  end
17
17
  rescue ::Sequel::Error => err
18
18
  # a fatal issue
@@ -32,7 +32,7 @@ module LogStash module Filters module Jdbc
32
32
  if connected?
33
33
  result = @db[statement].all
34
34
  else
35
- debug_log_messages.concat("and there is no connection to the remote db at this time")
35
+ debug_log_messages << "and there is no connection to the remote db at this time"
36
36
  end
37
37
  rescue ::Sequel::Error => err
38
38
  # a fatal issue
@@ -3,7 +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
+ require "logstash/plugin_mixins/scheduler"
7
7
  require_relative "jdbc/loader"
8
8
  require_relative "jdbc/loader_schedule"
9
9
  require_relative "jdbc/repeating_load_runner"
@@ -19,6 +19,8 @@ module LogStash module Filters class JdbcStatic < LogStash::Filters::Base
19
19
  # adds ecs_compatibility config which could be :disabled or :v1
20
20
  include LogStash::PluginMixins::ECSCompatibilitySupport(:disabled, :v1, :v8 => :v1)
21
21
 
22
+ include LogStash::PluginMixins::Scheduler
23
+
22
24
  config_name "jdbc_static"
23
25
 
24
26
  # Define the loaders, an Array of Hashes, to fetch remote data and create local tables.
@@ -162,7 +164,6 @@ module LogStash module Filters class JdbcStatic < LogStash::Filters::Base
162
164
  end
163
165
 
164
166
  def close
165
- @scheduler.stop if @scheduler
166
167
  @parsed_loaders.each(&:close)
167
168
  @processor.close
168
169
  end
@@ -175,9 +176,23 @@ module LogStash module Filters class JdbcStatic < LogStash::Filters::Base
175
176
  private
176
177
 
177
178
  def prepare_data_dir
179
+ # cleanup existing Derby file left behind in $HOME
180
+ derby_log = "#{ENV['HOME']}/derby.log"
181
+ if ::File.exist?(derby_log)
182
+ begin
183
+ ::File.delete(derby_log)
184
+ rescue Errno::EPERM => e
185
+ @logger.warn("Can't delete temporary file #{derby_log} due to access permissions")
186
+ rescue e
187
+ @logger.warn("Can't delete temporary file #{derby_log}", {message => e.message})
188
+ end
189
+ end
190
+
178
191
  # later, when local persistent databases are allowed set this property to LS_HOME/data/jdbc-static/
179
192
  # must take multi-pipelines into account and more than one config using the same jdbc-static settings
180
- java.lang.System.setProperty("derby.system.home", ENV["HOME"])
193
+ path_data = Pathname.new(LogStash::SETTINGS.get_value("path.data")).join("plugins", "shared", "derby_home")
194
+ path_data.mkpath
195
+ java.lang.System.setProperty("derby.system.home", path_data.to_path)
181
196
  logger.info("derby.system.home is: #{java.lang.System.getProperty("derby.system.home")}")
182
197
  end
183
198
 
@@ -194,13 +209,7 @@ module LogStash module Filters class JdbcStatic < LogStash::Filters::Base
194
209
  if @loader_schedule
195
210
  @loader_runner = Jdbc::RepeatingLoadRunner.new(*runner_args)
196
211
  @loader_runner.initial_load
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)
203
- end
212
+ scheduler.cron(@loader_schedule) { @loader_runner.repeated_load }
204
213
  else
205
214
  @loader_runner = Jdbc::SingleLoadRunner.new(*runner_args)
206
215
  @loader_runner.initial_load
@@ -3,12 +3,12 @@ require "logstash/inputs/base"
3
3
  require "logstash/namespace"
4
4
  require "logstash/plugin_mixins/jdbc/common"
5
5
  require "logstash/plugin_mixins/jdbc/jdbc"
6
- require "logstash/plugin_mixins/jdbc/scheduler"
7
6
  require "logstash/plugin_mixins/ecs_compatibility_support"
8
7
  require "logstash/plugin_mixins/ecs_compatibility_support/target_check"
9
8
  require "logstash/plugin_mixins/validator_support/field_reference_validation_adapter"
10
-
11
9
  require "logstash/plugin_mixins/event_support/event_factory_adapter"
10
+ require "logstash/plugin_mixins/scheduler"
11
+ require "fileutils"
12
12
 
13
13
  # this require_relative returns early unless the JRuby version is between 9.2.0.0 and 9.2.8.0
14
14
  require_relative "tzinfo_jruby_patch"
@@ -146,6 +146,8 @@ module LogStash module Inputs class Jdbc < LogStash::Inputs::Base
146
146
  # adds :field_reference validator adapter
147
147
  extend LogStash::PluginMixins::ValidatorSupport::FieldReferenceValidationAdapter
148
148
 
149
+ include LogStash::PluginMixins::Scheduler
150
+
149
151
  config_name "jdbc"
150
152
 
151
153
  # If undefined, Logstash will complain, even if codec is unused.
@@ -178,8 +180,10 @@ module LogStash module Inputs class Jdbc < LogStash::Inputs::Base
178
180
  # exactly once.
179
181
  config :schedule, :validate => :string
180
182
 
181
- # Path to file with last run time
182
- config :last_run_metadata_path, :validate => :string, :default => "#{ENV['HOME']}/.logstash_jdbc_last_run"
183
+ # Path to file with last run time.
184
+ # The default will write file to `<path.data>/plugins/inputs/jdbc/logstash_jdbc_last_run`
185
+ # NOTE: it must be a file path and not a directory path
186
+ config :last_run_metadata_path, :validate => :string
183
187
 
184
188
  # Use an incremental column value rather than a timestamp
185
189
  config :use_column_value, :validate => :boolean, :default => false
@@ -230,13 +234,32 @@ module LogStash module Inputs class Jdbc < LogStash::Inputs::Base
230
234
  config :target, :validate => :field_reference, :required => false
231
235
 
232
236
  attr_reader :database # for test mocking/stubbing
237
+ attr_reader :last_run_metadata_file_path # path to the file used as last run storage
233
238
 
234
239
  public
235
240
 
236
241
  def register
237
242
  @logger = self.logger
238
- require "rufus/scheduler"
239
- prepare_jdbc_connection
243
+
244
+ if @record_last_run
245
+ if @last_run_metadata_path.nil?
246
+ logstash_data_path = LogStash::SETTINGS.get_value("path.data")
247
+ logstash_data_path = Pathname.new(logstash_data_path).join("plugins", "inputs", "jdbc")
248
+ # Ensure that the filepath exists before writing, since it's deeply nested.
249
+ logstash_data_path.mkpath
250
+ logstash_data_file_path = logstash_data_path.join("logstash_jdbc_last_run")
251
+
252
+ ensure_default_metadatafile_location(logstash_data_file_path)
253
+
254
+ @last_run_metadata_file_path = logstash_data_file_path.to_path
255
+ else
256
+ # validate the path is a file and not a directory
257
+ if Pathname.new(@last_run_metadata_path).directory?
258
+ raise LogStash::ConfigurationError.new("The \"last_run_metadata_path\" argument must point to a file, received a directory: \"#{last_run_metadata_path}\"")
259
+ end
260
+ @last_run_metadata_file_path = @last_run_metadata_path
261
+ end
262
+ end
240
263
 
241
264
  if @use_column_value
242
265
  # Raise an error if @use_column_value is true, but no @tracking_column is set
@@ -259,8 +282,8 @@ module LogStash module Inputs class Jdbc < LogStash::Inputs::Base
259
282
  end
260
283
  end
261
284
 
262
- set_value_tracker(LogStash::PluginMixins::Jdbc::ValueTracking.build_last_value_tracker(self))
263
- set_statement_logger(LogStash::PluginMixins::Jdbc::CheckedCountLogger.new(@logger))
285
+ set_value_tracker LogStash::PluginMixins::Jdbc::ValueTracking.build_last_value_tracker(self)
286
+ set_statement_handler LogStash::PluginMixins::Jdbc::StatementHandler.build_statement_handler(self)
264
287
 
265
288
  @enable_encoding = !@charset.nil? || !@columns_charset.empty?
266
289
 
@@ -280,11 +303,25 @@ module LogStash module Inputs class Jdbc < LogStash::Inputs::Base
280
303
  converters[encoding] = converter
281
304
  end
282
305
  end
306
+
307
+ load_driver
308
+ begin
309
+ open_jdbc_connection
310
+ rescue Sequel::DatabaseConnectionError,
311
+ Sequel::DatabaseError,
312
+ Sequel::InvalidValue,
313
+ Java::JavaSql::SQLException => e
314
+ details = { exception: e.class, message: e.message }
315
+ details[:cause] = e.cause.inspect if e.cause
316
+ details[:backtrace] = e.backtrace if @logger.debug?
317
+ @logger.warn("Exception when executing JDBC query", details)
318
+ raise(LogStash::ConfigurationError, "Can't create a connection pool to the database")
319
+ end
283
320
  end # def register
284
321
 
285
322
  # test injection points
286
- def set_statement_logger(instance)
287
- @statement_handler = LogStash::PluginMixins::Jdbc::StatementHandler.build_statement_handler(self, instance)
323
+ def set_statement_handler(handler)
324
+ @statement_handler = handler
288
325
  end
289
326
 
290
327
  def set_value_tracker(instance)
@@ -292,24 +329,17 @@ module LogStash module Inputs class Jdbc < LogStash::Inputs::Base
292
329
  end
293
330
 
294
331
  def run(queue)
295
- load_driver
296
332
  if @schedule
297
- # input thread (Java) name example "[my-oracle]<jdbc"
298
- @scheduler = LogStash::PluginMixins::Jdbc::Scheduler.
299
- start_cron_scheduler(@schedule, thread_name: "[#{id}]<jdbc__scheduler") { execute_query(queue) }
300
- @scheduler.join
333
+ # scheduler input thread name example: "[my-oracle]|input|jdbc|scheduler"
334
+ scheduler.cron(@schedule) { execute_query(queue) }
335
+ scheduler.join
301
336
  else
302
337
  execute_query(queue)
303
338
  end
304
339
  end # def run
305
340
 
306
- def close
307
- @scheduler.shutdown if @scheduler
308
- end
309
-
310
341
  def stop
311
342
  close_jdbc_connection
312
- @scheduler.shutdown(:wait) if @scheduler
313
343
  end
314
344
 
315
345
  private
@@ -363,4 +393,23 @@ module LogStash module Inputs class Jdbc < LogStash::Inputs::Base
363
393
  value
364
394
  end
365
395
  end
396
+
397
+ def ensure_default_metadatafile_location(metadata_new_path)
398
+ old_default_path = Pathname.new("#{ENV['HOME']}/.logstash_jdbc_last_run")
399
+
400
+ if old_default_path.exist? && !metadata_new_path.exist?
401
+ # Previous versions of the plugin hosted the state file into $HOME/.logstash_jdbc_last_run.
402
+ # Copy in the new location
403
+ FileUtils.cp(old_default_path.to_path, metadata_new_path.to_path)
404
+ begin
405
+ # If there is a permission error in the delete of the old file inform the user to give
406
+ # the correct access rights
407
+ ::File.delete(old_default_path.to_path)
408
+ @logger.info("Successfully moved the #{old_default_path.to_path} into #{metadata_new_path.to_path}")
409
+ rescue => e
410
+ @logger.warn("Using new metadata file at #{metadata_new_path.to_path} but #{old_default_path} can't be removed.")
411
+ end
412
+ end
413
+ end
414
+
366
415
  end end end # class LogStash::Inputs::Jdbc
@@ -24,7 +24,8 @@ module LogStash module PluginMixins module Jdbc
24
24
  return @driver_impl if @driver_impl ||= nil
25
25
 
26
26
  require "java"
27
- require "sequel"
27
+
28
+ require_relative "sequel_bootstrap"
28
29
  require "sequel/adapters/jdbc"
29
30
 
30
31
  # execute all the driver loading related duties in a serial fashion to avoid
@@ -4,7 +4,7 @@ require "logstash/config/mixin"
4
4
  require "time"
5
5
  require "date"
6
6
  require_relative "value_tracking"
7
- require_relative "checked_count_logger"
7
+ require_relative "timezone_proxy"
8
8
  require_relative "statement_handler"
9
9
 
10
10
  java_import java.util.concurrent.locks.ReentrantLock
@@ -83,7 +83,8 @@ module LogStash module PluginMixins module Jdbc
83
83
  # Using this setting will manually assign a specified timezone offset, instead
84
84
  # of using the timezone setting of the local machine. You must use a canonical
85
85
  # timezone, *America/Denver*, for example.
86
- config :jdbc_default_timezone, :validate => :string
86
+ config :jdbc_default_timezone, :validate => :jdbc_timezone_spec
87
+ extend TimezoneProxy::JDBCTimezoneSpecValidator
87
88
 
88
89
  # General/Vendor-specific Sequel configuration options.
89
90
  #
@@ -103,6 +104,11 @@ module LogStash module PluginMixins module Jdbc
103
104
  # Number of seconds to sleep between connection attempts
104
105
  config :connection_retry_attempts_wait_time, :validate => :number, :default => 0.5
105
106
 
107
+ # Maximum number of times to try running statement
108
+ config :statement_retry_attempts, :validate => :number, :default => 1
109
+ # Number of seconds to sleep between statement execution
110
+ config :statement_retry_attempts_wait_time, :validate => :number, :default => 0.5
111
+
106
112
  # give users the ability to force Sequel application side into using local timezone
107
113
  config :plugin_timezone, :validate => ["local", "utc"], :default => "utc"
108
114
  end
@@ -158,7 +164,7 @@ module LogStash module PluginMixins module Jdbc
158
164
  @database.extension(:pagination)
159
165
  if @jdbc_default_timezone
160
166
  @database.extension(:named_timezones)
161
- @database.timezone = @jdbc_default_timezone
167
+ @database.timezone = TimezoneProxy.load(@jdbc_default_timezone)
162
168
  end
163
169
  if @jdbc_validate_connection
164
170
  @database.extension(:connection_validator)
@@ -187,31 +193,24 @@ module LogStash module PluginMixins module Jdbc
187
193
  end
188
194
  end
189
195
 
190
- public
191
- def prepare_jdbc_connection
192
- @connection_lock = ReentrantLock.new
193
- end
194
-
195
196
  public
196
197
  def close_jdbc_connection
197
198
  begin
198
199
  # pipeline restarts can also close the jdbc connection, block until the current executing statement is finished to avoid leaking connections
199
200
  # connections in use won't really get closed
200
- @connection_lock.lock
201
201
  @database.disconnect if @database
202
202
  rescue => e
203
203
  @logger.warn("Failed to close connection", :exception => e)
204
- ensure
205
- @connection_lock.unlock
206
204
  end
207
205
  end
208
206
 
209
207
  public
210
208
  def execute_statement
211
209
  success = false
210
+ retry_attempts = @statement_retry_attempts
211
+
212
212
  begin
213
- @connection_lock.lock
214
- open_jdbc_connection
213
+ retry_attempts -= 1
215
214
  sql_last_value = @use_column_value ? @value_tracker.value : Time.now.utc
216
215
  @tracking_column_warning_sent = false
217
216
  @statement_handler.perform_query(@database, @value_tracker.value) do |row|
@@ -219,17 +218,23 @@ module LogStash module PluginMixins module Jdbc
219
218
  yield extract_values_from(row)
220
219
  end
221
220
  success = true
222
- rescue Sequel::DatabaseConnectionError, Sequel::DatabaseError, Java::JavaSql::SQLException => e
221
+ rescue Sequel::Error, Java::JavaSql::SQLException => e
223
222
  details = { exception: e.class, message: e.message }
224
223
  details[:cause] = e.cause.inspect if e.cause
225
224
  details[:backtrace] = e.backtrace if @logger.debug?
226
225
  @logger.warn("Exception when executing JDBC query", details)
226
+
227
+ if retry_attempts == 0
228
+ @logger.error("Unable to execute statement. Tried #{@statement_retry_attempts} times.")
229
+ else
230
+ @logger.error("Unable to execute statement. Trying again.")
231
+ sleep(@statement_retry_attempts_wait_time)
232
+ retry
233
+ end
227
234
  else
228
235
  @value_tracker.set_value(sql_last_value)
229
- ensure
230
- close_jdbc_connection
231
- @connection_lock.unlock
232
236
  end
237
+
233
238
  return success
234
239
  end
235
240
 
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+
3
+ require "sequel"
4
+
5
+ # prevent Sequel's datetime_class from being modified,
6
+ # and ensure behaviour is restored to the library's default
7
+ # if something else in the Ruby VM has already changed it.
8
+ Sequel.synchronize do
9
+ def Sequel.datetime_class=(klass)
10
+ # noop
11
+ end
12
+ def Sequel.datetime_class
13
+ ::Time
14
+ end
15
+ end
16
+
17
+ # load the named_timezones extension, which will attempt to
18
+ # override the global Sequel::datetime_class; for safety,
19
+ # we reset it once more.
20
+ Sequel.extension(:named_timezones)
21
+ Sequel.datetime_class = ::Time