ar-octopus 0.8.4 → 0.8.5
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 +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
|