switchman 1.6.1 → 2.0.9

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 (56) hide show
  1. checksums.yaml +5 -5
  2. data/app/models/switchman/shard.rb +746 -11
  3. data/db/migrate/20130328212039_create_switchman_shards.rb +3 -1
  4. data/db/migrate/20130328224244_create_default_shard.rb +4 -2
  5. data/db/migrate/20161206323434_add_back_default_string_limits_switchman.rb +13 -0
  6. data/db/migrate/20180828183945_add_default_shard_index.rb +15 -0
  7. data/db/migrate/20180828192111_add_timestamps_to_shards.rb +17 -0
  8. data/db/migrate/20190114212900_add_unique_name_indexes.rb +9 -0
  9. data/lib/switchman/action_controller/caching.rb +2 -0
  10. data/lib/switchman/active_record/abstract_adapter.rb +14 -4
  11. data/lib/switchman/active_record/association.rb +64 -37
  12. data/lib/switchman/active_record/attribute_methods.rb +16 -5
  13. data/lib/switchman/active_record/base.rb +67 -22
  14. data/lib/switchman/active_record/batches.rb +3 -1
  15. data/lib/switchman/active_record/calculations.rb +16 -21
  16. data/lib/switchman/active_record/connection_handler.rb +71 -79
  17. data/lib/switchman/active_record/connection_pool.rb +28 -23
  18. data/lib/switchman/active_record/finder_methods.rb +20 -29
  19. data/lib/switchman/active_record/log_subscriber.rb +14 -19
  20. data/lib/switchman/active_record/migration.rb +80 -0
  21. data/lib/switchman/active_record/model_schema.rb +3 -1
  22. data/lib/switchman/active_record/persistence.rb +9 -1
  23. data/lib/switchman/active_record/postgresql_adapter.rb +166 -126
  24. data/lib/switchman/active_record/predicate_builder.rb +2 -0
  25. data/lib/switchman/active_record/query_cache.rb +22 -87
  26. data/lib/switchman/active_record/query_methods.rb +122 -126
  27. data/lib/switchman/active_record/reflection.rb +37 -20
  28. data/lib/switchman/active_record/relation.rb +50 -29
  29. data/lib/switchman/active_record/spawn_methods.rb +2 -0
  30. data/lib/switchman/active_record/statement_cache.rb +44 -52
  31. data/lib/switchman/active_record/table_definition.rb +4 -2
  32. data/lib/switchman/active_record/type_caster.rb +2 -0
  33. data/lib/switchman/active_record/where_clause_factory.rb +5 -2
  34. data/lib/switchman/active_support/cache.rb +18 -0
  35. data/lib/switchman/arel.rb +8 -25
  36. data/lib/switchman/call_super.rb +19 -0
  37. data/lib/switchman/connection_pool_proxy.rb +70 -24
  38. data/lib/switchman/database_server.rb +69 -59
  39. data/lib/switchman/default_shard.rb +3 -0
  40. data/lib/switchman/engine.rb +42 -40
  41. data/lib/switchman/environment.rb +2 -0
  42. data/lib/switchman/errors.rb +2 -0
  43. data/lib/switchman/{shackles → guard_rail}/relation.rb +7 -5
  44. data/lib/switchman/{shackles.rb → guard_rail.rb} +6 -4
  45. data/lib/switchman/open4.rb +2 -0
  46. data/lib/switchman/r_spec_helper.rb +14 -8
  47. data/lib/switchman/rails.rb +2 -0
  48. data/lib/switchman/schema_cache.rb +17 -0
  49. data/lib/switchman/sharded_instrumenter.rb +4 -2
  50. data/lib/switchman/standard_error.rb +4 -2
  51. data/lib/switchman/test_helper.rb +6 -3
  52. data/lib/switchman/version.rb +3 -1
  53. data/lib/switchman.rb +3 -1
  54. data/lib/tasks/switchman.rake +46 -72
  55. metadata +87 -41
  56. data/app/models/switchman/shard_internal.rb +0 -692
@@ -1,4 +1,6 @@
1
- class CreateSwitchmanShards < ActiveRecord::Migration
1
+ # frozen_string_literal: true
2
+
3
+ class CreateSwitchmanShards < ActiveRecord::Migration[4.2]
2
4
  def change
