ar-octopus 0.8.2 → 0.8.3

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.
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