switchman 3.0.1 → 3.0.2

Sign up to get free protection for your applications and to get access to all the features.
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