3
5
  create_table :switchman_shards do |t|
4
6
  t.string :name
@@ -1,9 +1,11 @@
1
- class CreateDefaultShard < ActiveRecord::Migration
1
+ # frozen_string_literal: true
2
+
3
+ class CreateDefaultShard < ActiveRecord::Migration[4.2]
2
4
  def up
3
5
  unless Switchman::Shard.default.is_a?(Switchman::Shard)
4
6
  Switchman::Shard.reset_column_information
5
7
  Switchman::Shard.create!(:default => true)
6
- Switchman::Shard.default(true)
8
+ Switchman::Shard.default(reload: true)
7
9
  end
8
10
  end
9
11
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddBackDefaultStringLimitsSwitchman < ActiveRecord::Migration[4.2]
4
+ def up
5
+ add_string_limit_if_missing :switchman_shards, :name
6
+ add_string_limit_if_missing :switchman_shards, :database_server_id
7
+ end
8
+
9
+ def add_string_limit_if_missing(table, column)
10
+ return if column_exists?(table, column, :string, limit: 255)
11
+ change_column table, column, :string, limit: 255
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddDefaultShardIndex < ActiveRecord::Migration[4.2]
4
+ def change
5
+ Switchman::Shard.where(default: nil).update_all(default: false)
6
+ change_column_default :switchman_shards, :default, false
7
+ change_column_null :switchman_shards, :default, false
8
+ options = if connection.adapter_name == 'PostgreSQL'
9
+ { unique: true, where: "\"default\"" }
10
+ else
11
+ {}
12
+ end
13
+ add_index :switchman_shards, :default, **options
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddTimestampsToShards < ActiveRecord::Migration[4.2]
4
+ def change
5
+ add_timestamps :switchman_shards, null: true
6
+ now = Time.now.utc
7
+ Switchman::Shard.update_all(updated_at: now, created_at: now) if Switchman::Shard.current.default?
8
+ change_column_null :switchman_shards, :updated_at, false
9
+ change_column_null :switchman_shards, :created_at, false
10
+
11
+ if Switchman::Shard.current.default?
12
+ Switchman::Shard.connection.schema_cache.clear!
13
+ Switchman::Shard.reset_column_information
14
+ Switchman::Shard.columns
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddUniqueNameIndexes < ActiveRecord::Migration[4.2]
4
+ def change
5
+ add_index :switchman_shards, [:database_server_id, :name], unique: true
6
+ add_index :switchman_shards, :database_server_id, unique: true, where: "name IS NULL", name: 'index_switchman_shards_unique_primary_shard'
7
+ add_index :switchman_shards, "(true)", unique: true, where: "database_server_id IS NULL AND name IS NULL", name: 'index_switchman_shards_unique_primary_db_and_shard'
8
+ end
9
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Switchman
2
4
  module ActionController
3
5
  module Caching
@@ -1,11 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'switchman/sharded_instrumenter'
2
4
 
3
5
  module Switchman
4
6
  module ActiveRecord
5
7
  module AbstractAdapter
6
8
  module ForeignKeyCheck
7
- def add_column(table, name, type, options = {})
8
- Engine.foreign_key_check(name, type, options)
9
+ def add_column(table, name, type, limit: nil, **)
10
+ Engine.foreign_key_check(name, type, limit: limit)
9
11
  super
10
12
  end
11
13
  end
@@ -27,8 +29,8 @@ module Switchman
27
29
  quote_table_name(name)
28
30
  end
29
31
 
30
- def use_qualified_names?
31
- false
32
+ def schema_migration
33
+ ::ActiveRecord::SchemaMigration
32
34
  end
33
35
 
34
36
  protected
@@ -38,6 +40,14 @@ module Switchman
38
40
  ensure
39
41
  @last_query_at = Time.now
40
42
  end
43
+
44
+ private
45
+
46
+ def id_value_for_database(value)
47
+ return super unless value.class.sharded_primary_key?
48
+ # do this the Rails 4.2 way, so that if Shard.current != self.shard, the id gets transposed
49
+ quote(value.id)
50
+ end
41
51
  end
42
52
  end
43
53
  end
@@ -1,13 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Switchman
2
4
  module ActiveRecord
3
5
  module Association
4
6
  def shard
