ar-octopus 0.8.5 → 0.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.travis.yml +6 -9
  4. data/Appraisals +8 -8
  5. data/README.mkdn +47 -15
  6. data/Rakefile +1 -0
  7. data/ar-octopus.gemspec +9 -12
  8. data/gemfiles/rails42.gemfile +2 -2
  9. data/gemfiles/{rails32.gemfile → rails5.gemfile} +2 -2
  10. data/gemfiles/{rails4.gemfile → rails51.gemfile} +2 -2
  11. data/gemfiles/{rails41.gemfile → rails52.gemfile} +2 -2
  12. data/lib/octopus/abstract_adapter.rb +4 -10
  13. data/lib/octopus/association.rb +1 -0
  14. data/lib/octopus/association_shard_tracking.rb +41 -71
  15. data/lib/octopus/collection_association.rb +9 -3
  16. data/lib/octopus/exception.rb +4 -0
  17. data/lib/octopus/finder_methods.rb +8 -0
  18. data/lib/octopus/load_balancing/round_robin.rb +2 -1
  19. data/lib/octopus/log_subscriber.rb +6 -2
  20. data/lib/octopus/migration.rb +123 -55
  21. data/lib/octopus/model.rb +42 -27
  22. data/lib/octopus/persistence.rb +33 -27
  23. data/lib/octopus/proxy.rb +147 -272
  24. data/lib/octopus/proxy_config.rb +251 -0
  25. data/lib/octopus/query_cache_for_shards.rb +24 -0
  26. data/lib/octopus/railtie.rb +0 -2
  27. data/lib/octopus/relation_proxy.rb +36 -1
  28. data/lib/octopus/result_patch.rb +19 -0
  29. data/lib/octopus/scope_proxy.rb +12 -5
  30. data/lib/octopus/shard_tracking.rb +8 -3
  31. data/lib/octopus/slave_group.rb +3 -3
  32. data/lib/octopus/version.rb +1 -1
  33. data/lib/octopus.rb +71 -18
  34. data/spec/config/shards.yml +12 -0
  35. data/spec/migrations/10_create_users_using_replication.rb +1 -1
  36. data/spec/migrations/11_add_field_in_all_slaves.rb +1 -1
  37. data/spec/migrations/12_create_users_using_block.rb +1 -1
  38. data/spec/migrations/13_create_users_using_block_and_using.rb +1 -1
  39. data/spec/migrations/14_create_users_on_shards_of_a_group_with_versions.rb +1 -1
  40. data/spec/migrations/15_create_user_on_shards_of_default_group_with_versions.rb +1 -1
  41. data/spec/migrations/1_create_users_on_master.rb +1 -1
  42. data/spec/migrations/2_create_users_on_canada.rb +1 -1
  43. data/spec/migrations/3_create_users_on_both_shards.rb +1 -1
  44. data/spec/migrations/4_create_users_on_shards_of_a_group.rb +1 -1
  45. data/spec/migrations/5_create_users_on_multiples_groups.rb +1 -1
  46. data/spec/migrations/6_raise_exception_with_invalid_shard_name.rb +1 -1
  47. data/spec/migrations/7_raise_exception_with_invalid_multiple_shard_names.rb +1 -1
  48. data/spec/migrations/8_raise_exception_with_invalid_group_name.rb +1 -1
  49. data/spec/migrations/9_raise_exception_with_multiple_invalid_group_names.rb +1 -1
  50. data/spec/octopus/association_shard_tracking_spec.rb +344 -16
  51. data/spec/octopus/load_balancing/round_robin_spec.rb +15 -0
  52. data/spec/octopus/log_subscriber_spec.rb +1 -1
  53. data/spec/octopus/migration_spec.rb +45 -11
  54. data/spec/octopus/model_spec.rb +204 -16
  55. data/spec/octopus/octopus_spec.rb +2 -2
  56. data/spec/octopus/proxy_spec.rb +44 -40
  57. data/spec/octopus/query_cache_for_shards_spec.rb +40 -0
  58. data/spec/octopus/relation_proxy_spec.rb +71 -23
  59. data/spec/octopus/replicated_slave_grouped_spec.rb +27 -0
  60. data/spec/octopus/replication_spec.rb +72 -2
  61. data/spec/octopus/scope_proxy_spec.rb +41 -7
  62. data/spec/spec_helper.rb +2 -0
  63. data/spec/support/database_connection.rb +1 -1
  64. data/spec/support/database_models.rb +1 -1
  65. data/spec/support/octopus_helper.rb +14 -6
  66. data/spec/tasks/octopus.rake_spec.rb +1 -1
  67. metadata +40 -30
  68. data/.ruby-version +0 -1
  69. data/init.rb +0 -1
  70. data/lib/octopus/has_and_belongs_to_many_association.rb +0 -9
  71. data/rails/init.rb +0 -1
