fluent-plugin-mysql 0.1.5 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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