switchman 3.1.0 → 3.2.0
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 +77 -9
- data/lib/switchman/active_record/attribute_methods.rb +1 -15
- data/lib/switchman/active_record/base.rb +37 -9
- data/lib/switchman/active_record/migration.rb +8 -6
- data/lib/switchman/engine.rb +1 -1
- data/lib/switchman/version.rb +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 39d8ce1bd79211b12278263fda3f0a88adc841051bd1e4f52e8d01ccd371978f
|
|
4
|
+
data.tar.gz: b126fe1b7b700fb424351d901c58b66f9719892deab08a2242de6cf6c5645b77
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 266b29135322d76e811feb72414a5c3a1621afe07e65175672092ddefd21296b73fa3fe3ce1b6ce0d34b2e5d1a071b186f0b859b44398ff65e3a19cc3e747830
|
|
7
|
+
data.tar.gz: e26ecce87b654d3dd8ae8caa3111b9cbbc89896ce00cca467ba4bd66527ddf76c2e0db1c4ebff30cd133dbcefc1580f4f6b2dc745ab795813cf85848893badca
|
|
@@ -83,16 +83,62 @@ module Switchman
|
|
|
83
83
|
|
|
84
84
|
module Preloader
|
|
85
85
|
module Association
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
86
|
+
# significant changes:
|
|
87
|
+
# * associate shards with records
|
|
88
|
+
# * look on all appropriate shards when loading records
|
|
89
|
+
module LoaderRecords
|
|
90
|
+
def populate_keys_to_load_and_already_loaded_records
|
|
91
|
+
@sharded_keys_to_load = {}
|
|
91
92
|
|
|
92
93
|
loaders.each do |loader|
|
|
93
|
-
loader.
|
|
94
|
-
loader.
|
|
94
|
+
multishard = loader.send(:reflection).options[:multishard]
|
|
95
|
+
belongs_to = loader.send(:reflection).macro == :belongs_to
|
|
96
|
+
loader.owners_by_key.each do |key, owners|
|
|
97
|
+
if (loaded_owner = owners.find { |owner| loader.loaded?(owner) })
|
|
98
|
+
already_loaded_records_by_key[key] = loader.target_for(loaded_owner)
|
|
99
|
+
else
|
|
100
|
+
shard_set = @sharded_keys_to_load[key] ||= Set.new
|
|
101
|
+
owner_key_name = loader.send(:owner_key_name)
|
|
102
|
+
owners.each do |owner|
|
|
103
|
+
if multishard && owner.respond_to?(:associated_shards)
|
|
104
|
+
shard_set.merge(owner.associated_shards.map(&:id))
|
|
105
|
+
elsif belongs_to && owner.class.sharded_column?(owner_key_name)
|
|
106
|
+
shard_set.add(Shard.shard_for(owner[owner_key_name], owner.shard).id)
|
|
107
|
+
elsif belongs_to
|
|
108
|
+
shard_set.add(Shard.current.id)
|
|
109
|
+
else
|
|
110
|
+
shard_set.add(owner.shard.id)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
@sharded_keys_to_load.delete_if { |key, _shards| already_loaded_records_by_key.include?(key) }
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def load_records
|
|
121
|
+
ret = []
|
|
122
|
+
|
|
123
|
+
shards_with_keys = @sharded_keys_to_load.each_with_object({}) do |(key, shards), h|
|
|
124
|
+
shards.each { |shard| (h[shard] ||= []) << key }
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
shards_with_keys.each do |shard, keys|
|
|
128
|
+
Shard.lookup(shard).activate do
|
|
129
|
+
scope_was = loader_query.scope
|
|
130
|
+
begin
|
|
131
|
+
loader_query.instance_variable_set(:@scope, loader_query.scope.shard(Shard.current(loader_query.scope.model.connection_class_for_self)))
|
|
132
|
+
ret += loader_query.load_records_for_keys(keys) do |record|
|
|
133
|
+
loaders.each { |l| l.set_inverse(record) }
|
|
134
|
+
end
|
|
135
|
+
ensure
|
|
136
|
+
loader_query.instance_variable_set(:@scope, scope_was)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
95
139
|
end
|
|
140
|
+
|
|
141
|
+
ret
|
|
96
142
|
end
|
|
97
143
|
end
|
|
98
144
|
|
|
@@ -110,6 +156,26 @@ module Switchman
|
|
|
110
156
|
end
|
|
111
157
|
end
|
|
112
158
|
|
|
159
|
+
# Disabling to keep closer to rails original
|
|
160
|
+
# rubocop:disable Naming/AccessorMethodName, Style/GuardClause
|
|
161
|
+
# significant changes:
|
|
162
|
+
# * globalize the key to lookup
|
|
163
|
+
def set_inverse(record)
|
|
164
|
+
global_key = if model.connection_class_for_self == UnshardedRecord
|
|
165
|
+
convert_key(record[association_key_name])
|
|
166
|
+
else
|
|
167
|
+
Shard.global_id_for(record[association_key_name], record.shard)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
if (owners = owners_by_key[convert_key(global_key)])
|
|
171
|
+
# Processing only the first owner
|
|
172
|
+
# because the record is modified but not an owner
|
|
173
|
+
association = owners.first.association(reflection.name)
|
|
174
|
+
association.set_inverse_instance(record)
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
# rubocop:enable Naming/AccessorMethodName, Style/GuardClause
|
|
178
|
+
|
|
113
179
|
# significant changes:
|
|
114
180
|
# * partition_by_shard the records_for call
|
|
115
181
|
# * re-globalize the fetched owner id before looking up in the map
|
|
@@ -120,7 +186,9 @@ module Switchman
|
|
|
120
186
|
# #compare_by_identity makes such owners different hash keys
|
|
121
187
|
@records_by_owner = {}.compare_by_identity
|
|
122
188
|
|
|
123
|
-
if ::Rails.version
|
|
189
|
+
if ::Rails.version >= '7.0'
|
|
190
|
+
raw_records ||= loader_query.records_for([self])
|
|
191
|
+
elsif owner_keys.empty?
|
|
124
192
|
raw_records ||= []
|
|
125
193
|
else
|
|
126
194
|
# determine the shard to search for each owner
|
|
@@ -162,7 +230,7 @@ module Switchman
|
|
|
162
230
|
record.shard)
|
|
163
231
|
end
|
|
164
232
|
|
|
165
|
-
owners_by_key[convert_key(owner_key)]
|
|
233
|
+
owners_by_key[convert_key(owner_key)]&.each do |owner|
|
|
166
234
|
entries = (@records_by_owner[owner] ||= [])
|
|
167
235
|
|
|
168
236
|
if reflection.collection? || entries.empty?
|
|
@@ -99,7 +99,7 @@ module Switchman
|
|
|
99
99
|
if reflection.options[:polymorphic]
|
|
100
100
|
# a polymorphic association has to be discovered at runtime. This code ends up being something like
|
|
101
101
|
# context_type.&.constantize&.connection_class_for_self
|
|
102
|
-
"read_attribute(:#{reflection.foreign_type})&.constantize&.connection_class_for_self"
|
|
102
|
+
"begin;read_attribute(:#{reflection.foreign_type})&.constantize&.connection_class_for_self;rescue NameError;::ActiveRecord::Base;end"
|
|
103
103
|
else
|
|
104
104
|
# otherwise we can just return a symbol for the statically known type of the association
|
|
105
105
|
"::#{reflection.klass.connection_class_for_self.name}"
|
|
@@ -256,20 +256,6 @@ module Switchman
|
|
|
256
256
|
attribute(attr_name)
|
|
257
257
|
end
|
|
258
258
|
end
|
|
259
|
-
|
|
260
|
-
private
|
|
261
|
-
|
|
262
|
-
def connection_class_for_self_for_reflection(reflection)
|
|
263
|
-
if reflection
|
|
264
|
-
if reflection.options[:polymorphic]
|
|
265
|
-
read_attribute(reflection.foreign_type)&.constantize&.connection_class_for_self
|
|
266
|
-
else
|
|
267
|
-
reflection.klass.connection_class_for_self
|
|
268
|
-
end
|
|
269
|
-
else
|
|
270
|
-
self.class.connection_class_for_self
|
|
271
|
-
end
|
|
272
|
-
end
|
|
273
259
|
end
|
|
274
260
|
end
|
|
275
261
|
end
|
|
@@ -22,16 +22,20 @@ module Switchman
|
|
|
22
22
|
@integral_id
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
%w[transaction insert_all upsert_all].each do |method|
|
|
26
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
27
|
+
def #{method}(*, **)
|
|
28
|
+
if self != ::ActiveRecord::Base && current_scope
|
|
29
|
+
current_scope.activate do
|
|
30
|
+
db = Shard.current(connection_class_for_self).database_server
|
|
31
|
+
db.unguard { super }
|
|
32
|
+
end
|
|
33
|
+
else
|
|
34
|
+
db = Shard.current(connection_class_for_self).database_server
|
|
35
|
+
db.unguard { super }
|
|
36
|
+
end
|
|
30
37
|
end
|
|
31
|
-
|
|
32
|
-
db = Shard.current(connection_class_for_self).database_server
|
|
33
|
-
db.unguard { super }
|
|
34
|
-
end
|
|
38
|
+
RUBY
|
|
35
39
|
end
|
|
36
40
|
|
|
37
41
|
def reset_column_information
|
|
@@ -138,9 +142,33 @@ module Switchman
|
|
|
138
142
|
else
|
|
139
143
|
Shard.current(self.class.connection_class_for_self)
|
|
140
144
|
end
|
|
145
|
+
readonly! if shadow_record? && !Switchman.config[:writable_shadow_records]
|
|
141
146
|
super
|
|
142
147
|
end
|
|
143
148
|
|
|
149
|
+
def shadow_record?
|
|
150
|
+
pkey = self[self.class.primary_key]
|
|
151
|
+
return false unless self.class.sharded_column?(self.class.primary_key) && pkey
|
|
152
|
+
|
|
153
|
+
pkey > Shard::IDS_PER_SHARD
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def save_shadow_record(new_attrs: attributes, target_shard: Shard.current)
|
|
157
|
+
return if target_shard == shard
|
|
158
|
+
|
|
159
|
+
shadow_attrs = {}
|
|
160
|
+
new_attrs.each do |attr, value|
|
|
161
|
+
shadow_attrs[attr] = if self.class.sharded_column?(attr)
|
|
162
|
+
Shard.relative_id_for(value, shard, target_shard)
|
|
163
|
+
else
|
|
164
|
+
value
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
target_shard.activate do
|
|
168
|
+
self.class.upsert(shadow_attrs, unique_by: self.class.primary_key)
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
144
172
|
def shard
|
|
145
173
|
@shard || Shard.current(self.class.connection_class_for_self) || Shard.default
|
|
146
174
|
end
|
|
@@ -20,13 +20,15 @@ module Switchman
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
module Migrator
|
|
23
|
-
# significant change:
|
|
24
|
-
#
|
|
25
|
-
# name you're accessing may not be consistent
|
|
26
|
-
# to run migrations against multiple shards in the same database
|
|
27
|
-
# concurrently
|
|
23
|
+
# significant change: use the shard name instead of the database name
|
|
24
|
+
# in the lock id. Especially if you're going through pgbouncer, the
|
|
25
|
+
# database name you're accessing may not be consistent
|
|
28
26
|
def generate_migrator_advisory_lock_id
|
|
29
|
-
|
|
27
|
+
db_name_hash = Zlib.crc32(Shard.current.name)
|
|
28
|
+
shard_name_hash = ::ActiveRecord::Migrator::MIGRATOR_SALT * db_name_hash
|
|
29
|
+
# Store in internalmetadata to allow other tools to be able to lock out migrations
|
|
30
|
+
::ActiveRecord::InternalMetadata[:migrator_advisory_lock_id] = shard_name_hash
|
|
31
|
+
shard_name_hash
|
|
30
32
|
end
|
|
31
33
|
|
|
32
34
|
# significant change: strip out prefer_secondary from config
|
data/lib/switchman/engine.rb
CHANGED
|
@@ -50,7 +50,7 @@ module Switchman
|
|
|
50
50
|
::ActiveRecord::Associations::CollectionProxy.include(ActiveRecord::Associations::CollectionProxy)
|
|
51
51
|
|
|
52
52
|
::ActiveRecord::Associations::Preloader::Association.prepend(ActiveRecord::Associations::Preloader::Association)
|
|
53
|
-
::ActiveRecord::Associations::Preloader::Association::
|
|
53
|
+
::ActiveRecord::Associations::Preloader::Association::LoaderRecords.prepend(ActiveRecord::Associations::Preloader::Association::LoaderRecords) unless ::Rails.version < '7.0'
|
|
54
54
|
::ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(ActiveRecord::AbstractAdapter)
|
|
55
55
|
::ActiveRecord::ConnectionAdapters::ConnectionPool.prepend(ActiveRecord::ConnectionPool)
|
|
56
56
|
::ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(ActiveRecord::QueryCache)
|
data/lib/switchman/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: switchman
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Cody Cutrer
|
|
8
8
|
- James Williams
|
|
9
9
|
- Jacob Fugal
|
|
10
|
-
autorequire:
|
|
10
|
+
autorequire:
|
|
11
11
|
bindir: bin
|
|
12
12
|
cert_chain: []
|
|
13
|
-
date: 2022-
|
|
13
|
+
date: 2022-11-22 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: activerecord
|
|
@@ -300,7 +300,7 @@ licenses:
|
|
|
300
300
|
- MIT
|
|
301
301
|
metadata:
|
|
302
302
|
rubygems_mfa_required: 'true'
|
|
303
|
-
post_install_message:
|
|
303
|
+
post_install_message:
|
|
304
304
|
rdoc_options: []
|
|
305
305
|
require_paths:
|
|
306
306
|
- lib
|
|
@@ -316,7 +316,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
316
316
|
version: '0'
|
|
317
317
|
requirements: []
|
|
318
318
|
rubygems_version: 3.1.6
|
|
319
|
-
signing_key:
|
|
319
|
+
signing_key:
|
|
320
320
|
specification_version: 4
|
|
321
321
|
summary: Rails sharding magic
|
|
322
322
|
test_files: []
|