switchman 3.4.1 → 4.2.5
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/abstract_adapter.rb +10 -6
- data/lib/switchman/active_record/associations.rb +71 -48
- data/lib/switchman/active_record/attribute_methods.rb +84 -37
- data/lib/switchman/active_record/base.rb +72 -41
- data/lib/switchman/active_record/calculations.rb +90 -54
- data/lib/switchman/active_record/connection_handler.rb +18 -0
- data/lib/switchman/active_record/connection_pool.rb +41 -23
- data/lib/switchman/active_record/database_configurations.rb +23 -13
- data/lib/switchman/active_record/finder_methods.rb +20 -14
- data/lib/switchman/active_record/log_subscriber.rb +3 -6
- data/lib/switchman/active_record/migration.rb +19 -19
- data/lib/switchman/active_record/pending_migration_connection.rb +17 -0
- data/lib/switchman/active_record/persistence.rb +22 -0
- data/lib/switchman/active_record/postgresql_adapter.rb +37 -22
- data/lib/switchman/active_record/predicate_builder.rb +2 -2
- data/lib/switchman/active_record/query_cache.rb +26 -17
- data/lib/switchman/active_record/query_methods.rb +148 -44
- data/lib/switchman/active_record/reflection.rb +9 -2
- data/lib/switchman/active_record/relation.rb +86 -16
- data/lib/switchman/active_record/spawn_methods.rb +3 -7
- data/lib/switchman/active_record/statement_cache.rb +4 -4
- data/lib/switchman/active_record/table_definition.rb +1 -1
- data/lib/switchman/active_record/tasks/database_tasks.rb +6 -1
- data/lib/switchman/active_record/test_fixtures.rb +71 -25
- data/lib/switchman/active_support/cache.rb +9 -4
- data/lib/switchman/arel.rb +16 -25
- data/lib/switchman/call_super.rb +2 -8
- data/lib/switchman/database_server.rb +67 -24
- data/lib/switchman/default_shard.rb +14 -3
- data/lib/switchman/engine.rb +35 -23
- data/lib/switchman/environment.rb +2 -2
- data/lib/switchman/errors.rb +4 -1
- data/lib/switchman/guard_rail/relation.rb +1 -2
- data/lib/switchman/parallel.rb +5 -5
- data/lib/switchman/r_spec_helper.rb +12 -11
- data/lib/switchman/shard.rb +168 -68
- data/lib/switchman/sharded_instrumenter.rb +9 -3
- data/lib/switchman/standard_error.rb +4 -0
- data/lib/switchman/test_helper.rb +3 -3
- data/lib/switchman/version.rb +1 -1
- data/lib/switchman.rb +27 -15
- data/lib/tasks/switchman.rake +96 -60
- metadata +28 -187
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4676a4e97375d1094b6775fa2a10f907218bbd2cb73f365c801a698822f779a0
|
|
4
|
+
data.tar.gz: 4f88954e9074fcd122ecbb8d965e1b16edb63c63d69fa5311f7e0a97e400c3d5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f043f994b2014ce1c980d6fe73a8137a5b497831bbe87592e0c3140e777d10bcbe1460216e3dcf7cc4d1c71cdbda98d3c2b0e5a6a9859f48120cbc2b9425c24f
|
|
7
|
+
data.tar.gz: 943659f21c0c99d8b1db896009a24601a8e698f13d0b2920e3532829a1bf0b1c5a319487d766d24c47b33a975b597ec4dc57e051ee0376e4b4dc6405515e38c8
|
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
|
|
@@ -5,7 +5,7 @@ module Switchman
|
|
|
5
5
|
module AbstractAdapter
|
|
6
6
|
module ForeignKeyCheck
|
|
7
7
|
def add_column(table, name, type, limit: nil, **)
|
|
8
|
-
Switchman.foreign_key_check(name, type, limit:
|
|
8
|
+
Switchman.foreign_key_check(name, type, limit:)
|
|
9
9
|
super
|
|
10
10
|
end
|
|
11
11
|
end
|
|
@@ -19,16 +19,20 @@ module Switchman
|
|
|
19
19
|
|
|
20
20
|
def initialize(*args)
|
|
21
21
|
super
|
|
22
|
-
|
|
22
|
+
|
|
23
|
+
@instrumenter = Switchman::ShardedInstrumenter.new(@instrumenter, self) if ::Rails.version < "8.0"
|
|
24
|
+
|
|
23
25
|
@last_query_at = Time.now
|
|
24
26
|
end
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
if ::Rails.version >= "8.0"
|
|
29
|
+
def instrumenter # :nodoc:
|
|
30
|
+
@instrumenter ||= Switchman::ShardedInstrumenter.new(::ActiveSupport::Notifications.instrumenter, self)
|
|
31
|
+
end
|
|
28
32
|
end
|
|
29
33
|
|
|
30
|
-
def
|
|
31
|
-
|
|
34
|
+
def quote_local_table_name(name)
|
|
35
|
+
quote_table_name(name)
|
|
32
36
|
end
|
|
33
37
|
|
|
34
38
|
protected
|
|
@@ -23,12 +23,26 @@ module Switchman
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
module CollectionAssociation
|
|
26
|
-
def find_target
|
|
27
|
-
shards = reflection.options[:multishard] && owner.respond_to?(:associated_shards)
|
|
26
|
+
def find_target(async: false)
|
|
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,
|
|
31
|
-
|
|
34
|
+
Shard.with_each_shard(shards,
|
|
35
|
+
[klass.connection_class_for_self, owner.class.connection_class_for_self].uniq) do
|
|
36
|
+
if reflection.options[:multishard] && owner.respond_to?(:associated_shards) && reflection.has_scope?
|
|
37
|
+
# Prevent duplicate results when reflection has a scope (when it would use the skip_statement_cache? path)
|
|
38
|
+
# otherwise, the super call will set the shard_value to the object, causing it to iterate too many times
|
|
39
|
+
# over the associated shards
|
|
40
|
+
scope.shard(Shard.current(scope.klass.connection_class_for_self), :association).to_a
|
|
41
|
+
elsif ::Rails.version < "8.0"
|
|
42
|
+
super()
|
|
43
|
+
else
|
|
44
|
+
super
|
|
45
|
+
end
|
|
32
46
|
end
|
|
33
47
|
end
|
|
34
48
|
|
|
@@ -128,7 +142,12 @@ module Switchman
|
|
|
128
142
|
Shard.lookup(shard).activate do
|
|
129
143
|
scope_was = loader_query.scope
|
|
130
144
|
begin
|
|
131
|
-
loader_query.instance_variable_set(
|
|
145
|
+
loader_query.instance_variable_set(
|
|
146
|
+
:@scope,
|
|
147
|
+
loader_query.scope.shard(
|
|
148
|
+
Shard.current(loader_query.scope.model.connection_class_for_self)
|
|
149
|
+
)
|
|
150
|
+
)
|
|
132
151
|
ret += loader_query.load_records_for_keys(keys) do |record|
|
|
133
152
|
loaders.each { |l| l.set_inverse(record) }
|
|
134
153
|
end
|
|
@@ -157,7 +176,7 @@ module Switchman
|
|
|
157
176
|
end
|
|
158
177
|
|
|
159
178
|
# Disabling to keep closer to rails original
|
|
160
|
-
# rubocop:disable Naming/AccessorMethodName
|
|
179
|
+
# rubocop:disable Naming/AccessorMethodName
|
|
161
180
|
# significant changes:
|
|
162
181
|
# * globalize the key to lookup
|
|
163
182
|
def set_inverse(record)
|
|
@@ -174,7 +193,7 @@ module Switchman
|
|
|
174
193
|
association.set_inverse_instance(record)
|
|
175
194
|
end
|
|
176
195
|
end
|
|
177
|
-
# rubocop:enable Naming/AccessorMethodName
|
|
196
|
+
# rubocop:enable Naming/AccessorMethodName
|
|
178
197
|
|
|
179
198
|
# significant changes:
|
|
180
199
|
# * partition_by_shard the records_for call
|
|
@@ -186,40 +205,7 @@ module Switchman
|
|
|
186
205
|
# #compare_by_identity makes such owners different hash keys
|
|
187
206
|
@records_by_owner = {}.compare_by_identity
|
|
188
207
|
|
|
189
|
-
|
|
190
|
-
raw_records ||= loader_query.records_for([self])
|
|
191
|
-
elsif owner_keys.empty?
|
|
192
|
-
raw_records ||= []
|
|
193
|
-
else
|
|
194
|
-
# determine the shard to search for each owner
|
|
195
|
-
if reflection.macro == :belongs_to
|
|
196
|
-
# for belongs_to, it's the shard of the foreign_key
|
|
197
|
-
partition_proc = lambda do |owner|
|
|
198
|
-
if owner.class.sharded_column?(owner_key_name)
|
|
199
|
-
Shard.shard_for(owner[owner_key_name], owner.shard)
|
|
200
|
-
else
|
|
201
|
-
Shard.current
|
|
202
|
-
end
|
|
203
|
-
end
|
|
204
|
-
elsif !reflection.options[:multishard]
|
|
205
|
-
# for non-multishard associations, it's *just* the owner's shard
|
|
206
|
-
partition_proc = ->(owner) { owner.shard }
|
|
207
|
-
end
|
|
208
|
-
|
|
209
|
-
raw_records ||= Shard.partition_by_shard(owners, partition_proc) do |partitioned_owners|
|
|
210
|
-
relative_owner_keys = partitioned_owners.map do |owner|
|
|
211
|
-
key = owner[owner_key_name]
|
|
212
|
-
if key && owner.class.sharded_column?(owner_key_name)
|
|
213
|
-
key = Shard.relative_id_for(key, owner.shard,
|
|
214
|
-
Shard.current(klass.connection_class_for_self))
|
|
215
|
-
end
|
|
216
|
-
convert_key(key)
|
|
217
|
-
end
|
|
218
|
-
relative_owner_keys.compact!
|
|
219
|
-
relative_owner_keys.uniq!
|
|
220
|
-
records_for(relative_owner_keys)
|
|
221
|
-
end
|
|
222
|
-
end
|
|
208
|
+
raw_records ||= loader_query.records_for([self])
|
|
223
209
|
|
|
224
210
|
@preloaded_records = raw_records.select do |record|
|
|
225
211
|
assignments = false
|
|
@@ -267,8 +253,8 @@ module Switchman
|
|
|
267
253
|
self.shard_source_value = :association
|
|
268
254
|
end
|
|
269
255
|
|
|
270
|
-
def shard(*
|
|
271
|
-
scope.shard(*
|
|
256
|
+
def shard(*)
|
|
257
|
+
scope.shard(*)
|
|
272
258
|
end
|
|
273
259
|
end
|
|
274
260
|
|
|
@@ -276,15 +262,52 @@ module Switchman
|
|
|
276
262
|
def association_foreign_key_changed?(reflection, record, key)
|
|
277
263
|
return false if reflection.through_reflection?
|
|
278
264
|
|
|
265
|
+
foreign_key = Array(reflection.foreign_key)
|
|
266
|
+
return false unless foreign_key.all? { |k| record._has_attribute?(k) }
|
|
267
|
+
|
|
279
268
|
# have to use send instead of _read_attribute because sharding
|
|
280
|
-
|
|
269
|
+
foreign_key.map { |k| record.send(k) } != Array(key)
|
|
281
270
|
end
|
|
282
271
|
|
|
283
272
|
def save_belongs_to_association(reflection)
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
273
|
+
association = association_instance_get(reflection.name)
|
|
274
|
+
return unless association&.loaded? && !association.stale_target?
|
|
275
|
+
|
|
276
|
+
record = association.load_target
|
|
277
|
+
return unless record && !record.destroyed?
|
|
278
|
+
|
|
279
|
+
autosave = reflection.options[:autosave]
|
|
280
|
+
|
|
281
|
+
if autosave && record.marked_for_destruction?
|
|
282
|
+
foreign_key = Array(reflection.foreign_key)
|
|
283
|
+
foreign_key.each { |key| self[key] = nil }
|
|
284
|
+
record.destroy
|
|
285
|
+
elsif autosave != false
|
|
286
|
+
saved = record.save(validate: !autosave) if record.new_record? || (autosave && record.changed_for_autosave?)
|
|
287
|
+
|
|
288
|
+
if association.updated?
|
|
289
|
+
primary_key = Array(compute_primary_key(reflection, record)).map(&:to_s)
|
|
290
|
+
foreign_key = Array(reflection.foreign_key)
|
|
291
|
+
|
|
292
|
+
primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
|
|
293
|
+
primary_key_foreign_key_pairs.each do |pk, fk|
|
|
294
|
+
# Notable change: add relative_id_for here
|
|
295
|
+
association_id = if record.class.sharded_column?(pk)
|
|
296
|
+
Shard.relative_id_for(
|
|
297
|
+
record._read_attribute(pk),
|
|
298
|
+
record.shard,
|
|
299
|
+
shard
|
|
300
|
+
)
|
|
301
|
+
else
|
|
302
|
+
record._read_attribute(pk)
|
|
303
|
+
end
|
|
304
|
+
self[fk] = association_id unless self[fk] == association_id
|
|
305
|
+
end
|
|
306
|
+
association.loaded!
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
saved if autosave
|
|
310
|
+
end
|
|
288
311
|
end
|
|
289
312
|
end
|
|
290
313
|
end
|
|
@@ -26,13 +26,22 @@ module Switchman
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def define_attribute_methods
|
|
29
|
-
super
|
|
29
|
+
result = super
|
|
30
30
|
# ensure that we're using the sharded attribute method
|
|
31
31
|
# and not the silly one in AR::AttributeMethods::PrimaryKey
|
|
32
|
-
return unless sharded_column?(@primary_key)
|
|
33
|
-
|
|
34
|
-
class_eval(
|
|
35
|
-
|
|
32
|
+
return result unless sharded_column?(@primary_key)
|
|
33
|
+
|
|
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__)
|
|
44
|
+
result
|
|
36
45
|
end
|
|
37
46
|
|
|
38
47
|
protected
|
|
@@ -46,18 +55,22 @@ module Switchman
|
|
|
46
55
|
raise if connection.open_transactions.positive?
|
|
47
56
|
end
|
|
48
57
|
|
|
49
|
-
def define_cached_method(owner, name, namespace:, as:, &
|
|
50
|
-
if ::Rails.version <
|
|
51
|
-
|
|
52
|
-
|
|
58
|
+
def define_cached_method(owner, name, namespace:, as:, &)
|
|
59
|
+
if ::Rails.version < "7.1.4"
|
|
60
|
+
# https://github.com/rails/rails/commit/a2a12fc2e3f4e6d06f81d4c74c88f8e6b3369ee6#diff-5b59ece6d9396b596f06271cec0ea726e3360911383511c49b1a66f454bfc2b6L30
|
|
61
|
+
# These arguments were effectively swapped in Rails 7.1.4, so previous versions need them reversed
|
|
62
|
+
owner.define_cached_method(as, namespace:, as: name, &)
|
|
53
63
|
else
|
|
54
|
-
owner.define_cached_method(name, namespace
|
|
64
|
+
owner.define_cached_method(name, namespace:, as:, &)
|
|
55
65
|
end
|
|
56
66
|
end
|
|
57
67
|
|
|
58
|
-
def define_method_global_attribute(attr_name, owner:)
|
|
68
|
+
def define_method_global_attribute(attr_name, owner:, as: attr_name)
|
|
59
69
|
if sharded_column?(attr_name)
|
|
60
|
-
define_cached_method(owner,
|
|
70
|
+
define_cached_method(owner,
|
|
71
|
+
"sharded_global_#{attr_name}",
|
|
72
|
+
as: "global_#{as}",
|
|
73
|
+
namespace: :switchman) do |batch|
|
|
61
74
|
batch << <<-RUBY
|
|
62
75
|
def sharded_global_#{attr_name}
|
|
63
76
|
raw_value = original_#{attr_name}
|
|
@@ -69,13 +82,16 @@ module Switchman
|
|
|
69
82
|
RUBY
|
|
70
83
|
end
|
|
71
84
|
else
|
|
72
|
-
define_method_unsharded_column(attr_name,
|
|
85
|
+
define_method_unsharded_column(attr_name, "global", owner)
|
|
73
86
|
end
|
|
74
87
|
end
|
|
75
88
|
|
|
76
|
-
def define_method_local_attribute(attr_name, owner:)
|
|
89
|
+
def define_method_local_attribute(attr_name, owner:, as: attr_name)
|
|
77
90
|
if sharded_column?(attr_name)
|
|
78
|
-
define_cached_method(owner,
|
|
91
|
+
define_cached_method(owner,
|
|
92
|
+
"sharded_local_#{attr_name}",
|
|
93
|
+
as: "local_#{as}",
|
|
94
|
+
namespace: :switchman) do |batch|
|
|
79
95
|
batch << <<-RUBY
|
|
80
96
|
def sharded_local_#{attr_name}
|
|
81
97
|
raw_value = original_#{attr_name}
|
|
@@ -85,7 +101,7 @@ module Switchman
|
|
|
85
101
|
RUBY
|
|
86
102
|
end
|
|
87
103
|
else
|
|
88
|
-
define_method_unsharded_column(attr_name,
|
|
104
|
+
define_method_unsharded_column(attr_name, "local", owner)
|
|
89
105
|
end
|
|
90
106
|
end
|
|
91
107
|
|
|
@@ -97,7 +113,13 @@ module Switchman
|
|
|
97
113
|
if reflection.options[:polymorphic]
|
|
98
114
|
# a polymorphic association has to be discovered at runtime. This code ends up being something like
|
|
99
115
|
# context_type.&.constantize&.connection_class_for_self
|
|
100
|
-
|
|
116
|
+
<<~RUBY
|
|
117
|
+
begin
|
|
118
|
+
read_attribute(:#{reflection.foreign_type})&.constantize&.connection_class_for_self
|
|
119
|
+
rescue NameError
|
|
120
|
+
::ActiveRecord::Base
|
|
121
|
+
end
|
|
122
|
+
RUBY
|
|
101
123
|
else
|
|
102
124
|
# otherwise we can just return a symbol for the statically known type of the association
|
|
103
125
|
"::#{reflection.klass.connection_class_for_self.name}"
|
|
@@ -107,16 +129,21 @@ module Switchman
|
|
|
107
129
|
end
|
|
108
130
|
end
|
|
109
131
|
|
|
110
|
-
def define_method_attribute(attr_name, owner:)
|
|
132
|
+
def define_method_attribute(attr_name, owner:, as: attr_name)
|
|
111
133
|
if sharded_column?(attr_name)
|
|
112
134
|
reflection = reflection_for_integer_attribute(attr_name)
|
|
113
135
|
class_name = connection_class_for_self_code_for_reflection(reflection)
|
|
114
|
-
safe_class_name = class_name.unpack1(
|
|
115
|
-
define_cached_method(owner,
|
|
116
|
-
|
|
136
|
+
safe_class_name = class_name.unpack1("h*")
|
|
137
|
+
define_cached_method(owner,
|
|
138
|
+
"sharded_#{safe_class_name}_#{attr_name}",
|
|
139
|
+
as:,
|
|
140
|
+
namespace: :switchman) do |batch|
|
|
141
|
+
batch << build_sharded_getter("sharded_#{safe_class_name}_#{attr_name}",
|
|
142
|
+
"original_#{as}",
|
|
143
|
+
class_name)
|
|
117
144
|
end
|
|
118
145
|
else
|
|
119
|
-
define_cached_method(owner,
|
|
146
|
+
define_cached_method(owner, "plain_#{attr_name}", as:, namespace: :switchman) do |batch|
|
|
120
147
|
batch << <<-RUBY
|
|
121
148
|
def plain_#{attr_name}
|
|
122
149
|
_read_attribute("#{attr_name}") { |n| missing_attribute(n, caller) }
|
|
@@ -149,16 +176,19 @@ module Switchman
|
|
|
149
176
|
RUBY
|
|
150
177
|
end
|
|
151
178
|
|
|
152
|
-
def define_method_attribute=(attr_name, owner:)
|
|
179
|
+
def define_method_attribute=(attr_name, owner:, as: attr_name)
|
|
153
180
|
if sharded_column?(attr_name)
|
|
154
181
|
reflection = reflection_for_integer_attribute(attr_name)
|
|
155
182
|
class_name = connection_class_for_self_code_for_reflection(reflection)
|
|
156
|
-
safe_class_name = class_name.unpack1(
|
|
157
|
-
define_cached_method(owner,
|
|
183
|
+
safe_class_name = class_name.unpack1("h*")
|
|
184
|
+
define_cached_method(owner,
|
|
185
|
+
"sharded_#{safe_class_name}_#{attr_name}=",
|
|
186
|
+
as: "#{as}=",
|
|
187
|
+
namespace: :switchman) do |batch|
|
|
158
188
|
batch << build_sharded_setter("sharded_#{safe_class_name}_#{attr_name}", attr_name, class_name)
|
|
159
189
|
end
|
|
160
190
|
else
|
|
161
|
-
define_cached_method(owner, "#{attr_name}=", as: "
|
|
191
|
+
define_cached_method(owner, "plain_#{attr_name}=", as: "#{as}=", namespace: :switchman) do |batch|
|
|
162
192
|
batch << <<-RUBY
|
|
163
193
|
def plain_#{attr_name}=(new_value)
|
|
164
194
|
_write_attribute('#{attr_name}', new_value)
|
|
@@ -176,9 +206,12 @@ module Switchman
|
|
|
176
206
|
RUBY
|
|
177
207
|
end
|
|
178
208
|
|
|
179
|
-
def define_method_original_attribute(attr_name, owner:)
|
|
209
|
+
def define_method_original_attribute(attr_name, owner:, as: attr_name)
|
|
180
210
|
if sharded_column?(attr_name)
|
|
181
|
-
define_cached_method(owner,
|
|
211
|
+
define_cached_method(owner,
|
|
212
|
+
"sharded_original_#{attr_name}",
|
|
213
|
+
as: "original_#{as}",
|
|
214
|
+
namespace: :switchman) do |batch|
|
|
182
215
|
batch << <<-RUBY
|
|
183
216
|
def sharded_original_#{attr_name}
|
|
184
217
|
_read_attribute("#{attr_name}") { |n| missing_attribute(n, caller) }
|
|
@@ -186,14 +219,17 @@ module Switchman
|
|
|
186
219
|
RUBY
|
|
187
220
|
end
|
|
188
221
|
else
|
|
189
|
-
define_method_unsharded_column(attr_name,
|
|
222
|
+
define_method_unsharded_column(attr_name, "global", owner)
|
|
190
223
|
end
|
|
191
224
|
end
|
|
192
225
|
|
|
193
|
-
def define_method_original_attribute=(attr_name, owner:)
|
|
226
|
+
def define_method_original_attribute=(attr_name, owner:, as: attr_name)
|
|
194
227
|
return unless sharded_column?(attr_name)
|
|
195
228
|
|
|
196
|
-
define_cached_method(owner,
|
|
229
|
+
define_cached_method(owner,
|
|
230
|
+
"sharded_original_#{attr_name}=",
|
|
231
|
+
as: "original_#{as}=",
|
|
232
|
+
namespace: :switchman) do |batch|
|
|
197
233
|
batch << <<-RUBY
|
|
198
234
|
def sharded_original_#{attr_name}=(new_value)
|
|
199
235
|
_write_attribute('#{attr_name}', new_value)
|
|
@@ -203,9 +239,12 @@ module Switchman
|
|
|
203
239
|
end
|
|
204
240
|
|
|
205
241
|
def define_method_unsharded_column(attr_name, prefix, owner)
|
|
206
|
-
return if columns_hash["#{prefix}_#{attr_name}"] || attr_name ==
|
|
242
|
+
return if columns_hash["#{prefix}_#{attr_name}"] || attr_name == "id"
|
|
207
243
|
|
|
208
|
-
define_cached_method(owner,
|
|
244
|
+
define_cached_method(owner,
|
|
245
|
+
"unsharded_#{prefix}_#{attr_name}",
|
|
246
|
+
as: "#{prefix}_#{attr_name}",
|
|
247
|
+
namespace: :switchman) do |batch|
|
|
209
248
|
batch << <<-RUBY
|
|
210
249
|
def unsharded_#{prefix}_#{attr_name}
|
|
211
250
|
raise NoMethodError, "undefined method `#{prefix}_#{attr_name}'; are you missing an association?"
|
|
@@ -217,8 +256,8 @@ module Switchman
|
|
|
217
256
|
|
|
218
257
|
def self.prepended(klass)
|
|
219
258
|
klass.singleton_class.prepend(ClassMethods)
|
|
220
|
-
klass.attribute_method_prefix
|
|
221
|
-
klass.attribute_method_affix prefix:
|
|
259
|
+
klass.attribute_method_prefix "global_", "local_", "original_"
|
|
260
|
+
klass.attribute_method_affix prefix: "original_", suffix: "="
|
|
222
261
|
end
|
|
223
262
|
|
|
224
263
|
# these are called if the specific methods haven't been defined yet
|
|
@@ -226,7 +265,11 @@ module Switchman
|
|
|
226
265
|
return super unless self.class.sharded_column?(attr_name)
|
|
227
266
|
|
|
228
267
|
reflection = self.class.send(:reflection_for_integer_attribute, attr_name)
|
|
229
|
-
::Switchman::Shard.relative_id_for(
|
|
268
|
+
::Switchman::Shard.relative_id_for(
|
|
269
|
+
super,
|
|
270
|
+
shard,
|
|
271
|
+
::Switchman::Shard.current(connection_class_for_self_for_reflection(reflection))
|
|
272
|
+
)
|
|
230
273
|
end
|
|
231
274
|
|
|
232
275
|
def attribute=(attr_name, new_value)
|
|
@@ -236,7 +279,11 @@ module Switchman
|
|
|
236
279
|
end
|
|
237
280
|
|
|
238
281
|
reflection = self.class.send(:reflection_for_integer_attribute, attr_name)
|
|
239
|
-
super(
|
|
282
|
+
super(attr_name, ::Switchman::Shard.relative_id_for(
|
|
283
|
+
new_value,
|
|
284
|
+
::Switchman::Shard.current(connection_class_for_self_for_reflection(reflection)),
|
|
285
|
+
shard
|
|
286
|
+
))
|
|
240
287
|
end
|
|
241
288
|
|
|
242
289
|
def global_attribute(attr_name)
|