ar-octopus 0.8.2 → 0.8.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +9 -9
  2. data/.rspec +1 -1
  3. data/.rubocop.yml +46 -0
  4. data/.rubocop_todo.yml +52 -0
  5. data/.ruby-version +1 -1
  6. data/.travis.yml +3 -9
  7. data/Appraisals +4 -0
  8. data/Rakefile +17 -16
  9. data/ar-octopus.gemspec +22 -16
  10. data/gemfiles/rails41.gemfile +7 -0
  11. data/init.rb +1 -1
  12. data/lib/ar-octopus.rb +1 -1
  13. data/lib/octopus.rb +38 -37
  14. data/lib/octopus/abstract_adapter.rb +0 -2
  15. data/lib/octopus/association.rb +8 -6
  16. data/lib/octopus/association_shard_tracking.rb +80 -81
  17. data/lib/octopus/collection_association.rb +7 -5
  18. data/lib/octopus/collection_proxy.rb +11 -9
  19. data/lib/octopus/has_and_belongs_to_many_association.rb +5 -3
  20. data/lib/octopus/load_balancing.rb +3 -2
  21. data/lib/octopus/load_balancing/round_robin.rb +12 -8
  22. data/lib/octopus/migration.rb +117 -108
  23. data/lib/octopus/model.rb +130 -134
  24. data/lib/octopus/persistence.rb +1 -1
  25. data/lib/octopus/proxy.rb +345 -339
  26. data/lib/octopus/railtie.rb +2 -2
  27. data/lib/octopus/relation_proxy.rb +6 -1
  28. data/lib/octopus/scope_proxy.rb +38 -36
  29. data/lib/octopus/shard_tracking.rb +36 -35
  30. data/lib/octopus/shard_tracking/attribute.rb +12 -14
  31. data/lib/octopus/shard_tracking/dynamic.rb +7 -3
  32. data/lib/octopus/singular_association.rb +5 -3
  33. data/lib/octopus/slave_group.rb +10 -8
  34. data/lib/octopus/version.rb +1 -1
  35. data/rails/init.rb +1 -1
  36. data/sample_app/autotest/discover.rb +2 -2
  37. data/sample_app/config/application.rb +1 -1
  38. data/sample_app/config/boot.rb +1 -1
  39. data/sample_app/config/environments/test.rb +1 -1
  40. data/sample_app/config/initializers/session_store.rb +1 -1
  41. data/sample_app/config/initializers/wrap_parameters.rb +1 -1
  42. data/sample_app/config/routes.rb +1 -1
  43. data/sample_app/db/migrate/20100720210335_create_sample_users.rb +2 -2
  44. data/sample_app/db/schema.rb +10 -10
  45. data/sample_app/db/seeds.rb +3 -3
  46. data/sample_app/features/step_definitions/seeds_steps.rb +4 -4
  47. data/sample_app/features/step_definitions/web_steps.rb +3 -4
  48. data/sample_app/features/support/env.rb +3 -4
  49. data/sample_app/features/support/paths.rb +4 -4
  50. data/sample_app/spec/spec_helper.rb +3 -3
  51. data/spec/migrations/10_create_users_using_replication.rb +3 -3
  52. data/spec/migrations/11_add_field_in_all_slaves.rb +3 -3
  53. data/spec/migrations/12_create_users_using_block.rb +7 -7
  54. data/spec/migrations/13_create_users_using_block_and_using.rb +4 -4
  55. data/spec/migrations/14_create_users_on_shards_of_a_group_with_versions.rb +2 -2
  56. data/spec/migrations/15_create_user_on_shards_of_default_group_with_versions.rb +2 -2
  57. data/spec/migrations/1_create_users_on_master.rb +3 -3
  58. data/spec/migrations/2_create_users_on_canada.rb +3 -3
  59. data/spec/migrations/3_create_users_on_both_shards.rb +3 -3
  60. data/spec/migrations/4_create_users_on_shards_of_a_group.rb +3 -3
  61. data/spec/migrations/5_create_users_on_multiples_groups.rb +2 -2
  62. data/spec/migrations/6_raise_exception_with_invalid_shard_name.rb +3 -3
  63. data/spec/migrations/7_raise_exception_with_invalid_multiple_shard_names.rb +3 -3
  64. data/spec/migrations/8_raise_exception_with_invalid_group_name.rb +3 -3
  65. data/spec/migrations/9_raise_exception_with_multiple_invalid_group_names.rb +4 -4
  66. data/spec/octopus/association_shard_tracking_spec.rb +413 -417
  67. data/spec/octopus/collection_proxy_spec.rb +6 -5
  68. data/spec/octopus/log_subscriber_spec.rb +4 -4
  69. data/spec/octopus/migration_spec.rb +48 -48
  70. data/spec/octopus/model_spec.rb +267 -292
  71. data/spec/octopus/octopus_spec.rb +40 -41
  72. data/spec/octopus/proxy_spec.rb +124 -124
  73. data/spec/octopus/relation_proxy_spec.rb +32 -32
  74. data/spec/octopus/replicated_slave_grouped_spec.rb +23 -23
  75. data/spec/octopus/replication_spec.rb +61 -66
  76. data/spec/octopus/scope_proxy_spec.rb +56 -10
  77. data/spec/octopus/sharded_replicated_slave_grouped_spec.rb +29 -29
  78. data/spec/octopus/sharded_spec.rb +10 -10
  79. data/spec/spec_helper.rb +6 -6
  80. data/spec/support/active_record/connection_adapters/modify_config_adapter.rb +1 -3
  81. data/spec/support/database_connection.rb +2 -2
  82. data/spec/support/database_models.rb +16 -17
  83. data/spec/support/octopus_helper.rb +19 -21
  84. data/spec/support/query_count.rb +1 -3
  85. data/spec/support/shared_contexts.rb +3 -3
  86. data/spec/tasks/octopus.rake_spec.rb +10 -10
  87. metadata +43 -26
data/lib/octopus/model.rb CHANGED
@@ -1,183 +1,179 @@
1
1
  require 'active_support/deprecation'
2
2
 
