ar-octopus 0.8.4 → 0.8.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +7 -3
- data/Appraisals +3 -0
- data/README.mkdn +18 -0
- data/Rakefile +6 -1
- data/gemfiles/rails42.gemfile +7 -0
- data/lib/octopus.rb +24 -0
- data/lib/octopus/model.rb +24 -4
- data/lib/octopus/persistence.rb +1 -1
- data/lib/octopus/proxy.rb +63 -16
- data/lib/octopus/relation_proxy.rb +1 -1
- data/lib/octopus/scope_proxy.rb +9 -3
- data/lib/octopus/version.rb +1 -1
- data/spec/config/shards.yml +13 -0
- data/spec/octopus/model_spec.rb +94 -1
- data/spec/octopus/relation_proxy_spec.rb +7 -0
- data/spec/octopus/replication_spec.rb +27 -17
- data/spec/support/database_models.rb +2 -0
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a2e171072013526c5597b2d233d983f129a84cec
|
4
|
+
data.tar.gz: 0c05c12e05f5dfb64aaf6f15e0accb59c2174a53
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 19f50eb815a6273a87430354c5124096d90d561a8b27c1bfe0073a5a81f3a2242c71f099b88db0bab031018c56a8662ed587122412a0042ba543bc41c6b0d745
|
7
|
+
data.tar.gz: 11828df91744a59445fe6a4add2c6081971f724ed9394b96eb12b36c6a09e69db2546d7dc1eadc56c19074ed5de1d7cd28f39a38c0bd7d3ad88030238a46a98e
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.1.
|
1
|
+
2.1.5
|
data/.travis.yml
CHANGED
@@ -4,14 +4,18 @@ env:
|
|
4
4
|
before_script:
|
5
5
|
- "bundle exec rake db:prepare"
|
6
6
|
rvm:
|
7
|
-
- 1.9.3
|
8
7
|
- 2.0.0
|
9
|
-
- 2.1.
|
8
|
+
- 2.1.5
|
9
|
+
- 2.2.0
|
10
10
|
gemfile:
|
11
|
-
- gemfiles/rails32.gemfile
|
12
11
|
- gemfiles/rails4.gemfile
|
13
12
|
- gemfiles/rails41.gemfile
|
13
|
+
- gemfiles/rails42.gemfile
|
14
14
|
notifications:
|
15
15
|
recipients:
|
16
16
|
- gabriel.sobrinho@gmail.com
|
17
17
|
- thiago.pradi@gmail.com
|
18
|
+
matrix:
|
19
|
+
include:
|
20
|
+
- rvm: 1.9.3
|
21
|
+
gemfile: gemfiles/rails32.gemfile
|
data/Appraisals
CHANGED
data/README.mkdn
CHANGED
@@ -158,6 +158,24 @@ class CustomConnection < ActiveRecord::Base
|
|
158
158
|
end
|
159
159
|
```
|
160
160
|
|
161
|
+
### allow_shard
|
162
|
+
If you'd like to use specific shards with a model that has a Rails-managed connection, you can use `allow_shard`:
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
class CustomConnectedModel
|
166
|
+
octopus_establish_connection(...)
|
167
|
+
allow_shard :my_shard
|
168
|
+
end
|
169
|
+
|
170
|
+
#This uses :my_shard
|
171
|
+
CustomConnectedModel.using(:my_shard).first
|
172
|
+
|
173
|
+
#This uses the Rails-managed connection pool (the call to 'using' is ignored)
|
174
|
+
CustomConnectedModel.using(:some_other_shard).first
|
175
|
+
```
|
176
|
+
|
177
|
+
This can be useful if you have a model that lives in a separate database and would like to add sharding or replication to it. For other use cases, you may be better off with <a href="https://github.com/tchandy/octopus/wiki/Slave-Groups">slave groups</a>.
|
178
|
+
|
161
179
|
## Contributing with Octopus
|
162
180
|
Contributors are welcome! To run the test suite, you need mysql, postgresql and sqlite3 installed. This is what you need to setup your Octopus development environment:
|
163
181
|
|
data/Rakefile
CHANGED
@@ -6,7 +6,7 @@ require 'appraisal'
|
|
6
6
|
RSpec::Core::RakeTask.new
|
7
7
|
RuboCop::RakeTask.new
|
8
8
|
|
9
|
-
task :default => [:spec
|
9
|
+
task :default => [:spec]
|
10
10
|
|
11
11
|
namespace :db do
|
12
12
|
desc 'Build the databases for tests'
|
@@ -127,6 +127,7 @@ namespace :db do
|
|
127
127
|
u.string :name
|
128
128
|
u.string :commentable_type
|
129
129
|
u.integer :commentable_id
|
130
|
+
u.boolean :open, default: false
|
130
131
|
end
|
131
132
|
|
132
133
|
BlankModel.using(shard_symbol).connection.create_table(:parts) do |u|
|
@@ -142,6 +143,10 @@ namespace :db do
|
|
142
143
|
u.string :name
|
143
144
|
end
|
144
145
|
|
146
|
+
BlankModel.using(shard_symbol).connection.create_table(:custom) do |u|
|
147
|
+
u.string :value
|
148
|
+
end
|
149
|
+
|
145
150
|
if shard_symbol == :alone_shard
|
146
151
|
BlankModel.using(shard_symbol).connection.create_table(:mmorpg_players) do |u|
|
147
152
|
u.string :player_name
|
data/lib/octopus.rb
CHANGED
@@ -64,6 +64,20 @@ module Octopus
|
|
64
64
|
@environments ||= config['environments'] || ['production']
|
65
65
|
end
|
66
66
|
|
67
|
+
def self.robust_environments=(environments)
|
68
|
+
@robust_environments = environments.map(&:to_s)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Environments in which to swallow failures from a single shard
|
72
|
+
# when iterating through all.
|
73
|
+
def self.robust_environments
|
74
|
+
@robust_environments ||= config['robust_environments'] || ['production']
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.robust_environment?
|
78
|
+
robust_environments.include? rails_env
|
79
|
+
end
|
80
|
+
|
67
81
|
def self.rails3?
|
68
82
|
ActiveRecord::VERSION::MAJOR <= 3
|
69
83
|
end
|
@@ -80,6 +94,16 @@ module Octopus
|
|
80
94
|
defined?(Rails)
|
81
95
|
end
|
82
96
|
|
97
|
+
attr_writer :logger
|
98
|
+
|
99
|
+
def self.logger
|
100
|
+
if defined?(Rails)
|
101
|
+
@logger ||= Rails.logger
|
102
|
+
else
|
103
|
+
@logger ||= Logger.new($stderr)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
83
107
|
def self.shards=(shards)
|
84
108
|
config[rails_env] = HashWithIndifferentAccess.new(shards)
|
85
109
|
ActiveRecord::Base.connection.initialize_shards(@config)
|
data/lib/octopus/model.rb
CHANGED
@@ -44,10 +44,12 @@ module Octopus
|
|
44
44
|
return unless Octopus.enabled?
|
45
45
|
|
46
46
|
if new_record? || self.class.connection_proxy.block
|
47
|
-
|
47
|
+
shard = self.class.connection_proxy.current_shard
|
48
48
|
else
|
49
|
-
|
49
|
+
shard = self.class.connection_proxy.last_current_shard || self.class.connection_proxy.current_shard
|
50
50
|
end
|
51
|
+
|
52
|
+
self.current_shard = shard if self.class.allowed_shard?(shard)
|
51
53
|
end
|
52
54
|
|
53
55
|
def should_set_current_shard?
|
@@ -75,6 +77,7 @@ module Octopus
|
|
75
77
|
def self.extended(base)
|
76
78
|
base.class_attribute(:replicated)
|
77
79
|
base.class_attribute(:sharded)
|
80
|
+
base.class_attribute(:allowed_shards)
|
78
81
|
base.hijack_methods
|
79
82
|
end
|
80
83
|
|
@@ -86,8 +89,13 @@ module Octopus
|
|
86
89
|
self.sharded = true
|
87
90
|
end
|
88
91
|
|
92
|
+
def allow_shard(*shards)
|
93
|
+
self.allowed_shards ||= []
|
94
|
+
self.allowed_shards += shards
|
95
|
+
end
|
96
|
+
|
89
97
|
def hijack_methods
|
90
|
-
around_save :run_on_shard
|
98
|
+
around_save :run_on_shard, :unless => -> { self.class.custom_octopus_connection }
|
91
99
|
after_initialize :set_current_shard
|
92
100
|
|
93
101
|
class << self
|
@@ -116,7 +124,19 @@ module Octopus
|
|
116
124
|
end
|
117
125
|
|
118
126
|
def should_use_normal_connection?
|
119
|
-
!Octopus.enabled?
|
127
|
+
if !Octopus.enabled?
|
128
|
+
true
|
129
|
+
elsif custom_octopus_connection
|
130
|
+
!connection_proxy.block || !allowed_shard?(connection_proxy.current_shard)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def allowed_shard?(shard)
|
135
|
+
if custom_octopus_connection
|
136
|
+
allowed_shards && shard && allowed_shards.include?(shard)
|
137
|
+
else
|
138
|
+
true
|
139
|
+
end
|
120
140
|
end
|
121
141
|
|
122
142
|
def connection_with_octopus
|
data/lib/octopus/persistence.rb
CHANGED
data/lib/octopus/proxy.rb
CHANGED
@@ -17,7 +17,7 @@ module Octopus
|
|
17
17
|
@slave_groups = HashWithIndifferentAccess.new
|
18
18
|
@groups = {}
|
19
19
|
@adapters = Set.new
|
20
|
-
@config = ActiveRecord::Base.connection_pool_without_octopus.
|
20
|
+
@config = ActiveRecord::Base.connection_pool_without_octopus.spec.config
|
21
21
|
|
22
22
|
unless config.nil?
|
23
23
|
@entire_sharded = config['entire_sharded']
|
@@ -202,6 +202,9 @@ module Octopus
|
|
202
202
|
# reconnect, but in Rails 3.1 the flag prevents this.
|
203
203
|
def safe_connection(connection_pool)
|
204
204
|
connection_pool.automatic_reconnect ||= true
|
205
|
+
if !connection_pool.connected? && @shards[:master].connection.query_cache_enabled
|
206
|
+
connection_pool.connection.enable_query_cache!
|
207
|
+
end
|
205
208
|
connection_pool.connection
|
206
209
|
end
|
207
210
|
|
@@ -218,7 +221,7 @@ module Octopus
|
|
218
221
|
end
|
219
222
|
|
220
223
|
def run_queries_on_shard(shard, &_block)
|
221
|
-
keeping_connection_proxy do
|
224
|
+
keeping_connection_proxy(shard) do
|
222
225
|
using_shard(shard) do
|
223
226
|
yield
|
224
227
|
end
|
@@ -233,8 +236,9 @@ module Octopus
|
|
233
236
|
|
234
237
|
def clean_connection_proxy
|
235
238
|
self.current_shard = :master
|
239
|
+
self.current_model = nil
|
236
240
|
self.current_group = nil
|
237
|
-
self.block =
|
241
|
+
self.block = nil
|
238
242
|
end
|
239
243
|
|
240
244
|
def check_schema_migrations(shard)
|
@@ -244,8 +248,7 @@ module Octopus
|
|
244
248
|
end
|
245
249
|
|
246
250
|
def transaction(options = {}, &block)
|
247
|
-
|
248
|
-
if !sharded && replicated
|
251
|
+
if !sharded && current_model_replicated?
|
249
252
|
run_queries_on_shard(:master) do
|
250
253
|
select_connection.transaction(options, &block)
|
251
254
|
end
|
@@ -281,23 +284,23 @@ module Octopus
|
|
281
284
|
|
282
285
|
def enable_query_cache!
|
283
286
|
clear_query_cache
|
284
|
-
|
287
|
+
with_each_healthy_shard { |v| v.connected? && safe_connection(v).enable_query_cache! }
|
285
288
|
end
|
286
289
|
|
287
290
|
def disable_query_cache!
|
288
|
-
|
291
|
+
with_each_healthy_shard { |v| v.connected? && safe_connection(v).disable_query_cache! }
|
289
292
|
end
|
290
293
|
|
291
294
|
def clear_query_cache
|
292
|
-
|
295
|
+
with_each_healthy_shard { |v| v.connected? && safe_connection(v).clear_query_cache }
|
293
296
|
end
|
294
297
|
|
295
298
|
def clear_active_connections!
|
296
|
-
|
299
|
+
with_each_healthy_shard(&:release_connection)
|
297
300
|
end
|
298
301
|
|
299
302
|
def clear_all_connections!
|
300
|
-
|
303
|
+
with_each_healthy_shard(&:disconnect!)
|
301
304
|
end
|
302
305
|
|
303
306
|
def connected?
|
@@ -322,6 +325,44 @@ module Octopus
|
|
322
325
|
|
323
326
|
protected
|
324
327
|
|
328
|
+
# Ensure that a single failing slave doesn't take down the entire application
|
329
|
+
def with_each_healthy_shard
|
330
|
+
@shards.each do |shard_name, v|
|
331
|
+
begin
|
332
|
+
yield(v)
|
333
|
+
rescue => e
|
334
|
+
if Octopus.robust_environment?
|
335
|
+
Octopus.logger.error "Error on shard #{shard_name}: #{e.message}"
|
336
|
+
else
|
337
|
+
raise
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
conn_handler = ActiveRecord::Base.connection_handler
|
343
|
+
if conn_handler.respond_to?(:connection_pool_list)
|
344
|
+
# Rails 4+
|
345
|
+
ar_pools = conn_handler.connection_pool_list
|
346
|
+
else
|
347
|
+
# Rails 3.2
|
348
|
+
ar_pools = conn_handler.connection_pools.values
|
349
|
+
end
|
350
|
+
|
351
|
+
ar_pools.each do |pool|
|
352
|
+
next if pool == @shards[:master] # Already handled this
|
353
|
+
|
354
|
+
begin
|
355
|
+
yield(pool)
|
356
|
+
rescue => e
|
357
|
+
if Octopus.robust_environment?
|
358
|
+
Octopus.logger.error "Error on pool (spec: #{pool.spec}): #{e.message}"
|
359
|
+
else
|
360
|
+
raise
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
325
366
|
def connection_pool_for(adapter, config)
|
326
367
|
if Octopus.rails4?
|
327
368
|
arg = ActiveRecord::ConnectionAdapters::ConnectionSpecification.new(adapter.dup, config)
|
@@ -356,7 +397,7 @@ module Octopus
|
|
356
397
|
end
|
357
398
|
|
358
399
|
def should_clean_connection_proxy?(method)
|
359
|
-
method.to_s =~ /insert|select|execute/ &&
|
400
|
+
method.to_s =~ /insert|select|execute/ && !current_model_replicated? && (!block || block != current_shard)
|
360
401
|
end
|
361
402
|
|
362
403
|
# Try to use slaves if and only if `replicated: true` is specified in `shards.yml` and no slaves groups are defined
|
@@ -364,6 +405,10 @@ module Octopus
|
|
364
405
|
@replicated && method.to_s =~ /select/ && !block && !slaves_grouped?
|
365
406
|
end
|
366
407
|
|
408
|
+
def current_model_replicated?
|
409
|
+
@replicated && (current_model.try(:replicated) || fully_replicated?)
|
410
|
+
end
|
411
|
+
|
367
412
|
def send_queries_to_selected_slave(method, *args, &block)
|
368
413
|
if current_model.replicated || fully_replicated?
|
369
414
|
selected_slave = @slaves_load_balancer.next
|
@@ -384,7 +429,7 @@ module Octopus
|
|
384
429
|
# while ensuring that we revert `current_shard` from the selected slave to the (shard's) master
|
385
430
|
# not to make queries other than SELECT leak to the slave.
|
386
431
|
def should_use_slaves_for_method?(method)
|
387
|
-
|
432
|
+
current_model_replicated? && method.to_s =~ /select/
|
388
433
|
end
|
389
434
|
|
390
435
|
def slaves_grouped?
|
@@ -409,14 +454,14 @@ module Octopus
|
|
409
454
|
#
|
410
455
|
# @see Octopus::Proxy#should_clean_connection?
|
411
456
|
# @see Octopus::Proxy#clean_connection_proxy
|
412
|
-
def keeping_connection_proxy(&_block)
|
457
|
+
def keeping_connection_proxy(shard, &_block)
|
413
458
|
last_block = block
|
414
459
|
|
415
460
|
begin
|
416
|
-
self.block =
|
461
|
+
self.block = shard
|
417
462
|
yield
|
418
463
|
ensure
|
419
|
-
self.block = last_block ||
|
464
|
+
self.block = last_block || nil
|
420
465
|
end
|
421
466
|
end
|
422
467
|
|
@@ -425,7 +470,9 @@ module Octopus
|
|
425
470
|
older_shard = current_shard
|
426
471
|
|
427
472
|
begin
|
428
|
-
|
473
|
+
unless current_model && !current_model.allowed_shard?(shard)
|
474
|
+
self.current_shard = shard
|
475
|
+
end
|
429
476
|
yield
|
430
477
|
ensure
|
431
478
|
self.current_shard = older_shard
|
data/lib/octopus/scope_proxy.rb
CHANGED
@@ -28,13 +28,19 @@ module Octopus
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def connection
|
31
|
-
@klass.
|
32
|
-
|
31
|
+
@klass.connection_proxy.current_shard = @current_shard
|
32
|
+
|
33
|
+
if @klass.custom_octopus_connection && @klass.allowed_shard?(@current_shard)
|
34
|
+
# Force use of proxy, given we called 'using' explicitly to get here
|
35
|
+
@klass.connection_proxy.current_model = @klass
|
36
|
+
@klass.connection_proxy
|
37
|
+
else
|
38
|
+
@klass.connection
|
39
|
+
end
|
33
40
|
end
|
34
41
|
|
35
42
|
def method_missing(method, *args, &block)
|
36
43
|
result = run_on_shard { @klass.send(method, *args, &block) }
|
37
|
-
|
38
44
|
if result.respond_to?(:all)
|
39
45
|
@klass = result
|
40
46
|
return self
|
data/lib/octopus/version.rb
CHANGED
data/spec/config/shards.yml
CHANGED
@@ -3,6 +3,12 @@ mysql: &mysql
|
|
3
3
|
username: <%= ENV['MYSQL_USER'] || 'root' %>
|
4
4
|
host: localhost
|
5
5
|
|
6
|
+
mysql_unavailable: &mysql_unavailable
|
7
|
+
adapter: mysql2
|
8
|
+
username: <%= ENV['MYSQL_USER'] || 'root' %>
|
9
|
+
host: 192.0.2.1
|
10
|
+
connect_timeout: 3
|
11
|
+
|
6
12
|
octopus: &octopus
|
7
13
|
shards:
|
8
14
|
alone_shard:
|
@@ -87,6 +93,13 @@ replicated_with_one_slave:
|
|
87
93
|
database: octopus_shard_2
|
88
94
|
<<: *mysql
|
89
95
|
|
96
|
+
replicated_with_one_slave_unavailable:
|
97
|
+
replicated: true
|
98
|
+
shards:
|
99
|
+
slave1:
|
100
|
+
database: octopus_shard_2
|
101
|
+
<<: *mysql_unavailable
|
102
|
+
|
90
103
|
|
91
104
|
production_fully_replicated:
|
92
105
|
replicated: true
|
data/spec/octopus/model_spec.rb
CHANGED
@@ -91,7 +91,7 @@ describe Octopus::Model do
|
|
91
91
|
|
92
92
|
it 'should work when you have a SQLite3 shard' do
|
93
93
|
u = User.using(:sqlite_shard).create!(:name => 'Sqlite3')
|
94
|
-
expect(User.using(:sqlite_shard).
|
94
|
+
expect(User.using(:sqlite_shard).where(name: 'Sqlite3').first).to eq(u)
|
95
95
|
end
|
96
96
|
|
97
97
|
it 'should clean #current_shard from proxy when using execute' do
|
@@ -471,6 +471,99 @@ describe Octopus::Model do
|
|
471
471
|
end
|
472
472
|
end
|
473
473
|
|
474
|
+
describe 'custom connection' do
|
475
|
+
context 'by default' do
|
476
|
+
it 'with plain call should use custom connection' do
|
477
|
+
expect(CustomConnection.connection.current_database).to eq('octopus_shard_2')
|
478
|
+
end
|
479
|
+
|
480
|
+
it 'should ignore using called on relation' do
|
481
|
+
expect(CustomConnection.using(:postgresql_shard).connection.current_database).to eq('octopus_shard_2')
|
482
|
+
end
|
483
|
+
|
484
|
+
it 'should ignore Octopus.using block' do
|
485
|
+
Octopus.using(:postgresql_shard) do
|
486
|
+
expect(CustomConnection.connection.current_database).to eq('octopus_shard_2')
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
490
|
+
it 'should save to correct shard' do
|
491
|
+
expect { CustomConnection.create(:value => 'custom value') }.to change {
|
492
|
+
CustomConnection
|
493
|
+
.connection
|
494
|
+
.execute("select count(*) as ct from custom where value = 'custom value'")
|
495
|
+
.to_a.first.first
|
496
|
+
}.by 1
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
context 'with allowed_shards configured' do
|
501
|
+
before do
|
502
|
+
CustomConnection.allow_shard :postgresql_shard
|
503
|
+
end
|
504
|
+
|
505
|
+
it 'with plain call should use custom connection' do
|
506
|
+
expect(CustomConnection.connection.current_database).to eq('octopus_shard_2')
|
507
|
+
end
|
508
|
+
|
509
|
+
it 'with using called on relation with allowed shard should use' do
|
510
|
+
expect(CustomConnection.using(:postgresql_shard).connection.current_database).to eq('octopus_shard_1')
|
511
|
+
end
|
512
|
+
|
513
|
+
it 'within Octopus.using block with allowed shard should use' do
|
514
|
+
Octopus.using(:postgresql_shard) do
|
515
|
+
expect(CustomConnection.connection.current_database).to eq('octopus_shard_1')
|
516
|
+
end
|
517
|
+
end
|
518
|
+
|
519
|
+
it 'with using called on relation with disallowed shard should not use' do
|
520
|
+
expect(CustomConnection.using(:brazil).connection.current_database).to eq('octopus_shard_2')
|
521
|
+
end
|
522
|
+
|
523
|
+
it 'within Octopus.using block with disallowed shard should not use' do
|
524
|
+
Octopus.using(:brazil) do
|
525
|
+
expect(CustomConnection.connection.current_database).to eq('octopus_shard_2')
|
526
|
+
end
|
527
|
+
end
|
528
|
+
|
529
|
+
it 'should save to correct shard' do
|
530
|
+
expect { CustomConnection.create(:value => 'custom value') }.to change {
|
531
|
+
CustomConnection
|
532
|
+
.connection
|
533
|
+
.execute("select count(*) as ct from custom where value = 'custom value'")
|
534
|
+
.to_a.first.first
|
535
|
+
}.by 1
|
536
|
+
end
|
537
|
+
|
538
|
+
it 'should clean up correctly' do
|
539
|
+
User.create!(:name => 'CleanUser')
|
540
|
+
CustomConnection.using(:postgresql_shard).first
|
541
|
+
expect(User.first).not_to be_nil
|
542
|
+
end
|
543
|
+
|
544
|
+
it 'should clean up correctly even inside block' do
|
545
|
+
User.create!(:name => 'CleanUser')
|
546
|
+
|
547
|
+
Octopus.using(:master) do
|
548
|
+
CustomConnection.using(:postgresql_shard).connection.execute('select count(*) from users')
|
549
|
+
expect(User.first).not_to be_nil
|
550
|
+
end
|
551
|
+
end
|
552
|
+
end
|
553
|
+
|
554
|
+
describe 'clear_active_connections!' do
|
555
|
+
it 'should not leak connection' do
|
556
|
+
CustomConnection.create(:value => 'custom value')
|
557
|
+
|
558
|
+
# This is what Rails, Sidekiq etc call--this normally handles all connection pools in the app
|
559
|
+
expect { ActiveRecord::Base.clear_active_connections! }
|
560
|
+
.to change { CustomConnection.connection_pool.active_connection? }
|
561
|
+
|
562
|
+
expect(CustomConnection.connection_pool.active_connection?).to be_falsey
|
563
|
+
end
|
564
|
+
end
|
565
|
+
end
|
566
|
+
|
474
567
|
describe 'when using set_table_name' do
|
475
568
|
it 'should work correctly' do
|
476
569
|
Bacon.using(:brazil).create!(:name => 'YUMMMYYYY')
|
@@ -12,6 +12,13 @@ describe Octopus::RelationProxy do
|
|
12
12
|
expect(@relation.current_shard).to eq(:canada)
|
13
13
|
end
|
14
14
|
|
15
|
+
unless Octopus.rails3?
|
16
|
+
it 'can define collection association with the same name as ancestor private method' do
|
17
|
+
@client.comments << Comment.using(:canada).create!(open: true)
|
18
|
+
expect(@client.comments.open).to be_a_kind_of(ActiveRecord::Relation)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
15
22
|
context 'when comparing to other Relation objects' do
|
16
23
|
before :each do
|
17
24
|
@relation.reset
|
@@ -27,23 +27,33 @@ describe 'when the database is replicated' do
|
|
27
27
|
end
|
28
28
|
|
29
29
|
describe 'When enabling the query cache' do
|
30
|
-
include_context 'with query cache enabled'
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
30
|
+
include_context 'with query cache enabled' do
|
31
|
+
it 'should do the queries with cache' do
|
32
|
+
OctopusHelper.using_environment :replicated_with_one_slave do
|
33
|
+
cat1 = Cat.using(:master).create!(:name => 'Master Cat 1')
|
34
|
+
_ct2 = Cat.using(:master).create!(:name => 'Master Cat 2')
|
35
|
+
expect(Cat.using(:master).find(cat1.id)).to eq(cat1)
|
36
|
+
expect(Cat.using(:master).find(cat1.id)).to eq(cat1)
|
37
|
+
expect(Cat.using(:master).find(cat1.id)).to eq(cat1)
|
38
|
+
|
39
|
+
cat3 = Cat.using(:slave1).create!(:name => 'Slave Cat 3')
|
40
|
+
_ct4 = Cat.using(:slave1).create!(:name => 'Slave Cat 4')
|
41
|
+
expect(Cat.find(cat3.id).id).to eq(cat3.id)
|
42
|
+
expect(Cat.find(cat3.id).id).to eq(cat3.id)
|
43
|
+
expect(Cat.find(cat3.id).id).to eq(cat3.id)
|
44
|
+
|
45
|
+
expect(counter.query_count).to eq(14)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe 'When enabling the query cache with slave unavailable' do
|
52
|
+
it "should not raise can't connect error" do
|
53
|
+
OctopusHelper.using_environment :replicated_with_one_slave_unavailable do
|
54
|
+
expect {
|
55
|
+
ActiveRecord::Base.connection.enable_query_cache!
|
56
|
+
}.to_not raise_error
|
47
57
|
end
|
48
58
|
end
|
49
59
|
end
|
@@ -27,6 +27,7 @@ end
|
|
27
27
|
|
28
28
|
# This class sets its own connection
|
29
29
|
class CustomConnection < ActiveRecord::Base
|
30
|
+
self.table_name = 'custom'
|
30
31
|
octopus_establish_connection(:adapter => 'mysql2', :database => 'octopus_shard_2', :username => 'root', :password => '')
|
31
32
|
end
|
32
33
|
|
@@ -75,6 +76,7 @@ end
|
|
75
76
|
|
76
77
|
class Comment < ActiveRecord::Base
|
77
78
|
belongs_to :commentable, :polymorphic => true
|
79
|
+
scope :open, -> { where(open: true) }
|
78
80
|
end
|
79
81
|
|
80
82
|
class Bacon < ActiveRecord::Base
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ar-octopus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thiago Pradi
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2015-03-12 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|
@@ -178,6 +178,7 @@ files:
|
|
178
178
|
- gemfiles/rails32.gemfile
|
179
179
|
- gemfiles/rails4.gemfile
|
180
180
|
- gemfiles/rails41.gemfile
|
181
|
+
- gemfiles/rails42.gemfile
|
181
182
|
- init.rb
|
182
183
|
- lib/ar-octopus.rb
|
183
184
|
- lib/octopus.rb
|
@@ -341,7 +342,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
341
342
|
version: '0'
|
342
343
|
requirements: []
|
343
344
|
rubyforge_project:
|
344
|
-
rubygems_version: 2.
|
345
|
+
rubygems_version: 2.4.4
|
345
346
|
signing_key:
|
346
347
|
specification_version: 4
|
347
348
|
summary: Easy Database Sharding for ActiveRecord
|