xbar 0.0.1 → 0.4.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/Appraisals +25 -0
- data/README.mkdn +215 -0
- data/Rakefile +337 -1
- data/examples/README +5 -0
- data/examples/config/simple.json +22 -0
- data/examples/example1.rb +34 -0
- data/examples/migrations/1_create_users.rb +10 -0
- data/examples/setup.rb +43 -0
- data/gemfiles/rails3.gemfile +8 -0
- data/gemfiles/rails3.gemfile.lock +74 -0
- data/gemfiles/rails31.gemfile +8 -0
- data/gemfiles/rails31.gemfile.lock +83 -0
- data/gemfiles/rails32.gemfile +7 -0
- data/gemfiles/rails32.gemfile.lock +117 -0
- data/gemfiles/rails4.gemfile +9 -0
- data/gemfiles/rails4.gemfile.lock +134 -0
- data/lib/migrations/1_create_usage_statistics.rb +23 -0
- data/lib/xbar/association.rb +49 -0
- data/lib/xbar/association_collection.rb +69 -0
- data/lib/xbar/colors.rb +32 -0
- data/lib/xbar/has_and_belongs_to_many_association.rb +17 -0
- data/lib/xbar/logger.rb +14 -0
- data/lib/xbar/mapper.rb +304 -0
- data/lib/xbar/migration.rb +76 -0
- data/lib/xbar/model.rb +165 -0
- data/lib/xbar/proxy.rb +249 -0
- data/lib/xbar/rails2/association.rb +133 -0
- data/lib/xbar/rails2/persistence.rb +39 -0
- data/lib/xbar/rails3/arel.rb +13 -0
- data/lib/xbar/rails3/association.rb +112 -0
- data/lib/xbar/rails3/persistence.rb +37 -0
- data/lib/xbar/rails3.1/singular_association.rb +34 -0
- data/lib/xbar/scope_proxy.rb +55 -0
- data/lib/xbar/shard.rb +95 -0
- data/lib/xbar/version.rb +2 -2
- data/lib/xbar.rb +121 -2
- data/run +27 -0
- data/spec/config/acme.json +53 -0
- data/spec/config/connection.rb +2 -0
- data/spec/config/default.json +160 -0
- data/spec/config/duplicate_shard.json +21 -0
- data/spec/config/missing_key.json +20 -0
- data/spec/config/new_shards.json +29 -0
- data/spec/config/no_master_shard.json +19 -0
- data/spec/config/not_entire_sharded.json +23 -0
- data/spec/config/octopus.json +27 -0
- data/spec/config/octopus_rails.json +25 -0
- data/spec/config/production_fully_replicated.json +21 -0
- data/spec/config/production_raise_error.json +17 -0
- data/spec/config/simple.json +22 -0
- data/spec/config/single_adapter.json +20 -0
- data/spec/console.rb +15 -0
- data/spec/migrations/10_create_users_using_replication.rb +12 -0
- data/spec/migrations/11_add_field_in_all_slaves.rb +11 -0
- data/spec/migrations/12_create_users_using_block.rb +23 -0
- data/spec/migrations/13_create_users_using_block_and_using.rb +15 -0
- data/spec/migrations/1_create_users_on_master.rb +9 -0
- data/spec/migrations/2_create_users_on_canada.rb +11 -0
- data/spec/migrations/3_create_users_on_both_shards.rb +11 -0
- data/spec/migrations/4_create_users_on_shards_of_a_group.rb +11 -0
- data/spec/migrations/5_create_users_on_multiples_groups.rb +11 -0
- data/spec/migrations/6_raise_exception_with_invalid_shard_name.rb +11 -0
- data/spec/migrations/7_raise_exception_with_invalid_multiple_shard_names.rb +11 -0
- data/spec/migrations/8_raise_exception_with_invalid_group_name.rb +11 -0
- data/spec/migrations/9_raise_exception_with_multiple_invalid_group_names.rb +11 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/support/database_models.rb +78 -0
- data/spec/support/xbar_helper.rb +42 -0
- data/spec/xbar/association_spec.rb +660 -0
- data/spec/xbar/controller_spec.rb +40 -0
- data/spec/xbar/logger_spec.rb +22 -0
- data/spec/xbar/mapper_spec.rb +283 -0
- data/spec/xbar/migration_spec.rb +110 -0
- data/spec/xbar/model_spec.rb +434 -0
- data/spec/xbar/proxy_spec.rb +124 -0
- data/spec/xbar/replication_spec.rb +94 -0
- data/spec/xbar/scope_proxy_spec.rb +22 -0
- data/spec/xbar/shard_spec.rb +36 -0
- data/xbar.gemspec +13 -3
- metadata +231 -10
@@ -0,0 +1,434 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe XBar::Model do
|
4
|
+
before(:each) do
|
5
|
+
set_xbar_env('default', 'test')
|
6
|
+
end
|
7
|
+
describe "#using method" do
|
8
|
+
it "should return self after calling the #using method" do
|
9
|
+
User.using(:canada).should == XBar::ScopeProxy.new(:canada, User)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should allow to send a block to the master shard" do
|
13
|
+
XBar.using(:master) do
|
14
|
+
User.create!(:name => "Block test")
|
15
|
+
end
|
16
|
+
|
17
|
+
User.using(:master).find_by_name("Block test").should_not be_nil
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should allow to pass a string as the shard name to a AR subclass' do
|
21
|
+
User.using('canada').create!(:name => 'Rafael Pilha')
|
22
|
+
|
23
|
+
User.using('canada').find_by_name('Rafael Pilha').should_not be_nil
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should allow to pass a string as the shard name to a block' do
|
27
|
+
XBar.using('canada') do
|
28
|
+
User.create!(:name => 'Rafael Pilha')
|
29
|
+
end
|
30
|
+
|
31
|
+
User.using('canada').find_by_name('Rafael Pilha').should_not be_nil
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should allow selecting the shards on scope" do
|
35
|
+
User.using(:canada).create!(:name => 'oi')
|
36
|
+
User.using(:canada).count.should == 1
|
37
|
+
User.count.should == 0
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should allow selecting the shard using #new" do
|
41
|
+
|
42
|
+
u = User.using(:canada).new
|
43
|
+
u.name = "Thiago"
|
44
|
+
u.save
|
45
|
+
|
46
|
+
User.using(:canada).count.should == 1
|
47
|
+
User.using(:brazil).count.should == 0
|
48
|
+
|
49
|
+
u1 = User.new
|
50
|
+
u1.name = "Joaquim"
|
51
|
+
|
52
|
+
u2 = User.using(:canada).new
|
53
|
+
u2.name = "Manuel"
|
54
|
+
u1.save
|
55
|
+
u2.save
|
56
|
+
User.using(:canada).all
|
57
|
+
|
58
|
+
User.using(:canada).all.should == [u, u2] # XXX
|
59
|
+
User.all.should == [u1]
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should select the correct shard" do
|
64
|
+
User.using(:canada)
|
65
|
+
User.create!(:name => 'oi')
|
66
|
+
User.count.should == 1
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should ensure that the connection will be cleaned" do
|
70
|
+
ActiveRecord::Base.connection.current_shard.should == :master
|
71
|
+
begin
|
72
|
+
XBar.using(:canada) do
|
73
|
+
raise "Some Exception"
|
74
|
+
end
|
75
|
+
rescue
|
76
|
+
end
|
77
|
+
|
78
|
+
ActiveRecord::Base.connection.current_shard.should == :master
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should allow creating more than one user" do
|
82
|
+
XBar.debug = true
|
83
|
+
User.using(:canada).create([{ :name => 'America User 1' }, { :name => 'America User 2' }])
|
84
|
+
User.create!(:name => "Thiago")
|
85
|
+
User.using(:canada).find_by_name("America User 1").should_not be_nil
|
86
|
+
User.using(:canada).find_by_name("America User 2")#.should_not be_nil
|
87
|
+
User.using(:master).find_by_name("Thiago").should_not be_nil
|
88
|
+
User.all.size.should == 1 # fail -- America User 2 is being created on master!!!
|
89
|
+
XBar.debug = false
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should work when you have a SQLite3 shard" do
|
93
|
+
u = User.using(:paris).create!(:name => "Sqlite3")
|
94
|
+
User.using(:paris).find_by_name("Sqlite3").should == u
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should clean #current_shard from proxy when using execute" do
|
98
|
+
User.using(:canada).connection().execute("select * from users limit 1;")
|
99
|
+
User.connection.current_shard.should == :master
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should allow scoping dynamically" do
|
103
|
+
User.using(:canada).using(:master).using(:canada).create!(:name => 'oi')
|
104
|
+
User.using(:canada).using(:master).count.should == 0
|
105
|
+
User.using(:master).using(:canada).count.should == 1
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should allow find inside blocks" do
|
109
|
+
@user = User.using(:brazil).create!(:name => "Thiago")
|
110
|
+
|
111
|
+
XBar.using(:brazil) do
|
112
|
+
User.first.should == @user
|
113
|
+
end
|
114
|
+
|
115
|
+
User.using(:brazil).find_by_name("Thiago").should == @user
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should clean the current_shard after executing the current query" do
|
119
|
+
User.using(:canada).create!(:name => "oi")
|
120
|
+
User.count.should == 0
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should support both groups and alone shards" do
|
124
|
+
u = User.using(:london).create!(:name => "Alone")
|
125
|
+
User.using(:london).count.should == 1
|
126
|
+
User.using(:canada).count.should == 0
|
127
|
+
User.using(:brazil).count.should == 0
|
128
|
+
User.count.should == 0
|
129
|
+
end
|
130
|
+
|
131
|
+
describe "#current_shard attribute" do
|
132
|
+
it "should store the attribute when you create or find an object" do
|
133
|
+
u = User.using(:london).create!(:name => "Alone")
|
134
|
+
u.current_shard.should == :london
|
135
|
+
User.using(:canada).create!(:name => 'oi')
|
136
|
+
u = User.using(:canada).find_by_name("oi")
|
137
|
+
u.current_shard.should == :canada
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should store the attribute when you find multiple instances" do
|
141
|
+
5.times { User.using(:london).create!(:name => "Alone") }
|
142
|
+
|
143
|
+
User.using(:london).all.each do |u|
|
144
|
+
u.current_shard.should == :london
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
it "should works when you find, and after that, alter that object" do
|
149
|
+
alone_user = User.using(:london).create!(:name => "Alone")
|
150
|
+
master_user = User.using(:master).create!(:name => "Master")
|
151
|
+
alone_user.name = "teste"
|
152
|
+
alone_user.save
|
153
|
+
User.using(:master).find(:first).name.should == "Master"
|
154
|
+
User.using(:london).find(:first).name.should == "teste"
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should work for the reload method" do
|
158
|
+
User.using(:london).create!(:name => "Alone")
|
159
|
+
u = User.using(:london).find_by_name("Alone")
|
160
|
+
u.reload
|
161
|
+
u.name.should == "Alone"
|
162
|
+
end
|
163
|
+
|
164
|
+
it "should work passing some arguments to reload method" do
|
165
|
+
User.using(:london).create!(:name => "Alone")
|
166
|
+
u = User.using(:london).find_by_name("Alone")
|
167
|
+
u.reload(:lock => true)
|
168
|
+
u.name.should == "Alone"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
describe "passing a block" do
|
173
|
+
it "should allow queries be executed inside the block, ponting to a specific shard" do
|
174
|
+
XBar.using(:canada) do
|
175
|
+
User.create(:name => "oi")
|
176
|
+
end
|
177
|
+
|
178
|
+
User.using(:canada).count.should == 1
|
179
|
+
User.using(:master).count.should == 0
|
180
|
+
User.count.should == 0
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should allow execute queries inside a model" do
|
184
|
+
u = User.new
|
185
|
+
u.awesome_queries()
|
186
|
+
User.using(:canada).count.should == 1
|
187
|
+
User.count.should == 0
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
describe "raising errors" do
|
192
|
+
it "should raise a error when you specify a shard that doesn't exist" do
|
193
|
+
lambda { User.using(:crazy_shard).create!(:name => 'Thiago') }.should raise_error("Nonexistent Shard Name: crazy_shard")
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
describe "using a postgresql shard" do
|
199
|
+
it "should update the Arel Engine" do
|
200
|
+
if ActiveRecord::VERSION::STRING > '2.4.0'
|
201
|
+
User.using(:moscow).arel_engine.connection.adapter_name.should == "PostgreSQL"
|
202
|
+
User.using(:london).arel_engine.connection.adapter_name.should == "Mysql2"
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
it "should works with writes and reads" do
|
207
|
+
u = User.using(:moscow).create!(:name => "PostgreSQL User")
|
208
|
+
User.using(:moscow).find(:all).should == [u]
|
209
|
+
User.using(:london).find(:all).should == []
|
210
|
+
User.connection_handler.connection_pools["ActiveRecord::Base"] = User.connection.shards[:master]
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
describe "AR basic methods" do
|
215
|
+
it "xbar_establish_connection" do
|
216
|
+
CustomConnection.connection.current_database.should == "rogue"
|
217
|
+
end
|
218
|
+
|
219
|
+
it "increment" do
|
220
|
+
u = User.using(:brazil).create!(:name => "Teste", :number => 10)
|
221
|
+
u = User.using(:brazil).find_by_number(10)
|
222
|
+
u.increment(:number)
|
223
|
+
u.save
|
224
|
+
u = User.using(:brazil).find_by_number(11).should_not be_nil
|
225
|
+
end
|
226
|
+
|
227
|
+
it "increment!" do
|
228
|
+
u = User.using(:brazil).create!(:name => "Teste", :number => 10)
|
229
|
+
u = User.using(:brazil).find_by_number(10)
|
230
|
+
u.increment!(:number)
|
231
|
+
u = User.using(:brazil).find_by_number(11).should_not be_nil
|
232
|
+
end
|
233
|
+
|
234
|
+
it "toggle" do
|
235
|
+
u = User.using(:brazil).create!(:name => "Teste", :admin => false)
|
236
|
+
u = User.using(:brazil).find_by_name('Teste')
|
237
|
+
u.toggle(:admin)
|
238
|
+
u.save
|
239
|
+
u = User.using(:brazil).find_by_name('Teste').admin.should be_true
|
240
|
+
end
|
241
|
+
|
242
|
+
it "toggle!" do
|
243
|
+
u = User.using(:brazil).create!(:name => "Teste", :admin => false)
|
244
|
+
u = User.using(:brazil).find_by_name('Teste')
|
245
|
+
u.toggle!(:admin)
|
246
|
+
u = User.using(:brazil).find_by_name('Teste').admin.should be_true
|
247
|
+
end
|
248
|
+
|
249
|
+
it "count" do
|
250
|
+
u = User.using(:brazil).create!(:name => "User1")
|
251
|
+
u2 = User.using(:brazil).create!(:name => "User2")
|
252
|
+
u3 = User.using(:brazil).create!(:name => "User3")
|
253
|
+
User.using(:brazil).find(:all, :conditions => {:name => "User2"}).count.should == 1
|
254
|
+
end
|
255
|
+
|
256
|
+
it "update_attributes" do
|
257
|
+
@user = User.using(:brazil).create!(:name => "User1")
|
258
|
+
@user2 = User.using(:brazil).find(@user.id)
|
259
|
+
@user2.update_attributes(:name => "Joaquim")
|
260
|
+
User.using(:brazil).find_by_name("Joaquim").should_not be_nil
|
261
|
+
end
|
262
|
+
|
263
|
+
it "using update_attributes inside a block" do
|
264
|
+
XBar.using(:brazil) do
|
265
|
+
@user = User.create!(:name => "User1")
|
266
|
+
@user2 = User.find(@user.id)
|
267
|
+
@user2.update_attributes(:name => "Joaquim")
|
268
|
+
end
|
269
|
+
|
270
|
+
User.find_by_name("Joaquim").should be_nil
|
271
|
+
User.using(:brazil).find_by_name("Joaquim").should_not be_nil
|
272
|
+
end
|
273
|
+
|
274
|
+
it "update_attribute" do
|
275
|
+
@user = User.using(:brazil).create!(:name => "User1")
|
276
|
+
@user2 = User.using(:brazil).find(@user.id)
|
277
|
+
@user2.update_attribute(:name, "Joaquim")
|
278
|
+
User.using(:brazil).find_by_name("Joaquim").should_not be_nil
|
279
|
+
end
|
280
|
+
|
281
|
+
it "transaction" do
|
282
|
+
u = User.create!(:name => "Thiago")
|
283
|
+
|
284
|
+
User.using(:brazil).count.should == 0
|
285
|
+
User.using(:master).count.should == 1
|
286
|
+
|
287
|
+
User.using(:brazil).transaction do
|
288
|
+
User.find_by_name("Thiago").should be_nil
|
289
|
+
User.create!(:name => "Brazil")
|
290
|
+
end
|
291
|
+
|
292
|
+
User.using(:brazil).count.should == 1
|
293
|
+
User.using(:master).count.should == 1
|
294
|
+
end
|
295
|
+
|
296
|
+
describe "deleting a record" do
|
297
|
+
before(:each) do
|
298
|
+
@user = User.using(:brazil).create!(:name => "User1")
|
299
|
+
@user2 = User.using(:brazil).find(@user.id)
|
300
|
+
end
|
301
|
+
|
302
|
+
it "delete" do
|
303
|
+
@user2.delete
|
304
|
+
lambda { User.using(:brazil).find(@user2.id) }.should raise_error(ActiveRecord::RecordNotFound)
|
305
|
+
end
|
306
|
+
|
307
|
+
it "delete within block shouldn't lose shard" do
|
308
|
+
XBar.using(:brazil) do
|
309
|
+
@user2.delete
|
310
|
+
@user3 = User.create(:name => "User3")
|
311
|
+
|
312
|
+
User.connection.current_shard.should == :brazil
|
313
|
+
User.find(@user3.id).should == @user3
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
it "destroy" do
|
318
|
+
@user2.destroy
|
319
|
+
lambda { User.using(:brazil).find(@user2.id) }.should raise_error(ActiveRecord::RecordNotFound)
|
320
|
+
end
|
321
|
+
|
322
|
+
it "destroy within block shouldn't lose shard" do
|
323
|
+
XBar.using(:brazil) do
|
324
|
+
@user2.destroy
|
325
|
+
@user3 = User.create(:name => "User3")
|
326
|
+
|
327
|
+
User.connection.current_shard.should == :brazil
|
328
|
+
User.find(@user3.id).should == @user3
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
describe "when using set_table_name" do
|
335
|
+
it 'should work correctly' do
|
336
|
+
Bacon.using(:brazil).create!(:name => "YUMMMYYYY")
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
describe "when using a environment with a single adapter" do
|
341
|
+
before (:each) do
|
342
|
+
set_xbar_env('single_adapter', 'test')
|
343
|
+
@proxy = XBar::Proxy.new
|
344
|
+
end
|
345
|
+
|
346
|
+
it 'should_clean_table_name? should return false' do
|
347
|
+
@proxy.should_clean_table_name?.should == false
|
348
|
+
end
|
349
|
+
|
350
|
+
it 'model object should not receive call to clean table name' do
|
351
|
+
begin
|
352
|
+
|
353
|
+
Keyboard.using(:master).create!(:name => "Master Cat")
|
354
|
+
# Keyboard.create!(:name => "Master Cat")
|
355
|
+
Keyboard.should_not_receive(:reset_table_name)
|
356
|
+
|
357
|
+
rescue
|
358
|
+
fail 'Keyboard.using(:master).create!(:name => "Master Cat")'
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
describe "when you have joins/include" do
|
364
|
+
|
365
|
+
before(:each) do
|
366
|
+
|
367
|
+
@client1 = Client.using(:brazil).create(:name => "Thiago")
|
368
|
+
|
369
|
+
XBar.using(:canada) do
|
370
|
+
@client2 = Client.create(:name => "Mike")
|
371
|
+
@client3 = Client.create(:name => "Joao")
|
372
|
+
@item1 = Item.create(:client => @client2, :name => "Item 1")
|
373
|
+
@item2 = Item.create(:client => @client2, :name => "Item 2")
|
374
|
+
@item3 = Item.create(:client => @client3, :name => "Item 3")
|
375
|
+
@part1 = Part.create(:item => @item1, :name => "Part 1")
|
376
|
+
@part2 = Part.create(:item => @item1, :name => "Part 2")
|
377
|
+
@part3 = Part.create(:item => @item2, :name => "Part 3")
|
378
|
+
end
|
379
|
+
|
380
|
+
@item4 = Item.using(:brazil).create(:client => @client1, :name => "Item 4")
|
381
|
+
end
|
382
|
+
|
383
|
+
it "should work with the rails 2.x syntax" do
|
384
|
+
items = Item.using(:canada).find(:all, :joins => :client, :conditions => { :clients => { :id => @client2.id } })
|
385
|
+
items.should == [@item1, @item2]
|
386
|
+
end
|
387
|
+
|
388
|
+
it "should work using the rails 3.x syntax" do
|
389
|
+
if XBar.rails3?
|
390
|
+
items = Item.using(:canada).joins(:client).where("clients.id = #{@client2.id}").all
|
391
|
+
items.should == [@item1, @item2]
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
it "should work for include also, rails 2.x syntax" do
|
396
|
+
items = Item.using(:canada).find(:all, :include => :client, :conditions => { :clients => { :id => @client2.id } })
|
397
|
+
items.should == [@item1, @item2]
|
398
|
+
end
|
399
|
+
|
400
|
+
it "should work for include also, rails 3.x syntax" do
|
401
|
+
if XBar.rails3?
|
402
|
+
items = Item.using(:canada).includes(:client).where("clients.id = #{@client2.id}").all
|
403
|
+
items.should == [@item1, @item2]
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
407
|
+
it "should work for multiple includes, with rails 2.x syntax" do
|
408
|
+
parts = Part.using(:canada).find(:all, :include => {:item => :client}, :conditions => {:clients => { :id => @client2.id}})
|
409
|
+
parts.should == [@part1, @part2, @part3]
|
410
|
+
parts.first.item.client.should == @client2
|
411
|
+
end
|
412
|
+
|
413
|
+
it "should work for multiple join, with rails 2.x syntax" do
|
414
|
+
parts = Part.using(:canada).find(:all, :joins => {:item => :client}, :conditions => {:clients => { :id => @client2.id}})
|
415
|
+
parts.should == [@part1, @part2, @part3]
|
416
|
+
parts.first.item.client.should == @client2
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
describe "ActiveRecord::Base Validations" do
|
421
|
+
it "should work correctly when using validations" do
|
422
|
+
@key = Keyboard.create!(:name => "Key")
|
423
|
+
lambda { Keyboard.using(:brazil).create!(:name => "Key") }.should_not raise_error()
|
424
|
+
lambda { Keyboard.create!(:name => "Key") }.should raise_error()
|
425
|
+
end
|
426
|
+
|
427
|
+
it "should work correctly when using validations with using syntax" do
|
428
|
+
@key = Keyboard.using(:brazil).create!(:name => "Key")
|
429
|
+
lambda { Keyboard.create!(:name => "Key") }.should_not raise_error()
|
430
|
+
lambda { Keyboard.using(:brazil).create!(:name => "Key") }.should raise_error()
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe XBar::Proxy do
|
4
|
+
let(:proxy) { XBar::Proxy.new }
|
5
|
+
|
6
|
+
describe "creating a new instance" do
|
7
|
+
before(:all) do
|
8
|
+
set_xbar_env('default', 'test')
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should have all the configured shards" do
|
12
|
+
proxy.shards.keys.to_set.should == [
|
13
|
+
"master", "london", "paris", "moscow", "russia", "russia_east",
|
14
|
+
"russia_central", "russia_west", "canada", "canada_east",
|
15
|
+
"canada_central", "canada_west", "brazil", "china", "china_east",
|
16
|
+
"china_west"].to_set
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should have all the configured shards in the shards list" do
|
20
|
+
proxy.shard_list.keys.to_set.should == [
|
21
|
+
"master", "london", "paris", "moscow", "russia", "russia_east",
|
22
|
+
"russia_central", "russia_west", "canada", "canada_east",
|
23
|
+
"canada_central", "canada_west", "brazil", "china", "china_east",
|
24
|
+
"china_west"].to_set
|
25
|
+
end
|
26
|
+
|
27
|
+
it "every shard in the shard list should have the correct name" do
|
28
|
+
proxy.shard_list.each do |name, shard|
|
29
|
+
shard.instance_variable_get(:@shard_name).should == name
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should initialize the block attribute as false" do
|
34
|
+
proxy.in_block_scope?.should be_false
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should not verify connections for default" do
|
38
|
+
proxy.verify_connection.should be_false
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should respond correctly to respond_to?(:pk_and_sequence_for)' do
|
42
|
+
pending
|
43
|
+
proxy.respond_to?(:pk_and_sequence_for).should be_true
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should respond correctly to respond_to?(:primary_key)' do
|
47
|
+
pending
|
48
|
+
proxy.respond_to?(:primary_key).should be_true
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "#should_clean_table_name?" do
|
52
|
+
it 'should return true when you have a environment with multiple database types' do
|
53
|
+
proxy.should_clean_table_name?.should be_true
|
54
|
+
end
|
55
|
+
|
56
|
+
context "when using a environment with a single adapter" do
|
57
|
+
before(:each) do
|
58
|
+
set_xbar_env("single_adapter", "test")
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should return false' do
|
62
|
+
proxy.should_clean_table_name?.should be_false
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "using a Rails environment where XBar is not enabled" do
|
69
|
+
before(:each) do
|
70
|
+
Rails = mock
|
71
|
+
# XBar is not enabled for the 'staging' environment
|
72
|
+
Rails.stub(:env).and_return('staging')
|
73
|
+
set_xbar_env('default')
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should use the master connection" do
|
77
|
+
user = User.create!(:name =>"Thiago")
|
78
|
+
user.name = "New Thiago"
|
79
|
+
user.save
|
80
|
+
User.find_by_name("New Thiago").should_not be_nil
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should work with the 'using' syntax" do
|
84
|
+
user = User.using(:russia).create!(:name =>"Thiago")
|
85
|
+
user.name = "New Thiago"
|
86
|
+
user.save
|
87
|
+
User.using(:russia).find_by_name("New Thiago").should == user
|
88
|
+
User.find_by_name("New Thiago").should == user
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should work when using blocks" do
|
92
|
+
XBar.using(:russia) do
|
93
|
+
@user = User.create!(:name =>"Thiago")
|
94
|
+
end
|
95
|
+
User.find_by_name("Thiago").should == @user
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should work with associations" do
|
99
|
+
u = Client.create!(:name => "Thiago")
|
100
|
+
i = Item.create(:name => "Item")
|
101
|
+
u.items << i
|
102
|
+
u.save
|
103
|
+
end
|
104
|
+
|
105
|
+
after(:each) do
|
106
|
+
Object.send(:remove_const, :Rails)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe "connections should be managed properly" do
|
111
|
+
|
112
|
+
before(:each) do
|
113
|
+
set_xbar_env('default', 'test')
|
114
|
+
end
|
115
|
+
|
116
|
+
describe "should return the shard name" do
|
117
|
+
it "when current_shard not explicitly set" do
|
118
|
+
proxy.current_shard.should == :master
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe "when the database is replicated" do
|
4
|
+
it "should send all writes/reads queries to master when you have a non replicated model" do
|
5
|
+
using_environment :production_replicated do
|
6
|
+
u = User.create!(:name => "Replicated")
|
7
|
+
User.count.should == 1
|
8
|
+
User.find(u).should == u
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should send all writes queries to master" do
|
13
|
+
using_environment :production_replicated do
|
14
|
+
Cat.create!(:name => "Slave Cat")
|
15
|
+
# The next line will break if the database really is replicated.
|
16
|
+
# This test is assuming that no external mechanism is actually
|
17
|
+
# propagating the writes.
|
18
|
+
Cat.find_by_name("Slave Cat").should be_nil # will read from slave
|
19
|
+
Client.create!(:name => "Slave Client")
|
20
|
+
Client.find_by_name("Slave Client").should_not be_nil # will read from master
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should allow to create multiple models on the master" do
|
25
|
+
using_environment :production_replicated do
|
26
|
+
Cat.create!([{:name => "Slave Cat 1"}, {:name => "Slave Cat 2"}])
|
27
|
+
Cat.find_by_name("Slave Cat 1").should be_nil
|
28
|
+
Cat.find_by_name("Slave Cat 2").should be_nil
|
29
|
+
Cat.using(:master).find_by_name("Slave Cat 1").should_not be_nil # MBS
|
30
|
+
Cat.using(:master).find_by_name("Slave Cat 2").should_not be_nil # MBS
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should allow #using syntax to send queries to master" do
|
35
|
+
Cat.create!(:name => "Master Cat")
|
36
|
+
|
37
|
+
using_environment :production_fully_replicated do
|
38
|
+
Cat.using(:master).find_by_name("Master Cat").should_not be_nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should send the count query to a slave" do
|
43
|
+
pending()
|
44
|
+
# using_environment :production_replicated do
|
45
|
+
# Cat.create!(:name => "Slave Cat")
|
46
|
+
# Cat.count.should == 0
|
47
|
+
# end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
describe "when the database is replicated and the entire application is replicated" do
|
53
|
+
before(:each) do
|
54
|
+
XBar.stub!(:env).and_return("production_fully_replicated")
|
55
|
+
clean_connection_proxy()
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should send all writes queries to master" do
|
59
|
+
using_environment :production_fully_replicated do
|
60
|
+
Cat.create!(:name => "Slave Cat")
|
61
|
+
Cat.find_by_name("Slave Cat").should be_nil
|
62
|
+
Client.create!(:name => "Slave Client")
|
63
|
+
Client.find_by_name("Slave Client").should be_nil
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should work with validate_uniquess_of" do
|
68
|
+
Keyboard.create!(:name => "thiago")
|
69
|
+
|
70
|
+
using_environment :production_fully_replicated do
|
71
|
+
k = Keyboard.new(:name => "thiago")
|
72
|
+
k.save.should be_false
|
73
|
+
if XBar.rails31?
|
74
|
+
k.errors.messages[:name].first.should == "has already been taken"
|
75
|
+
else
|
76
|
+
k.errors.should == {:name=>["has already been taken"]}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should reset current shard if slave throws an exception" do
|
82
|
+
|
83
|
+
using_environment :production_fully_replicated do
|
84
|
+
Cat.create!(:name => "Slave Cat")
|
85
|
+
Cat.connection.current_shard.should eql(:master)
|
86
|
+
begin
|
87
|
+
Cat.find(:all, :conditions => 'rubbish = true')
|
88
|
+
rescue
|
89
|
+
end
|
90
|
+
Cat.connection.current_shard.should eql(:master)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe XBar::ScopeProxy do
|
4
|
+
before(:each) do
|
5
|
+
set_xbar_env('default', 'test')
|
6
|
+
end
|
7
|
+
it "should allow nested queries" do
|
8
|
+
if XBar.rails3?
|
9
|
+
@user1 = User.using(:brazil).create!(:name => "Thiago P", :number => 3)
|
10
|
+
@user2 = User.using(:brazil).create!(:name => "Thiago", :number => 1)
|
11
|
+
@user3 = User.using(:brazil).create!(:name => "Thiago", :number => 2)
|
12
|
+
|
13
|
+
User.using(:brazil).where(:name => "Thiago").where(:number => 4).order(:number).all.should == []
|
14
|
+
User.using(:brazil).where(:name => "Thiago").using(:canada).where(:number => 2).using(:brazil).order(:number).all.should == [@user3]
|
15
|
+
User.using(:brazil).where(:name => "Thiago").using(:canada).where(:number => 4).using(:brazil).order(:number).all.should == []
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should raise a exception when trying to send a query to a shard that don't exists" do
|
20
|
+
lambda { User.using(:dont_exists).all }.should raise_exception("Nonexistent Shard Name: dont_exists")
|
21
|
+
end
|
22
|
+
end
|