fluent-plugin-mysql 0.1.5 → 0.2.0

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
  SHA1:
3
- metadata.gz: b6ce2ad3922449224b551869fffcf66e65444849
4
- data.tar.gz: b912540a7fe49953ac5f52ef17573c001d110fc9
3
+ metadata.gz: 6c8d2f2fd5a6164a01413ca4d9179d6a93d8b4f0
4
+ data.tar.gz: 05a99dbf24a3458cbd2031f87da2b1cd154c7d66
5
5
  SHA512:
6
- metadata.gz: a8d3a56d4a190b8be00e5f610a14b33f1713880d208a94d922ae616b9621642d4e638b92819e62170dd071af4784eb5817f189dfc9287128a0243e77a1ed3dd9
7
- data.tar.gz: 61934977b358d2eaab543e2905f036dd3e12e738527683c8e7a6acbbb1ff48e2e64f2168a931cde7b94470bcd55717512379f8d20eedc9cf13e850a442abb4dc
6
+ metadata.gz: 1aaf277b0400baf6235a2a1ebfa954e55c3f1b09f63c81f524f1d8af2751eb9e53de9f0828630de1e2bf8c36468445a51951e88f6823528b18db4716226c0a75
7
+ data.tar.gz: f1889ff991d71cc77e0c35201ed8bd169464e118671865ad839717d97082b3e9118c416342f568a2e2506f290760da350034c5e9835cf5120a468a64c1caf774
data/.travis.yml CHANGED
@@ -1,7 +1,6 @@
1
1
  language: ruby
2
2
 
3
3
  rvm:
4
- - 2.0.0
5
4
  - 2.1
6
5
  - 2.2.3
7
6
  - 2.3.0
@@ -9,11 +8,6 @@ rvm:
9
8
  gemfile:
10
9
  - Gemfile
11
10
 
12
- matrix:
13
- exclude:
14
- - rvm: 2.0.0
15
- gemfile: Gemfile
16
-
17
11
  before_install:
18
12
  - gem update bundler
19
13
 
data/README.md CHANGED
@@ -8,6 +8,8 @@ fluent-plugin-mysql-bulk merged this repository.
8
8
 
9
9
  [mysql plugin](README_mysql.md) is deprecated. You should use mysql_bulk.
10
10
 
11
+ v0.1.5 only supports fluentd-0.12.X and v0.2.0 only supports fluentd-0.14.X.
12
+
11
13
  ## Parameters
12
14
 
13
15
  param|value
@@ -228,6 +230,92 @@ then `created_at` column is set from time attribute in a fluentd packet with tim
228
230
  +-----+-----------+---------------------+
