RUBY-320 ReplSetConnection now caches tags and maps them to pools for reading.
This commit is contained in:
parent
45c40e7267
commit
8b2de82464
@ -12,6 +12,7 @@ module Mongo
|
|||||||
self.port = data[1].nil? ? Connection::DEFAULT_PORT : data[1].to_i
|
self.port = data[1].nil? ? Connection::DEFAULT_PORT : data[1].to_i
|
||||||
end
|
end
|
||||||
self.address = "#{host}:#{port}"
|
self.address = "#{host}:#{port}"
|
||||||
|
self.config = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def eql?(other)
|
def eql?(other)
|
||||||
@ -98,7 +99,7 @@ module Mongo
|
|||||||
# Note: this excludes arbiters.
|
# Note: this excludes arbiters.
|
||||||
def node_list
|
def node_list
|
||||||
connect unless connected?
|
connect unless connected?
|
||||||
set_config
|
set_config unless self.config
|
||||||
|
|
||||||
return [] unless config
|
return [] unless config
|
||||||
|
|
||||||
@ -110,6 +111,7 @@ module Mongo
|
|||||||
|
|
||||||
def arbiters
|
def arbiters
|
||||||
connect unless connected?
|
connect unless connected?
|
||||||
|
set_config unless self.config
|
||||||
return [] unless config['arbiters']
|
return [] unless config['arbiters']
|
||||||
|
|
||||||
config['arbiters'].map do |arbiter|
|
config['arbiters'].map do |arbiter|
|
||||||
@ -117,6 +119,14 @@ module Mongo
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def tags
|
||||||
|
connect unless connected?
|
||||||
|
set_config unless self.config
|
||||||
|
return {} unless config['tags'] && !config['tags'].empty?
|
||||||
|
|
||||||
|
config['tags']
|
||||||
|
end
|
||||||
|
|
||||||
def primary?
|
def primary?
|
||||||
self.config['ismaster'] == true || self.config['ismaster'] == 1
|
self.config['ismaster'] == true || self.config['ismaster'] == 1
|
||||||
end
|
end
|
||||||
|
@ -21,7 +21,7 @@ module Mongo
|
|||||||
# Instantiates and manages connections to a MongoDB replica set.
|
# Instantiates and manages connections to a MongoDB replica set.
|
||||||
class ReplSetConnection < Connection
|
class ReplSetConnection < Connection
|
||||||
attr_reader :nodes, :secondaries, :arbiters, :secondary_pools,
|
attr_reader :nodes, :secondaries, :arbiters, :secondary_pools,
|
||||||
:replica_set_name, :read_pool, :seeds
|
:replica_set_name, :read_pool, :seeds, :tags_to_pools
|
||||||
|
|
||||||
# Create a connection to a MongoDB replica set.
|
# Create a connection to a MongoDB replica set.
|
||||||
#
|
#
|
||||||
@ -172,6 +172,7 @@ module Mongo
|
|||||||
@primary_pool = manager.primary_pool
|
@primary_pool = manager.primary_pool
|
||||||
@read_pool = manager.read_pool
|
@read_pool = manager.read_pool
|
||||||
@secondary_pools = manager.secondary_pools
|
@secondary_pools = manager.secondary_pools
|
||||||
|
@tags_to_pools = manager.tags_to_pools
|
||||||
@seeds = manager.seeds
|
@seeds = manager.seeds
|
||||||
@manager = manager
|
@manager = manager
|
||||||
@hosts = manager.hosts
|
@hosts = manager.hosts
|
||||||
@ -334,9 +335,10 @@ module Mongo
|
|||||||
# an exception.
|
# an exception.
|
||||||
def checkout_tagged(tags)
|
def checkout_tagged(tags)
|
||||||
tags.each do |k, v|
|
tags.each do |k, v|
|
||||||
if pool = @tags_to_pools[{k.to_s => v}]
|
pools = @tags_to_pools[{k => v}]
|
||||||
socket = pool.checkout
|
if !pools.empty?
|
||||||
@sockets_to_pools[socket] = pool
|
socket = pools.first.checkout
|
||||||
|
@sockets_to_pools[socket] = pools.first
|
||||||
return socket
|
return socket
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -47,6 +47,8 @@ module Mongo
|
|||||||
@sockets = []
|
@sockets = []
|
||||||
@pids = {}
|
@pids = {}
|
||||||
@checked_out = []
|
@checked_out = []
|
||||||
|
@ping_time = nil
|
||||||
|
@last_ping = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def close
|
def close
|
||||||
@ -71,9 +73,23 @@ module Mongo
|
|||||||
[@host, @port]
|
[@host, @port]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Refresh ping time only if we haven't
|
||||||
|
# checked within the last five minutes.
|
||||||
|
def ping_time
|
||||||
|
if !@last_ping
|
||||||
|
@last_ping = Time.now
|
||||||
|
@ping_time = refresh_ping_time
|
||||||
|
elsif Time.now - @last_ping > 300
|
||||||
|
@last_ping = Time.now
|
||||||
|
@ping_time = refresh_ping_time
|
||||||
|
else
|
||||||
|
@ping_time
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Return the time it takes on average
|
# Return the time it takes on average
|
||||||
# to do a round-trip against this node.
|
# to do a round-trip against this node.
|
||||||
def ping_time
|
def refresh_ping_time
|
||||||
trials = []
|
trials = []
|
||||||
begin
|
begin
|
||||||
PING_ATTEMPTS.times do
|
PING_ATTEMPTS.times do
|
||||||
@ -86,13 +102,15 @@ module Mongo
|
|||||||
end
|
end
|
||||||
|
|
||||||
trials.sort!
|
trials.sort!
|
||||||
|
|
||||||
|
# Delete shortest and longest times
|
||||||
trials.delete_at(trials.length-1)
|
trials.delete_at(trials.length-1)
|
||||||
trials.delete_at(0)
|
trials.delete_at(0)
|
||||||
|
|
||||||
total = 0.0
|
total = 0.0
|
||||||
trials.each { |t| total += t }
|
trials.each { |t| total += t }
|
||||||
|
|
||||||
(total / trials.length).floor
|
(total / trials.length).ceil
|
||||||
end
|
end
|
||||||
|
|
||||||
# Return a socket to the pool.
|
# Return a socket to the pool.
|
||||||
|
@ -2,7 +2,8 @@ module Mongo
|
|||||||
class PoolManager
|
class PoolManager
|
||||||
|
|
||||||
attr_reader :connection, :seeds, :arbiters, :primary, :secondaries,
|
attr_reader :connection, :seeds, :arbiters, :primary, :secondaries,
|
||||||
:primary_pool, :read_pool, :secondary_pools, :hosts, :nodes, :max_bson_size
|
:primary_pool, :read_pool, :secondary_pools, :hosts, :nodes, :max_bson_size,
|
||||||
|
:tags_to_pools
|
||||||
|
|
||||||
def initialize(connection, seeds)
|
def initialize(connection, seeds)
|
||||||
@connection = connection
|
@connection = connection
|
||||||
@ -85,8 +86,6 @@ module Mongo
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
# Note that @arbiters and @read_pool will be
|
|
||||||
# assigned automatically.
|
|
||||||
def reference_manager_data(manager)
|
def reference_manager_data(manager)
|
||||||
@primary = manager.primary
|
@primary = manager.primary
|
||||||
@primary_pool = manager.primary_pool
|
@primary_pool = manager.primary_pool
|
||||||
@ -106,6 +105,7 @@ module Mongo
|
|||||||
@secondary_pools = []
|
@secondary_pools = []
|
||||||
@hosts = []
|
@hosts = []
|
||||||
@members = []
|
@members = []
|
||||||
|
@tags_to_pools = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
# Connect to each member of the replica set
|
# Connect to each member of the replica set
|
||||||
@ -130,6 +130,23 @@ module Mongo
|
|||||||
members
|
members
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def associate_tags_with_pool(tags, pool)
|
||||||
|
tags.each_key do |key|
|
||||||
|
@tags_to_pools[{key => tags[key]}] ||= []
|
||||||
|
@tags_to_pools[{key => tags[key]}] << pool
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sort each tag pool entry in descending order
|
||||||
|
# according to ping time.
|
||||||
|
def sort_tag_pools!
|
||||||
|
@tags_to_pools.each_value do |pool_list|
|
||||||
|
pool_list.sort! do |a, b|
|
||||||
|
a.ping_time <=> b.ping_time
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Initialize the connection pools for the primary and secondary nodes.
|
# Initialize the connection pools for the primary and secondary nodes.
|
||||||
def initialize_pools(members)
|
def initialize_pools(members)
|
||||||
members.each do |member|
|
members.each do |member|
|
||||||
@ -141,15 +158,19 @@ module Mongo
|
|||||||
:size => self.connection.pool_size,
|
:size => self.connection.pool_size,
|
||||||
:timeout => self.connection.connect_timeout,
|
:timeout => self.connection.connect_timeout,
|
||||||
:node => member)
|
:node => member)
|
||||||
|
associate_tags_with_pool(member.tags, @primary_pool)
|
||||||
elsif member.secondary? && !@secondaries.include?(member.host_port)
|
elsif member.secondary? && !@secondaries.include?(member.host_port)
|
||||||
@secondaries << member.host_port
|
@secondaries << member.host_port
|
||||||
@secondary_pools << Pool.new(self.connection, member.host, member.port,
|
pool = Pool.new(self.connection, member.host, member.port,
|
||||||
:size => self.connection.pool_size,
|
:size => self.connection.pool_size,
|
||||||
:timeout => self.connection.connect_timeout,
|
:timeout => self.connection.connect_timeout,
|
||||||
:node => member)
|
:node => member)
|
||||||
|
@secondary_pools << pool
|
||||||
|
associate_tags_with_pool(member.tags, pool)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
sort_tag_pools!
|
||||||
@max_bson_size = members.first.config['maxBsonObjectSize'] ||
|
@max_bson_size = members.first.config['maxBsonObjectSize'] ||
|
||||||
Mongo::DEFAULT_MAX_BSON_SIZE
|
Mongo::DEFAULT_MAX_BSON_SIZE
|
||||||
@arbiters = members.first.arbiters
|
@arbiters = members.first.arbiters
|
||||||
|
@ -10,7 +10,7 @@ end
|
|||||||
|
|
||||||
class ReplSetManager
|
class ReplSetManager
|
||||||
|
|
||||||
attr_accessor :host, :start_port, :ports, :name, :mongods
|
attr_accessor :host, :start_port, :ports, :name, :mongods, :tags, :version
|
||||||
|
|
||||||
def initialize(opts={})
|
def initialize(opts={})
|
||||||
@start_port = opts[:start_port] || 30000
|
@start_port = opts[:start_port] || 30000
|
||||||
@ -21,6 +21,10 @@ class ReplSetManager
|
|||||||
@config = {"_id" => @name, "members" => []}
|
@config = {"_id" => @name, "members" => []}
|
||||||
@durable = opts.fetch(:durable, false)
|
@durable = opts.fetch(:durable, false)
|
||||||
@path = File.join(File.expand_path(File.dirname(__FILE__)), "data")
|
@path = File.join(File.expand_path(File.dirname(__FILE__)), "data")
|
||||||
|
@oplog_size = opts.fetch(:oplog_size, 512)
|
||||||
|
@tags = [{"dc" => "ny", "rack" => "a", "db" => "main"},
|
||||||
|
{"dc" => "ny", "rack" => "b", "db" => "main"},
|
||||||
|
{"dc" => "sf", "rack" => "a", "db" => "main"}]
|
||||||
|
|
||||||
@arbiter_count = opts[:arbiter_count] || 2
|
@arbiter_count = opts[:arbiter_count] || 2
|
||||||
@secondary_count = opts[:secondary_count] || 2
|
@secondary_count = opts[:secondary_count] || 2
|
||||||
@ -33,6 +37,9 @@ class ReplSetManager
|
|||||||
end
|
end
|
||||||
|
|
||||||
@mongods = {}
|
@mongods = {}
|
||||||
|
version_string = `mongod --version`
|
||||||
|
version_string =~ /(\d\.\d\.\d)/
|
||||||
|
@version = $1.split(".").map {|d| d.to_i }
|
||||||
end
|
end
|
||||||
|
|
||||||
def start_set
|
def start_set
|
||||||
@ -42,7 +49,11 @@ class ReplSetManager
|
|||||||
|
|
||||||
n = 0
|
n = 0
|
||||||
(@primary_count + @secondary_count).times do
|
(@primary_count + @secondary_count).times do
|
||||||
init_node(n)
|
init_node(n) do |attrs|
|
||||||
|
if @version[0] >= 2
|
||||||
|
attrs['tags'] = @tags[n % @tags.size]
|
||||||
|
end
|
||||||
|
end
|
||||||
n += 1
|
n += 1
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -96,9 +107,21 @@ class ReplSetManager
|
|||||||
@config['members'] << member
|
@config['members'] << member
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def journal_switch
|
||||||
|
if @version[0] >= 2
|
||||||
|
if @durable
|
||||||
|
"--journal"
|
||||||
|
else
|
||||||
|
"--nojournal"
|
||||||
|
end
|
||||||
|
elsif @durable
|
||||||
|
"--journal"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def start_cmd(n)
|
def start_cmd(n)
|
||||||
@mongods[n]['start'] = "mongod --replSet #{@name} --logpath '#{@mongods[n]['log_path']}' " +
|
@mongods[n]['start'] = "mongod --replSet #{@name} --logpath '#{@mongods[n]['log_path']}' " +
|
||||||
" --dbpath #{@mongods[n]['db_path']} --port #{@mongods[n]['port']} --fork"
|
"--oplogSize #{@oplog_size} #{journal_switch} --dbpath #{@mongods[n]['db_path']} --port #{@mongods[n]['port']} --fork"
|
||||||
@mongods[n]['start'] += " --dur" if @durable
|
@mongods[n]['start'] += " --dur" if @durable
|
||||||
@mongods[n]['start']
|
@mongods[n]['start']
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user