RUBY-416 threading with refresh test fixes
This commit is contained in:
parent
bf9bb83b6d
commit
274ce690e7
|
@ -114,6 +114,7 @@ module Mongo
|
||||||
|
|
||||||
# No connection manager by default.
|
# No connection manager by default.
|
||||||
@manager = nil
|
@manager = nil
|
||||||
|
@old_managers = []
|
||||||
|
|
||||||
# Lock for request ids.
|
# Lock for request ids.
|
||||||
@id_lock = Mutex.new
|
@id_lock = Mutex.new
|
||||||
|
@ -125,6 +126,7 @@ module Mongo
|
||||||
@safe_mutexes = Hash.new {|hash, key| hash[key] = Mutex.new}
|
@safe_mutexes = Hash.new {|hash, key| hash[key] = Mutex.new}
|
||||||
|
|
||||||
@connect_mutex = Mutex.new
|
@connect_mutex = Mutex.new
|
||||||
|
@refresh_mutex = Mutex.new
|
||||||
|
|
||||||
check_opts(opts)
|
check_opts(opts)
|
||||||
setup(opts)
|
setup(opts)
|
||||||
|
@ -147,14 +149,15 @@ module Mongo
|
||||||
|
|
||||||
discovered_seeds = @manager ? @manager.seeds : []
|
discovered_seeds = @manager ? @manager.seeds : []
|
||||||
@manager = PoolManager.new(self, discovered_seeds)
|
@manager = PoolManager.new(self, discovered_seeds)
|
||||||
|
Thread.current[:manager] = @manager
|
||||||
|
|
||||||
@manager.connect
|
@manager.connect
|
||||||
@refresh_version += 1
|
@refresh_version += 1
|
||||||
|
|
||||||
if @require_primary && self.primary.nil? #TODO: in v2.0, we'll let this be optional and do a lazy connect.
|
if @require_primary && @manager.primary.nil? #TODO: in v2.0, we'll let this be optional and do a lazy connect.
|
||||||
close
|
close
|
||||||
raise ConnectionFailure, "Failed to connect to primary node."
|
raise ConnectionFailure, "Failed to connect to primary node."
|
||||||
elsif self.read_pool.nil?
|
elsif @manager.read_pool.nil?
|
||||||
close
|
close
|
||||||
raise ConnectionFailure, "Failed to connect to any node."
|
raise ConnectionFailure, "Failed to connect to any node."
|
||||||
else
|
else
|
||||||
|
@ -196,19 +199,19 @@ module Mongo
|
||||||
def hard_refresh!
|
def hard_refresh!
|
||||||
log(:info, "Initiating hard refresh...")
|
log(:info, "Initiating hard refresh...")
|
||||||
discovered_seeds = @manager ? @manager.seeds : []
|
discovered_seeds = @manager ? @manager.seeds : []
|
||||||
background_manager = PoolManager.new(self, discovered_seeds | @seeds)
|
new_manager = PoolManager.new(self, discovered_seeds | @seeds)
|
||||||
background_manager.connect
|
new_manager.connect
|
||||||
|
|
||||||
# TODO: make sure that connect has succeeded
|
# TODO: make sure that connect has succeeded
|
||||||
old_manager = @manager
|
@old_managers << @manager
|
||||||
@manager = background_manager
|
@manager = new_manager
|
||||||
old_manager.close(:soft => true)
|
|
||||||
@refresh_version += 1
|
@refresh_version += 1
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
def connected?
|
def connected?
|
||||||
@connected && (self.primary_pool || self.read_pool)
|
@connected && (@manager.primary_pool || @manager.read_pool)
|
||||||
end
|
end
|
||||||
|
|
||||||
# @deprecated
|
# @deprecated
|
||||||
|
@ -221,14 +224,14 @@ module Mongo
|
||||||
#
|
#
|
||||||
# @return [String]
|
# @return [String]
|
||||||
def host
|
def host
|
||||||
self.primary_pool.host
|
@manager.primary_pool.host
|
||||||
end
|
end
|
||||||
|
|
||||||
# The replica set primary's port.
|
# The replica set primary's port.
|
||||||
#
|
#
|
||||||
# @return [Integer]
|
# @return [Integer]
|
||||||
def port
|
def port
|
||||||
self.primary_pool.port
|
@manager.primary_pool.port
|
||||||
end
|
end
|
||||||
|
|
||||||
def nodes
|
def nodes
|
||||||
|
@ -242,7 +245,7 @@ module Mongo
|
||||||
#
|
#
|
||||||
# @return [Boolean]
|
# @return [Boolean]
|
||||||
def read_primary?
|
def read_primary?
|
||||||
self.read_pool == self.primary_pool
|
@manager.read_pool == @manager.primary_pool
|
||||||
end
|
end
|
||||||
alias :primary? :read_primary?
|
alias :primary? :read_primary?
|
||||||
|
|
||||||
|
@ -294,96 +297,68 @@ module Mongo
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Generic socket checkout
|
||||||
|
# Takes a block that returns a socket from pool
|
||||||
|
def checkout(&block)
|
||||||
|
if connected?
|
||||||
|
sync_refresh
|
||||||
|
else
|
||||||
|
connect
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
socket = block.call
|
||||||
|
rescue => ex
|
||||||
|
checkin(socket) if socket
|
||||||
|
raise ex
|
||||||
|
end
|
||||||
|
|
||||||
|
if socket
|
||||||
|
socket
|
||||||
|
else
|
||||||
|
@connected = false
|
||||||
|
raise ConnectionFailure.new("Could not checkout a socket")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Checkout a socket for reading (i.e., a secondary node).
|
# Checkout a socket for reading (i.e., a secondary node).
|
||||||
# Note that @read_pool might point to the primary pool
|
# Note that @read_pool might point to the primary pool
|
||||||
# if no read pool has been defined.
|
# if no read pool has been defined.
|
||||||
def checkout_reader
|
def checkout_reader
|
||||||
if connected?
|
checkout do
|
||||||
sync_refresh
|
socket = get_socket_from_pool(:read)
|
||||||
else
|
|
||||||
connect
|
|
||||||
end
|
|
||||||
begin
|
|
||||||
socket = get_socket_from_pool(self.read_pool)
|
|
||||||
if !socket
|
if !socket
|
||||||
connect
|
connect
|
||||||
socket = get_socket_from_pool(self.primary_pool)
|
socket = get_socket_from_pool(:primary)
|
||||||
end
|
end
|
||||||
rescue => ex
|
|
||||||
checkin(socket) if socket
|
|
||||||
raise ex
|
|
||||||
end
|
|
||||||
|
|
||||||
if socket
|
|
||||||
socket
|
socket
|
||||||
else
|
|
||||||
@connected = false
|
|
||||||
raise ConnectionFailure.new("Could not connect to a node for reading.")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Checkout a socket from a secondary
|
||||||
|
# For :read_preference => :secondary_only
|
||||||
def checkout_secondary
|
def checkout_secondary
|
||||||
if connected?
|
checkout do
|
||||||
sync_refresh
|
get_socket_from_pool(:secondary)
|
||||||
else
|
|
||||||
connect
|
|
||||||
end
|
|
||||||
begin
|
|
||||||
socket = get_socket_from_pool(self.secondary_pool)
|
|
||||||
rescue => ex
|
|
||||||
checkin(socket) if socket
|
|
||||||
raise ex
|
|
||||||
end
|
|
||||||
|
|
||||||
if socket
|
|
||||||
socket
|
|
||||||
else
|
|
||||||
@connected = false
|
|
||||||
raise ConnectionFailure.new("Could not connect to a secondary for reading.")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Checkout a socket for writing (i.e., a primary node).
|
# Checkout a socket for writing (i.e., a primary node).
|
||||||
def checkout_writer
|
def checkout_writer
|
||||||
if connected?
|
checkout do
|
||||||
sync_refresh
|
get_socket_from_pool(:primary)
|
||||||
else
|
|
||||||
connect
|
|
||||||
end
|
|
||||||
begin
|
|
||||||
socket = get_socket_from_pool(self.primary_pool)
|
|
||||||
|
|
||||||
if !socket
|
|
||||||
connect
|
|
||||||
socket = get_socket_from_pool(self.primary_pool)
|
|
||||||
end
|
|
||||||
rescue => ex
|
|
||||||
checkin(socket)
|
|
||||||
raise ex
|
|
||||||
end
|
|
||||||
|
|
||||||
if socket
|
|
||||||
socket
|
|
||||||
else
|
|
||||||
@connected = false
|
|
||||||
raise ConnectionFailure.new("Could not connect to primary node.")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Checkin a socket used for reading.
|
# Checkin a socket used for reading.
|
||||||
def checkin_reader(socket)
|
def checkin_reader(socket)
|
||||||
if !((self.read_pool && self.read_pool.checkin(socket)) ||
|
socket.pool.checkin(socket)
|
||||||
(self.primary_pool && self.primary_pool.checkin(socket)))
|
|
||||||
close_socket(socket)
|
|
||||||
end
|
|
||||||
sync_refresh
|
sync_refresh
|
||||||
end
|
end
|
||||||
|
|
||||||
# Checkin a socket used for writing.
|
# Checkin a socket used for writing.
|
||||||
def checkin_writer(socket)
|
def checkin_writer(socket)
|
||||||
if !self.primary_pool || !self.primary_pool.checkin(socket)
|
socket.pool.checkin(socket)
|
||||||
close_socket(socket)
|
|
||||||
end
|
|
||||||
sync_refresh
|
sync_refresh
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -395,7 +370,20 @@ module Mongo
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_socket_from_pool(pool)
|
def get_socket_from_pool(pool_type)
|
||||||
|
if Thread.current[:manager] != @manager
|
||||||
|
Thread.current[:manager] = @manager
|
||||||
|
end
|
||||||
|
|
||||||
|
pool = case pool_type
|
||||||
|
when :primary
|
||||||
|
primary_pool
|
||||||
|
when :secondary
|
||||||
|
secondary_pool
|
||||||
|
when :read
|
||||||
|
read_pool
|
||||||
|
end
|
||||||
|
|
||||||
begin
|
begin
|
||||||
if pool
|
if pool
|
||||||
socket = pool.checkout
|
socket = pool.checkout
|
||||||
|
@ -407,46 +395,50 @@ module Mongo
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def local_manager
|
||||||
|
Thread.current[:manager]
|
||||||
|
end
|
||||||
|
|
||||||
def arbiters
|
def arbiters
|
||||||
@manager.arbiters.nil? ? [] : @manager.arbiters
|
local_manager.arbiters.nil? ? [] : local_manager.arbiters
|
||||||
end
|
end
|
||||||
|
|
||||||
def primary
|
def primary
|
||||||
@manager ? @manager.primary : nil
|
local_manager ? local_manager.primary : nil
|
||||||
end
|
end
|
||||||
|
|
||||||
# Note: might want to freeze these after connecting.
|
# Note: might want to freeze these after connecting.
|
||||||
def secondaries
|
def secondaries
|
||||||
@manager ? @manager.secondaries : []
|
local_manager ? local_manager.secondaries : []
|
||||||
end
|
end
|
||||||
|
|
||||||
def hosts
|
def hosts
|
||||||
@manager ? @manager.hosts : []
|
local_manager ? local_manager.hosts : []
|
||||||
end
|
end
|
||||||
|
|
||||||
def primary_pool
|
def primary_pool
|
||||||
@manager ? @manager.primary_pool : nil
|
local_manager ? local_manager.primary_pool : nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def read_pool
|
def read_pool
|
||||||
@manager ? @manager.read_pool : nil
|
local_manager ? local_manager.read_pool : nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def secondary_pool
|
def secondary_pool
|
||||||
@manager ? @manager.secondary_pool : nil
|
local_manager ? local_manager.secondary_pool : nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def secondary_pools
|
def secondary_pools
|
||||||
@manager ? @manager.secondary_pools : []
|
local_manager ? local_manager.secondary_pools : []
|
||||||
end
|
end
|
||||||
|
|
||||||
def tag_map
|
def tag_map
|
||||||
@manager ? @manager.tag_map : {}
|
local_manager ? local_manager.tag_map : {}
|
||||||
end
|
end
|
||||||
|
|
||||||
def max_bson_size
|
def max_bson_size
|
||||||
if @manager && @manager.max_bson_size
|
if local_manager && local_manager.max_bson_size
|
||||||
@manager.max_bson_size
|
local_manager.max_bson_size
|
||||||
else
|
else
|
||||||
Mongo::DEFAULT_MAX_BSON_SIZE
|
Mongo::DEFAULT_MAX_BSON_SIZE
|
||||||
end
|
end
|
||||||
|
@ -514,11 +506,31 @@ module Mongo
|
||||||
"Could not find a connection tagged with #{tags}."
|
"Could not find a connection tagged with #{tags}."
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def prune_managers
|
||||||
|
@old_managers.each do |manager|
|
||||||
|
if manager != @manager
|
||||||
|
if manager.closed?
|
||||||
|
@old_managers.delete(manager)
|
||||||
|
else
|
||||||
|
manager.close(:soft => true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def sync_refresh
|
def sync_refresh
|
||||||
if @refresh_mode == :sync &&
|
if @refresh_mode == :sync &&
|
||||||
((Time.now - @last_refresh) > @refresh_interval)
|
((Time.now - @last_refresh) > @refresh_interval)
|
||||||
@last_refresh = Time.now
|
@last_refresh = Time.now
|
||||||
|
|
||||||
|
if @refresh_mutex.try_lock
|
||||||
|
begin
|
||||||
|
prune_managers
|
||||||
refresh
|
refresh
|
||||||
|
ensure
|
||||||
|
@refresh_mutex.unlock
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -65,24 +65,14 @@ module Mongo
|
||||||
# close only those sockets that are not checked out.
|
# close only those sockets that are not checked out.
|
||||||
def close(opts={})
|
def close(opts={})
|
||||||
@connection_mutex.synchronize do
|
@connection_mutex.synchronize do
|
||||||
if opts[:soft]
|
if opts[:soft] && !@checked_out.empty?
|
||||||
sockets_to_close = @sockets - @checked_out
|
close_sockets(@sockets - @checked_out)
|
||||||
else
|
else
|
||||||
sockets_to_close = @sockets
|
close_sockets(@sockets)
|
||||||
end
|
|
||||||
sockets_to_close.each do |sock|
|
|
||||||
begin
|
|
||||||
sock.close unless sock.closed?
|
|
||||||
rescue IOError => ex
|
|
||||||
warn "IOError when attempting to close socket connected to #{@host}:#{@port}: #{ex.inspect}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@sockets.clear
|
|
||||||
@pids.clear
|
|
||||||
@checked_out.clear
|
|
||||||
@closed = true
|
@closed = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def closed?
|
def closed?
|
||||||
@closed
|
@closed
|
||||||
|
@ -300,5 +290,18 @@ module Mongo
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def close_sockets(sockets)
|
||||||
|
sockets.each do |socket|
|
||||||
|
begin
|
||||||
|
socket.close unless socket.closed?
|
||||||
|
rescue IOError => ex
|
||||||
|
warn "IOError when attempting to close socket connected to #{@host}:#{@port}: #{ex.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -84,6 +84,10 @@ module Mongo
|
||||||
@refresh_required
|
@refresh_required
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def closed?
|
||||||
|
pools.all? { |pool| pool.closed? }
|
||||||
|
end
|
||||||
|
|
||||||
def close(opts={})
|
def close(opts={})
|
||||||
begin
|
begin
|
||||||
if @primary_pool
|
if @primary_pool
|
||||||
|
@ -114,6 +118,10 @@ module Mongo
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def pools
|
||||||
|
[@primary_pool, *@secondary_pools]
|
||||||
|
end
|
||||||
|
|
||||||
def validate_existing_member(member)
|
def validate_existing_member(member)
|
||||||
config = member.set_config
|
config = member.set_config
|
||||||
if !config
|
if !config
|
||||||
|
|
Loading…
Reference in New Issue