ar-octopus 0.8.5 → 0.10.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/.travis.yml +6 -9
- data/Appraisals +8 -8
- data/README.mkdn +47 -15
- data/Rakefile +1 -0
- data/ar-octopus.gemspec +9 -12
- data/gemfiles/rails42.gemfile +2 -2
- data/gemfiles/{rails32.gemfile → rails5.gemfile} +2 -2
- data/gemfiles/{rails4.gemfile → rails51.gemfile} +2 -2
- data/gemfiles/{rails41.gemfile → rails52.gemfile} +2 -2
- data/lib/octopus/abstract_adapter.rb +4 -10
- data/lib/octopus/association.rb +1 -0
- data/lib/octopus/association_shard_tracking.rb +41 -71
- data/lib/octopus/collection_association.rb +9 -3
- data/lib/octopus/exception.rb +4 -0
- data/lib/octopus/finder_methods.rb +8 -0
- data/lib/octopus/load_balancing/round_robin.rb +2 -1
- data/lib/octopus/log_subscriber.rb +6 -2
- data/lib/octopus/migration.rb +123 -55
- data/lib/octopus/model.rb +42 -27
- data/lib/octopus/persistence.rb +33 -27
- data/lib/octopus/proxy.rb +147 -272
- data/lib/octopus/proxy_config.rb +251 -0
- data/lib/octopus/query_cache_for_shards.rb +24 -0
- data/lib/octopus/railtie.rb +0 -2
- data/lib/octopus/relation_proxy.rb +36 -1
- data/lib/octopus/result_patch.rb +19 -0
- data/lib/octopus/scope_proxy.rb +12 -5
- data/lib/octopus/shard_tracking.rb +8 -3
- data/lib/octopus/slave_group.rb +3 -3
- data/lib/octopus/version.rb +1 -1
- data/lib/octopus.rb +71 -18
- data/spec/config/shards.yml +12 -0
- data/spec/migrations/10_create_users_using_replication.rb +1 -1
- data/spec/migrations/11_add_field_in_all_slaves.rb +1 -1
- data/spec/migrations/12_create_users_using_block.rb +1 -1
- data/spec/migrations/13_create_users_using_block_and_using.rb +1 -1
- data/spec/migrations/14_create_users_on_shards_of_a_group_with_versions.rb +1 -1
- data/spec/migrations/15_create_user_on_shards_of_default_group_with_versions.rb +1 -1
- data/spec/migrations/1_create_users_on_master.rb +1 -1
- data/spec/migrations/2_create_users_on_canada.rb +1 -1
- data/spec/migrations/3_create_users_on_both_shards.rb +1 -1
- data/spec/migrations/4_create_users_on_shards_of_a_group.rb +1 -1
- data/spec/migrations/5_create_users_on_multiples_groups.rb +1 -1
- data/spec/migrations/6_raise_exception_with_invalid_shard_name.rb +1 -1
- data/spec/migrations/7_raise_exception_with_invalid_multiple_shard_names.rb +1 -1
- data/spec/migrations/8_raise_exception_with_invalid_group_name.rb +1 -1
- data/spec/migrations/9_raise_exception_with_multiple_invalid_group_names.rb +1 -1
- data/spec/octopus/association_shard_tracking_spec.rb +344 -16
- data/spec/octopus/load_balancing/round_robin_spec.rb +15 -0
- data/spec/octopus/log_subscriber_spec.rb +1 -1
- data/spec/octopus/migration_spec.rb +45 -11
- data/spec/octopus/model_spec.rb +204 -16
- data/spec/octopus/octopus_spec.rb +2 -2
- data/spec/octopus/proxy_spec.rb +44 -40
- data/spec/octopus/query_cache_for_shards_spec.rb +40 -0
- data/spec/octopus/relation_proxy_spec.rb +71 -23
- data/spec/octopus/replicated_slave_grouped_spec.rb +27 -0
- data/spec/octopus/replication_spec.rb +72 -2
- data/spec/octopus/scope_proxy_spec.rb +41 -7
- data/spec/spec_helper.rb +2 -0
- data/spec/support/database_connection.rb +1 -1
- data/spec/support/database_models.rb +1 -1
- data/spec/support/octopus_helper.rb +14 -6
- data/spec/tasks/octopus.rake_spec.rb +1 -1
- metadata +40 -30
- data/.ruby-version +0 -1
- data/init.rb +0 -1
- data/lib/octopus/has_and_belongs_to_many_association.rb +0 -9
- data/rails/init.rb +0 -1
data/spec/octopus/model_spec.rb
CHANGED
@@ -2,6 +2,10 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Octopus::Model do
|
4
4
|
describe '#using method' do
|
5
|
+
it 'raise when Model#using receives a block' do
|
6
|
+
expect { User.using(:master) { true } }.to raise_error(Octopus::Exception, /User\.using is not allowed to receive a block/)
|
7
|
+
end
|
8
|
+
|
5
9
|
it 'should allow to send a block to the master shard' do
|
6
10
|
Octopus.using(:master) do
|
7
11
|
User.create!(:name => 'Block test')
|
@@ -16,6 +20,16 @@ describe Octopus::Model do
|
|
16
20
|
expect(User.using('canada').find_by_name('Rafael Pilha')).not_to be_nil
|
17
21
|
end
|
18
22
|
|
23
|
+
it 'should allow comparison of a string shard name with symbol shard name' do
|
24
|
+
u = User.using('canada').create!(:name => 'Rafael Pilha')
|
25
|
+
expect(u).to eq(User.using(:canada).find_by_name('Rafael Pilha'))
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should allow comparison of a symbol shard name with string shard name' do
|
29
|
+
u = User.using(:canada).create!(:name => 'Rafael Pilha')
|
30
|
+
expect(u).to eq(User.using('canada').find_by_name('Rafael Pilha'))
|
31
|
+
end
|
32
|
+
|
19
33
|
it 'should allow to pass a string as the shard name to a block' do
|
20
34
|
Octopus.using('canada') do
|
21
35
|
User.create!(:name => 'Rafael Pilha')
|
@@ -49,6 +63,28 @@ describe Octopus::Model do
|
|
49
63
|
expect(User.all).to eq([u1])
|
50
64
|
end
|
51
65
|
|
66
|
+
it "should allow the #select method to fetch the correct data when using a block" do
|
67
|
+
canadian_user = User.using(:canada).create!(:name => 'Rafael Pilha')
|
68
|
+
|
69
|
+
Octopus.using('canada') do
|
70
|
+
@all_canadian_user_ids = User.select('id').to_a
|
71
|
+
end
|
72
|
+
|
73
|
+
expect(@all_canadian_user_ids).to eq([canadian_user])
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should allow objects to be fetch using different blocks - GH #306" do
|
77
|
+
canadian_user = User.using(:canada).create!(:name => 'Rafael Pilha')
|
78
|
+
|
79
|
+
Octopus.using(:canada) { @users = User.where('id is not null') }
|
80
|
+
Octopus.using(:canada) { @user = @users.first }
|
81
|
+
|
82
|
+
Octopus.using(:canada) { @user2 = User.where('id is not null').first }
|
83
|
+
|
84
|
+
expect(@user).to eq(canadian_user)
|
85
|
+
expect(@user2).to eq(canadian_user)
|
86
|
+
end
|
87
|
+
|
52
88
|
describe 'multiple calls to the same scope' do
|
53
89
|
it 'works with nil response' do
|
54
90
|
scope = User.using(:canada)
|
@@ -76,11 +112,26 @@ describe Octopus::Model do
|
|
76
112
|
Octopus.using(:canada) do
|
77
113
|
fail 'Some Exception'
|
78
114
|
end
|
79
|
-
end.to raise_error
|
115
|
+
end.to raise_error(RuntimeError)
|
80
116
|
|
81
117
|
expect(ActiveRecord::Base.connection.current_shard).to eq(:master)
|
82
118
|
end
|
83
119
|
|
120
|
+
it 'should ensure that the connection will be cleaned with custom master' do
|
121
|
+
OctopusHelper.using_environment :octopus do
|
122
|
+
Octopus.config[:master_shard] = :brazil
|
123
|
+
expect(ActiveRecord::Base.connection.current_shard).to eq(:brazil)
|
124
|
+
expect do
|
125
|
+
Octopus.using(:canada) do
|
126
|
+
fail 'Some Exception'
|
127
|
+
end
|
128
|
+
end.to raise_error(RuntimeError)
|
129
|
+
|
130
|
+
expect(ActiveRecord::Base.connection.current_shard).to eq(:brazil)
|
131
|
+
Octopus.config[:master_shard] = nil
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
84
135
|
it 'should allow creating more than one user' do
|
85
136
|
User.using(:canada).create([{ :name => 'America User 1' }, { :name => 'America User 2' }])
|
86
137
|
User.create!(:name => 'Thiago')
|
@@ -99,6 +150,15 @@ describe Octopus::Model do
|
|
99
150
|
expect(User.connection.current_shard).to eq(:master)
|
100
151
|
end
|
101
152
|
|
153
|
+
it 'should clean #current_shard from proxy when using execute' do
|
154
|
+
OctopusHelper.using_environment :octopus do
|
155
|
+
Octopus.config[:master_shard] = :brazil
|
156
|
+
User.using(:canada).connection.execute('select * from users limit 1;')
|
157
|
+
expect(User.connection.current_shard).to eq(:brazil)
|
158
|
+
Octopus.config[:master_shard] = nil
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
102
162
|
it 'should allow scoping dynamically' do
|
103
163
|
User.using(:canada).using(:master).using(:canada).create!(:name => 'oi')
|
104
164
|
expect(User.using(:canada).using(:master).count).to eq(0)
|
@@ -246,15 +306,19 @@ describe Octopus::Model do
|
|
246
306
|
|
247
307
|
describe 'using a postgresql shard' do
|
248
308
|
it 'should update the Arel Engine' do
|
249
|
-
|
250
|
-
|
309
|
+
if Octopus.atleast_rails52?
|
310
|
+
expect(User.using(:postgresql_shard).connection.adapter_name).to eq('PostgreSQL')
|
311
|
+
expect(User.using(:alone_shard).connection.adapter_name).to eq('Mysql2')
|
312
|
+
else
|
313
|
+
expect(User.using(:postgresql_shard).arel_engine.connection.adapter_name).to eq('PostgreSQL')
|
314
|
+
expect(User.using(:alone_shard).arel_engine.connection.adapter_name).to eq('Mysql2')
|
315
|
+
end
|
251
316
|
end
|
252
317
|
|
253
318
|
it 'should works with writes and reads' do
|
254
319
|
u = User.using(:postgresql_shard).create!(:name => 'PostgreSQL User')
|
255
320
|
expect(User.using(:postgresql_shard).all).to eq([u])
|
256
321
|
expect(User.using(:alone_shard).all).to eq([])
|
257
|
-
User.connection_handler.connection_pools['ActiveRecord::Base'] = User.connection.instance_variable_get(:@shards)[:master]
|
258
322
|
end
|
259
323
|
end
|
260
324
|
|
@@ -263,6 +327,12 @@ describe Octopus::Model do
|
|
263
327
|
expect(CustomConnection.connection.current_database).to eq('octopus_shard_2')
|
264
328
|
end
|
265
329
|
|
330
|
+
it 'reuses parent model connection' do
|
331
|
+
klass = Class.new(CustomConnection)
|
332
|
+
|
333
|
+
expect(klass.connection).to be klass.connection
|
334
|
+
end
|
335
|
+
|
266
336
|
it 'should not mess with custom connection table names' do
|
267
337
|
expect(Advert.connection.current_database).to eq('octopus_shard_1')
|
268
338
|
Advert.create!(:name => 'Teste')
|
@@ -328,6 +398,18 @@ describe Octopus::Model do
|
|
328
398
|
expect(User.using(:master).maximum(:number)).to eq(12)
|
329
399
|
end
|
330
400
|
|
401
|
+
it 'sum' do
|
402
|
+
u = User.using(:brazil).create!(:name => 'Teste', :number => 11)
|
403
|
+
v = User.using(:master).create!(:name => 'Teste', :number => 12)
|
404
|
+
|
405
|
+
expect(User.using(:master).sum(:number)).to eq(12)
|
406
|
+
expect(User.using(:brazil).sum(:number)).to eq(11)
|
407
|
+
|
408
|
+
expect(User.where(id: v.id).sum(:number)).to eq(12)
|
409
|
+
expect(User.using(:brazil).where(id: u.id).sum(:number)).to eq(11)
|
410
|
+
expect(User.using(:master).where(id: v.id).sum(:number)).to eq(12)
|
411
|
+
end
|
412
|
+
|
331
413
|
describe 'any?' do
|
332
414
|
before { User.using(:brazil).create!(:name => 'User1') }
|
333
415
|
|
@@ -418,19 +500,113 @@ describe Octopus::Model do
|
|
418
500
|
expect(user.as_json(:except => [:created_at, :updated_at, :id])).to eq('admin' => nil, 'name' => 'User1', 'number' => nil)
|
419
501
|
end
|
420
502
|
|
421
|
-
|
422
|
-
|
503
|
+
describe 'transaction' do
|
504
|
+
context 'without assigning a database' do
|
505
|
+
it 'works as expected' do
|
506
|
+
_u = User.create!(:name => 'Thiago')
|
423
507
|
|
424
|
-
|
425
|
-
|
508
|
+
expect(User.using(:brazil).count).to eq(0)
|
509
|
+
expect(User.using(:master).count).to eq(1)
|
510
|
+
|
511
|
+
User.using(:brazil).transaction do
|
512
|
+
expect(User.find_by_name('Thiago')).to be_nil
|
513
|
+
User.create!(:name => 'Brazil')
|
514
|
+
end
|
426
515
|
|
427
|
-
|
428
|
-
|
429
|
-
|
516
|
+
expect(User.using(:brazil).count).to eq(1)
|
517
|
+
expect(User.using(:master).count).to eq(1)
|
518
|
+
end
|
430
519
|
end
|
431
520
|
|
432
|
-
|
433
|
-
|
521
|
+
context 'when assigning a database' do
|
522
|
+
it 'works as expected' do
|
523
|
+
klass = User.using(:brazil)
|
524
|
+
|
525
|
+
klass.transaction do
|
526
|
+
klass.create!(:name => 'Brazil')
|
527
|
+
end
|
528
|
+
|
529
|
+
expect(klass.find_by_name('Brazil')).to be_present
|
530
|
+
end
|
531
|
+
end
|
532
|
+
end
|
533
|
+
|
534
|
+
describe "#finder methods" do
|
535
|
+
before(:each) do
|
536
|
+
@user1 = User.using(:brazil).create!(:name => 'User1')
|
537
|
+
@user2 = User.using(:brazil).create!(:name => 'User2')
|
538
|
+
@user3 = User.using(:brazil).create!(:name => 'User3')
|
539
|
+
end
|
540
|
+
|
541
|
+
it "#find_each should work with a block" do
|
542
|
+
result_array = []
|
543
|
+
|
544
|
+
User.using(:brazil).where("name is not NULL").find_each do |user|
|
545
|
+
result_array << user
|
546
|
+
end
|
547
|
+
|
548
|
+
expect(result_array).to eq([@user1, @user2, @user3])
|
549
|
+
end
|
550
|
+
|
551
|
+
it "#find_each should work with a where.not(...)" do
|
552
|
+
result_array = []
|
553
|
+
|
554
|
+
User.using(:brazil).where.not(:name => 'User2').find_each do |user|
|
555
|
+
result_array << user
|
556
|
+
end
|
557
|
+
|
558
|
+
expect(result_array).to eq([@user1, @user3])
|
559
|
+
end
|
560
|
+
|
561
|
+
it "#find_each should work as an enumerator" do
|
562
|
+
result_array = []
|
563
|
+
|
564
|
+
User.using(:brazil).where("name is not NULL").find_each.each do |user|
|
565
|
+
result_array << user
|
566
|
+
end
|
567
|
+
|
568
|
+
expect(result_array).to eq([@user1, @user2, @user3])
|
569
|
+
end
|
570
|
+
|
571
|
+
it "#find_each should work as a lazy enumerator" do
|
572
|
+
result_array = []
|
573
|
+
|
574
|
+
User.using(:brazil).where("name is not NULL").find_each.lazy.each do |user|
|
575
|
+
result_array << user
|
576
|
+
end
|
577
|
+
|
578
|
+
expect(result_array).to eq([@user1, @user2, @user3])
|
579
|
+
end
|
580
|
+
|
581
|
+
it "#find_in_batches should work with a block" do
|
582
|
+
result_array = []
|
583
|
+
|
584
|
+
User.using(:brazil).where("name is not NULL").find_in_batches(batch_size: 1) do |user|
|
585
|
+
result_array << user
|
586
|
+
end
|
587
|
+
|
588
|
+
expect(result_array).to eq([[@user1], [@user2], [@user3]])
|
589
|
+
end
|
590
|
+
|
591
|
+
it "#find_in_batches should work as an enumerator" do
|
592
|
+
result_array = []
|
593
|
+
|
594
|
+
User.using(:brazil).where("name is not NULL").find_in_batches(batch_size: 1).each do |user|
|
595
|
+
result_array << user
|
596
|
+
end
|
597
|
+
|
598
|
+
expect(result_array).to eq([[@user1], [@user2], [@user3]])
|
599
|
+
end
|
600
|
+
|
601
|
+
it "#find_in_batches should work as a lazy enumerator" do
|
602
|
+
result_array = []
|
603
|
+
|
604
|
+
User.using(:brazil).where("name is not NULL").find_in_batches(batch_size: 1).lazy.each do |user|
|
605
|
+
result_array << user
|
606
|
+
end
|
607
|
+
|
608
|
+
expect(result_array).to eq([[@user1], [@user2], [@user3]])
|
609
|
+
end
|
434
610
|
end
|
435
611
|
|
436
612
|
describe 'deleting a record' do
|
@@ -622,20 +798,21 @@ describe Octopus::Model do
|
|
622
798
|
it 'should work correctly when using validations' do
|
623
799
|
@key = Keyboard.create!(:name => 'Key')
|
624
800
|
expect { Keyboard.using(:brazil).create!(:name => 'Key') }.not_to raise_error
|
625
|
-
expect { Keyboard.create!(:name => 'Key') }.to raise_error
|
801
|
+
expect { Keyboard.create!(:name => 'Key') }.to raise_error(ActiveRecord::RecordInvalid)
|
626
802
|
end
|
627
803
|
|
628
804
|
it 'should work correctly when using validations with using syntax' do
|
629
805
|
@key = Keyboard.using(:brazil).create!(:name => 'Key')
|
630
806
|
expect { Keyboard.create!(:name => 'Key') }.not_to raise_error
|
631
|
-
expect { Keyboard.using(:brazil).create!(:name => 'Key') }
|
807
|
+
expect { Keyboard.using(:brazil).create!(:name => 'Key') }
|
808
|
+
.to raise_error(ActiveRecord::RecordInvalid)
|
632
809
|
end
|
633
810
|
end
|
634
811
|
|
635
812
|
describe '#replicated_model method' do
|
636
813
|
it 'should be replicated' do
|
637
814
|
OctopusHelper.using_environment :production_replicated do
|
638
|
-
expect(ActiveRecord::Base.connection_proxy.
|
815
|
+
expect(ActiveRecord::Base.connection_proxy.replicated).to be true
|
639
816
|
end
|
640
817
|
end
|
641
818
|
|
@@ -645,5 +822,16 @@ describe Octopus::Model do
|
|
645
822
|
expect(Cat.replicated).to be true
|
646
823
|
end
|
647
824
|
end
|
825
|
+
|
826
|
+
it "should work on a fully replicated environment" do
|
827
|
+
OctopusHelper.using_environment :production_fully_replicated do
|
828
|
+
User.using(:slave1).create!(name: 'Thiago')
|
829
|
+
User.using(:slave2).create!(name: 'Thiago')
|
830
|
+
|
831
|
+
replicated_cat = User.find_by_name 'Thiago'
|
832
|
+
|
833
|
+
expect(replicated_cat.current_shard.to_s).to match(/master/)
|
834
|
+
end
|
835
|
+
end
|
648
836
|
end
|
649
837
|
end
|
@@ -37,10 +37,10 @@ describe Octopus, :shards => [] do
|
|
37
37
|
end
|
38
38
|
|
39
39
|
it 'should permit users to configure shards on initializer files, instead of on a yml file.' do
|
40
|
-
expect { User.using(:crazy_shard).create!(:name => 'Joaquim') }.to raise_error
|
40
|
+
expect { User.using(:crazy_shard).create!(:name => 'Joaquim') }.to raise_error(RuntimeError)
|
41
41
|
|
42
42
|
Octopus.setup do |config|
|
43
|
-
config.shards = { :crazy_shard => { :adapter => 'mysql2', :database => 'octopus_shard_5', :username => 'root', :password => '' } }
|
43
|
+
config.shards = { :crazy_shard => { :adapter => 'mysql2', :database => 'octopus_shard_5', :username => "#{ENV['MYSQL_USER'] || 'root'}", :password => '' } }
|
44
44
|
end
|
45
45
|
|
46
46
|
expect { User.using(:crazy_shard).create!(:name => 'Joaquim') }.not_to raise_error
|
data/spec/octopus/proxy_spec.rb
CHANGED
@@ -6,10 +6,10 @@ describe Octopus::Proxy do
|
|
6
6
|
describe 'creating a new instance', :shards => [] do
|
7
7
|
it 'should initialize all shards and groups' do
|
8
8
|
# FIXME: Don't test implementation details
|
9
|
-
expect(proxy.
|
9
|
+
expect(proxy.shards).to include('canada', 'brazil', 'master', 'sqlite_shard', 'russia', 'alone_shard',
|
10
10
|
'aug2009', 'postgresql_shard', 'aug2010', 'aug2011')
|
11
11
|
|
12
|
-
expect(proxy.
|
12
|
+
expect(proxy.shards).to include('protocol_shard')
|
13
13
|
|
14
14
|
expect(proxy.has_group?('country_shards')).to be true
|
15
15
|
expect(proxy.shards_for_group('country_shards')).to include(:canada, :brazil, :russia)
|
@@ -23,24 +23,20 @@ describe Octopus::Proxy do
|
|
23
23
|
end
|
24
24
|
|
25
25
|
it 'should initialize replicated attribute as false' do
|
26
|
-
expect(proxy.
|
26
|
+
expect(proxy.replicated).to be_falsey
|
27
27
|
end
|
28
28
|
|
29
|
-
it 'should work with
|
30
|
-
config = proxy.
|
29
|
+
it 'should work with thinking sphinx' do
|
30
|
+
config = proxy.config
|
31
31
|
expect(config[:adapter]).to eq('mysql2')
|
32
32
|
expect(config[:database]).to eq('octopus_shard_1')
|
33
|
-
expect(config[:username]).to eq('root')
|
33
|
+
expect(config[:username]).to eq("#{ENV['MYSQL_USER'] || 'root'}")
|
34
34
|
end
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
end
|
41
|
-
|
42
|
-
it 'should respond correctly to respond_to?(:pk_and_sequence_for)' do
|
43
|
-
expect(proxy.respond_to?(:pk_and_sequence_for)).to be true
|
36
|
+
unless Octopus.rails50? || Octopus.rails51?|| Octopus.rails52?
|
37
|
+
it 'should respond correctly to respond_to?(:pk_and_sequence_for)' do
|
38
|
+
expect(proxy.respond_to?(:pk_and_sequence_for)).to be true
|
39
|
+
end
|
44
40
|
end
|
45
41
|
|
46
42
|
it 'should respond correctly to respond_to?(:primary_key)' do
|
@@ -61,22 +57,6 @@ describe Octopus::Proxy do
|
|
61
57
|
end
|
62
58
|
end
|
63
59
|
|
64
|
-
describe '#should_clean_table_name?' do
|
65
|
-
it 'should return true when you have a environment with multiple database types' do
|
66
|
-
expect(proxy.should_clean_table_name?).to be true
|
67
|
-
end
|
68
|
-
|
69
|
-
context 'when using a environment with a single table name' do
|
70
|
-
before(:each) do
|
71
|
-
OctopusHelper.octopus_env = 'production_replicated'
|
72
|
-
end
|
73
|
-
|
74
|
-
it 'should return false' do
|
75
|
-
expect(proxy.should_clean_table_name?).to be false
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
60
|
describe 'should raise error if you have duplicated shard names' do
|
81
61
|
before(:each) do
|
82
62
|
OctopusHelper.octopus_env = 'production_raise_error'
|
@@ -93,11 +73,11 @@ describe Octopus::Proxy do
|
|
93
73
|
end
|
94
74
|
|
95
75
|
it 'should initialize just the master shard' do
|
96
|
-
expect(proxy.
|
76
|
+
expect(proxy.shards.keys).to eq(['master'])
|
97
77
|
end
|
98
78
|
|
99
79
|
it 'should not initialize replication' do
|
100
|
-
expect(proxy.
|
80
|
+
expect(proxy.replicated).to be_nil
|
101
81
|
end
|
102
82
|
end
|
103
83
|
end
|
@@ -108,11 +88,11 @@ describe Octopus::Proxy do
|
|
108
88
|
end
|
109
89
|
|
110
90
|
it 'should have the replicated attribute as true' do
|
111
|
-
expect(proxy.
|
91
|
+
expect(proxy.replicated).to be true
|
112
92
|
end
|
113
93
|
|
114
94
|
it 'should initialize the list of shards' do
|
115
|
-
expect(proxy.
|
95
|
+
expect(proxy.slaves_list).to eq(%w(slave1 slave2 slave3 slave4))
|
116
96
|
end
|
117
97
|
end
|
118
98
|
|
@@ -135,7 +115,7 @@ describe Octopus::Proxy do
|
|
135
115
|
Octopus.instance_variable_set(:@environments, nil)
|
136
116
|
Octopus.config
|
137
117
|
|
138
|
-
expect(proxy.
|
118
|
+
expect(proxy.replicated).to be true
|
139
119
|
expect(Octopus.environments).to eq(%w(staging production))
|
140
120
|
end
|
141
121
|
|
@@ -145,7 +125,7 @@ describe Octopus::Proxy do
|
|
145
125
|
Octopus.instance_variable_set(:@environments, nil)
|
146
126
|
Octopus.config
|
147
127
|
|
148
|
-
expect(proxy.
|
128
|
+
expect(proxy.shards.keys.to_set).to eq(Set.new(%w(slave1 slave2 master)))
|
149
129
|
end
|
150
130
|
|
151
131
|
it 'should initialize correctly the shard octopus_shard value for logging' do
|
@@ -154,7 +134,7 @@ describe Octopus::Proxy do
|
|
154
134
|
Octopus.instance_variable_set(:@environments, nil)
|
155
135
|
Octopus.config
|
156
136
|
|
157
|
-
expect(proxy.
|
137
|
+
expect(proxy.shards['slave1'].spec.config).to have_key :octopus_shard
|
158
138
|
end
|
159
139
|
|
160
140
|
it 'should initialize correctly the shards for the production environment' do
|
@@ -163,7 +143,7 @@ describe Octopus::Proxy do
|
|
163
143
|
Octopus.instance_variable_set(:@environments, nil)
|
164
144
|
Octopus.config
|
165
145
|
|
166
|
-
expect(proxy.
|
146
|
+
expect(proxy.shards.keys.to_set).to eq(Set.new(%w(slave3 slave4 master)))
|
167
147
|
end
|
168
148
|
|
169
149
|
describe 'using the master connection', :shards => [:russia, :master] do
|
@@ -211,6 +191,14 @@ describe Octopus::Proxy do
|
|
211
191
|
expect(proxy.shard_name).to eq(:master)
|
212
192
|
end
|
213
193
|
|
194
|
+
it 'when current_shard is empty with custom master' do
|
195
|
+
OctopusHelper.using_environment :octopus do
|
196
|
+
Octopus.config[:master_shard] = :brazil
|
197
|
+
expect(proxy.shard_name).to eq(:brazil)
|
198
|
+
Octopus.config[:master_shard] = nil
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
214
202
|
it 'when current_shard is a single shard' do
|
215
203
|
proxy.current_shard = :canada
|
216
204
|
expect(proxy.shard_name).to eq(:canada)
|
@@ -224,12 +212,12 @@ describe Octopus::Proxy do
|
|
224
212
|
|
225
213
|
describe 'should return the connection based on shard_name' do
|
226
214
|
it 'when current_shard is empty' do
|
227
|
-
expect(proxy.select_connection).to eq(proxy.
|
215
|
+
expect(proxy.select_connection).to eq(proxy.shards[:master].connection)
|
228
216
|
end
|
229
217
|
|
230
218
|
it 'when current_shard is a single shard' do
|
231
219
|
proxy.current_shard = :canada
|
232
|
-
expect(proxy.select_connection).to eq(proxy.
|
220
|
+
expect(proxy.select_connection).to eq(proxy.shards[:canada].connection)
|
233
221
|
end
|
234
222
|
end
|
235
223
|
end
|
@@ -264,6 +252,22 @@ describe Octopus::Proxy do
|
|
264
252
|
end
|
265
253
|
end
|
266
254
|
|
255
|
+
|
256
|
+
describe 'cleaning the connection proxy' do
|
257
|
+
it 'should not clean #current_shard from proxy when using a block and calling #execute' do
|
258
|
+
Octopus.using(:canada) do
|
259
|
+
expect(User.connection.current_shard).to eq(:canada)
|
260
|
+
|
261
|
+
connection = User.connection
|
262
|
+
|
263
|
+
result = connection.execute('select * from users limit 1;')
|
264
|
+
result = connection.execute('select * from users limit 1;')
|
265
|
+
|
266
|
+
expect(User.connection.current_shard).to eq(:canada)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
267
271
|
describe 'connection reuse' do
|
268
272
|
before :each do
|
269
273
|
@item_brazil_conn = Item.using(:brazil).new(:name => 'Brazil Item').class.connection.select_connection
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
unless Octopus.rails4? || Octopus.rails50?
|
4
|
+
describe Octopus::ConnectionPool::QueryCacheForShards do
|
5
|
+
subject(:query_cache_on_shard) { ActiveRecord::Base.using(:brazil).connection.query_cache_enabled }
|
6
|
+
|
7
|
+
context 'Octopus enabled' do
|
8
|
+
context 'when query cache is enabled on the primary connection_pool' do
|
9
|
+
before { ActiveRecord::Base.connection_pool.enable_query_cache! }
|
10
|
+
it { is_expected.to be true }
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'when query cache is disabled on the primary connection_pool' do
|
14
|
+
before { ActiveRecord::Base.connection_pool.disable_query_cache! }
|
15
|
+
it { is_expected.to be false }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'Octopus disabled' do
|
20
|
+
before do
|
21
|
+
Rails = double
|
22
|
+
allow(Rails).to receive(:env).and_return('staging')
|
23
|
+
end
|
24
|
+
|
25
|
+
after do
|
26
|
+
Object.send(:remove_const, :Rails)
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'when query cache is enabled on the primary connection_pool' do
|
30
|
+
before { ActiveRecord::Base.connection_pool.enable_query_cache! }
|
31
|
+
it { is_expected.to be true }
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'when query cache is disabled on the primary connection_pool' do
|
35
|
+
before { ActiveRecord::Base.connection_pool.disable_query_cache! }
|
36
|
+
it { is_expected.to be false }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -12,10 +12,39 @@ describe Octopus::RelationProxy do
|
|
12
12
|
expect(@relation.current_shard).to eq(:canada)
|
13
13
|
end
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
it 'can define collection association with the same name as ancestor private method' do
|
16
|
+
@client.comments << Comment.using(:canada).create!(open: true)
|
17
|
+
expect(@client.comments.open).to be_a_kind_of(ActiveRecord::Relation)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'can be dumped and loaded' do
|
21
|
+
expect(Marshal.load(Marshal.dump(@relation))).to eq @relation
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'maintains the current shard when using where.not(...)' do
|
25
|
+
where_chain = @relation.where
|
26
|
+
expect(where_chain.current_shard).to eq(@relation.current_shard)
|
27
|
+
not_relation = where_chain.not("1=0")
|
28
|
+
expect(not_relation.current_shard).to eq(@relation.current_shard)
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'when a new relation is constructed from the original relation' do
|
32
|
+
context 'and a where(...) is used' do
|
33
|
+
it 'does not tamper with the original relation' do
|
34
|
+
relation = Item.using(:canada).where(id: 1)
|
35
|
+
original_sql = relation.to_sql
|
36
|
+
new_relation = relation.where(id: 2)
|
37
|
+
expect(relation.to_sql).to eq(original_sql)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'and a where.not(...) is used' do
|
42
|
+
it 'does not tamper with the original relation' do
|
43
|
+
relation = Item.using(:canada).where(id: 1)
|
44
|
+
original_sql = relation.to_sql
|
45
|
+
new_relation = relation.where.not(id: 2)
|
46
|
+
expect(relation.to_sql).to eq(original_sql)
|
47
|
+
end
|
19
48
|
end
|
20
49
|
end
|
21
50
|
|
@@ -29,26 +58,36 @@ describe Octopus::RelationProxy do
|
|
29
58
|
end
|
30
59
|
end
|
31
60
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
expect{@relation.ar_relation}.not_to raise_error
|
36
|
-
end
|
61
|
+
it "can deliver methods in ActiveRecord::Batches correctly when given a block" do
|
62
|
+
expect { @relation.find_each(&:inspect) }.not_to raise_error
|
63
|
+
end
|
37
64
|
|
38
|
-
|
39
|
-
|
40
|
-
|
65
|
+
it "can deliver methods in ActiveRecord::Batches correctly as an enumerator" do
|
66
|
+
expect { @relation.find_each.each(&:inspect) }.not_to raise_error
|
67
|
+
end
|
41
68
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
expect(@relation.__id__).not_to eq(i.__id__)
|
46
|
-
end
|
69
|
+
it "can deliver methods in ActiveRecord::Batches correctly as a lazy enumerator" do
|
70
|
+
expect { @relation.find_each.lazy.each(&:inspect) }.not_to raise_error
|
71
|
+
end
|
47
72
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
73
|
+
context 'under Rails 4' do
|
74
|
+
it 'is an Octopus::RelationProxy' do
|
75
|
+
expect{@relation.ar_relation}.not_to raise_error
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should be able to return its ActiveRecord::Relation' do
|
79
|
+
expect(@relation.ar_relation.is_a?(ActiveRecord::Relation)).to be true
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'is equal to an identically-defined, but different, RelationProxy' do
|
83
|
+
i = @client.items
|
84
|
+
expect(@relation).to eq(i)
|
85
|
+
expect(@relation.__id__).not_to eq(i.__id__)
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'is equal to its own underlying ActiveRecord::Relation' do
|
89
|
+
expect(@relation).to eq(@relation.ar_relation)
|
90
|
+
expect(@relation.ar_relation).to eq(@relation)
|
52
91
|
end
|
53
92
|
end
|
54
93
|
|
@@ -68,14 +107,23 @@ describe Octopus::RelationProxy do
|
|
68
107
|
it 'uses the correct shard' do
|
69
108
|
expect(Item.using(:brazil).count).to eq(0)
|
70
109
|
_clients_on_brazil = Client.using(:brazil).all
|
71
|
-
|
110
|
+
Octopus.using(:brazil) do
|
72
111
|
expect(@relation.count).to eq(1)
|
73
112
|
end
|
74
113
|
end
|
75
114
|
|
115
|
+
it 'uses the correct shard in block when method_missing is triggered on CollectionProxy objects' do
|
116
|
+
Octopus.using(:brazil) do
|
117
|
+
@client.items.each do |item|
|
118
|
+
expect(item.current_shard).to eq(:canada)
|
119
|
+
expect(ActiveRecord::Base.connection.current_shard).to eq(:brazil)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
76
124
|
it 'lazily evaluates on the correct shard' do
|
77
125
|
expect(Item.using(:brazil).count).to eq(0)
|
78
|
-
|
126
|
+
Octopus.using(:brazil) do
|
79
127
|
expect(@relation.select(:client_id).count).to eq(1)
|
80
128
|
end
|
81
129
|
end
|