3
- module Octopus::Model
4
- def self.extended(base)
5
- base.send(:include, Octopus::ShardTracking::Attribute)
6
- base.send(:include, InstanceMethods)
7
- base.extend(ClassMethods)
8
- end
3
+ module Octopus
4
+ module Model
5
+ def self.extended(base)
6
+ base.send(:include, Octopus::ShardTracking::Attribute)
7
+ base.send(:include, InstanceMethods)
8
+ base.extend(ClassMethods)
9
+ end
9
10
 
10
- module SharedMethods
11
- def clean_table_name
12
- return unless self.connection_proxy.should_clean_table_name?
11
+ module SharedMethods
12
+ def clean_table_name
13
+ return unless connection_proxy.should_clean_table_name?
13
14
 
14
- if self != ActiveRecord::Base && self.respond_to?(:reset_table_name) && !self.custom_octopus_table_name
15
- self.reset_table_name()
16
- end
15
+ if self != ActiveRecord::Base && self.respond_to?(:reset_table_name) && !custom_octopus_table_name
16
+ reset_table_name
17
+ end
17
18
 
18
- self.reset_column_information
19
- self.instance_variable_set(:@quoted_table_name, nil)
20
- end
19
+ reset_column_information
20
+ instance_variable_set(:@quoted_table_name, nil)
21
+ end
21
22
 
22
- def using(shard)
23
- if Octopus.enabled?
24
- clean_table_name
25
- Octopus::ScopeProxy.new(shard, self)
26
- else
27
- self
23
+ def using(shard)
24
+ if Octopus.enabled?
25
+ clean_table_name
26
+ Octopus::ScopeProxy.new(shard, self)
27
+ else
28
+ self
29
+ end
28
30
  end
29
31
  end
30
- end
31
32
 
32
- module InstanceMethods
33
- include SharedMethods
33
+ module InstanceMethods
34
+ include SharedMethods
34
35
 
35
- def self.included(base)
36
- base.send(:alias_method, :equality_without_octopus, :==)
37
- base.send(:alias_method, :==, :equality_with_octopus)
38
- base.send(:alias_method, :eql?, :==)
39
- base.send(:alias_method_chain, :perform_validations, :octopus)
40
- end
36
+ def self.included(base)
37
+ base.send(:alias_method, :equality_without_octopus, :==)
38
+ base.send(:alias_method, :==, :equality_with_octopus)
39
+ base.send(:alias_method, :eql?, :==)
40
+ base.send(:alias_method_chain, :perform_validations, :octopus)
41
+ end
41
42
 
42
- def set_current_shard
43
- return unless Octopus.enabled?
43
+ def set_current_shard
44
+ return unless Octopus.enabled?
44
45
 
45
- if new_record? || self.class.connection_proxy.block
46
- self.current_shard = self.class.connection_proxy.current_shard
47
- else
48
- self.current_shard = self.class.connection_proxy.last_current_shard || self.class.connection_proxy.current_shard
46
+ if new_record? || self.class.connection_proxy.block
47
+ self.current_shard = self.class.connection_proxy.current_shard
48
+ else
49
+ self.current_shard = self.class.connection_proxy.last_current_shard || self.class.connection_proxy.current_shard
50
+ end
49
51
  end
50
- end
51
52
 
52
- def should_set_current_shard?
53
- self.respond_to?(:current_shard) && !self.current_shard.nil?
54
- end
53
+ def should_set_current_shard?
54
+ self.respond_to?(:current_shard) && !current_shard.nil?
55
+ end
55
56
 
56
- def equality_with_octopus(comparison_object)
57
- equality_without_octopus(comparison_object) && comparison_object.current_shard == current_shard
58
- end
57
+ def equality_with_octopus(comparison_object)
58
+ equality_without_octopus(comparison_object) && comparison_object.current_shard == current_shard
59
+ end
59
60
 
60
- def perform_validations_with_octopus(*args)
61
- if Octopus.enabled? and should_set_current_shard?
62
- Octopus.using(self.current_shard) do
61
+ def perform_validations_with_octopus(*args)
62
+ if Octopus.enabled? && should_set_current_shard?
63
+ Octopus.using(current_shard) do
64
+ perform_validations_without_octopus(*args)
65
+ end
66
+ else
63
67
  perform_validations_without_octopus(*args)
64
68
  end
65
- else
66
- perform_validations_without_octopus(*args)
67
69
  end
68
70
  end
69
- end
70
71
 
71
- module ClassMethods
72
- include SharedMethods
72
+ module ClassMethods
73
+ include SharedMethods
73
74
 
74
- def self.extended(base)
75
- base.class_attribute(:replicated)
76
- base.class_attribute(:sharded)
77
- base.hijack_methods
78
- end
75
+ def self.extended(base)
76
+ base.class_attribute(:replicated)
77
+ base.class_attribute(:sharded)
78
+ base.hijack_methods
79
+ end
79
80
 
80
- def replicated_model
81
- self.replicated = true
82
- end
81
+ def replicated_model
82
+ self.replicated = true
83
+ end
83
84
 
84
- def sharded_model
85
- self.sharded = true
86
- end
85
+ def sharded_model
86
+ self.sharded = true
87
+ end
87
88
 
88
- def hijack_methods
89
- around_save :run_on_shard
90
- after_initialize :set_current_shard
89
+ def hijack_methods
90
+ around_save :run_on_shard
91
+ after_initialize :set_current_shard
91
92
 
92
- class << self
93
- attr_accessor :custom_octopus_connection
94
- attr_accessor :custom_octopus_table_name
93
+ class << self
94
+ attr_accessor :custom_octopus_connection
95
+ attr_accessor :custom_octopus_table_name
95
96
 
96
- alias_method_chain :connection, :octopus
97
- alias_method_chain :connection_pool, :octopus
98
- alias_method_chain :clear_all_connections!, :octopus
99
- alias_method_chain :clear_active_connections!, :octopus
100
- alias_method_chain :connected?, :octopus
97
+ alias_method_chain :connection, :octopus
98
+ alias_method_chain :connection_pool, :octopus
99
+ alias_method_chain :clear_all_connections!, :octopus
100
+ alias_method_chain :clear_active_connections!, :octopus
101
+ alias_method_chain :connected?, :octopus
101
102
 
102
- if Octopus.rails3?
103
- alias_method_chain(:set_table_name, :octopus)
104
- end
103
+ alias_method_chain(:set_table_name, :octopus) if Octopus.rails3?
105
104
 
