ar-octopus 0.8.1 → 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/README.mkdn +80 -55
- data/lib/octopus.rb +20 -6
- data/lib/octopus/{rails3/abstract_adapter.rb → abstract_adapter.rb} +1 -4
- data/lib/octopus/association.rb +5 -99
- data/lib/octopus/association_shard_tracking.rb +105 -0
- data/lib/octopus/collection_association.rb +9 -0
- data/lib/octopus/collection_proxy.rb +14 -0
- data/lib/octopus/has_and_belongs_to_many_association.rb +2 -12
- data/lib/octopus/load_balancing.rb +3 -0
- data/lib/octopus/load_balancing/round_robin.rb +15 -0
- data/lib/octopus/{rails3/log_subscriber.rb → log_subscriber.rb} +0 -0
- data/lib/octopus/model.rb +74 -85
- data/lib/octopus/{rails3/persistence.rb → persistence.rb} +0 -0
- data/lib/octopus/proxy.rb +166 -29
- data/lib/octopus/relation_proxy.rb +39 -0
- data/lib/octopus/scope_proxy.rb +7 -10
- data/lib/octopus/shard_tracking.rb +45 -0
- data/lib/octopus/shard_tracking/attribute.rb +24 -0
- data/lib/octopus/shard_tracking/dynamic.rb +7 -0
- data/lib/octopus/singular_association.rb +7 -0
- data/lib/octopus/slave_group.rb +11 -0
- data/lib/octopus/version.rb +1 -1
- data/spec/config/shards.yml +53 -0
- data/spec/octopus/{association_spec.rb → association_shard_tracking_spec.rb} +1 -1
- data/spec/octopus/collection_proxy_spec.rb +15 -0
- data/spec/octopus/model_spec.rb +2 -2
- data/spec/octopus/octopus_spec.rb +34 -0
- data/spec/octopus/relation_proxy_spec.rb +77 -0
- data/spec/octopus/replicated_slave_grouped_spec.rb +64 -0
- data/spec/octopus/sharded_replicated_slave_grouped_spec.rb +55 -0
- data/spec/support/octopus_helper.rb +1 -0
- metadata +26 -9
- data/lib/octopus/association_collection.rb +0 -49
- data/lib/octopus/rails3/singular_association.rb +0 -34
@@ -0,0 +1,9 @@
|
|
1
|
+
module Octopus::CollectionAssociation
|
2
|
+
def self.included(base)
|
3
|
+
base.sharded_methods :reader, :writer, :ids_reader, :ids_writer, :create, :create!,
|
4
|
+
:build, :any?, :count, :empty?, :first, :include?, :last, :length,
|
5
|
+
:load_target, :many?, :reload, :size, :select, :uniq
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
ActiveRecord::Associations::CollectionAssociation.send(:include, Octopus::CollectionAssociation)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Octopus::CollectionProxy
|
2
|
+
def self.included(base)
|
3
|
+
base.send(:include, Octopus::ShardTracking::Dynamic)
|
4
|
+
base.sharded_methods :any?, :build, :count, :create, :create!, :concat, :delete, :delete_all,
|
5
|
+
:destroy, :destroy_all, :empty?, :find, :first, :include?, :last, :length,
|
6
|
+
:many?, :pluck, :replace, :select, :size, :sum, :to_a, :uniq
|
7
|
+
end
|
8
|
+
|
9
|
+
def current_shard
|
10
|
+
@association.owner.current_shard
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
ActiveRecord::Associations::CollectionProxy.send(:include, Octopus::CollectionProxy)
|
@@ -1,17 +1,7 @@
|
|
1
1
|
module Octopus::HasAndBelongsToManyAssociation
|
2
2
|
def self.included(base)
|
3
|
-
base.
|
4
|
-
alias_method_chain :insert_record, :octopus
|
5
|
-
end
|
6
|
-
end
|
7
|
-
|
8
|
-
def insert_record_with_octopus(record, force = true, validate = true)
|
9
|
-
if should_wrap_the_connection?
|
10
|
-
Octopus.using(@owner.current_shard) { insert_record_without_octopus(record, force, validate) }
|
11
|
-
else
|
12
|
-
insert_record_without_octopus(record, force, validate)
|
13
|
-
end
|
3
|
+
base.sharded_methods :insert_record
|
14
4
|
end
|
15
5
|
end
|
16
6
|
|
17
|
-
ActiveRecord::Associations::HasAndBelongsToManyAssociation.send(:include, Octopus::HasAndBelongsToManyAssociation)
|
7
|
+
ActiveRecord::Associations::HasAndBelongsToManyAssociation.send(:include, Octopus::HasAndBelongsToManyAssociation)
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'octopus/load_balancing'
|
2
|
+
|
3
|
+
# The round-robin load balancing of slaves belonging to the same shard.
|
4
|
+
# It is a pool that contains slaves which queries are distributed to.
|
5
|
+
class Octopus::LoadBalancing::RoundRobin
|
6
|
+
def initialize(slaves_list)
|
7
|
+
@slaves_list = slaves_list
|
8
|
+
@slave_index = 0
|
9
|
+
end
|
10
|
+
|
11
|
+
# Returns the next available slave in the pool
|
12
|
+
def next
|
13
|
+
@slaves_list[@slave_index = (@slave_index + 1) % @slaves_list.length]
|
14
|
+
end
|
15
|
+
end
|
File without changes
|
data/lib/octopus/model.rb
CHANGED
@@ -2,10 +2,9 @@ require 'active_support/deprecation'
|
|
2
2
|
|
3
3
|
module Octopus::Model
|
4
4
|
def self.extended(base)
|
5
|
+
base.send(:include, Octopus::ShardTracking::Attribute)
|
5
6
|
base.send(:include, InstanceMethods)
|
6
7
|
base.extend(ClassMethods)
|
7
|
-
base.hijack_connection()
|
8
|
-
base.hijack_initializer()
|
9
8
|
end
|
10
9
|
|
11
10
|
module SharedMethods
|
@@ -28,82 +27,6 @@ module Octopus::Model
|
|
28
27
|
self
|
29
28
|
end
|
30
29
|
end
|
31
|
-
|
32
|
-
def hijack_initializer()
|
33
|
-
attr_accessor :current_shard
|
34
|
-
around_save :run_on_shard
|
35
|
-
|
36
|
-
def set_current_shard
|
37
|
-
return unless Octopus.enabled?
|
38
|
-
|
39
|
-
if new_record? || self.class.connection_proxy.block
|
40
|
-
self.current_shard = self.class.connection_proxy.current_shard
|
41
|
-
else
|
42
|
-
self.current_shard = self.class.connection_proxy.last_current_shard || self.class.connection_proxy.current_shard
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
after_initialize :set_current_shard
|
47
|
-
end
|
48
|
-
|
49
|
-
def hijack_connection()
|
50
|
-
def self.should_use_normal_connection?
|
51
|
-
!Octopus.enabled? || self.custom_octopus_connection
|
52
|
-
end
|
53
|
-
|
54
|
-
def self.connection_proxy
|
55
|
-
@@connection_proxy ||= Octopus::Proxy.new
|
56
|
-
end
|
57
|
-
|
58
|
-
def self.connection_with_octopus
|
59
|
-
if should_use_normal_connection?
|
60
|
-
connection_without_octopus
|
61
|
-
else
|
62
|
-
self.connection_proxy.current_model = self
|
63
|
-
self.connection_proxy
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def self.connection_pool_with_octopus
|
68
|
-
if should_use_normal_connection?
|
69
|
-
connection_pool_without_octopus
|
70
|
-
else
|
71
|
-
connection_proxy.connection_pool
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
def self.clear_active_connections_with_octopus!
|
76
|
-
if should_use_normal_connection?
|
77
|
-
clear_active_connections_without_octopus!
|
78
|
-
else
|
79
|
-
connection_proxy.clear_active_connections!
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def self.clear_all_connections_with_octopus!
|
84
|
-
if should_use_normal_connection?
|
85
|
-
clear_all_connections_without_octopus!
|
86
|
-
else
|
87
|
-
connection_proxy.clear_all_connections!
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
def self.connected_with_octopus?
|
92
|
-
if should_use_normal_connection?
|
93
|
-
connected_without_octopus?
|
94
|
-
else
|
95
|
-
connection_proxy.connected?
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
class << self
|
100
|
-
alias_method_chain :connection, :octopus
|
101
|
-
alias_method_chain :connection_pool, :octopus
|
102
|
-
alias_method_chain :clear_all_connections!, :octopus
|
103
|
-
alias_method_chain :clear_active_connections!, :octopus
|
104
|
-
alias_method_chain :connected?, :octopus
|
105
|
-
end
|
106
|
-
end
|
107
30
|
end
|
108
31
|
|
109
32
|
module InstanceMethods
|
@@ -116,18 +39,20 @@ module Octopus::Model
|
|
116
39
|
base.send(:alias_method_chain, :perform_validations, :octopus)
|
117
40
|
end
|
118
41
|
|
119
|
-
def
|
120
|
-
|
121
|
-
end
|
42
|
+
def set_current_shard
|
43
|
+
return unless Octopus.enabled?
|
122
44
|
|
123
|
-
|
124
|
-
|
125
|
-
self.class.connection_proxy.run_queries_on_shard(self.current_shard, &block)
|
45
|
+
if new_record? || self.class.connection_proxy.block
|
46
|
+
self.current_shard = self.class.connection_proxy.current_shard
|
126
47
|
else
|
127
|
-
|
48
|
+
self.current_shard = self.class.connection_proxy.last_current_shard || self.class.connection_proxy.current_shard
|
128
49
|
end
|
129
50
|
end
|
130
51
|
|
52
|
+
def should_set_current_shard?
|
53
|
+
self.respond_to?(:current_shard) && !self.current_shard.nil?
|
54
|
+
end
|
55
|
+
|
131
56
|
def equality_with_octopus(comparison_object)
|
132
57
|
equality_without_octopus(comparison_object) && comparison_object.current_shard == current_shard
|
133
58
|
end
|
@@ -161,10 +86,19 @@ module Octopus::Model
|
|
161
86
|
end
|
162
87
|
|
163
88
|
def hijack_methods
|
89
|
+
around_save :run_on_shard
|
90
|
+
after_initialize :set_current_shard
|
91
|
+
|
164
92
|
class << self
|
165
93
|
attr_accessor :custom_octopus_connection
|
166
94
|
attr_accessor :custom_octopus_table_name
|
167
95
|
|
96
|
+
alias_method_chain :connection, :octopus
|
97
|
+
alias_method_chain :connection_pool, :octopus
|
98
|
+
alias_method_chain :clear_all_connections!, :octopus
|
99
|
+
alias_method_chain :clear_active_connections!, :octopus
|
100
|
+
alias_method_chain :connected?, :octopus
|
101
|
+
|
168
102
|
if Octopus.rails3?
|
169
103
|
alias_method_chain(:set_table_name, :octopus)
|
170
104
|
end
|
@@ -176,6 +110,61 @@ module Octopus::Model
|
|
176
110
|
end
|
177
111
|
end
|
178
112
|
|
113
|
+
def connection_proxy
|
114
|
+
cached = ActiveRecord::Base.class_variable_get :@@connection_proxy rescue nil
|
115
|
+
cached ||
|
116
|
+
begin
|
117
|
+
p = Octopus::Proxy.new
|
118
|
+
ActiveRecord::Base.class_variable_set :@@connection_proxy, p
|
119
|
+
p
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def should_use_normal_connection?
|
124
|
+
!Octopus.enabled? || custom_octopus_connection
|
125
|
+
end
|
126
|
+
|
127
|
+
def connection_with_octopus
|
128
|
+
if should_use_normal_connection?
|
129
|
+
connection_without_octopus
|
130
|
+
else
|
131
|
+
connection_proxy.current_model = self
|
132
|
+
connection_proxy
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def connection_pool_with_octopus
|
137
|
+
if should_use_normal_connection?
|
138
|
+
connection_pool_without_octopus
|
139
|
+
else
|
140
|
+
connection_proxy.connection_pool
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def clear_active_connections_with_octopus!
|
145
|
+
if should_use_normal_connection?
|
146
|
+
clear_active_connections_without_octopus!
|
147
|
+
else
|
148
|
+
connection_proxy.clear_active_connections!
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def clear_all_connections_with_octopus!
|
153
|
+
if should_use_normal_connection?
|
154
|
+
clear_all_connections_without_octopus!
|
155
|
+
else
|
156
|
+
connection_proxy.clear_all_connections!
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def connected_with_octopus?
|
161
|
+
if should_use_normal_connection?
|
162
|
+
connected_without_octopus?
|
163
|
+
else
|
164
|
+
connection_proxy.connected?
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
179
168
|
def set_table_name_with_octopus(value = nil, &block)
|
180
169
|
self.custom_octopus_table_name = true
|
181
170
|
set_table_name_without_octopus(value, &block)
|
File without changes
|
data/lib/octopus/proxy.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
require "set"
|
2
|
+
require 'octopus/slave_group'
|
3
|
+
require 'octopus/load_balancing/round_robin'
|
2
4
|
|
3
5
|
class Octopus::Proxy
|
4
|
-
attr_accessor :config
|
6
|
+
attr_accessor :config, :sharded
|
5
7
|
|
6
8
|
def initialize(config = Octopus.config)
|
7
9
|
initialize_shards(config)
|
@@ -10,18 +12,20 @@ class Octopus::Proxy
|
|
10
12
|
|
11
13
|
def initialize_shards(config)
|
12
14
|
@shards = HashWithIndifferentAccess.new
|
15
|
+
@shards_slave_groups = HashWithIndifferentAccess.new
|
16
|
+
@slave_groups = HashWithIndifferentAccess.new
|
13
17
|
@groups = {}
|
14
18
|
@adapters = Set.new
|
15
19
|
@config = ActiveRecord::Base.connection_pool_without_octopus.connection.instance_variable_get(:@config)
|
16
20
|
|
17
21
|
if !config.nil?
|
18
22
|
@entire_sharded = config['entire_sharded']
|
19
|
-
shards_config = config[Octopus.rails_env()]
|
23
|
+
@shards_config = config[Octopus.rails_env()]
|
20
24
|
end
|
21
25
|
|
22
|
-
shards_config ||= []
|
26
|
+
@shards_config ||= []
|
23
27
|
|
24
|
-
shards_config.each do |key, value|
|
28
|
+
@shards_config.each do |key, value|
|
25
29
|
if value.is_a?(String)
|
26
30
|
value = resolve_string_connection(value).merge(:octopus_shard => key)
|
27
31
|
initialize_adapter(value['adapter'])
|
@@ -30,6 +34,24 @@ class Octopus::Proxy
|
|
30
34
|
value.merge!(:octopus_shard => key)
|
31
35
|
initialize_adapter(value['adapter'])
|
32
36
|
@shards[key.to_sym] = connection_pool_for(value, "#{value['adapter']}_connection")
|
37
|
+
|
38
|
+
slave_group_configs = value.select do |k,v|
|
39
|
+
structurally_slave_group? v
|
40
|
+
end
|
41
|
+
|
42
|
+
if slave_group_configs.present?
|
43
|
+
slave_groups = HashWithIndifferentAccess.new
|
44
|
+
slave_group_configs.each do |slave_group_name, slave_configs|
|
45
|
+
slaves = HashWithIndifferentAccess.new
|
46
|
+
slave_configs.each do |slave_name, slave_config|
|
47
|
+
@shards[slave_name.to_sym] = connection_pool_for(slave_config, "#{value['adapter']}_connection")
|
48
|
+
slaves[slave_name.to_sym] = slave_name.to_sym
|
49
|
+
end
|
50
|
+
slave_groups[slave_group_name.to_sym] = Octopus::SlaveGroup.new(slaves)
|
51
|
+
end
|
52
|
+
@shards_slave_groups[key.to_sym] = slave_groups
|
53
|
+
@sharded = true
|
54
|
+
end
|
33
55
|
elsif value.is_a?(Hash)
|
34
56
|
@groups[key.to_s] = []
|
35
57
|
|
@@ -42,6 +64,11 @@ class Octopus::Proxy
|
|
42
64
|
@shards[k.to_sym] = connection_pool_for(config_with_octopus_shard, "#{v['adapter']}_connection")
|
43
65
|
@groups[key.to_s] << k.to_sym
|
44
66
|
end
|
67
|
+
|
68
|
+
if structurally_slave_group? value
|
69
|
+
slaves = Hash[@groups[key.to_s].map { |v| [v, v ] }]
|
70
|
+
@slave_groups[key.to_sym] = Octopus::SlaveGroup.new(slaves)
|
71
|
+
end
|
45
72
|
end
|
46
73
|
end
|
47
74
|
|
@@ -58,7 +85,7 @@ class Octopus::Proxy
|
|
58
85
|
|
59
86
|
@slaves_list = @shards.keys.map {|sym| sym.to_s}.sort
|
60
87
|
@slaves_list.delete('master')
|
61
|
-
@
|
88
|
+
@slaves_load_balancer = Octopus::LoadBalancing::RoundRobin.new(@slaves_list)
|
62
89
|
end
|
63
90
|
|
64
91
|
def current_model
|
@@ -74,8 +101,29 @@ class Octopus::Proxy
|
|
74
101
|
end
|
75
102
|
|
76
103
|
def current_shard=(shard_symbol)
|
104
|
+
self.current_slave_group = nil
|
77
105
|
if shard_symbol.is_a?(Array)
|
78
106
|
shard_symbol.each {|symbol| raise "Nonexistent Shard Name: #{symbol}" if @shards[symbol].nil? }
|
107
|
+
elsif shard_symbol.is_a?(Hash)
|
108
|
+
hash = shard_symbol
|
109
|
+
shard_symbol = hash[:shard]
|
110
|
+
slave_group_symbol = hash[:slave_group]
|
111
|
+
|
112
|
+
if shard_symbol.nil? && slave_group_symbol.nil?
|
113
|
+
raise "Neither shard or slave group must be specified"
|
114
|
+
end
|
115
|
+
|
116
|
+
if shard_symbol.present?
|
117
|
+
raise "Nonexistent Shard Name: #{shard_symbol}" if @shards[shard_symbol].nil?
|
118
|
+
end
|
119
|
+
|
120
|
+
if slave_group_symbol.present?
|
121
|
+
if (@shards_slave_groups.try(:[], shard_symbol).present? && @shards_slave_groups[shard_symbol][slave_group_symbol].nil?) ||
|
122
|
+
(@shards_slave_groups.try(:[], shard_symbol).nil? && @slave_groups[slave_group_symbol].nil?)
|
123
|
+
raise "Nonexistent Slave Group Name: #{slave_group_symbol} in shards config: #{@shards_config.inspect}"
|
124
|
+
end
|
125
|
+
self.current_slave_group = slave_group_symbol
|
126
|
+
end
|
79
127
|
else
|
80
128
|
raise "Nonexistent Shard Name: #{shard_symbol}" if @shards[shard_symbol].nil?
|
81
129
|
end
|
@@ -96,6 +144,14 @@ class Octopus::Proxy
|
|
96
144
|
Thread.current["octopus.current_group"] = group_symbol
|
97
145
|
end
|
98
146
|
|
147
|
+
def current_slave_group
|
148
|
+
Thread.current["octopus.current_slave_group"]
|
149
|
+
end
|
150
|
+
|
151
|
+
def current_slave_group=(slave_group_symbol)
|
152
|
+
Thread.current["octopus.current_slave_group"] = slave_group_symbol
|
153
|
+
end
|
154
|
+
|
99
155
|
def block
|
100
156
|
Thread.current["octopus.block"]
|
101
157
|
end
|
@@ -112,6 +168,10 @@ class Octopus::Proxy
|
|
112
168
|
Thread.current["octopus.last_current_shard"] = last_current_shard
|
113
169
|
end
|
114
170
|
|
171
|
+
def fully_replicated?
|
172
|
+
@fully_replicated || Thread.current["octopus.fully_replicated"]
|
173
|
+
end
|
174
|
+
|
115
175
|
# Public: Whether or not a group exists with the given name converted to a
|
116
176
|
# string.
|
117
177
|
#
|
@@ -157,16 +217,10 @@ class Octopus::Proxy
|
|
157
217
|
end
|
158
218
|
|
159
219
|
def run_queries_on_shard(shard, &block)
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
self.block = true
|
165
|
-
self.current_shard = shard
|
166
|
-
yield
|
167
|
-
ensure
|
168
|
-
self.block = last_block || false
|
169
|
-
self.current_shard = older_shard
|
220
|
+
keeping_connection_proxy do
|
221
|
+
using_shard(shard) do
|
222
|
+
yield
|
223
|
+
end
|
170
224
|
end
|
171
225
|
end
|
172
226
|
|
@@ -176,7 +230,7 @@ class Octopus::Proxy
|
|
176
230
|
end
|
177
231
|
end
|
178
232
|
|
179
|
-
def
|
233
|
+
def clean_connection_proxy()
|
180
234
|
self.current_shard = :master
|
181
235
|
self.current_group = nil
|
182
236
|
self.block = false
|
@@ -189,7 +243,8 @@ class Octopus::Proxy
|
|
189
243
|
end
|
190
244
|
|
191
245
|
def transaction(options = {}, &block)
|
192
|
-
|
246
|
+
replicated = @replicated && (current_model.replicated || fully_replicated?)
|
247
|
+
if !sharded && replicated
|
193
248
|
self.run_queries_on_shard(:master) do
|
194
249
|
select_connection.transaction(options, &block)
|
195
250
|
end
|
@@ -199,11 +254,15 @@ class Octopus::Proxy
|
|
199
254
|
end
|
200
255
|
|
201
256
|
def method_missing(method, *args, &block)
|
202
|
-
if
|
257
|
+
if should_clean_connection_proxy?(method)
|
203
258
|
conn = select_connection()
|
204
259
|
self.last_current_shard = self.current_shard
|
205
|
-
|
260
|
+
clean_connection_proxy()
|
206
261
|
conn.send(method, *args, &block)
|
262
|
+
elsif should_send_queries_to_shard_slave_group?(method)
|
263
|
+
send_queries_to_shard_slave_group(method, *args, &block)
|
264
|
+
elsif should_send_queries_to_slave_group?(method)
|
265
|
+
send_queries_to_slave_group(method, *args, &block)
|
207
266
|
elsif should_send_queries_to_replicated_databases?(method)
|
208
267
|
send_queries_to_selected_slave(method, *args, &block)
|
209
268
|
else
|
@@ -244,6 +303,22 @@ class Octopus::Proxy
|
|
244
303
|
@shards.any? { |k, v| v.connected? }
|
245
304
|
end
|
246
305
|
|
306
|
+
def should_send_queries_to_shard_slave_group?(method)
|
307
|
+
should_use_slaves_for_method?(method) && @shards_slave_groups.try(:[], current_shard).try(:[], current_slave_group).present?
|
308
|
+
end
|
309
|
+
|
310
|
+
def send_queries_to_shard_slave_group(method, *args, &block)
|
311
|
+
send_queries_to_balancer(@shards_slave_groups[current_shard][current_slave_group], method, *args, &block)
|
312
|
+
end
|
313
|
+
|
314
|
+
def should_send_queries_to_slave_group?(method)
|
315
|
+
should_use_slaves_for_method?(method) && @slave_groups.try(:[], current_slave_group).present?
|
316
|
+
end
|
317
|
+
|
318
|
+
def send_queries_to_slave_group(method, *args, &block)
|
319
|
+
send_queries_to_balancer(@slave_groups[current_slave_group], method, *args, &block)
|
320
|
+
end
|
321
|
+
|
247
322
|
protected
|
248
323
|
|
249
324
|
def connection_pool_for(adapter, config)
|
@@ -274,27 +349,89 @@ class Octopus::Proxy
|
|
274
349
|
resolver.spec.config.stringify_keys
|
275
350
|
end
|
276
351
|
|
277
|
-
def
|
352
|
+
def should_clean_connection_proxy?(method)
|
278
353
|
method.to_s =~ /insert|select|execute/ && !@replicated && !self.block
|
279
354
|
end
|
280
355
|
|
356
|
+
# Try to use slaves if and only if `replicated: true` is specified in `shards.yml` and no slaves groups are defined
|
281
357
|
def should_send_queries_to_replicated_databases?(method)
|
282
|
-
@replicated && method.to_s =~ /select/ && !self.block
|
358
|
+
@replicated && method.to_s =~ /select/ && !self.block && !slaves_grouped?
|
283
359
|
end
|
284
360
|
|
285
361
|
def send_queries_to_selected_slave(method, *args, &block)
|
286
|
-
|
362
|
+
if current_model.replicated || fully_replicated?
|
363
|
+
selected_slave = @slaves_load_balancer.next
|
364
|
+
else
|
365
|
+
selected_slave = :master
|
366
|
+
end
|
287
367
|
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
368
|
+
send_queries_to_slave(selected_slave, method, *args, &block)
|
369
|
+
end
|
370
|
+
|
371
|
+
# We should use slaves if and only if its safe to do so.
|
372
|
+
#
|
373
|
+
# We can safely use slaves when:
|
374
|
+
# (1) `replicated: true` is specified in `shards.yml`
|
375
|
+
# (2) The current model is `replicated()`, or `fully_replicated: true` is specified in `shards.yml` which means that
|
376
|
+
# all the model is `replicated()`
|
377
|
+
# (3) It's a SELECT query
|
378
|
+
# while ensuring that we revert `current_shard` from the selected slave to the (shard's) master
|
379
|
+
# not to make queries other than SELECT leak to the slave.
|
380
|
+
def should_use_slaves_for_method?(method)
|
381
|
+
@replicated && (current_model.replicated || fully_replicated?) && method.to_s =~ /select/
|
382
|
+
end
|
383
|
+
|
384
|
+
def slaves_grouped?
|
385
|
+
@slave_groups.present?
|
386
|
+
end
|
294
387
|
|
388
|
+
# Temporarily switch `current_shard` to the next slave in a slave group and send queries to it
|
389
|
+
# while preserving `current_shard`
|
390
|
+
def send_queries_to_balancer(balancer, method, *args, &block)
|
391
|
+
send_queries_to_slave(balancer.next, method, *args, &block)
|
392
|
+
end
|
393
|
+
|
394
|
+
# Temporarily switch `current_shard` to the specified slave and send queries to it
|
395
|
+
# while preserving `current_shard`
|
396
|
+
def send_queries_to_slave(slave, method, *args, &block)
|
397
|
+
using_shard(slave) do
|
295
398
|
select_connection.send(method, *args, &block)
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
# Temporarily block cleaning connection proxy and run the block
|
403
|
+
#
|
404
|
+
# @see Octopus::Proxy#should_clean_connection?
|
405
|
+
# @see Octopus::Proxy#clean_connection_proxy
|
406
|
+
def keeping_connection_proxy(&block)
|
407
|
+
last_block = self.block
|
408
|
+
|
409
|
+
begin
|
410
|
+
self.block = true
|
411
|
+
yield
|
412
|
+
ensure
|
413
|
+
self.block = last_block || false
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
# Temporarily switch `current_shard` and run the block
|
418
|
+
def using_shard(shard, &block)
|
419
|
+
older_shard = self.current_shard
|
420
|
+
|
421
|
+
begin
|
422
|
+
self.current_shard = shard
|
423
|
+
yield
|
296
424
|
ensure
|
297
|
-
self.current_shard =
|
425
|
+
self.current_shard = older_shard
|
298
426
|
end
|
299
427
|
end
|
428
|
+
|
429
|
+
def structurally_slave?(config)
|
430
|
+
config.is_a?(Hash) && config.key?("adapter")
|
431
|
+
end
|
432
|
+
|
433
|
+
def structurally_slave_group?(config)
|
434
|
+
config.is_a?(Hash) && config.values.any? {|v| structurally_slave? v }
|
435
|
+
end
|
436
|
+
|
300
437
|
end
|