Raise exception if connecting to single slave with slave_ok set to false; pass slave_ok on to queries when set to true.

This commit is contained in:
Kyle Banker 2009-10-15 12:23:29 -04:00
parent c6d4150a51
commit 4e2781faf1
8 changed files with 95 additions and 5 deletions

View File

@ -130,7 +130,7 @@ module Mongo
end
raise RuntimeError, "Unknown options [#{options.inspect}]" unless options.empty?
cursor = @db.query(self, Query.new(selector, fields, skip, limit, sort, hint, snapshot, timeout))
cursor = @db.query(self, Query.new(selector, fields, skip, limit, sort, hint, snapshot, timeout, @db.slave_ok?))
if block_given?
yield cursor
cursor.close()

View File

@ -160,6 +160,9 @@ module Mongo
is_master = master?
@semaphore.lock if semaphore_is_locked
if !@slave_ok && !is_master
raise ConfigurationError, "Trying to connect directly to slave; if this is what you want, specify :slave_ok => true."
end
@slave_ok || is_master
rescue SocketError, SystemCallError, IOError => ex
close if @socket

View File

@ -15,6 +15,12 @@
# Exceptions raised by the MongoDB driver.
module Mongo
# Generic Mongo Ruby Driver exception class.
class MongoRubyError < StandardError; end
# Raised when configuration options cause connections, queries, etc., to fail.
class ConfigurationError < MongoRubyError; end
# Raised when a database operation fails.
class OperationFailure < RuntimeError; end

View File

@ -25,5 +25,6 @@ module Mongo
OP_DELETE = 2006
OP_KILL_CURSORS = 2007
OP_QUERY_SLAVE_OK = 4
OP_QUERY_NO_CURSOR_TIMEOUT = 16
end

View File

@ -67,9 +67,9 @@ module Mongo
# the normal cursor timeout behavior of the mongod process.
# When +false+, the returned cursor will never timeout. Care should
# be taken to ensure that cursors with timeout disabled are properly closed.
def initialize(sel={}, return_fields=nil, number_to_skip=0, number_to_return=0, order_by=nil, hint=nil, snapshot=nil, timeout=true)
@number_to_skip, @number_to_return, @order_by, @hint, @snapshot, @timeout =
number_to_skip, number_to_return, order_by, hint, snapshot, timeout
def initialize(sel={}, return_fields=nil, number_to_skip=0, number_to_return=0, order_by=nil, hint=nil, snapshot=nil, timeout=true, slave_ok=false)
@number_to_skip, @number_to_return, @order_by, @hint, @snapshot, @timeout, @slave_ok =
number_to_skip, number_to_return, order_by, hint, snapshot, timeout, slave_ok
@explain = nil
self.selector = sel
self.fields = return_fields
@ -121,7 +121,9 @@ module Mongo
# Returns an integer indicating which query options have been selected.
# See http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol#MongoWireProtocol-OPQUERY
def query_opts
@timeout ? 0 : OP_QUERY_NO_CURSOR_TIMEOUT
timeout = @timeout ? 0 : OP_QUERY_NO_CURSOR_TIMEOUT
slave_ok = @slave_ok ? OP_QUERY_SLAVE_OK : 0
slave_ok + timeout
end
def to_s

View File

@ -37,4 +37,22 @@ class TestQueryMessage < Test::Unit::TestCase
assert_equal 16, buf[16]
end
def test_timeout_opcodes
@timeout = true
@slave_ok = true
@query = Query.new({}, nil, 0, 0, nil, nil, nil, @timeout, @slave_ok)
@query_message = QueryMessage.new('db', 'collection', @query)
buf = @query_message.buf.instance_variable_get(:@buf)
assert_equal 4, buf[16]
@timeout = false
@slave_ok = true
@query = Query.new({}, nil, 0, 0, nil, nil, nil, @timeout, @slave_ok)
@query_message = QueryMessage.new('db', 'collection', @query)
buf = @query_message.buf.instance_variable_get(:@buf)
assert_equal 20, buf[16]
end
end

View File

@ -33,4 +33,22 @@ class TestQuery < Test::Unit::TestCase
assert_equal 16, @query.query_opts
end
def test_slave_ok_opcodes
@slave_ok = true
@query = Query.new({}, nil, 0, 0, nil, nil, nil, true, @slave_ok)
assert_equal 4, @query.query_opts
@slave_ok = false
@query = Query.new({}, nil, 0, 0, nil, nil, nil, true, @slave_ok)
assert_equal 0, @query.query_opts
end
def test_combined_opcodes
@timeout = false
@slave_ok = true
@query = Query.new({}, nil, 0, 0, nil, nil, nil, @timeout, @slave_ok)
assert_equal 20, @query.query_opts
end
end

View File

@ -0,0 +1,42 @@
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
require 'mongo'
require 'test/unit'
# NOTE: these tests are run only if we can connect to a single MongoDB in slave mode.
class SlaveConnectionTest < Test::Unit::TestCase
include Mongo
def self.connect_to_slave
@@host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
@@port = ENV['MONGO_RUBY_DRIVER_PORT'] || Connection::DEFAULT_PORT
db = Connection.new(@@host, @@port, :slave_ok => true).db('ruby-mongo-demo')
!db.master?
end
if self.connect_to_slave
puts "Connected to slave; running slave tests."
def test_connect_to_slave
assert_raise Mongo::ConfigurationError do
@db = Connection.new(@@host, @@port, :slave_ok => false).db('ruby-mongo-demo')
end
end
def test_slave_ok_sent_to_queries
@db = Connection.new(@@host, @@port, :slave_ok => true).db('ruby-mongo-demo')
@coll = @db['test-collection']
@cursor = @coll.find({})
assert_equal true, @cursor.query.instance_variable_get(:@slave_ok)
end
else
puts "Not connected to slave; skipping slave connection tests."
def test_slave_ok_false_on_queries
@db = Connection.new(@@host, @@port).db('ruby-mongo-demo')
@coll = @db['test-collection']
@cursor = @coll.find({})
assert_nil @cursor.query.instance_variable_get(:@slave_ok)
end
end
end