@@ -19,7 +19,9 @@ module Octopus
19
19
  def self.included(base)
20
20
  base.extend(ClassMethods)
21
21
 
22
- base.alias_method_chain :announce, :octopus
22
+ base.send :alias_method, :announce_without_octopus, :announce
23
+ base.send :alias_method, :announce, :announce_with_octopus
24
+
23
25
  base.class_attribute :current_shard, :current_group, :current_group_specified, :instance_reader => false, :instance_writer => false
24
26
  end
25
27
 
@@ -51,7 +53,7 @@ module Octopus
51
53
  shards.merge(Array.wrap(shard))
52
54
  end
53
55
 
54
- shards.to_a.presence || [:master]
56
+ shards.to_a.presence || [Octopus.master_shard]
55
57
  end
56
58
  end
57
59
  end
@@ -60,81 +62,145 @@ end
60
62
  module Octopus
61
63
  module Migrator
62
64
  def self.included(base)
63
- base.extend(ClassMethods)
65
+ unless Octopus.atleast_rails52?
66
+ base.extend(ClassMethods)
67
+
68
+ base.class_eval do
69
+ class << self
70
+ alias_method :migrate_without_octopus, :migrate
71
+ alias_method :migrate, :migrate_with_octopus
72
+
73
+ alias_method :up_without_octopus, :up
74
+ alias_method :up, :up_with_octopus
64
75
 
65
- base.class_eval do
66
- class << self
67
- alias_method_chain :migrate, :octopus
68
- alias_method_chain :up, :octopus
69
- alias_method_chain :down, :octopus
70
- alias_method_chain :run, :octopus
76
+ alias_method :down_without_octopus, :down
77
+ alias_method :down, :down_with_octopus
78
+
79
+ alias_method :run_without_octopus, :run
80
+ alias_method :run, :run_with_octopus
81
+
82
+ alias_method :rollback_without_octopus, :rollback
83
+ alias_method :rollback, :rollback_with_octopus
84
+ end
71
85
  end
72
86
  end
73
87
 
74
- base.alias_method_chain :run, :octopus
75
- base.alias_method_chain :migrate, :octopus
76
- base.alias_method_chain :migrations, :octopus
77
- end
88
+ base.send :alias_method, :run_without_octopus, :run
89
+ base.send :alias_method, :run, :run_with_octopus
78
90
 
79
- def run_with_octopus(&block)
80
- run_without_octopus(&block)
81
- rescue ActiveRecord::UnknownMigrationVersionError => e
82
- raise unless migrations(true).detect { |m| m.version == e.version }
83
- end
91
+ base.send :alias_method, :migrate_without_octopus, :migrate
92
+ base.send :alias_method, :migrate, :migrate_with_octopus
84
93
 
85
- def migrate_with_octopus(&block)
86
- migrate_without_octopus(&block)
87
- rescue ActiveRecord::UnknownMigrationVersionError => e
88
- raise unless migrations(true).detect { |m| m.version == e.version }
94
+ base.send :alias_method, :migrations_without_octopus, :migrations
95
+ base.send :alias_method, :migrations, :migrations_with_octopus
89
96
  end
97
+ if Octopus.atleast_rails52?
98
+ ### Post RAILS 5.2 Migration methods
99
+
100
+ def run_with_octopus(&block)
101
+ return run_without_octopus(&block) unless connection.is_a?(Octopus::Proxy)
102
+ shards = migrations.map(&:shards).flatten.map(&:to_s)
103
+ connection.send_queries_to_multiple_shards(shards) do
104
+ run_without_octopus(&block)
105
+ end
106
+ rescue ActiveRecord::UnknownMigrationVersionError => e
107
+ raise unless migrations(true).detect { |m| m.version == e.version }
108
+ end
90
109
 
91
- def migrations_with_octopus(shard_agnostic = false)
92
- connection = ActiveRecord::Base.connection
93
- migrations = migrations_without_octopus
94
- return migrations if !connection.is_a?(Octopus::Proxy) || shard_agnostic
110
+ def migrate_with_octopus(&block)
111
+ return migrate_without_octopus(&block) unless connection.is_a?(Octopus::Proxy)
112
+ shards = migrations.map(&:shards).flatten.map(&:to_s)
113
+ connection.send_queries_to_multiple_shards(shards) do
114
+ migrate_without_octopus(&block)
115
+ end
116
+ rescue ActiveRecord::UnknownMigrationVersionError => e
117
+ raise unless migrations(true).detect { |m| m.version == e.version }
118
+ end
95
119
 
