ebisu_connection 0.0.8 → 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 +7 -0
- data/.gitignore +5 -14
- data/Appraisals +17 -0
- data/README.md +85 -8
- data/Rakefile +5 -0
- data/ebisu_connection.gemspec +5 -3
- data/gemfiles/rails3.gemfile +9 -0
- data/gemfiles/rails40.gemfile +9 -0
- data/gemfiles/rails41.gemfile +9 -0
- data/lib/ebisu_connection/conf_file.rb +10 -7
- data/lib/ebisu_connection/connection_manager.rb +11 -11
- data/lib/ebisu_connection/greatest_common_divisor.rb +34 -0
- data/lib/ebisu_connection/slave.rb +41 -0
- data/lib/ebisu_connection/slave_group.rb +53 -0
- data/lib/ebisu_connection/version.rb +1 -1
- data/lib/ebisu_connection.rb +15 -5
- data/spec/spec_helper.rb +5 -0
- data/spec/unit/greatest_common_divisor_spec.rb +38 -0
- data/spec/unit/slave_group_spec.rb +36 -0
- data/spec/unit/slave_spec.rb +58 -0
- metadata +116 -100
- data/lib/ebisu_connection/slaves.rb +0 -119
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3bea33e80ff8e417162f05398ebb64531751825b
|
4
|
+
data.tar.gz: a908cbe78a57d95209b8660de521a1371f0e5b05
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c407ba2f24d9466e390eae96ff9931630b4f5ddd0361135d1e07c3d491e7d01e297c1f501ef0004130c5a49c6d0e9fd934877e2ff1ec8d244fd0ffd515851d2e
|
7
|
+
data.tar.gz: 871ee55618b50ffc9e833f9933d1706f1127a081d2d1c644d634cf26906d9c90e5f440fb3a537edb4b2735ab7f8cf069606003e74281b0b9bd8b1428aa143309
|
data/.gitignore
CHANGED
@@ -1,17 +1,8 @@
|
|
1
1
|
*.gem
|
2
|
-
*.rbc
|
3
2
|
.bundle
|
4
|
-
.
|
5
|
-
.yardoc
|
3
|
+
.ruby-version
|
6
4
|
Gemfile.lock
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
lib/bundler/man
|
12
|
-
pkg
|
13
|
-
rdoc
|
14
|
-
spec/reports
|
15
|
-
test/tmp
|
16
|
-
test/version_tmp
|
17
|
-
tmp
|
5
|
+
gemfiles/*.lock
|
6
|
+
bundle
|
7
|
+
log/*
|
8
|
+
.*.sw[a-z]
|
data/Appraisals
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
appraise "rails3" do
|
2
|
+
gem "activerecord", "~> 3.2.0"
|
3
|
+
gem "activesupport", "~> 3.2.0"
|
4
|
+
gem "railties", "~> 3.2.0"
|
5
|
+
end
|
6
|
+
|
7
|
+
appraise "rails40" do
|
8
|
+
gem "activerecord", "~> 4.0.0"
|
9
|
+
gem "activesupport", "~> 4.0.0"
|
10
|
+
gem "railties", "~> 4.0.0"
|
11
|
+
end
|
12
|
+
|
13
|
+
appraise "rails41" do
|
14
|
+
gem "activerecord", "~> 4.1.0"
|
15
|
+
gem "activesupport", "~> 4.1.0"
|
16
|
+
gem "railties", "~> 4.1.0"
|
17
|
+
end
|
data/README.md
CHANGED
@@ -1,13 +1,11 @@
|
|
1
1
|
# EbisuConnection
|
2
2
|
|
3
3
|
EbisuConnection supports to connect with Mysql slave servers. It doesn't need Load Balancer.
|
4
|
-
You can assign a performance weight to each slave server. And slave config is reflected dynamic.
|
4
|
+
You can assign a performance weight to each slave server. And slave config is reflected dynamic.
|
5
5
|
EbisuConnection uses FreshConnection (https://github.com/tsukasaoishi/fresh_connection).
|
6
6
|
|
7
7
|
## Installation
|
8
8
|
|
9
|
-
EbisuConnection has tested Rails3.2.16 and Rails4.0.2.
|
10
|
-
|
11
9
|
Add this line to your application's Gemfile:
|
12
10
|
|
13
11
|
gem 'ebisu_connection'
|
@@ -40,7 +38,7 @@ config/database.yml
|
|
40
38
|
password: slave
|
41
39
|
host: slave
|
42
40
|
|
43
|
-
slave is base config to connect to slave servers.
|
41
|
+
```slave``` is base config to connect to slave servers.
|
44
42
|
Others will use the master setting. If you want to change, write in the slave.
|
45
43
|
|
46
44
|
Config of each slave server fill out config/slave.yaml
|
@@ -58,13 +56,73 @@ config/slave.yaml is checked by end of action. If config changed, it's reflected
|
|
58
56
|
|
59
57
|
String format is it. You can write config with hash.
|
60
58
|
|
61
|
-
###
|
59
|
+
### use multiple slave servers group
|
60
|
+
If you may want to user multiple slave group, write multiple slave group to config/database.yml.
|
62
61
|
|
63
|
-
|
62
|
+
production:
|
63
|
+
adapter: mysql2
|
64
|
+
encoding: utf8
|
65
|
+
reconnect: true
|
66
|
+
database: kaeru
|
67
|
+
pool: 5
|
68
|
+
username: master
|
69
|
+
password: master
|
70
|
+
host: localhost
|
71
|
+
socket: /var/run/mysqld/mysqld.sock
|
64
72
|
|
65
|
-
|
73
|
+
slave:
|
74
|
+
username: slave
|
75
|
+
password: slave
|
76
|
+
host: slave
|
77
|
+
|
78
|
+
admin_slave:
|
79
|
+
username: slave
|
80
|
+
password: slave
|
81
|
+
host: admin_slaves
|
82
|
+
|
83
|
+
Config of each slave server fill out config/slave.yaml
|
84
|
+
|
85
|
+
production:
|
86
|
+
slave:
|
87
|
+
- "slave1, 10"
|
88
|
+
- "slave2, 20"
|
89
|
+
-
|
90
|
+
host: "slave3"
|
91
|
+
weight: 30
|
92
|
+
admin_slave:
|
93
|
+
- "slave3, 10"
|
94
|
+
- "slave4, 20"
|
66
95
|
|
67
|
-
|
96
|
+
|
97
|
+
And call establish_fresh_connection method in model that access to ```admin_slave``` slave group.
|
98
|
+
|
99
|
+
class AdminUser < ActiveRecord::Base
|
100
|
+
establish_fresh_connection :admin_slave
|
101
|
+
end
|
102
|
+
|
103
|
+
The children is access to same slave group of parent.
|
104
|
+
|
105
|
+
class Parent < ActiveRecord::Base
|
106
|
+
establish_fresh_connection :admin_slave
|
107
|
+
end
|
108
|
+
|
109
|
+
class AdminUser < Parent
|
110
|
+
end
|
111
|
+
|
112
|
+
class Benefit < Parent
|
113
|
+
end
|
114
|
+
|
115
|
+
AdminUser and Benefit access to ```admin_slave``` slave group.
|
116
|
+
|
117
|
+
|
118
|
+
### Declare model that doesn't use slave db
|
119
|
+
|
120
|
+
class SomethingModel < ActiveRecord::Base
|
121
|
+
master_db_only!
|
122
|
+
end
|
123
|
+
|
124
|
+
If model that always access to master servers is exist, You may want to write ```master_db_only!``` in model.
|
125
|
+
The model that master_db_only model's child is always access to master db.
|
68
126
|
|
69
127
|
## Usage
|
70
128
|
|
@@ -91,3 +149,22 @@ In transaction, Always will be access to master server.
|
|
91
149
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
92
150
|
4. Push to the branch (`git push origin my-new-feature`)
|
93
151
|
5. Create new Pull Request
|
152
|
+
|
153
|
+
## Test
|
154
|
+
|
155
|
+
I'm glad that you would do test!
|
156
|
+
To run the test suite, you need mysql installed.
|
157
|
+
How to setup your test environment.
|
158
|
+
|
159
|
+
```bash
|
160
|
+
bundle install --path bundle
|
161
|
+
GEM_HOME=bundle/ruby/(your ruby version) gem install bundler --pre
|
162
|
+
bundle exec appraisal install
|
163
|
+
```
|
164
|
+
|
165
|
+
This command run the spec suite for all rails versions supported.
|
166
|
+
|
167
|
+
```base
|
168
|
+
bundle exec appraisal rake spec
|
169
|
+
```
|
170
|
+
|
data/Rakefile
CHANGED
data/ebisu_connection.gemspec
CHANGED
@@ -18,9 +18,11 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_dependency '
|
22
|
-
spec.add_dependency 'fresh_connection', '>= 0.1.8'
|
21
|
+
spec.add_dependency 'fresh_connection', '~> 0.2.0'
|
23
22
|
|
24
23
|
spec.add_development_dependency "bundler", "~> 1.3"
|
25
|
-
spec.add_development_dependency "rake"
|
24
|
+
spec.add_development_dependency "rake", '>= 10.0.0'
|
25
|
+
spec.add_development_dependency "rspec", '>= 2.14.1'
|
26
|
+
spec.add_development_dependency 'mysql2', '>= 0.3.15'
|
27
|
+
spec.add_development_dependency 'appraisal', '>= 1.0.0'
|
26
28
|
end
|
@@ -2,7 +2,6 @@ module EbisuConnection
|
|
2
2
|
class ConfFile
|
3
3
|
class << self
|
4
4
|
attr_writer :slaves_file, :check_interval
|
5
|
-
attr_accessor :slave_type
|
6
5
|
|
7
6
|
def if_modify
|
8
7
|
if time_to_check? && modify?
|
@@ -15,12 +14,18 @@ module EbisuConnection
|
|
15
14
|
@spec = nil
|
16
15
|
end
|
17
16
|
|
18
|
-
def slaves_conf
|
17
|
+
def slaves_conf(slave_group)
|
19
18
|
@slaves_conf ||= get_slaves_conf
|
19
|
+
if @slaves_conf.is_a?(Hash)
|
20
|
+
@slaves_conf[slave_group] || @slaves_conf
|
21
|
+
else
|
22
|
+
@slaves_conf
|
23
|
+
end
|
20
24
|
end
|
21
25
|
|
22
|
-
def spec
|
26
|
+
def spec(slave_group)
|
23
27
|
@spec ||= get_spec
|
28
|
+
@spec.merge(@spec[slave_group] || {})
|
24
29
|
end
|
25
30
|
|
26
31
|
def slaves_file
|
@@ -50,13 +55,11 @@ module EbisuConnection
|
|
50
55
|
def get_slaves_conf
|
51
56
|
@file_mtime = File.mtime(slaves_file)
|
52
57
|
conf = YAML.load_file(slaves_file)
|
53
|
-
conf
|
54
|
-
slave_type ? conf[slave_type.to_s] : conf
|
58
|
+
conf[Rails.env.to_s] || {}
|
55
59
|
end
|
56
60
|
|
57
61
|
def get_spec
|
58
|
-
|
59
|
-
ret.merge(ret["slave"] || {})
|
62
|
+
ActiveRecord::Base.configurations[Rails.env]
|
60
63
|
end
|
61
64
|
end
|
62
65
|
end
|
@@ -3,17 +3,9 @@ require 'fresh_connection/abstract_connection_manager'
|
|
3
3
|
|
4
4
|
module EbisuConnection
|
5
5
|
class ConnectionManager < FreshConnection::AbstractConnectionManager
|
6
|
-
|
7
|
-
delegate :slaves_file, :slaves_file=, :check_interval, :check_interval=,
|
8
|
-
:slave_type, :slave_type=, :to => EbisuConnection::ConfFile
|
6
|
+
delegate :if_modify, :conf_clear!, :to => ConfFile
|
9
7
|
|
10
|
-
|
11
|
-
end
|
12
|
-
|
13
|
-
delegate :if_modify, :conf_clear!, :slaves_conf, :spec,
|
14
|
-
:to => EbisuConnection::ConfFile
|
15
|
-
|
16
|
-
def initialize
|
8
|
+
def initialize(slave_group = "slave")
|
17
9
|
super
|
18
10
|
@slaves = {}
|
19
11
|
end
|
@@ -87,7 +79,15 @@ module EbisuConnection
|
|
87
79
|
end
|
88
80
|
|
89
81
|
def get_slaves
|
90
|
-
|
82
|
+
SlaveGroup.new(slaves_conf, spec)
|
83
|
+
end
|
84
|
+
|
85
|
+
def slaves_conf
|
86
|
+
ConfFile.slaves_conf(@slave_group)
|
87
|
+
end
|
88
|
+
|
89
|
+
def spec
|
90
|
+
ConfFile.spec(@slave_group)
|
91
91
|
end
|
92
92
|
end
|
93
93
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module EbisuConnection
|
2
|
+
class GreatestCommonDivisor
|
3
|
+
class << self
|
4
|
+
def calc(set)
|
5
|
+
self.new(set).calc
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(set)
|
10
|
+
@set = set.sort.uniq
|
11
|
+
end
|
12
|
+
|
13
|
+
def calc
|
14
|
+
n = @set.shift
|
15
|
+
return n if n == 1 || @set.empty?
|
16
|
+
|
17
|
+
while !@set.empty?
|
18
|
+
m = @set.shift
|
19
|
+
n = gcd_euclid(m, n)
|
20
|
+
end
|
21
|
+
n
|
22
|
+
end
|
23
|
+
|
24
|
+
def gcd_euclid(m, n)
|
25
|
+
m, n = n, m if m < n
|
26
|
+
while n != 0
|
27
|
+
work = m % n
|
28
|
+
m = n
|
29
|
+
n = work
|
30
|
+
end
|
31
|
+
m
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'active_support/core_ext/hash/keys'
|
2
|
+
require 'active_support/core_ext/object/blank'
|
3
|
+
|
4
|
+
module EbisuConnection
|
5
|
+
class Slave
|
6
|
+
attr_reader :hostname, :weight
|
7
|
+
|
8
|
+
def initialize(conf, spec)
|
9
|
+
case conf
|
10
|
+
when String
|
11
|
+
host, weight = conf.split(/\s*,\s*/)
|
12
|
+
@hostname, port = host.split(/\s*:\s*/)
|
13
|
+
when Hash
|
14
|
+
conf.stringify_keys!
|
15
|
+
@hostname = conf["host"]
|
16
|
+
weight = conf["weight"]
|
17
|
+
port = conf["port"]
|
18
|
+
else
|
19
|
+
raise ArgumentError, "slaves config is invalid"
|
20
|
+
end
|
21
|
+
|
22
|
+
modify_spec = {"host" => @hostname}
|
23
|
+
modify_spec["port"] = port.to_i if port.present?
|
24
|
+
|
25
|
+
@spec = spec.merge(modify_spec)
|
26
|
+
@weight = (weight || 1).to_i
|
27
|
+
end
|
28
|
+
|
29
|
+
def connection
|
30
|
+
@connection ||= ActiveRecord::Base.send("mysql2_connection", @spec)
|
31
|
+
end
|
32
|
+
|
33
|
+
def disconnect!
|
34
|
+
if @connection
|
35
|
+
@connection.disconnect!
|
36
|
+
@connection = nil
|
37
|
+
end
|
38
|
+
rescue
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module EbisuConnection
|
2
|
+
class SlaveGroup
|
3
|
+
class AllSlavesHasGoneError < StandardError; end
|
4
|
+
|
5
|
+
def initialize(slaves_conf, spec)
|
6
|
+
@slaves = slaves_conf.map do |conf|
|
7
|
+
Slave.new(conf, spec)
|
8
|
+
end
|
9
|
+
|
10
|
+
recalc_roulette
|
11
|
+
end
|
12
|
+
|
13
|
+
def sample
|
14
|
+
raise AllSlavesHasGoneError if @slaves.blank?
|
15
|
+
@slaves[@roulette.sample]
|
16
|
+
end
|
17
|
+
|
18
|
+
def remove_connection(connection)
|
19
|
+
return unless s = @slaves.detect{|s| s.connection == connection}
|
20
|
+
s.disconnect! rescue nil
|
21
|
+
@slaves.delete(s)
|
22
|
+
raise AllSlavesHasGoneError if @slaves.blank?
|
23
|
+
recalc_roulette
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def all_disconnect!
|
28
|
+
@reserve_release = nil
|
29
|
+
@slaves.each {|s| s.disconnect!}
|
30
|
+
end
|
31
|
+
|
32
|
+
def reserve_release_connection!
|
33
|
+
@reserve_release = true
|
34
|
+
end
|
35
|
+
|
36
|
+
def reserved_release?
|
37
|
+
!!@reserve_release
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def recalc_roulette
|
43
|
+
weight_list = @slaves.map {|s| s.weight }
|
44
|
+
|
45
|
+
@roulette = []
|
46
|
+
gcd = GreatestCommonDivisor.calc(weight_list)
|
47
|
+
weight_list.each_with_index do |w, index|
|
48
|
+
weight = w / gcd
|
49
|
+
@roulette.concat([index] * weight)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/ebisu_connection.rb
CHANGED
@@ -1,7 +1,17 @@
|
|
1
1
|
require "fresh_connection"
|
2
|
-
require "ebisu_connection/version"
|
3
|
-
require "ebisu_connection/conf_file"
|
4
|
-
require "ebisu_connection/slaves"
|
5
|
-
require "ebisu_connection/connection_manager"
|
6
2
|
|
7
|
-
|
3
|
+
module EbisuConnection
|
4
|
+
extend ActiveSupport::Autoload
|
5
|
+
|
6
|
+
autoload :ConfFile
|
7
|
+
autoload :ConnectionManager
|
8
|
+
autoload :SlaveGroup
|
9
|
+
autoload :Slave
|
10
|
+
autoload :GreatestCommonDivisor
|
11
|
+
|
12
|
+
class << self
|
13
|
+
delegate :slaves_file=, :check_interval=, :to => ConfFile
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
FreshConnection.connection_manager = EbisuConnection::ConnectionManager
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe EbisuConnection::GreatestCommonDivisor do
|
4
|
+
before(:all) do
|
5
|
+
@g = EbisuConnection::GreatestCommonDivisor
|
6
|
+
end
|
7
|
+
|
8
|
+
context ".calc" do
|
9
|
+
it "return nil if set is empty" do
|
10
|
+
expect(@g.calc([])).to be_nil
|
11
|
+
end
|
12
|
+
|
13
|
+
it "return first element if set has one element" do
|
14
|
+
expect(@g.calc([1])).to eq(1)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "return first element if set has elements that is all same" do
|
18
|
+
set = [1] * 100
|
19
|
+
expect(@g.calc(set)).to eq(1)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "return 1 if set includes 1 in elements" do
|
23
|
+
set = (1..100).to_a
|
24
|
+
expect(@g.calc(set)).to eq(1)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "return gcd" do
|
28
|
+
expect(@g.calc([2,4])).to eq(2)
|
29
|
+
expect(@g.calc([2,4,6])).to eq(2)
|
30
|
+
expect(@g.calc([4,6])).to eq(2)
|
31
|
+
expect(@g.calc([3,4,6])).to eq(1)
|
32
|
+
expect(@g.calc([3,6])).to eq(3)
|
33
|
+
expect(@g.calc([10,10,2])).to eq(2)
|
34
|
+
expect(@g.calc([10,10,2,10,5])).to eq(1)
|
35
|
+
expect(@g.calc([10,10,10,5])).to eq(5)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe EbisuConnection::SlaveGroup do
|
4
|
+
before(:all) do
|
5
|
+
@sg = EbisuConnection::SlaveGroup
|
6
|
+
@spec = {
|
7
|
+
"adapter" => "mysql2",
|
8
|
+
"database" => "ebisu_connection_test",
|
9
|
+
"username" => "root"
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
context "#sample" do
|
14
|
+
it "raise exception if slaves empty" do
|
15
|
+
inst = @sg.new([], {})
|
16
|
+
expect{
|
17
|
+
inst.sample
|
18
|
+
}.to raise_error(EbisuConnection::SlaveGroup::AllSlavesHasGoneError)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "return slve instance object" do
|
22
|
+
inst = @sg.new(["h"], {})
|
23
|
+
expect(inst.sample).to be_a(EbisuConnection::Slave)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "#remove_connection" do
|
28
|
+
it "raise exception AllSlavesHasGoneError when slaves size is one" do
|
29
|
+
inst = @sg.new(["localhost"], @spec)
|
30
|
+
c = inst.sample.connection
|
31
|
+
expect {
|
32
|
+
inst.remove_connection(c)
|
33
|
+
}.to raise_error(EbisuConnection::SlaveGroup::AllSlavesHasGoneError)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe EbisuConnection::Slave do
|
4
|
+
before(:all) do
|
5
|
+
@spec = {
|
6
|
+
"adapter" => "mysql2",
|
7
|
+
"database" => "ebisu_connection_test",
|
8
|
+
"username" => "root"
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
context "initialize(conf is String)" do
|
13
|
+
it "hostname only" do
|
14
|
+
s = EbisuConnection::Slave.new("host_1", {})
|
15
|
+
expect(s.hostname).to eq("host_1")
|
16
|
+
expect(s.weight).to eq(1)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "hostname and weight" do
|
20
|
+
s = EbisuConnection::Slave.new("host_1, 10", {})
|
21
|
+
expect(s.hostname).to eq("host_1")
|
22
|
+
expect(s.weight).to eq(10)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "hostname, weight and port" do
|
26
|
+
s = EbisuConnection::Slave.new("host_1:1975, 10", {})
|
27
|
+
expect(s.hostname).to eq("host_1")
|
28
|
+
expect(s.weight).to eq(10)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "initialize(conf is Hash)" do
|
33
|
+
it "hostname only" do
|
34
|
+
s = EbisuConnection::Slave.new({:host => "host_1"}, {})
|
35
|
+
expect(s.hostname).to eq("host_1")
|
36
|
+
expect(s.weight).to eq(1)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "hostname and weight" do
|
40
|
+
s = EbisuConnection::Slave.new({:host => "host_1", :weight => 10}, {})
|
41
|
+
expect(s.hostname).to eq("host_1")
|
42
|
+
expect(s.weight).to eq(10)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "hostname, weight and port" do
|
46
|
+
s = EbisuConnection::Slave.new({:host => "host_1", :weight => 10, :port => 1975}, {})
|
47
|
+
expect(s.hostname).to eq("host_1")
|
48
|
+
expect(s.weight).to eq(10)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "#connection" do
|
53
|
+
it "return Mysql2Adapter object" do
|
54
|
+
s = EbisuConnection::Slave.new("localhost", @spec)
|
55
|
+
expect(s.connection).to be_a(ActiveRecord::ConnectionAdapters::Mysql2Adapter)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
metadata
CHANGED
@@ -1,138 +1,154 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: ebisu_connection
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 0
|
9
|
-
- 8
|
10
|
-
version: 0.0.8
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
11
5
|
platform: ruby
|
12
|
-
authors:
|
6
|
+
authors:
|
13
7
|
- tsukasaoishi
|
14
8
|
autorequire:
|
15
9
|
bindir: bin
|
16
10
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
11
|
+
date: 2014-04-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: fresh_connection
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.2.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.2.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.3'
|
34
|
+
type: :development
|
23
35
|
prerelease: false
|
24
|
-
|
25
|
-
|
26
|
-
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
27
45
|
- - ">="
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
|
30
|
-
|
31
|
-
- 3
|
32
|
-
- 2
|
33
|
-
- 0
|
34
|
-
version: 3.2.0
|
35
|
-
type: :runtime
|
36
|
-
version_requirements: *id001
|
37
|
-
- !ruby/object:Gem::Dependency
|
38
|
-
name: fresh_connection
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 10.0.0
|
48
|
+
type: :development
|
39
49
|
prerelease: false
|
40
|
-
|
41
|
-
|
42
|
-
requirements:
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
43
52
|
- - ">="
|
44
|
-
- !ruby/object:Gem::Version
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
name: bundler
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 10.0.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.14.1
|
62
|
+
type: :development
|
55
63
|
prerelease: false
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 2.14.1
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: mysql2
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.3.15
|
66
76
|
type: :development
|
67
|
-
version_requirements: *id003
|
68
|
-
- !ruby/object:Gem::Dependency
|
69
|
-
name: rake
|
70
77
|
prerelease: false
|
71
|
-
|
72
|
-
|
73
|
-
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.3.15
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: appraisal
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
74
87
|
- - ">="
|
75
|
-
- !ruby/object:Gem::Version
|
76
|
-
|
77
|
-
segments:
|
78
|
-
- 0
|
79
|
-
version: "0"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 1.0.0
|
80
90
|
type: :development
|
81
|
-
|
82
|
-
|
83
|
-
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 1.0.0
|
97
|
+
description: EbisuConnection supports to connect with Mysql slave servers. It doesn't
|
98
|
+
need Load Balancer.
|
99
|
+
email:
|
84
100
|
- tsukasa.oishi@gmail.com
|
85
101
|
executables: []
|
86
|
-
|
87
102
|
extensions: []
|
88
|
-
|
89
103
|
extra_rdoc_files: []
|
90
|
-
|
91
|
-
|
92
|
-
-
|
104
|
+
files:
|
105
|
+
- ".gitignore"
|
106
|
+
- Appraisals
|
93
107
|
- Gemfile
|
94
108
|
- LICENSE.txt
|
95
109
|
- README.md
|
96
110
|
- Rakefile
|
97
111
|
- ebisu_connection.gemspec
|
112
|
+
- gemfiles/rails3.gemfile
|
113
|
+
- gemfiles/rails40.gemfile
|
114
|
+
- gemfiles/rails41.gemfile
|
98
115
|
- lib/ebisu_connection.rb
|
99
116
|
- lib/ebisu_connection/conf_file.rb
|
100
117
|
- lib/ebisu_connection/connection_manager.rb
|
101
|
-
- lib/ebisu_connection/
|
118
|
+
- lib/ebisu_connection/greatest_common_divisor.rb
|
119
|
+
- lib/ebisu_connection/slave.rb
|
120
|
+
- lib/ebisu_connection/slave_group.rb
|
102
121
|
- lib/ebisu_connection/version.rb
|
103
|
-
|
104
|
-
|
105
|
-
|
122
|
+
- spec/spec_helper.rb
|
123
|
+
- spec/unit/greatest_common_divisor_spec.rb
|
124
|
+
- spec/unit/slave_group_spec.rb
|
125
|
+
- spec/unit/slave_spec.rb
|
126
|
+
homepage: ''
|
127
|
+
licenses:
|
106
128
|
- MIT
|
129
|
+
metadata: {}
|
107
130
|
post_install_message:
|
108
131
|
rdoc_options: []
|
109
|
-
|
110
|
-
require_paths:
|
132
|
+
require_paths:
|
111
133
|
- lib
|
112
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
113
|
-
|
114
|
-
requirements:
|
134
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
115
136
|
- - ">="
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
version: "0"
|
121
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
|
-
none: false
|
123
|
-
requirements:
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
140
|
+
requirements:
|
124
141
|
- - ">="
|
125
|
-
- !ruby/object:Gem::Version
|
126
|
-
|
127
|
-
segments:
|
128
|
-
- 0
|
129
|
-
version: "0"
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '0'
|
130
144
|
requirements: []
|
131
|
-
|
132
145
|
rubyforge_project:
|
133
|
-
rubygems_version:
|
146
|
+
rubygems_version: 2.2.0
|
134
147
|
signing_key:
|
135
|
-
specification_version:
|
148
|
+
specification_version: 4
|
136
149
|
summary: EbisuConnection supports to connect with Mysql slave servers.
|
137
|
-
test_files:
|
138
|
-
|
150
|
+
test_files:
|
151
|
+
- spec/spec_helper.rb
|
152
|
+
- spec/unit/greatest_common_divisor_spec.rb
|
153
|
+
- spec/unit/slave_group_spec.rb
|
154
|
+
- spec/unit/slave_spec.rb
|
@@ -1,119 +0,0 @@
|
|
1
|
-
module EbisuConnection
|
2
|
-
class Slaves
|
3
|
-
class AllSlavesHasGoneError < StandardError; end
|
4
|
-
|
5
|
-
class Slave
|
6
|
-
attr_reader :hostname, :weight
|
7
|
-
|
8
|
-
def initialize(conf, spec)
|
9
|
-
case conf
|
10
|
-
when String
|
11
|
-
@hostname, weight = conf.split(/\s*,\s*/)
|
12
|
-
edit_spec = {"host" => @hostname}
|
13
|
-
when Hash
|
14
|
-
conf = stringify_keys(conf)
|
15
|
-
weight = conf.delete("weight")
|
16
|
-
edit_spec = conf
|
17
|
-
@hostname = conf["host"]
|
18
|
-
else
|
19
|
-
raise ArgumentError, "slaves config is invalid"
|
20
|
-
end
|
21
|
-
|
22
|
-
@spec = spec.merge(edit_spec)
|
23
|
-
@weight = (weight || 1).to_i
|
24
|
-
end
|
25
|
-
|
26
|
-
def connection
|
27
|
-
@connection ||= ActiveRecord::Base.send("#{@spec["adapter"]}_connection", @spec)
|
28
|
-
end
|
29
|
-
|
30
|
-
def disconnect!
|
31
|
-
if @connection
|
32
|
-
@connection.disconnect!
|
33
|
-
@connection = nil
|
34
|
-
end
|
35
|
-
rescue
|
36
|
-
end
|
37
|
-
|
38
|
-
private
|
39
|
-
|
40
|
-
def stringify_keys(hash)
|
41
|
-
stringify_hash = {}
|
42
|
-
hash.each do |k,v|
|
43
|
-
stringify_hash[k.to_s] = v
|
44
|
-
end
|
45
|
-
stringify_hash
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def initialize(slaves_conf, spec)
|
50
|
-
@slaves = slaves_conf.map do |conf|
|
51
|
-
Slave.new(conf, spec)
|
52
|
-
end
|
53
|
-
|
54
|
-
recalc_roulette
|
55
|
-
end
|
56
|
-
|
57
|
-
def sample
|
58
|
-
raise AllSlavesHasGoneError if @slaves.blank?
|
59
|
-
@slaves[@roulette.sample]
|
60
|
-
end
|
61
|
-
|
62
|
-
def remove_connection(connection)
|
63
|
-
return unless s = @slaves.detect{|s| s.connection == connection}
|
64
|
-
s.disconnect! rescue nil
|
65
|
-
@slaves.delete(s)
|
66
|
-
raise AllSlavesHasGoneError if @slaves.blank?
|
67
|
-
recalc_roulette
|
68
|
-
nil
|
69
|
-
end
|
70
|
-
|
71
|
-
def all_disconnect!
|
72
|
-
@reserve_release = nil
|
73
|
-
@slaves.each {|s| s.disconnect!}
|
74
|
-
end
|
75
|
-
|
76
|
-
def reserve_release_connection!
|
77
|
-
@reserve_release = true
|
78
|
-
end
|
79
|
-
|
80
|
-
def reserved_release?
|
81
|
-
!!@reserve_release
|
82
|
-
end
|
83
|
-
|
84
|
-
private
|
85
|
-
|
86
|
-
def recalc_roulette
|
87
|
-
weight_list = @slaves.map {|s| s.weight }
|
88
|
-
|
89
|
-
@roulette = []
|
90
|
-
gcd = get_gcd(weight_list)
|
91
|
-
weight_list.each_with_index do |w, index|
|
92
|
-
weight = w / gcd
|
93
|
-
@roulette.concat([index] * weight)
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def get_gcd(list)
|
98
|
-
list = list.sort.uniq
|
99
|
-
n = list.shift
|
100
|
-
return n if n == 1 || list.empty?
|
101
|
-
|
102
|
-
while !list.empty?
|
103
|
-
m = list.shift
|
104
|
-
n = gcd_euclid(m, n)
|
105
|
-
end
|
106
|
-
n
|
107
|
-
end
|
108
|
-
|
109
|
-
def gcd_euclid(m, n)
|
110
|
-
m, n = n, m if m < n
|
111
|
-
while n != 0
|
112
|
-
work = m % n
|
113
|
-
m = n
|
114
|
-
n = work
|
115
|
-
end
|
116
|
-
m
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|