slavery 2.1.1 → 3.0.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/.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
|