mongo 1.6.4 → 1.7.0.rc0
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/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
|