mongo 1.6.4 → 1.7.0.rc0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +13 -13
- data/Rakefile +7 -10
- data/docs/{GridFS.md → GRID_FS.md} +0 -0
- data/docs/HISTORY.md +16 -0
- data/docs/READ_PREFERENCE.md +70 -10
- data/docs/TUTORIAL.md +2 -2
- data/lib/mongo.rb +2 -0
- data/lib/mongo/collection.rb +62 -11
- data/lib/mongo/connection.rb +31 -41
- data/lib/mongo/cursor.rb +42 -86
- data/lib/mongo/db.rb +10 -8
- data/lib/mongo/networking.rb +30 -65
- data/lib/mongo/repl_set_connection.rb +91 -170
- data/lib/mongo/sharded_connection.rb +221 -0
- data/lib/mongo/util/node.rb +29 -36
- data/lib/mongo/util/pool.rb +10 -3
- data/lib/mongo/util/pool_manager.rb +77 -90
- data/lib/mongo/util/sharding_pool_manager.rb +143 -0
- data/lib/mongo/util/support.rb +22 -2
- data/lib/mongo/util/tcp_socket.rb +10 -15
- data/lib/mongo/util/uri_parser.rb +17 -10
- data/lib/mongo/version.rb +1 -1
- data/test/collection_test.rb +133 -1
- data/test/connection_test.rb +50 -4
- data/test/db_api_test.rb +3 -3
- data/test/db_test.rb +6 -1
- data/test/replica_sets/basic_test.rb +3 -6
- data/test/replica_sets/complex_connect_test.rb +14 -2
- data/test/replica_sets/complex_read_preference_test.rb +237 -0
- data/test/replica_sets/connect_test.rb +47 -67
- data/test/replica_sets/count_test.rb +1 -1
- data/test/replica_sets/cursor_test.rb +70 -0
- data/test/replica_sets/read_preference_test.rb +171 -118
- data/test/replica_sets/refresh_test.rb +3 -3
- data/test/replica_sets/refresh_with_threads_test.rb +2 -2
- data/test/replica_sets/rs_test_helper.rb +2 -2
- data/test/sharded_cluster/basic_test.rb +112 -0
- data/test/sharded_cluster/mongo_config_test.rb +126 -0
- data/test/sharded_cluster/sc_test_helper.rb +39 -0
- data/test/test_helper.rb +3 -3
- data/test/threading/threading_with_large_pool_test.rb +1 -1
- data/test/tools/mongo_config.rb +307 -0
- data/test/tools/repl_set_manager.rb +12 -12
- data/test/unit/collection_test.rb +1 -1
- data/test/unit/cursor_test.rb +11 -6
- data/test/unit/db_test.rb +4 -0
- data/test/unit/grid_test.rb +2 -0
- data/test/unit/read_test.rb +39 -8
- data/test/uri_test.rb +4 -8
- metadata +144 -127
data/lib/mongo/cursor.rb
CHANGED
@@ -77,6 +77,9 @@ module Mongo
|
|
77
77
|
value = collection.read_preference
|
78
78
|
end
|
79
79
|
@read_preference = value.is_a?(Hash) ? value.dup : value
|
80
|
+
@tag_sets = opts.fetch(:tag_sets, @collection.tag_sets)
|
81
|
+
@acceptable_latency = opts.fetch(:acceptable_latency, @collection.acceptable_latency)
|
82
|
+
|
80
83
|
batch_size(opts[:batch_size] || 0)
|
81
84
|
|
82
85
|
@full_collection_name = "#{@collection.db.name}.#{@collection.name}"
|
@@ -98,10 +101,6 @@ module Mongo
|
|
98
101
|
else
|
99
102
|
@command = false
|
100
103
|
end
|
101
|
-
|
102
|
-
@checkin_read_pool = false
|
103
|
-
@checkin_connection = false
|
104
|
-
@read_pool = nil
|
105
104
|
end
|
106
105
|
|
107
106
|
# Guess whether the cursor is alive on the server.
|
@@ -199,7 +198,7 @@ module Mongo
|
|
199
198
|
# This method overrides any sort order specified in the Collection#find
|
200
199
|
# method, and only the last sort applied has an effect.
|
201
200
|
#
|
202
|
-
# @param [Symbol, Array]
|
201
|
+
# @param [Symbol, Array, Hash, OrderedHash] order either 1) a key to sort by 2)
|
203
202
|
# an array of [key, direction] pairs to sort by or 3) a hash of
|
204
203
|
# field => direction pairs to sort by. Direction should be specified as
|
205
204
|
# Mongo::ASCENDING (or :ascending / :asc) or Mongo::DESCENDING
|
@@ -339,7 +338,7 @@ module Mongo
|
|
339
338
|
message.put_int(1)
|
340
339
|
message.put_long(@cursor_id)
|
341
340
|
log(:debug, "Cursor#close #{@cursor_id}")
|
342
|
-
@connection.send_message(Mongo::Constants::OP_KILL_CURSORS, message, :
|
341
|
+
@connection.send_message(Mongo::Constants::OP_KILL_CURSORS, message, :socket => @socket)
|
343
342
|
end
|
344
343
|
@cursor_id = 0
|
345
344
|
@closed = true
|
@@ -462,19 +461,37 @@ module Mongo
|
|
462
461
|
end
|
463
462
|
end
|
464
463
|
|
464
|
+
# Sends initial query -- which is always a read unless it is a command
|
465
|
+
#
|
466
|
+
# Upon ConnectionFailure, tries query 3 times if socket was not provided
|
467
|
+
# and the query is either not a command or is a secondary_ok command.
|
468
|
+
#
|
469
|
+
# Pins pools upon successful read and unpins pool upon ConnectionFailure
|
470
|
+
#
|
465
471
|
def send_initial_query
|
466
|
-
|
467
|
-
sock = @socket || checkout_socket_from_connection
|
472
|
+
tries = 0
|
468
473
|
instrument(:find, instrument_payload) do
|
469
474
|
begin
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
+
tries += 1
|
476
|
+
message = construct_query_message
|
477
|
+
sock = @socket || checkout_socket_from_connection
|
478
|
+
results, @n_received, @cursor_id = @connection.receive_message(
|
479
|
+
Mongo::Constants::OP_QUERY, message, nil, sock, @command,
|
480
|
+
nil, @options & OP_QUERY_EXHAUST != 0)
|
481
|
+
rescue ConnectionFailure => ex
|
482
|
+
if tries < 3 && !@socket && (!@command || Mongo::Support.secondary_ok?(@selector))
|
483
|
+
@connection.unpin_pool(sock.pool) if sock
|
484
|
+
@connection.refresh
|
485
|
+
retry
|
486
|
+
else
|
487
|
+
raise ex
|
488
|
+
end
|
489
|
+
rescue OperationFailure, OperationTimeout => ex
|
475
490
|
raise ex
|
491
|
+
ensure
|
492
|
+
checkin_socket(sock) unless @socket
|
476
493
|
end
|
477
|
-
|
494
|
+
@connection.pin_pool(sock.pool) if !@command && !@socket
|
478
495
|
@returned += @n_received
|
479
496
|
@cache += results
|
480
497
|
@query_run = true
|
@@ -502,97 +519,36 @@ module Mongo
|
|
502
519
|
# Cursor id.
|
503
520
|
message.put_long(@cursor_id)
|
504
521
|
log(:debug, "cursor.refresh() for cursor #{@cursor_id}") if @logger
|
505
|
-
|
522
|
+
|
523
|
+
sock = @socket || checkout_socket_from_connection
|
506
524
|
|
507
525
|
begin
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
raise ex
|
526
|
+
results, @n_received, @cursor_id = @connection.receive_message(
|
527
|
+
Mongo::Constants::OP_GET_MORE, message, nil, sock, @command, nil)
|
528
|
+
ensure
|
529
|
+
checkin_socket(sock) unless @socket
|
513
530
|
end
|
514
|
-
|
531
|
+
|
515
532
|
@returned += @n_received
|
516
533
|
@cache += results
|
517
534
|
close_cursor_if_query_complete
|
518
535
|
end
|
519
536
|
|
520
537
|
def checkout_socket_from_connection
|
521
|
-
socket = nil
|
522
538
|
begin
|
523
|
-
@
|
524
|
-
|
525
|
-
socket = @connection.checkout_writer
|
526
|
-
elsif @read_preference == :secondary_only
|
527
|
-
socket = @connection.checkout_secondary
|
539
|
+
if @command && !Mongo::Support::secondary_ok?(@selector)
|
540
|
+
@connection.checkout_reader(:primary)
|
528
541
|
else
|
529
|
-
@
|
530
|
-
socket = @connection.checkout_reader
|
542
|
+
@connection.checkout_reader(@read_preference, @tag_sets, @acceptable_latency)
|
531
543
|
end
|
532
544
|
rescue SystemStackError, NoMemoryError, SystemCallError => ex
|
533
545
|
@connection.close
|
534
546
|
raise ex
|
535
547
|
end
|
536
|
-
|
537
|
-
socket
|
538
|
-
end
|
539
|
-
|
540
|
-
def checkout_socket_for_op_get_more
|
541
|
-
if @read_pool && (@read_pool != @connection.read_pool)
|
542
|
-
checkout_socket_from_read_pool
|
543
|
-
else
|
544
|
-
checkout_socket_from_connection
|
545
|
-
end
|
546
|
-
end
|
547
|
-
|
548
|
-
def checkout_socket_from_read_pool
|
549
|
-
new_pool = @connection.secondary_pools.detect do |pool|
|
550
|
-
pool.host == @read_pool.host && pool.port == @read_pool.port
|
551
|
-
end
|
552
|
-
if new_pool
|
553
|
-
sock = nil
|
554
|
-
begin
|
555
|
-
@read_pool = new_pool
|
556
|
-
sock = new_pool.checkout
|
557
|
-
@checkin_read_pool = true
|
558
|
-
rescue SystemStackError, NoMemoryError, SystemCallError => ex
|
559
|
-
@connection.close
|
560
|
-
raise ex
|
561
|
-
end
|
562
|
-
return sock
|
563
|
-
else
|
564
|
-
raise Mongo::OperationFailure, "Failure to continue iterating " +
|
565
|
-
"cursor because the the replica set member persisting this " +
|
566
|
-
"cursor at #{@read_pool.host_string} cannot be found."
|
567
|
-
end
|
568
548
|
end
|
569
549
|
|
570
550
|
def checkin_socket(sock)
|
571
|
-
|
572
|
-
@read_pool.checkin(sock)
|
573
|
-
@checkin_read_pool = false
|
574
|
-
elsif @checkin_connection
|
575
|
-
if @command || @read_preference == :primary
|
576
|
-
@connection.checkin_writer(sock)
|
577
|
-
else
|
578
|
-
@connection.checkin_reader(sock)
|
579
|
-
end
|
580
|
-
@checkin_connection = false
|
581
|
-
end
|
582
|
-
end
|
583
|
-
|
584
|
-
def force_checkin_socket(sock)
|
585
|
-
if @checkin_read_pool
|
586
|
-
@read_pool.checkin(sock)
|
587
|
-
@checkin_read_pool = false
|
588
|
-
else
|
589
|
-
if @command || @read_preference == :primary
|
590
|
-
@connection.checkin_writer(sock)
|
591
|
-
else
|
592
|
-
@connection.checkin_reader(sock)
|
593
|
-
end
|
594
|
-
@checkin_connection = false
|
595
|
-
end
|
551
|
+
@connection.checkin(sock)
|
596
552
|
end
|
597
553
|
|
598
554
|
def construct_query_message
|
data/lib/mongo/db.rb
CHANGED
@@ -53,6 +53,9 @@ module Mongo
|
|
53
53
|
# The length of time that Collection.ensure_index should cache index calls
|
54
54
|
attr_accessor :cache_time
|
55
55
|
|
56
|
+
# Read Preference
|
57
|
+
attr_accessor :read_preference, :tag_sets, :acceptable_latency
|
58
|
+
|
56
59
|
# Instances of DB are normally obtained by calling Mongo#db.
|
57
60
|
#
|
58
61
|
# @param [String] name the database name.
|
@@ -72,6 +75,7 @@ module Mongo
|
|
72
75
|
# value is provided, the default value set on this instance's Connection object will be used. This
|
73
76
|
# default can be overridden upon instantiation of any collection by explicity setting a :safe value
|
74
77
|
# on initialization
|
78
|
+
#
|
75
79
|
# @option opts [Integer] :cache_time (300) Set the time that all ensure_index calls should cache the command.
|
76
80
|
#
|
77
81
|
# @core databases constructor_details
|
@@ -87,6 +91,8 @@ module Mongo
|
|
87
91
|
value = @connection.read_preference
|
88
92
|
end
|
89
93
|
@read_preference = value.is_a?(Hash) ? value.dup : value
|
94
|
+
@tag_sets = opts.fetch(:tag_sets, @connection.tag_sets)
|
95
|
+
@acceptable_latency = opts.fetch(:acceptable_latency, @connection.acceptable_latency)
|
90
96
|
@cache_time = opts[:cache_time] || 300 #5 minutes.
|
91
97
|
end
|
92
98
|
|
@@ -112,8 +118,11 @@ module Mongo
|
|
112
118
|
end
|
113
119
|
end
|
114
120
|
|
115
|
-
|
121
|
+
begin
|
122
|
+
socket = @connection.checkout_reader(:primary_preferred)
|
116
123
|
issue_authentication(username, password, save_auth, :socket => socket)
|
124
|
+
ensure
|
125
|
+
socket.pool.checkin(socket) if socket
|
117
126
|
end
|
118
127
|
|
119
128
|
@connection.authenticate_pools
|
@@ -628,13 +637,6 @@ module Mongo
|
|
628
637
|
doc
|
629
638
|
end
|
630
639
|
|
631
|
-
# The value of the read preference. This will be
|
632
|
-
# either +:primary+, +:secondary+, or an object
|
633
|
-
# representing the tags to be read from.
|
634
|
-
def read_preference
|
635
|
-
@read_preference
|
636
|
-
end
|
637
|
-
|
638
640
|
private
|
639
641
|
|
640
642
|
def system_command_collection
|
data/lib/mongo/networking.rb
CHANGED
@@ -23,30 +23,23 @@ module Mongo
|
|
23
23
|
opts = {}
|
24
24
|
end
|
25
25
|
|
26
|
-
connection = opts.fetch(:connection, :writer)
|
27
|
-
|
28
26
|
add_message_headers(message, operation)
|
29
27
|
packed_message = message.to_s
|
30
28
|
|
31
|
-
sock = nil
|
29
|
+
sock = opts.fetch(:socket, nil)
|
32
30
|
begin
|
33
|
-
if
|
34
|
-
sock
|
31
|
+
if operation == Mongo::Constants::OP_KILL_CURSORS && read_preference != :primary
|
32
|
+
sock ||= checkout_reader
|
35
33
|
else
|
36
|
-
sock
|
34
|
+
sock ||= checkout_writer
|
37
35
|
end
|
38
|
-
|
39
36
|
send_message_on_socket(packed_message, sock)
|
40
37
|
rescue SystemStackError, NoMemoryError, SystemCallError => ex
|
41
38
|
close
|
42
39
|
raise ex
|
43
40
|
ensure
|
44
41
|
if sock
|
45
|
-
|
46
|
-
checkin_writer(sock)
|
47
|
-
else
|
48
|
-
checkin_reader(sock)
|
49
|
-
end
|
42
|
+
sock.pool.checkin(sock)
|
50
43
|
end
|
51
44
|
end
|
52
45
|
end
|
@@ -77,9 +70,9 @@ module Mongo
|
|
77
70
|
sock = checkout_writer
|
78
71
|
send_message_on_socket(packed_message, sock)
|
79
72
|
docs, num_received, cursor_id = receive(sock, last_error_id)
|
80
|
-
|
73
|
+
checkin(sock)
|
81
74
|
rescue ConnectionFailure, OperationFailure, OperationTimeout => ex
|
82
|
-
|
75
|
+
checkin(sock)
|
83
76
|
raise ex
|
84
77
|
rescue SystemStackError, NoMemoryError, SystemCallError => ex
|
85
78
|
close
|
@@ -119,24 +112,13 @@ module Mongo
|
|
119
112
|
packed_message = message.to_s
|
120
113
|
|
121
114
|
result = ''
|
122
|
-
sock = nil
|
123
|
-
begin
|
124
|
-
if socket
|
125
|
-
sock = socket
|
126
|
-
should_checkin = false
|
127
|
-
else
|
128
|
-
if command || read == :primary
|
129
|
-
sock = checkout_writer
|
130
|
-
elsif read == :secondary
|
131
|
-
sock = checkout_reader
|
132
|
-
else
|
133
|
-
sock = checkout_tagged(read)
|
134
|
-
end
|
135
|
-
should_checkin = true
|
136
|
-
end
|
137
115
|
|
138
|
-
|
139
|
-
|
116
|
+
begin
|
117
|
+
send_message_on_socket(packed_message, socket)
|
118
|
+
result = receive(socket, request_id, exhaust)
|
119
|
+
rescue ConnectionFailure => ex
|
120
|
+
checkin(socket)
|
121
|
+
raise ex
|
140
122
|
rescue SystemStackError, NoMemoryError, SystemCallError => ex
|
141
123
|
close
|
142
124
|
raise ex
|
@@ -145,16 +127,6 @@ module Mongo
|
|
145
127
|
close if ex.class == IRB::Abort
|
146
128
|
end
|
147
129
|
raise ex
|
148
|
-
ensure
|
149
|
-
if should_checkin
|
150
|
-
if command || read == :primary
|
151
|
-
checkin_writer(sock)
|
152
|
-
elsif read == :secondary
|
153
|
-
checkin_reader(sock)
|
154
|
-
else
|
155
|
-
# TODO: sock = checkout_tagged(read)
|
156
|
-
end
|
157
|
-
end
|
158
130
|
end
|
159
131
|
result
|
160
132
|
end
|
@@ -162,30 +134,25 @@ module Mongo
|
|
162
134
|
private
|
163
135
|
|
164
136
|
def receive(sock, cursor_id, exhaust=false)
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
while(cursor_id != 0) do
|
171
|
-
receive_header(sock, cursor_id, exhaust)
|
172
|
-
number_received, cursor_id = receive_response_header(sock)
|
173
|
-
new_docs, n = read_documents(number_received, sock)
|
174
|
-
docs += new_docs
|
175
|
-
num_received += n
|
176
|
-
end
|
177
|
-
|
178
|
-
return [docs, num_received, cursor_id]
|
179
|
-
else
|
137
|
+
if exhaust
|
138
|
+
docs = []
|
139
|
+
num_received = 0
|
140
|
+
|
141
|
+
while(cursor_id != 0) do
|
180
142
|
receive_header(sock, cursor_id, exhaust)
|
181
143
|
number_received, cursor_id = receive_response_header(sock)
|
182
|
-
|
183
|
-
|
184
|
-
|
144
|
+
new_docs, n = read_documents(number_received, sock)
|
145
|
+
docs += new_docs
|
146
|
+
num_received += n
|
185
147
|
end
|
186
|
-
|
187
|
-
|
188
|
-
|
148
|
+
|
149
|
+
return [docs, num_received, cursor_id]
|
150
|
+
else
|
151
|
+
receive_header(sock, cursor_id, exhaust)
|
152
|
+
number_received, cursor_id = receive_response_header(sock)
|
153
|
+
docs, num_received = read_documents(number_received, sock)
|
154
|
+
|
155
|
+
return [docs, num_received, cursor_id]
|
189
156
|
end
|
190
157
|
end
|
191
158
|
|
@@ -194,7 +161,6 @@ module Mongo
|
|
194
161
|
|
195
162
|
# unpacks to size, request_id, response_to
|
196
163
|
response_to = header.unpack('VVV')[2]
|
197
|
-
|
198
164
|
if !exhaust && expected_response != response_to
|
199
165
|
raise Mongo::ConnectionFailure, "Expected response #{expected_response} but got #{response_to}"
|
200
166
|
end
|
@@ -316,7 +282,6 @@ module Mongo
|
|
316
282
|
end
|
317
283
|
total_bytes_sent
|
318
284
|
rescue => ex
|
319
|
-
close
|
320
285
|
raise ConnectionFailure, "Operation failed with the following exception: #{ex}:#{ex.message}"
|
321
286
|
end
|
322
287
|
end
|
@@ -327,7 +292,7 @@ module Mongo
|
|
327
292
|
begin
|
328
293
|
message = receive_data(length, socket)
|
329
294
|
rescue OperationTimeout, ConnectionFailure => ex
|
330
|
-
close
|
295
|
+
socket.close
|
331
296
|
|
332
297
|
if ex.class == OperationTimeout
|
333
298
|
raise OperationTimeout, "Timed out waiting on socket read."
|
@@ -21,11 +21,11 @@ module Mongo
|
|
21
21
|
# Instantiates and manages connections to a MongoDB replica set.
|
22
22
|
class ReplSetConnection < Connection
|
23
23
|
|
24
|
-
REPL_SET_OPTS = [:read, :refresh_mode, :refresh_interval, :
|
25
|
-
:
|
24
|
+
REPL_SET_OPTS = [:read, :refresh_mode, :refresh_interval, :read_secondary,
|
25
|
+
:rs_name, :name, :tag_sets, :secondary_acceptable_latency_ms]
|
26
26
|
|
27
27
|
attr_reader :replica_set_name, :seeds, :refresh_interval, :refresh_mode,
|
28
|
-
:refresh_version, :manager
|
28
|
+
:refresh_version, :manager, :tag_sets, :acceptable_latency
|
29
29
|
|
30
30
|
# Create a connection to a MongoDB replica set.
|
31
31
|
#
|
@@ -36,38 +36,48 @@ module Mongo
|
|
36
36
|
# Connection#arbiters. This is useful if your application needs to connect manually to nodes other
|
37
37
|
# than the primary.
|
38
38
|
#
|
39
|
-
# @
|
39
|
+
# @overload initialize(seeds=ENV["MONGODB_URI"], opts={})
|
40
|
+
# @param [Array<String>, Array<Array(String, Integer)>] seeds
|
40
41
|
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
#
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
68
|
-
#
|
69
|
-
#
|
70
|
-
#
|
42
|
+
# @option opts [Boolean, Hash] :safe (false) Set the default safe-mode options
|
43
|
+
# propagated to DB objects instantiated off of this Connection. This
|
44
|
+
# default can be overridden upon instantiation of any DB by explicitly setting a :safe value
|
45
|
+
# on initialization.
|
46
|
+
# @option opts [:primary, :primary_preferred, :secondary, :secondary_preferred, :nearest] :read_preference (:primary)
|
47
|
+
# A "read preference" determines the candidate replica set members to which a query or command can be sent.
|
48
|
+
# [:primary]
|
49
|
+
# * Read from primary only.
|
50
|
+
# * Cannot be combined with tags.
|
51
|
+
# [:primary_preferred]
|
52
|
+
# * Read from primary if available, otherwise read from a secondary.
|
53
|
+
# [:secondary]
|
54
|
+
# * Read from secondary if available.
|
55
|
+
# [:secondary_preferred]
|
56
|
+
# * Read from a secondary if available, otherwise read from the primary.
|
57
|
+
# [:nearest]
|
58
|
+
# * Read from any member.
|
59
|
+
# @option opts [Array<Hash{ String, Symbol => Tag Value }>] :tag_sets ([])
|
60
|
+
# Read from replica-set members with these tags.
|
61
|
+
# @option opts [Integer] :secondary_acceptable_latency_ms (15) The acceptable
|
62
|
+
# nearest available member for a member to be considered "near".
|
63
|
+
# @option opts [Logger] :logger (nil) Logger instance to receive driver operation log.
|
64
|
+
# @option opts [Integer] :pool_size (1) The maximum number of socket connections allowed per
|
65
|
+
# connection pool. Note: this setting is relevant only for multi-threaded applications.
|
66
|
+
# @option opts [Float] :pool_timeout (5.0) When all of the connections a pool are checked out,
|
67
|
+
# this is the number of seconds to wait for a new connection to be released before throwing an exception.
|
68
|
+
# Note: this setting is relevant only for multi-threaded applications.
|
69
|
+
# @option opts [Float] :op_timeout (nil) The number of seconds to wait for a read operation to time out.
|
70
|
+
# @option opts [Float] :connect_timeout (30) The number of seconds to wait before timing out a
|
71
|
+
# connection attempt.
|
72
|
+
# @option opts [Boolean] :ssl (false) If true, create the connection to the server using SSL.
|
73
|
+
# @option opts [Boolean] :refresh_mode (false) Set this to :sync to periodically update the
|
74
|
+
# state of the connection every :refresh_interval seconds. Replica set connection failures
|
75
|
+
# will always trigger a complete refresh. This option is useful when you want to add new nodes
|
76
|
+
# or remove replica set nodes not currently in use by the driver.
|
77
|
+
# @option opts [Integer] :refresh_interval (90) If :refresh_mode is enabled, this is the number of seconds
|
78
|
+
# between calls to check the replica set's state.
|
79
|
+
# @note the number of seed nodes does not have to be equal to the number of replica set members.
|
80
|
+
# The purpose of seed nodes is to permit the driver to find at least one replica set member even if a member is down.
|
71
81
|
#
|
72
82
|
# @example Connect to a replica set and provide two seed nodes.
|
73
83
|
# Mongo::ReplSetConnection.new(['localhost:30000', 'localhost:30001'])
|
@@ -80,26 +90,20 @@ module Mongo
|
|
80
90
|
#
|
81
91
|
# @see http://api.mongodb.org/ruby/current/file.REPLICA_SETS.html Replica sets in Ruby
|
82
92
|
#
|
83
|
-
# @raise [MongoArgumentError]
|
93
|
+
# @raise [MongoArgumentError] This is raised for usage errors.
|
84
94
|
#
|
85
|
-
# @raise [
|
86
|
-
# driver fails to connect to a replica set with that name.
|
95
|
+
# @raise [ConnectionFailure] This is raised for the various connection failures.
|
87
96
|
def initialize(*args)
|
88
|
-
|
89
|
-
opts = args.pop
|
90
|
-
else
|
91
|
-
opts = {}
|
92
|
-
end
|
93
|
-
|
97
|
+
opts = args.last.is_a?(Hash) ? args.pop : {}
|
94
98
|
nodes = args
|
95
99
|
|
96
100
|
if nodes.empty? and ENV.has_key?('MONGODB_URI')
|
97
|
-
parser = URIParser.new ENV['MONGODB_URI']
|
101
|
+
parser = URIParser.new ENV['MONGODB_URI']
|
98
102
|
if parser.direct?
|
99
103
|
raise MongoArgumentError, "Mongo::ReplSetConnection.new called with no arguments, but ENV['MONGODB_URI'] implies a direct connection."
|
100
104
|
end
|
101
|
-
opts = parser.connection_options
|
102
|
-
nodes = parser.nodes
|
105
|
+
opts = parser.connection_options.merge! opts
|
106
|
+
nodes = [parser.nodes]
|
103
107
|
end
|
104
108
|
|
105
109
|
unless nodes.length > 0
|
@@ -118,7 +122,6 @@ module Mongo
|
|
118
122
|
end
|
119
123
|
end
|
120
124
|
|
121
|
-
# TODO: add a method for replacing this list of node.
|
122
125
|
@seeds.freeze
|
123
126
|
|
124
127
|
# Refresh
|
@@ -160,8 +163,8 @@ module Mongo
|
|
160
163
|
@connect_mutex.synchronize do
|
161
164
|
return if @connected
|
162
165
|
|
163
|
-
|
164
|
-
@manager = PoolManager.new(self,
|
166
|
+
seeds = @manager.nil? ? @seeds : @manager.seeds
|
167
|
+
@manager = PoolManager.new(self, seeds)
|
165
168
|
|
166
169
|
Thread.current[:managers] ||= Hash.new
|
167
170
|
Thread.current[:managers][self] = @manager
|
@@ -169,10 +172,7 @@ module Mongo
|
|
169
172
|
@manager.connect
|
170
173
|
@refresh_version += 1
|
171
174
|
|
172
|
-
if @
|
173
|
-
close
|
174
|
-
raise ConnectionFailure, "Failed to connect to primary node."
|
175
|
-
elsif @manager.read_pool.nil?
|
175
|
+
if @manager.pools.empty?
|
176
176
|
close
|
177
177
|
raise ConnectionFailure, "Failed to connect to any node."
|
178
178
|
else
|
@@ -213,7 +213,7 @@ module Mongo
|
|
213
213
|
# to get the refresh lock.
|
214
214
|
def hard_refresh!
|
215
215
|
log(:info, "Initiating hard refresh...")
|
216
|
-
discovered_seeds = @manager
|
216
|
+
discovered_seeds = @manager.seeds
|
217
217
|
new_manager = PoolManager.new(self, discovered_seeds | @seeds)
|
218
218
|
new_manager.connect
|
219
219
|
|
@@ -228,7 +228,7 @@ module Mongo
|
|
228
228
|
end
|
229
229
|
|
230
230
|
def connected?
|
231
|
-
@connected &&
|
231
|
+
@connected && !@manager.pools.empty?
|
232
232
|
end
|
233
233
|
|
234
234
|
# @deprecated
|
@@ -296,124 +296,68 @@ module Mongo
|
|
296
296
|
end
|
297
297
|
|
298
298
|
# Returns +true+ if it's okay to read from a secondary node.
|
299
|
-
# Since this is a replica set, this must always be true.
|
300
299
|
#
|
301
300
|
# This method exist primarily so that Cursor objects will
|
302
301
|
# generate query messages with a slaveOkay value of +true+.
|
303
302
|
#
|
304
303
|
# @return [Boolean] +true+
|
305
304
|
def slave_ok?
|
306
|
-
|
305
|
+
@read != :primary
|
307
306
|
end
|
308
307
|
|
309
308
|
def authenticate_pools
|
310
|
-
|
311
|
-
primary_pool.authenticate_existing
|
312
|
-
end
|
313
|
-
secondary_pools.each do |pool|
|
314
|
-
pool.authenticate_existing
|
315
|
-
end
|
309
|
+
pools.each { |pool| pool.authenticate_existing }
|
316
310
|
end
|
317
311
|
|
318
312
|
def logout_pools(db)
|
319
|
-
|
320
|
-
primary_pool.logout_existing(db)
|
321
|
-
end
|
322
|
-
secondary_pools.each do |pool|
|
323
|
-
pool.logout_existing(db)
|
324
|
-
end
|
313
|
+
pools.each { |pool| pool.logout_existing(db) }
|
325
314
|
end
|
326
315
|
|
327
316
|
# Generic socket checkout
|
328
317
|
# Takes a block that returns a socket from pool
|
329
|
-
def checkout
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
end
|
335
|
-
|
318
|
+
def checkout
|
319
|
+
ensure_manager
|
320
|
+
|
321
|
+
connected? ? sync_refresh : connect
|
322
|
+
|
336
323
|
begin
|
337
|
-
socket =
|
324
|
+
socket = yield
|
338
325
|
rescue => ex
|
339
326
|
checkin(socket) if socket
|
340
327
|
raise ex
|
341
328
|
end
|
342
|
-
|
329
|
+
|
343
330
|
if socket
|
344
331
|
socket
|
345
332
|
else
|
346
333
|
@connected = false
|
347
334
|
raise ConnectionFailure.new("Could not checkout a socket.")
|
348
335
|
end
|
349
|
-
|
350
|
-
|
351
|
-
# Checkout best available socket by trying primary
|
352
|
-
# pool first and then falling back to secondary.
|
353
|
-
def checkout_best
|
354
|
-
checkout do
|
355
|
-
socket = get_socket_from_pool(:primary)
|
356
|
-
if !socket
|
357
|
-
connect
|
358
|
-
socket = get_socket_from_pool(:secondary)
|
359
|
-
end
|
360
|
-
socket
|
361
|
-
end
|
336
|
+
socket
|
362
337
|
end
|
363
338
|
|
364
|
-
|
365
|
-
# Note that @read_pool might point to the primary pool
|
366
|
-
# if no read pool has been defined.
|
367
|
-
def checkout_reader
|
368
|
-
checkout do
|
369
|
-
socket = get_socket_from_pool(:read)
|
370
|
-
if !socket
|
371
|
-
connect
|
372
|
-
socket = get_socket_from_pool(:primary)
|
373
|
-
end
|
374
|
-
socket
|
375
|
-
end
|
376
|
-
end
|
377
|
-
|
378
|
-
# Checkout a socket from a secondary
|
379
|
-
# For :read_preference => :secondary_only
|
380
|
-
def checkout_secondary
|
339
|
+
def checkout_reader(mode=@read, tag_sets=@tag_sets, acceptable_latency=@acceptable_latency)
|
381
340
|
checkout do
|
382
|
-
|
341
|
+
pool = read_pool(mode, tag_sets, acceptable_latency)
|
342
|
+
get_socket_from_pool(pool)
|
383
343
|
end
|
384
344
|
end
|
385
345
|
|
386
346
|
# Checkout a socket for writing (i.e., a primary node).
|
387
347
|
def checkout_writer
|
388
348
|
checkout do
|
389
|
-
get_socket_from_pool(
|
349
|
+
get_socket_from_pool(primary_pool)
|
390
350
|
end
|
391
351
|
end
|
392
352
|
|
393
353
|
# Checkin a socket used for reading.
|
394
|
-
def
|
395
|
-
if socket
|
396
|
-
socket.pool.checkin(socket)
|
397
|
-
end
|
398
|
-
sync_refresh
|
399
|
-
end
|
400
|
-
|
401
|
-
# Checkin a socket used for writing.
|
402
|
-
def checkin_writer(socket)
|
403
|
-
if socket
|
354
|
+
def checkin(socket)
|
355
|
+
if socket && socket.pool
|
404
356
|
socket.pool.checkin(socket)
|
405
357
|
end
|
406
358
|
sync_refresh
|
407
359
|
end
|
408
360
|
|
409
|
-
def close_socket(socket)
|
410
|
-
begin
|
411
|
-
socket.close if socket
|
412
|
-
rescue IOError
|
413
|
-
log(:info, "Tried to close socket #{socket} but already closed.")
|
414
|
-
end
|
415
|
-
end
|
416
|
-
|
417
361
|
def ensure_manager
|
418
362
|
Thread.current[:managers] ||= Hash.new
|
419
363
|
|
@@ -422,25 +366,19 @@ module Mongo
|
|
422
366
|
end
|
423
367
|
end
|
424
368
|
|
425
|
-
def
|
426
|
-
|
369
|
+
def pin_pool(pool)
|
370
|
+
@manager.pinned_pools[Thread.current] = pool if @manager
|
371
|
+
end
|
427
372
|
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
when :secondary
|
432
|
-
secondary_pool
|
433
|
-
when :read
|
434
|
-
read_pool
|
435
|
-
end
|
373
|
+
def unpin_pool(pool)
|
374
|
+
@manager.pinned_pools[Thread.current] = nil if @manager
|
375
|
+
end
|
436
376
|
|
377
|
+
def get_socket_from_pool(pool)
|
437
378
|
begin
|
438
|
-
if pool
|
439
|
-
|
440
|
-
|
441
|
-
rescue ConnectionFailure => ex
|
442
|
-
log(:info, "Failed to checkout from #{pool} with #{ex.class}; #{ex.message}")
|
443
|
-
return nil
|
379
|
+
pool.checkout if pool
|
380
|
+
rescue ConnectionFailure
|
381
|
+
nil
|
444
382
|
end
|
445
383
|
end
|
446
384
|
|
@@ -469,8 +407,8 @@ module Mongo
|
|
469
407
|
local_manager ? local_manager.primary_pool : nil
|
470
408
|
end
|
471
409
|
|
472
|
-
def read_pool
|
473
|
-
local_manager ? local_manager.read_pool : nil
|
410
|
+
def read_pool(mode=@read, tags=@tag_sets, acceptable_latency=@acceptable_latency)
|
411
|
+
local_manager ? local_manager.read_pool(mode, tags, acceptable_latency) : nil
|
474
412
|
end
|
475
413
|
|
476
414
|
def secondary_pool
|
@@ -497,9 +435,6 @@ module Mongo
|
|
497
435
|
|
498
436
|
# Parse option hash
|
499
437
|
def setup(opts)
|
500
|
-
# Require a primary node to connect?
|
501
|
-
@require_primary = opts.fetch(:require_primary, true)
|
502
|
-
|
503
438
|
# Refresh
|
504
439
|
@refresh_mode = opts.fetch(:refresh_mode, false)
|
505
440
|
@refresh_interval = opts.fetch(:refresh_interval, 90)
|
@@ -516,17 +451,20 @@ module Mongo
|
|
516
451
|
"Refresh mode must be either :sync or false."
|
517
452
|
end
|
518
453
|
|
519
|
-
#
|
454
|
+
# Determine read preference
|
520
455
|
if opts[:read_secondary]
|
521
456
|
warn ":read_secondary options has now been deprecated and will " +
|
522
457
|
"be removed in driver v2.0. Use the :read option instead."
|
523
458
|
@read_secondary = opts.fetch(:read_secondary, false)
|
524
|
-
@read = :
|
459
|
+
@read = :secondary_preferred
|
525
460
|
else
|
526
461
|
@read = opts.fetch(:read, :primary)
|
527
462
|
Mongo::Support.validate_read_preference(@read)
|
528
463
|
end
|
529
464
|
|
465
|
+
@tag_sets = opts.fetch(:tag_sets, [])
|
466
|
+
@acceptable_latency = opts.fetch(:secondary_acceptable_latency_ms, 15)
|
467
|
+
|
530
468
|
# Replica set name
|
531
469
|
if opts[:rs_name]
|
532
470
|
warn ":rs_name option has been deprecated and will be removed in v2.0. " +
|
@@ -540,24 +478,6 @@ module Mongo
|
|
540
478
|
|
541
479
|
super opts
|
542
480
|
end
|
543
|
-
|
544
|
-
# Checkout a socket connected to a node with one of
|
545
|
-
# the provided tags. If no such node exists, raise
|
546
|
-
# an exception.
|
547
|
-
#
|
548
|
-
# NOTE: will be available in driver release v2.0.
|
549
|
-
def checkout_tagged(tags)
|
550
|
-
tags.each do |k, v|
|
551
|
-
pool = self.tag_map[{k.to_s => v}]
|
552
|
-
if pool
|
553
|
-
socket = pool.checkout
|
554
|
-
return socket
|
555
|
-
end
|
556
|
-
end
|
557
|
-
|
558
|
-
raise NodeWithTagsNotFound,
|
559
|
-
"Could not find a connection tagged with #{tags}."
|
560
|
-
end
|
561
481
|
|
562
482
|
def prune_managers
|
563
483
|
@old_managers.each do |manager|
|
@@ -574,8 +494,9 @@ module Mongo
|
|
574
494
|
def sync_refresh
|
575
495
|
if @refresh_mode == :sync &&
|
576
496
|
((Time.now - @last_refresh) > @refresh_interval)
|
497
|
+
|
577
498
|
@last_refresh = Time.now
|
578
|
-
|
499
|
+
|
579
500
|
if @refresh_mutex.try_lock
|
580
501
|
begin
|
581
502
|
refresh
|