ar-octopus 0.8.1 → 0.8.2
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 +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
|