mongo 1.5.1 → 1.5.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/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.
|