96
- migrations.select { |m| m.shards.include?(connection.current_shard.to_sym) }
97
- end
120
+ def migrations_with_octopus(shard_agnostic = true)
121
+ migrations = migrations_without_octopus
122
+ return migrations if !connection.is_a?(Octopus::Proxy) || shard_agnostic
98
123
 
99
- module ClassMethods
100
- def migrate_with_octopus(migrations_paths, target_version = nil, &block)
101
- return migrate_without_octopus(migrations_paths, target_version, &block) unless connection.is_a?(Octopus::Proxy)
124
+ migrations.select { |m| m.shards.include?(connection.current_shard.to_sym) }
125
+ end
126
+
127
+ private
128
+
129
+ def connection
130
+ ActiveRecord::Base.connection
131
+ end
132
+
133
+ else
134
+ ### Pre RAILS 5.2 Migration methods
135
+
136
+ def run_with_octopus(&block)
137
+ run_without_octopus(&block)
138
+ rescue ActiveRecord::UnknownMigrationVersionError => e
139
+ raise unless migrations(true).detect { |m| m.version == e.version }
140
+ end
102
141
 
103
- connection.send_queries_to_multiple_shards(connection.shard_names) do
104
- migrate_without_octopus(migrations_paths, target_version, &block)
105
- end
142
+ def migrate_with_octopus(&block)
143
+ migrate_without_octopus(&block)
144
+ rescue ActiveRecord::UnknownMigrationVersionError => e
145
+ raise unless migrations(true).detect { |m| m.version == e.version }
106
146
  end
107
147
 
108
- def up_with_octopus(migrations_paths, target_version = nil, &block)
109
- return up_without_octopus(migrations_paths, target_version, &block) unless connection.is_a?(Octopus::Proxy)
110
- return up_without_octopus(migrations_paths, target_version, &block) unless connection.current_shard == :master
148
+ def migrations_with_octopus(shard_agnostic = false)
149
+ connection = ActiveRecord::Base.connection
150
+ migrations = migrations_without_octopus
151
+ return migrations if !connection.is_a?(Octopus::Proxy) || shard_agnostic
111
152
 
112
- connection.send_queries_to_multiple_shards(connection.shard_names) do
113
- up_without_octopus(migrations_paths, target_version, &block)
114
- end
153
+ migrations.select { |m| m.shards.include?(connection.current_shard.to_sym) }
115
154
  end
116
155
 
117
- def down_with_octopus(migrations_paths, target_version = nil, &block)
118
- return down_without_octopus(migrations_paths, target_version, &block) unless connection.is_a?(Octopus::Proxy)
119
- return down_without_octopus(migrations_paths, target_version, &block) unless connection.current_shard == :master
156
+ module ClassMethods
157
+ def migrate_with_octopus(migrations_paths, target_version = nil, &block)
158
+ return migrate_without_octopus(migrations_paths, target_version, &block) unless connection.is_a?(Octopus::Proxy)
120
159
 
121
- connection.send_queries_to_multiple_shards(connection.shard_names) do
122
- down_without_octopus(migrations_paths, target_version, &block)
160
+ connection.send_queries_to_multiple_shards(connection.shard_names) do
161
+ migrate_without_octopus(migrations_paths, target_version, &block)
162
+ end
123
163
  end
124
- end
125
164
 
126
- def run_with_octopus(direction, migrations_paths, target_version)
127
- return run_without_octopus(direction, migrations_paths, target_version) unless connection.is_a?(Octopus::Proxy)
165
+ def up_with_octopus(migrations_paths, target_version = nil, &block)
166
+ return up_without_octopus(migrations_paths, target_version, &block) unless connection.is_a?(Octopus::Proxy)
167
+ return up_without_octopus(migrations_paths, target_version, &block) unless connection.current_shard.to_s == Octopus.master_shard.to_s
128
168
 
129
- connection.send_queries_to_multiple_shards(connection.shard_names) do
130
- run_without_octopus(direction, migrations_paths, target_version)
169
+ connection.send_queries_to_multiple_shards(connection.shard_names) do
170
+ up_without_octopus(migrations_paths, target_version, &block)
171
+ end
131
172
  end
132
- end
133
173
 
134
- private
174
+ def down_with_octopus(migrations_paths, target_version = nil, &block)
175
+ return down_without_octopus(migrations_paths, target_version, &block) unless connection.is_a?(Octopus::Proxy)
176
+ return down_without_octopus(migrations_paths, target_version, &block) unless connection.current_shard.to_s == Octopus.master_shard.to_s
135
177
 
