master_slave_adapter_soundcloud 0.1.0 → 0.1.1
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.
- data/Readme.md +180 -0
- data/VERSION +1 -1
- data/master_slave_adapter.gemspec +1 -1
- metadata +4 -4
- data/README +0 -43
data/Readme.md
ADDED
@@ -0,0 +1,180 @@
|
|
1
|
+
# Replication Aware Master Slave Adapter
|
2
|
+
|
3
|
+
Improved version of the [master_slave_adapter plugin][1], packaged as a gem.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
1. automatic selection of master or slave connection: `with_consistency`
|
8
|
+
2. manual selection of master or slave connection: `with_master`, `with_slave`
|
9
|
+
3. transaction callbacks: `on_commit`, `on_rollback`
|
10
|
+
4. also:
|
11
|
+
* support for multiple slaves
|
12
|
+
* (partial) support for [database_cleaner][2]
|
13
|
+
|
14
|
+
### Automatic Selection of Master or Slave
|
15
|
+
|
16
|
+
* _note that this feature currently only works with MySQL_
|
17
|
+
* _see also this [blog post][3] for a more detailed explanation_
|
18
|
+
|
19
|
+
The adapter will run all reads against a slave database, unless a) the read is inside an open transaction or b) the
|
20
|
+
adapter determines that the slave lags behind the master _relative to the last write_. For this to work, an initial
|
21
|
+
initial consistency requirement ("`Clock`") must be passed to the adapter. Based on this clock value, the adapter
|
22
|
+
determines if a (randomly chosen) slave meets this requirement. If not, all statements are executed against master,
|
23
|
+
otherwise, the slave connection is used until either a transaction is opened or a write occurs. After a successful write
|
24
|
+
or transaction, the adapter determines a new consistency requirement, which is returned and can be used for subsequent
|
25
|
+
operations. Note that after a write or transaction, the adapter keeps using the master connection.
|
26
|
+
|
27
|
+
As an example, a Rails application could run the following function as an `around_filter`:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
def with_consistency_filter
|
31
|
+
if logged_in?
|
32
|
+
# it's a good idea to use this feature on a per-user basis
|
33
|
+
cache_key = [ CACHE_NAMESPACE, current_user.id.to_s ].join(":")
|
34
|
+
|
35
|
+
clock = cached_clock(cache_key) ||
|
36
|
+
ActiveRecord::Base.connection.master_clock
|
37
|
+
|
38
|
+
new_clock = ActiveRecord::Base.with_consistency(clock) do
|
39
|
+
# inside the controller, ActiveRecord models can be used just as normal.
|
40
|
+
# The adapter will take care of choosing the right connection.
|
41
|
+
yield
|
42
|
+
end
|
43
|
+
|
44
|
+
[ new_clock, clock ].compact.max.tap do |c|
|
45
|
+
cache_clock!(cache_key, c)
|
46
|
+
end if new_clock != clock
|
47
|
+
else
|
48
|
+
# anonymous users will have to wait until the slaves have caught up
|
49
|
+
with_slave { yield }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
Note that we use the current `master_clock` as a reference point. This will give the user a recent view of the data,
|
55
|
+
possibly reading from master, and if no write occurs inside the `with_consistency` block, we have a reasonable value to
|
56
|
+
cache and reuse on subsequent requests. Alternatively, we could have used
|
57
|
+
`ActiveRecord::ConnectionAdapters::MasterSlaveAdapter::Clock.zero` to indicate no particular consistency requirement.
|
58
|
+
Since `with_consistency` blocks can be nested, the controller code could later decide to require a more recent view on
|
59
|
+
the data.
|
60
|
+
|
61
|
+
### Manual Selection of Master or Slave
|
62
|
+
|
63
|
+
The original functionality of the adapter has been preserved:
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
ActiveRecord::Base.with_master do
|
67
|
+
# everything inside here will go to master
|
68
|
+
end
|
69
|
+
|
70
|
+
ActiveRecord::Base.with_slave do
|
71
|
+
# everything inside here will go to one of the slaves
|
72
|
+
# opening a transaction or writing will switch to master
|
73
|
+
# for the rest of the block
|
74
|
+
end
|
75
|
+
```
|
76
|
+
|
77
|
+
`with_master`, `with_slave` as well as `with_consistency` can be nested deliberately.
|
78
|
+
|
79
|
+
### Transaction Callbacks
|
80
|
+
|
81
|
+
This feature was originally developed at [SoundCloud][4] for the standard `MysqlAdapter`. It allows arbitrary blocks of
|
82
|
+
code to be deferred for execution until the next transaction completes (or rolls back).
|
83
|
+
|
84
|
+
```irb
|
85
|
+
irb> ActiveRecord::Base.on_commit { puts "COMMITTED!" }
|
86
|
+
irb> ActiveRecord::Base.on_rollback { puts "ROLLED BACK!" }
|
87
|
+
irb> ActiveRecord::Base.connection.transaction do
|
88
|
+
irb* # ...
|
89
|
+
irb> end
|
90
|
+
COMMITTED!
|
91
|
+
=> nil
|
92
|
+
irb> ActiveRecord::Base.connection.transaction do
|
93
|
+
irb* # ...
|
94
|
+
irb* raise "failed operation"
|
95
|
+
irb> end
|
96
|
+
ROLLED BACK!
|
97
|
+
# stack trace omitted
|
98
|
+
=> nil
|
99
|
+
```
|
100
|
+
|
101
|
+
Note that a transaction callback will be fired only *once*, so you might want to do:
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
class MyModel
|
105
|
+
after_save do
|
106
|
+
connection.on_commit do
|
107
|
+
# ...
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
```
|
112
|
+
|
113
|
+
### Support for Multiple Slaves
|
114
|
+
|
115
|
+
The adapter keeps a list of slave connections (see *Configuration*) and chooses randomly between them. The selection is
|
116
|
+
made at the beginning of a `with_slave` or `with_consistency` block and doesn't change until the block returns. Hence, a
|
117
|
+
nested `with_slave` or `with_consistency` might run against a different slave.
|
118
|
+
|
119
|
+
### Database Cleaner
|
120
|
+
|
121
|
+
At [SoundCloud][4], we're using [database_cleaner][2]'s 'truncation strategy' to wipe the database between [cucumber][5]
|
122
|
+
'feature's. As our cucumber suite proved valuable while testing the `with_consistency` feature, we had to support
|
123
|
+
`truncate_table` as an `ActiveRecord::Base.connection` instance method. We might add other strategies if there's enough
|
124
|
+
interest.
|
125
|
+
|
126
|
+
## Configuration
|
127
|
+
|
128
|
+
Example configuration for the development environment in `database.yml`:
|
129
|
+
|
130
|
+
```yaml
|
131
|
+
development:
|
132
|
+
adapter: master_slave # use master_slave adapter
|
133
|
+
connection_adapter: mysql # actual adapter to use (only mysql is supported atm)
|
134
|
+
disable_connection_test: false # when an instance is checked out from the connection pool,
|
135
|
+
# we check if the connections are still alive, reconnecting if necessary
|
136
|
+
# these values are picked up as defaults in the 'master' and 'slaves' sections:
|
137
|
+
database: aweapp_development
|
138
|
+
username: aweappuser
|
139
|
+
password: s3cr3t
|
140
|
+
|
141
|
+
master:
|
142
|
+
host: masterhost
|
143
|
+
username: readwrite_user # override default value
|
144
|
+
|
145
|
+
slaves:
|
146
|
+
- host: slave01
|
147
|
+
- host: slave02
|
148
|
+
```
|
149
|
+
|
150
|
+
## Installation
|
151
|
+
|
152
|
+
Using plain rubygems:
|
153
|
+
|
154
|
+
```sh
|
155
|
+
$ gem install master_slave_adapter_soundcloud
|
156
|
+
```
|
157
|
+
|
158
|
+
Using bundler:
|
159
|
+
|
160
|
+
```sh
|
161
|
+
$ cat >> Gemfile
|
162
|
+
gem 'master_slave_adapter_soundcloud', '~> 0.1', :require => 'master_slave_adaper'
|
163
|
+
^D
|
164
|
+
$ bundle install
|
165
|
+
```
|
166
|
+
|
167
|
+
## Credits
|
168
|
+
|
169
|
+
* Maurício Lenhares - _original master_slave_adapter plugin_
|
170
|
+
* Torsten Curdt - _with_consistency, maintainership & open source licenses_
|
171
|
+
* Sean Treadway - _chief everything & transaction callbacks_
|
172
|
+
* Kim Altintop - _strong lax monoidal endofunctors_
|
173
|
+
* Omid Aladini - _chief operator & everything else_
|
174
|
+
|
175
|
+
|
176
|
+
[1]: https://github.com/mauricio/master_slave_adapter
|
177
|
+
[2]: https://github.com/bmabey/database_cleaner
|
178
|
+
[3]: http://www.yourdailygeekery.com/2011/06/14/master-slave-consistency.html
|
179
|
+
[4]: http://backstage.soundcloud.com
|
180
|
+
[5]: http://cukes.info
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.1
|
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.require_path = 'lib'
|
19
19
|
|
20
20
|
s.required_ruby_version = '>= 1.9.2'
|
21
|
-
s.required_rubygems_version = '1.3.7'
|
21
|
+
s.required_rubygems_version = '>= 1.3.7'
|
22
22
|
s.add_development_dependency 'rspec'
|
23
23
|
|
24
24
|
s.add_dependency 'activerecord', '= 2.3.9'
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
version: 0.1.
|
8
|
+
- 1
|
9
|
+
version: 0.1.1
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Mauricio Linhares
|
@@ -61,8 +61,8 @@ files:
|
|
61
61
|
- .gitignore
|
62
62
|
- Gemfile
|
63
63
|
- LICENSE
|
64
|
-
- README
|
65
64
|
- Rakefile
|
65
|
+
- Readme.md
|
66
66
|
- VERSION
|
67
67
|
- lib/active_record/connection_adapters/master_slave_adapter.rb
|
68
68
|
- lib/master_slave_adapter.rb
|
@@ -90,7 +90,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
90
90
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
91
|
none: false
|
92
92
|
requirements:
|
93
|
-
- - "
|
93
|
+
- - ">="
|
94
94
|
- !ruby/object:Gem::Version
|
95
95
|
segments:
|
96
96
|
- 1
|
data/README
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
master_slave_adapter
|
2
|
-
====
|
3
|
-
|
4
|
-
This simple plugin acts as a common ActiveRecord adapter and allows you to
|
5
|
-
setup a master-slave environment using any database you like (and is supported
|
6
|
-
by ActiveRecord).
|
7
|
-
|
8
|
-
This plugin works by handling two connections, one to a master database,
|
9
|
-
that will receive all non-"SELECT" statements, and another to a slave database
|
10
|
-
that that is going to receive all SELECT statements. It also tries to do as
|
11
|
-
little black magic as possible, it works just like any other ActiveRecord database
|
12
|
-
adapter and performs no monkeypatching at all, so it's easy and simple to use
|
13
|
-
and understand.
|
14
|
-
|
15
|
-
The master database connection will also receive SELECT calls if a transaction
|
16
|
-
is active at the moment or if a command is executed inside a "with_master" block:
|
17
|
-
|
18
|
-
ActiveRecord::Base.with_master do # :with_master instructs the adapter
|
19
|
-
@users = User.all # to use the master connection inside the block
|
20
|
-
end
|
21
|
-
|
22
|
-
To use this adapter you just have to install the plugin:
|
23
|
-
|
24
|
-
ruby script/plugin install git://github.com/tcurdt/master_slave_adapter_mauricio.git
|
25
|
-
|
26
|
-
And then configure it at your database.yml file:
|
27
|
-
|
28
|
-
development:
|
29
|
-
database: sample_development
|
30
|
-
username: root
|
31
|
-
adapter: master_slave # the adapter must be set to "master_slave"
|
32
|
-
host: 10.21.34.80
|
33
|
-
master_slave_adapter: mysql # here's where you'll place the real database adapter name
|
34
|
-
disable_connection_test: true # this will disable the connection test before use,
|
35
|
-
# can possibly improve the performance but you could also
|
36
|
-
# hit stale connections, default is false
|
37
|
-
eager_load_connections: true # connections are lazy loaded by default, you can load gem eagerly setting this to true
|
38
|
-
master: # and here's where you'll add the master database configuration
|
39
|
-
database: talkies_development # you shouldn't specify an "adapter" here, the
|
40
|
-
username: root # value at "master_slave_adapter" is going to be used
|
41
|
-
host: 10.21.34.82
|
42
|
-
adapter: postgresql # you can use another adapter for the master connection if needed
|
43
|
-
# if you don't set it the "master_slave_adapter" property will be used
|