fluent-plugin-mysql-replicator 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ vendor/*
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 2.0.0
5
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in fluent-plugin-mysql-replicator.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,14 @@
1
+ Copyright (c) 2013- Kentaro Yoshida
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
14
+
data/README.md ADDED
@@ -0,0 +1,70 @@
1
+ # fluent-plugin-mysql-replicator [![Build Status](https://travis-ci.org/y-ken/fluent-plugin-mysql-replicator.png?branch=master)](https://travis-ci.org/y-ken/fluent-plugin-mysql-replicator)
2
+
3
+ ## Overview
4
+
5
+ Fluentd input plugin to track insert/update/delete event from MySQL database server.
6
+
7
+ ## Installation
8
+
9
+ `````
10
+ ### native gem
11
+ gem install fluent-plugin-mysql-replicator
12
+
13
+ ### td-agent gem
14
+ /usr/lib64/fluent/ruby/bin/fluent-gem install fluent-plugin-mysql-replicator
15
+ `````
16
+
17
+ ## Tutorial
18
+
19
+ #### configuration
20
+
21
+ `````
22
+ <source>
23
+ type mysql_replicator
24
+ host localhost
25
+ username your_mysql_user
26
+ password your_mysql_password
27
+ database myweb
28
+ interval 5s
29
+ tag replicator
30
+ query SELECT id, text from search_test
31
+ </source>
32
+
33
+ <match replicator.*>
34
+ type stdout
35
+ </match>
36
+ `````
37
+
38
+ #### sample query
39
+
40
+ `````
41
+ $ mysql -e "create database myweb"
42
+ $ mysql myweb -e "create table search_test(id int auto_increment, text text, PRIMARY KEY (id))"
43
+ $ sleep 10
44
+ $ mysql myweb -e "insert into search_test(text) values('aaa')"
45
+ $ sleep 10
46
+ $ mysql myweb -e "update search_test set text='bbb' where text = 'aaa'"
47
+ $ sleep 10
48
+ $ mysql myweb -e "delete from search_test where text='bbb'"
49
+ `````
50
+
51
+ #### result
52
+
53
+ `````
54
+ $ tail -f /var/log/td-agent/td-agent.log
55
+ 2013-11-25 18:22:25 +0900 replicator.insert: {"id":"1","text":"aaa"}
56
+ 2013-11-25 18:22:35 +0900 replicator.update: {"id":"1","text":"bbb"}
57
+ 2013-11-25 18:22:45 +0900 replicator.delete: {"id":"1"}
58
+ `````
59
+
60
+ ## TODO
61
+
62
+ Pull requests are very welcome!!
63
+
64
+ ## Copyright
65
+
66
+ Copyright © 2013- Kentaro Yoshida ([@yoshi_ken](https://twitter.com/yoshi_ken))
67
+
68
+ ## License
69
+
70
+ Apache License, Version 2.0
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+ Rake::TestTask.new(:test) do |test|
4
+ test.libs << 'lib' << 'test'
5
+ test.pattern = 'test/**/test_*.rb'
6
+ test.verbose = true
7
+ end
8
+
9
+ task :default => :test
@@ -0,0 +1,18 @@
1
+ # -*- encoding: utf-8 -*-
2
+ Gem::Specification.new do |s|
3
+ s.name = "fluent-plugin-mysql-replicator"
4
+ s.version = "0.0.1"
5
+ s.authors = ["Kentaro Yoshida"]
6
+ s.email = ["y.ken.studio@gmail.com"]
7
+ s.homepage = "https://github.com/y-ken/fluent-plugin-mysql-replicator"
8
+ s.summary = %q{Fluentd input plugin to track insert/update/delete event from MySQL database server.}
9
+
10
+ s.files = `git ls-files`.split("\n")
11
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
12
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
13
+ s.require_paths = ["lib"]
14
+
15
+ s.add_development_dependency "rake"
16
+ s.add_runtime_dependency "fluentd"
17
+ s.add_runtime_dependency "mysql2"
18
+ end
@@ -0,0 +1,109 @@
1
+ module Fluent
2
+ class MysqlReplicatorInput < Fluent::Input
3
+ Plugin.register_input('mysql_replicator', self)
4
+
5
+ def initialize
6
+ require 'mysql2'
7
+ require 'digest/sha1'
8
+ super
9
+ end
10
+
11
+ config_param :host, :string, :default => 'localhost'
12
+ config_param :port, :integer, :default => 3306
13
+ config_param :username, :string, :default => 'root'
14
+ config_param :password, :string, :default => nil
15
+ config_param :database, :string, :default => nil
16
+ config_param :encoding, :string, :default => 'utf8'
17
+ config_param :interval, :string, :default => '1m'
18
+ config_param :tag, :string
19
+ config_param :query, :string
20
+ config_param :primary_key, :string, :default => 'id'
21
+
22
+ def configure(conf)
23
+ super
24
+ @interval = Config.time_value(@interval)
25
+ $log.info "adding mysql_replicator job: [#{@query}] interval: #{@interval}sec"
26
+ end
27
+
28
+ def start
29
+ @thread = Thread.new(&method(:run))
30
+ end
31
+
32
+ def shutdown
33
+ Thread.kill(@thread)
34
+ end
35
+
36
+ def run
37
+ begin
38
+ poll
39
+ rescue StandardError => e
40
+ $log.error "error: #{e.message}"
41
+ $log.error e.backtrace.join("\n")
42
+ end
43
+ end
44
+
45
+ def poll
46
+ table_hash = Hash.new
47
+ ids = Array.new
48
+ loop do
49
+ previous_ids = ids
50
+ current_ids = Array.new
51
+ query(@query).each do |row|
52
+ current_ids << row[@primary_key]
53
+ current_hash = Digest::SHA1.hexdigest(row.flatten.join)
54
+ if !table_hash.include?(row[@primary_key])
55
+ emit_record(:insert, row)
56
+ elsif table_hash[row[@primary_key]] != current_hash
57
+ emit_record(:update, row)
58
+ end
59
+ table_hash[row[@primary_key]] = current_hash
60
+ end
61
+ ids = current_ids
62
+ deleted_ids = previous_ids - current_ids
63
+ if deleted_ids.count > 0
64
+ hash_delete_by_list(table_hash, deleted_ids)
65
+ deleted_ids.each {|id| emit_record(:delete, {@primary_key => id})}
66
+ end
67
+ sleep @interval
68
+ end
69
+ end
70
+
71
+ def hash_delete_by_list (hash, deleted_keys)
72
+ deleted_keys.each{|k| hash.delete(k)}
73
+ end
74
+
75
+ def emit_record(type, record)
76
+ tag = "#{@tag}.#{type.to_s}"
77
+ Engine.emit(tag, Engine.now, record)
78
+ end
79
+
80
+ def query(query)
81
+ @mysql ||= get_connection
82
+ begin
83
+ return @mysql.query(query, :cast => false, :cache_rows => false)
84
+ rescue Exception => e
85
+ $log.warn "mysql_replicator: #{e}"
86
+ sleep @interval
87
+ retry
88
+ end
89
+ end
90
+
91
+ def get_connection
92
+ begin
93
+ return Mysql2::Client.new({
94
+ :host => @host,
95
+ :port => @port,
96
+ :username => @username,
97
+ :password => @password,
98
+ :database => @database,
99
+ :encoding => @encoding,
100
+ :reconnect => true
101
+ })
102
+ rescue Exception => e
103
+ $log.warn "mysql_replicator: #{e}"
104
+ sleep @interval
105
+ retry
106
+ end
107
+ end
108
+ end
109
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,28 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+
12
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
13
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
14
+ require 'fluent/test'
15
+ unless ENV.has_key?('VERBOSE')
16
+ nulllogger = Object.new
17
+ nulllogger.instance_eval {|obj|
18
+ def method_missing(method, *args)
19
+ # pass
20
+ end
21
+ }
22
+ $log = nulllogger
23
+ end
24
+
25
+ require 'fluent/plugin/in_mysql_replicator'
26
+
27
+ class Test::Unit::TestCase
28
+ end
@@ -0,0 +1,38 @@
1
+ require 'helper'
2
+
3
+ class MysqlReplicatorInputTest < Test::Unit::TestCase
4
+ def setup
5
+ Fluent::Test.setup
6
+ end
7
+
8
+ CONFIG = %[
9
+ host localhost
10
+ port 3306
11
+ interval 30
12
+ tag input.mysql
13
+ query SELECT id, text from search_text
14
+ record_hostname yes
15
+ ]
16
+
17
+ def create_driver(conf=CONFIG,tag='test')
18
+ Fluent::Test::OutputTestDriver.new(Fluent::MysqlReplicatorInput, tag).configure(conf)
19
+ end
20
+
21
+ def test_configure
22
+ assert_raise(Fluent::ConfigError) {
23
+ d = create_driver('')
24
+ }
25
+ d = create_driver %[
26
+ host localhost
27
+ port 3306
28
+ interval 30
29
+ tag input.mysql
30
+ query SELECT id, text from search_text
31
+ ]
32
+ d.instance.inspect
33
+ assert_equal 'localhost', d.instance.host
34
+ assert_equal 3306, d.instance.port
35
+ assert_equal 30, d.instance.interval
36
+ assert_equal 'input.mysql', d.instance.tag
37
+ end
38
+ end
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fluent-plugin-mysql-replicator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Kentaro Yoshida
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-11-26 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: fluentd
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: mysql2
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description:
63
+ email:
64
+ - y.ken.studio@gmail.com
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - .gitignore
70
+ - .travis.yml
71
+ - Gemfile
72
+ - LICENSE
73
+ - README.md
74
+ - Rakefile
75
+ - fluent-plugin-mysql-replicator.gemspec
76
+ - lib/fluent/plugin/in_mysql_replicator.rb
77
+ - test/helper.rb
78
+ - test/plugin/test_in_mysql_replicator.rb
79
+ homepage: https://github.com/y-ken/fluent-plugin-mysql-replicator
80
+ licenses: []
81
+ post_install_message:
82
+ rdoc_options: []
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ! '>='
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ requirements: []
98
+ rubyforge_project:
99
+ rubygems_version: 1.8.23
100
+ signing_key:
101
+ specification_version: 3
102
+ summary: Fluentd input plugin to track insert/update/delete event from MySQL database
103
+ server.
104
+ test_files:
105
+ - test/helper.rb
106
+ - test/plugin/test_in_mysql_replicator.rb