106
- def table_name=(value = nil)
107
- self.custom_octopus_table_name = true
108
- super
105
+ def table_name=(value = nil)
106
+ self.custom_octopus_table_name = true
107
+ super
108
+ end
109
109
  end
110
110
  end
111
- end
112
111
 
113
- def connection_proxy
114
- cached = ActiveRecord::Base.class_variable_get :@@connection_proxy rescue nil
115
- cached ||
116
- begin
117
- p = Octopus::Proxy.new
118
- ActiveRecord::Base.class_variable_set :@@connection_proxy, p
119
- p
120
- end
121
- end
112
+ def connection_proxy
113
+ ActiveRecord::Base.class_variable_defined?(:@@connection_proxy) &&
114
+ ActiveRecord::Base.class_variable_get(:@@connection_proxy) ||
115
+ ActiveRecord::Base.class_variable_set(:@@connection_proxy, Octopus::Proxy.new)
116
+ end
122
117
 
123
- def should_use_normal_connection?
124
- !Octopus.enabled? || custom_octopus_connection
125
- end
118
+ def should_use_normal_connection?
119
+ !Octopus.enabled? || custom_octopus_connection
120
+ end
126
121
 
127
- def connection_with_octopus
128
- if should_use_normal_connection?
129
- connection_without_octopus
130
- else
131
- connection_proxy.current_model = self
132
- connection_proxy
122
+ def connection_with_octopus
123
+ if should_use_normal_connection?
124
+ connection_without_octopus
125
+ else
126
+ connection_proxy.current_model = self
127
+ connection_proxy
128
+ end
133
129
  end
134
- end
135
130
 
136
- def connection_pool_with_octopus
137
- if should_use_normal_connection?
138
- connection_pool_without_octopus
139
- else
140
- connection_proxy.connection_pool
131
+ def connection_pool_with_octopus
132
+ if should_use_normal_connection?
133
+ connection_pool_without_octopus
134
+ else
135
+ connection_proxy.connection_pool
136
+ end
141
137
  end
142
- end
143
138
 
144
- def clear_active_connections_with_octopus!
145
- if should_use_normal_connection?
146
- clear_active_connections_without_octopus!
147
- else
148
- connection_proxy.clear_active_connections!
139
+ def clear_active_connections_with_octopus!
140
+ if should_use_normal_connection?
141
+ clear_active_connections_without_octopus!
142
+ else
143
+ connection_proxy.clear_active_connections!
144
+ end
149
145
  end
150
- end
151
146
 
152
- def clear_all_connections_with_octopus!
153
- if should_use_normal_connection?
154
- clear_all_connections_without_octopus!
155
- else
156
- connection_proxy.clear_all_connections!
147
+ def clear_all_connections_with_octopus!
148
+ if should_use_normal_connection?
149
+ clear_all_connections_without_octopus!
150
+ else
151
+ connection_proxy.clear_all_connections!
152
+ end
157
153
  end
158
- end
159
154
 
160
- def connected_with_octopus?
161
- if should_use_normal_connection?
162
- connected_without_octopus?
163
- else
164
- connection_proxy.connected?
155
+ def connected_with_octopus?
156
+ if should_use_normal_connection?
157
+ connected_without_octopus?
158
+ else
159
+ connection_proxy.connected?
160
+ end
165
161
  end
166
- end
167
162
 
168
- def set_table_name_with_octopus(value = nil, &block)
169
- self.custom_octopus_table_name = true
170
- set_table_name_without_octopus(value, &block)
171
- end
163
+ def set_table_name_with_octopus(value = nil, &block)
164
+ self.custom_octopus_table_name = true
165
+ set_table_name_without_octopus(value, &block)
166
+ end
172
167
 
173
- def octopus_establish_connection(spec = ENV['DATABASE_URL'])
174
- self.custom_octopus_connection = true if spec
175
- establish_connection(spec)
176
- end
168
+ def octopus_establish_connection(spec = ENV['DATABASE_URL'])
169
+ self.custom_octopus_connection = true if spec
170
+ establish_connection(spec)
171
+ end
177
172
 
178
- def octopus_set_table_name(value = nil)
179
- ActiveSupport::Deprecation.warn "Calling `octopus_set_table_name` is deprecated and will be removed in Octopus 1.0.", caller
180
- set_table_name(value)
173
+ def octopus_set_table_name(value = nil)
174
+ ActiveSupport::Deprecation.warn 'Calling `octopus_set_table_name` is deprecated and will be removed in Octopus 1.0.', caller
175
+ set_table_name(value)
176
+ end
181
177
  end
182
178
  end
183
179
  end
@@ -25,7 +25,7 @@ module Octopus
25
25
  run_on_shard { super }
26
26
  end
27
27
 
28
- def touch(name=nil)
28
+ def touch(name = nil)
29
29
  run_on_shard { super }
30
30
  end
31
31
 
data/lib/octopus/proxy.rb CHANGED
@@ -1,437 +1,443 @@
1
- require "set"
1
+ require 'set'
2
2
  require 'octopus/slave_group'
3
3
  require 'octopus/load_balancing/round_robin'
4
4
 
5
- class Octopus::Proxy
6
- attr_accessor :config, :sharded
5
+ module Octopus
6
+ class Proxy
7
+ attr_accessor :config, :sharded
7
8
 
8
- def initialize(config = Octopus.config)
9
- initialize_shards(config)
10
- initialize_replication(config) if !config.nil? && config["replicated"]
11
- end
9
+ def initialize(config = Octopus.config)
10
+ initialize_shards(config)
11
+ initialize_replication(config) if !config.nil? && config['replicated']
12
+ end
12
13
 
