Skip to content

Commit 389ca53

Browse files
committed
Adds support for sending queries using ActiveRecord::Base.connection.execute to slaves
1 parent bfce8ce commit 389ca53

File tree

2 files changed

+42
-11
lines changed

2 files changed

+42
-11
lines changed

lib/octopus/proxy.rb

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
require 'set'
22
require 'octopus/slave_group'
33
require 'octopus/load_balancing/round_robin'
4+
require 'octopus/query_analysis'
45

56
module Octopus
67
class Proxy
8+
include ::Octopus::QueryAnalysis
9+
710
attr_accessor :config, :sharded
811

912
CURRENT_MODEL_KEY = 'octopus.current_model'.freeze
@@ -293,11 +296,11 @@ def method_missing(method, *args, &block)
293296
self.last_current_shard = current_shard
294297
clean_connection_proxy
295298
conn.send(method, *args, &block)
296-
elsif should_send_queries_to_shard_slave_group?(method)
299+
elsif should_send_queries_to_shard_slave_group?(method, args)
297300
send_queries_to_shard_slave_group(method, *args, &block)
298-
elsif should_send_queries_to_slave_group?(method)
301+
elsif should_send_queries_to_slave_group?(method, args)
299302
send_queries_to_slave_group(method, *args, &block)
300-
elsif should_send_queries_to_replicated_databases?(method)
303+
elsif should_send_queries_to_replicated_databases?(method, args)
301304
send_queries_to_selected_slave(method, *args, &block)
302305
else
303306
select_connection.send(method, *args, &block)
@@ -337,16 +340,16 @@ def connected?
337340
@shards.any? { |_k, v| v.connected? }
338341
end
339342

340-
def should_send_queries_to_shard_slave_group?(method)
341-
should_use_slaves_for_method?(method) && @shards_slave_groups.try(:[], current_shard).try(:[], current_slave_group).present?
343+
def should_send_queries_to_shard_slave_group?(method, args)
344+
should_use_slaves_for_method?(method, args) && @shards_slave_groups.try(:[], current_shard).try(:[], current_slave_group).present?
342345
end
343346

344347
def send_queries_to_shard_slave_group(method, *args, &block)
345348
send_queries_to_balancer(@shards_slave_groups[current_shard][current_slave_group], method, *args, &block)
346349
end
347350

348-
def should_send_queries_to_slave_group?(method)
349-
should_use_slaves_for_method?(method) && @slave_groups.try(:[], current_slave_group).present?
351+
def should_send_queries_to_slave_group?(method, args)
352+
should_use_slaves_for_method?(method, args) && @slave_groups.try(:[], current_slave_group).present?
350353
end
351354

352355
def send_queries_to_slave_group(method, *args, &block)
@@ -431,8 +434,8 @@ def should_clean_connection_proxy?(method)
431434
end
432435

433436
# Try to use slaves if and only if `replicated: true` is specified in `shards.yml` and no slaves groups are defined
434-
def should_send_queries_to_replicated_databases?(method)
435-
@replicated && method.to_s =~ /select/ && !block && !slaves_grouped?
437+
def should_send_queries_to_replicated_databases?(method, args)
438+
@replicated && select?(method, args) && !block && !slaves_grouped?
436439
end
437440

438441
def current_model_replicated?
@@ -458,8 +461,20 @@ def send_queries_to_selected_slave(method, *args, &block)
458461
# (3) It's a SELECT query
459462
# while ensuring that we revert `current_shard` from the selected slave to the (shard's) master
460463
# not to make queries other than SELECT leak to the slave.
461-
def should_use_slaves_for_method?(method)
462-
current_model_replicated? && method.to_s =~ /select/
464+
def should_use_slaves_for_method?(method, args)
465+
current_model_replicated? && select?(method, args)
466+
end
467+
468+
# Given an ActiveRecord::Base.connection method and its arguments, determine if it is a select query.
469+
def select?(method, args)
470+
is_select = method.to_s =~ /select/
471+
unless is_select
472+
query = args.first
473+
if query.kind_of? String
474+
is_select = method.to_s =~ /execute/ && definitely_select_query?(query) && !possibly_multiple_queries?(query)
475+
end
476+
end
477+
is_select
463478
end
464479

465480
def slaves_grouped?

lib/octopus/query_analysis.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
module Octopus
2+
module QueryAnalysis
3+
# Given a mysql query string, determines if it is definitely a select query. Due to the simple regex used, it will
4+
# sometimes miss detecting valid select queries, hence why it only determines if something is definitely a select.
5+
def definitely_select_query?( str )
6+
str =~ /^\s*select/i
7+
end
8+
9+
# Given a mysql query string, determines if the string might contain multiple queries.
10+
# We are simply checking if it contains a semi colon with non whitespace to the right of it, so this check will
11+
# sometimes falsely detect a string containing one query as sometimes having multiple queries.
12+
def possibly_multiple_queries?( str )
13+
str =~ /;.*\S+.*$/
14+
end
15+
end
16+
end

0 commit comments

Comments
 (0)