5
- if @reflection.options[:polymorphic] || @reflection.klass.shard_category == @owner.class.shard_category
6
- # polymorphic associations assume the same shard as the owning item
7
- @owner.shard
8
- else
9
- Shard.default
10
- end
7
+ reflection.shard(owner)
11
8
  end
12
9
 
13
10
  def build_record(*args)
@@ -18,12 +15,6 @@ module Switchman
18
15
  self.shard.activate { super }
19
16
  end
20
17
 
21
- def association_scope
22
- if klass
23
- shard.activate(klass.shard_category) { super }
24
- end
25
- end
26
-
27
18
  def scope
28
19
  shard_value = @reflection.options[:multishard] ? @owner : self.shard
29
20
  @owner.shard.activate { super.shard(shard_value, :association) }
@@ -41,9 +32,11 @@ module Switchman
41
32
  end
42
33
 
43
34
  module CollectionAssociation
44
- def get_records
45
- shards = reflection.options[:multishard] && owner.respond_to?(:associated_shards) ? owner.associated_shards : [owner.shard]
46
- Shard.with_each_shard(shards, [klass.shard_category]) do
35
+ def find_target
36
+ shards = reflection.options[:multishard] && owner.respond_to?(:associated_shards) ? owner.associated_shards : [shard]
37
+ # activate both the owner and the target's shard category, so that Reflection#join_id_for,
38
+ # when called for the owner, will be returned relative to shard the query will execute on
39
+ Shard.with_each_shard(shards, [klass.shard_category, owner.class.shard_category].uniq) do
47
40
  super
48
41
  end
49
42
  end
@@ -69,30 +62,49 @@ module Switchman
69
62
  end
70
63
  end
71
64
 
72
- if ::Rails.version >= '5'
73
- module Extension
74
- def self.build(_model, _reflection)
75
- end
76
-
77
- def self.valid_options
78
- [:multishard]
79
- end
65
+ module Extension
66
+ def self.build(_model, _reflection)
80
67
  end
81
68
 
82
- ::ActiveRecord::Associations::Builder::Association.extensions << Extension
83
- else
84
- module Builder
85
- module CollectionAssociation
86
- def valid_options
87
- super + [:multishard]
88
- end
89
- end
69
+ def self.valid_options
70
+ [:multishard]
90
71
  end
91
72
  end
92
73
 
74
+ ::ActiveRecord::Associations::Builder::Association.extensions << Extension
75
+
93
76
  module Preloader
94
77
  module Association
78
+ if ::Rails.version >= "5.2" and ::Rails.version < "6.0"
79
+ def run(preloader)
80
+ associated_records_by_owner.each do |owner, records|
81
+ associate_records_to_owner(owner, records)
82
+ end
83
+ end
84
+ end
85
+
86
+ if ::Rails.version >= "6.0"
87
+ # Copypasta from Activerecord but with added global_id_for goodness.
88
+ def records_for(ids)
89
+ scope.where(association_key_name => ids).load do |record|
90
+ global_key = if record.class.shard_category == :unsharded
91
+ convert_key(record[association_key_name])
92
+ else
93
+ Shard.global_id_for(record[association_key_name], record.shard)
94
+ end
95
+ owner = owners_by_key[global_key.to_s].first
96
+ association = owner.association(reflection.name)
97
+ association.set_inverse_instance(record)
98
+ end
99
+ end
100
+
101
+ def records_by_owner
102
+ associated_records_by_owner
103
+ end
104
+ end
105
+
95
106
  def associated_records_by_owner(preloader = nil)
107
+ return @associated_records_by_owner if defined?(@associated_records_by_owner)
96
108
  owners_map = owners_by_key
97
109
 
98
110
  if klass.nil? || owners_map.empty?
@@ -140,12 +152,13 @@ module Switchman
140
152
  records.flatten!
141
153
  end
142
154
 
143
- if ::Rails.version >= '4.1'
144
- @preloaded_records = records
145
- end
155
+ # This ivar may look unused, but remember this is an extension of
156
+ # rails' AR::Associations::Preloader::Association class. It gets used
157
+ # by that class (and its subclasses).
158
+ @preloaded_records = records
146
159
 
147
160
  # Each record may have multiple owners, and vice-versa