13
- def initialize_shards(config)
14
- @shards = HashWithIndifferentAccess.new
15
- @shards_slave_groups = HashWithIndifferentAccess.new
16
- @slave_groups = HashWithIndifferentAccess.new
17
- @groups = {}
18
- @adapters = Set.new
19
- @config = ActiveRecord::Base.connection_pool_without_octopus.connection.instance_variable_get(:@config)
20
-
21
- if !config.nil?
22
- @entire_sharded = config['entire_sharded']
23
- @shards_config = config[Octopus.rails_env()]
24
- end
25
-
26
- @shards_config ||= []
27
-
28
- @shards_config.each do |key, value|
29
- if value.is_a?(String)
30
- value = resolve_string_connection(value).merge(:octopus_shard => key)
31
- initialize_adapter(value['adapter'])
32
- @shards[key.to_sym] = connection_pool_for(value, "#{value['adapter']}_connection")
33
- elsif value.is_a?(Hash) && value.has_key?("adapter")
34
- value.merge!(:octopus_shard => key)
35
- initialize_adapter(value['adapter'])
36
- @shards[key.to_sym] = connection_pool_for(value, "#{value['adapter']}_connection")
37
-
38
- slave_group_configs = value.select do |k,v|
39
- structurally_slave_group? v
40
- end
14
+ def initialize_shards(config)
15
+ @shards = HashWithIndifferentAccess.new
16
+ @shards_slave_groups = HashWithIndifferentAccess.new
17
+ @slave_groups = HashWithIndifferentAccess.new
18
+ @groups = {}
19
+ @adapters = Set.new
20
+ @config = ActiveRecord::Base.connection_pool_without_octopus.connection.instance_variable_get(:@config)
21
+
22
+ unless config.nil?
23
+ @entire_sharded = config['entire_sharded']
24
+ @shards_config = config[Octopus.rails_env]
25
+ end
26
+
27
+ @shards_config ||= []
41
28
 
42
- if slave_group_configs.present?
43
- slave_groups = HashWithIndifferentAccess.new
44
- slave_group_configs.each do |slave_group_name, slave_configs|
45
- slaves = HashWithIndifferentAccess.new
46
- slave_configs.each do |slave_name, slave_config|
47
- @shards[slave_name.to_sym] = connection_pool_for(slave_config, "#{value['adapter']}_connection")
48
- slaves[slave_name.to_sym] = slave_name.to_sym
29
+ @shards_config.each do |key, value|
30
+ if value.is_a?(String)
31
+ value = resolve_string_connection(value).merge(:octopus_shard => key)
32
+ initialize_adapter(value['adapter'])
33
+ @shards[key.to_sym] = connection_pool_for(value, "#{value['adapter']}_connection")
34
+ elsif value.is_a?(Hash) && value.key?('adapter')
35
+ value.merge!(:octopus_shard => key)
36
+ initialize_adapter(value['adapter'])
37
+ @shards[key.to_sym] = connection_pool_for(value, "#{value['adapter']}_connection")
38
+
39
+ slave_group_configs = value.select do |_k, v|
40
+ structurally_slave_group? v
41
+ end
42
+
43
+ if slave_group_configs.present?
44
+ slave_groups = HashWithIndifferentAccess.new
45
+ slave_group_configs.each do |slave_group_name, slave_configs|
46
+ slaves = HashWithIndifferentAccess.new
47
+ slave_configs.each do |slave_name, slave_config|
48
+ @shards[slave_name.to_sym] = connection_pool_for(slave_config, "#{value['adapter']}_connection")
49
+ slaves[slave_name.to_sym] = slave_name.to_sym
50
+ end
51
+ slave_groups[slave_group_name.to_sym] = Octopus::SlaveGroup.new(slaves)
49
52
  end
50
- slave_groups[slave_group_name.to_sym] = Octopus::SlaveGroup.new(slaves)
53
+ @shards_slave_groups[key.to_sym] = slave_groups
54
+ @sharded = true
51
55
  end
52
- @shards_slave_groups[key.to_sym] = slave_groups
53
- @sharded = true
54
- end
55
- elsif value.is_a?(Hash)
56
- @groups[key.to_s] = []
56
+ elsif value.is_a?(Hash)
57
+ @groups[key.to_s] = []
57
58
 
58
- value.each do |k, v|
59
- raise "You have duplicated shard names!" if @shards.has_key?(k.to_sym)
59
+ value.each do |k, v|
60
+ fail 'You have duplicated shard names!' if @shards.key?(k.to_sym)
60
61
 
61
- initialize_adapter(v['adapter'])
62
- config_with_octopus_shard = v.merge(:octopus_shard => k)
62
+ initialize_adapter(v['adapter'])
63
+ config_with_octopus_shard = v.merge(:octopus_shard => k)
63
64
 
64
- @shards[k.to_sym] = connection_pool_for(config_with_octopus_shard, "#{v['adapter']}_connection")
65
- @groups[key.to_s] << k.to_sym
66
- end
65
+ @shards[k.to_sym] = connection_pool_for(config_with_octopus_shard, "#{v['adapter']}_connection")
66
+ @groups[key.to_s] << k.to_sym
67
+ end
67
68
 
68
- if structurally_slave_group? value
69
- slaves = Hash[@groups[key.to_s].map { |v| [v, v ] }]
70
- @slave_groups[key.to_sym] = Octopus::SlaveGroup.new(slaves)
69
+ if structurally_slave_group? value
70
+ slaves = Hash[@groups[key.to_s].map { |v| [v, v] }]
71
+ @slave_groups[key.to_sym] = Octopus::SlaveGroup.new(slaves)
72
+ end
71
73
  end
72
74
  end
75
+
76
+ @shards[:master] ||= ActiveRecord::Base.connection_pool_without_octopus
73
77
  end
74
78
 
75
- @shards[:master] ||= ActiveRecord::Base.connection_pool_without_octopus()
76
- end
79
+ def initialize_replication(config)
80
+ @replicated = true
81
+ if config.key?('fully_replicated')
82
+ @fully_replicated = config['fully_replicated']
83
+ else
84
+ @fully_replicated = true
85
+ end
77
86
 
78
- def initialize_replication(config)
79
- @replicated = true
80
- if config.has_key?("fully_replicated")
81
- @fully_replicated = config["fully_replicated"]
82
- else
83
- @fully_replicated = true
87
+ @slaves_list = @shards.keys.map { |sym| sym.to_s }.sort
88
+ @slaves_list.delete('master')
89
+ @slaves_load_balancer = Octopus::LoadBalancing::RoundRobin.new(@slaves_list)
84
90
  end
85
91
 
86
- @slaves_list = @shards.keys.map {|sym| sym.to_s}.sort
87
- @slaves_list.delete('master')
88
- @slaves_load_balancer = Octopus::LoadBalancing::RoundRobin.new(@slaves_list)
89
- end
90
-
91
- def current_model
92
- Thread.current["octopus.current_model"]
93
- end
92
+ def current_model
93
+ Thread.current['octopus.current_model']
94
+ end
94
95
 