229
231
  ```
230
232
 
233
+ ## Configuration Example(bulk insert with tag placeholder for table name)
234
+
235
+ This description is for v0.14.X users.
236
+
237
+ ```
238
+ <match mysql.input>
239
+ @type mysql_bulk
240
+ host localhost
241
+ database test_app_development
242
+ username root
243
+ password hogehoge
244
+ column_names id,user_name,created_at
245
+ key_names id,user,${time}
246
+ table users_${tag}
247
+ <buffer tag>
248
+ @type memory
249
+ flush_interval 60s
250
+ </buffer>
251
+ </match>
252
+ ```
253
+
254
+ Assume following input is coming:
255
+
256
+ ```js
257
+ 2016-09-26 18:42:13+09:00: mysql.input: {"user":"toyama","dummy":"hogehoge"}
258
+ 2016-09-26 18:42:16+09:00: mysql.input: {"user":"toyama2","dummy":"hogehoge"}
259
+ 2016-09-26 18:42:19+09:00: mysql.input: {"user":"toyama3","dummy":"hogehoge"}
260
+ ```
261
+
262
+ then `created_at` column is set from time attribute in a fluentd packet:
263
+
264
+ ```sql
265
+ mysql> select * from users_mysql_input;
266
+ +----+-----------+---------------------+
267
+ | id | user_name | created_at |
268
+ +----+-----------+---------------------+
269
+ | 1 | toyama | 2016-09-26 18:42:13 |
270
+ | 2 | toyama2 | 2016-09-26 18:42:16 |
271
+ | 3 | toyama3 | 2016-09-26 18:42:19 |
272
+ +----+-----------+---------------------+
273
+ 3 rows in set (0.00 sec)
274
+ ```
275
+
276
+ ## Configuration Example(bulk insert with time format placeholder for table name)
277
+
278
+ This description is for v0.14.X users.
279
+
280
+ ```
281
+ <match mysql.input>
282
+ @type mysql_bulk
283
+ host localhost
284
+ database test_app_development
285
+ username root
286
+ password hogehoge
287
+ column_names id,user_name,created_at
288
+ key_names id,user,${time}
289
+ table users_%Y%m%d
290
+ <buffer time>
291
+ @type memory
292
+ timekey 60s
293
+ timekey_wait 60s
294
+ </buffer>
295
+ </match>
296
+ ```
297
+
298
+ Assume following input is coming:
299
+
300
+ ```js
301
+ 2016-09-26 18:37:06+09:00: mysql.input: {"user":"toyama","dummy":"hogehoge"}
302
+ 2016-09-26 18:37:08+09:00: mysql.input: {"user":"toyama2","dummy":"hogehoge"}
303
+ 2016-09-26 18:37:11+09:00: mysql.input: {"user":"toyama3","dummy":"hogehoge"}
304
+ ```
305
+
306
+ then `created_at` column is set from time attribute in a fluentd packet:
307
+
308
+ ```sql
309
+ mysql> select * from users_20160926;
310
+ +----+-----------+---------------------+
311
+ | id | user_name | created_at |
312
+ +----+-----------+---------------------+
313
+ | 1 | toyama | 2016-09-26 18:37:06 |
314
+ | 2 | toyama2 | 2016-09-26 18:37:08 |
315
+ | 3 | toyama3 | 2016-09-26 18:37:11 |
316
+ +----+-----------+---------------------+
317
+ 3 rows in set (0.00 sec)
318
+ ```
231
319
 
232
320
  ## spec
233
321
 
@@ -1,7 +1,7 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  Gem::Specification.new do |gem|
3
3
  gem.name = "fluent-plugin-mysql"
4
- gem.version = "0.1.5"
4
+ gem.version = "0.2.0"
5
5
  gem.authors = ["TAGOMORI Satoshi", "Toyama Hiroshi"]
6
6
  gem.email = ["tagomoris@gmail.com", "toyama0919@gmail.com"]
7
7
  gem.description = %q{fluent plugin to insert mysql as json(single column) or insert statement}
@@ -14,9 +14,10 @@ Gem::Specification.new do |gem|
14
14
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
15
  gem.require_paths = ["lib"]
16
16
 
17
- gem.add_runtime_dependency "fluentd", '~> 0.12.0'
17
+ gem.add_runtime_dependency "fluentd", ['>= 0.14.8', '< 2']
18
18
  gem.add_runtime_dependency "mysql2-cs-bind"
19
19
  gem.add_runtime_dependency "jsonpath"
20
20
  gem.add_development_dependency "rake"
21
21
  gem.add_development_dependency "test-unit"
22
+ gem.add_development_dependency "timecop", "~> 0.8.0"
22
23
  end
@@ -1,36 +1,38 @@
1
1
  # -*- encoding : utf-8 -*-
2
- module Fluent
3
- class Fluent::MysqlBulkOutput < Fluent::BufferedOutput
2
+ require 'fluent/plugin/output'
3
+
4
+ module Fluent::Plugin
5
+ class MysqlBulkOutput < Output
4
6
  Fluent::Plugin.register_output('mysql_bulk', self)
5
7
 
6
- include Fluent::SetTimeKeyMixin
8
+ helpers :compat_parameters, :inject
7
9
 
8
10
  config_param :host, :string, default: '127.0.0.1',
9
- :desc => "Database host."
11
+ desc: "Database host."
10
12
  config_param :port, :integer, default: 3306,
11
- :desc => "Database port."
13
+ desc: "Database port."
12
14
  config_param :database, :string,
13
- :desc => "Database name."
15
+ desc: "Database name."
14
16
  config_param :username, :string,
15
- :desc => "Database user."
17
+ desc: "Database user."
16
18
  config_param :password, :string, default: '', secret: true,
17
- :desc => "Database password."
19
+ desc: "Database password."
18
20
 
19
21
  config_param :column_names, :string,
20
- :desc => "Bulk insert column."
22
+ desc: "Bulk insert column."
21
23
  config_param :key_names, :string, default: nil,
22
- :desc => <<-DESC
24
+ desc: <<-DESC
23
25
  Value key names, ${time} is placeholder Time.at(time).strftime("%Y-%m-%d %H:%M:%S").
24
26
  DESC
25
27
  config_param :json_key_names, :string, default: nil,
26
- :desc => "Key names which store data as json"
28
+ desc: "Key names which store data as json"
27
29
  config_param :table, :string,
28
- :desc => "Bulk insert table."
30
+ desc: "Bulk insert table."
29
31
 
30
32
  config_param :on_duplicate_key_update, :bool, default: false,
31
- :desc => "On duplicate key update enable."
33
+ desc: "On duplicate key update enable."
32
34
  config_param :on_duplicate_update_keys, :string, default: nil,
33
- :desc => "On duplicate key update column, comma separator."
35
+ desc: "On duplicate key update column, comma separator."
34
36
 
35
37
  attr_accessor :handler
36
38
 
@@ -39,12 +41,8 @@ DESC
39
41
  require 'mysql2-cs-bind'
40
42
  end
41
43
 
42
- # Define `log` method for v0.10.42 or earlier
43
- unless method_defined?(:log)
44
- define_method("log") { $log }
45
- end
46
-
47
44
  def configure(conf)
45
+ compat_parameters_convert(conf, :buffer, :inject)
48
46
  super
49
47
 
50
48
  if @column_names.nil?
@@ -72,8 +70,11 @@ DESC
72
70
 
73
71
  def start
74
72
  super
75
- result = client.xquery("SHOW COLUMNS FROM #{@table}")
76
- @max_lengths = []
73
+ end
74
+
75
+ def check_table_schema(database: @database, table: @table)
76
+ result = client(database).xquery("SHOW COLUMNS FROM #{table}")
77
+ max_lengths = []
77
78
  @column_names.each do |column|
78
79
  info = result.select { |x| x['Field'] == column }.first
79
80
  r = /(char|varchar)\(([\d]+)\)/
@@ -82,8 +83,9 @@ DESC
82
83
  rescue
83
84
  max_length = nil
84
85
  end
85
- @max_lengths << max_length
86
+ max_lengths << max_length
86
87
  end
88
+ max_lengths
87
89
  end
88
90
 
89
91
  def shutdown
@@ -91,31 +93,45 @@ DESC
91
93
  end
92
94
 
93
95
  def format(tag, time, record)
94
- [tag, time, format_proc.call(tag, time, record)].to_msgpack
96
+ record = inject_values_to_record(tag, time, record)
97
+ [tag, time, record].to_msgpack
95
98
  end
96
99
 
97
- def client
100
+ def formatted_to_msgpack_binary
101
+ true
102
+ end
103
+
104
+ def client(database)
98
105
  Mysql2::Client.new(
99
106
  host: @host,
100
107
  port: @port,
101
108
  username: @username,
102
109
  password: @password,
103
- database: @database,
110
+ database: database,
104
111
  flags: Mysql2::Client::MULTI_STATEMENTS
105
112
  )
106
113
  end
107
114
 
115
+ def expand_placeholders(metadata)
116
+ database = extract_placeholders(@database, metadata).gsub('.', '_')
117
+ table = extract_placeholders(@table, metadata).gsub('.', '_')
118
+ return database, table
119
+ end
120
+
108
121
  def write(chunk)
109
- @handler = client
122
+ database, table = expand_placeholders(chunk.metadata)
123
+ max_lengths = check_table_schema(database: database, table: table)
124
+ @handler = client(database)
110
125
  values = []
111
126
  values_template = "(#{ @column_names.map { |key| '?' }.join(',') })"
112
127
  chunk.msgpack_each do |tag, time, data|
128
+ data = format_proc.call(tag, time, data, max_lengths)
113
129
  values << Mysql2::Client.pseudo_bind(values_template, data)
114
130
  end
115
- sql = "INSERT INTO #{@table} (#{@column_names.join(',')}) VALUES #{values.join(',')}"
131
+ sql = "INSERT INTO #{table} (#{@column_names.join(',')}) VALUES #{values.join(',')}"
116
132
  sql += @on_duplicate_key_update_sql if @on_duplicate_key_update
117
133
 
118
- log.info "bulk insert values size (table: #{@table}) => #{values.size}"
134
+ log.info "bulk insert values size (table: #{table}) => #{values.size}"
119
135
  @handler.xquery(sql)
120
136
  @handler.close
121
137
  end
@@ -123,16 +139,16 @@ DESC
123
139
  private
124
140
 
125
141
  def format_proc
126
- proc do |tag, time, record|
142
+ proc do |tag, time, record, max_lengths|
127
143
  values = []
128
144
  @key_names.each_with_index do |key, i|
129
145
  if key == '${time}'
130
146
  value = Time.at(time).strftime('%Y-%m-%d %H:%M:%S')
131
147
  else
132
- if @max_lengths[i].nil? || record[key].nil?
148
+ if max_lengths[i].nil? || record[key].nil?
133
149
  value = record[key]
134
150
  else
135
- value = record[key].slice(0, @max_lengths[i])
151
+ value = record[key].slice(0, max_lengths[i])
136
152
  end
137
153
 
138
154
  if @json_key_names && @json_key_names.include?(key)
@@ -1,13 +1,32 @@
1
+ # coding: utf-8
1
2
  require 'helper'
2
3
  require 'mysql2-cs-bind'
4
+ require 'fluent/test/driver/output'
5
+ require 'fluent/plugin/buffer'
6
+ require 'fluent/config'
7
+ require 'time'
8
+ require 'timecop'
3
9
 
4
10
  class MysqlBulkOutputTest < Test::Unit::TestCase
5
11
  def setup
6
12
  Fluent::Test.setup
7
13
  end
8
14
 
9
- def create_driver(conf = CONFIG, tag = 'test')
10
- d = Fluent::Test::BufferedOutputTestDriver.new(Fluent::MysqlBulkOutput, tag).configure(conf)
15
+ def config_element(name = 'test', argument = '', params = {}, elements = [])
16
+ Fluent::Config::Element.new(name, argument, params, elements)
17
+ end
18
+
19
+ CONFIG = %[
20
+ database test_app_development
21
+ username root
22
+ password hogehoge
23
+ column_names id,user_name,created_at
24
+ key_names id,users,created_at
25
+ table users
26
+ ]
27
+
28
+ def create_driver(conf = CONFIG)
29
+ d = Fluent::Test::Driver::Output.new(Fluent::Plugin::MysqlBulkOutput).configure(conf)
11
30
  d.instance.instance_eval {
12
31
  def client
13
32
  obj = Object.new
@@ -21,9 +40,88 @@ class MysqlBulkOutputTest < Test::Unit::TestCase
21
40
  d
22
41
  end
23
42
 
43
+ def create_metadata(timekey: nil, tag: nil, variables: nil)
44
+ Fluent::Plugin::Buffer::Metadata.new(timekey, tag, variables)
45
+ end
46
+
47
+ class TestExpandPlaceholders < self
48
+ data("table" => {"database" => "test_app_development",
49
+ "table" => "users_${tag}",
50
+ "extracted_database" => "test_app_development",
51
+ "extracted_table" => "users_input_mysql"
52
+ },
53
+ "database" => {"database" => "test_app_development_${tag}",
54
+ "table" => "users",
55
+ "extracted_database" => "test_app_development_input_mysql",
56
+ "extracted_table" => "users"
57
+ },
58
+ )
59
+ def test_expand_tag_placeholder(data)
60
+ config = config_element('ROOT', '', {
61
+ "@type" => "mysql_bulk",
62
+ "host" => "localhost",
63
+ "database" => data["database"],
64
+ "username" => "root",
65
+ "password" => "hogehoge",
66
+ "column_names" => "id,user_name,created_at",
67
+ "table" => data["table"],
68
+ }, [config_element('buffer', 'tag', {
69
+ "@type" => "memory",
70
+ "flush_interval" => "60s",
71
+ }, [])])
72
+ d = create_driver(config)
73
+ time = Time.now
74
+ metadata = create_metadata(timekey: time.to_i, tag: 'input.mysql')
75
+ database, table = d.instance.expand_placeholders(metadata)
76
+ assert_equal(data["extracted_database"], database)
77
+ assert_equal(data["extracted_table"], table)
78
+ end
79
+
80
+ def setup
81
+ Timecop.freeze(Time.parse("2016-09-26"))
82
+ end
83
+
84
+ data("table" => {"database" => "test_app_development",
85
+ "table" => "users_%Y%m%d",
86
+ "extracted_database" => "test_app_development",
87
+ "extracted_table" => "users_20160926"
88
+ },
89
+ "database" => {"database" => "test_app_development_%Y%m%d",
90
+ "table" => "users",
91
+ "extracted_database" => "test_app_development_20160926",
92
+ "extracted_table" => "users"
93
+ },
94
+ )
95
+ def test_expand_time_placeholder(data)
96
+ config = config_element('ROOT', '', {
97
+ "@type" => "mysql_bulk",
98
+ "host" => "localhost",
99
+ "database" => data["database"],
100
+ "username" => "root",
101
+ "password" => "hogehoge",
102
+ "column_names" => "id,user_name,created_at",
103
+ "table" => data["table"],
104
+ }, [config_element('buffer', 'time', {
105
+ "@type" => "memory",
106
+ "timekey" => "60s",
107
+ "timekey_wait" => "60s"
108
+ }, [])])
109
+ d = create_driver(config)
110
+ time = Time.now
111
+ metadata = create_metadata(timekey: time.to_i, tag: 'input.mysql')
112
+ database, table = d.instance.expand_placeholders(metadata)
113
+ assert_equal(data["extracted_database"], database)
114
+ assert_equal(data["extracted_table"], table)
115
+ end
116
+
117
+ def teardown
118
+ Timecop.return
119
+ end
120
+ end
121
+
24
122
  def test_configure_error
25
123
  assert_raise(Fluent::ConfigError) do
26
- d = create_driver %[
124
+ create_driver %[
27
125
  host localhost
28
126
  database test_app_development
29
127
  username root
@@ -36,7 +134,7 @@ class MysqlBulkOutputTest < Test::Unit::TestCase
36
134
  end
37
135
 
38
136
  assert_raise(Fluent::ConfigError) do
39
- d = create_driver %[
137
+ create_driver %[
40
138
  host localhost
41
139
  database test_app_development
42
140
  username root
@@ -49,7 +147,7 @@ class MysqlBulkOutputTest < Test::Unit::TestCase
49
147
  end
50
148
 
51
149
  assert_raise(Fluent::ConfigError) do
52
- d = create_driver %[
150
+ create_driver %[
53
151
  host localhost
54
152
  username root
55
153
  password hogehoge
@@ -65,7 +163,7 @@ class MysqlBulkOutputTest < Test::Unit::TestCase
65
163
  def test_configure
66
164
  # not define format(default csv)
67
165
  assert_nothing_raised(Fluent::ConfigError) do
68
- d = create_driver %[
166
+ create_driver %[
69
167
  host localhost
70
168
  database test_app_development
71
169
  username root
@@ -79,7 +177,7 @@ class MysqlBulkOutputTest < Test::Unit::TestCase
79
177
  end
80
178
 
81
179
  assert_nothing_raised(Fluent::ConfigError) do
82
- d = create_driver %[
180
+ create_driver %[
83
181
  database test_app_development
84
182
  username root
85
183
  password hogehoge
@@ -89,7 +187,7 @@ class MysqlBulkOutputTest < Test::Unit::TestCase
89
187
  end
90
188
 
91
189
  assert_nothing_raised(Fluent::ConfigError) do
92
- d = create_driver %[
190
+ create_driver %[
93
191
  database test_app_development
94
192
  username root
95
193
  password hogehoge
@@ -101,7 +199,7 @@ class MysqlBulkOutputTest < Test::Unit::TestCase
101
199
  end
102
200
 
103
201
  assert_nothing_raised(Fluent::ConfigError) do
104
- d = create_driver %[
202
+ create_driver %[
105
203
  database test_app_development
106
204
  username root
107
205
  password hogehoge
@@ -114,7 +212,7 @@ class MysqlBulkOutputTest < Test::Unit::TestCase
114
212
  end
115
213
 
116
214
  assert_nothing_raised(Fluent::ConfigError) do
117
- d = create_driver %[
215
+ create_driver %[
118
216
  database test_app_development
119
217
  username root
120
218
  password hogehoge
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-mysql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - TAGOMORI Satoshi
@@ -15,16 +15,22 @@ dependencies:
15
15
  name: fluentd
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - "~>"
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: 0.14.8
21
+ - - "<"
19
22
  - !ruby/object:Gem::Version
20
- version: 0.12.0
23
+ version: '2'
21
24
  type: :runtime
22
25
  prerelease: false
23
26
  version_requirements: !ruby/object:Gem::Requirement
24
27
  requirements:
25
- - - "~>"
28
+ - - ">="
26
29
  - !ruby/object:Gem::Version
27
- version: 0.12.0
30
+ version: 0.14.8
31
+ - - "<"
32
+ - !ruby/object:Gem::Version
33
+ version: '2'
28
34
  - !ruby/object:Gem::Dependency
29
35
  name: mysql2-cs-bind
30
36
  requirement: !ruby/object:Gem::Requirement
@@ -81,6 +87,20 @@ dependencies:
81
87
  - - ">="
82
88
  - !ruby/object:Gem::Version
83
89
  version: '0'
90
+ - !ruby/object:Gem::Dependency
91
+ name: timecop
92
+ requirement: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.8.0
97
+ type: :development
98
+ prerelease: false
99
+ version_requirements: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 0.8.0
84
104
  description: fluent plugin to insert mysql as json(single column) or insert statement
85
105
  email:
86
106
  - tagomoris@gmail.com