slavery 2.0.0 → 2.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/.gitignore +1 -0
- data/.travis.yml +13 -0
- data/README.md +16 -0
- data/bin/console +5 -0
- data/gemfiles/rails3.2.gemfile +9 -0
- data/gemfiles/rails4.2.gemfile +5 -0
- data/gemfiles/rails4.gemfile +5 -0
- data/lib/slavery.rb +6 -26
- data/lib/slavery/active_record/base.rb +1 -1
- data/lib/slavery/active_record/connection_handling.rb +6 -0
- data/lib/slavery/active_record/log_subscriber.rb +12 -0
- data/lib/slavery/base.rb +1 -1
- data/lib/slavery/connection_holder.rb +23 -0
- data/lib/slavery/transaction.rb +18 -0
- data/lib/slavery/version.rb +1 -1
- data/spec/active_record/log_subscriber_spec.rb +40 -0
- data/spec/slavery_spec.rb +27 -3
- data/spec/spec_helper.rb +3 -2
- metadata +16 -5
- data/lib/slavery/slave_connection_holder.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5346c12e4fcf3eaad7b55ce70ca0ffea79f33fe0
|
4
|
+
data.tar.gz: 74b684b645a28770ecb4d8ae08f6bd5b8daaf7b3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7b9d823e65b28450161595daaef467bc96dc819ed38c4f2d8e9ea663958fca628dd59942700ad829a36903b415088c3ee07ac0df80f3b81e52fd463f588862b2
|
7
|
+
data.tar.gz: ff44e84b3580ce6bccafe3d42ca0b00a07dfb6d83bce83be21bce320c3c2df1b3ab2b4de430612332a0136d072f64ffea0b1ca52cac0d0ac7e41a631efcdffcb
|
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# Slavery - Simple, conservative slave reads for ActiveRecord
|
2
2
|
|
3
|
+
[](https://travis-ci.org/kenn/slavery)
|
4
|
+
|
3
5
|
Slavery is a simple, easy to use gem for ActiveRecord that enables conservative slave reads, which means it doesn't automatically redirect all SELECTs to slaves.
|
4
6
|
|
5
7
|
Instead, you can do `Slavery.on_slave { User.count }` to send a particular query to a slave.
|
@@ -49,6 +51,13 @@ development_slave:
|
|
49
51
|
<<: *common
|
50
52
|
```
|
51
53
|
|
54
|
+
Optionally, you can use a database url for your connections:
|
55
|
+
|
56
|
+
```yaml
|
57
|
+
development: postgres://root:@localhost:5432/myapp_development
|
58
|
+
development_slave: postgres://root:@localhost:5432/myapp_development_slave
|
59
|
+
```
|
60
|
+
|
52
61
|
At this point, Slavery does nothing. Run tests and confirm that nothing is broken.
|
53
62
|
|
54
63
|
## Usage
|
@@ -101,6 +110,12 @@ With this user, writes on slave should raise an exception.
|
|
101
110
|
Slavery.on_slave { User.create } # => ActiveRecord::StatementInvalid: Mysql2::Error: INSERT command denied...
|
102
111
|
```
|
103
112
|
|
113
|
+
With Postgres you can set the entire database to be readonly:
|
114
|
+
|
115
|
+
```SQL
|
116
|
+
ALTER DATABASE myapp_development_slave SET default_transaction_read_only = true;
|
117
|
+
```
|
118
|
+
|
104
119
|
It is a good idea to confirm this behavior in your test code as well.
|
105
120
|
|
106
121
|
## Disable temporarily
|
@@ -139,4 +154,5 @@ Slavery.spec_key = "slave" #instead of production_slave
|
|
139
154
|
|
140
155
|
## Changelog
|
141
156
|
|
157
|
+
* v2.1.0: Debug log support / Database URL support / Rails 3.2 & 4.0 compatibility (Thanks to [@citrus](https://github.com/citrus))
|
142
158
|
* v2.0.0: Rails 5 support
|
data/bin/console
ADDED
data/lib/slavery.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
require 'active_record'
|
2
|
+
require 'slavery/version'
|
2
3
|
require 'slavery/base'
|
3
4
|
require 'slavery/error'
|
4
|
-
require 'slavery/
|
5
|
-
require 'slavery/
|
5
|
+
require 'slavery/connection_holder'
|
6
|
+
require 'slavery/transaction'
|
6
7
|
require 'slavery/active_record/base'
|
8
|
+
require 'slavery/active_record/connection_handling'
|
7
9
|
require 'slavery/active_record/relation'
|
10
|
+
require 'slavery/active_record/log_subscriber'
|
8
11
|
|
9
12
|
module Slavery
|
10
13
|
class << self
|
@@ -12,10 +15,7 @@ module Slavery
|
|
12
15
|
attr_writer :spec_key
|
13
16
|
|
14
17
|
def spec_key
|
15
|
-
|
16
|
-
when String then @spec_key
|
17
|
-
when NilClass then @spec_key = "#{ActiveRecord::ConnectionHandling::RAILS_ENV.call}_slave"
|
18
|
-
end
|
18
|
+
@spec_key ||= "#{ActiveRecord::ConnectionHandling::RAILS_ENV.call}_slave"
|
19
19
|
end
|
20
20
|
|
21
21
|
def on_slave(&block)
|
@@ -25,25 +25,5 @@ module Slavery
|
|
25
25
|
def on_master(&block)
|
26
26
|
Base.new(:master).run &block
|
27
27
|
end
|
28
|
-
|
29
|
-
def slave_connection_holder
|
30
|
-
@slave_connection_holder ||= begin
|
31
|
-
SlaveConnectionHolder.activate
|
32
|
-
SlaveConnectionHolder
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def base_transaction_depth
|
37
|
-
@base_transaction_depth ||= begin
|
38
|
-
testcase = ActiveSupport::TestCase
|
39
|
-
if defined?(testcase) &&
|
40
|
-
testcase.respond_to?(:use_transactional_fixtures) &&
|
41
|
-
testcase.try(:use_transactional_fixtures)
|
42
|
-
1
|
43
|
-
else
|
44
|
-
0
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
28
|
end
|
49
29
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class LogSubscriber
|
3
|
+
|
4
|
+
alias_method :debug_without_slavery, :debug
|
5
|
+
|
6
|
+
def debug(msg)
|
7
|
+
db = Slavery.disabled ? "" : color("[#{Thread.current[:slavery] || "master"}]", ActiveSupport::LogSubscriber::GREEN, true)
|
8
|
+
debug_without_slavery(db + msg)
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
end
|
data/lib/slavery/base.rb
CHANGED
@@ -22,7 +22,7 @@ module Slavery
|
|
22
22
|
|
23
23
|
def inside_transaction?
|
24
24
|
open_transactions = run_on(:master) { ActiveRecord::Base.connection.open_transactions }
|
25
|
-
open_transactions > Slavery.
|
25
|
+
open_transactions > Slavery::Transaction.base_depth
|
26
26
|
end
|
27
27
|
|
28
28
|
def run_on(target)
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Slavery
|
2
|
+
class ConnectionHolder < ActiveRecord::Base
|
3
|
+
self.abstract_class = true
|
4
|
+
|
5
|
+
class << self
|
6
|
+
# for delayed activation
|
7
|
+
def activate
|
8
|
+
spec = ActiveRecord::Base.configurations[Slavery.spec_key]
|
9
|
+
raise Error.new('Slavery.spec_key invalid!') if spec.nil?
|
10
|
+
establish_connection spec
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def connection_holder
|
17
|
+
@connection_holder ||= begin
|
18
|
+
ConnectionHolder.activate
|
19
|
+
ConnectionHolder
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Slavery
|
2
|
+
class Transaction
|
3
|
+
class << self
|
4
|
+
def base_depth
|
5
|
+
@base_depth ||= begin
|
6
|
+
testcase = ActiveSupport::TestCase
|
7
|
+
if defined?(testcase) &&
|
8
|
+
testcase.respond_to?(:use_transactional_fixtures) &&
|
9
|
+
testcase.try(:use_transactional_fixtures)
|
10
|
+
1
|
11
|
+
else
|
12
|
+
0
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/slavery/version.rb
CHANGED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
describe ActiveRecord::LogSubscriber do
|
5
|
+
|
6
|
+
describe 'logging' do
|
7
|
+
|
8
|
+
let(:log) { StringIO.new }
|
9
|
+
let(:logger) { Logger.new(log) }
|
10
|
+
|
11
|
+
before do
|
12
|
+
ActiveRecord::Base.logger = logger
|
13
|
+
end
|
14
|
+
|
15
|
+
after do
|
16
|
+
Slavery.disabled = false
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'it prefixes log messages with master' do
|
20
|
+
User.count
|
21
|
+
log.rewind
|
22
|
+
expect(log.read).to include('[master]')
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'it prefixes log messages with the slave connection' do
|
26
|
+
User.on_slave.count
|
27
|
+
log.rewind
|
28
|
+
expect(log.read).to include('[slave]')
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'it does nothing when slavery is disabled' do
|
32
|
+
Slavery.disabled = true
|
33
|
+
User.count
|
34
|
+
log.rewind
|
35
|
+
expect(log.read).to_not include('[master]')
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
data/spec/slavery_spec.rb
CHANGED
@@ -95,15 +95,15 @@ describe Slavery do
|
|
95
95
|
describe 'configuration' do
|
96
96
|
before do
|
97
97
|
# Backup connection and configs
|
98
|
-
@backup_conn = Slavery.instance_variable_get :@
|
98
|
+
@backup_conn = Slavery.instance_variable_get :@connection_holder
|
99
99
|
@backup_config = ActiveRecord::Base.configurations.dup
|
100
100
|
@backup_disabled = Slavery.disabled
|
101
|
-
Slavery.instance_variable_set :@
|
101
|
+
Slavery.instance_variable_set :@connection_holder, nil
|
102
102
|
end
|
103
103
|
|
104
104
|
after do
|
105
105
|
# Restore connection and configs
|
106
|
-
Slavery.instance_variable_set :@
|
106
|
+
Slavery.instance_variable_set :@connection_holder, @backup_conn
|
107
107
|
ActiveRecord::Base.configurations = @backup_config
|
108
108
|
Slavery.disabled = @backup_disabled
|
109
109
|
end
|
@@ -120,5 +120,29 @@ describe Slavery do
|
|
120
120
|
|
121
121
|
expect(Slavery.on_slave { User.count }).to be 2
|
122
122
|
end
|
123
|
+
|
124
|
+
it 'connects to slave when specified as a hash' do
|
125
|
+
Slavery.spec_key = 'test_slave'
|
126
|
+
hash = ActiveRecord::Base.configurations['test_slave']
|
127
|
+
expect(Slavery::ConnectionHolder).to receive(:establish_connection).with(hash)
|
128
|
+
Slavery::ConnectionHolder.activate
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'connects to slave when specified as a url' do
|
132
|
+
expected = if Gem::Version.new(ActiveRecord::VERSION::STRING) < Gem::Version.new('4.1.0')
|
133
|
+
'postgres://root:@localhost:5432/test_slave'
|
134
|
+
else
|
135
|
+
{
|
136
|
+
'adapter' => 'postgresql',
|
137
|
+
'username' => 'root',
|
138
|
+
'port' => 5432,
|
139
|
+
'database' => 'test_slave',
|
140
|
+
'host' => 'localhost'
|
141
|
+
}
|
142
|
+
end
|
143
|
+
Slavery.spec_key = 'test_slave_url'
|
144
|
+
expect(Slavery::ConnectionHolder).to receive(:establish_connection).with(expected)
|
145
|
+
Slavery::ConnectionHolder.activate
|
146
|
+
end
|
123
147
|
end
|
124
148
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -6,8 +6,9 @@ ENV['RACK_ENV'] = 'test'
|
|
6
6
|
require 'slavery'
|
7
7
|
|
8
8
|
ActiveRecord::Base.configurations = {
|
9
|
-
'test' =>
|
10
|
-
'test_slave'
|
9
|
+
'test' => { adapter: 'sqlite3', database: 'test_db' },
|
10
|
+
'test_slave' => { adapter: 'sqlite3', database: 'test_slave_db' },
|
11
|
+
'test_slave_url' => "postgres://root:@localhost:5432/test_slave"
|
11
12
|
}
|
12
13
|
|
13
14
|
# Prepare databases
|
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: 2.
|
4
|
+
version: 2.1.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: 2016-
|
11
|
+
date: 2016-12-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -55,24 +55,34 @@ dependencies:
|
|
55
55
|
description: Simple, conservative slave reads for ActiveRecord
|
56
56
|
email:
|
57
57
|
- kenn.ejima@gmail.com
|
58
|
-
executables:
|
58
|
+
executables:
|
59
|
+
- console
|
59
60
|
extensions: []
|
60
61
|
extra_rdoc_files: []
|
61
62
|
files:
|
62
63
|
- ".gitignore"
|
63
64
|
- ".rspec"
|
65
|
+
- ".travis.yml"
|
64
66
|
- Gemfile
|
65
67
|
- LICENSE.txt
|
66
68
|
- README.md
|
67
69
|
- Rakefile
|
70
|
+
- bin/console
|
71
|
+
- gemfiles/rails3.2.gemfile
|
72
|
+
- gemfiles/rails4.2.gemfile
|
73
|
+
- gemfiles/rails4.gemfile
|
68
74
|
- lib/slavery.rb
|
69
75
|
- lib/slavery/active_record/base.rb
|
76
|
+
- lib/slavery/active_record/connection_handling.rb
|
77
|
+
- lib/slavery/active_record/log_subscriber.rb
|
70
78
|
- lib/slavery/active_record/relation.rb
|
71
79
|
- lib/slavery/base.rb
|
80
|
+
- lib/slavery/connection_holder.rb
|
72
81
|
- lib/slavery/error.rb
|
73
|
-
- lib/slavery/
|
82
|
+
- lib/slavery/transaction.rb
|
74
83
|
- lib/slavery/version.rb
|
75
84
|
- slavery.gemspec
|
85
|
+
- spec/active_record/log_subscriber_spec.rb
|
76
86
|
- spec/slavery_spec.rb
|
77
87
|
- spec/spec_helper.rb
|
78
88
|
homepage: https://github.com/kenn/slavery
|
@@ -94,10 +104,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
94
104
|
version: '0'
|
95
105
|
requirements: []
|
96
106
|
rubyforge_project:
|
97
|
-
rubygems_version: 2.5.
|
107
|
+
rubygems_version: 2.5.2
|
98
108
|
signing_key:
|
99
109
|
specification_version: 4
|
100
110
|
summary: Simple, conservative slave reads for ActiveRecord
|
101
111
|
test_files:
|
112
|
+
- spec/active_record/log_subscriber_spec.rb
|
102
113
|
- spec/slavery_spec.rb
|
103
114
|
- spec/spec_helper.rb
|
@@ -1,13 +0,0 @@
|
|
1
|
-
module Slavery
|
2
|
-
class SlaveConnectionHolder < ActiveRecord::Base
|
3
|
-
self.abstract_class = true
|
4
|
-
|
5
|
-
class << self
|
6
|
-
# for delayed activation
|
7
|
-
def activate
|
8
|
-
raise Error.new('Slavery.spec_key invalid!') unless ActiveRecord::Base.configurations[Slavery.spec_key]
|
9
|
-
establish_connection Slavery.spec_key.to_sym
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|