multi_db 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +30 -0
- data/lib/multi_db/connection_proxy.rb +16 -6
- data/multi_db.gemspec +1 -1
- data/spec/config/database.yml +19 -0
- data/spec/connection_proxy_spec.rb +215 -155
- data/spec/spec_helper.rb +1 -1
- metadata +1 -1
data/README.rdoc
CHANGED
@@ -137,6 +137,35 @@ And add this to your ApplicationController:
|
|
137
137
|
*NOTE*: It's not possible to toggle this mode in a running process, as the dynamically
|
138
138
|
generated methods will have the initially defined "stickyness" built in.
|
139
139
|
|
140
|
+
=== Using the weighted scheduler
|
141
|
+
The standard scheduler roundrobins queries to evenly to all slaves. This means that if you're using servers with different capacity (slower machines, some slaves receiving traffic from other apps etc) you might run into problems. The weighted scheduler tries to address this by assigning a weight attribute to each slave and distribute queries evenly among the server pool.
|
142
|
+
|
143
|
+
In your database.yml file add your weights like so:
|
144
|
+
test_slave_database_1:
|
145
|
+
<<: *creds
|
146
|
+
host: my.slavedb_1
|
147
|
+
weight: 1
|
148
|
+
|
149
|
+
test_slave_database_2:
|
150
|
+
<<: *creds
|
151
|
+
host: my.slavedb_2
|
152
|
+
weight: 10
|
153
|
+
|
154
|
+
The above configuration will lead to slavedb_2 to receive 9 times more queries than slavedb_1. Adding in a new slave with:
|
155
|
+
test_slave_database_3:
|
156
|
+
<<: *creds
|
157
|
+
host: my.slavedb_3
|
158
|
+
weight: 5
|
159
|
+
|
160
|
+
leads to a distribution of 1:10:5. For 100k queries the numbers could look like this:
|
161
|
+
Slave 1, with weight 1: 6302 queries
|
162
|
+
Slave 2, with weight 10: 62764 queries
|
163
|
+
Slave 3, with weight 5: 30934 queries
|
164
|
+
|
165
|
+
The weighted scheduler does not guarantee that the same slave will not receive two queries in a row. We feel this is not an issue, or rather, that such a guarantee doesn't help much as it's the complexity of the queries rather than the number that creates problems.
|
166
|
+
|
167
|
+
If no weight param is given for a slave, a weight of 1 is assumed. A weight of 0 is caught and silently transformed into a weight of 1.
|
168
|
+
|
140
169
|
=== Usage outside of Rails
|
141
170
|
|
142
171
|
You can use multi_db together with other framworks or in standalone scripts.
|
@@ -216,6 +245,7 @@ threadsafe and allows sharding of data.
|
|
216
245
|
|
217
246
|
=== Contributors
|
218
247
|
|
248
|
+
* David Palm http://github.com/dvdplm
|
219
249
|
* Matt Conway http://github.com/wr0ngway
|
220
250
|
* Matthias Marshall http://github.com/webops
|
221
251
|
|
@@ -40,7 +40,7 @@ module MultiDb
|
|
40
40
|
|
41
41
|
# Replaces the connection of ActiveRecord::Base with a proxy and
|
42
42
|
# establishes the connections to the slaves.
|
43
|
-
def setup!
|
43
|
+
def setup!(scheduler = Scheduler)
|
44
44
|
self.master_models ||= DEFAULT_MASTER_MODELS
|
45
45
|
self.environment ||= (defined?(RAILS_ENV) ? RAILS_ENV : 'development')
|
46
46
|
self.sticky_slave ||= false
|
@@ -50,10 +50,10 @@ module MultiDb
|
|
50
50
|
raise "No slaves databases defined for environment: #{self.environment}" if slaves.empty?
|
51
51
|
master.send :include, MultiDb::ActiveRecordExtensions
|
52
52
|
ActiveRecord::Observer.send :include, MultiDb::ObserverExtensions
|
53
|
-
master.connection_proxy = new(master, slaves)
|
53
|
+
master.connection_proxy = new(master, slaves, scheduler)
|
54
54
|
master.logger.info("** multi_db with master and #{slaves.length} slave#{"s" if slaves.length > 1} loaded.")
|
55
55
|
end
|
56
|
-
|
56
|
+
|
57
57
|
protected
|
58
58
|
|
59
59
|
# Slave entries in the database.yml must be named like this
|
@@ -65,12 +65,18 @@ module MultiDb
|
|
65
65
|
# These would be available later as MultiDb::SlaveDatabaseSomeserver
|
66
66
|
def init_slaves
|
67
67
|
returning([]) do |slaves|
|
68
|
-
ActiveRecord::Base.configurations.
|
68
|
+
ActiveRecord::Base.configurations.each do |name, values|
|
69
69
|
if name.to_s =~ /#{self.environment}_(slave_database.*)/
|
70
|
+
weight = if values['weight'].blank?
|
71
|
+
1
|
72
|
+
else
|
73
|
+
(v=values['weight'].to_i.abs).zero?? 1 : v
|
74
|
+
end
|
70
75
|
MultiDb.module_eval %Q{
|
71
76
|
class #{$1.camelize} < ActiveRecord::Base
|
72
77
|
self.abstract_class = true
|
73
78
|
establish_connection :#{name}
|
79
|
+
WEIGHT = #{weight} unless const_defined?('WEIGHT')
|
74
80
|
end
|
75
81
|
}, __FILE__, __LINE__
|
76
82
|
slaves << "MultiDb::#{$1.camelize}".constantize
|
@@ -83,8 +89,8 @@ module MultiDb
|
|
83
89
|
|
84
90
|
end
|
85
91
|
|
86
|
-
def initialize(master, slaves)
|
87
|
-
@slaves =
|
92
|
+
def initialize(master, slaves, scheduler = Scheduler)
|
93
|
+
@slaves = scheduler.new(slaves)
|
88
94
|
@master = master
|
89
95
|
@reconnect = false
|
90
96
|
self.current = @slaves.current
|
@@ -94,6 +100,10 @@ module MultiDb
|
|
94
100
|
def slave
|
95
101
|
@slaves.current
|
96
102
|
end
|
103
|
+
|
104
|
+
def scheduler
|
105
|
+
@slaves
|
106
|
+
end
|
97
107
|
|
98
108
|
def with_master
|
99
109
|
self.current = @master
|
data/multi_db.gemspec
CHANGED
data/spec/config/database.yml
CHANGED
@@ -21,3 +21,22 @@ test_slave_database_2:
|
|
21
21
|
password:
|
22
22
|
host: 127.0.0.1
|
23
23
|
pool: 5
|
24
|
+
weight: 10
|
25
|
+
|
26
|
+
test_slave_database_3:
|
27
|
+
adapter: mysql
|
28
|
+
database: multi_db_test
|
29
|
+
username: root
|
30
|
+
password:
|
31
|
+
host: 127.0.0.1
|
32
|
+
pool: 5
|
33
|
+
weight: 5
|
34
|
+
|
35
|
+
test_slave_database_4:
|
36
|
+
adapter: mysql
|
37
|
+
database: multi_db_test
|
38
|
+
username: root
|
39
|
+
password:
|
40
|
+
host: 127.0.0.1
|
41
|
+
pool: 5
|
42
|
+
weight: 10
|
@@ -20,210 +20,270 @@ describe MultiDb::ConnectionProxy do
|
|
20
20
|
@sql = 'SELECT 1 + 1 FROM DUAL'
|
21
21
|
end
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
end
|
23
|
+
describe "with standard Scheduler" do
|
24
|
+
before(:each) do
|
25
|
+
MultiDb::ConnectionProxy.master_models = ['MasterModel']
|
26
|
+
MultiDb::ConnectionProxy.setup!
|
27
|
+
@proxy = ActiveRecord::Base.connection_proxy
|
28
|
+
@master = @proxy.master.retrieve_connection
|
29
|
+
@slave1 = MultiDb::SlaveDatabase1.retrieve_connection
|
30
|
+
@slave2 = MultiDb::SlaveDatabase2.retrieve_connection
|
31
|
+
@slave3 = MultiDb::SlaveDatabase3.retrieve_connection
|
32
|
+
@slave4 = MultiDb::SlaveDatabase4.retrieve_connection
|
33
|
+
end
|
35
34
|
|
36
|
-
|
37
|
-
|
38
|
-
|
35
|
+
after(:each) do
|
36
|
+
ActiveRecord::Base.send :alias_method, :reload, :reload_without_master
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'AR::B should respond to #connection_proxy' do
|
40
|
+
ActiveRecord::Base.connection_proxy.should be_kind_of(MultiDb::ConnectionProxy)
|
41
|
+
end
|
39
42
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
+
it 'FooModel#connection should return an instance of MultiDb::ConnectionProxy' do
|
44
|
+
FooModel.connection.should be_kind_of(MultiDb::ConnectionProxy)
|
45
|
+
end
|
43
46
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
+
it 'MasterModel#connection should not return an instance of MultiDb::ConnectionProxy' do
|
48
|
+
MasterModel.connection.should_not be_kind_of(MultiDb::ConnectionProxy)
|
49
|
+
end
|
47
50
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
51
|
+
it "should generate classes for each entry in the database.yml" do
|
52
|
+
defined?(MultiDb::SlaveDatabase1).should_not be_nil
|
53
|
+
defined?(MultiDb::SlaveDatabase2).should_not be_nil
|
54
|
+
end
|
52
55
|
|
53
|
-
|
54
|
-
|
55
|
-
@proxy.with_master do
|
56
|
-
@proxy.current.should == @proxy.master
|
56
|
+
it 'should handle nested with_master-blocks correctly' do
|
57
|
+
@proxy.current.should_not == @proxy.master
|
57
58
|
@proxy.with_master do
|
58
59
|
@proxy.current.should == @proxy.master
|
59
60
|
@proxy.with_master do
|
60
61
|
@proxy.current.should == @proxy.master
|
62
|
+
@proxy.with_master do
|
63
|
+
@proxy.current.should == @proxy.master
|
64
|
+
end
|
65
|
+
@proxy.current.should == @proxy.master
|
61
66
|
end
|
62
67
|
@proxy.current.should == @proxy.master
|
63
68
|
end
|
64
|
-
@proxy.current.
|
69
|
+
@proxy.current.should_not == @proxy.master
|
65
70
|
end
|
66
|
-
@proxy.current.should_not == @proxy.master
|
67
|
-
end
|
68
71
|
|
69
|
-
|
70
|
-
|
71
|
-
@proxy.select_all(@sql)
|
72
|
-
ActiveRecord::Base.transaction do
|
72
|
+
it 'should perform transactions on the master' do
|
73
|
+
@master.should_receive(:select_all).exactly(1) # makes sure the first one goes to a slave
|
73
74
|
@proxy.select_all(@sql)
|
75
|
+
ActiveRecord::Base.transaction do
|
76
|
+
@proxy.select_all(@sql)
|
77
|
+
end
|
74
78
|
end
|
75
|
-
end
|
76
79
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
80
|
+
it 'should switch to the next reader on selects' do
|
81
|
+
@slave1.should_receive(:select_one).exactly(2)
|
82
|
+
@slave2.should_receive(:select_one).exactly(2)
|
83
|
+
6.times { @proxy.select_one(@sql) }
|
84
|
+
end
|
82
85
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
86
|
+
it 'should not switch to the next reader when whithin a with_master-block' do
|
87
|
+
@master.should_receive(:select_one).twice
|
88
|
+
@slave1.should_not_receive(:select_one)
|
89
|
+
@slave2.should_not_receive(:select_one)
|
90
|
+
@proxy.with_master do
|
91
|
+
2.times { @proxy.select_one(@sql) }
|
92
|
+
end
|
89
93
|
end
|
90
|
-
end
|
91
94
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
95
|
+
it 'should send dangerous methods to the master' do
|
96
|
+
meths = [:insert, :update, :delete, :execute]
|
97
|
+
meths.each do |meth|
|
98
|
+
@slave1.stub!(meth).and_raise(RuntimeError)
|
99
|
+
@master.should_receive(meth).and_return(true)
|
100
|
+
@proxy.send(meth, @sql)
|
101
|
+
end
|
98
102
|
end
|
99
|
-
end
|
100
103
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
end
|
106
|
-
|
107
|
-
it 'should cache queries using select_all' do
|
108
|
-
ActiveRecord::Base.cache do
|
109
|
-
# next_reader will be called and switch to the SlaveDatabase2
|
110
|
-
@slave2.should_receive(:select_all).exactly(1)
|
111
|
-
@slave1.should_not_receive(:select_all)
|
112
|
-
@master.should_not_receive(:select_all)
|
113
|
-
3.times { @proxy.select_all(@sql) }
|
104
|
+
it 'should dynamically generate safe methods' do
|
105
|
+
@proxy.should_not respond_to(:select_value)
|
106
|
+
@proxy.select_value(@sql)
|
107
|
+
@proxy.should respond_to(:select_value)
|
114
108
|
end
|
115
|
-
end
|
116
109
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
@
|
110
|
+
it 'should cache queries using select_all' do
|
111
|
+
ActiveRecord::Base.cache do
|
112
|
+
# next_reader will be called and switch to the SlaveDatabase2
|
113
|
+
@slave2.should_receive(:select_all).exactly(1)
|
114
|
+
@slave1.should_not_receive(:select_all)
|
115
|
+
@master.should_not_receive(:select_all)
|
116
|
+
3.times { @proxy.select_all(@sql) }
|
122
117
|
end
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'should invalidate the cache on insert, delete and update' do
|
121
|
+
ActiveRecord::Base.cache do
|
122
|
+
meths = [:insert, :update, :delete, :insert, :update]
|
123
|
+
meths.each do |meth|
|
124
|
+
@master.should_receive(meth).and_return(true)
|
125
|
+
end
|
126
|
+
@slave2.should_receive(:select_all).twice
|
127
|
+
@slave1.should_receive(:select_all).once
|
128
|
+
5.times do |i|
|
129
|
+
@proxy.select_all(@sql)
|
130
|
+
@proxy.send(meths[i])
|
131
|
+
end
|
128
132
|
end
|
129
133
|
end
|
130
|
-
end
|
131
134
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
135
|
+
it 'should retry the next slave when one fails and finally fall back to the master' do
|
136
|
+
@slave1.should_receive(:select_all).once.and_raise(RuntimeError)
|
137
|
+
@slave2.should_receive(:select_all).once.and_raise(RuntimeError)
|
138
|
+
@slave3.should_receive(:select_all).once.and_raise(RuntimeError)
|
139
|
+
@slave4.should_receive(:select_all).once.and_raise(RuntimeError)
|
140
|
+
@master.should_receive(:select_all).and_return(true)
|
141
|
+
@proxy.select_all(@sql)
|
142
|
+
end
|
138
143
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
144
|
+
it 'should try to reconnect the master connection after the master has failed' do
|
145
|
+
@master.should_receive(:update).and_raise(RuntimeError)
|
146
|
+
lambda { @proxy.update(@sql) }.should raise_error
|
147
|
+
@master.should_receive(:reconnect!).and_return(true)
|
148
|
+
@master.should_receive(:insert).and_return(1)
|
149
|
+
@proxy.insert(@sql)
|
150
|
+
end
|
146
151
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
152
|
+
it 'should reload models from the master' do
|
153
|
+
foo = FooModel.create!(:bar => 'baz')
|
154
|
+
foo.bar = "not_saved"
|
155
|
+
@slave1.should_not_receive(:select_all)
|
156
|
+
@slave2.should_not_receive(:select_all)
|
157
|
+
foo.reload
|
158
|
+
# we didn't stub @master#select_all here, check that we actually hit the db
|
159
|
+
foo.bar.should == 'baz'
|
160
|
+
end
|
156
161
|
|
157
|
-
|
162
|
+
describe 'with sticky_slave ' do
|
158
163
|
|
159
|
-
|
160
|
-
|
164
|
+
before { MultiDb::ConnectionProxy.sticky_slave = true }
|
165
|
+
after { MultiDb::ConnectionProxy.sticky_slave = false }
|
161
166
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
+
it 'should not switch to the next reader automatically' do
|
168
|
+
@slave1.should_receive(:select_all).exactly(3)
|
169
|
+
@slave2.should_receive(:select_all).exactly(0)
|
170
|
+
3.times { @proxy.select_all(@sql) }
|
171
|
+
end
|
167
172
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
173
|
+
it '#next_reader! should switch to the next slave' do
|
174
|
+
@slave1.should_receive(:select_one).exactly(3)
|
175
|
+
@slave2.should_receive(:select_one).exactly(7)
|
176
|
+
3.times { @proxy.select_one(@sql) }
|
177
|
+
@proxy.next_reader!
|
178
|
+
7.times { @proxy.select_one(@sql) }
|
179
|
+
end
|
175
180
|
|
176
|
-
|
181
|
+
end
|
177
182
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
183
|
+
describe '(accessed from multiple threads)' do
|
184
|
+
# NOTE: We cannot put expectations on the connection objects itself
|
185
|
+
# for the threading specs, as connection pooling will cause
|
186
|
+
# different connections being returned for different threads.
|
182
187
|
|
183
|
-
|
184
|
-
@proxy.current.should == MultiDb::SlaveDatabase1
|
185
|
-
@proxy.next_reader!.should == MultiDb::SlaveDatabase2
|
186
|
-
Thread.new do
|
188
|
+
it '#current and #next_reader! should be local to the thread' do
|
187
189
|
@proxy.current.should == MultiDb::SlaveDatabase1
|
188
190
|
@proxy.next_reader!.should == MultiDb::SlaveDatabase2
|
191
|
+
Thread.new do
|
192
|
+
@proxy.current.should == MultiDb::SlaveDatabase1
|
193
|
+
@proxy.next_reader!.should == MultiDb::SlaveDatabase2
|
194
|
+
@proxy.current.should == MultiDb::SlaveDatabase2
|
195
|
+
@proxy.next_reader!.should == MultiDb::SlaveDatabase1
|
196
|
+
@proxy.current.should == MultiDb::SlaveDatabase1
|
197
|
+
end
|
189
198
|
@proxy.current.should == MultiDb::SlaveDatabase2
|
190
|
-
@proxy.next_reader!.should == MultiDb::SlaveDatabase1
|
191
|
-
@proxy.current.should == MultiDb::SlaveDatabase1
|
192
199
|
end
|
193
|
-
@proxy.current.should == MultiDb::SlaveDatabase2
|
194
|
-
end
|
195
200
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
201
|
+
it '#with_master should be local to the thread' do
|
202
|
+
@proxy.current.should_not == @proxy.master
|
203
|
+
@proxy.with_master do
|
204
|
+
@proxy.current.should == @proxy.master
|
205
|
+
Thread.new do
|
206
|
+
@proxy.current.should_not == @proxy.master
|
207
|
+
@proxy.with_master do
|
208
|
+
@proxy.current.should == @proxy.master
|
209
|
+
end
|
210
|
+
@proxy.current.should_not == @proxy.master
|
204
211
|
end
|
205
|
-
@proxy.current.
|
212
|
+
@proxy.current.should == @proxy.master
|
206
213
|
end
|
207
|
-
@proxy.current.
|
214
|
+
@proxy.current.should_not == @proxy.master
|
208
215
|
end
|
209
|
-
@proxy.current.should_not == @proxy.master
|
210
|
-
end
|
211
216
|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
217
|
+
it 'should switch to the next reader even whithin with_master-block in different threads' do
|
218
|
+
# Because of connection pooling in AR 2.2, the second thread will cause
|
219
|
+
# a new connection being created behind the scenes. We therefore just test
|
220
|
+
# that these connections are beting retrieved for the right databases here.
|
221
|
+
@proxy.master.should_not_receive(:retrieve_connection).and_return(@master)
|
222
|
+
MultiDb::SlaveDatabase1.should_receive(:retrieve_connection).twice.and_return(@slave1)
|
223
|
+
MultiDb::SlaveDatabase2.should_receive(:retrieve_connection).once.and_return(@slave2)
|
224
|
+
MultiDb::SlaveDatabase3.should_receive(:retrieve_connection).once.and_return(@slave3)
|
225
|
+
MultiDb::SlaveDatabase4.should_receive(:retrieve_connection).once.and_return(@slave4)
|
226
|
+
@proxy.with_master do
|
227
|
+
Thread.new do
|
228
|
+
5.times { @proxy.select_one(@sql) }
|
229
|
+
end.join
|
230
|
+
end
|
223
231
|
end
|
232
|
+
|
224
233
|
end
|
225
234
|
|
226
|
-
end
|
235
|
+
end # with normal scheduler
|
227
236
|
|
237
|
+
describe "alternative scheduler" do
|
238
|
+
class MyScheduler
|
239
|
+
def initialize(slaves); :done; end
|
240
|
+
def current; :current; end
|
241
|
+
def next_reader!; :next end
|
242
|
+
def blacklist!; :blacklisted; end
|
243
|
+
end
|
244
|
+
|
245
|
+
it "has a 'scheduler' method that returns the current scheduler instance" do
|
246
|
+
my_scheduler = mock('My Scheduler!', :current => nil)
|
247
|
+
MyScheduler.should_receive(:new).and_return(my_scheduler)
|
248
|
+
MultiDb::ConnectionProxy.setup!(MyScheduler)
|
249
|
+
ActiveRecord::Base.connection_proxy.should respond_to(:scheduler)
|
250
|
+
ActiveRecord::Base.connection_proxy.scheduler.should be(my_scheduler)
|
251
|
+
end
|
252
|
+
|
253
|
+
it "can be initialized with an optional alternative scheduling class" do
|
254
|
+
slaves = MultiDb::ConnectionProxy.send(:init_slaves)
|
255
|
+
proxy = MultiDb::ConnectionProxy.send(:new, @master, slaves, MyScheduler)
|
256
|
+
proxy.scheduler.should be_an_instance_of(MyScheduler)
|
257
|
+
end
|
258
|
+
|
259
|
+
it "uses an alternative scheduler if setup! is called with a compatible class" do
|
260
|
+
MultiDb::ConnectionProxy.setup!(MyScheduler)
|
261
|
+
ActiveRecord::Base.connection_proxy.scheduler.should be_an_instance_of(MyScheduler)
|
262
|
+
end
|
263
|
+
|
264
|
+
it "uses the default scheduler if no param is passed" do
|
265
|
+
MultiDb::ConnectionProxy.setup!
|
266
|
+
ActiveRecord::Base.connection_proxy.scheduler.should be_an_instance_of(MultiDb::Scheduler)
|
267
|
+
end
|
268
|
+
|
269
|
+
describe "has weights for query distribution" do
|
270
|
+
before do
|
271
|
+
MultiDb::ConnectionProxy.setup!
|
272
|
+
end
|
273
|
+
|
274
|
+
it "adds a WEIGHT constant to the MultiDb::SlaveDatabaseN 'models'" do
|
275
|
+
MultiDb::SlaveDatabase1.const_defined?('WEIGHT').should be_true
|
276
|
+
end
|
277
|
+
|
278
|
+
it "sets the WEIGHT to 1 if no weight is configured" do
|
279
|
+
MultiDb::SlaveDatabase1::WEIGHT.should == 1
|
280
|
+
end
|
281
|
+
|
282
|
+
it "sets the WEIGHT to whatever it is configured to" do
|
283
|
+
MultiDb::SlaveDatabase2::WEIGHT.should == 10
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
end # with alternative scheduler
|
228
288
|
end
|
229
289
|
|
data/spec/spec_helper.rb
CHANGED