switchman 3.0.1 → 3.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8cfc736ed07b0e8b80b62928efb9e523a6e8e6ab31841ef4f7022ffbba7a7b57
4
- data.tar.gz: 1879cbef2d1e8ab2d451fcf31ebe3b0a37c10e27eb1099ca96e73f58446adfae
3
+ metadata.gz: 1224320ec4f3ff0d8f05d1c5537ce06f33932e4c9da069d82183aa7562e1cab4
4
+ data.tar.gz: 1cd6f52c82fbdea9c3aac4c569b291428d2476abc4a2198c09beb1da64700473
5
5
  SHA512:
6
- metadata.gz: c82b93120355776072460d8e2ec1a648ed76454ed178ee3a102b66303dc9716b23bdea855a8047995c454b2528bbe580ceccba43a36edc4afb2d4458e984b493
7
- data.tar.gz: a97fb461a01e06ad8498635382d316ca84e0830a54984e45ce4c1cb575c3ff11e121185c67618246d175ff61eee2c1931acc5b48876235d65b6802a349621077
6
+ metadata.gz: 052e1675cb5d6b07844dab24c802c6f98b8e75fe4c0146a407e275d89633b5c3058801538a2537f82a7798260ad9d5dd2989536ad88d06a37bb33cf9082444dd
7
+ data.tar.gz: ebaa91ea8f6be5f878ac2f8b57a7954853603cb20770f083263fd369774cedc4b33c8127a8b17746bbf4e5b91e1e742df2970be8dab08aa708baae3bc130b988
data/lib/switchman.rb CHANGED
@@ -17,4 +17,6 @@ module Switchman
17
17
  def self.cache=(cache)
18
18
  @cache = cache
19
19
  end
20
+
21
+ class OrderOnMultiShardQuery < RuntimeError; end
20
22
  end
@@ -85,7 +85,7 @@ module Switchman
85
85
  # Copypasta from Activerecord but with added global_id_for goodness.
86
86
  def records_for(ids)
87
87
  scope.where(association_key_name => ids).load do |record|
88
- global_key = if record.class.connection_classes == UnshardedRecord
88
+ global_key = if model.connection_classes == UnshardedRecord
89
89
  convert_key(record[association_key_name])
90
90
  else
91
91
  Shard.global_id_for(record[association_key_name], record.shard)
@@ -50,7 +50,7 @@ module Switchman
50
50
 
51
51
  def calculate_simple_average(column_name, distinct)
52
52
  # See activerecord#execute_simple_calculation
53
- relation = reorder(nil)
53
+ relation = except(:order)
54
54
  column = aggregate_column(column_name)
55
55
  relation.select_values = [operation_over_aggregate_column(column, 'average', distinct).as('average'),
56
56
  operation_over_aggregate_column(column, 'count', distinct).as('count')]
@@ -281,7 +281,7 @@ module Switchman
281
281
  remove = true if type == :primary &&
282
282
  remove_nonlocal_primary_keys &&
283
283
  predicate.left.relation.klass == klass &&
284
- predicate.is_a?(::Arel::Nodes::Equality)
284
+ (predicate.is_a?(::Arel::Nodes::Equality) || predicate.is_a?(::Arel::Nodes::HomogeneousIn))
285
285
 
286
286
  current_source_shard =
287
287
  if source_shard
@@ -301,7 +301,7 @@ module Switchman
301
301
  new_right_value =
302
302
  case right
303
303
  when Array
304
- right.map { |val| transpose_predicate_value(val, current_source_shard, target_shard, type, remove) }
304
+ right.map { |val| transpose_predicate_value(val, current_source_shard, target_shard, type, remove).presence }.compact
305
305
  else
306
306
  transpose_predicate_value(right, current_source_shard, target_shard, type, remove)
307
307
  end
@@ -315,7 +315,13 @@ module Switchman
315
315
  predicate.class.new(predicate.left, right.class.new(new_right_value, right.attribute))
316
316
  end
317
317
  elsif predicate.is_a?(::Arel::Nodes::HomogeneousIn)
318
- predicate.class.new(new_right_value, predicate.attribute, predicate.type)
318
+ # switch to a regular In, so that Relation::WhereClause#contradiction? knows about it
319
+ if new_right_value.empty?
320
+ klass = predicate.type == :in ? ::Arel::Nodes::In : ::Arel::Nodes::NotIn
321
+ klass.new(predicate.attribute, new_right_value)
322
+ else
323
+ predicate.class.new(new_right_value, predicate.attribute, predicate.type)
324
+ end
319
325
  else
320
326
  predicate.class.new(predicate.left, new_right_value)
321
327
  end
@@ -112,10 +112,57 @@ module Switchman
112
112
  shards.first.activate(klass.connection_classes) { yield(self, shards.first) }
113
113
  end
114
114
  else
115
- # TODO: implement local limit to avoid querying extra shards
116
- Shard.with_each_shard(shards, [klass.connection_classes]) do
117
- shard(Shard.current(klass.connection_classes), :to_a).activate(&block)
115
+ result_count = 0
116
+ can_order = false
117
+ result = Shard.with_each_shard(shards, [klass.connection_classes]) do
118
+ # don't even query other shards if we're already past the limit
119
+ next if limit_value && result_count >= limit_value && order_values.empty?
120
+
121
+ relation = shard(Shard.current(klass.connection_classes), :to_a)
122
+ # do a minimal query if possible
123
+ relation = relation.limit(limit_value - result_count) if limit_value && !result_count.zero? && order_values.empty?
124
+
125
+ shard_results = relation.activate(&block)
126
+
127
+ if shard_results.present?
128
+ can_order ||= can_order_cross_shard_results? unless order_values.empty?
129
+ raise OrderOnMultiShardQuery if !can_order && !order_values.empty? && result_count.positive?
130
+
131
+ result_count += shard_results.is_a?(Array) ? shard_results.length : 1
132
+ end
133
+ shard_results
134
+ end
135
+
136
+ result = reorder_cross_shard_results(result) if can_order
137
+ result.slice!(limit_value..-1) if limit_value
138
+ result
139
+ end
140
+ end
141
+
142
+ def can_order_cross_shard_results?
143
+ # we only presume to be able to post-sort the most basic of orderings
144
+ order_values.all? { |ov| ov.is_a?(::Arel::Nodes::Ordering) && ov.expr.is_a?(::Arel::Attributes::Attribute) }
145
+ end
146
+
147
+ def reorder_cross_shard_results(results)
148
+ results.sort! do |l, r|
149
+ result = 0
150
+ order_values.each do |ov|
151
+ a = l.attribute(ov.expr.name)
152
+ b = r.attribute(ov.expr.name)
153
+ next if a == b
154
+
155
+ if a.nil? || b.nil?
156
+ result = 1 if a.nil?
157
+ result *= -1 if ov.is_a?(::Arel::Nodes::Descending)
158
+ else
159
+ result = a <=> b
160
+ end
161
+
162
+ result *= -1 if ov.is_a?(::Arel::Nodes::Descending)
163
+ break unless result.zero?
118
164
  end
165
+ result
119
166
  end
120
167
  end
121
168
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Switchman
4
- VERSION = '3.0.1'
4
+ VERSION = '3.0.2'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: switchman
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.1
4
+ version: 3.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cody Cutrer
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2021-05-24 00:00:00.000000000 Z
13
+ date: 2021-06-09 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord