slavery 2.1.1 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/README.md +2 -8
- data/lib/slavery.rb +5 -5
- data/lib/slavery/active_record/base.rb +3 -5
- data/lib/slavery/active_record/relation.rb +4 -4
- data/lib/slavery/base.rb +8 -4
- data/lib/slavery/connection_holder.rb +12 -7
- data/lib/slavery/version.rb +1 -1
- data/spec/configuration_spec.rb +7 -28
- data/spec/slavery_spec.rb +18 -5
- data/spec/spec_helper.rb +6 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d8d8a53892c3dc8ba6beea003eeb3e8dda2fcb7f
|
4
|
+
data.tar.gz: 884ce3b0bccb928742362447f0a1afdc9f7edc5c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 603d8b62b416668e3cec066d49308aa78f194557d14741e551f14280b337dcbc081e52cf48848141c1ab0a0ae8cdc948cc6ba1e85ddb177c2ad61d8a1357ffc3
|
7
|
+
data.tar.gz: b0fd5c1246b7ada095fa7a1ef93a88ef1cb6d134a6a4bf0e1b2f9ce9635a1b6a3dd729059729cc5ee10f442b491a7805aeacce26fe7c5b53110e73fc9d5bf243
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -66,6 +66,7 @@ To start using Slavery, you need to add `Slavery.on_slave` in your code. Queries
|
|
66
66
|
|
67
67
|
```ruby
|
68
68
|
Slavery.on_slave { User.count } # => runs on slave
|
69
|
+
Slavery.on_slave(:two) { User.count } # => runs on another slave configured as `development_slave_two`
|
69
70
|
```
|
70
71
|
|
71
72
|
You can nest `on_slave` and `on_master` interchangeably. The following code works as expected.
|
@@ -158,15 +159,8 @@ ActiveRecord::Base.configurations = {
|
|
158
159
|
ActiveRecord::Base.establish_connection(:development)
|
159
160
|
```
|
160
161
|
|
161
|
-
## Custom slave key in database.yml
|
162
|
-
|
163
|
-
This is useful for deploying on EngineYard where the configuration key in database.yml is simple "slave". Put the following line in `config/initializers/slavery.rb`.
|
164
|
-
|
165
|
-
```ruby
|
166
|
-
Slavery.spec_key = "slave" #instead of production_slave
|
167
|
-
```
|
168
|
-
|
169
162
|
## Changelog
|
170
163
|
|
164
|
+
* v3.0.0: Support for multiple slave targets ([@punchh](https://github.com/punchh))
|
171
165
|
* v2.1.0: Debug log support / Database URL support / Rails 3.2 & 4.0 compatibility (Thanks to [@citrus](https://github.com/citrus))
|
172
166
|
* v2.0.0: Rails 5 support
|
data/lib/slavery.rb
CHANGED
@@ -12,14 +12,14 @@ require 'slavery/active_record/log_subscriber'
|
|
12
12
|
module Slavery
|
13
13
|
class << self
|
14
14
|
attr_accessor :disabled
|
15
|
-
attr_writer :spec_key
|
16
15
|
|
17
|
-
def
|
18
|
-
@
|
16
|
+
def slave_connections
|
17
|
+
@slave_connections ||= {}
|
19
18
|
end
|
20
19
|
|
21
|
-
def on_slave(&block)
|
22
|
-
|
20
|
+
def on_slave(name = :null_state, &block)
|
21
|
+
raise Slavery::Error.new('invalid slave target') unless name.is_a?(Symbol)
|
22
|
+
Base.new(name).run &block
|
23
23
|
end
|
24
24
|
|
25
25
|
def on_master(&block)
|
@@ -5,21 +5,19 @@ module ActiveRecord
|
|
5
5
|
|
6
6
|
def connection
|
7
7
|
case Thread.current[:slavery]
|
8
|
-
when :slave
|
9
|
-
Slavery.connection_holder.connection_without_slavery
|
10
8
|
when :master, NilClass
|
11
9
|
connection_without_slavery
|
12
10
|
else
|
13
|
-
|
11
|
+
Slavery.connection_holder(Thread.current[:slavery]).connection_without_slavery
|
14
12
|
end
|
15
13
|
end
|
16
14
|
|
17
15
|
# Generate scope at top level e.g. User.on_slave
|
18
|
-
def on_slave
|
16
|
+
def on_slave(name = :null_state)
|
19
17
|
# Why where(nil)?
|
20
18
|
# http://stackoverflow.com/questions/18198963/with-rails-4-model-scoped-is-deprecated-but-model-all-cant-replace-it
|
21
19
|
context = where(nil)
|
22
|
-
context.slavery_target = :
|
20
|
+
context.slavery_target = name || :null_state
|
23
21
|
context
|
24
22
|
end
|
25
23
|
end
|
@@ -6,8 +6,8 @@ module ActiveRecord
|
|
6
6
|
alias_method :exec_queries_without_slavery, :exec_queries
|
7
7
|
|
8
8
|
def exec_queries
|
9
|
-
if slavery_target
|
10
|
-
Slavery.on_slave { exec_queries_without_slavery }
|
9
|
+
if slavery_target
|
10
|
+
Slavery.on_slave(slavery_target) { exec_queries_without_slavery }
|
11
11
|
else
|
12
12
|
exec_queries_without_slavery
|
13
13
|
end
|
@@ -18,8 +18,8 @@ module ActiveRecord
|
|
18
18
|
alias_method :calculate_without_slavery, :calculate
|
19
19
|
|
20
20
|
def calculate(*args)
|
21
|
-
if slavery_target
|
22
|
-
Slavery.on_slave { calculate_without_slavery(*args) }
|
21
|
+
if slavery_target
|
22
|
+
Slavery.on_slave(slavery_target) { calculate_without_slavery(*args) }
|
23
23
|
else
|
24
24
|
calculate_without_slavery(*args)
|
25
25
|
end
|
data/lib/slavery/base.rb
CHANGED
@@ -11,12 +11,16 @@ module Slavery
|
|
11
11
|
private
|
12
12
|
|
13
13
|
def decide_with(target)
|
14
|
-
if Slavery.disabled
|
14
|
+
if Slavery.disabled || target == :master
|
15
15
|
:master
|
16
|
+
elsif inside_transaction?
|
17
|
+
raise Slavery::Error.new('on_slave cannot be used inside transaction block!')
|
18
|
+
elsif target == :null_state
|
19
|
+
:slave
|
20
|
+
elsif target.present?
|
21
|
+
"slave_#{target}".to_sym
|
16
22
|
else
|
17
|
-
raise Slavery::Error.new('on_slave cannot be used
|
18
|
-
|
19
|
-
target
|
23
|
+
raise Slavery::Error.new('on_slave cannot be used with a nil target!')
|
20
24
|
end
|
21
25
|
end
|
22
26
|
|
@@ -4,19 +4,24 @@ module Slavery
|
|
4
4
|
|
5
5
|
class << self
|
6
6
|
# for delayed activation
|
7
|
-
def activate
|
8
|
-
spec = ActiveRecord::Base.configurations[
|
9
|
-
raise Error.new('
|
7
|
+
def activate(target)
|
8
|
+
spec = ActiveRecord::Base.configurations["#{ActiveRecord::ConnectionHandling::RAILS_ENV.call}_#{target}"]
|
9
|
+
raise Error.new("Slave target '#{target}' is invalid!") if spec.nil?
|
10
10
|
establish_connection spec
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
15
|
class << self
|
16
|
-
def connection_holder
|
17
|
-
|
18
|
-
|
19
|
-
ConnectionHolder
|
16
|
+
def connection_holder(target)
|
17
|
+
klass_name = "Slavery#{target.to_s.camelize}ConnectionHolder"
|
18
|
+
slave_connections[klass_name] ||= begin
|
19
|
+
klass = Class.new(Slavery::ConnectionHolder) do
|
20
|
+
self.abstract_class = true
|
21
|
+
end
|
22
|
+
Object.const_set(klass_name, klass)
|
23
|
+
klass.activate(target)
|
24
|
+
klass
|
20
25
|
end
|
21
26
|
end
|
22
27
|
end
|
data/lib/slavery/version.rb
CHANGED
data/spec/configuration_spec.rb
CHANGED
@@ -3,15 +3,18 @@ require 'spec_helper'
|
|
3
3
|
describe 'configuration' do
|
4
4
|
before do
|
5
5
|
# Backup connection and configs
|
6
|
-
@backup_conn = Slavery.instance_variable_get :@
|
6
|
+
@backup_conn = Slavery.instance_variable_get :@slave_connections
|
7
7
|
@backup_config = ActiveRecord::Base.configurations.dup
|
8
8
|
@backup_disabled = Slavery.disabled
|
9
|
-
|
9
|
+
@backup_conn.each_key do |klass_name|
|
10
|
+
Object.send(:remove_const, klass_name) if Object.const_defined?(klass_name)
|
11
|
+
end
|
12
|
+
Slavery.instance_variable_set :@slave_connections, {}
|
10
13
|
end
|
11
14
|
|
12
15
|
after do
|
13
16
|
# Restore connection and configs
|
14
|
-
Slavery.instance_variable_set :@
|
17
|
+
Slavery.instance_variable_set :@slave_connections, @backup_conn
|
15
18
|
ActiveRecord::Base.configurations = @backup_config
|
16
19
|
Slavery.disabled = @backup_disabled
|
17
20
|
end
|
@@ -22,34 +25,10 @@ describe 'configuration' do
|
|
22
25
|
expect { Slavery.on_slave { User.count } }.to raise_error(Slavery::Error)
|
23
26
|
end
|
24
27
|
|
25
|
-
it 'connects to master if slave configuration
|
28
|
+
it 'connects to master if slave configuration is disabled' do
|
26
29
|
ActiveRecord::Base.configurations['test_slave'] = nil
|
27
30
|
Slavery.disabled = true
|
28
31
|
|
29
32
|
expect(Slavery.on_slave { User.count }).to be 2
|
30
33
|
end
|
31
|
-
|
32
|
-
it 'connects to slave when specified as a hash' do
|
33
|
-
Slavery.spec_key = 'test_slave'
|
34
|
-
hash = ActiveRecord::Base.configurations['test_slave']
|
35
|
-
expect(Slavery::ConnectionHolder).to receive(:establish_connection).with(hash)
|
36
|
-
Slavery::ConnectionHolder.activate
|
37
|
-
end
|
38
|
-
|
39
|
-
it 'connects to slave when specified as a url' do
|
40
|
-
expected = if Gem::Version.new(ActiveRecord::VERSION::STRING) < Gem::Version.new('4.1.0')
|
41
|
-
'postgres://root:@localhost:5432/test_slave'
|
42
|
-
else
|
43
|
-
{
|
44
|
-
'adapter' => 'postgresql',
|
45
|
-
'username' => 'root',
|
46
|
-
'host' => 'localhost',
|
47
|
-
'port' => 5432,
|
48
|
-
'database' => 'test_slave'
|
49
|
-
}
|
50
|
-
end
|
51
|
-
Slavery.spec_key = 'test_slave_url'
|
52
|
-
expect(Slavery::ConnectionHolder).to receive(:establish_connection).with(expected)
|
53
|
-
Slavery::ConnectionHolder.activate
|
54
|
-
end
|
55
34
|
end
|
data/spec/slavery_spec.rb
CHANGED
@@ -12,11 +12,13 @@ describe Slavery do
|
|
12
12
|
it 'sets thread local' do
|
13
13
|
Slavery.on_master { expect(slavery_value).to be :master }
|
14
14
|
Slavery.on_slave { expect(slavery_value).to be :slave }
|
15
|
+
Slavery.on_slave(:two) { expect(slavery_value).to be :slave_two}
|
15
16
|
end
|
16
17
|
|
17
18
|
it 'returns value from block' do
|
18
19
|
expect(Slavery.on_master { User.count }).to be 2
|
19
20
|
expect(Slavery.on_slave { User.count }).to be 1
|
21
|
+
expect(Slavery.on_slave(:two) { User.count }).to be 0
|
20
22
|
end
|
21
23
|
|
22
24
|
it 'handles nested calls' do
|
@@ -61,11 +63,6 @@ describe Slavery do
|
|
61
63
|
Slavery.disabled = backup
|
62
64
|
end
|
63
65
|
|
64
|
-
it 'sets the Slavery database spec name by configuration' do
|
65
|
-
Slavery.spec_key = 'custom_slave'
|
66
|
-
expect(Slavery.spec_key).to eq 'custom_slave'
|
67
|
-
end
|
68
|
-
|
69
66
|
it 'avoids stack overflow with 3rdparty gem that defines alias_method. namely newrelic...' do
|
70
67
|
class ActiveRecord::Relation
|
71
68
|
alias_method :calculate_without_thirdparty, :calculate
|
@@ -82,13 +79,29 @@ describe Slavery do
|
|
82
79
|
end
|
83
80
|
end
|
84
81
|
|
82
|
+
it 'works with nils like slave' do
|
83
|
+
expect(User.on_slave(nil).count).to be User.on_slave.count
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'raises on blanks and strings' do
|
87
|
+
expect { User.on_slave("").count }.to raise_error(Slavery::Error)
|
88
|
+
expect { User.on_slave("two").count }.to raise_error(Slavery::Error)
|
89
|
+
expect { User.on_slave("slave").count }.to raise_error(Slavery::Error)
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'raises with non existent extension' do
|
93
|
+
expect { Slavery.on_slave(:non_existent) { User.first } }.to raise_error(Slavery::Error)
|
94
|
+
end
|
95
|
+
|
85
96
|
it 'works with any scopes' do
|
86
97
|
expect(User.count).to be 2
|
98
|
+
expect(User.on_slave(:two).count).to be 0
|
87
99
|
expect(User.on_slave.count).to be 1
|
88
100
|
|
89
101
|
# Why where(nil)?
|
90
102
|
# http://stackoverflow.com/questions/18198963/with-rails-4-model-scoped-is-deprecated-but-model-all-cant-replace-it
|
91
103
|
expect(User.where(nil).to_a.size).to be 2
|
104
|
+
expect(User.on_slave(:two).where(nil).to_a.size).to be 0
|
92
105
|
expect(User.on_slave.where(nil).to_a.size).to be 1
|
93
106
|
end
|
94
107
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -7,7 +7,8 @@ require 'slavery'
|
|
7
7
|
|
8
8
|
ActiveRecord::Base.configurations = {
|
9
9
|
'test' => { 'adapter' => 'sqlite3', 'database' => 'test_db' },
|
10
|
-
'test_slave' => { 'adapter' => 'sqlite3', 'database' => '
|
10
|
+
'test_slave' => { 'adapter' => 'sqlite3', 'database' => 'test_slave_one' },
|
11
|
+
'test_slave_two' => { 'adapter' => 'sqlite3', 'database' => 'test_slave_two'},
|
11
12
|
'test_slave_url' => 'postgres://root:@localhost:5432/test_slave'
|
12
13
|
}
|
13
14
|
|
@@ -34,6 +35,10 @@ class Seeder
|
|
34
35
|
create_tables
|
35
36
|
User.create
|
36
37
|
|
38
|
+
# Populate on slave two
|
39
|
+
connect(:test_slave_two)
|
40
|
+
create_tables
|
41
|
+
|
37
42
|
# Reconnect to master
|
38
43
|
connect(:test)
|
39
44
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: slavery
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kenn Ejima
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-10-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -105,7 +105,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
105
105
|
version: '0'
|
106
106
|
requirements: []
|
107
107
|
rubyforge_project:
|
108
|
-
rubygems_version: 2.6.
|
108
|
+
rubygems_version: 2.6.13
|
109
109
|
signing_key:
|
110
110
|
specification_version: 4
|
111
111
|
summary: Simple, conservative slave reads for ActiveRecord
|