RUBY-189 use result of ismaster's maxBsonObjectSize
This commit is contained in:
parent
01c38eabdd
commit
9da68bb3db
|
@ -88,6 +88,8 @@ static VALUE InvalidDocument;
|
||||||
static VALUE DigestMD5;
|
static VALUE DigestMD5;
|
||||||
static VALUE RB_HASH;
|
static VALUE RB_HASH;
|
||||||
|
|
||||||
|
static int max_bson_size;
|
||||||
|
|
||||||
#if HAVE_RUBY_ENCODING_H
|
#if HAVE_RUBY_ENCODING_H
|
||||||
#include "ruby/encoding.h"
|
#include "ruby/encoding.h"
|
||||||
#define STR_NEW(p,n) \
|
#define STR_NEW(p,n) \
|
||||||
|
@ -582,9 +584,9 @@ static void write_doc(buffer_t buffer, VALUE hash, VALUE check_keys, VALUE move_
|
||||||
length = buffer_get_position(buffer) - start_position;
|
length = buffer_get_position(buffer) - start_position;
|
||||||
|
|
||||||
// make sure that length doesn't exceed 4MB
|
// make sure that length doesn't exceed 4MB
|
||||||
if (length > 4 * 1024 * 1024) {
|
if (length > max_bson_size) {
|
||||||
buffer_free(buffer);
|
buffer_free(buffer);
|
||||||
rb_raise(InvalidDocument, "Document too large: BSON documents are limited to 4MB.");
|
rb_raise(InvalidDocument, "Document too large: BSON documents are limited to %d bytes.", max_bson_size);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SAFE_WRITE_AT_POS(buffer, length_location, (const char*)&length, 4);
|
SAFE_WRITE_AT_POS(buffer, length_location, (const char*)&length, 4);
|
||||||
|
@ -902,6 +904,13 @@ static VALUE objectid_generate(VALUE self)
|
||||||
return oid;
|
return oid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void method_update_max_bson_size(VALUE self, VALUE connection) {
|
||||||
|
max_bson_size = FIX2INT(rb_funcall(connection, rb_intern("max_bson_size"), 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
static VALUE method_max_bson_size(VALUE self) {
|
||||||
|
return INT2FIX(max_bson_size);
|
||||||
|
}
|
||||||
|
|
||||||
void Init_cbson() {
|
void Init_cbson() {
|
||||||
VALUE bson, CBson, Digest, ext_version, digest;
|
VALUE bson, CBson, Digest, ext_version, digest;
|
||||||
|
@ -939,6 +948,8 @@ void Init_cbson() {
|
||||||
rb_define_const(CBson, "VERSION", ext_version);
|
rb_define_const(CBson, "VERSION", ext_version);
|
||||||
rb_define_module_function(CBson, "serialize", method_serialize, 3);
|
rb_define_module_function(CBson, "serialize", method_serialize, 3);
|
||||||
rb_define_module_function(CBson, "deserialize", method_deserialize, 1);
|
rb_define_module_function(CBson, "deserialize", method_deserialize, 1);
|
||||||
|
rb_define_module_function(CBson, "max_bson_size", method_max_bson_size, 0);
|
||||||
|
rb_define_module_function(CBson, "update_max_bson_size", method_update_max_bson_size, 1);
|
||||||
|
|
||||||
rb_require("digest/md5");
|
rb_require("digest/md5");
|
||||||
Digest = rb_const_get(rb_cObject, rb_intern("Digest"));
|
Digest = rb_const_get(rb_cObject, rb_intern("Digest"));
|
||||||
|
@ -953,4 +964,6 @@ void Init_cbson() {
|
||||||
rb_str_new2(hostname));
|
rb_str_new2(hostname));
|
||||||
memcpy(hostname_digest, RSTRING_PTR(digest), 16);
|
memcpy(hostname_digest, RSTRING_PTR(digest), 16);
|
||||||
hostname_digest[16] = '\0';
|
hostname_digest[16] = '\0';
|
||||||
|
|
||||||
|
max_bson_size = 4 * 1024 * 1024;
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
|
@ -61,6 +61,8 @@ public class RubyBSONEncoder extends BSONEncoder {
|
||||||
private boolean _check_keys;
|
private boolean _check_keys;
|
||||||
private boolean _move_id;
|
private boolean _move_id;
|
||||||
|
|
||||||
|
private static final int DEFAULT_MAX_BSON_SIZE = 4 * 1024 * 1024;
|
||||||
|
private static int _max_bson_size = DEFAULT_MAX_BSON_SIZE;
|
||||||
private static final int BIT_SIZE = 64;
|
private static final int BIT_SIZE = 64;
|
||||||
private static final long MAX = (1L << (BIT_SIZE - 1)) - 1;
|
private static final long MAX = (1L << (BIT_SIZE - 1)) - 1;
|
||||||
private static final BigInteger LONG_MAX = BigInteger.valueOf(MAX);
|
private static final BigInteger LONG_MAX = BigInteger.valueOf(MAX);
|
||||||
|
@ -84,6 +86,17 @@ public class RubyBSONEncoder extends BSONEncoder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static RubyFixnum max_bson_size(RubyObject obj) {
|
||||||
|
Ruby _run = obj.getRuntime();
|
||||||
|
return _run.newFixnum(_max_bson_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void update_max_bson_size(RubyObject obj, RubyObject conn) {
|
||||||
|
Ruby _run = obj.getRuntime();
|
||||||
|
_max_bson_size = ((Long)JavaEmbedUtils.invokeMethod( _run, conn, "max_bson_size",
|
||||||
|
new Object[] {}, Object.class)).intValue();
|
||||||
|
}
|
||||||
|
|
||||||
public RubyString encode( Object arg ) {
|
public RubyString encode( Object arg ) {
|
||||||
RubyHash o = (RubyHash)arg;
|
RubyHash o = (RubyHash)arg;
|
||||||
BasicOutputBuffer buf = new BasicOutputBuffer();
|
BasicOutputBuffer buf = new BasicOutputBuffer();
|
||||||
|
@ -202,10 +215,10 @@ public class RubyBSONEncoder extends BSONEncoder {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we're within the 4MB limit
|
// Make sure we're within the 4MB limit
|
||||||
if ( _buf.size() > 4 * 1024 * 1024 ) {
|
if ( _buf.size() > _max_bson_size ) {
|
||||||
_rbRaise( (RubyClass)_rbclsInvalidDocument,
|
_rbRaise( (RubyClass)_rbclsInvalidDocument,
|
||||||
"Document is too large (" + _buf.size() + "). BSON documents are limited to 4MB (" +
|
"Document is too large (" + _buf.size() + "). BSON documents are limited to " +
|
||||||
4 * 1024 * 1024 + ").");
|
_max_bson_size + " bytes." );
|
||||||
}
|
}
|
||||||
|
|
||||||
_buf.write( EOO );
|
_buf.write( EOO );
|
||||||
|
|
|
@ -28,5 +28,12 @@ module BSON
|
||||||
CBson.deserialize(ByteBuffer.new(buf).to_s)
|
CBson.deserialize(ByteBuffer.new(buf).to_s)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.max_bson_size
|
||||||
|
CBson.max_bson_size
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.update_max_bson_size(connection)
|
||||||
|
CBson.update_max_bson_size(connection)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,5 +17,12 @@ module BSON
|
||||||
callback.get
|
callback.get
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.max_bson_size
|
||||||
|
Java::OrgJbson::RubyBSONEncoder.max_bson_size(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.update_max_bson_size(connection)
|
||||||
|
Java::OrgJbson::RubyBSONEncoder.update_max_bson_size(self, connection)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,6 +20,10 @@ module BSON
|
||||||
# A BSON seralizer/deserializer in pure Ruby.
|
# A BSON seralizer/deserializer in pure Ruby.
|
||||||
class BSON_RUBY
|
class BSON_RUBY
|
||||||
|
|
||||||
|
DEFAULT_MAX_BSON_SIZE = 4 * 1024 * 1024
|
||||||
|
|
||||||
|
@@max_bson_size = DEFAULT_MAX_BSON_SIZE
|
||||||
|
|
||||||
MINKEY = -1
|
MINKEY = -1
|
||||||
EOO = 0
|
EOO = 0
|
||||||
NUMBER = 1
|
NUMBER = 1
|
||||||
|
@ -68,6 +72,14 @@ module BSON
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.update_max_bson_size(connection)
|
||||||
|
@@max_bson_size = connection.max_bson_size
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.max_bson_size
|
||||||
|
@@max_bson_size
|
||||||
|
end
|
||||||
|
|
||||||
def self.serialize_cstr(buf, val)
|
def self.serialize_cstr(buf, val)
|
||||||
buf.put_binary(to_utf8_binary(val.to_s))
|
buf.put_binary(to_utf8_binary(val.to_s))
|
||||||
buf.put_binary(NULL_BYTE)
|
buf.put_binary(NULL_BYTE)
|
||||||
|
@ -120,8 +132,8 @@ module BSON
|
||||||
end
|
end
|
||||||
|
|
||||||
serialize_eoo_element(@buf)
|
serialize_eoo_element(@buf)
|
||||||
if @buf.size > 4 * 1024 * 1024
|
if @buf.size > @@max_bson_size
|
||||||
raise InvalidDocument, "Document is too large (#{@buf.size}). BSON documents are limited to 4MB (#{4 * 1024 * 1024})."
|
raise InvalidDocument, "Document is too large (#{@buf.size}). BSON documents are limited to #{@@max_bson_size} bytes."
|
||||||
end
|
end
|
||||||
@buf.put_int(@buf.size, 0)
|
@buf.put_int(@buf.size, 0)
|
||||||
@buf
|
@buf
|
||||||
|
|
|
@ -11,6 +11,8 @@ module Mongo
|
||||||
DESCENDING = -1
|
DESCENDING = -1
|
||||||
GEO2D = '2d'
|
GEO2D = '2d'
|
||||||
|
|
||||||
|
DEFAULT_MAX_BSON_SIZE = 4 * 1024 * 1024
|
||||||
|
|
||||||
module Constants
|
module Constants
|
||||||
OP_REPLY = 1
|
OP_REPLY = 1
|
||||||
OP_MSG = 1000
|
OP_MSG = 1000
|
||||||
|
|
|
@ -440,7 +440,9 @@ module Mongo
|
||||||
set_primary(@host_to_try)
|
set_primary(@host_to_try)
|
||||||
end
|
end
|
||||||
|
|
||||||
if !connected?
|
if connected?
|
||||||
|
BSON::BSON_CODER.update_max_bson_size(self)
|
||||||
|
else
|
||||||
raise ConnectionFailure, "Failed to connect to a master node at #{@host_to_try[0]}:#{@host_to_try[1]}"
|
raise ConnectionFailure, "Failed to connect to a master node at #{@host_to_try[0]}:#{@host_to_try[1]}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -470,6 +472,15 @@ module Mongo
|
||||||
@primary_pool = nil
|
@primary_pool = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Returns the maximum BSON object size as returned by the core server.
|
||||||
|
# Use the 4MB default when the server doesn't report this.
|
||||||
|
#
|
||||||
|
# @return [Integer]
|
||||||
|
def max_bson_size
|
||||||
|
config = self['admin'].command({:ismaster => 1})
|
||||||
|
config['maxBsonObjectSize'] || Mongo::DEFAULT_MAX_BSON_SIZE
|
||||||
|
end
|
||||||
|
|
||||||
# Checkout a socket for reading (i.e., a secondary node).
|
# Checkout a socket for reading (i.e., a secondary node).
|
||||||
# Note: this is overridden in ReplSetConnection.
|
# Note: this is overridden in ReplSetConnection.
|
||||||
def checkout_reader
|
def checkout_reader
|
||||||
|
|
|
@ -111,7 +111,9 @@ module Mongo
|
||||||
|
|
||||||
pick_secondary_for_read if @read_secondary
|
pick_secondary_for_read if @read_secondary
|
||||||
|
|
||||||
if !connected?
|
if connected?
|
||||||
|
BSON::BSON_CODER.update_max_bson_size(self)
|
||||||
|
else
|
||||||
if @secondary_pools.empty?
|
if @secondary_pools.empty?
|
||||||
raise ConnectionFailure, "Failed to connect any given host:port"
|
raise ConnectionFailure, "Failed to connect any given host:port"
|
||||||
else
|
else
|
||||||
|
|
|
@ -67,13 +67,17 @@ class BSONTest < Test::Unit::TestCase
|
||||||
assert_doc_pass(doc)
|
assert_doc_pass(doc)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_document_length
|
def test_limit_max_bson_size
|
||||||
doc = {'name' => 'a' * 5 * 1024 * 1024}
|
doc = {'name' => 'a' * BSON_CODER.max_bson_size}
|
||||||
assert_raise InvalidDocument do
|
assert_raise InvalidDocument do
|
||||||
assert @encoder.serialize(doc)
|
assert @encoder.serialize(doc)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_max_bson_size
|
||||||
|
assert BSON_CODER.max_bson_size >= BSON::DEFAULT_MAX_BSON_SIZE
|
||||||
|
end
|
||||||
|
|
||||||
def test_round_trip
|
def test_round_trip
|
||||||
doc = {'doc' => 123}
|
doc = {'doc' => 123}
|
||||||
@encoder.deserialize(@encoder.serialize(doc))
|
@encoder.deserialize(@encoder.serialize(doc))
|
||||||
|
|
|
@ -165,6 +165,41 @@ class TestConnection < Test::Unit::TestCase
|
||||||
assert unlocked, "mongod failed to unlock"
|
assert unlocked, "mongod failed to unlock"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_max_bson_size_value
|
||||||
|
conn = standard_connection
|
||||||
|
if conn.server_version > "1.6"
|
||||||
|
assert_equal conn['admin'].command({:ismaster => 1})['maxBsonObjectSize'], conn.max_bson_size
|
||||||
|
end
|
||||||
|
|
||||||
|
conn.connect
|
||||||
|
assert_equal BSON::BSON_CODER.max_bson_size, conn.max_bson_size
|
||||||
|
doc = {'n' => 'a' * (BSON_CODER.max_bson_size - 11)}
|
||||||
|
assert_raise InvalidDocument do
|
||||||
|
assert BSON::BSON_CODER.serialize(doc)
|
||||||
|
end
|
||||||
|
|
||||||
|
limit = 7 * 1024 * 1024
|
||||||
|
conn.stubs(:max_bson_size).returns(limit)
|
||||||
|
conn.connect
|
||||||
|
assert_equal limit, conn.max_bson_size
|
||||||
|
assert_equal limit, BSON::BSON_CODER.max_bson_size
|
||||||
|
doc = {'n' => 'a' * ((limit) - 11)}
|
||||||
|
assert_raise_error InvalidDocument, "limited to #{limit}" do
|
||||||
|
assert BSON::BSON_CODER.serialize(doc)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_max_bson_size_with_old_mongod
|
||||||
|
conn = standard_connection(:connect => false)
|
||||||
|
|
||||||
|
admin_db = Object.new
|
||||||
|
admin_db.expects(:command).returns({'ok' => 1, 'ismaster' => 1}).twice
|
||||||
|
conn.expects(:[]).with('admin').returns(admin_db).twice
|
||||||
|
|
||||||
|
conn.connect
|
||||||
|
assert_equal Mongo::DEFAULT_MAX_BSON_SIZE, BSON::BSON_CODER.max_bson_size
|
||||||
|
end
|
||||||
|
|
||||||
context "Saved authentications" do
|
context "Saved authentications" do
|
||||||
setup do
|
setup do
|
||||||
@conn = standard_connection
|
@conn = standard_connection
|
||||||
|
|
Loading…
Reference in New Issue