148
- records_by_owner = owners.each_with_object({}) do |owner,h|
161
+ @associated_records_by_owner = owners.each_with_object({}) do |owner,h|
149
162
  h[owner] = []
150
163
  end
151
164
  records.each do |record|
@@ -153,10 +166,11 @@ module Switchman
153
166
  owner_key = Shard.global_id_for(owner_key, record.shard) if owner_key && record.class.sharded_column?(association_key_name)
154
167
 
155
168
  owners_map[owner_key.to_s].each do |owner|
156
- records_by_owner[owner] << record
169
+ owner.association(reflection.name).set_inverse_instance(record)
170
+ @associated_records_by_owner[owner] << record
157
171
  end
158
172
  end
159
- records_by_owner
173
+ @associated_records_by_owner
160
174
  end
161
175
 
162
176
  def owners_by_key
@@ -174,6 +188,12 @@ module Switchman
174
188
  end
175
189
 
176
190
  module CollectionProxy
191
+ def initialize(*args)
192
+ super
193
+ self.shard_value = scope.shard_value
194
+ self.shard_source_value = :association
195
+ end
196
+
177
197
  def shard(*args)
178
198
  scope.shard(*args)
179
199
  end
@@ -185,6 +205,13 @@ module Switchman
185
205
  (record.has_attribute?(reflection.foreign_key) && record.send(reflection.foreign_key) != key) || # have to use send instead of [] because sharding
186
206
  record.attribute_changed?(reflection.foreign_key)
187
207
  end
208
+
209
+ def save_belongs_to_association(reflection)
210
+ # this seems counter-intuitive, but the autosave code will assign to attribute bypassing switchman,
211
+ # after reading the id attribute _without_ bypassing switchman. So we need Shard.current for the
212
+ # category of the associated record to match Shard.current for the category of self
213
+ shard.activate(shard_category_for_reflection(reflection)) { super }
214
+ end
188
215
  end
189
216
  end
190
217
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Switchman
2
4
  module ActiveRecord
3
5
  module AttributeMethods
@@ -27,7 +29,7 @@ module Switchman
27
29
  def reflection_for_integer_attribute(attr_name)
28
30
  attr_name = attr_name.to_s
29
31
  columns_hash[attr_name] && columns_hash[attr_name].type == :integer &&
30
- reflections.find { |_, r| r.belongs_to? && r.foreign_key.to_s == attr_name }.try(:last)
32
+ reflections.find { |_, r| r.belongs_to? && r.foreign_key.to_s == attr_name }&.last
31
33
  rescue ::ActiveRecord::StatementInvalid
32
34
  # this is for when models are referenced in initializers before migrations have been run
33
35
  raise if connection.open_transactions > 0
@@ -61,12 +63,15 @@ module Switchman
61
63
  end
62
64
  end
63
65
 
66
+ # see also Base#shard_category_for_reflection
67
+ # the difference being this will output static strings for the common cases, making them
68
+ # more performant
64
69
  def shard_category_code_for_reflection(reflection)
65
70
  if reflection
66
71
  if reflection.options[:polymorphic]
67
72
  # a polymorphic association has to be discovered at runtime. This code ends up being something like
68
- # context_type.try(:constantize).try(:shard_category) || :default
69
- "read_attribute(:#{reflection.foreign_type}).try(:constantize).try(:shard_category) || :default"
73
+ # context_type.&.constantize&.shard_category || :primary
74
+ "read_attribute(:#{reflection.foreign_type})&.constantize&.shard_category || :primary"
70
75
  else
71
76
  # otherwise we can just return a symbol for the statically known type of the association
72
77
  reflection.klass.shard_category.inspect
@@ -79,7 +84,13 @@ module Switchman
79
84
  def define_method_original_attribute(attr_name)
80
85
  if sharded_column?(attr_name)
81
86
  reflection = reflection_for_integer_attribute(attr_name)
82
- generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
87
+ if attr_name == "id" && ::Rails.version >= '5.1.2'
88
+ return if self.method_defined?(:original_id)
89
+ owner = self
90
+ else
91
+ owner = generated_attribute_methods
92
+ end
93
+ owner.module_eval <<-RUBY, __FILE__, __LINE__ + 1
83
94
  # rename the original method to original_
84
95
  alias_method 'original_#{attr_name}', '#{attr_name}'