136
- def connection
137
- ActiveRecord::Base.connection
178
+ connection.send_queries_to_multiple_shards(connection.shard_names) do
179
+ down_without_octopus(migrations_paths, target_version, &block)
180
+ end
181
+ end
182
+
183
+ def run_with_octopus(direction, migrations_paths, target_version)
184
+ return run_without_octopus(direction, migrations_paths, target_version) unless connection.is_a?(Octopus::Proxy)
185
+
186
+ connection.send_queries_to_multiple_shards(connection.shard_names) do
187
+ run_without_octopus(direction, migrations_paths, target_version)
188
+ end
189
+ end
190
+
191
+ def rollback_with_octopus(migrations_paths, steps = 1)
192
+ return rollback_without_octopus(migrations_paths, steps) unless connection.is_a?(Octopus::Proxy)
193
+
194
+ connection.send_queries_to_multiple_shards(connection.shard_names) do
195
+ rollback_without_octopus(migrations_paths, steps)
196
+ end
197
+ end
198
+
199
+ private
200
+
201
+ def connection
202
+ ActiveRecord::Base.connection
203
+ end
138
204
  end
139
205
  end
140
206
  end
@@ -151,7 +217,9 @@ end
151
217
  module Octopus
152
218
  module UnknownMigrationVersionError
153
219
  def self.included(base)
154
- base.alias_method_chain :initialize, :octopus
220
+ base.send :alias_method, :initialize_without_octopus, :initialize
221
+ base.send :alias_method, :initialize, :initialize_with_octopus
222
+
155
223
  base.send(:attr_accessor, :version)
156
224
  end
157
225
 
data/lib/octopus/model.rb CHANGED
@@ -9,20 +9,16 @@ module Octopus
9
9
  end
10
10
 
11
11
  module SharedMethods
12
- def clean_table_name
13
- return unless connection_proxy.should_clean_table_name?
12
+ def using(shard)
13
+ if block_given?
14
+ raise Octopus::Exception, <<-EOF
15
+ #{name}.using is not allowed to receive a block, it works just like a regular scope.
14
16
 
15
- if self != ActiveRecord::Base && self.respond_to?(:reset_table_name) && !custom_octopus_table_name
16
- reset_table_name
17
+ If you are trying to scope everything to a specific shard, use Octopus.using instead.
18
+ EOF
17
19
  end
18
20
 
19
- reset_column_information
20
- instance_variable_set(:@quoted_table_name, nil)
21
- end
22
-
23
- def using(shard)
24
21
  if Octopus.enabled?
25
- clean_table_name
26
22
  Octopus::ScopeProxy.new(shard, self)
27
23
  else
28
24
  self
@@ -37,19 +33,29 @@ module Octopus
37
33
  base.send(:alias_method, :equality_without_octopus, :==)
38
34
  base.send(:alias_method, :==, :equality_with_octopus)
39
35
  base.send(:alias_method, :eql?, :==)
40
- base.send(:alias_method_chain, :perform_validations, :octopus)
36
+ base.send(:alias_method, :perform_validations_without_octopus, :perform_validations)
37
+ base.send(:alias_method, :perform_validations, :perform_validations_with_octopus)
41
38
  end
42
39
 
43
40
  def set_current_shard
44
41
  return unless Octopus.enabled?
42
+ shard = self.class.connection_proxy.current_shard
43
+ self.current_shard = shard if self.class.allowed_shard?(shard)
44
+ end
45
45
 
46
- if new_record? || self.class.connection_proxy.block
47
- shard = self.class.connection_proxy.current_shard
48
- else
49
- shard = self.class.connection_proxy.last_current_shard || self.class.connection_proxy.current_shard
50
- end
46
+ def init_with(coder)
47
+ obj = super
51
48
 
52
- self.current_shard = shard if self.class.allowed_shard?(shard)
49
+ return obj unless Octopus.enabled?
50
+ return obj if obj.class.connection_proxy.current_model_replicated?
51
+
52
+ current_shard_value = coder['attributes']['current_shard'].value if coder['attributes']['current_shard'].present? && coder['attributes']['current_shard'].value.present?
53
+
54
+ coder['attributes'].send(:attributes).send(:values).delete('current_shard')
55
+ coder['attributes'].send(:attributes).send(:delegate_hash).delete('current_shard')
56
+
57
+ obj.current_shard = current_shard_value if current_shard_value.present?
58
+ obj
53
59
  end
54
60
 
