switchman 3.3.7 → 3.4.0

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: 07ef0eeff511916fa7913f453f2b3cde7d5317dfee9e03b58cf3645b2830c845
4
- data.tar.gz: 2c2c12374387e6856674f14cf965087a0d27c1ccdacc6470b69aec0eb77af22f
3
+ metadata.gz: be3660461c9c52c63b304de0bb3e5a18e3fe3cdfe7d5f61802b2db2b232f97be
4
+ data.tar.gz: 47248f2cb8c6c35396d537e7bd1f938399811d11eb1a07a96778654ccf5dc3f9
5
5
  SHA512:
6
- metadata.gz: d62c36fe017fbf801312eb13295e19fbb66c5e9b2aa23996400c3654acca130525580728602328377f4b601d5a9ba09847ef6082948f29af7d1217d7471ffa70
7
- data.tar.gz: b6afb2bd1e0f69d93dd7b1e7a739cd7a1b6896eee43be063f8452d102de21991f8ab49da3fecc65859e24d66dd54e19f49c6352e22a50dcbd1add00518f154c0
6
+ metadata.gz: 60d5b3eb13458b61bbdc8d2a729e10f870284d35fa0694612d793ed8ff9fd15667777dda2e4d28e4fb435a707aff0ca483e51032b396d7496b5d3792e6d6be16
7
+ data.tar.gz: fa52c0f4ddf8fea8602917d64f165602ee14ffb9ea72f609a7f9d11e2ac511b5ba3cae9a8ae9915791c268adc0c9f9ef12f142817d0e472bf1c3a17b1f3e262c
@@ -50,7 +50,7 @@ module Switchman
50
50
  def shard
51
51
  if @owner.class.sharded_column?(@reflection.foreign_key) &&
52
52
  (foreign_id = @owner[@reflection.foreign_key])
53
- Shard.shard_for(foreign_id, @owner.shard)
53
+ Shard.shard_for(foreign_id, @owner.loaded_from_shard)
54
54
  else
55
55
  super
56
56
  end
@@ -46,7 +46,6 @@ module Switchman
46
46
  raise if connection.open_transactions.positive?
47
47
  end
48
48
 
49
- # rubocop:disable Naming/MethodParameterName
50
49
  def define_cached_method(owner, name, namespace:, as:, &block)
51
50
  if ::Rails.version < '7.0'
52
51
  yield owner
@@ -55,7 +54,6 @@ module Switchman
55
54
  owner.define_cached_method(name, namespace: namespace, as: as, &block)
56
55
  end
57
56
  end
58
- # rubocop:enable Naming/MethodParameterName
59
57
 
60
58
  def define_method_global_attribute(attr_name, owner:)
61
59
  if sharded_column?(attr_name)
@@ -136,7 +134,7 @@ module Switchman
136
134
 
137
135
  abs_raw_value = raw_value.abs