85
96
  # and replace with one that transposes the id
@@ -117,7 +128,7 @@ module Switchman
117
128
  klass.extend(ClassMethods)
118
129
  klass.attribute_method_prefix "global_", "local_", "original_"
119
130
  end
120
-
131
+
121
132
  # ensure that we're using the sharded attribute method
122
133
  # and not the silly one in AR::AttributeMethods::PrimaryKey
123
134
  def id
@@ -1,11 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Switchman
2
4
  module ActiveRecord
3
5
  module Base
4
6
  module ClassMethods
5
7
  delegate :shard, to: :all
6
8
 
9
+ def find_ids_in_ranges(opts={}, &block)
10
+ opts.reverse_merge!(:loose => true)
11
+ all.find_ids_in_ranges(opts, &block)
12
+ end
13
+
7
14
  def shard_category
8
- @shard_category || (self.superclass < ::ActiveRecord::Base && self.superclass.shard_category) || :default
15
+ connection_specification_name.to_sym
9
16
  end
10
17
 
11
18
  def shard_category=(category)
@@ -14,34 +21,32 @@ module Switchman
14
21
  categories[shard_category].delete(self)
15
22
  categories.delete(shard_category) if categories[shard_category].empty?
16
23
  end
17
- connection_handler.uninitialize_ar(self)
18
24
  categories[category] ||= []
19
25
  categories[category] << self
20
- @shard_category = category
21
- connection_handler.initialize_categories(superclass)
26
+ self.connection_specification_name = category.to_s
22
27
  end
23
28
 
24
29
  def integral_id?
25
30
  if @integral_id == nil
26
- @integral_id = columns_hash[primary_key].try(:type) == :integer
31
+ @integral_id = columns_hash[primary_key]&.type == :integer
27
32
  end
28
33
  @integral_id
29
34
  end
30
35
 
31
- def transaction(*args)
36
+ def transaction(**)
32
37
  if self != ::ActiveRecord::Base && current_scope
33
38
  current_scope.activate do
34
39
  db = Shard.current(shard_category).database_server
35
- if ::Shackles.environment != db.shackles_environment
36
- db.unshackle { super }
40
+ if ::GuardRail.environment != db.guard_rail_environment
41
+ db.unguard { super }
37
42
  else
38
43
  super
39
44
  end
40
45
  end
41
46
  else
42
47
  db = Shard.current(shard_category).database_server
43
- if ::Shackles.environment != db.shackles_environment
44
- db.unshackle { super }
48
+ if ::GuardRail.environment != db.guard_rail_environment
49
+ db.unguard { super }
45
50
  else
46
51
  super
47
52
  end
@@ -65,6 +70,14 @@ module Switchman
65
70
  result
66
71
  end
67
72
  end
73
+
74
+ def clear_query_caches_for_current_thread
75
+ ::ActiveRecord::Base.connection_handlers.each_value do |handler|
76
+ handler.connection_pool_list.each do |pool|
77
+ pool.connection(switch_shard: false).clear_query_cache if pool.active_connection?
78
+ end
79
+ end
80
+ end
68
81
  end
69
82
 
70
83
  def self.included(klass)
@@ -94,22 +107,18 @@ module Switchman
94
107
  end
95
108
  end
96
109
 
97
- def scope_class
98
- self.class.base_class
99
- end
100
-
101
- def save(*args)
110
+ def save(*, **)
102
111
  @shard_set_in_stone = true
103
- scope_class.shard(shard, :implicit).scoping { super }
112
+ (self.class.current_scope || self.class.default_scoped).shard(shard, :implicit).scoping { super }
104
113
  end
105
114
 
106
- def save!(*args)
115
+ def save!(*, **)
107
116
  @shard_set_in_stone = true
108
- scope_class.shard(shard, :implicit).scoping { super }
117
+ (self.class.current_scope || self.class.default_scoped).shard(shard, :implicit).scoping { super }
109
118
  end
110
119
 
111
120
  def destroy
112
- scope_class.shard(shard, :implicit).scoping { super }
121
+ self.class.shard(shard, :implicit).scoping { super }
113
122
  end
114
123
 
115
124
  def clone
@@ -121,14 +130,14 @@ module Switchman
121
130
  result
122
131
  end
123
132
 
