master_slave_adapter 0.2.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.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ tags
2
+ test/*
3
+ pkg/*
4
+ .rvmrc
5
+ Gemfile.lock
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
data/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ # 0.2.0 (April 2, 2012)
2
+
3
+ * Add support for ActiveRecord's query cache
4
+
5
+ # 0.1.10 (March 06, 2012)
6
+
7
+ * Delegate #visitor to master connection
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2011 Maurício Linhares,
2
+ Torsten Curdt,
3
+ Kim Altintop,
4
+ Omid Aladini,
5
+ SoundCloud Ltd
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all
15
+ copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'bundler'
2
+ require 'rspec/core/rake_task'
3
+
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ desc 'Run specs'
7
+ RSpec::Core::RakeTask.new
8
+
9
+ desc 'Default: Run specs'
10
+ task :default => :spec
data/Readme.md ADDED
@@ -0,0 +1,181 @@
1
+ # Replication Aware Master Slave Adapter [![Build Status](https://secure.travis-ci.org/soundcloud/large-hadron-migrator.png)][6]
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
181
+ [6]: http://travis-ci.org/soundcloud/master_slave_adapter
data/TODO.txt ADDED
@@ -0,0 +1,8 @@
1
+ Read only mode
2
+ --------------
3
+
4
+ - Add better fallback to connection_for_read
5
+
6
+ - write tests
7
+ - extract adapter specific code
8
+ - make everything nice
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.0
@@ -0,0 +1 @@
1
+ require 'master_slave_adapter'
@@ -0,0 +1 @@
1
+ require 'master_slave_adapter'