95
- def current_model=(model)
96
- Thread.current["octopus.current_model"] = model.is_a?(ActiveRecord::Base) ? model.class : model
97
- end
96
+ def current_model=(model)
97
+ Thread.current['octopus.current_model'] = model.is_a?(ActiveRecord::Base) ? model.class : model
98
+ end
98
99
 
99
- def current_shard
100
- Thread.current["octopus.current_shard"] ||= :master
101
- end
100
+ def current_shard
101
+ Thread.current['octopus.current_shard'] ||= :master
102
+ end
102
103
 
103
- def current_shard=(shard_symbol)
104
- self.current_slave_group = nil
105
- if shard_symbol.is_a?(Array)
106
- shard_symbol.each {|symbol| raise "Nonexistent Shard Name: #{symbol}" if @shards[symbol].nil? }
107
- elsif shard_symbol.is_a?(Hash)
108
- hash = shard_symbol
109
- shard_symbol = hash[:shard]
110
- slave_group_symbol = hash[:slave_group]
111
-
112
- if shard_symbol.nil? && slave_group_symbol.nil?
113
- raise "Neither shard or slave group must be specified"
114
- end
104
+ def current_shard=(shard_symbol)
105
+ self.current_slave_group = nil
106
+ if shard_symbol.is_a?(Array)
107
+ shard_symbol.each { |symbol| fail "Nonexistent Shard Name: #{symbol}" if @shards[symbol].nil? }
108
+ elsif shard_symbol.is_a?(Hash)
109
+ hash = shard_symbol
110
+ shard_symbol = hash[:shard]
111
+ slave_group_symbol = hash[:slave_group]
112
+
113
+ if shard_symbol.nil? && slave_group_symbol.nil?
114
+ fail 'Neither shard or slave group must be specified'
115
+ end
115
116
 
116
- if shard_symbol.present?
117
- raise "Nonexistent Shard Name: #{shard_symbol}" if @shards[shard_symbol].nil?
118
- end
117
+ if shard_symbol.present?
118
+ fail "Nonexistent Shard Name: #{shard_symbol}" if @shards[shard_symbol].nil?
119
+ end
119
120
 
120
- if slave_group_symbol.present?
121
- if (@shards_slave_groups.try(:[], shard_symbol).present? && @shards_slave_groups[shard_symbol][slave_group_symbol].nil?) ||
122
- (@shards_slave_groups.try(:[], shard_symbol).nil? && @slave_groups[slave_group_symbol].nil?)
123
- raise "Nonexistent Slave Group Name: #{slave_group_symbol} in shards config: #{@shards_config.inspect}"
121
+ if slave_group_symbol.present?
122
+ if (@shards_slave_groups.try(:[], shard_symbol).present? && @shards_slave_groups[shard_symbol][slave_group_symbol].nil?) ||
123
+ (@shards_slave_groups.try(:[], shard_symbol).nil? && @slave_groups[slave_group_symbol].nil?)
124
+ fail "Nonexistent Slave Group Name: #{slave_group_symbol} in shards config: #{@shards_config.inspect}"
125
+ end
126
+ self.current_slave_group = slave_group_symbol
124
127
  end
125
- self.current_slave_group = slave_group_symbol
128
+ else
129
+ fail "Nonexistent Shard Name: #{shard_symbol}" if @shards[shard_symbol].nil?
126
130
  end
127
- else
128
- raise "Nonexistent Shard Name: #{shard_symbol}" if @shards[shard_symbol].nil?
131
+
132
+ Thread.current['octopus.current_shard'] = shard_symbol
129
133
  end
130
134
 
131
- Thread.current["octopus.current_shard"] = shard_symbol
132
- end
135
+ def current_group
136
+ Thread.current['octopus.current_group']
137
+ end
133
138
 
134
- def current_group
135
- Thread.current["octopus.current_group"]
136
- end
139
+ def current_group=(group_symbol)
140
+ # TODO: Error message should include all groups if given more than one bad name.
141
+ [group_symbol].flatten.compact.each do |group|
142
+ fail "Nonexistent Group Name: #{group}" unless has_group?(group)
143
+ end
137
144
 
138
- def current_group=(group_symbol)
139
- # TODO: Error message should include all groups if given more than one bad name.
140
- [group_symbol].flatten.compact.each do |group|
141
- raise "Nonexistent Group Name: #{group}" unless has_group?(group)
145
+ Thread.current['octopus.current_group'] = group_symbol
142
146
  end
143
147
 
144
- Thread.current["octopus.current_group"] = group_symbol
145
- end
146
-
147
- def current_slave_group
148
- Thread.current["octopus.current_slave_group"]
149
- end
148
+ def current_slave_group
149
+ Thread.current['octopus.current_slave_group']
150
+ end
150
151
 
151
- def current_slave_group=(slave_group_symbol)
152
- Thread.current["octopus.current_slave_group"] = slave_group_symbol
153
- end
152
+ def current_slave_group=(slave_group_symbol)
153
+ Thread.current['octopus.current_slave_group'] = slave_group_symbol
154
+ end
154
155
 
155
- def block
156
- Thread.current["octopus.block"]
157
- end
156
+ def block
157
+ Thread.current['octopus.block']
158
+ end
158
159
 
159
- def block=(block)
160
- Thread.current["octopus.block"] = block
161
- end
160
+ def block=(block)
161
+ Thread.current['octopus.block'] = block
162
+ end
162
163
 
163
- def last_current_shard
164
- Thread.current["octopus.last_current_shard"]
165
- end
164
+ def last_current_shard
165
+ Thread.current['octopus.last_current_shard']
166
+ end
166
167
 
167
- def last_current_shard=(last_current_shard)
168
- Thread.current["octopus.last_current_shard"] = last_current_shard
169
- end
168
+ def last_current_shard=(last_current_shard)
169
+ Thread.current['octopus.last_current_shard'] = last_current_shard
170
+ end
170
171
 
171
- def fully_replicated?
172
- @fully_replicated || Thread.current["octopus.fully_replicated"]
173
- end
172
+ def fully_replicated?
173
+ @fully_replicated || Thread.current['octopus.fully_replicated']
174
+ end
174
175
 