124
- def transaction(options={}, &block)
133
+ def transaction(**kwargs, &block)
125
134
  shard.activate(self.class.shard_category) do
126
- self.class.transaction(options, &block)
135
+ self.class.transaction(**kwargs, &block)
127
136
  end
128
137
  end
129
138
 
130
139
  def hash
131
- self.class.sharded_primary_key? ? Shard.global_id_for(id).hash : super
140
+ self.class.sharded_primary_key? ? self.class.hash ^ Shard.global_id_for(id).hash : super
132
141
  end
133
142
 
134
143
  def to_param
@@ -141,6 +150,42 @@ module Switchman
141
150
  @shard_set_in_stone = false
142
151
  copy
143
152
  end
153
+
154
+ def quoted_id
155
+ return super unless self.class.sharded_primary_key?
156
+ # do this the Rails 4.2 way, so that if Shard.current != self.shard, the id gets transposed
157
+ self.class.connection.quote(id)
158
+ end
159
+
160
+ def update_columns(*)
161
+ db = Shard.current(self.class.shard_category).database_server
162
+ if ::GuardRail.environment != db.guard_rail_environment
163
+ return db.unguard { super }
164
+ else
165
+ super
166
+ end
167
+ end
168
+
169
+ protected
170
+
171
+ # see also AttributeMethods#shard_category_code_for_reflection
172
+ def shard_category_for_reflection(reflection)
173
+ if reflection
174
+ if reflection.options[:polymorphic]
175
+ begin
176
+ read_attribute(reflection.foreign_type)&.constantize&.shard_category || :primary
177
+ rescue NameError
178
+ # in case someone is abusing foreign_type to not point to an actual class
179
+ :primary
180
+ end
181
+ else
182
+ # otherwise we can just return a symbol for the statically known type of the association
183
+ reflection.klass.shard_category
184
+ end
185
+ else
186
+ shard_category
187
+ end
188
+ end
144
189
  end
145
190
  end
146
191
  end
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Switchman
2
4
  module ActiveRecord
3
5
  module Batches
4
6
  def batch_order
5
- "#{connection.quote_local_table_name(table_name)}.#{quoted_primary_key} ASC"
7
+ ::Arel.sql("#{connection.quote_local_table_name(table_name)}.#{quoted_primary_key} ASC")
6
8
  end
7
9
  end
8
10
  end
@@ -1,16 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Switchman
2
4
  module ActiveRecord
3
5
  module Calculations
4
- CALL_SUPER = Object.new.freeze
5
- private_constant :CALL_SUPER
6
6
 
7
7
  def pluck(*column_names)
8
- return super(*column_names[1..-1]) if column_names.first.equal?(CALL_SUPER)
9
8
  target_shard = Shard.current(klass.shard_category)
10
9
  shard_count = 0
11
10
  result = self.activate do |relation, shard|
12
11
  shard_count += 1
13
- results = relation.pluck(CALL_SUPER, *column_names)
12
+ results = relation.call_super(:pluck, Calculations, *column_names)
14
13
  if column_names.length > 1
15
14
  column_names.each_with_index do |column_name, idx|
16
15
  if klass.sharded_column?(column_name)
@@ -30,13 +29,12 @@ module Switchman
30
29
  result
31
30
  end
32
31
 
33
- def execute_simple_calculation(operation, column_name, distinct, super_method: false)
34
- return super(operation, column_name, distinct) if super_method
32
+ def execute_simple_calculation(operation, column_name, distinct)
35
33
  operation = operation.to_s.downcase
36
34
  if operation == "average"
37
35
  result = calculate_simple_average(column_name, distinct)
38
36
  else
39
- result = self.activate{ |relation| relation.send(:execute_simple_calculation, operation, column_name, distinct, super_method: true) }
37
+ result = self.activate{ |relation| relation.call_super(:execute_simple_calculation, Calculations, operation, column_name, distinct) }
40
38
  if result.is_a?(Array)
41
39
  case operation
42
40
  when "count", "sum"
@@ -61,12 +59,12 @@ module Switchman
61
59
  initial_results = relation.activate{ |rel| klass.connection.select_all(rel) }
62
60
  if initial_results.is_a?(Array)
63
61
  initial_results.each do |r|
