ar-octopus 0.8.5 → 0.10.2
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.
- 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
|