175
- # Public: Whether or not a group exists with the given name converted to a
176
- # string.
177
- #
178
- # Returns a boolean.
179
- def has_group?(group)
180
- @groups.has_key?(group.to_s)
181
- end
176
+ # Public: Whether or not a group exists with the given name converted to a
177
+ # string.
178
+ #
179
+ # Returns a boolean.
180
+ def has_group?(group)
181
+ @groups.key?(group.to_s)
182
+ end
182
183
 
183
- # Public: Retrieves names of all loaded shards.
184
- #
185
- # Returns an array of shard names as symbols
186
- def shard_names
187
- @shards.keys
188
- end
184
+ # Public: Retrieves names of all loaded shards.
185
+ #
186
+ # Returns an array of shard names as symbols
187
+ def shard_names
188
+ @shards.keys
189
+ end
189
190
 
190
- # Public: Retrieves the defined shards for a given group.
191
- #
192
- # Returns an array of shard names as symbols or nil if the group is not
193
- # defined.
194
- def shards_for_group(group)
195
- @groups.fetch(group.to_s, nil)
196
- end
191
+ # Public: Retrieves the defined shards for a given group.
192
+ #
193
+ # Returns an array of shard names as symbols or nil if the group is not
194
+ # defined.
195
+ def shards_for_group(group)
196
+ @groups.fetch(group.to_s, nil)
197
+ end
197
198
 
198
- # Rails 3.1 sets automatic_reconnect to false when it removes
199
- # connection pool. Octopus can potentially retain a reference to a closed
200
- # connection pool. Previously, that would work since the pool would just
201
- # reconnect, but in Rails 3.1 the flag prevents this.
202
- def safe_connection(connection_pool)
203
- connection_pool.automatic_reconnect ||= true
204
- connection_pool.connection()
205
- end
199
+ # Rails 3.1 sets automatic_reconnect to false when it removes
200
+ # connection pool. Octopus can potentially retain a reference to a closed
201
+ # connection pool. Previously, that would work since the pool would just
202
+ # reconnect, but in Rails 3.1 the flag prevents this.
203
+ def safe_connection(connection_pool)
204
+ connection_pool.automatic_reconnect ||= true
205
+ connection_pool.connection
206
+ end
206
207
 
207
- def select_connection
208
- safe_connection(@shards[shard_name])
209
- end
208
+ def select_connection
209
+ safe_connection(@shards[shard_name])
210
+ end
210
211
 
211
- def shard_name
212
- current_shard.is_a?(Array) ? current_shard.first : current_shard
213
- end
212
+ def shard_name
213
+ current_shard.is_a?(Array) ? current_shard.first : current_shard
214
+ end
214
215
 
215
- def should_clean_table_name?
216
- @adapters.size > 1
217
- end
216
+ def should_clean_table_name?
217
+ @adapters.size > 1
218
+ end
218
219
 
219
- def run_queries_on_shard(shard, &block)
220
- keeping_connection_proxy do
221
- using_shard(shard) do
222
- yield
220
+ def run_queries_on_shard(shard, &_block)
221
+ keeping_connection_proxy do
222
+ using_shard(shard) do
223
+ yield
224
+ end
223
225
  end
224
226
  end
225
- end
226
227
 
227
- def send_queries_to_multiple_shards(shards, &block)
228
- shards.each do |shard|
229
- self.run_queries_on_shard(shard, &block)
228
+ def send_queries_to_multiple_shards(shards, &block)
229
+ shards.each do |shard|
230
+ run_queries_on_shard(shard, &block)
231
+ end
230
232
  end
231
- end
232
233
 
233
- def clean_connection_proxy()
234
- self.current_shard = :master
235
- self.current_group = nil
236
- self.block = false
237
- end
234
+ def clean_connection_proxy
235
+ self.current_shard = :master
236
+ self.current_group = nil
237
+ self.block = false
238
+ end
238
239
 
239
- def check_schema_migrations(shard)
240
- if !OctopusModel.using(shard).connection.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name())
241
- OctopusModel.using(shard).connection.initialize_schema_migrations_table
240
+ def check_schema_migrations(shard)
241
+ OctopusModel.using(shard).connection.table_exists?(
242
+ ActiveRecord::Migrator.schema_migrations_table_name,
243
+ ) || OctopusModel.using(shard).connection.initialize_schema_migrations_table
242
244
  end
243
- end
244
245
 
245
- def transaction(options = {}, &block)
246
- replicated = @replicated && (current_model.replicated || fully_replicated?)
247
- if !sharded && replicated
248
- self.run_queries_on_shard(:master) do
246
+ def transaction(options = {}, &block)
247
+ replicated = @replicated && (current_model.replicated || fully_replicated?)
248
+ if !sharded && replicated
249
+ run_queries_on_shard(:master) do
250
+ select_connection.transaction(options, &block)
251
+ end
252
+ else
249
253
  select_connection.transaction(options, &block)
250
254
  end
251
- else
252
- select_connection.transaction(options, &block)
253
255
  end
254
- end
255
256
 
256
- def method_missing(method, *args, &block)
257
- if should_clean_connection_proxy?(method)
258
- conn = select_connection()
259
- self.last_current_shard = self.current_shard
260
- clean_connection_proxy()
261
- conn.send(method, *args, &block)
262
- elsif should_send_queries_to_shard_slave_group?(method)
263
- send_queries_to_shard_slave_group(method, *args, &block)
264
- elsif should_send_queries_to_slave_group?(method)
265
- send_queries_to_slave_group(method, *args, &block)
266
- elsif should_send_queries_to_replicated_databases?(method)
267
- send_queries_to_selected_slave(method, *args, &block)
268
- else
269
- select_connection().send(method, *args, &block)
257
+ def method_missing(method, *args, &block)
258
+ if should_clean_connection_proxy?(method)
259
+ conn = select_connection
260
+ self.last_current_shard = current_shard
261
+ clean_connection_proxy
262
+ conn.send(method, *args, &block)
263
+ elsif should_send_queries_to_shard_slave_group?(method)
264
+ send_queries_to_shard_slave_group(method, *args, &block)
265
+ elsif should_send_queries_to_slave_group?(method)
266
+ send_queries_to_slave_group(method, *args, &block)
267
+ elsif should_send_queries_to_replicated_databases?(method)
268
+ send_queries_to_selected_slave(method, *args, &block)
269
+ else
270
+ select_connection.send(method, *args, &block)
271
+ end
270
272
  end
