mongo 1.1.1 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE.txt +1 -13
- data/{README.rdoc → README.md} +113 -151
- data/Rakefile +17 -4
- data/docs/1.0_UPGRADE.md +21 -0
- data/docs/CREDITS.md +119 -0
- data/docs/HISTORY.md +158 -0
- data/docs/TUTORIAL.md +247 -0
- data/lib/mongo.rb +1 -1
- data/lib/mongo/collection.rb +67 -32
- data/lib/mongo/connection.rb +39 -8
- data/lib/mongo/cursor.rb +1 -1
- data/lib/mongo/db.rb +19 -6
- data/lib/mongo/exceptions.rb +3 -0
- data/lib/mongo/gridfs/grid.rb +4 -1
- data/lib/mongo/gridfs/grid_file_system.rb +5 -2
- data/lib/mongo/util/pool.rb +115 -0
- data/mongo.gemspec +4 -5
- data/test/bson/bson_test.rb +3 -1
- data/test/collection_test.rb +30 -0
- data/test/connection_test.rb +65 -52
- data/test/cursor_test.rb +27 -0
- data/test/replica_sets/connect_test.rb +24 -1
- data/test/safe_test.rb +42 -0
- data/test/unit/db_test.rb +2 -0
- data/test/unit/grid_test.rb +49 -0
- data/test/unit/safe_test.rb +125 -0
- metadata +18 -22
- data/HISTORY +0 -215
- data/bin/bson_benchmark.rb +0 -59
- data/bin/fail_if_no_c.rb +0 -11
- data/bin/insert.rb +0 -35
- data/bin/oid.rb +0 -13
- data/examples/admin.rb +0 -43
- data/examples/capped.rb +0 -22
- data/examples/cursor.rb +0 -48
- data/examples/gridfs.rb +0 -44
- data/examples/index_test.rb +0 -126
- data/examples/info.rb +0 -31
- data/examples/queries.rb +0 -70
- data/examples/simple.rb +0 -24
- data/examples/strict.rb +0 -35
- data/examples/types.rb +0 -36
data/lib/mongo/exceptions.rb
CHANGED
@@ -42,6 +42,9 @@ module Mongo
|
|
42
42
|
# Raised on failures in connection to the database server.
|
43
43
|
class ConnectionError < MongoRubyError; end
|
44
44
|
|
45
|
+
# Raised on failures in connection to the database server.
|
46
|
+
class ReplicaSetConnectionError < ConnectionError; end
|
47
|
+
|
45
48
|
# Raised on failures in connection to the database server.
|
46
49
|
class ConnectionTimeoutError < MongoRubyError; end
|
47
50
|
|
data/lib/mongo/gridfs/grid.rb
CHANGED
@@ -38,7 +38,10 @@ module Mongo
|
|
38
38
|
@chunks = @db["#{fs_name}.chunks"]
|
39
39
|
@fs_name = fs_name
|
40
40
|
|
41
|
-
|
41
|
+
# Ensure indexes only if not connected to slave.
|
42
|
+
unless db.connection.slave_ok?
|
43
|
+
@chunks.create_index([['files_id', Mongo::ASCENDING], ['n', Mongo::ASCENDING]], :unique => true)
|
44
|
+
end
|
42
45
|
end
|
43
46
|
|
44
47
|
# Store a file in the file store. This method is designed only for writing new files;
|
@@ -39,8 +39,11 @@ module Mongo
|
|
39
39
|
|
40
40
|
@default_query_opts = {:sort => [['filename', 1], ['uploadDate', -1]], :limit => 1}
|
41
41
|
|
42
|
-
|
43
|
-
|
42
|
+
# Ensure indexes only if not connected to slave.
|
43
|
+
unless db.connection.slave_ok?
|
44
|
+
@files.create_index([['filename', 1], ['uploadDate', -1]])
|
45
|
+
@chunks.create_index([['files_id', Mongo::ASCENDING], ['n', Mongo::ASCENDING]], :unique => true)
|
46
|
+
end
|
44
47
|
end
|
45
48
|
|
46
49
|
# Open a file for reading or writing. Note that the options for this method only apply
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# --
|
4
|
+
# Copyright (C) 2008-2010 10gen Inc.
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
|
18
|
+
module Mongo
|
19
|
+
class Pool
|
20
|
+
|
21
|
+
def initialize(host, port, socket, options={})
|
22
|
+
@host, @port = host, port
|
23
|
+
|
24
|
+
# Pool size and timeout.
|
25
|
+
@size = options[:pool_size] || 1
|
26
|
+
@timeout = options[:timeout] || 5.0
|
27
|
+
|
28
|
+
# Mutex for synchronizing pool access
|
29
|
+
@connection_mutex = Mutex.new
|
30
|
+
|
31
|
+
# Global safe option. This is false by default.
|
32
|
+
@safe = options[:safe] || false
|
33
|
+
|
34
|
+
# Create a mutex when a new key, in this case a socket,
|
35
|
+
# is added to the hash.
|
36
|
+
@safe_mutexes = Hash.new { |h, k| h[k] = Mutex.new }
|
37
|
+
|
38
|
+
# Condition variable for signal and wait
|
39
|
+
@queue = ConditionVariable.new
|
40
|
+
|
41
|
+
@sockets = []
|
42
|
+
@checked_out = []
|
43
|
+
end
|
44
|
+
|
45
|
+
def close
|
46
|
+
end
|
47
|
+
|
48
|
+
# Return a socket to the pool.
|
49
|
+
def checkin(socket)
|
50
|
+
@connection_mutex.synchronize do
|
51
|
+
@checked_out.delete(socket)
|
52
|
+
@queue.signal
|
53
|
+
end
|
54
|
+
true
|
55
|
+
end
|
56
|
+
|
57
|
+
# Adds a new socket to the pool and checks it out.
|
58
|
+
#
|
59
|
+
# This method is called exclusively from #checkout;
|
60
|
+
# therefore, it runs within a mutex.
|
61
|
+
def checkout_new_socket
|
62
|
+
begin
|
63
|
+
socket = TCPSocket.new(@host, @port)
|
64
|
+
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
65
|
+
rescue => ex
|
66
|
+
raise ConnectionFailure, "Failed to connect socket: #{ex}"
|
67
|
+
end
|
68
|
+
@sockets << socket
|
69
|
+
@checked_out << socket
|
70
|
+
socket
|
71
|
+
end
|
72
|
+
|
73
|
+
# Checks out the first available socket from the pool.
|
74
|
+
#
|
75
|
+
# This method is called exclusively from #checkout;
|
76
|
+
# therefore, it runs within a mutex.
|
77
|
+
def checkout_existing_socket
|
78
|
+
socket = (@sockets - @checked_out).first
|
79
|
+
@checked_out << socket
|
80
|
+
socket
|
81
|
+
end
|
82
|
+
|
83
|
+
# Check out an existing socket or create a new socket if the maximum
|
84
|
+
# pool size has not been exceeded. Otherwise, wait for the next
|
85
|
+
# available socket.
|
86
|
+
def checkout
|
87
|
+
connect if !connected?
|
88
|
+
start_time = Time.now
|
89
|
+
loop do
|
90
|
+
if (Time.now - start_time) > @timeout
|
91
|
+
raise ConnectionTimeoutError, "could not obtain connection within " +
|
92
|
+
"#{@timeout} seconds. The max pool size is currently #{@size}; " +
|
93
|
+
"consider increasing the pool size or timeout."
|
94
|
+
end
|
95
|
+
|
96
|
+
@connection_mutex.synchronize do
|
97
|
+
socket = if @checked_out.size < @sockets.size
|
98
|
+
checkout_existing_socket
|
99
|
+
elsif @sockets.size < @size
|
100
|
+
checkout_new_socket
|
101
|
+
end
|
102
|
+
|
103
|
+
return socket if socket
|
104
|
+
|
105
|
+
# Otherwise, wait
|
106
|
+
if @logger
|
107
|
+
@logger.warn "MONGODB Waiting for available connection; " +
|
108
|
+
"#{@checked_out.size} of #{@size} connections checked out."
|
109
|
+
end
|
110
|
+
@queue.wait(@connection_mutex)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
data/mongo.gemspec
CHANGED
@@ -11,10 +11,9 @@ Gem::Specification.new do |s|
|
|
11
11
|
|
12
12
|
s.require_paths = ['lib']
|
13
13
|
|
14
|
-
s.files = ['README.
|
15
|
-
'mongo.gemspec', 'LICENSE.txt']
|
14
|
+
s.files = ['README.md', 'Rakefile', 'mongo.gemspec', 'LICENSE.txt']
|
16
15
|
s.files += ['lib/mongo.rb'] + Dir['lib/mongo/**/*.rb']
|
17
|
-
s.files += Dir['examples/**/*.rb'] + Dir['bin/**/*.rb']
|
16
|
+
s.files += Dir['docs/**/*.md'] + Dir['examples/**/*.rb'] + Dir['bin/**/*.rb']
|
18
17
|
s.files += Dir['bin/mongo_console']
|
19
18
|
s.test_files = Dir['test/**/*.rb']
|
20
19
|
|
@@ -25,8 +24,8 @@ Gem::Specification.new do |s|
|
|
25
24
|
s.test_files -= Dir['test/mongo_bson/*.rb'] # remove these files from the manifest
|
26
25
|
|
27
26
|
s.has_rdoc = true
|
28
|
-
s.rdoc_options = ['--main', 'README.
|
29
|
-
s.extra_rdoc_files = ['README.
|
27
|
+
s.rdoc_options = ['--main', 'README.md', '--inline-source']
|
28
|
+
s.extra_rdoc_files = ['README.md']
|
30
29
|
|
31
30
|
s.authors = ['Jim Menard', 'Mike Dirolf', 'Kyle Banker']
|
32
31
|
s.email = 'mongodb-dev@googlegroups.com'
|
data/test/bson/bson_test.rb
CHANGED
@@ -437,11 +437,13 @@ class BSONTest < Test::Unit::TestCase
|
|
437
437
|
# HashWithIndifferentAccess can cause problems for _id but not for other
|
438
438
|
# keys. rather than require rails to test with HWIA directly, we do this
|
439
439
|
# somewhat hacky test.
|
440
|
+
#
|
441
|
+
# Note that the driver only eliminates duplicate ids when move_id is true.
|
440
442
|
def test_no_duplicate_id
|
441
443
|
dup = {"_id" => "foo", :_id => "foo"}
|
442
444
|
one = {"_id" => "foo"}
|
443
445
|
|
444
|
-
assert_equal @encoder.serialize(one).to_a, @encoder.serialize(dup).to_a
|
446
|
+
assert_equal @encoder.serialize(one, false, true).to_a, @encoder.serialize(dup, false, true).to_a
|
445
447
|
end
|
446
448
|
|
447
449
|
def test_duplicate_keys
|
data/test/collection_test.rb
CHANGED
@@ -26,6 +26,20 @@ class TestCollection < Test::Unit::TestCase
|
|
26
26
|
assert @coll.pk_factory.is_a?(Object)
|
27
27
|
end
|
28
28
|
|
29
|
+
class TestPK
|
30
|
+
def self.create_pk
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_pk_factory_on_collection
|
35
|
+
@coll = Collection.new(@@db, 'foo', TestPK)
|
36
|
+
assert_equal TestPK, @coll.pk_factory
|
37
|
+
|
38
|
+
|
39
|
+
@coll2 = Collection.new(@@db, 'foo', :pk => TestPK)
|
40
|
+
assert_equal TestPK, @coll2.pk_factory
|
41
|
+
end
|
42
|
+
|
29
43
|
def test_valid_names
|
30
44
|
assert_raise Mongo::InvalidNSName do
|
31
45
|
@@db["te$t"]
|
@@ -628,6 +642,22 @@ class TestCollection < Test::Unit::TestCase
|
|
628
642
|
assert @collection.index_information['a_1']['unique'] == true
|
629
643
|
end
|
630
644
|
|
645
|
+
should "drop duplicates" do
|
646
|
+
@collection.insert({:a => 1})
|
647
|
+
@collection.insert({:a => 1})
|
648
|
+
assert_equal 2, @collection.find({:a => 1}).count
|
649
|
+
@collection.create_index([['a', Mongo::ASCENDING]], :unique => true, :dropDups => true)
|
650
|
+
assert_equal 1, @collection.find({:a => 1}).count
|
651
|
+
end
|
652
|
+
|
653
|
+
should "drop duplicates with ruby-like drop_dups key" do
|
654
|
+
@collection.insert({:a => 1})
|
655
|
+
@collection.insert({:a => 1})
|
656
|
+
assert_equal 2, @collection.find({:a => 1}).count
|
657
|
+
@collection.create_index([['a', Mongo::ASCENDING]], :unique => true, :drop_dups => true)
|
658
|
+
assert_equal 1, @collection.find({:a => 1}).count
|
659
|
+
end
|
660
|
+
|
631
661
|
should "create an index in the background" do
|
632
662
|
if @@version > '1.3.1'
|
633
663
|
@collection.create_index([['b', Mongo::ASCENDING]], :background => true)
|
data/test/connection_test.rb
CHANGED
@@ -9,15 +9,15 @@ class TestConnection < Test::Unit::TestCase
|
|
9
9
|
include BSON
|
10
10
|
|
11
11
|
def setup
|
12
|
-
@
|
12
|
+
@conn = standard_connection
|
13
13
|
end
|
14
14
|
|
15
15
|
def teardown
|
16
|
-
@
|
16
|
+
@conn[MONGO_TEST_DB].get_last_error
|
17
17
|
end
|
18
18
|
|
19
19
|
def test_server_info
|
20
|
-
server_info = @
|
20
|
+
server_info = @conn.server_info
|
21
21
|
assert server_info.keys.include?("version")
|
22
22
|
assert Mongo::Support.ok?(server_info)
|
23
23
|
end
|
@@ -29,63 +29,76 @@ class TestConnection < Test::Unit::TestCase
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def test_server_version
|
32
|
-
assert_match /\d\.\d+(\.\d+)?/, @
|
32
|
+
assert_match /\d\.\d+(\.\d+)?/, @conn.server_version.to_s
|
33
33
|
end
|
34
34
|
|
35
35
|
def test_invalid_database_names
|
36
|
-
assert_raise TypeError do @
|
36
|
+
assert_raise TypeError do @conn.db(4) end
|
37
37
|
|
38
|
-
assert_raise Mongo::InvalidNSName do @
|
39
|
-
assert_raise Mongo::InvalidNSName do @
|
40
|
-
assert_raise Mongo::InvalidNSName do @
|
41
|
-
assert_raise Mongo::InvalidNSName do @
|
42
|
-
assert_raise Mongo::InvalidNSName do @
|
43
|
-
assert_raise Mongo::InvalidNSName do @
|
38
|
+
assert_raise Mongo::InvalidNSName do @conn.db('') end
|
39
|
+
assert_raise Mongo::InvalidNSName do @conn.db('te$t') end
|
40
|
+
assert_raise Mongo::InvalidNSName do @conn.db('te.t') end
|
41
|
+
assert_raise Mongo::InvalidNSName do @conn.db('te\\t') end
|
42
|
+
assert_raise Mongo::InvalidNSName do @conn.db('te/t') end
|
43
|
+
assert_raise Mongo::InvalidNSName do @conn.db('te st') end
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_replica_set_connection_name
|
47
|
+
assert_raise_error(Mongo::ReplicaSetConnectionError, "replSet") do
|
48
|
+
standard_connection(:name => "replica-set-foo")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_options_passed_to_db
|
53
|
+
@pk_mock = Object.new
|
54
|
+
db = @conn.db('test', :pk => @pk_mock, :strict => true)
|
55
|
+
assert_equal @pk_mock, db.pk_factory
|
56
|
+
assert db.strict?
|
44
57
|
end
|
45
58
|
|
46
59
|
def test_database_info
|
47
|
-
@
|
48
|
-
@
|
60
|
+
@conn.drop_database(MONGO_TEST_DB)
|
61
|
+
@conn.db(MONGO_TEST_DB).collection('info-test').insert('a' => 1)
|
49
62
|
|
50
|
-
info = @
|
63
|
+
info = @conn.database_info
|
51
64
|
assert_not_nil info
|
52
65
|
assert_kind_of Hash, info
|
53
66
|
assert_not_nil info[MONGO_TEST_DB]
|
54
67
|
assert info[MONGO_TEST_DB] > 0
|
55
68
|
|
56
|
-
@
|
69
|
+
@conn.drop_database(MONGO_TEST_DB)
|
57
70
|
end
|
58
71
|
|
59
72
|
def test_copy_database
|
60
|
-
@
|
61
|
-
@
|
62
|
-
old_object = @
|
63
|
-
new_object = @
|
73
|
+
@conn.db('old').collection('copy-test').insert('a' => 1)
|
74
|
+
@conn.copy_database('old', 'new', host_port)
|
75
|
+
old_object = @conn.db('old').collection('copy-test').find.next_document
|
76
|
+
new_object = @conn.db('new').collection('copy-test').find.next_document
|
64
77
|
assert_equal old_object, new_object
|
65
|
-
@
|
66
|
-
@
|
78
|
+
@conn.drop_database('old')
|
79
|
+
@conn.drop_database('new')
|
67
80
|
end
|
68
81
|
|
69
82
|
def test_copy_database_with_auth
|
70
|
-
@
|
71
|
-
@
|
83
|
+
@conn.db('old').collection('copy-test').insert('a' => 1)
|
84
|
+
@conn.db('old').add_user('bob', 'secret')
|
72
85
|
|
73
86
|
assert_raise Mongo::OperationFailure do
|
74
|
-
@
|
87
|
+
@conn.copy_database('old', 'new', host_port, 'bob', 'badpassword')
|
75
88
|
end
|
76
89
|
|
77
|
-
result = @
|
90
|
+
result = @conn.copy_database('old', 'new', host_port, 'bob', 'secret')
|
78
91
|
assert Mongo::Support.ok?(result)
|
79
92
|
|
80
|
-
@
|
81
|
-
@
|
93
|
+
@conn.drop_database('old')
|
94
|
+
@conn.drop_database('new')
|
82
95
|
end
|
83
96
|
|
84
97
|
def test_database_names
|
85
|
-
@
|
86
|
-
@
|
98
|
+
@conn.drop_database(MONGO_TEST_DB)
|
99
|
+
@conn.db(MONGO_TEST_DB).collection('info-test').insert('a' => 1)
|
87
100
|
|
88
|
-
names = @
|
101
|
+
names = @conn.database_names
|
89
102
|
assert_not_nil names
|
90
103
|
assert_kind_of Array, names
|
91
104
|
assert names.length >= 1
|
@@ -112,15 +125,15 @@ class TestConnection < Test::Unit::TestCase
|
|
112
125
|
end
|
113
126
|
|
114
127
|
def test_drop_database
|
115
|
-
db = @
|
128
|
+
db = @conn.db('ruby-mongo-will-be-deleted')
|
116
129
|
coll = db.collection('temp')
|
117
130
|
coll.remove
|
118
131
|
coll.insert(:name => 'temp')
|
119
132
|
assert_equal 1, coll.count()
|
120
|
-
assert @
|
133
|
+
assert @conn.database_names.include?('ruby-mongo-will-be-deleted')
|
121
134
|
|
122
|
-
@
|
123
|
-
assert !@
|
135
|
+
@conn.drop_database('ruby-mongo-will-be-deleted')
|
136
|
+
assert !@conn.database_names.include?('ruby-mongo-will-be-deleted')
|
124
137
|
end
|
125
138
|
|
126
139
|
def test_nodes
|
@@ -138,15 +151,15 @@ class TestConnection < Test::Unit::TestCase
|
|
138
151
|
end
|
139
152
|
|
140
153
|
def test_fsync_lock
|
141
|
-
assert !@
|
142
|
-
@
|
143
|
-
assert @
|
144
|
-
assert_equal 1, @
|
145
|
-
assert_equal "unlock requested", @
|
154
|
+
assert !@conn.locked?
|
155
|
+
@conn.lock!
|
156
|
+
assert @conn.locked?
|
157
|
+
assert_equal 1, @conn['admin']['$cmd.sys.inprog'].find_one['fsyncLock'], "Not fsync-locked"
|
158
|
+
assert_equal "unlock requested", @conn.unlock!['info']
|
146
159
|
unlocked = false
|
147
160
|
counter = 0
|
148
161
|
while counter < 5
|
149
|
-
if @
|
162
|
+
if @conn['admin']['$cmd.sys.inprog'].find_one['fsyncLock'].nil?
|
150
163
|
unlocked = true
|
151
164
|
break
|
152
165
|
else
|
@@ -154,7 +167,7 @@ class TestConnection < Test::Unit::TestCase
|
|
154
167
|
counter += 1
|
155
168
|
end
|
156
169
|
end
|
157
|
-
assert !@
|
170
|
+
assert !@conn.locked?
|
158
171
|
assert unlocked, "mongod failed to unlock"
|
159
172
|
end
|
160
173
|
|
@@ -192,35 +205,35 @@ class TestConnection < Test::Unit::TestCase
|
|
192
205
|
|
193
206
|
context "Connection exceptions" do
|
194
207
|
setup do
|
195
|
-
@
|
196
|
-
@coll = @
|
208
|
+
@con = standard_connection(:pool_size => 10, :timeout => 10)
|
209
|
+
@coll = @con[MONGO_TEST_DB]['test-connection-exceptions']
|
197
210
|
end
|
198
211
|
|
199
212
|
should "release connection if an exception is raised on send_message" do
|
200
|
-
@
|
201
|
-
assert_equal 0, @
|
213
|
+
@con.stubs(:send_message_on_socket).raises(ConnectionFailure)
|
214
|
+
assert_equal 0, @con.checked_out.size
|
202
215
|
assert_raise ConnectionFailure do
|
203
216
|
@coll.insert({:test => "insert"})
|
204
217
|
end
|
205
|
-
assert_equal 0, @
|
218
|
+
assert_equal 0, @con.checked_out.size
|
206
219
|
end
|
207
220
|
|
208
221
|
should "release connection if an exception is raised on send_with_safe_check" do
|
209
|
-
@
|
210
|
-
assert_equal 0, @
|
222
|
+
@con.stubs(:receive).raises(ConnectionFailure)
|
223
|
+
assert_equal 0, @con.checked_out.size
|
211
224
|
assert_raise ConnectionFailure do
|
212
225
|
@coll.insert({:test => "insert"}, :safe => true)
|
213
226
|
end
|
214
|
-
assert_equal 0, @
|
227
|
+
assert_equal 0, @con.checked_out.size
|
215
228
|
end
|
216
229
|
|
217
230
|
should "release connection if an exception is raised on receive_message" do
|
218
|
-
@
|
219
|
-
assert_equal 0, @
|
231
|
+
@con.stubs(:receive).raises(ConnectionFailure)
|
232
|
+
assert_equal 0, @con.checked_out.size
|
220
233
|
assert_raise ConnectionFailure do
|
221
234
|
@coll.find.to_a
|
222
235
|
end
|
223
|
-
assert_equal 0, @
|
236
|
+
assert_equal 0, @con.checked_out.size
|
224
237
|
end
|
225
238
|
end
|
226
239
|
end
|