55
61
  def should_set_current_shard?
@@ -57,7 +63,7 @@ module Octopus
57
63
  end
58
64
 
59
65
  def equality_with_octopus(comparison_object)
60
- equality_without_octopus(comparison_object) && comparison_object.current_shard == current_shard
66
+ equality_without_octopus(comparison_object) && comparison_object.current_shard.to_s == current_shard.to_s
61
67
  end
62
68
 
63
69
  def perform_validations_with_octopus(*args)
@@ -95,20 +101,29 @@ module Octopus
95
101
  end
96
102
 
97
103
  def hijack_methods
98
- around_save :run_on_shard, :unless => -> { self.class.custom_octopus_connection }
99
104
  after_initialize :set_current_shard
100
105
 
106
+ around_save :run_on_shard, :unless => lambda { self.class.custom_octopus_connection }
107
+
108
+ class_attribute :custom_octopus_connection
109
+
101
110
  class << self
102
- attr_accessor :custom_octopus_connection
103
111
  attr_accessor :custom_octopus_table_name
104
112
 
105
- alias_method_chain :connection, :octopus
106
- alias_method_chain :connection_pool, :octopus
107
- alias_method_chain :clear_all_connections!, :octopus
108
- alias_method_chain :clear_active_connections!, :octopus
109
- alias_method_chain :connected?, :octopus
113
+ alias_method :connection_without_octopus, :connection
114
+ alias_method :connection, :connection_with_octopus
115
+
116
+ alias_method :connection_pool_without_octopus, :connection_pool
117
+ alias_method :connection_pool, :connection_pool_with_octopus
118
+
119
+ alias_method :clear_all_connections_without_octopus!, :clear_all_connections!
120
+ alias_method :clear_all_connections!, :clear_all_connections_with_octopus!
121
+
122
+ alias_method :clear_active_connections_without_octopus!, :clear_active_connections!
123
+ alias_method :clear_active_connections!, :clear_active_connections_with_octopus!
110
124
 
111
- alias_method_chain(:set_table_name, :octopus) if Octopus.rails3?
125
+ alias_method :connected_without_octopus?, :connected?
126
+ alias_method :connected?, :connected_with_octopus?
112
127
 
113
128
  def table_name=(value = nil)
114
129
  self.custom_octopus_table_name = true
@@ -133,7 +148,7 @@ module Octopus
133
148
 
134
149
  def allowed_shard?(shard)
135
150
  if custom_octopus_connection
136
- allowed_shards && shard && allowed_shards.include?(shard)
151
+ allowed_shards && shard && (allowed_shards.include?(shard.to_s) || allowed_shards.include?(shard.to_sym))
137
152
  else
138
153
  true
139
154
  end
@@ -1,39 +1,45 @@
1
1
  module Octopus
2
- module Rails3
3
- module Persistence
4
- def update_attribute(*args)
5
- run_on_shard { super }
6
- end
2
+ module Persistence
3
+ def update_attribute(*args)
4
+ run_on_shard { super }
5
+ end
7
6
 
8
- def update_attributes(*args)
9
- run_on_shard { super }
10
- end
7
+ def update_attributes(*args)
8
+ run_on_shard { super }
9
+ end
11
10
 
12
- def update_attributes!(*args)
13
- run_on_shard { super }
14
- end
11
+ def update_attributes!(*args)
12
+ run_on_shard { super }
13
+ end
15
14
 
16
- def reload(*args)
17
- run_on_shard { super }
18
- end
15
+ def reload(*args)
16
+ run_on_shard { super }
17
+ end
19
18
 
20
- def delete
21
- run_on_shard { super }
22
- end
19
+ def delete
20
+ run_on_shard { super }
21
+ end
23
22
 
24
- def destroy
25
- run_on_shard { super }
26
- end
23
+ def destroy
24
+ run_on_shard { super }
25
+ end
27
26
 
28
- def touch(*args)
29
- run_on_shard { super }
30
- end
27
+ def touch(*args)
28
+ run_on_shard { super }
29
+ end
30
+
31
+ def update_column(*args)
32
+ run_on_shard { super }
33
+ end
34
+
35
+ def increment!(*args)
36
+ run_on_shard { super }
37
+ end
31
38
 
32
- def update_column(*args)
33
- run_on_shard { super }
34
- end
39
+ def decrement!(*args)
40
+ run_on_shard { super }
35
41
  end
36
42
  end
37
43
  end
38
44
 
39
- ActiveRecord::Base.send(:include, Octopus::Rails3::Persistence)
45
+ ActiveRecord::Base.send(:include, Octopus::Persistence)