switchman 3.1.0 → 3.5.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/Rakefile +15 -14
- data/db/migrate/20180828183945_add_default_shard_index.rb +1 -1
- data/db/migrate/20190114212900_add_unique_name_indexes.rb +10 -4
- data/lib/switchman/active_record/associations.rb +97 -17
- data/lib/switchman/active_record/attribute_methods.rb +72 -43
- data/lib/switchman/active_record/base.rb +107 -21
- data/lib/switchman/active_record/calculations.rb +37 -33
- data/lib/switchman/active_record/connection_pool.rb +21 -2
- data/lib/switchman/active_record/database_configurations.rb +12 -7
- data/lib/switchman/active_record/finder_methods.rb +1 -1
- data/lib/switchman/active_record/log_subscriber.rb +2 -2
- data/lib/switchman/active_record/migration.rb +35 -8
- data/lib/switchman/active_record/persistence.rb +8 -0
- data/lib/switchman/active_record/postgresql_adapter.rb +11 -10
- data/lib/switchman/active_record/query_cache.rb +1 -1
- data/lib/switchman/active_record/query_methods.rb +172 -132
- data/lib/switchman/active_record/relation.rb +21 -11
- data/lib/switchman/active_record/spawn_methods.rb +2 -2
- data/lib/switchman/active_record/statement_cache.rb +9 -5
- data/lib/switchman/active_record/tasks/database_tasks.rb +1 -1
- data/lib/switchman/active_record/test_fixtures.rb +19 -16
- data/lib/switchman/active_support/cache.rb +4 -1
- data/lib/switchman/arel.rb +6 -6
- data/lib/switchman/call_super.rb +8 -2
- data/lib/switchman/database_server.rb +21 -26
- data/lib/switchman/default_shard.rb +3 -3
- data/lib/switchman/engine.rb +33 -18
- data/lib/switchman/environment.rb +2 -2
- data/lib/switchman/errors.rb +13 -0
- data/lib/switchman/guard_rail/relation.rb +2 -1
- data/lib/switchman/parallel.rb +2 -2
- data/lib/switchman/r_spec_helper.rb +10 -10
- data/lib/switchman/shard.rb +49 -32
- data/lib/switchman/sharded_instrumenter.rb +5 -1
- data/lib/switchman/shared_schema_cache.rb +11 -0
- data/lib/switchman/test_helper.rb +1 -1
- data/lib/switchman/version.rb +1 -1
- data/lib/switchman.rb +10 -4
- data/lib/tasks/switchman.rake +42 -39
- metadata +24 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1f6f878b1a2a9ba1dc0c75ce4fcd5d299178ea7940a048190bd4b181ae306339
|
|
4
|
+
data.tar.gz: 19b31ce1e456fbecf2a9ba9b15644bc6073cd521b6a604da81a0c5f8977f579a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bad542adf6c09d1e1e50ef3054a8ca8d106bd3eedb7cb9dbbe544e44dd88c7f19226e7dd0d805ad5a5685009be7b9c9fe20623182c8c624c88724d00d7bc5656
|
|
7
|
+
data.tar.gz: 43b76369798a3683f838e392466cec0628d0c4a309354db7ccadcafca604f6eeffdcc460e036ca8d0f105c7abed2987baa23d51b830e628951c057adb554f305
|
data/Rakefile
CHANGED
|
@@ -2,37 +2,38 @@
|
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
begin
|
|
5
|
-
require
|
|
5
|
+
require "bundler/setup"
|
|
6
6
|
rescue LoadError
|
|
7
|
-
puts
|
|
7
|
+
puts "You must `gem install bundler` and `bundle install` to run rake tasks"
|
|
8
8
|
end
|
|
9
9
|
begin
|
|
10
|
-
require
|
|
10
|
+
require "rdoc/task"
|
|
11
11
|
rescue LoadError
|
|
12
|
-
require
|
|
13
|
-
require
|
|
12
|
+
require "rdoc/rdoc"
|
|
13
|
+
require "rake/rdoctask"
|
|
14
14
|
RDoc::Task = Rake::RDocTask
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
RDoc::Task.new(:rdoc) do |rdoc|
|
|
18
|
-
rdoc.rdoc_dir =
|
|
19
|
-
rdoc.title =
|
|
20
|
-
rdoc.options <<
|
|
21
|
-
rdoc.rdoc_files.include(
|
|
18
|
+
rdoc.rdoc_dir = "rdoc"
|
|
19
|
+
rdoc.title = "Switchman"
|
|
20
|
+
rdoc.options << "--line-numbers"
|
|
21
|
+
rdoc.rdoc_files.include("lib/**/*.rb")
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
load "./spec/tasks/coverage.rake"
|
|
25
|
+
APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
|
|
26
|
+
load "rails/tasks/engine.rake"
|
|
26
27
|
|
|
27
28
|
Bundler::GemHelper.install_tasks
|
|
28
29
|
|
|
29
|
-
require
|
|
30
|
+
require "rspec/core/rake_task"
|
|
30
31
|
RSpec::Core::RakeTask.new
|
|
31
32
|
|
|
32
|
-
require
|
|
33
|
+
require "rubocop/rake_task"
|
|
33
34
|
|
|
34
35
|
RuboCop::RakeTask.new do |task|
|
|
35
|
-
task.options = [
|
|
36
|
+
task.options = ["-S"]
|
|
36
37
|
end
|
|
37
38
|
|
|
38
39
|
task default: %i[spec]
|
|
@@ -5,7 +5,7 @@ class AddDefaultShardIndex < ActiveRecord::Migration[4.2]
|
|
|
5
5
|
Switchman::Shard.where(default: nil).update_all(default: false) if Switchman::Shard.current.default?
|
|
6
6
|
change_column_default :switchman_shards, :default, false
|
|
7
7
|
change_column_null :switchman_shards, :default, false
|
|
8
|
-
options = if connection.adapter_name ==
|
|
8
|
+
options = if connection.adapter_name == "PostgreSQL"
|
|
9
9
|
{ unique: true, where: '"default"' }
|
|
10
10
|
else
|
|
11
11
|
{}
|
|
@@ -3,9 +3,15 @@
|
|
|
3
3
|
class AddUniqueNameIndexes < ActiveRecord::Migration[4.2]
|
|
4
4
|
def change
|
|
5
5
|
add_index :switchman_shards, %i[database_server_id name], unique: true
|
|
6
|
-
add_index :switchman_shards,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
add_index :switchman_shards,
|
|
7
|
+
:database_server_id,
|
|
8
|
+
unique: true,
|
|
9
|
+
where: "name IS NULL",
|
|
10
|
+
name: "index_switchman_shards_unique_primary_shard"
|
|
11
|
+
add_index :switchman_shards,
|
|
12
|
+
"(true)",
|
|
13
|
+
unique: true,
|
|
14
|
+
where: "database_server_id IS NULL AND name IS NULL",
|
|
15
|
+
name: "index_switchman_shards_unique_primary_db_and_shard"
|
|
10
16
|
end
|
|
11
17
|
end
|
|
@@ -24,10 +24,15 @@ module Switchman
|
|
|
24
24
|
|
|
25
25
|
module CollectionAssociation
|
|
26
26
|
def find_target
|
|
27
|
-
shards = reflection.options[:multishard] && owner.respond_to?(:associated_shards)
|
|
27
|
+
shards = if reflection.options[:multishard] && owner.respond_to?(:associated_shards)
|
|
28
|
+
owner.associated_shards
|
|
29
|
+
else
|
|
30
|
+
[shard]
|
|
31
|
+
end
|
|
28
32
|
# activate both the owner and the target's shard category, so that Reflection#join_id_for,
|
|
29
33
|
# when called for the owner, will be returned relative to shard the query will execute on
|
|
30
|
-
Shard.with_each_shard(shards,
|
|
34
|
+
Shard.with_each_shard(shards,
|
|
35
|
+
[klass.connection_class_for_self, owner.class.connection_class_for_self].uniq) do
|
|
31
36
|
super
|
|
32
37
|
end
|
|
33
38
|
end
|
|
@@ -50,7 +55,7 @@ module Switchman
|
|
|
50
55
|
def shard
|
|
51
56
|
if @owner.class.sharded_column?(@reflection.foreign_key) &&
|
|
52
57
|
(foreign_id = @owner[@reflection.foreign_key])
|
|
53
|
-
Shard.shard_for(foreign_id, @owner.
|
|
58
|
+
Shard.shard_for(foreign_id, @owner.loaded_from_shard)
|
|
54
59
|
else
|
|
55
60
|
super
|
|
56
61
|
end
|
|
@@ -83,16 +88,67 @@ module Switchman
|
|
|
83
88
|
|
|
84
89
|
module Preloader
|
|
85
90
|
module Association
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
+
# significant changes:
|
|
92
|
+
# * associate shards with records
|
|
93
|
+
# * look on all appropriate shards when loading records
|
|
94
|
+
module LoaderRecords
|
|
95
|
+
def populate_keys_to_load_and_already_loaded_records
|
|
96
|
+
@sharded_keys_to_load = {}
|
|
91
97
|
|
|
92
98
|
loaders.each do |loader|
|
|
93
|
-
loader.
|
|
94
|
-
loader.
|
|
99
|
+
multishard = loader.send(:reflection).options[:multishard]
|
|
100
|
+
belongs_to = loader.send(:reflection).macro == :belongs_to
|
|
101
|
+
loader.owners_by_key.each do |key, owners|
|
|
102
|
+
if (loaded_owner = owners.find { |owner| loader.loaded?(owner) })
|
|
103
|
+
already_loaded_records_by_key[key] = loader.target_for(loaded_owner)
|
|
104
|
+
else
|
|
105
|
+
shard_set = @sharded_keys_to_load[key] ||= Set.new
|
|
106
|
+
owner_key_name = loader.send(:owner_key_name)
|
|
107
|
+
owners.each do |owner|
|
|
108
|
+
if multishard && owner.respond_to?(:associated_shards)
|
|
109
|
+
shard_set.merge(owner.associated_shards.map(&:id))
|
|
110
|
+
elsif belongs_to && owner.class.sharded_column?(owner_key_name)
|
|
111
|
+
shard_set.add(Shard.shard_for(owner[owner_key_name], owner.shard).id)
|
|
112
|
+
elsif belongs_to
|
|
113
|
+
shard_set.add(Shard.current.id)
|
|
114
|
+
else
|
|
115
|
+
shard_set.add(owner.shard.id)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
95
120
|
end
|
|
121
|
+
|
|
122
|
+
@sharded_keys_to_load.delete_if { |key, _shards| already_loaded_records_by_key.include?(key) }
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def load_records
|
|
126
|
+
ret = []
|
|
127
|
+
|
|
128
|
+
shards_with_keys = @sharded_keys_to_load.each_with_object({}) do |(key, shards), h|
|
|
129
|
+
shards.each { |shard| (h[shard] ||= []) << key }
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
shards_with_keys.each do |shard, keys|
|
|
133
|
+
Shard.lookup(shard).activate do
|
|
134
|
+
scope_was = loader_query.scope
|
|
135
|
+
begin
|
|
136
|
+
loader_query.instance_variable_set(
|
|
137
|
+
:@scope,
|
|
138
|
+
loader_query.scope.shard(
|
|
139
|
+
Shard.current(loader_query.scope.model.connection_class_for_self)
|
|
140
|
+
)
|
|
141
|
+
)
|
|
142
|
+
ret += loader_query.load_records_for_keys(keys) do |record|
|
|
143
|
+
loaders.each { |l| l.set_inverse(record) }
|
|
144
|
+
end
|
|
145
|
+
ensure
|
|
146
|
+
loader_query.instance_variable_set(:@scope, scope_was)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
ret
|
|
96
152
|
end
|
|
97
153
|
end
|
|
98
154
|
|
|
@@ -110,6 +166,26 @@ module Switchman
|
|
|
110
166
|
end
|
|
111
167
|
end
|
|
112
168
|
|
|
169
|
+
# Disabling to keep closer to rails original
|
|
170
|
+
# rubocop:disable Naming/AccessorMethodName, Style/GuardClause
|
|
171
|
+
# significant changes:
|
|
172
|
+
# * globalize the key to lookup
|
|
173
|
+
def set_inverse(record)
|
|
174
|
+
global_key = if model.connection_class_for_self == UnshardedRecord
|
|
175
|
+
convert_key(record[association_key_name])
|
|
176
|
+
else
|
|
177
|
+
Shard.global_id_for(record[association_key_name], record.shard)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
if (owners = owners_by_key[convert_key(global_key)])
|
|
181
|
+
# Processing only the first owner
|
|
182
|
+
# because the record is modified but not an owner
|
|
183
|
+
association = owners.first.association(reflection.name)
|
|
184
|
+
association.set_inverse_instance(record)
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
# rubocop:enable Naming/AccessorMethodName, Style/GuardClause
|
|
188
|
+
|
|
113
189
|
# significant changes:
|
|
114
190
|
# * partition_by_shard the records_for call
|
|
115
191
|
# * re-globalize the fetched owner id before looking up in the map
|
|
@@ -120,7 +196,9 @@ module Switchman
|
|
|
120
196
|
# #compare_by_identity makes such owners different hash keys
|
|
121
197
|
@records_by_owner = {}.compare_by_identity
|
|
122
198
|
|
|
123
|
-
if ::Rails.version
|
|
199
|
+
if ::Rails.version >= "7.0"
|
|
200
|
+
raw_records ||= loader_query.records_for([self])
|
|
201
|
+
elsif owner_keys.empty?
|
|
124
202
|
raw_records ||= []
|
|
125
203
|
else
|
|
126
204
|
# determine the shard to search for each owner
|
|
@@ -142,7 +220,8 @@ module Switchman
|
|
|
142
220
|
relative_owner_keys = partitioned_owners.map do |owner|
|
|
143
221
|
key = owner[owner_key_name]
|
|
144
222
|
if key && owner.class.sharded_column?(owner_key_name)
|
|
145
|
-
key = Shard.relative_id_for(key,
|
|
223
|
+
key = Shard.relative_id_for(key,
|
|
224
|
+
owner.shard,
|
|
146
225
|
Shard.current(klass.connection_class_for_self))
|
|
147
226
|
end
|
|
148
227
|
convert_key(key)
|
|
@@ -162,7 +241,7 @@ module Switchman
|
|
|
162
241
|
record.shard)
|
|
163
242
|
end
|
|
164
243
|
|
|
165
|
-
owners_by_key[convert_key(owner_key)]
|
|
244
|
+
owners_by_key[convert_key(owner_key)]&.each do |owner|
|
|
166
245
|
entries = (@records_by_owner[owner] ||= [])
|
|
167
246
|
|
|
168
247
|
if reflection.collection? || entries.empty?
|
|
@@ -205,10 +284,11 @@ module Switchman
|
|
|
205
284
|
end
|
|
206
285
|
|
|
207
286
|
module AutosaveAssociation
|
|
208
|
-
def
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
287
|
+
def association_foreign_key_changed?(reflection, record, key)
|
|
288
|
+
return false if reflection.through_reflection?
|
|
289
|
+
|
|
290
|
+
# have to use send instead of _read_attribute because sharding
|
|
291
|
+
record.has_attribute?(reflection.foreign_key) && record.send(reflection.foreign_key) != key
|
|
212
292
|
end
|
|
213
293
|
|
|
214
294
|
def save_belongs_to_association(reflection)
|
|
@@ -31,8 +31,16 @@ module Switchman
|
|
|
31
31
|
# and not the silly one in AR::AttributeMethods::PrimaryKey
|
|
32
32
|
return unless sharded_column?(@primary_key)
|
|
33
33
|
|
|
34
|
-
class_eval(
|
|
35
|
-
|
|
34
|
+
class_eval(
|
|
35
|
+
build_sharded_getter("id",
|
|
36
|
+
"_read_attribute(@primary_key)",
|
|
37
|
+
"::#{connection_class_for_self.name}"),
|
|
38
|
+
__FILE__,
|
|
39
|
+
__LINE__
|
|
40
|
+
)
|
|
41
|
+
class_eval(build_sharded_setter("id", @primary_key, "::#{connection_class_for_self.name}"),
|
|
42
|
+
__FILE__,
|
|
43
|
+
__LINE__)
|
|
36
44
|
end
|
|
37
45
|
|
|
38
46
|
protected
|
|
@@ -46,20 +54,21 @@ module Switchman
|
|
|
46
54
|
raise if connection.open_transactions.positive?
|
|
47
55
|
end
|
|
48
56
|
|
|
49
|
-
# rubocop:disable Naming/MethodParameterName
|
|
50
57
|
def define_cached_method(owner, name, namespace:, as:, &block)
|
|
51
|
-
if ::Rails.version <
|
|
58
|
+
if ::Rails.version < "7.0"
|
|
52
59
|
yield owner
|
|
53
60
|
owner.rename_method(as, name)
|
|
54
61
|
else
|
|
55
62
|
owner.define_cached_method(name, namespace: namespace, as: as, &block)
|
|
56
63
|
end
|
|
57
64
|
end
|
|
58
|
-
# rubocop:enable Naming/MethodParameterName
|
|
59
65
|
|
|
60
66
|
def define_method_global_attribute(attr_name, owner:)
|
|
61
67
|
if sharded_column?(attr_name)
|
|
62
|
-
define_cached_method(owner,
|
|
68
|
+
define_cached_method(owner,
|
|
69
|
+
"global_#{attr_name}",
|
|
70
|
+
as: "sharded_global_#{attr_name}",
|
|
71
|
+
namespace: :switchman) do |batch|
|
|
63
72
|
batch << <<-RUBY
|
|
64
73
|
def sharded_global_#{attr_name}
|
|
65
74
|
raw_value = original_#{attr_name}
|
|
@@ -71,13 +80,16 @@ module Switchman
|
|
|
71
80
|
RUBY
|
|
72
81
|
end
|
|
73
82
|
else
|
|
74
|
-
define_method_unsharded_column(attr_name,
|
|
83
|
+
define_method_unsharded_column(attr_name, "global", owner)
|
|
75
84
|
end
|
|
76
85
|
end
|
|
77
86
|
|
|
78
87
|
def define_method_local_attribute(attr_name, owner:)
|
|
79
88
|
if sharded_column?(attr_name)
|
|
80
|
-
define_cached_method(owner,
|
|
89
|
+
define_cached_method(owner,
|
|
90
|
+
"local_#{attr_name}",
|
|
91
|
+
as: "sharded_local_#{attr_name}",
|
|
92
|
+
namespace: :switchman) do |batch|
|
|
81
93
|
batch << <<-RUBY
|
|
82
94
|
def sharded_local_#{attr_name}
|
|
83
95
|
raw_value = original_#{attr_name}
|
|
@@ -87,7 +99,7 @@ module Switchman
|
|
|
87
99
|
RUBY
|
|
88
100
|
end
|
|
89
101
|
else
|
|
90
|
-
define_method_unsharded_column(attr_name,
|
|
102
|
+
define_method_unsharded_column(attr_name, "local", owner)
|
|
91
103
|
end
|
|
92
104
|
end
|
|
93
105
|
|
|
@@ -99,7 +111,13 @@ module Switchman
|
|
|
99
111
|
if reflection.options[:polymorphic]
|
|
100
112
|
# a polymorphic association has to be discovered at runtime. This code ends up being something like
|
|
101
113
|
# context_type.&.constantize&.connection_class_for_self
|
|
102
|
-
|
|
114
|
+
<<~RUBY
|
|
115
|
+
begin
|
|
116
|
+
read_attribute(:#{reflection.foreign_type})&.constantize&.connection_class_for_self
|
|
117
|
+
rescue NameError
|
|
118
|
+
::ActiveRecord::Base
|
|
119
|
+
end
|
|
120
|
+
RUBY
|
|
103
121
|
else
|
|
104
122
|
# otherwise we can just return a symbol for the statically known type of the association
|
|
105
123
|
"::#{reflection.klass.connection_class_for_self.name}"
|
|
@@ -113,9 +131,14 @@ module Switchman
|
|
|
113
131
|
if sharded_column?(attr_name)
|
|
114
132
|
reflection = reflection_for_integer_attribute(attr_name)
|
|
115
133
|
class_name = connection_class_for_self_code_for_reflection(reflection)
|
|
116
|
-
safe_class_name = class_name.unpack1(
|
|
117
|
-
define_cached_method(owner,
|
|
118
|
-
|
|
134
|
+
safe_class_name = class_name.unpack1("h*")
|
|
135
|
+
define_cached_method(owner,
|
|
136
|
+
attr_name,
|
|
137
|
+
as: "sharded_#{safe_class_name}_#{attr_name}",
|
|
138
|
+
namespace: :switchman) do |batch|
|
|
139
|
+
batch << build_sharded_getter("sharded_#{safe_class_name}_#{attr_name}",
|
|
140
|
+
"original_#{attr_name}",
|
|
141
|
+
class_name)
|
|
119
142
|
end
|
|
120
143
|
else
|
|
121
144
|
define_cached_method(owner, attr_name, as: "plain_#{attr_name}", namespace: :switchman) do |batch|
|
|
@@ -136,7 +159,7 @@ module Switchman
|
|
|
136
159
|
|
|
137
160
|
abs_raw_value = raw_value.abs
|
|
138
161
|
current_shard = ::Switchman::Shard.current(#{attr_connection_class})
|
|
139
|
-
same_shard =
|
|
162
|
+
same_shard = loaded_from_shard == current_shard
|
|
140
163
|
return raw_value if same_shard && abs_raw_value < ::Switchman::Shard::IDS_PER_SHARD
|
|
141
164
|
|
|
142
165
|
value_shard_id = abs_raw_value / ::Switchman::Shard::IDS_PER_SHARD
|
|
@@ -144,9 +167,9 @@ module Switchman
|
|
|
144
167
|
# of a local id
|
|
145
168
|
return raw_value % ::Switchman::Shard::IDS_PER_SHARD if value_shard_id == current_shard.id
|
|
146
169
|
return raw_value if !same_shard && abs_raw_value > ::Switchman::Shard::IDS_PER_SHARD
|
|
147
|
-
return
|
|
170
|
+
return loaded_from_shard.global_id_for(raw_value) if !same_shard && abs_raw_value < ::Switchman::Shard::IDS_PER_SHARD
|
|
148
171
|
|
|
149
|
-
::Switchman::Shard.relative_id_for(raw_value,
|
|
172
|
+
::Switchman::Shard.relative_id_for(raw_value, loaded_from_shard, current_shard)
|
|
150
173
|
end
|
|
151
174
|
RUBY
|
|
152
175
|
end
|
|
@@ -155,8 +178,11 @@ module Switchman
|
|
|
155
178
|
if sharded_column?(attr_name)
|
|
156
179
|
reflection = reflection_for_integer_attribute(attr_name)
|
|
157
180
|
class_name = connection_class_for_self_code_for_reflection(reflection)
|
|
158
|
-
safe_class_name = class_name.unpack1(
|
|
159
|
-
define_cached_method(owner,
|
|
181
|
+
safe_class_name = class_name.unpack1("h*")
|
|
182
|
+
define_cached_method(owner,
|
|
183
|
+
"#{attr_name}=",
|
|
184
|
+
as: "sharded_#{safe_class_name}_#{attr_name}=",
|
|
185
|
+
namespace: :switchman) do |batch|
|
|
160
186
|
batch << build_sharded_setter("sharded_#{safe_class_name}_#{attr_name}", attr_name, class_name)
|
|
161
187
|
end
|
|
162
188
|
else
|
|
@@ -173,14 +199,17 @@ module Switchman
|
|
|
173
199
|
def build_sharded_setter(attr_name, attr_field, attr_connection_class)
|
|
174
200
|
<<-RUBY
|
|
175
201
|
def #{attr_name}=(new_value)
|
|
176
|
-
self.original_#{attr_field} = ::Switchman::Shard.relative_id_for(new_value, ::Switchman::Shard.current(#{attr_connection_class}),
|
|
202
|
+
self.original_#{attr_field} = ::Switchman::Shard.relative_id_for(new_value, ::Switchman::Shard.current(#{attr_connection_class}), loaded_from_shard)
|
|
177
203
|
end
|
|
178
204
|
RUBY
|
|
179
205
|
end
|
|
180
206
|
|
|
181
207
|
def define_method_original_attribute(attr_name, owner:)
|
|
182
208
|
if sharded_column?(attr_name)
|
|
183
|
-
define_cached_method(owner,
|
|
209
|
+
define_cached_method(owner,
|
|
210
|
+
"original_#{attr_name}",
|
|
211
|
+
as: "sharded_original_#{attr_name}",
|
|
212
|
+
namespace: :switchman) do |batch|
|
|
184
213
|
batch << <<-RUBY
|
|
185
214
|
def sharded_original_#{attr_name}
|
|
186
215
|
_read_attribute("#{attr_name}") { |n| missing_attribute(n, caller) }
|
|
@@ -188,14 +217,17 @@ module Switchman
|
|
|
188
217
|
RUBY
|
|
189
218
|
end
|
|
190
219
|
else
|
|
191
|
-
define_method_unsharded_column(attr_name,
|
|
220
|
+
define_method_unsharded_column(attr_name, "global", owner)
|
|
192
221
|
end
|
|
193
222
|
end
|
|
194
223
|
|
|
195
224
|
def define_method_original_attribute=(attr_name, owner:)
|
|
196
225
|
return unless sharded_column?(attr_name)
|
|
197
226
|
|
|
198
|
-
define_cached_method(owner,
|
|
227
|
+
define_cached_method(owner,
|
|
228
|
+
"original_#{attr_name}=",
|
|
229
|
+
as: "sharded_original_#{attr_name}=",
|
|
230
|
+
namespace: :switchman) do |batch|
|
|
199
231
|
batch << <<-RUBY
|
|
200
232
|
def sharded_original_#{attr_name}=(new_value)
|
|
201
233
|
_write_attribute('#{attr_name}', new_value)
|
|
@@ -205,9 +237,12 @@ module Switchman
|
|
|
205
237
|
end
|
|
206
238
|
|
|
207
239
|
def define_method_unsharded_column(attr_name, prefix, owner)
|
|
208
|
-
return if columns_hash["#{prefix}_#{attr_name}"] || attr_name ==
|
|
240
|
+
return if columns_hash["#{prefix}_#{attr_name}"] || attr_name == "id"
|
|
209
241
|
|
|
210
|
-
define_cached_method(owner,
|
|
242
|
+
define_cached_method(owner,
|
|
243
|
+
"#{prefix}_#{attr_name}",
|
|
244
|
+
as: "unsharded_#{prefix}_#{attr_name}",
|
|
245
|
+
namespace: :switchman) do |batch|
|
|
211
246
|
batch << <<-RUBY
|
|
212
247
|
def unsharded_#{prefix}_#{attr_name}
|
|
213
248
|
raise NoMethodError, "undefined method `#{prefix}_#{attr_name}'; are you missing an association?"
|
|
@@ -219,8 +254,8 @@ module Switchman
|
|
|
219
254
|
|
|
220
255
|
def self.prepended(klass)
|
|
221
256
|
klass.singleton_class.prepend(ClassMethods)
|
|
222
|
-
klass.attribute_method_prefix
|
|
223
|
-
klass.attribute_method_affix prefix:
|
|
257
|
+
klass.attribute_method_prefix "global_", "local_", "original_"
|
|
258
|
+
klass.attribute_method_affix prefix: "original_", suffix: "="
|
|
224
259
|
end
|
|
225
260
|
|
|
226
261
|
# these are called if the specific methods haven't been defined yet
|
|
@@ -228,7 +263,11 @@ module Switchman
|
|
|
228
263
|
return super unless self.class.sharded_column?(attr_name)
|
|
229
264
|
|
|
230
265
|
reflection = self.class.send(:reflection_for_integer_attribute, attr_name)
|
|
231
|
-
::Switchman::Shard.relative_id_for(
|
|
266
|
+
::Switchman::Shard.relative_id_for(
|
|
267
|
+
super,
|
|
268
|
+
shard,
|
|
269
|
+
::Switchman::Shard.current(connection_class_for_self_for_reflection(reflection))
|
|
270
|
+
)
|
|
232
271
|
end
|
|
233
272
|
|
|
234
273
|
def attribute=(attr_name, new_value)
|
|
@@ -238,7 +277,11 @@ module Switchman
|
|
|
238
277
|
end
|
|
239
278
|
|
|
240
279
|
reflection = self.class.send(:reflection_for_integer_attribute, attr_name)
|
|
241
|
-
super(::Switchman::Shard.relative_id_for(
|
|
280
|
+
super(::Switchman::Shard.relative_id_for(
|
|
281
|
+
new_value,
|
|
282
|
+
::Switchman::Shard.current(connection_class_for_self_for_reflection(reflection)),
|
|
283
|
+
shard
|
|
284
|
+
))
|
|
242
285
|
end
|
|
243
286
|
|
|
244
287
|
def global_attribute(attr_name)
|
|
@@ -251,25 +294,11 @@ module Switchman
|
|
|
251
294
|
|
|
252
295
|
def local_attribute(attr_name)
|
|
253
296
|
if self.class.sharded_column?(attr_name)
|
|
254
|
-
::Switchman::Shard.local_id_for(attribute(attr_name)
|
|
297
|
+
::Switchman::Shard.local_id_for(attribute(attr_name)).first
|
|
255
298
|
else
|
|
256
299
|
attribute(attr_name)
|
|
257
300
|
end
|
|
258
301
|
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
302
|
end
|
|
274
303
|
end
|
|
275
304
|
end
|