active_record_shards 4.0.0.beta9 → 5.0.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/README.md +126 -57
- data/lib/active_record_shards/association_collection_connection_selection.rb +16 -14
- data/lib/active_record_shards/configuration_parser.rb +6 -5
- data/lib/active_record_shards/{connection_switcher_5_1.rb → connection_switcher-5-1.rb} +11 -1
- data/lib/active_record_shards/connection_switcher-6-0.rb +30 -0
- data/lib/active_record_shards/connection_switcher.rb +131 -46
- data/lib/active_record_shards/default_replica_patches.rb +237 -0
- data/lib/active_record_shards/migration.rb +124 -0
- data/lib/active_record_shards/model.rb +36 -21
- data/lib/active_record_shards/schema_dumper_extension.rb +42 -0
- data/lib/active_record_shards/shard_selection.rb +47 -7
- data/lib/active_record_shards/shard_support.rb +7 -7
- data/lib/active_record_shards/sql_comments.rb +11 -4
- data/lib/active_record_shards/tasks.rb +46 -29
- data/lib/active_record_shards.rb +59 -12
- metadata +64 -52
- data/lib/active_record_shards/base_config.rb +0 -26
- data/lib/active_record_shards/connection_resolver.rb +0 -31
- data/lib/active_record_shards/connection_switcher_5_0.rb +0 -19
- data/lib/active_record_shards/default_slave_patches.rb +0 -136
- data/lib/active_record_shards/no_shard_selection.rb +0 -18
- data/lib/active_record_shards/sharded_model.rb +0 -31
- data/lib/active_record_shards/slave_db.rb +0 -105
@@ -1,136 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
module ActiveRecordShards
|
3
|
-
module DefaultSlavePatches
|
4
|
-
def self.wrap_method_in_on_slave(class_method, base, method)
|
5
|
-
base_methods =
|
6
|
-
if class_method
|
7
|
-
base.methods + base.private_methods
|
8
|
-
else
|
9
|
-
base.instance_methods + base.private_instance_methods
|
10
|
-
end
|
11
|
-
|
12
|
-
return unless base_methods.include?(method)
|
13
|
-
_, method, punctuation = method.to_s.match(/^(.*?)([\?\!]?)$/).to_a
|
14
|
-
base.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
15
|
-
#{class_method ? 'class << self' : ''}
|
16
|
-
def #{method}_with_default_slave#{punctuation}(*args, &block)
|
17
|
-
on_slave_unless_tx do
|
18
|
-
#{method}_without_default_slave#{punctuation}(*args, &block)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
alias_method :#{method}_without_default_slave#{punctuation}, :#{method}#{punctuation}
|
23
|
-
alias_method :#{method}#{punctuation}, :#{method}_with_default_slave#{punctuation}
|
24
|
-
#{class_method ? 'end' : ''}
|
25
|
-
RUBY
|
26
|
-
end
|
27
|
-
|
28
|
-
def columns_with_force_slave(*args, &block)
|
29
|
-
force_cx_switch_slave_block do
|
30
|
-
columns_without_force_slave(*args, &block)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def table_exists_with_force_slave?(*args, &block)
|
35
|
-
force_cx_switch_slave_block do
|
36
|
-
table_exists_without_force_slave?(*args, &block)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def transaction_with_slave_off(*args, &block)
|
41
|
-
if on_slave_by_default?
|
42
|
-
begin
|
43
|
-
old_val = Thread.current[:_active_record_shards_slave_off]
|
44
|
-
Thread.current[:_active_record_shards_slave_off] = true
|
45
|
-
transaction_without_slave_off(*args, &block)
|
46
|
-
ensure
|
47
|
-
Thread.current[:_active_record_shards_slave_off] = old_val
|
48
|
-
end
|
49
|
-
else
|
50
|
-
transaction_without_slave_off(*args, &block)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
module InstanceMethods
|
55
|
-
# fix ActiveRecord to do the right thing, and use our aliased quote_value
|
56
|
-
def quote_value(*args, &block)
|
57
|
-
self.class.quote_value(*args, &block)
|
58
|
-
end
|
59
|
-
|
60
|
-
def reload_with_slave_off(*args, &block)
|
61
|
-
self.class.on_master { reload_without_slave_off(*args, &block) }
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
CLASS_SLAVE_METHODS = [:find_by_sql, :count_by_sql, :calculate, :find_one, :find_some, :find_every, :exists?].freeze
|
66
|
-
|
67
|
-
def self.extended(base)
|
68
|
-
CLASS_SLAVE_METHODS.each { |m| ActiveRecordShards::DefaultSlavePatches.wrap_method_in_on_slave(true, base, m) }
|
69
|
-
|
70
|
-
base.class_eval do
|
71
|
-
include InstanceMethods
|
72
|
-
|
73
|
-
alias_method :reload_without_slave_off, :reload
|
74
|
-
alias_method :reload, :reload_with_slave_off
|
75
|
-
|
76
|
-
class << self
|
77
|
-
alias_method :columns_without_force_slave, :columns
|
78
|
-
alias_method :columns, :columns_with_force_slave
|
79
|
-
|
80
|
-
alias_method :table_exists_without_force_slave?, :table_exists?
|
81
|
-
alias_method :table_exists?, :table_exists_with_force_slave?
|
82
|
-
|
83
|
-
alias_method :transaction_without_slave_off, :transaction
|
84
|
-
alias_method :transaction, :transaction_with_slave_off
|
85
|
-
end
|
86
|
-
end
|
87
|
-
if ActiveRecord::Associations.const_defined?(:HasAndBelongsToManyAssociation)
|
88
|
-
ActiveRecordShards::DefaultSlavePatches.wrap_method_in_on_slave(false, ActiveRecord::Associations::HasAndBelongsToManyAssociation, :construct_sql)
|
89
|
-
ActiveRecordShards::DefaultSlavePatches.wrap_method_in_on_slave(false, ActiveRecord::Associations::HasAndBelongsToManyAssociation, :construct_find_options!)
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
def on_slave_unless_tx
|
94
|
-
if on_slave_by_default? && !Thread.current[:_active_record_shards_slave_off]
|
95
|
-
on_slave { yield }
|
96
|
-
else
|
97
|
-
yield
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
module ActiveRelationPatches
|
102
|
-
def self.included(base)
|
103
|
-
[:calculate, :exists?, :pluck, :find_with_associations].each do |m|
|
104
|
-
ActiveRecordShards::DefaultSlavePatches.wrap_method_in_on_slave(false, base, m)
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
def on_slave_unless_tx
|
109
|
-
@klass.on_slave_unless_tx { yield }
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
# in rails 4.1+, they create a join class that's used to pull in records for HABTM.
|
114
|
-
# this simplifies the hell out of our existence, because all we have to do is inerit on-slave-by-default
|
115
|
-
# down from the parent now.
|
116
|
-
module HasAndBelongsToManyBuilderExtension
|
117
|
-
def self.included(base)
|
118
|
-
base.class_eval do
|
119
|
-
alias_method :through_model_without_inherit_default_slave_from_lhs, :through_model
|
120
|
-
alias_method :through_model, :through_model_with_inherit_default_slave_from_lhs
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
def through_model_with_inherit_default_slave_from_lhs
|
125
|
-
model = through_model_without_inherit_default_slave_from_lhs
|
126
|
-
def model.on_slave_by_default?
|
127
|
-
left_reflection.klass.on_slave_by_default?
|
128
|
-
end
|
129
|
-
|
130
|
-
model.extend(ActiveRecordShards::Ext::ShardedModel) if model.left_reflection.klass.is_sharded?
|
131
|
-
|
132
|
-
model
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
module ActiveRecordShards
|
3
|
-
class NoShardSelection
|
4
|
-
class NoShardSelected < RuntimeError; end
|
5
|
-
|
6
|
-
def shard
|
7
|
-
raise NoShardSelected, "Missing shard information on connection"
|
8
|
-
end
|
9
|
-
|
10
|
-
def connection_config
|
11
|
-
raise NoShardSelected, "No shard selected, can't connect"
|
12
|
-
end
|
13
|
-
|
14
|
-
def on_shard?
|
15
|
-
false
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
@@ -1,31 +0,0 @@
|
|
1
|
-
module ActiveRecordShards
|
2
|
-
module Ext
|
3
|
-
module ShardedModel
|
4
|
-
def self.extended(base)
|
5
|
-
base.extend(ActiveRecordShards::ConnectionSwitcher)
|
6
|
-
base.include(InstanceMethods)
|
7
|
-
base.after_initialize :initialize_shard
|
8
|
-
end
|
9
|
-
|
10
|
-
def is_sharded? # rubocop:disable Naming/PredicateName
|
11
|
-
true
|
12
|
-
end
|
13
|
-
|
14
|
-
module InstanceMethods
|
15
|
-
def initialize_shard
|
16
|
-
@from_shard = self.class.current_shard_selection.shard
|
17
|
-
end
|
18
|
-
|
19
|
-
def from_shard
|
20
|
-
@from_shard
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
class ShardedModel < ActiveRecord::Base
|
27
|
-
self.abstract_class = true
|
28
|
-
|
29
|
-
extend ActiveRecordShards::Ext::ShardedModel
|
30
|
-
end
|
31
|
-
end
|
@@ -1,105 +0,0 @@
|
|
1
|
-
module ActiveRecordShards
|
2
|
-
module SlaveDb
|
3
|
-
def on_slave_if(condition, &block)
|
4
|
-
condition ? on_slave(&block) : yield
|
5
|
-
end
|
6
|
-
|
7
|
-
def on_slave_unless(condition, &block)
|
8
|
-
on_slave_if(!condition, &block)
|
9
|
-
end
|
10
|
-
|
11
|
-
def on_master_if(condition, &block)
|
12
|
-
condition ? on_master(&block) : yield
|
13
|
-
end
|
14
|
-
|
15
|
-
def on_master_unless(condition, &block)
|
16
|
-
on_master_if(!condition, &block)
|
17
|
-
end
|
18
|
-
|
19
|
-
def on_master_or_slave(which, &block)
|
20
|
-
if block_given?
|
21
|
-
on_cx_switch_block(which, &block)
|
22
|
-
else
|
23
|
-
MasterSlaveProxy.new(self, which)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def on_slave?
|
28
|
-
current_slave_selection
|
29
|
-
end
|
30
|
-
|
31
|
-
# Executes queries using the slave database. Fails over to master if no slave is found.
|
32
|
-
# if you want to execute a block of code on the slave you can go:
|
33
|
-
# Account.on_slave do
|
34
|
-
# Account.first
|
35
|
-
# end
|
36
|
-
# the first account will be found on the slave DB
|
37
|
-
#
|
38
|
-
# For one-liners you can simply do
|
39
|
-
# Account.on_slave.first
|
40
|
-
def on_slave(&block)
|
41
|
-
on_master_or_slave(:slave, &block)
|
42
|
-
end
|
43
|
-
|
44
|
-
def on_master(&block)
|
45
|
-
on_master_or_slave(:master, &block)
|
46
|
-
end
|
47
|
-
|
48
|
-
def force_cx_switch_slave_block
|
49
|
-
old_options = current_slave_selection
|
50
|
-
switch_slave_connection(slave: true)
|
51
|
-
yield
|
52
|
-
ensure
|
53
|
-
switch_slave_connection(slave: old_options)
|
54
|
-
end
|
55
|
-
|
56
|
-
def on_cx_switch_block(which, &block)
|
57
|
-
@disallow_slave ||= 0
|
58
|
-
@disallow_slave += 1 if which == :master
|
59
|
-
|
60
|
-
switch_to_slave = @disallow_slave.zero?
|
61
|
-
old_options = current_slave_selection
|
62
|
-
|
63
|
-
switch_slave_connection(slave: switch_to_slave)
|
64
|
-
|
65
|
-
# we avoid_readonly_scope to prevent some stack overflow problems, like when
|
66
|
-
# .columns calls .with_scope which calls .columns and onward, endlessly.
|
67
|
-
if self == ActiveRecord::Base || !switch_to_slave
|
68
|
-
yield
|
69
|
-
else
|
70
|
-
readonly.scoping(&block)
|
71
|
-
end
|
72
|
-
ensure
|
73
|
-
@disallow_slave -= 1 if which == :master
|
74
|
-
switch_slave_connection(slave: old_options)
|
75
|
-
end
|
76
|
-
|
77
|
-
def current_slave_selection=(on_slave)
|
78
|
-
Thread.current[:slave_selection] = on_slave
|
79
|
-
end
|
80
|
-
|
81
|
-
def current_slave_selection
|
82
|
-
!!Thread.current[:slave_selection]
|
83
|
-
end
|
84
|
-
|
85
|
-
def connection_config
|
86
|
-
super.merge(slave: current_slave_selection)
|
87
|
-
end
|
88
|
-
|
89
|
-
def switch_slave_connection(options)
|
90
|
-
self.current_slave_selection = options[:slave]
|
91
|
-
ensure_shard_connection
|
92
|
-
end
|
93
|
-
|
94
|
-
class MasterSlaveProxy
|
95
|
-
def initialize(target, which)
|
96
|
-
@target = target
|
97
|
-
@which = which
|
98
|
-
end
|
99
|
-
|
100
|
-
def method_missing(method, *args, &block) # rubocop:disable Style/MethodMissing
|
101
|
-
@target.on_master_or_slave(@which) { @target.send(method, *args, &block) }
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|