64
- r["average"] = type_cast_calculated_value(r["average"], column_or_type_for(column_name), "average")
65
- r["count"] = type_cast_calculated_value(r["count"], column_or_type_for(column_name), "count")
62
+ r["average"] = type_cast_calculated_value(r["average"], type_for(column_name), "average")
63
+ r["count"] = type_cast_calculated_value(r["count"], type_for(column_name), "count")
66
64
  end
67
65
  result = initial_results.map{|r| r["average"] * r["count"]}.sum / initial_results.map{|r| r["count"]}.sum
68
66
  else
69
- result = type_cast_calculated_value(initial_results.first["average"], column_or_type_for(column_name), "average")
67
+ result = type_cast_calculated_value(initial_results.first["average"], type_for(column_name), "average")
70
68
  end
71
69
  result
72
70
  end
@@ -76,7 +74,7 @@ module Switchman
76
74
  opts = grouped_calculation_options(operation.to_s.downcase, column_name, distinct)
77
75
 
78
76
  relation = build_grouped_calculation_relation(opts)
79
- target_shard = Shard.current(:default)
77
+ target_shard = Shard.current(:primary)
80
78
 
81
79
  rows = relation.activate do |rel, shard|
82
80
  calculated_data = klass.connection.select_all(rel)
@@ -89,14 +87,14 @@ module Switchman
89
87
 
90
88
  calculated_data.map do |row|
91
89
  row[opts[:aggregate_alias]] = type_cast_calculated_value(
92
- row[opts[:aggregate_alias]], column_or_type_for(opts[:column_name]), opts[:operation])
90
+ row[opts[:aggregate_alias]], type_for(opts[:column_name]), opts[:operation])
93
91
  row['count'] = row['count'].to_i if opts[:operation] == 'average'
94
92
 
95
- opts[:group_columns].each do |aliaz, column_or_type, column_name|
93
+ opts[:group_columns].each do |aliaz, type, column_name|
96
94
  if opts[:associated] && (aliaz == opts[:group_aliases].first)
97
95
  row[aliaz] = key_records[Shard.relative_id_for(row[aliaz], shard, target_shard)]
98
96
  elsif column_name && @klass.sharded_column?(column_name)
99
- row[aliaz] = Shard.relative_id_for(type_cast_calculated_value(row[aliaz], column_or_type), shard, target_shard)
97
+ row[aliaz] = Shard.relative_id_for(type_cast_calculated_value(row[aliaz], type), shard, target_shard)
100
98
  end
101
99
  end
102
100
  row
@@ -112,10 +110,6 @@ module Switchman
112
110
  field.respond_to?(:name) ? field.name.to_s : field.to_s.split('.').last
113
111
  end
114
112
 
115
- def column_or_type_for(field)
116
- ::Rails.version < '4.2' ? column_for(field) : type_for(field)
117
- end
118
-
119
113
  def grouped_calculation_options(operation, column_name, distinct)
120
114
  opts = {:operation => operation, :column_name => column_name, :distinct => distinct}
121
115
 
@@ -129,9 +123,10 @@ module Switchman
129
123
  group_fields = group_attrs
130
124
  end
131
125
 
132
- group_aliases = group_fields.map { |field| column_alias_for(field) }
126
+ # to_s is because Rails 5 returns a string but Rails 6 returns a symbol.
127
+ group_aliases = group_fields.map { |field| column_alias_for(field.downcase.to_s).to_s }
133
128
  group_columns = group_aliases.zip(group_fields).map { |aliaz, field|
134
- [aliaz, column_or_type_for(field), column_name_for(field)]
129
+ [aliaz, type_for(field), column_name_for(field)]
135
130
  }
136
131
  opts.merge!(:association => association, :associated => associated,
137
132
  :group_aliases => group_aliases, :group_columns => group_columns,
@@ -167,7 +162,7 @@ module Switchman
167
162
  'count', opts[:distinct]).as('count')
168
163
  end
169
164
 
170
- haves = ::Rails.version >= "5" ? having_clause.send(:predicates) : having_values
165
+ haves = having_clause.send(:predicates)
171
166
  select_values += select_values unless haves.empty?
172
167
  select_values.concat opts[:group_fields].zip(opts[:group_aliases]).map { |field,aliaz|
173
168
  if field.respond_to?(:as)