271
- end
272
273
 
273
- def respond_to?(method, include_private = false)
274
- super || select_connection.respond_to?(method, include_private)
275
- end
274
+ def respond_to?(method, include_private = false)
275
+ super || select_connection.respond_to?(method, include_private)
276
+ end
276
277
 
277
- def connection_pool
278
- return @shards[current_shard]
279
- end
278
+ def connection_pool
279
+ @shards[current_shard]
280
+ end
280
281
 
281
- def enable_query_cache!
282
- clear_query_cache
283
- @shards.each { |k, v| safe_connection(v).enable_query_cache! }
284
- end
282
+ def enable_query_cache!
283
+ clear_query_cache
284
+ @shards.each { |_k, v| safe_connection(v).enable_query_cache! }
285
+ end
285
286
 
286
- def disable_query_cache!
287
- @shards.each { |k, v| safe_connection(v).disable_query_cache! }
288
- end
287
+ def disable_query_cache!
288
+ @shards.each { |_k, v| safe_connection(v).disable_query_cache! }
289
+ end
289
290
 
290
- def clear_query_cache
291
- @shards.each { |k, v| safe_connection(v).clear_query_cache }
292
- end
291
+ def clear_query_cache
292
+ @shards.each { |_k, v| safe_connection(v).clear_query_cache }
293
+ end
293
294
 
294
- def clear_active_connections!
295
- @shards.each { |k, v| v.release_connection }
296
- end
295
+ def clear_active_connections!
296
+ @shards.each { |_k, v| v.release_connection }
297
+ end
297
298
 
298
- def clear_all_connections!
299
- @shards.each { |k, v| v.disconnect! }
300
- end
299
+ def clear_all_connections!
300
+ @shards.each { |_k, v| v.disconnect! }
301
+ end
301
302
 
302
- def connected?
303
- @shards.any? { |k, v| v.connected? }
304
- end
303
+ def connected?
304
+ @shards.any? { |_k, v| v.connected? }
305
+ end
305
306
 
306
- def should_send_queries_to_shard_slave_group?(method)
307
- should_use_slaves_for_method?(method) && @shards_slave_groups.try(:[], current_shard).try(:[], current_slave_group).present?
308
- end
307
+ def should_send_queries_to_shard_slave_group?(method)
308
+ should_use_slaves_for_method?(method) && @shards_slave_groups.try(:[], current_shard).try(:[], current_slave_group).present?
309
+ end
309
310
 
310
- def send_queries_to_shard_slave_group(method, *args, &block)
311
- send_queries_to_balancer(@shards_slave_groups[current_shard][current_slave_group], method, *args, &block)
312
- end
311
+ def send_queries_to_shard_slave_group(method, *args, &block)
312
+ send_queries_to_balancer(@shards_slave_groups[current_shard][current_slave_group], method, *args, &block)
313
+ end
313
314
 
314
- def should_send_queries_to_slave_group?(method)
315
- should_use_slaves_for_method?(method) && @slave_groups.try(:[], current_slave_group).present?
316
- end
315
+ def should_send_queries_to_slave_group?(method)
316
+ should_use_slaves_for_method?(method) && @slave_groups.try(:[], current_slave_group).present?
317
+ end
317
318
 
318
- def send_queries_to_slave_group(method, *args, &block)
319
- send_queries_to_balancer(@slave_groups[current_slave_group], method, *args, &block)
320
- end
319
+ def send_queries_to_slave_group(method, *args, &block)
320
+ send_queries_to_balancer(@slave_groups[current_slave_group], method, *args, &block)
321
+ end
321
322
 
322
- protected
323
+ protected
323
324
 
324
- def connection_pool_for(adapter, config)
325
- if Octopus.rails4?
326
- arg = ActiveRecord::ConnectionAdapters::ConnectionSpecification.new(adapter.dup, config)
327
- else
328
- arg = ActiveRecord::Base::ConnectionSpecification.new(adapter.dup, config)
325
+ def connection_pool_for(adapter, config)
326
+ if Octopus.rails4?
327
+ arg = ActiveRecord::ConnectionAdapters::ConnectionSpecification.new(adapter.dup, config)
328
+ else
329
+ arg = ActiveRecord::Base::ConnectionSpecification.new(adapter.dup, config)
330
+ end
331
+
332
+ ActiveRecord::ConnectionAdapters::ConnectionPool.new(arg)
329
333
  end
330
334
 
331
- ActiveRecord::ConnectionAdapters::ConnectionPool.new(arg)
332
- end
335
+ def initialize_adapter(adapter)
336
+ @adapters << adapter
337
+ begin
338
+ require "active_record/connection_adapters/#{adapter}_adapter"
339
+ rescue LoadError
340
+ raise "Please install the #{adapter} adapter: `gem install activerecord-#{adapter}-adapter` (#{$ERROR_INFO})"
341
+ end
342
+ end
333
343
 
334
- def initialize_adapter(adapter)
335
- @adapters << adapter
336
- begin
337
- require "active_record/connection_adapters/#{adapter}_adapter"
338
- rescue LoadError
339
- raise "Please install the #{adapter} adapter: `gem install activerecord-#{adapter}-adapter` (#{$!})"
344
+ def resolve_string_connection(spec)
345
+ if Octopus.rails41?
346
+ resolver = ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver.new({})
347
+ resolver.spec(spec).config.stringify_keys
348
+ else
349
+ if Octopus.rails4?
350
+ resolver = ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver.new(spec, {})
351
+ else
352
+ resolver = ActiveRecord::Base::ConnectionSpecification::Resolver.new(spec, {})
353
+ end
354
+ resolver.spec.config.stringify_keys
355
+ end
340
356
  end
341
- end
342
357
 
343
- def resolve_string_connection(spec)
344
- if Octopus.rails4?
345
- resolver = ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver.new(spec, {})
346
- else
347
- resolver = ActiveRecord::Base::ConnectionSpecification::Resolver.new(spec, {})
358
+ def should_clean_connection_proxy?(method)
359
+ method.to_s =~ /insert|select|execute/ && !@replicated && !block
348
360
  end
