fluent-plugin-postgres-replicator 0.0.2 → 0.1.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 +4 -4
- data/.travis.yml +2 -2
- data/README.md +22 -7
- data/fluent-plugin-postgres-replicator.gemspec +2 -2
- data/lib/fluent/plugin/in_postgres_replicator.rb +103 -104
- data/test/plugin/test_in_postgres_replicator.rb +7 -9
- metadata +11 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8e07e9cc31d9517ca679f7d82b4e64c497a788c2
|
4
|
+
data.tar.gz: 234391df516fbe3a7b57c66c13b28d231d85d752
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 874a6e13a95cd16be2549b686db73c8bb00f0f5c0ec6c609828203cb38ffc585bb8e24c142f0b98640d205cac3335f4560ff32952aeb7b18df5a733cb2849463
|
7
|
+
data.tar.gz: d62069b79b4744282714d04361cf7f44ae38132af5677f35683449c0a64454f405d26b8fcbaefa389e99c87935b3599e22ff7df19efc61d33a30121a10d4afc9
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -5,28 +5,31 @@
|
|
5
5
|
|
6
6
|
Fluentd input plugin to track insert/update event from PostgreSQL.
|
7
7
|
|
8
|
+
## Requirements
|
9
|
+
|
10
|
+
| fluent-plugin-postgres-replicator | fluentd | ruby |
|
11
|
+
|-----------------------------------|---------|------|
|
12
|
+
| >= 0.1.0 | >= v0.14.0 | >= 2.1 |
|
13
|
+
| < 0.1.0 | >= v0.12.0 | >= 1.9 |
|
14
|
+
|
8
15
|
## Installation
|
9
16
|
|
10
17
|
```sh
|
11
|
-
# requirements
|
12
18
|
$ apt-get install libpq-dev
|
13
19
|
|
14
20
|
$ gem install fluent-plugin-postgres-replicator
|
15
21
|
```
|
16
22
|
|
17
|
-
##
|
18
|
-
|
19
|
-
In your Fluentd configuration, use `type postgres_replicator`.
|
20
|
-
Default values would look like this:
|
23
|
+
## Configuration
|
21
24
|
|
22
25
|
```
|
23
26
|
<source>
|
24
|
-
type postgres_replicator
|
27
|
+
@type postgres_replicator
|
25
28
|
host localhost
|
26
29
|
username pipeline
|
27
30
|
password pipeline
|
28
31
|
database pipeline
|
29
|
-
|
32
|
+
sql SELECT hour, project, total_pages from wiki_stats;
|
30
33
|
primary_keys hour,project
|
31
34
|
interval 1m
|
32
35
|
tag replicator.pipeline.wiki_stats.${event}.${primary_keys}
|
@@ -37,6 +40,18 @@ Default values would look like this:
|
|
37
40
|
|
38
41
|
Bug reports and pull requests are welcome.
|
39
42
|
|
43
|
+
You can run the test on your machine as below.
|
44
|
+
|
45
|
+
```console
|
46
|
+
$ docker run --name fluent-plugin-postgres-replicator-testing -e POSTGRES_DB=pg_repli_test_db -p 5432:5432 -d postgres:9.4.12-alpine
|
47
|
+
$ psql -c 'create table pg_repli_test_table (id int8 primary key, total int8) ;' -U postgres -h 127.0.0.1 -d pg_repli_test_db
|
48
|
+
$ psql -c 'insert into pg_repli_test_table values (1, 10) ;' -U postgres -h 127.0.0.1 -d pg_repli_test_db
|
49
|
+
$ psql -c 'insert into pg_repli_test_table values (2, 20) ;' -U postgres -h 127.0.0.1 -d pg_repli_test_db
|
50
|
+
|
51
|
+
$ bundle install --path vendor/bundle
|
52
|
+
$ bundle exec rake test
|
53
|
+
```
|
54
|
+
|
40
55
|
## License
|
41
56
|
|
42
57
|
- Copyright (c) 2016 innossh
|
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |gem|
|
6
6
|
gem.name = "fluent-plugin-postgres-replicator"
|
7
|
-
gem.version = "0.0
|
7
|
+
gem.version = "0.1.0"
|
8
8
|
gem.authors = ["innossh"]
|
9
9
|
gem.email = ["innossh@users.noreply.github.com"]
|
10
10
|
|
@@ -17,7 +17,7 @@ Gem::Specification.new do |gem|
|
|
17
17
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
18
|
gem.require_paths = ["lib"]
|
19
19
|
|
20
|
-
gem.add_runtime_dependency "fluentd"
|
20
|
+
gem.add_runtime_dependency "fluentd", [">= 0.14.0", "< 2"]
|
21
21
|
gem.add_runtime_dependency "pg"
|
22
22
|
gem.add_development_dependency "rake"
|
23
23
|
gem.add_development_dependency "test-unit"
|
@@ -1,126 +1,125 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
1
|
+
require 'fluent/plugin/input'
|
2
|
+
require 'pg'
|
3
|
+
require 'digest/sha1'
|
4
|
+
|
5
|
+
module Fluent::Plugin
|
6
|
+
class PostgresReplicatorInput < Input
|
7
|
+
Fluent::Plugin.register_input('postgres_replicator', self)
|
8
|
+
|
9
|
+
config_param :host, :string, :default => 'localhost'
|
10
|
+
config_param :port, :integer, :default => 5432
|
11
|
+
config_param :username, :string, :default => 'root'
|
12
|
+
config_param :password, :string, :default => nil, :secret => true
|
13
|
+
config_param :database, :string, :default => nil
|
14
|
+
config_param :sql, :string, :default => nil
|
15
|
+
config_param :primary_keys, :string
|
16
|
+
config_param :interval, :string, :default => '10s'
|
17
|
+
config_param :tag, :string
|
18
|
+
|
19
|
+
def configure(conf)
|
20
|
+
super
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
-
@interval = Fluent::Config.time_value(@interval)
|
23
|
-
if @primary_keys.nil?
|
24
|
-
raise Fluent::ConfigError, "primary_keys MUST be specified"
|
22
|
+
@interval = Fluent::Config.time_value(@interval)
|
23
|
+
@primary_keys = @primary_keys.split(/\s*,\s*/)
|
25
24
|
end
|
26
|
-
|
27
|
-
|
25
|
+
|
26
|
+
def start
|
27
|
+
super
|
28
|
+
|
29
|
+
@thread = Thread.new(&method(:run))
|
28
30
|
end
|
29
|
-
@primary_keys = @primary_keys.split(/\s*,\s*/)
|
30
|
-
end
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
end
|
32
|
+
def shutdown
|
33
|
+
Thread.kill(@thread)
|
35
34
|
|
36
|
-
|
37
|
-
|
38
|
-
end
|
35
|
+
super
|
36
|
+
end
|
39
37
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
38
|
+
def run
|
39
|
+
begin
|
40
|
+
poll
|
41
|
+
rescue StandardError => e
|
42
|
+
log.error "failed to execute query. error: #{e.message}"
|
43
|
+
log.error e.backtrace.join("\n")
|
44
|
+
end
|
46
45
|
end
|
47
|
-
end
|
48
46
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
47
|
+
def poll
|
48
|
+
hash_values = Hash.new
|
49
|
+
conn = get_connection()
|
50
|
+
loop do
|
51
|
+
rows_count = 0
|
52
|
+
start_time = Time.now
|
53
|
+
rows, conn = query(@sql, conn)
|
54
|
+
rows.each do |row|
|
55
|
+
row_ids = Array.new
|
56
|
+
@primary_keys.each do |primary_key|
|
57
|
+
if !row[primary_key].nil?
|
58
|
+
row_ids << row[primary_key]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
if row_ids.size != @primary_keys.size
|
62
|
+
log.error "primary_keys column value is something wrong. :tag=>#{@tag} :primary_keys=>#{@primary_keys}"
|
63
|
+
break
|
61
64
|
end
|
62
|
-
end
|
63
|
-
if row_ids.size != @primary_keys.size
|
64
|
-
log.error "primary_keys column value is something wrong. :tag=>#{@tag} :primary_keys=>#{@primary_keys}"
|
65
|
-
break
|
66
|
-
end
|
67
65
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
66
|
+
hash_value_id = row_ids.join('_')
|
67
|
+
hash_value = Digest::SHA1.hexdigest(row.flatten.join)
|
68
|
+
if !hash_values.include?(hash_value_id)
|
69
|
+
tag = format_tag(@tag, {:event => :insert})
|
70
|
+
emit_record(tag, row)
|
71
|
+
elsif hash_values[hash_value_id] != hash_value
|
72
|
+
tag = format_tag(@tag, {:event => :update})
|
73
|
+
emit_record(tag, row)
|
74
|
+
end
|
75
|
+
hash_values[hash_value_id] = hash_value
|
76
|
+
rows_count += 1
|
76
77
|
end
|
77
|
-
|
78
|
-
|
78
|
+
conn.close
|
79
|
+
elapsed_time = sprintf('%0.02f', Time.now - start_time)
|
80
|
+
log.info "success to execute replicator. :tag=>#{@tag} :rows_count=>#{rows_count} :elapsed_time=>#{elapsed_time} sec"
|
81
|
+
sleep @interval
|
79
82
|
end
|
80
|
-
conn.close
|
81
|
-
elapsed_time = sprintf('%0.02f', Time.now - start_time)
|
82
|
-
log.info "success to execute replicator. :tag=>#{@tag} :rows_count=>#{rows_count} :elapsed_time=>#{elapsed_time} sec"
|
83
|
-
sleep @interval
|
84
|
-
end
|
85
83
|
|
86
|
-
|
84
|
+
end
|
87
85
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
86
|
+
def query(sql, conn = nil)
|
87
|
+
begin
|
88
|
+
conn = (conn.nil? || conn.finished?) ? get_connection : conn
|
89
|
+
return conn.query(sql), conn
|
90
|
+
rescue Exception => e
|
91
|
+
log.warn "failed to execute query and will retry. error: #{e}"
|
92
|
+
sleep @interval
|
93
|
+
retry
|
94
|
+
end
|
96
95
|
end
|
97
|
-
end
|
98
96
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
97
|
+
def format_tag(tag, param)
|
98
|
+
pattern = {'${event}' => param[:event].to_s, '${primary_keys}' => @primary_keys.join('_')}
|
99
|
+
tag.gsub(/(\${[a-z_]+})/) do
|
100
|
+
log.warn "placeholder value is not found. :tag=>#{tag} :placeholder=>#{$1}" unless pattern.include?($1)
|
101
|
+
pattern[$1]
|
102
|
+
end
|
104
103
|
end
|
105
|
-
end
|
106
104
|
|
107
|
-
|
108
|
-
|
109
|
-
|
105
|
+
def emit_record(tag, record)
|
106
|
+
router.emit(tag, Fluent::Engine.now, record)
|
107
|
+
end
|
110
108
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
109
|
+
def get_connection
|
110
|
+
begin
|
111
|
+
return PG::Connection.new({
|
112
|
+
:host => @host,
|
113
|
+
:port => @port,
|
114
|
+
:user => @username,
|
115
|
+
:password => @password,
|
116
|
+
:dbname => @database
|
117
|
+
})
|
118
|
+
rescue Exception => e
|
119
|
+
log.warn "failed to get connection and will retry. error: #{e}"
|
120
|
+
sleep @interval
|
121
|
+
retry
|
122
|
+
end
|
124
123
|
end
|
125
124
|
end
|
126
125
|
end
|
@@ -1,5 +1,5 @@
|
|
1
|
-
require 'test/unit'
|
2
1
|
require 'fluent/test'
|
2
|
+
require 'fluent/test/driver/input'
|
3
3
|
require 'fluent/plugin/in_postgres_replicator'
|
4
4
|
|
5
5
|
class PostgresReplicatorTest < Test::Unit::TestCase
|
@@ -20,7 +20,7 @@ class PostgresReplicatorTest < Test::Unit::TestCase
|
|
20
20
|
]
|
21
21
|
|
22
22
|
def create_driver(conf = CONFIG)
|
23
|
-
Fluent::Test::
|
23
|
+
Fluent::Test::Driver::Input.new(Fluent::Plugin::PostgresReplicatorInput).configure(conf)
|
24
24
|
end
|
25
25
|
|
26
26
|
def test_configure
|
@@ -43,14 +43,12 @@ class PostgresReplicatorTest < Test::Unit::TestCase
|
|
43
43
|
def test_emit
|
44
44
|
d = create_driver
|
45
45
|
|
46
|
-
d.run
|
47
|
-
sleep 2
|
48
|
-
end
|
46
|
+
d.run(expect_records: 2, timeout: 2)
|
49
47
|
|
50
|
-
|
51
|
-
assert_equal true,
|
52
|
-
assert_equal ['pgreplicator.pg_repli_test_db.pg_repli_test_table.insert.id', @time, {'id' => '1', 'total' => '10'}],
|
53
|
-
assert_equal ['pgreplicator.pg_repli_test_db.pg_repli_test_table.insert.id', @time, {'id' => '2', 'total' => '20'}],
|
48
|
+
events = d.events
|
49
|
+
assert_equal true, events.length == 2
|
50
|
+
assert_equal ['pgreplicator.pg_repli_test_db.pg_repli_test_table.insert.id', @time, {'id' => '1', 'total' => '10'}], events[0]
|
51
|
+
assert_equal ['pgreplicator.pg_repli_test_db.pg_repli_test_table.insert.id', @time, {'id' => '2', 'total' => '20'}], events[1]
|
54
52
|
end
|
55
53
|
|
56
54
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fluent-plugin-postgres-replicator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- innossh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-07-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fluentd
|
@@ -16,14 +16,20 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 0.14.0
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '2'
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
27
|
- - ">="
|
25
28
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
29
|
+
version: 0.14.0
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '2'
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
34
|
name: pg
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -105,7 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
105
111
|
version: '0'
|
106
112
|
requirements: []
|
107
113
|
rubyforge_project:
|
108
|
-
rubygems_version: 2.
|
114
|
+
rubygems_version: 2.5.1
|
109
115
|
signing_key:
|
110
116
|
specification_version: 4
|
111
117
|
summary: PostgreSQL replication input plugin for Fluent
|