ar-octopus-ruby-3 0.11.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 +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.rubocop.yml +46 -0
- data/.rubocop_todo.yml +56 -0
- data/.travis.yml +18 -0
- data/Appraisals +16 -0
- data/Gemfile +4 -0
- data/README.mkdn +257 -0
- data/Rakefile +175 -0
- data/TODO.txt +7 -0
- data/ar-octopus.gemspec +44 -0
- data/gemfiles/rails42.gemfile +7 -0
- data/gemfiles/rails5.gemfile +7 -0
- data/gemfiles/rails51.gemfile +7 -0
- data/gemfiles/rails52.gemfile +7 -0
- data/lib/ar-octopus.rb +1 -0
- data/lib/octopus/abstract_adapter.rb +33 -0
- data/lib/octopus/association.rb +14 -0
- data/lib/octopus/association_shard_tracking.rb +74 -0
- data/lib/octopus/collection_association.rb +17 -0
- data/lib/octopus/collection_proxy.rb +16 -0
- data/lib/octopus/exception.rb +4 -0
- data/lib/octopus/finder_methods.rb +8 -0
- data/lib/octopus/load_balancing/round_robin.rb +20 -0
- data/lib/octopus/load_balancing.rb +4 -0
- data/lib/octopus/log_subscriber.rb +26 -0
- data/lib/octopus/migration.rb +236 -0
- data/lib/octopus/model.rb +216 -0
- data/lib/octopus/persistence.rb +45 -0
- data/lib/octopus/proxy.rb +399 -0
- data/lib/octopus/proxy_config.rb +251 -0
- data/lib/octopus/query_cache_for_shards.rb +24 -0
- data/lib/octopus/railtie.rb +11 -0
- data/lib/octopus/relation_proxy.rb +74 -0
- data/lib/octopus/result_patch.rb +19 -0
- data/lib/octopus/scope_proxy.rb +68 -0
- data/lib/octopus/shard_tracking/attribute.rb +22 -0
- data/lib/octopus/shard_tracking/dynamic.rb +11 -0
- data/lib/octopus/shard_tracking.rb +46 -0
- data/lib/octopus/singular_association.rb +9 -0
- data/lib/octopus/slave_group.rb +13 -0
- data/lib/octopus/version.rb +3 -0
- data/lib/octopus.rb +209 -0
- data/lib/tasks/octopus.rake +16 -0
- data/sample_app/.gitignore +4 -0
- data/sample_app/.rspec +1 -0
- data/sample_app/Gemfile +20 -0
- data/sample_app/Gemfile.lock +155 -0
- data/sample_app/README +3 -0
- data/sample_app/README.rdoc +261 -0
- data/sample_app/Rakefile +7 -0
- data/sample_app/app/assets/images/rails.png +0 -0
- data/sample_app/app/assets/javascripts/application.js +15 -0
- data/sample_app/app/assets/stylesheets/application.css +13 -0
- data/sample_app/app/controllers/application_controller.rb +4 -0
- data/sample_app/app/helpers/application_helper.rb +2 -0
- data/sample_app/app/mailers/.gitkeep +0 -0
- data/sample_app/app/models/.gitkeep +0 -0
- data/sample_app/app/models/item.rb +3 -0
- data/sample_app/app/models/user.rb +3 -0
- data/sample_app/app/views/layouts/application.html.erb +14 -0
- data/sample_app/autotest/discover.rb +2 -0
- data/sample_app/config/application.rb +62 -0
- data/sample_app/config/boot.rb +6 -0
- data/sample_app/config/cucumber.yml +8 -0
- data/sample_app/config/database.yml +28 -0
- data/sample_app/config/environment.rb +5 -0
- data/sample_app/config/environments/development.rb +37 -0
- data/sample_app/config/environments/production.rb +67 -0
- data/sample_app/config/environments/test.rb +37 -0
- data/sample_app/config/initializers/backtrace_silencers.rb +7 -0
- data/sample_app/config/initializers/inflections.rb +15 -0
- data/sample_app/config/initializers/mime_types.rb +5 -0
- data/sample_app/config/initializers/secret_token.rb +7 -0
- data/sample_app/config/initializers/session_store.rb +8 -0
- data/sample_app/config/initializers/wrap_parameters.rb +14 -0
- data/sample_app/config/locales/en.yml +5 -0
- data/sample_app/config/routes.rb +58 -0
- data/sample_app/config/shards.yml +28 -0
- data/sample_app/config.ru +4 -0
- data/sample_app/db/migrate/20100720172715_create_users.rb +15 -0
- data/sample_app/db/migrate/20100720172730_create_items.rb +16 -0
- data/sample_app/db/migrate/20100720210335_create_sample_users.rb +11 -0
- data/sample_app/db/schema.rb +29 -0
- data/sample_app/db/seeds.rb +16 -0
- data/sample_app/doc/README_FOR_APP +2 -0
- data/sample_app/features/migrate.feature +45 -0
- data/sample_app/features/seed.feature +15 -0
- data/sample_app/features/step_definitions/seeds_steps.rb +13 -0
- data/sample_app/features/step_definitions/web_steps.rb +218 -0
- data/sample_app/features/support/database.rb +13 -0
- data/sample_app/features/support/env.rb +57 -0
- data/sample_app/features/support/paths.rb +33 -0
- data/sample_app/lib/assets/.gitkeep +0 -0
- data/sample_app/lib/tasks/.gitkeep +0 -0
- data/sample_app/lib/tasks/cucumber.rake +64 -0
- data/sample_app/log/.gitkeep +0 -0
- data/sample_app/public/404.html +26 -0
- data/sample_app/public/422.html +26 -0
- data/sample_app/public/500.html +26 -0
- data/sample_app/public/favicon.ico +0 -0
- data/sample_app/public/images/rails.png +0 -0
- data/sample_app/public/index.html +279 -0
- data/sample_app/public/javascripts/application.js +2 -0
- data/sample_app/public/javascripts/controls.js +965 -0
- data/sample_app/public/javascripts/dragdrop.js +974 -0
- data/sample_app/public/javascripts/effects.js +1123 -0
- data/sample_app/public/javascripts/prototype.js +4874 -0
- data/sample_app/public/javascripts/rails.js +118 -0
- data/sample_app/public/robots.txt +5 -0
- data/sample_app/public/stylesheets/.gitkeep +0 -0
- data/sample_app/script/cucumber +10 -0
- data/sample_app/script/rails +6 -0
- data/sample_app/spec/models/item_spec.rb +5 -0
- data/sample_app/spec/models/user_spec.rb +5 -0
- data/sample_app/spec/spec_helper.rb +27 -0
- data/sample_app/vendor/assets/javascripts/.gitkeep +0 -0
- data/sample_app/vendor/assets/stylesheets/.gitkeep +0 -0
- data/sample_app/vendor/plugins/.gitkeep +0 -0
- data/spec/config/shards.yml +231 -0
- data/spec/migrations/10_create_users_using_replication.rb +9 -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/14_create_users_on_shards_of_a_group_with_versions.rb +11 -0
- data/spec/migrations/15_create_user_on_shards_of_default_group_with_versions.rb +9 -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/octopus/association_shard_tracking_spec.rb +1036 -0
- data/spec/octopus/collection_proxy_spec.rb +16 -0
- data/spec/octopus/load_balancing/round_robin_spec.rb +15 -0
- data/spec/octopus/log_subscriber_spec.rb +19 -0
- data/spec/octopus/migration_spec.rb +151 -0
- data/spec/octopus/model_spec.rb +837 -0
- data/spec/octopus/octopus_spec.rb +123 -0
- data/spec/octopus/proxy_spec.rb +303 -0
- data/spec/octopus/query_cache_for_shards_spec.rb +40 -0
- data/spec/octopus/relation_proxy_spec.rb +132 -0
- data/spec/octopus/replicated_slave_grouped_spec.rb +91 -0
- data/spec/octopus/replication_spec.rb +196 -0
- data/spec/octopus/scope_proxy_spec.rb +97 -0
- data/spec/octopus/sharded_replicated_slave_grouped_spec.rb +55 -0
- data/spec/octopus/sharded_spec.rb +33 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/support/active_record/connection_adapters/modify_config_adapter.rb +15 -0
- data/spec/support/database_connection.rb +4 -0
- data/spec/support/database_models.rb +118 -0
- data/spec/support/octopus_helper.rb +66 -0
- data/spec/support/query_count.rb +17 -0
- data/spec/support/shared_contexts.rb +18 -0
- data/spec/tasks/octopus.rake_spec.rb +32 -0
- metadata +351 -0
|
@@ -0,0 +1,837 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Octopus::Model do
|
|
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
|
+
|
|
9
|
+
it 'should allow to send a block to the master shard' do
|
|
10
|
+
Octopus.using(:master) do
|
|
11
|
+
User.create!(:name => 'Block test')
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
expect(User.using(:master).find_by_name('Block test')).not_to be_nil
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it 'should allow to pass a string as the shard name to a AR subclass' do
|
|
18
|
+
User.using('canada').create!(:name => 'Rafael Pilha')
|
|
19
|
+
|
|
20
|
+
expect(User.using('canada').find_by_name('Rafael Pilha')).not_to be_nil
|
|
21
|
+
end
|
|
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
|
+
|
|
33
|
+
it 'should allow to pass a string as the shard name to a block' do
|
|
34
|
+
Octopus.using('canada') do
|
|
35
|
+
User.create!(:name => 'Rafael Pilha')
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
expect(User.using('canada').find_by_name('Rafael Pilha')).not_to be_nil
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'should allow selecting the shards on scope' do
|
|
42
|
+
User.using(:canada).create!(:name => 'oi')
|
|
43
|
+
expect(User.using(:canada).count).to eq(1)
|
|
44
|
+
expect(User.count).to eq(0)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it 'should allow selecting the shard using #new' do
|
|
48
|
+
u = User.using(:canada).new
|
|
49
|
+
u.name = 'Thiago'
|
|
50
|
+
u.save
|
|
51
|
+
|
|
52
|
+
expect(User.using(:canada).count).to eq(1)
|
|
53
|
+
expect(User.using(:brazil).count).to eq(0)
|
|
54
|
+
|
|
55
|
+
u1 = User.new
|
|
56
|
+
u1.name = 'Joaquim'
|
|
57
|
+
u2 = User.using(:canada).new
|
|
58
|
+
u2.name = 'Manuel'
|
|
59
|
+
u1.save
|
|
60
|
+
u2.save
|
|
61
|
+
|
|
62
|
+
expect(User.using(:canada).all).to eq([u, u2])
|
|
63
|
+
expect(User.all).to eq([u1])
|
|
64
|
+
end
|
|
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
|
+
|
|
88
|
+
describe 'multiple calls to the same scope' do
|
|
89
|
+
it 'works with nil response' do
|
|
90
|
+
scope = User.using(:canada)
|
|
91
|
+
expect(scope.count).to eq(0)
|
|
92
|
+
expect(scope.first).to be_nil
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it 'works with non-nil response' do
|
|
96
|
+
user = User.using(:canada).create!(:name => 'oi')
|
|
97
|
+
scope = User.using(:canada)
|
|
98
|
+
expect(scope.count).to eq(1)
|
|
99
|
+
expect(scope.first).to eq(user)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it 'should select the correct shard' do
|
|
104
|
+
User.using(:canada)
|
|
105
|
+
User.create!(:name => 'oi')
|
|
106
|
+
expect(User.count).to eq(1)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it 'should ensure that the connection will be cleaned' do
|
|
110
|
+
expect(ActiveRecord::Base.connection.current_shard).to eq(:master)
|
|
111
|
+
expect do
|
|
112
|
+
Octopus.using(:canada) do
|
|
113
|
+
fail 'Some Exception'
|
|
114
|
+
end
|
|
115
|
+
end.to raise_error(RuntimeError)
|
|
116
|
+
|
|
117
|
+
expect(ActiveRecord::Base.connection.current_shard).to eq(:master)
|
|
118
|
+
end
|
|
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
|
+
|
|
135
|
+
it 'should allow creating more than one user' do
|
|
136
|
+
User.using(:canada).create([{ :name => 'America User 1' }, { :name => 'America User 2' }])
|
|
137
|
+
User.create!(:name => 'Thiago')
|
|
138
|
+
expect(User.using(:canada).find_by_name('America User 1')).not_to be_nil
|
|
139
|
+
expect(User.using(:canada).find_by_name('America User 2')).not_to be_nil
|
|
140
|
+
expect(User.using(:master).find_by_name('Thiago')).not_to be_nil
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it 'should work when you have a SQLite3 shard' do
|
|
144
|
+
u = User.using(:sqlite_shard).create!(:name => 'Sqlite3')
|
|
145
|
+
expect(User.using(:sqlite_shard).where(name: 'Sqlite3').first).to eq(u)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
it 'should clean #current_shard from proxy when using execute' do
|
|
149
|
+
User.using(:canada).connection.execute('select * from users limit 1;')
|
|
150
|
+
expect(User.connection.current_shard).to eq(:master)
|
|
151
|
+
end
|
|
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
|
+
|
|
162
|
+
it 'should allow scoping dynamically' do
|
|
163
|
+
User.using(:canada).using(:master).using(:canada).create!(:name => 'oi')
|
|
164
|
+
expect(User.using(:canada).using(:master).count).to eq(0)
|
|
165
|
+
expect(User.using(:master).using(:canada).count).to eq(1)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
it 'should allow find inside blocks' do
|
|
169
|
+
@user = User.using(:brazil).create!(:name => 'Thiago')
|
|
170
|
+
|
|
171
|
+
Octopus.using(:brazil) do
|
|
172
|
+
expect(User.first).to eq(@user)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
expect(User.using(:brazil).find_by_name('Thiago')).to eq(@user)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
it 'should clean the current_shard after executing the current query' do
|
|
179
|
+
User.using(:canada).create!(:name => 'oi')
|
|
180
|
+
expect(User.count).to eq(0)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
it 'should support both groups and alone shards' do
|
|
184
|
+
_u = User.using(:alone_shard).create!(:name => 'Alone')
|
|
185
|
+
expect(User.using(:alone_shard).count).to eq(1)
|
|
186
|
+
expect(User.using(:canada).count).to eq(0)
|
|
187
|
+
expect(User.using(:brazil).count).to eq(0)
|
|
188
|
+
expect(User.count).to eq(0)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
it 'should work with named scopes' do
|
|
192
|
+
u = User.using(:brazil).create!(:name => 'Thiago')
|
|
193
|
+
|
|
194
|
+
expect(User.thiago.using(:brazil).first).to eq(u)
|
|
195
|
+
expect(User.using(:brazil).thiago.first).to eq(u)
|
|
196
|
+
|
|
197
|
+
Octopus.using(:brazil) do
|
|
198
|
+
expect(User.thiago.first).to eq(u)
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
describe '#current_shard attribute' do
|
|
203
|
+
it 'should store the attribute when you create or find an object' do
|
|
204
|
+
u = User.using(:alone_shard).create!(:name => 'Alone')
|
|
205
|
+
expect(u.current_shard).to eq(:alone_shard)
|
|
206
|
+
User.using(:canada).create!(:name => 'oi')
|
|
207
|
+
u = User.using(:canada).find_by_name('oi')
|
|
208
|
+
expect(u.current_shard).to eq(:canada)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
it 'should store the attribute when you find multiple instances' do
|
|
212
|
+
5.times { User.using(:alone_shard).create!(:name => 'Alone') }
|
|
213
|
+
|
|
214
|
+
User.using(:alone_shard).all.each do |u|
|
|
215
|
+
expect(u.current_shard).to eq(:alone_shard)
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
it 'should works when you find, and after that, alter that object' do
|
|
220
|
+
alone_user = User.using(:alone_shard).create!(:name => 'Alone')
|
|
221
|
+
_mstr_user = User.using(:master).create!(:name => 'Master')
|
|
222
|
+
alone_user.name = 'teste'
|
|
223
|
+
alone_user.save
|
|
224
|
+
expect(User.using(:master).first.name).to eq('Master')
|
|
225
|
+
expect(User.using(:alone_shard).first.name).to eq('teste')
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
it 'should work for the reload method' do
|
|
229
|
+
User.using(:alone_shard).create!(:name => 'Alone')
|
|
230
|
+
u = User.using(:alone_shard).find_by_name('Alone')
|
|
231
|
+
u.reload
|
|
232
|
+
expect(u.name).to eq('Alone')
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
it 'should work passing some arguments to reload method' do
|
|
236
|
+
User.using(:alone_shard).create!(:name => 'Alone')
|
|
237
|
+
u = User.using(:alone_shard).find_by_name('Alone')
|
|
238
|
+
u.reload(:lock => true)
|
|
239
|
+
expect(u.name).to eq('Alone')
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
describe 'passing a block' do
|
|
244
|
+
it 'should allow queries be executed inside the block, ponting to a specific shard' do
|
|
245
|
+
Octopus.using(:canada) do
|
|
246
|
+
User.create(:name => 'oi')
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
expect(User.using(:canada).count).to eq(1)
|
|
250
|
+
expect(User.using(:master).count).to eq(0)
|
|
251
|
+
expect(User.count).to eq(0)
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
it 'should allow execute queries inside a model' do
|
|
255
|
+
u = User.new
|
|
256
|
+
u.awesome_queries
|
|
257
|
+
expect(User.using(:canada).count).to eq(1)
|
|
258
|
+
expect(User.count).to eq(0)
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
describe 'raising errors' do
|
|
263
|
+
it "should raise a error when you specify a shard that doesn't exist" do
|
|
264
|
+
expect { User.using(:crazy_shard).create!(:name => 'Thiago') }.to raise_error('Nonexistent Shard Name: crazy_shard')
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
describe 'equality' do
|
|
269
|
+
let(:canada1) do
|
|
270
|
+
u = User.new
|
|
271
|
+
u.id = 1
|
|
272
|
+
u.current_shard = :canada
|
|
273
|
+
u
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
let(:canada1_dup) do
|
|
277
|
+
u = User.new
|
|
278
|
+
u.id = 1
|
|
279
|
+
u.current_shard = :canada
|
|
280
|
+
u
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
let(:brazil1) do
|
|
284
|
+
u = User.new
|
|
285
|
+
u.id = 1
|
|
286
|
+
u.current_shard = :brazil
|
|
287
|
+
u
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
it 'should work with persisted objects' do
|
|
291
|
+
u = User.using(:brazil).create(:name => 'Mike')
|
|
292
|
+
expect(User.using(:brazil).find_by_name('Mike')).to eq(u)
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
it 'should check current_shard when determining equality' do
|
|
296
|
+
expect(canada1).not_to eq(brazil1)
|
|
297
|
+
expect(canada1).to eq(canada1_dup)
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
it 'delegates equality check on scopes' do
|
|
301
|
+
u = User.using(:brazil).create!(:name => 'Mike')
|
|
302
|
+
expect(User.using(:brazil).where(:name => 'Mike')).to eq([u])
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
describe 'using a postgresql shard' do
|
|
308
|
+
it 'should update the Arel Engine' do
|
|
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
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
it 'should works with writes and reads' do
|
|
319
|
+
u = User.using(:postgresql_shard).create!(:name => 'PostgreSQL User')
|
|
320
|
+
expect(User.using(:postgresql_shard).all).to eq([u])
|
|
321
|
+
expect(User.using(:alone_shard).all).to eq([])
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
describe 'AR basic methods' do
|
|
326
|
+
it 'establish_connection' do
|
|
327
|
+
expect(CustomConnection.connection.current_database).to eq('octopus_shard_2')
|
|
328
|
+
end
|
|
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
|
+
|
|
336
|
+
it 'should not mess with custom connection table names' do
|
|
337
|
+
expect(Advert.connection.current_database).to eq('octopus_shard_1')
|
|
338
|
+
Advert.create!(:name => 'Teste')
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
it 'increment' do
|
|
342
|
+
_ = User.using(:brazil).create!(:name => 'Teste', :number => 10)
|
|
343
|
+
u = User.using(:brazil).find_by_number(10)
|
|
344
|
+
u.increment(:number)
|
|
345
|
+
u.save
|
|
346
|
+
expect(User.using(:brazil).find_by_number(11)).not_to be_nil
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
it 'increment!' do
|
|
350
|
+
_ = User.using(:brazil).create!(:name => 'Teste', :number => 10)
|
|
351
|
+
u = User.using(:brazil).find_by_number(10)
|
|
352
|
+
u.increment!(:number)
|
|
353
|
+
expect(User.using(:brazil).find_by_number(11)).not_to be_nil
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
it 'decrement' do
|
|
357
|
+
_ = User.using(:brazil).create!(:name => 'Teste', :number => 10)
|
|
358
|
+
u = User.using(:brazil).find_by_number(10)
|
|
359
|
+
u.decrement(:number)
|
|
360
|
+
u.save
|
|
361
|
+
expect(User.using(:brazil).find_by_number(9)).not_to be_nil
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
it 'decrement!' do
|
|
365
|
+
_ = User.using(:brazil).create!(:name => 'Teste', :number => 10)
|
|
366
|
+
u = User.using(:brazil).find_by_number(10)
|
|
367
|
+
u.decrement!(:number)
|
|
368
|
+
expect(User.using(:brazil).find_by_number(9)).not_to be_nil
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
it 'toggle' do
|
|
372
|
+
_ = User.using(:brazil).create!(:name => 'Teste', :admin => false)
|
|
373
|
+
u = User.using(:brazil).find_by_name('Teste')
|
|
374
|
+
u.toggle(:admin)
|
|
375
|
+
u.save
|
|
376
|
+
expect(User.using(:brazil).find_by_name('Teste').admin).to be true
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
it 'toggle!' do
|
|
380
|
+
_ = User.using(:brazil).create!(:name => 'Teste', :admin => false)
|
|
381
|
+
u = User.using(:brazil).find_by_name('Teste')
|
|
382
|
+
u.toggle!(:admin)
|
|
383
|
+
expect(User.using(:brazil).find_by_name('Teste').admin).to be true
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
it 'count' do
|
|
387
|
+
_u = User.using(:brazil).create!(:name => 'User1')
|
|
388
|
+
_v = User.using(:brazil).create!(:name => 'User2')
|
|
389
|
+
_w = User.using(:brazil).create!(:name => 'User3')
|
|
390
|
+
expect(User.using(:brazil).where(:name => 'User2').all.count).to eq(1)
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
it 'maximum' do
|
|
394
|
+
_u = User.using(:brazil).create!(:name => 'Teste', :number => 11)
|
|
395
|
+
_v = User.using(:master).create!(:name => 'Teste', :number => 12)
|
|
396
|
+
|
|
397
|
+
expect(User.using(:brazil).maximum(:number)).to eq(11)
|
|
398
|
+
expect(User.using(:master).maximum(:number)).to eq(12)
|
|
399
|
+
end
|
|
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
|
+
|
|
413
|
+
describe 'any?' do
|
|
414
|
+
before { User.using(:brazil).create!(:name => 'User1') }
|
|
415
|
+
|
|
416
|
+
it 'works when true' do
|
|
417
|
+
scope = User.using(:brazil).where(:name => 'User1')
|
|
418
|
+
expect(scope.any?).to be true
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
it 'works when false' do
|
|
422
|
+
scope = User.using(:brazil).where(:name => 'User2')
|
|
423
|
+
expect(scope.any?).to be false
|
|
424
|
+
end
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
it 'exists?' do
|
|
428
|
+
@user = User.using(:brazil).create!(:name => 'User1')
|
|
429
|
+
|
|
430
|
+
expect(User.using(:brazil).where(:name => 'User1').exists?).to be true
|
|
431
|
+
expect(User.using(:brazil).where(:name => 'User2').exists?).to be false
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
describe 'touch' do
|
|
435
|
+
it 'updates updated_at by default' do
|
|
436
|
+
@user = User.using(:brazil).create!(:name => 'User1')
|
|
437
|
+
User.using(:brazil).where(:id => @user.id).update_all(:updated_at => Time.now - 3.months)
|
|
438
|
+
@user.touch
|
|
439
|
+
expect(@user.reload.updated_at.in_time_zone('GMT').to_date).to eq(Time.now.in_time_zone('GMT').to_date)
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
it 'updates passed in attribute name' do
|
|
443
|
+
@user = User.using(:brazil).create!(:name => 'User1')
|
|
444
|
+
User.using(:brazil).where(:id => @user.id).update_all(:created_at => Time.now - 3.months)
|
|
445
|
+
@user.touch(:created_at)
|
|
446
|
+
expect(@user.reload.created_at.in_time_zone('GMT').to_date).to eq(Time.now.in_time_zone('GMT').to_date)
|
|
447
|
+
end
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
describe '#pluck' do
|
|
451
|
+
before { User.using(:brazil).create!(:name => 'User1') }
|
|
452
|
+
|
|
453
|
+
it 'should works from scope proxy' do
|
|
454
|
+
names = User.using(:brazil).pluck(:name)
|
|
455
|
+
expect(names).to eq(['User1'])
|
|
456
|
+
expect(User.using(:master).pluck(:name)).to eq([])
|
|
457
|
+
end
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
it 'update_column' do
|
|
461
|
+
@user = User.using(:brazil).create!(:name => 'User1')
|
|
462
|
+
@user2 = User.using(:brazil).find(@user.id)
|
|
463
|
+
@user2.update_column(:name, 'Joaquim Shard Brazil')
|
|
464
|
+
expect(User.using(:brazil).find_by_name('Joaquim Shard Brazil')).not_to be_nil
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
it 'update_attributes' do
|
|
468
|
+
@user = User.using(:brazil).create!(:name => 'User1')
|
|
469
|
+
@user2 = User.using(:brazil).find(@user.id)
|
|
470
|
+
@user2.update_attributes(:name => 'Joaquim')
|
|
471
|
+
expect(User.using(:brazil).find_by_name('Joaquim')).not_to be_nil
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
it 'using update_attributes inside a block' do
|
|
475
|
+
Octopus.using(:brazil) do
|
|
476
|
+
@user = User.create!(:name => 'User1')
|
|
477
|
+
@user2 = User.find(@user.id)
|
|
478
|
+
@user2.update_attributes(:name => 'Joaquim')
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
expect(User.find_by_name('Joaquim')).to be_nil
|
|
482
|
+
expect(User.using(:brazil).find_by_name('Joaquim')).not_to be_nil
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
it 'update_attribute' do
|
|
486
|
+
@user = User.using(:brazil).create!(:name => 'User1')
|
|
487
|
+
@user2 = User.using(:brazil).find(@user.id)
|
|
488
|
+
@user2.update_attribute(:name, 'Joaquim')
|
|
489
|
+
expect(User.using(:brazil).find_by_name('Joaquim')).not_to be_nil
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
it 'as_json' do
|
|
493
|
+
ActiveRecord::Base.include_root_in_json = false
|
|
494
|
+
|
|
495
|
+
Octopus.using(:brazil) do
|
|
496
|
+
User.create!(:name => 'User1')
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
user = User.using(:brazil).where(:name => 'User1').first
|
|
500
|
+
expect(user.as_json(:except => [:created_at, :updated_at, :id, :current_shard])).to eq('admin' => nil, 'name' => 'User1', 'number' => nil)
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
describe 'transaction' do
|
|
504
|
+
context 'without assigning a database' do
|
|
505
|
+
it 'works as expected' do
|
|
506
|
+
_u = User.create!(:name => 'Thiago')
|
|
507
|
+
|
|
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
|
|
515
|
+
|
|
516
|
+
expect(User.using(:brazil).count).to eq(1)
|
|
517
|
+
expect(User.using(:master).count).to eq(1)
|
|
518
|
+
end
|
|
519
|
+
end
|
|
520
|
+
|
|
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
|
|
610
|
+
end
|
|
611
|
+
|
|
612
|
+
describe 'deleting a record' do
|
|
613
|
+
before(:each) do
|
|
614
|
+
@user = User.using(:brazil).create!(:name => 'User1')
|
|
615
|
+
@user2 = User.using(:brazil).find(@user.id)
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
it 'delete' do
|
|
619
|
+
@user2.delete
|
|
620
|
+
expect { User.using(:brazil).find(@user2.id) }.to raise_error(ActiveRecord::RecordNotFound)
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
it "delete within block shouldn't lose shard" do
|
|
624
|
+
Octopus.using(:brazil) do
|
|
625
|
+
@user2.delete
|
|
626
|
+
@user3 = User.create(:name => 'User3')
|
|
627
|
+
|
|
628
|
+
expect(User.connection.current_shard).to eq(:brazil)
|
|
629
|
+
expect(User.find(@user3.id)).to eq(@user3)
|
|
630
|
+
end
|
|
631
|
+
end
|
|
632
|
+
|
|
633
|
+
it 'destroy' do
|
|
634
|
+
@user2.destroy
|
|
635
|
+
expect { User.using(:brazil).find(@user2.id) }.to raise_error(ActiveRecord::RecordNotFound)
|
|
636
|
+
end
|
|
637
|
+
|
|
638
|
+
it "destroy within block shouldn't lose shard" do
|
|
639
|
+
Octopus.using(:brazil) do
|
|
640
|
+
@user2.destroy
|
|
641
|
+
@user3 = User.create(:name => 'User3')
|
|
642
|
+
|
|
643
|
+
expect(User.connection.current_shard).to eq(:brazil)
|
|
644
|
+
expect(User.find(@user3.id)).to eq(@user3)
|
|
645
|
+
end
|
|
646
|
+
end
|
|
647
|
+
end
|
|
648
|
+
end
|
|
649
|
+
|
|
650
|
+
describe 'custom connection' do
|
|
651
|
+
context 'by default' do
|
|
652
|
+
it 'with plain call should use custom connection' do
|
|
653
|
+
expect(CustomConnection.connection.current_database).to eq('octopus_shard_2')
|
|
654
|
+
end
|
|
655
|
+
|
|
656
|
+
it 'should ignore using called on relation' do
|
|
657
|
+
expect(CustomConnection.using(:postgresql_shard).connection.current_database).to eq('octopus_shard_2')
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
it 'should ignore Octopus.using block' do
|
|
661
|
+
Octopus.using(:postgresql_shard) do
|
|
662
|
+
expect(CustomConnection.connection.current_database).to eq('octopus_shard_2')
|
|
663
|
+
end
|
|
664
|
+
end
|
|
665
|
+
|
|
666
|
+
it 'should save to correct shard' do
|
|
667
|
+
expect { CustomConnection.create(:value => 'custom value') }.to change {
|
|
668
|
+
CustomConnection
|
|
669
|
+
.connection
|
|
670
|
+
.execute("select count(*) as ct from custom where value = 'custom value'")
|
|
671
|
+
.to_a.first.first
|
|
672
|
+
}.by 1
|
|
673
|
+
end
|
|
674
|
+
end
|
|
675
|
+
|
|
676
|
+
context 'with allowed_shards configured' do
|
|
677
|
+
before do
|
|
678
|
+
CustomConnection.allow_shard :postgresql_shard
|
|
679
|
+
end
|
|
680
|
+
|
|
681
|
+
it 'with plain call should use custom connection' do
|
|
682
|
+
expect(CustomConnection.connection.current_database).to eq('octopus_shard_2')
|
|
683
|
+
end
|
|
684
|
+
|
|
685
|
+
it 'with using called on relation with allowed shard should use' do
|
|
686
|
+
expect(CustomConnection.using(:postgresql_shard).connection.current_database).to eq('octopus_shard_1')
|
|
687
|
+
end
|
|
688
|
+
|
|
689
|
+
it 'within Octopus.using block with allowed shard should use' do
|
|
690
|
+
Octopus.using(:postgresql_shard) do
|
|
691
|
+
expect(CustomConnection.connection.current_database).to eq('octopus_shard_1')
|
|
692
|
+
end
|
|
693
|
+
end
|
|
694
|
+
|
|
695
|
+
it 'with using called on relation with disallowed shard should not use' do
|
|
696
|
+
expect(CustomConnection.using(:brazil).connection.current_database).to eq('octopus_shard_2')
|
|
697
|
+
end
|
|
698
|
+
|
|
699
|
+
it 'within Octopus.using block with disallowed shard should not use' do
|
|
700
|
+
Octopus.using(:brazil) do
|
|
701
|
+
expect(CustomConnection.connection.current_database).to eq('octopus_shard_2')
|
|
702
|
+
end
|
|
703
|
+
end
|
|
704
|
+
|
|
705
|
+
it 'should save to correct shard' do
|
|
706
|
+
expect { CustomConnection.create(:value => 'custom value') }.to change {
|
|
707
|
+
CustomConnection
|
|
708
|
+
.connection
|
|
709
|
+
.execute("select count(*) as ct from custom where value = 'custom value'")
|
|
710
|
+
.to_a.first.first
|
|
711
|
+
}.by 1
|
|
712
|
+
end
|
|
713
|
+
|
|
714
|
+
it 'should clean up correctly' do
|
|
715
|
+
User.create!(:name => 'CleanUser')
|
|
716
|
+
CustomConnection.using(:postgresql_shard).first
|
|
717
|
+
expect(User.first).not_to be_nil
|
|
718
|
+
end
|
|
719
|
+
|
|
720
|
+
it 'should clean up correctly even inside block' do
|
|
721
|
+
User.create!(:name => 'CleanUser')
|
|
722
|
+
|
|
723
|
+
Octopus.using(:master) do
|
|
724
|
+
CustomConnection.using(:postgresql_shard).connection.execute('select count(*) from users')
|
|
725
|
+
expect(User.first).not_to be_nil
|
|
726
|
+
end
|
|
727
|
+
end
|
|
728
|
+
end
|
|
729
|
+
|
|
730
|
+
describe 'clear_active_connections!' do
|
|
731
|
+
it 'should not leak connection' do
|
|
732
|
+
CustomConnection.create(:value => 'custom value')
|
|
733
|
+
|
|
734
|
+
# This is what Rails, Sidekiq etc call--this normally handles all connection pools in the app
|
|
735
|
+
expect { ActiveRecord::Base.clear_active_connections! }
|
|
736
|
+
.to change { CustomConnection.connection_pool.active_connection? }
|
|
737
|
+
|
|
738
|
+
expect(CustomConnection.connection_pool.active_connection?).to be_falsey
|
|
739
|
+
end
|
|
740
|
+
end
|
|
741
|
+
end
|
|
742
|
+
|
|
743
|
+
describe 'when using set_table_name' do
|
|
744
|
+
it 'should work correctly' do
|
|
745
|
+
Bacon.using(:brazil).create!(:name => 'YUMMMYYYY')
|
|
746
|
+
end
|
|
747
|
+
|
|
748
|
+
it 'should work correctly with a block' do
|
|
749
|
+
Cheese.using(:brazil).create!(:name => 'YUMMMYYYY')
|
|
750
|
+
end
|
|
751
|
+
end
|
|
752
|
+
|
|
753
|
+
describe 'when using table_name=' do
|
|
754
|
+
it 'should work correctly' do
|
|
755
|
+
Ham.using(:brazil).create!(:name => 'YUMMMYYYY')
|
|
756
|
+
end
|
|
757
|
+
end
|
|
758
|
+
|
|
759
|
+
describe 'when using a environment with a single adapter' do
|
|
760
|
+
it 'should not clean the table name' do
|
|
761
|
+
OctopusHelper.using_environment :production_fully_replicated do
|
|
762
|
+
expect(Keyboard).not_to receive(:reset_table_name)
|
|
763
|
+
Keyboard.using(:master).create!(:name => 'Master Cat')
|
|
764
|
+
end
|
|
765
|
+
end
|
|
766
|
+
end
|
|
767
|
+
|
|
768
|
+
describe 'when you have joins/include' do
|
|
769
|
+
before(:each) do
|
|
770
|
+
@client1 = Client.using(:brazil).create(:name => 'Thiago')
|
|
771
|
+
|
|
772
|
+
Octopus.using(:canada) do
|
|
773
|
+
@client2 = Client.create(:name => 'Mike')
|
|
774
|
+
@client3 = Client.create(:name => 'Joao')
|
|
775
|
+
@item1 = Item.create(:client => @client2, :name => 'Item 1')
|
|
776
|
+
@item2 = Item.create(:client => @client2, :name => 'Item 2')
|
|
777
|
+
@item3 = Item.create(:client => @client3, :name => 'Item 3')
|
|
778
|
+
@part1 = Part.create(:item => @item1, :name => 'Part 1')
|
|
779
|
+
@part2 = Part.create(:item => @item1, :name => 'Part 2')
|
|
780
|
+
@part3 = Part.create(:item => @item2, :name => 'Part 3')
|
|
781
|
+
end
|
|
782
|
+
|
|
783
|
+
@item4 = Item.using(:brazil).create(:client => @client1, :name => 'Item 4')
|
|
784
|
+
end
|
|
785
|
+
|
|
786
|
+
it 'should work using the rails 3.x syntax' do
|
|
787
|
+
items = Item.using(:canada).joins(:client).where("clients.id = #{@client2.id}").all
|
|
788
|
+
expect(items).to eq([@item1, @item2])
|
|
789
|
+
end
|
|
790
|
+
|
|
791
|
+
it 'should work for include also, rails 3.x syntax' do
|
|
792
|
+
items = Item.using(:canada).includes(:client).where(:clients => { :id => @client2.id }).all
|
|
793
|
+
expect(items).to eq([@item1, @item2])
|
|
794
|
+
end
|
|
795
|
+
end
|
|
796
|
+
|
|
797
|
+
describe 'ActiveRecord::Base Validations' do
|
|
798
|
+
it 'should work correctly when using validations' do
|
|
799
|
+
@key = Keyboard.create!(:name => 'Key')
|
|
800
|
+
expect { Keyboard.using(:brazil).create!(:name => 'Key') }.not_to raise_error
|
|
801
|
+
expect { Keyboard.create!(:name => 'Key') }.to raise_error(ActiveRecord::RecordInvalid)
|
|
802
|
+
end
|
|
803
|
+
|
|
804
|
+
it 'should work correctly when using validations with using syntax' do
|
|
805
|
+
@key = Keyboard.using(:brazil).create!(:name => 'Key')
|
|
806
|
+
expect { Keyboard.create!(:name => 'Key') }.not_to raise_error
|
|
807
|
+
expect { Keyboard.using(:brazil).create!(:name => 'Key') }
|
|
808
|
+
.to raise_error(ActiveRecord::RecordInvalid)
|
|
809
|
+
end
|
|
810
|
+
end
|
|
811
|
+
|
|
812
|
+
describe '#replicated_model method' do
|
|
813
|
+
it 'should be replicated' do
|
|
814
|
+
OctopusHelper.using_environment :production_replicated do
|
|
815
|
+
expect(ActiveRecord::Base.connection_proxy.replicated).to be true
|
|
816
|
+
end
|
|
817
|
+
end
|
|
818
|
+
|
|
819
|
+
it 'should mark the Cat model as replicated' do
|
|
820
|
+
OctopusHelper.using_environment :production_replicated do
|
|
821
|
+
expect(User.replicated).to be_falsey
|
|
822
|
+
expect(Cat.replicated).to be true
|
|
823
|
+
end
|
|
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
|
|
836
|
+
end
|
|
837
|
+
end
|