349
- resolver.spec.config.stringify_keys
350
- end
351
361
 
352
- def should_clean_connection_proxy?(method)
353
- method.to_s =~ /insert|select|execute/ && !@replicated && !self.block
354
- end
362
+ # Try to use slaves if and only if `replicated: true` is specified in `shards.yml` and no slaves groups are defined
363
+ def should_send_queries_to_replicated_databases?(method)
364
+ @replicated && method.to_s =~ /select/ && !block && !slaves_grouped?
365
+ end
355
366
 
356
- # Try to use slaves if and only if `replicated: true` is specified in `shards.yml` and no slaves groups are defined
357
- def should_send_queries_to_replicated_databases?(method)
358
- @replicated && method.to_s =~ /select/ && !self.block && !slaves_grouped?
359
- end
367
+ def send_queries_to_selected_slave(method, *args, &block)
368
+ if current_model.replicated || fully_replicated?
369
+ selected_slave = @slaves_load_balancer.next
370
+ else
371
+ selected_slave = :master
372
+ end
360
373
 
361
- def send_queries_to_selected_slave(method, *args, &block)
362
- if current_model.replicated || fully_replicated?
363
- selected_slave = @slaves_load_balancer.next
364
- else
365
- selected_slave = :master
374
+ send_queries_to_slave(selected_slave, method, *args, &block)
366
375
  end
367
376
 
368
- send_queries_to_slave(selected_slave, method, *args, &block)
369
- end
370
-
371
- # We should use slaves if and only if its safe to do so.
372
- #
373
- # We can safely use slaves when:
374
- # (1) `replicated: true` is specified in `shards.yml`
375
- # (2) The current model is `replicated()`, or `fully_replicated: true` is specified in `shards.yml` which means that
376
- # all the model is `replicated()`
377
- # (3) It's a SELECT query
378
- # while ensuring that we revert `current_shard` from the selected slave to the (shard's) master
379
- # not to make queries other than SELECT leak to the slave.
380
- def should_use_slaves_for_method?(method)
381
- @replicated && (current_model.replicated || fully_replicated?) && method.to_s =~ /select/
382
- end
377
+ # We should use slaves if and only if its safe to do so.
378
+ #
379
+ # We can safely use slaves when:
380
+ # (1) `replicated: true` is specified in `shards.yml`
381
+ # (2) The current model is `replicated()`, or `fully_replicated: true` is specified in `shards.yml` which means that
382
+ # all the model is `replicated()`
383
+ # (3) It's a SELECT query
384
+ # while ensuring that we revert `current_shard` from the selected slave to the (shard's) master
385
+ # not to make queries other than SELECT leak to the slave.
386
+ def should_use_slaves_for_method?(method)
387
+ @replicated && (current_model.replicated || fully_replicated?) && method.to_s =~ /select/
388
+ end
383
389
 
384
- def slaves_grouped?
385
- @slave_groups.present?
386
- end
390
+ def slaves_grouped?
391
+ @slave_groups.present?
392
+ end
387
393
 
388
- # Temporarily switch `current_shard` to the next slave in a slave group and send queries to it
389
- # while preserving `current_shard`
390
- def send_queries_to_balancer(balancer, method, *args, &block)
391
- send_queries_to_slave(balancer.next, method, *args, &block)
392
- end
394
+ # Temporarily switch `current_shard` to the next slave in a slave group and send queries to it
395
+ # while preserving `current_shard`
396
+ def send_queries_to_balancer(balancer, method, *args, &block)
397
+ send_queries_to_slave(balancer.next, method, *args, &block)
398
+ end
393
399
 
394
- # Temporarily switch `current_shard` to the specified slave and send queries to it
395
- # while preserving `current_shard`
396
- def send_queries_to_slave(slave, method, *args, &block)
397
- using_shard(slave) do
398
- select_connection.send(method, *args, &block)
400
+ # Temporarily switch `current_shard` to the specified slave and send queries to it
401
+ # while preserving `current_shard`
402
+ def send_queries_to_slave(slave, method, *args, &block)
403
+ using_shard(slave) do
404
+ select_connection.send(method, *args, &block)
405
+ end
399
406
  end
400
- end
401
407
 
402
- # Temporarily block cleaning connection proxy and run the block
403
- #
404
- # @see Octopus::Proxy#should_clean_connection?
405
- # @see Octopus::Proxy#clean_connection_proxy
406
- def keeping_connection_proxy(&block)
407
- last_block = self.block
408
+ # Temporarily block cleaning connection proxy and run the block
409
+ #
410
+ # @see Octopus::Proxy#should_clean_connection?
411
+ # @see Octopus::Proxy#clean_connection_proxy
412
+ def keeping_connection_proxy(&_block)
413
+ last_block = block
408
414
 
409
- begin
410
- self.block = true
411
- yield
412
- ensure
413
- self.block = last_block || false
415
+ begin
416
+ self.block = true
417
+ yield
418
+ ensure
419
+ self.block = last_block || false
420
+ end
414
421
  end
415
- end
416
422
 
417
- # Temporarily switch `current_shard` and run the block
418
- def using_shard(shard, &block)
419
- older_shard = self.current_shard
423
+ # Temporarily switch `current_shard` and run the block
424
+ def using_shard(shard, &_block)
425
+ older_shard = current_shard
420
426
 
421
- begin
422
- self.current_shard = shard
423
- yield
424
- ensure
425
- self.current_shard = older_shard
427
+ begin
428
+ self.current_shard = shard
429
+ yield
430
+ ensure
431
+ self.current_shard = older_shard
432
+ end
426
433
  end
427
- end
428
434
 
429
- def structurally_slave?(config)
430
- config.is_a?(Hash) && config.key?("adapter")
431
- end
435
+ def structurally_slave?(config)
436
+ config.is_a?(Hash) && config.key?('adapter')
437
+ end
432
438
 
433
- def structurally_slave_group?(config)
434
- config.is_a?(Hash) && config.values.any? {|v| structurally_slave? v }
439
+ def structurally_slave_group?(config)
440
+ config.is_a?(Hash) && config.values.any? { |v| structurally_slave? v }
441
+ end
435
442
  end
436
-
437
443
  end