mongo 1.5.1 → 1.5.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +11 -6
- data/docs/HISTORY.md +7 -0
- data/lib/mongo.rb +4 -5
- data/lib/mongo/collection.rb +1 -1
- data/lib/mongo/connection.rb +13 -6
- data/lib/mongo/cursor.rb +26 -13
- data/lib/mongo/networking.rb +39 -24
- data/lib/mongo/repl_set_connection.rb +67 -43
- data/lib/mongo/util/node.rb +9 -2
- data/lib/mongo/util/pool.rb +6 -6
- data/lib/mongo/util/pool_manager.rb +29 -12
- data/lib/mongo/version.rb +1 -1
- data/test/connection_test.rb +36 -0
- data/test/replica_sets/basic_test.rb +56 -0
- data/test/replica_sets/connect_test.rb +13 -4
- data/test/replica_sets/pooled_insert_test.rb +58 -0
- data/test/replica_sets/query_test.rb +1 -1
- data/test/unit/pool_manager_test.rb +1 -0
- metadata +123 -130
- data/lib/mongo/util/timeout.rb +0 -42
- data/test/bson/bson_string_test.rb +0 -30
- data/test/pool_test.rb +0 -21
- data/test/replica_sets/threading_test.rb +0 -111
- data/test/timeout_test.rb +0 -14
data/Rakefile
CHANGED
@@ -1,11 +1,16 @@
|
|
1
1
|
# -*- mode: ruby; -*-
|
2
|
-
|
3
|
-
require 'rubygems
|
2
|
+
if RUBY_VERSION < '1.9.0'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rubygems/specification'
|
5
|
+
end
|
4
6
|
require 'fileutils'
|
5
|
-
require 'rake'
|
6
7
|
require 'rake/testtask'
|
7
|
-
require 'rake/gempackagetask'
|
8
8
|
require 'rbconfig'
|
9
|
+
require 'rake'
|
10
|
+
begin
|
11
|
+
require 'ci/reporter/rake/test_unit'
|
12
|
+
rescue LoadError
|
13
|
+
end
|
9
14
|
include Config
|
10
15
|
ENV['TEST_MODE'] = 'TRUE'
|
11
16
|
|
@@ -154,11 +159,11 @@ namespace :bamboo do
|
|
154
159
|
end
|
155
160
|
|
156
161
|
namespace :test do
|
157
|
-
task :ruby
|
162
|
+
task :ruby do
|
158
163
|
Rake::Task['test:ruby'].invoke
|
159
164
|
end
|
160
165
|
|
161
|
-
task :c
|
166
|
+
task :c do
|
162
167
|
Rake::Task['gem:install_extensions'].invoke
|
163
168
|
Rake::Task['test:c'].invoke
|
164
169
|
end
|
data/docs/HISTORY.md
CHANGED
data/lib/mongo.rb
CHANGED
@@ -16,14 +16,13 @@
|
|
16
16
|
# limitations under the License.
|
17
17
|
# ++
|
18
18
|
|
19
|
-
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
20
|
-
|
21
19
|
require 'mongo/version'
|
22
20
|
|
23
21
|
module Mongo
|
24
|
-
ASCENDING
|
25
|
-
DESCENDING
|
26
|
-
GEO2D
|
22
|
+
ASCENDING = 1
|
23
|
+
DESCENDING = -1
|
24
|
+
GEO2D = '2d'
|
25
|
+
GEOHAYSTACK = 'geoHaystack'
|
27
26
|
|
28
27
|
DEFAULT_MAX_BSON_SIZE = 4 * 1024 * 1024
|
29
28
|
|
data/lib/mongo/collection.rb
CHANGED
@@ -888,7 +888,7 @@ module Mongo
|
|
888
888
|
field_spec[spec.to_s] = 1
|
889
889
|
elsif spec.is_a?(Array) && spec.all? {|field| field.is_a?(Array) }
|
890
890
|
spec.each do |f|
|
891
|
-
if [Mongo::ASCENDING, Mongo::DESCENDING, Mongo::GEO2D].include?(f[1])
|
891
|
+
if [Mongo::ASCENDING, Mongo::DESCENDING, Mongo::GEO2D, Mongo::GEOHAYSTACK].include?(f[1])
|
892
892
|
field_spec[f[0].to_s] = f[1]
|
893
893
|
else
|
894
894
|
raise MongoArgumentError, "Invalid index field #{f[1].inspect}; " +
|
data/lib/mongo/connection.rb
CHANGED
@@ -458,12 +458,13 @@ module Mongo
|
|
458
458
|
@primary_pool
|
459
459
|
end
|
460
460
|
|
461
|
-
# The value of the read preference.
|
462
|
-
# this is a single-node connection, the value
|
463
|
-
# is +:primary+, and the connection will read
|
464
|
-
# from whichever type of node it's connected to.
|
461
|
+
# The value of the read preference.
|
465
462
|
def read_preference
|
466
|
-
|
463
|
+
if slave_ok?
|
464
|
+
:secondary
|
465
|
+
else
|
466
|
+
:primary
|
467
|
+
end
|
467
468
|
end
|
468
469
|
|
469
470
|
# Close the connection to the database.
|
@@ -614,7 +615,13 @@ module Mongo
|
|
614
615
|
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
615
616
|
end
|
616
617
|
|
617
|
-
|
618
|
+
if @connect_timeout
|
619
|
+
Mongo::TimeoutHandler.timeout(@connect_timeout, OperationTimeout) do
|
620
|
+
config = self['admin'].command({:ismaster => 1}, :socket => socket)
|
621
|
+
end
|
622
|
+
else
|
623
|
+
config = self['admin'].command({:ismaster => 1}, :socket => socket)
|
624
|
+
end
|
618
625
|
rescue OperationFailure, SocketError, SystemCallError, IOError => ex
|
619
626
|
close
|
620
627
|
ensure
|
data/lib/mongo/cursor.rb
CHANGED
@@ -86,7 +86,7 @@ module Mongo
|
|
86
86
|
if(!@timeout)
|
87
87
|
add_option(OP_QUERY_NO_CURSOR_TIMEOUT)
|
88
88
|
end
|
89
|
-
if(@
|
89
|
+
if(@read_preference != :primary)
|
90
90
|
add_option(OP_QUERY_SLAVE_OK)
|
91
91
|
end
|
92
92
|
if(@tailable)
|
@@ -136,7 +136,7 @@ module Mongo
|
|
136
136
|
# If the server has stopped being the master (e.g., it's one of a
|
137
137
|
# pair but it has died or something like that) then we close that
|
138
138
|
# connection. The next request will re-open on master server.
|
139
|
-
if err
|
139
|
+
if err.include?("not master")
|
140
140
|
@connection.close
|
141
141
|
raise ConnectionFailure.new(err, doc['code'], doc)
|
142
142
|
end
|
@@ -463,9 +463,8 @@ module Mongo
|
|
463
463
|
|
464
464
|
def send_initial_query
|
465
465
|
message = construct_query_message
|
466
|
-
payload = instrument_payload if @logger
|
467
466
|
sock = @socket || checkout_socket_from_connection
|
468
|
-
instrument(:find,
|
467
|
+
instrument(:find, instrument_payload) do
|
469
468
|
begin
|
470
469
|
results, @n_received, @cursor_id = @connection.receive_message(
|
471
470
|
Mongo::Constants::OP_QUERY, message, nil, sock, @command,
|
@@ -518,13 +517,21 @@ module Mongo
|
|
518
517
|
end
|
519
518
|
|
520
519
|
def checkout_socket_from_connection
|
521
|
-
|
522
|
-
|
523
|
-
@
|
524
|
-
|
525
|
-
|
526
|
-
|
520
|
+
socket = nil
|
521
|
+
begin
|
522
|
+
@checkin_connection = true
|
523
|
+
if @command || @read_preference == :primary
|
524
|
+
socket = @connection.checkout_writer
|
525
|
+
else
|
526
|
+
@read_pool = @connection.read_pool
|
527
|
+
socket = @connection.checkout_reader
|
528
|
+
end
|
529
|
+
rescue SystemStackError, NoMemoryError, SystemCallError => ex
|
530
|
+
@connection.close
|
531
|
+
raise ex
|
527
532
|
end
|
533
|
+
|
534
|
+
socket
|
528
535
|
end
|
529
536
|
|
530
537
|
def checkout_socket_for_op_get_more
|
@@ -540,9 +547,15 @@ module Mongo
|
|
540
547
|
pool.host == @read_pool.host && pool.port == @read_pool.port
|
541
548
|
end
|
542
549
|
if new_pool
|
543
|
-
|
544
|
-
|
545
|
-
|
550
|
+
sock = nil
|
551
|
+
begin
|
552
|
+
@read_pool = new_pool
|
553
|
+
sock = new_pool.checkout
|
554
|
+
@checkin_read_pool = true
|
555
|
+
rescue SystemStackError, NoMemoryError, SystemCallError => ex
|
556
|
+
@connection.close
|
557
|
+
raise ex
|
558
|
+
end
|
546
559
|
return sock
|
547
560
|
else
|
548
561
|
raise Mongo::OperationFailure, "Failure to continue iterating " +
|
data/lib/mongo/networking.rb
CHANGED
@@ -28,19 +28,25 @@ module Mongo
|
|
28
28
|
add_message_headers(message, operation)
|
29
29
|
packed_message = message.to_s
|
30
30
|
|
31
|
-
|
32
|
-
sock = checkout_writer
|
33
|
-
else
|
34
|
-
sock = checkout_reader
|
35
|
-
end
|
36
|
-
|
31
|
+
sock = nil
|
37
32
|
begin
|
38
|
-
send_message_on_socket(packed_message, sock)
|
39
|
-
ensure
|
40
33
|
if connection == :writer
|
41
|
-
|
34
|
+
sock = checkout_writer
|
42
35
|
else
|
43
|
-
|
36
|
+
sock = checkout_reader
|
37
|
+
end
|
38
|
+
|
39
|
+
send_message_on_socket(packed_message, sock)
|
40
|
+
rescue SystemStackError, NoMemoryError, SystemCallError => ex
|
41
|
+
close
|
42
|
+
raise ex
|
43
|
+
ensure
|
44
|
+
if sock
|
45
|
+
if connection == :writer
|
46
|
+
checkin_writer(sock)
|
47
|
+
else
|
48
|
+
checkin_reader(sock)
|
49
|
+
end
|
44
50
|
end
|
45
51
|
end
|
46
52
|
end
|
@@ -66,14 +72,18 @@ module Mongo
|
|
66
72
|
last_error_id = add_message_headers(last_error_message, Mongo::Constants::OP_QUERY)
|
67
73
|
|
68
74
|
packed_message = message.append!(last_error_message).to_s
|
69
|
-
sock =
|
75
|
+
sock = nil
|
70
76
|
begin
|
77
|
+
sock = checkout_writer
|
71
78
|
send_message_on_socket(packed_message, sock)
|
72
79
|
docs, num_received, cursor_id = receive(sock, last_error_id)
|
73
80
|
checkin_writer(sock)
|
74
81
|
rescue ConnectionFailure, OperationFailure, OperationTimeout => ex
|
75
82
|
checkin_writer(sock)
|
76
83
|
raise ex
|
84
|
+
rescue SystemStackError, NoMemoryError, SystemCallError => ex
|
85
|
+
close
|
86
|
+
raise ex
|
77
87
|
end
|
78
88
|
|
79
89
|
if num_received == 1 && (error = docs[0]['err'] || docs[0]['errmsg'])
|
@@ -103,24 +113,29 @@ module Mongo
|
|
103
113
|
read=:primary, exhaust=false)
|
104
114
|
request_id = add_message_headers(message, operation)
|
105
115
|
packed_message = message.to_s
|
106
|
-
if socket
|
107
|
-
sock = socket
|
108
|
-
should_checkin = false
|
109
|
-
else
|
110
|
-
if command || read == :primary
|
111
|
-
sock = checkout_writer
|
112
|
-
elsif read == :secondary
|
113
|
-
sock = checkout_reader
|
114
|
-
else
|
115
|
-
sock = checkout_tagged(read)
|
116
|
-
end
|
117
|
-
should_checkin = true
|
118
|
-
end
|
119
116
|
|
120
117
|
result = ''
|
118
|
+
sock = nil
|
121
119
|
begin
|
120
|
+
if socket
|
121
|
+
sock = socket
|
122
|
+
should_checkin = false
|
123
|
+
else
|
124
|
+
if command || read == :primary
|
125
|
+
sock = checkout_writer
|
126
|
+
elsif read == :secondary
|
127
|
+
sock = checkout_reader
|
128
|
+
else
|
129
|
+
sock = checkout_tagged(read)
|
130
|
+
end
|
131
|
+
should_checkin = true
|
132
|
+
end
|
133
|
+
|
122
134
|
send_message_on_socket(packed_message, sock)
|
123
135
|
result = receive(sock, request_id, exhaust)
|
136
|
+
rescue SystemStackError, NoMemoryError, SystemCallError => ex
|
137
|
+
close
|
138
|
+
raise ex
|
124
139
|
ensure
|
125
140
|
if should_checkin
|
126
141
|
if command || read == :primary
|
@@ -51,9 +51,8 @@ module Mongo
|
|
51
51
|
# @option options [Float] :pool_timeout (5.0) When all of the connections a pool are checked out,
|
52
52
|
# this is the number of seconds to wait for a new connection to be released before throwing an exception.
|
53
53
|
# Note: this setting is relevant only for multi-threaded applications.
|
54
|
-
# @option opts [Float] :op_timeout (
|
55
|
-
#
|
56
|
-
# @option opts [Float] :connect_timeout (nil) The number of seconds to wait before timing out a
|
54
|
+
# @option opts [Float] :op_timeout (30) The number of seconds to wait for a read operation to time out.
|
55
|
+
# @option opts [Float] :connect_timeout (30) The number of seconds to wait before timing out a
|
57
56
|
# connection attempt.
|
58
57
|
# @option opts [Boolean] :ssl (false) If true, create the connection to the server using SSL.
|
59
58
|
# @option opts [Boolean] :refresh_mode (false) Set this to :sync to periodically update the
|
@@ -91,8 +90,10 @@ module Mongo
|
|
91
90
|
raise MongoArgumentError, "A ReplSetConnection requires at least one seed node."
|
92
91
|
end
|
93
92
|
|
94
|
-
# The list of seed
|
93
|
+
# The original, immutable list of seed node.
|
94
|
+
# TODO: add a method for replacing this list of node.
|
95
95
|
@seeds = args
|
96
|
+
@seeds.freeze
|
96
97
|
|
97
98
|
# TODO: get rid of this
|
98
99
|
@nodes = @seeds.dup
|
@@ -152,10 +153,12 @@ module Mongo
|
|
152
153
|
def connect
|
153
154
|
log(:info, "Connecting...")
|
154
155
|
return if @connected
|
155
|
-
manager = PoolManager.new(self, @seeds)
|
156
|
-
manager.connect
|
157
156
|
|
158
|
-
|
157
|
+
discovered_seeds = @manager ? @manager.seeds : []
|
158
|
+
@manager = PoolManager.new(self, discovered_seeds)
|
159
|
+
|
160
|
+
@manager.connect
|
161
|
+
@refresh_version += 1
|
159
162
|
|
160
163
|
if @require_primary && self.primary.nil? #TODO: in v2.0, we'll let this be optional and do a lazy connect.
|
161
164
|
close
|
@@ -200,19 +203,21 @@ module Mongo
|
|
200
203
|
# to get the refresh lock.
|
201
204
|
def hard_refresh!
|
202
205
|
log(:info, "Initiating hard refresh...")
|
203
|
-
|
206
|
+
discovered_seeds = @manager ? @manager.seeds : []
|
207
|
+
background_manager = PoolManager.new(self, discovered_seeds | @seeds)
|
204
208
|
background_manager.connect
|
205
209
|
|
206
210
|
# TODO: make sure that connect has succeeded
|
207
211
|
old_manager = @manager
|
208
|
-
|
212
|
+
@manager = background_manager
|
209
213
|
old_manager.close(:soft => true)
|
214
|
+
@refresh_version += 1
|
210
215
|
|
211
216
|
return true
|
212
217
|
end
|
213
218
|
|
214
219
|
def connected?
|
215
|
-
self.primary_pool || self.read_pool
|
220
|
+
@connected && (self.primary_pool || self.read_pool)
|
216
221
|
end
|
217
222
|
|
218
223
|
# @deprecated
|
@@ -255,9 +260,13 @@ module Mongo
|
|
255
260
|
end
|
256
261
|
|
257
262
|
# Close the connection to the database.
|
258
|
-
def close
|
263
|
+
def close(opts={})
|
264
|
+
if opts[:soft]
|
265
|
+
@manager.close(:soft => true) if @manager
|
266
|
+
else
|
267
|
+
@manager.close if @manager
|
268
|
+
end
|
259
269
|
@connected = false
|
260
|
-
@manager.close(:soft => true) if @manager
|
261
270
|
end
|
262
271
|
|
263
272
|
# If a ConnectionFailure is raised, this method will be called
|
@@ -298,12 +307,22 @@ module Mongo
|
|
298
307
|
# Note that @read_pool might point to the primary pool
|
299
308
|
# if no read pool has been defined.
|
300
309
|
def checkout_reader
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
if !socket
|
310
|
+
if connected?
|
311
|
+
sync_refresh
|
312
|
+
else
|
305
313
|
connect
|
306
|
-
|
314
|
+
end
|
315
|
+
|
316
|
+
begin
|
317
|
+
socket = get_socket_from_pool(self.read_pool)
|
318
|
+
|
319
|
+
if !socket
|
320
|
+
connect
|
321
|
+
socket = get_socket_from_pool(self.primary_pool)
|
322
|
+
end
|
323
|
+
rescue => ex
|
324
|
+
checkin(socket) if socket
|
325
|
+
raise ex
|
307
326
|
end
|
308
327
|
|
309
328
|
if socket
|
@@ -315,12 +334,21 @@ module Mongo
|
|
315
334
|
|
316
335
|
# Checkout a socket for writing (i.e., a primary node).
|
317
336
|
def checkout_writer
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
if !socket
|
337
|
+
if connected?
|
338
|
+
sync_refresh
|
339
|
+
else
|
322
340
|
connect
|
341
|
+
end
|
342
|
+
begin
|
323
343
|
socket = get_socket_from_pool(self.primary_pool)
|
344
|
+
|
345
|
+
if !socket
|
346
|
+
connect
|
347
|
+
socket = get_socket_from_pool(self.primary_pool)
|
348
|
+
end
|
349
|
+
rescue => ex
|
350
|
+
checkin(socket)
|
351
|
+
raise ex
|
324
352
|
end
|
325
353
|
|
326
354
|
if socket
|
@@ -332,8 +360,8 @@ module Mongo
|
|
332
360
|
|
333
361
|
# Checkin a socket used for reading.
|
334
362
|
def checkin_reader(socket)
|
335
|
-
if !self.read_pool.checkin(socket)
|
336
|
-
|
363
|
+
if !((self.read_pool && self.read_pool.checkin(socket)) ||
|
364
|
+
(self.primary_pool && self.primary_pool.checkin(socket)))
|
337
365
|
close_socket(socket)
|
338
366
|
end
|
339
367
|
sync_refresh
|
@@ -341,7 +369,7 @@ module Mongo
|
|
341
369
|
|
342
370
|
# Checkin a socket used for writing.
|
343
371
|
def checkin_writer(socket)
|
344
|
-
if !self.primary_pool.checkin(socket)
|
372
|
+
if !self.primary_pool || !self.primary_pool.checkin(socket)
|
345
373
|
close_socket(socket)
|
346
374
|
end
|
347
375
|
sync_refresh
|
@@ -349,7 +377,7 @@ module Mongo
|
|
349
377
|
|
350
378
|
def close_socket(socket)
|
351
379
|
begin
|
352
|
-
socket.close
|
380
|
+
socket.close if socket
|
353
381
|
rescue IOError
|
354
382
|
log(:info, "Tried to close socket #{socket} but already closed.")
|
355
383
|
end
|
@@ -372,36 +400,40 @@ module Mongo
|
|
372
400
|
end
|
373
401
|
|
374
402
|
def primary
|
375
|
-
@manager.primary
|
403
|
+
@manager ? @manager.primary : nil
|
376
404
|
end
|
377
405
|
|
378
406
|
# Note: might want to freeze these after connecting.
|
379
407
|
def secondaries
|
380
|
-
@manager.secondaries
|
408
|
+
@manager ? @manager.secondaries : []
|
381
409
|
end
|
382
410
|
|
383
411
|
def hosts
|
384
|
-
@manager.hosts
|
412
|
+
@manager ? @manager.hosts : []
|
385
413
|
end
|
386
414
|
|
387
415
|
def primary_pool
|
388
|
-
@manager.primary_pool
|
416
|
+
@manager ? @manager.primary_pool : nil
|
389
417
|
end
|
390
418
|
|
391
419
|
def read_pool
|
392
|
-
@manager.read_pool
|
420
|
+
@manager ? @manager.read_pool : nil
|
393
421
|
end
|
394
422
|
|
395
423
|
def secondary_pools
|
396
|
-
@manager.secondary_pools
|
424
|
+
@manager ? @manager.secondary_pools : []
|
397
425
|
end
|
398
426
|
|
399
427
|
def tag_map
|
400
|
-
@manager.tag_map
|
428
|
+
@manager ? @manager.tag_map : {}
|
401
429
|
end
|
402
430
|
|
403
431
|
def max_bson_size
|
404
|
-
@manager.max_bson_size
|
432
|
+
if @manager && @manager.max_bson_size
|
433
|
+
@manager.max_bson_size
|
434
|
+
else
|
435
|
+
Mongo::DEFAULT_MAX_BSON_SIZE
|
436
|
+
end
|
405
437
|
end
|
406
438
|
|
407
439
|
private
|
@@ -437,10 +469,10 @@ module Mongo
|
|
437
469
|
@pool_timeout = opts[:pool_timeout] || opts[:timeout] || 5.0
|
438
470
|
|
439
471
|
# Timeout on socket read operation.
|
440
|
-
@op_timeout = opts[:op_timeout] ||
|
472
|
+
@op_timeout = opts[:op_timeout] || 30
|
441
473
|
|
442
474
|
# Timeout on socket connect.
|
443
|
-
@connect_timeout = opts[:connect_timeout] ||
|
475
|
+
@connect_timeout = opts[:connect_timeout] || 30
|
444
476
|
|
445
477
|
# Mutex for synchronizing pool access
|
446
478
|
# TODO: remove this.
|
@@ -468,14 +500,6 @@ module Mongo
|
|
468
500
|
connect if should_connect
|
469
501
|
end
|
470
502
|
|
471
|
-
# Given a pool manager, update this connection's
|
472
|
-
# view of the replica set.
|
473
|
-
def update_config(new_manager)
|
474
|
-
@manager = new_manager
|
475
|
-
@seeds = @manager.seeds.dup
|
476
|
-
@refresh_version += 1
|
477
|
-
end
|
478
|
-
|
479
503
|
# Checkout a socket connected to a node with one of
|
480
504
|
# the provided tags. If no such node exists, raise
|
481
505
|
# an exception.
|