138
136
  current_shard = ::Switchman::Shard.current(#{attr_connection_class})
139
- same_shard = shard == current_shard
137
+ same_shard = loaded_from_shard == current_shard
140
138
  return raw_value if same_shard && abs_raw_value < ::Switchman::Shard::IDS_PER_SHARD
141
139
 
142
140
  value_shard_id = abs_raw_value / ::Switchman::Shard::IDS_PER_SHARD
@@ -144,9 +142,9 @@ module Switchman
144
142
  # of a local id
145
143
  return raw_value % ::Switchman::Shard::IDS_PER_SHARD if value_shard_id == current_shard.id
146
144
  return raw_value if !same_shard && abs_raw_value > ::Switchman::Shard::IDS_PER_SHARD
147
- return shard.global_id_for(raw_value) if !same_shard && abs_raw_value < ::Switchman::Shard::IDS_PER_SHARD
145
+ return loaded_from_shard.global_id_for(raw_value) if !same_shard && abs_raw_value < ::Switchman::Shard::IDS_PER_SHARD
148
146
 
149
- ::Switchman::Shard.relative_id_for(raw_value, shard, current_shard)
147
+ ::Switchman::Shard.relative_id_for(raw_value, loaded_from_shard, current_shard)
150
148
  end
151
149
  RUBY
152
150
  end
@@ -173,7 +171,7 @@ module Switchman
173
171
  def build_sharded_setter(attr_name, attr_field, attr_connection_class)
174
172
  <<-RUBY
175
173
  def #{attr_name}=(new_value)
176
- self.original_#{attr_field} = ::Switchman::Shard.relative_id_for(new_value, ::Switchman::Shard.current(#{attr_connection_class}), shard)
174
+ self.original_#{attr_field} = ::Switchman::Shard.relative_id_for(new_value, ::Switchman::Shard.current(#{attr_connection_class}), loaded_from_shard)
177
175
  end
178
176
  RUBY
179
177
  end
@@ -142,6 +142,8 @@ module Switchman
142
142
  else
143
143
  Shard.current(self.class.connection_class_for_self)
144
144
  end
145
+
146
+ @loaded_from_shard ||= Shard.current(self.class.connection_class_for_self)
145
147
  readonly! if shadow_record? && !Switchman.config[:writable_shadow_records]
146
148
  super
147
149
  end
@@ -169,27 +171,42 @@ module Switchman
169
171
  end
170
172
  end
171
173
 
174
+ # Returns "the shard that this record was actually loaded from" , as
175
+ # opposed to "the shard this record belongs on", which might be
176
+ # different if this is a shadow record.
177
+ def loaded_from_shard
178
+ @loaded_from_shard || fallback_shard
179
+ end
180
+
172
181
  def shard
173
- @shard || Shard.current(self.class.connection_class_for_self) || Shard.default
182
+ @shard || fallback_shard
174
183
  end
175
184
 
176
185
  def shard=(new_shard)
177
186
  raise ::ActiveRecord::ReadOnlyRecord if !new_record? || @shard_set_in_stone
178
187
 
179
- return if shard == new_shard
188
+ if shard == new_shard
189
+ @loaded_from_shard = new_shard
190
+ return
191
+ end
180
192
 
181
193
  attributes.each do |attr, value|
182
194
  self[attr] = Shard.relative_id_for(value, shard, new_shard) if self.class.sharded_column?(attr)
183
195
  end
196
+ @loaded_from_shard = new_shard
184
197
  @shard = new_shard
185
198
  end
186
199
 
187
200
  def save(*, **)
201
+ raise Errors::ManuallyCreatedShadowRecordError if creating_shadow_record?
202
+
188
203
  @shard_set_in_stone = true
189
204
  super
190
205
  end
191
206
 
192
207
  def save!(*, **)
208
+ raise Errors::ManuallyCreatedShadowRecordError if creating_shadow_record?
209
+
193
210
  @shard_set_in_stone = true
194
211
  super
195
212
  end
@@ -221,7 +238,7 @@ module Switchman
221
238
  end
222
239
 
223
240
  def hash
224
- self.class.sharded_primary_key? ? self.class.hash ^ global_id.hash : super
241
+ self.class.sharded_primary_key? ? [self.class, global_id].hash : super
225
242
  end
226
243
 
227
244
  def to_param
@@ -270,6 +287,16 @@ module Switchman
270
287
  self.class.connection_class_for_self
271
288
  end
272
289
  end
290
+
291
+ private
292
+
293
+ def fallback_shard
294
+ Shard.current(self.class.connection_class_for_self) || Shard.default
295
+ end
296
+
297
+ def creating_shadow_record?
298
+ new_record? && shadow_record?
299
+ end
273
300
  end
274
301
  end
275
302
  end
@@ -16,6 +16,14 @@ module Switchman
16
16
  db = shard.database_server
17
17
  db.unguard { super }
18
18
  end
19
+
20
+ def reload(*)
21
+ res = super
22
+ # When a shadow record is reloaded the real record is returned. So
23
+ # we need to ensure the loaded_from_shard is set correctly after a reload.
24
+ @loaded_from_shard = @shard
25
+ res
26
+ end
19
27
  end
20
28
  end
21
29
  end
@@ -4,7 +4,7 @@ module Switchman
4
4
  module ActiveRecord
5
5
  module Relation
6
6
  def self.prepended(klass)
7
- klass::SINGLE_VALUE_METHODS.concat %i[shard shard_source]
7
+ klass::SINGLE_VALUE_METHODS.push(:shard, :shard_source)
8
8
  end
9
9
 
10
10
  def initialize(*, **)
@@ -5,5 +5,11 @@ module Switchman
5
5
  class NonExistentShardError < RuntimeError; end
6
6
 
7
7
  class ParallelShardExecError < RuntimeError; end
8
+
9
+ class ManuallyCreatedShadowRecordError < RuntimeError
10
+ def initialize(msg = "It looks like you're trying to manually create a shadow record. Please use Switchman::ActiveRecord::Base#save_shadow_record instead.")
11
+ super
12
+ end
13
+ end
8
14
  end
9
15
  end
@@ -65,4 +65,4 @@ module Switchman
65
65
  end
66
66
  end
67
67
 
68
- ::Parallel::UndumpableException.prepend(::Switchman::Parallel::UndumpableException)
68
+ Parallel::UndumpableException.prepend(Switchman::Parallel::UndumpableException)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Switchman
4
- VERSION = '3.3.7'
4
+ VERSION = '3.4.0'
5
5
  end
@@ -3,9 +3,9 @@
3
3
  # In rails 7.0+ if you have only 1 db in the env it doesn't try to do explicit activation
4
4
  # (and for rails purposes we only have one db per env because each database server is a separate env)
5
5
  if Rails.version < '7.0'
6
- task_prefix = ::Rake::Task.task_defined?('app:db:migrate') ? 'app:db' : 'db'
7
- ::Rake::Task["#{task_prefix}:migrate"].clear_actions.enhance do
8
- ::ActiveRecord::Tasks::DatabaseTasks.migrate
6
+ task_prefix = Rake::Task.task_defined?('app:db:migrate') ? 'app:db' : 'db'
7
+ Rake::Task["#{task_prefix}:migrate"].clear_actions.enhance do
8
+ ActiveRecord::Tasks::DatabaseTasks.migrate
9
9
  # Ensure this doesn't blow up when running inside the dummy app
10
10
  Rake::Task["#{task_prefix}:_dump"].invoke
11
11
  end
@@ -19,8 +19,8 @@ module Switchman
19
19
  end
20
20
 
21
21
  def self.scope(base_scope = Shard,
22
- database_server: ENV['DATABASE_SERVER'],
23
- shard: ENV['SHARD'])
22
+ database_server: ENV.fetch('DATABASE_SERVER', nil),
23
+ shard: ENV.fetch('SHARD', nil))
24
24
  servers = DatabaseServer.all
25
25
 
26
26
  if database_server
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.3.7
4
+ version: 3.4.0
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: 2023-03-22 00:00:00.000000000 Z
13
+ date: 2023-03-31 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord
@@ -192,6 +192,20 @@ dependencies:
192
192
  - - "~>"
193
193
  - !ruby/object:Gem::Version
194
194
  version: '1.10'
195
+ - !ruby/object:Gem::Dependency
196
+ name: rubocop-performance
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - "~>"
200
+ - !ruby/object:Gem::Version
201
+ version: '1.16'
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - "~>"
207
+ - !ruby/object:Gem::Version
208
+ version: '1.16'
195
209
  - !ruby/object:Gem::Dependency
196
210
  name: rubocop-rake
197
211
  requirement: !ruby/object:Gem::Requirement