switchman 3.3.7 → 3.4.1
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 +4 -4
- data/lib/switchman/active_record/associations.rb +1 -1
- data/lib/switchman/active_record/attribute_methods.rb +4 -6
- data/lib/switchman/active_record/base.rb +41 -3
- data/lib/switchman/active_record/persistence.rb +8 -0
- data/lib/switchman/active_record/relation.rb +1 -1
- data/lib/switchman/errors.rb +10 -0
- data/lib/switchman/parallel.rb +1 -1
- data/lib/switchman/version.rb +1 -1
- data/lib/tasks/switchman.rake +5 -5
- metadata +16 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e080637567925bab42e7d6c87a2963486b16fb5a09eab87ef61fd8056d773ddf
|
|
4
|
+
data.tar.gz: 2694d7353b3cb7df6976aa812897540f1a9fc2188c5a6d2cc15bebfbbabb1371
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b3318e8323ebda712ad41873279793ddd5b0d5849a536c9f2dc0fcd3addefb35ff4d364882f27eb59d0c8e27e7c5d79e5c5edc6de66a73024746f79fed23170f
|
|
7
|
+
data.tar.gz: a2d2a1b1682febaffe2856dbdee8eca77241d5781e60fe037677f39c5c77b30d4d4ec5d6b0ff5e8c6cdd947fa79171fc99f0b19ffc3a5a77185bb400069ab537
|
|
@@ -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.
|
|
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 =
|
|
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
|
|
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,
|
|
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}),
|
|
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,53 @@ module Switchman
|
|
|
169
171
|
end
|
|
170
172
|
end
|
|
171
173
|
|
|
174
|
+
def destroy_shadow_records(target_shards: [Shard.current])
|
|
175
|
+
raise Errors::ShadowRecordError, 'Cannot be called on a shadow record.' if shadow_record?
|
|
176
|
+
raise Errors::MethodUnsupportedForUnshardedTableError, 'Cannot be called on a record belonging to an unsharded table.' unless self.class.sharded_column?(self.class.primary_key)
|
|
177
|
+
|
|
178
|
+
Array(target_shards).each do |target_shard|
|
|
179
|
+
next if target_shard == shard
|
|
180
|
+
|
|
181
|
+
target_shard.activate { self.class.where('id = ?', global_id).delete_all }
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Returns "the shard that this record was actually loaded from" , as
|
|
186
|
+
# opposed to "the shard this record belongs on", which might be
|
|
187
|
+
# different if this is a shadow record.
|
|
188
|
+
def loaded_from_shard
|
|
189
|
+
@loaded_from_shard || fallback_shard
|
|
190
|
+
end
|
|
191
|
+
|
|
172
192
|
def shard
|
|
173
|
-
@shard ||
|
|
193
|
+
@shard || fallback_shard
|
|
174
194
|
end
|
|
175
195
|
|
|
176
196
|
def shard=(new_shard)
|
|
177
197
|
raise ::ActiveRecord::ReadOnlyRecord if !new_record? || @shard_set_in_stone
|
|
178
198
|
|
|
179
|
-
|
|
199
|
+
if shard == new_shard
|
|
200
|
+
@loaded_from_shard = new_shard
|
|
201
|
+
return
|
|
202
|
+
end
|
|
180
203
|
|
|
181
204
|
attributes.each do |attr, value|
|
|
182
205
|
self[attr] = Shard.relative_id_for(value, shard, new_shard) if self.class.sharded_column?(attr)
|
|
183
206
|
end
|
|
207
|
+
@loaded_from_shard = new_shard
|
|
184
208
|
@shard = new_shard
|
|
185
209
|
end
|
|
186
210
|
|
|
187
211
|
def save(*, **)
|
|
212
|
+
raise Errors::ManuallyCreatedShadowRecordError if creating_shadow_record?
|
|
213
|
+
|
|
188
214
|
@shard_set_in_stone = true
|
|
189
215
|
super
|
|
190
216
|
end
|
|
191
217
|
|
|
192
218
|
def save!(*, **)
|
|
219
|
+
raise Errors::ManuallyCreatedShadowRecordError if creating_shadow_record?
|
|
220
|
+
|
|
193
221
|
@shard_set_in_stone = true
|
|
194
222
|
super
|
|
195
223
|
end
|
|
@@ -221,7 +249,7 @@ module Switchman
|
|
|
221
249
|
end
|
|
222
250
|
|
|
223
251
|
def hash
|
|
224
|
-
self.class.sharded_primary_key? ? self.class
|
|
252
|
+
self.class.sharded_primary_key? ? [self.class, global_id].hash : super
|
|
225
253
|
end
|
|
226
254
|
|
|
227
255
|
def to_param
|
|
@@ -270,6 +298,16 @@ module Switchman
|
|
|
270
298
|
self.class.connection_class_for_self
|
|
271
299
|
end
|
|
272
300
|
end
|
|
301
|
+
|
|
302
|
+
private
|
|
303
|
+
|
|
304
|
+
def fallback_shard
|
|
305
|
+
Shard.current(self.class.connection_class_for_self) || Shard.default
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def creating_shadow_record?
|
|
309
|
+
new_record? && shadow_record?
|
|
310
|
+
end
|
|
273
311
|
end
|
|
274
312
|
end
|
|
275
313
|
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
|
data/lib/switchman/errors.rb
CHANGED
|
@@ -2,8 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
module Switchman
|
|
4
4
|
module Errors
|
|
5
|
+
class ManuallyCreatedShadowRecordError < RuntimeError
|
|
6
|
+
def initialize(msg = "It looks like you're trying to manually create a shadow record. Please use Switchman::ActiveRecord::Base#save_shadow_record instead.")
|
|
7
|
+
super
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
5
11
|
class NonExistentShardError < RuntimeError; end
|
|
6
12
|
|
|
7
13
|
class ParallelShardExecError < RuntimeError; end
|
|
14
|
+
|
|
15
|
+
class ShadowRecordError < RuntimeError; end
|
|
16
|
+
|
|
17
|
+
class UnshardedTableError < RuntimeError; end
|
|
8
18
|
end
|
|
9
19
|
end
|
data/lib/switchman/parallel.rb
CHANGED
data/lib/switchman/version.rb
CHANGED
data/lib/tasks/switchman.rake
CHANGED
|
@@ -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 =
|
|
7
|
-
|
|
8
|
-
|
|
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
|
|
23
|
-
shard: ENV
|
|
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.
|
|
4
|
+
version: 3.4.1
|
|
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-
|
|
13
|
+
date: 2023-04-04 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
|