mongo 1.3.1 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +9 -6
- data/Rakefile +3 -4
- data/docs/HISTORY.md +20 -2
- data/docs/READ_PREFERENCE.md +39 -0
- data/docs/RELEASES.md +1 -1
- data/docs/REPLICA_SETS.md +23 -2
- data/docs/TAILABLE_CURSORS.md +51 -0
- data/docs/TUTORIAL.md +4 -4
- data/docs/WRITE_CONCERN.md +5 -2
- data/lib/mongo.rb +7 -22
- data/lib/mongo/collection.rb +96 -29
- data/lib/mongo/connection.rb +107 -62
- data/lib/mongo/cursor.rb +136 -57
- data/lib/mongo/db.rb +26 -5
- data/lib/mongo/exceptions.rb +17 -1
- data/lib/mongo/gridfs/grid.rb +1 -1
- data/lib/mongo/repl_set_connection.rb +273 -156
- data/lib/mongo/util/logging.rb +42 -0
- data/lib/mongo/util/node.rb +183 -0
- data/lib/mongo/util/pool.rb +76 -13
- data/lib/mongo/util/pool_manager.rb +208 -0
- data/lib/mongo/util/ssl_socket.rb +38 -0
- data/lib/mongo/util/support.rb +9 -1
- data/lib/mongo/util/timeout.rb +42 -0
- data/lib/mongo/version.rb +3 -0
- data/mongo.gemspec +2 -2
- data/test/bson/binary_test.rb +1 -1
- data/test/bson/bson_string_test.rb +30 -0
- data/test/bson/bson_test.rb +6 -3
- data/test/bson/byte_buffer_test.rb +1 -1
- data/test/bson/hash_with_indifferent_access_test.rb +1 -1
- data/test/bson/json_test.rb +1 -1
- data/test/bson/object_id_test.rb +2 -18
- data/test/bson/ordered_hash_test.rb +38 -3
- data/test/bson/test_helper.rb +46 -0
- data/test/bson/timestamp_test.rb +32 -10
- data/test/collection_test.rb +89 -3
- data/test/connection_test.rb +35 -20
- data/test/cursor_test.rb +63 -2
- data/test/db_test.rb +12 -2
- data/test/pool_test.rb +21 -0
- data/test/replica_sets/connect_test.rb +26 -13
- data/test/replica_sets/connection_string_test.rb +1 -4
- data/test/replica_sets/count_test.rb +1 -0
- data/test/replica_sets/insert_test.rb +1 -0
- data/test/replica_sets/pooled_insert_test.rb +4 -1
- data/test/replica_sets/query_secondaries.rb +2 -1
- data/test/replica_sets/query_test.rb +2 -1
- data/test/replica_sets/read_preference_test.rb +43 -0
- data/test/replica_sets/refresh_test.rb +123 -0
- data/test/replica_sets/replication_ack_test.rb +9 -4
- data/test/replica_sets/rs_test_helper.rb +2 -2
- data/test/timeout_test.rb +14 -0
- data/test/tools/repl_set_manager.rb +134 -23
- data/test/unit/collection_test.rb +6 -8
- data/test/unit/connection_test.rb +4 -4
- data/test/unit/cursor_test.rb +23 -5
- data/test/unit/db_test.rb +2 -0
- data/test/unit/grid_test.rb +2 -0
- data/test/unit/node_test.rb +73 -0
- data/test/unit/pool_manager_test.rb +47 -0
- data/test/unit/read_test.rb +101 -0
- metadata +214 -138
- data/lib/mongo/test.rb +0 -20
- data/test/async/collection_test.rb +0 -224
- data/test/async/connection_test.rb +0 -24
- data/test/async/cursor_test.rb +0 -162
- data/test/async/worker_pool_test.rb +0 -99
- data/test/load/resque/load.rb +0 -21
- data/test/load/resque/processor.rb +0 -26
- data/test/load/unicorn/unicorn.rb +0 -29
- data/test/tools/load.rb +0 -58
- data/test/tools/sharding_manager.rb +0 -202
- data/test/tools/test.rb +0 -4
- data/test/unit/repl_set_connection_test.rb +0 -59
data/lib/mongo/db.rb
CHANGED
@@ -63,7 +63,7 @@ module Mongo
|
|
63
63
|
# @option opts [Boolean] :strict (False) If true, collections must exist to be accessed and must
|
64
64
|
# not exist to be created. See DB#collection and DB#create_collection.
|
65
65
|
#
|
66
|
-
# @option opts [Object, #create_pk(doc)] :pk (
|
66
|
+
# @option opts [Object, #create_pk(doc)] :pk (BSON::ObjectId) A primary key factory object,
|
67
67
|
# which should take a hash and return a hash which merges the original hash with any primary key
|
68
68
|
# fields the factory wishes to inject. (NOTE: if the object already has a primary key,
|
69
69
|
# the factory should not inject a new key).
|
@@ -82,6 +82,12 @@ module Mongo
|
|
82
82
|
@strict = opts[:strict]
|
83
83
|
@pk_factory = opts[:pk]
|
84
84
|
@safe = opts.fetch(:safe, @connection.safe)
|
85
|
+
if value = opts[:read]
|
86
|
+
Mongo::Support.validate_read_preference(value)
|
87
|
+
else
|
88
|
+
value = @connection.read_preference
|
89
|
+
end
|
90
|
+
@read_preference = value.is_a?(Hash) ? value.dup : value
|
85
91
|
@cache_time = opts[:cache_time] || 300 #5 minutes.
|
86
92
|
end
|
87
93
|
|
@@ -121,13 +127,14 @@ module Mongo
|
|
121
127
|
auth['user'] = username
|
122
128
|
auth['nonce'] = nonce
|
123
129
|
auth['key'] = Mongo::Support.auth_key(username, password, nonce)
|
124
|
-
if ok?(self.command(auth, :check_response => false, :socket => opts[:socket]))
|
130
|
+
if ok?(doc = self.command(auth, :check_response => false, :socket => opts[:socket]))
|
125
131
|
if save_auth
|
126
132
|
@connection.add_auth(@name, username, password)
|
127
133
|
end
|
128
134
|
true
|
129
135
|
else
|
130
|
-
|
136
|
+
message = "Failed to authenticate user '#{username}' on db '#{self.name}'"
|
137
|
+
raise Mongo::AuthenticationError.new(message, doc['code'], doc)
|
131
138
|
end
|
132
139
|
end
|
133
140
|
|
@@ -269,7 +276,8 @@ module Mongo
|
|
269
276
|
#
|
270
277
|
# @return [Mongo::Collection]
|
271
278
|
def create_collection(name, opts={})
|
272
|
-
|
279
|
+
name = name.to_s
|
280
|
+
if collection_names.include?(name)
|
273
281
|
if strict?
|
274
282
|
raise MongoDBError, "Collection #{name} already exists. " +
|
275
283
|
"Currently in strict mode."
|
@@ -503,7 +511,13 @@ module Mongo
|
|
503
511
|
if result.nil?
|
504
512
|
raise OperationFailure, "Database command '#{selector.keys.first}' failed: returned null."
|
505
513
|
elsif (check_response && !ok?(result))
|
506
|
-
|
514
|
+
message = "Database command '#{selector.keys.first}' failed: ("
|
515
|
+
message << result.map do |key, value|
|
516
|
+
"#{key}: '#{value}'"
|
517
|
+
end.join('; ')
|
518
|
+
message << ').'
|
519
|
+
code = result['code'] || result['assertionCode']
|
520
|
+
raise OperationFailure.new(message, code, result)
|
507
521
|
else
|
508
522
|
result
|
509
523
|
end
|
@@ -608,6 +622,13 @@ module Mongo
|
|
608
622
|
doc
|
609
623
|
end
|
610
624
|
|
625
|
+
# The value of the read preference. This will be
|
626
|
+
# either +:primary+, +:secondary+, or an object
|
627
|
+
# representing the tags to be read from.
|
628
|
+
def read_preference
|
629
|
+
@read_preference
|
630
|
+
end
|
631
|
+
|
611
632
|
private
|
612
633
|
|
613
634
|
def system_command_collection
|
data/lib/mongo/exceptions.rb
CHANGED
@@ -22,7 +22,20 @@ module Mongo
|
|
22
22
|
class MongoRubyError < StandardError; end
|
23
23
|
|
24
24
|
# Raised when MongoDB itself has returned an error.
|
25
|
-
class MongoDBError < RuntimeError
|
25
|
+
class MongoDBError < RuntimeError
|
26
|
+
|
27
|
+
# @return The entire failed command's response object, if available.
|
28
|
+
attr_reader :result
|
29
|
+
|
30
|
+
# @return The failed command's error code, if availab.e
|
31
|
+
attr_reader :error_code
|
32
|
+
|
33
|
+
def initialize(message=nil, error_code=nil, result=nil)
|
34
|
+
@error_code = error_code
|
35
|
+
@result = result
|
36
|
+
super(message)
|
37
|
+
end
|
38
|
+
end
|
26
39
|
|
27
40
|
# Raised when configuration options cause connections, queries, etc., to fail.
|
28
41
|
class ConfigurationError < MongoRubyError; end
|
@@ -48,6 +61,9 @@ module Mongo
|
|
48
61
|
# Raised on failures in connection to the database server.
|
49
62
|
class ConnectionTimeoutError < MongoRubyError; end
|
50
63
|
|
64
|
+
# Raised when no tags in a read preference maps to a given connection.
|
65
|
+
class NodeWithTagsNotFound < MongoRubyError; end
|
66
|
+
|
51
67
|
# Raised when a connection operation fails.
|
52
68
|
class ConnectionFailure < MongoDBError; end
|
53
69
|
|
data/lib/mongo/gridfs/grid.rb
CHANGED
@@ -63,7 +63,7 @@ module Mongo
|
|
63
63
|
# @option opts [Boolean] :safe (false) When safe mode is enabled, the chunks sent to the server
|
64
64
|
# will be validated using an md5 hash. If validation fails, an exception will be raised.
|
65
65
|
#
|
66
|
-
# @return [
|
66
|
+
# @return [BSON::ObjectId] the file's id.
|
67
67
|
def put(data, opts={})
|
68
68
|
opts = opts.dup
|
69
69
|
filename = opts[:filename]
|
@@ -16,11 +16,15 @@
|
|
16
16
|
# limitations under the License.
|
17
17
|
# ++
|
18
18
|
|
19
|
+
require 'sync'
|
20
|
+
|
19
21
|
module Mongo
|
20
22
|
|
21
23
|
# Instantiates and manages connections to a MongoDB replica set.
|
22
24
|
class ReplSetConnection < Connection
|
23
|
-
attr_reader :nodes, :secondaries, :arbiters, :
|
25
|
+
attr_reader :nodes, :secondaries, :arbiters, :secondary_pools,
|
26
|
+
:replica_set_name, :read_pool, :seeds, :tags_to_pools,
|
27
|
+
:refresh_interval, :refresh_mode
|
24
28
|
|
25
29
|
# Create a connection to a MongoDB replica set.
|
26
30
|
#
|
@@ -38,14 +42,30 @@ module Mongo
|
|
38
42
|
# propogated to DB objects instantiated off of this Connection. This
|
39
43
|
# default can be overridden upon instantiation of any DB by explicity setting a :safe value
|
40
44
|
# on initialization.
|
41
|
-
# @option options [
|
42
|
-
#
|
43
|
-
#
|
45
|
+
# @option options [:primary, :secondary] :read (:primary) The default read preference for Mongo::DB
|
46
|
+
# objects created from this connection object. If +:secondary+ is chosen, reads will be sent
|
47
|
+
# to one of the closest available secondary nodes. If a secondary node cannot be located, the
|
48
|
+
# read will be sent to the primary.
|
49
|
+
# @option options [Logger] :logger (nil) Logger instance to receive driver operation log.
|
44
50
|
# @option options [Integer] :pool_size (1) The maximum number of socket connections allowed per
|
45
51
|
# connection pool. Note: this setting is relevant only for multi-threaded applications.
|
46
|
-
# @option options [Float] :
|
52
|
+
# @option options [Float] :pool_timeout (5.0) When all of the connections a pool are checked out,
|
47
53
|
# this is the number of seconds to wait for a new connection to be released before throwing an exception.
|
48
54
|
# Note: this setting is relevant only for multi-threaded applications.
|
55
|
+
# @option opts [Float] :op_timeout (nil) The number of seconds to wait for a read operation to time out.
|
56
|
+
# Disabled by default.
|
57
|
+
# @option opts [Float] :connect_timeout (nil) The number of seconds to wait before timing out a
|
58
|
+
# connection attempt.
|
59
|
+
# @option opts [Boolean] :ssl (false) If true, create the connection to the server using SSL.
|
60
|
+
# @option opts [Boolean] :refresh_mode (:sync) Set this to :async to enable a background thread that
|
61
|
+
# periodically updates the state of the connection. If, for example, you initially connect while a secondary
|
62
|
+
# is down, this will reconnect to that secondary behind the scenes to
|
63
|
+
# prevent you from having to reconnect manually. If set to :sync, refresh will happen
|
64
|
+
# synchronously. If +false+, no automatic refresh will occur unless there's a connection failure.
|
65
|
+
# @option opts [Integer] :refresh_interval (90) If :refresh_mode is enabled, this is the number of seconds
|
66
|
+
# between calls to check the replica set's state.
|
67
|
+
# @option opts [Boolean] :require_primary (true) If true, require a primary node for the connection
|
68
|
+
# to succeed. Otherwise, connection will succeed as long as there's at least one secondary node.
|
49
69
|
#
|
50
70
|
# @example Connect to a replica set and provide two seed nodes. Note that the number of seed nodes does
|
51
71
|
# not have to be equal to the number of replica set members. The purpose of seed nodes is to permit
|
@@ -63,6 +83,8 @@ module Mongo
|
|
63
83
|
# @raise [ReplicaSetConnectionError] This is raised if a replica set name is specified and the
|
64
84
|
# driver fails to connect to a replica set with that name.
|
65
85
|
def initialize(*args)
|
86
|
+
extend Sync_m
|
87
|
+
|
66
88
|
if args.last.is_a?(Hash)
|
67
89
|
opts = args.pop
|
68
90
|
else
|
@@ -70,70 +92,146 @@ module Mongo
|
|
70
92
|
end
|
71
93
|
|
72
94
|
unless args.length > 0
|
73
|
-
raise MongoArgumentError, "A ReplSetConnection requires at least one node."
|
95
|
+
raise MongoArgumentError, "A ReplSetConnection requires at least one seed node."
|
74
96
|
end
|
75
97
|
|
76
|
-
#
|
77
|
-
@
|
98
|
+
# The list of seed nodes
|
99
|
+
@seeds = args
|
78
100
|
|
79
|
-
#
|
80
|
-
@
|
101
|
+
# TODO: get rid of this
|
102
|
+
@nodes = @seeds.dup
|
81
103
|
|
82
|
-
#
|
83
|
-
@
|
84
|
-
|
104
|
+
# The members of the replica set, stored as instances of Mongo::Node.
|
105
|
+
@members = []
|
106
|
+
|
107
|
+
# Connection pool for primary node
|
108
|
+
@primary = nil
|
109
|
+
@primary_pool = nil
|
85
110
|
|
86
111
|
# Connection pools for each secondary node
|
112
|
+
@secondaries = []
|
87
113
|
@secondary_pools = []
|
114
|
+
|
115
|
+
# The secondary pool to which we'll be sending reads.
|
116
|
+
# This may be identical to the primary pool.
|
88
117
|
@read_pool = nil
|
89
118
|
|
119
|
+
# A list of arbiter addresses (for client information only)
|
120
|
+
@arbiters = []
|
121
|
+
|
122
|
+
# Refresh
|
123
|
+
@refresh_mode = opts.fetch(:refresh_mode, :sync)
|
124
|
+
@refresh_interval = opts[:refresh_interval] || 90
|
125
|
+
|
126
|
+
if ![:sync, :async, false].include?(@refresh_mode)
|
127
|
+
raise MongoArgumentError,
|
128
|
+
"Refresh mode must be one of :sync, :async, or false."
|
129
|
+
end
|
130
|
+
|
90
131
|
# Are we allowing reads from secondaries?
|
91
|
-
|
92
|
-
|
132
|
+
if opts[:read_secondary]
|
133
|
+
warn ":read_secondary options has now been deprecated and will " +
|
134
|
+
"be removed in driver v2.0. Use the :read option instead."
|
135
|
+
@read_secondary = opts.fetch(:read_secondary, false)
|
136
|
+
@read = :secondary
|
137
|
+
else
|
138
|
+
@read = opts.fetch(:read, :primary)
|
139
|
+
Mongo::Support.validate_read_preference(@read)
|
140
|
+
end
|
141
|
+
|
142
|
+
@connected = false
|
143
|
+
|
144
|
+
# Store the refresher thread
|
145
|
+
@refresh_thread = nil
|
146
|
+
|
147
|
+
# Maps
|
148
|
+
@sockets_to_pools = {}
|
149
|
+
@tags_to_pools = {}
|
150
|
+
|
151
|
+
# Replica set name
|
152
|
+
if opts[:rs_name]
|
153
|
+
warn ":rs_name option has been deprecated and will be removed in v2.0. " +
|
154
|
+
"Please use :name instead."
|
155
|
+
@replica_set_name = opts[:rs_name]
|
156
|
+
else
|
157
|
+
@replica_set_name = opts[:name]
|
158
|
+
end
|
159
|
+
|
160
|
+
# Require a primary node to connect?
|
161
|
+
@require_primary = opts.fetch(:require_primary, true)
|
93
162
|
|
94
163
|
setup(opts)
|
95
164
|
end
|
96
165
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
#
|
103
|
-
# @raise [ConnectionFailure] if unable to connect to any host or port.
|
166
|
+
def inspect
|
167
|
+
"<Mongo::ReplSetConnection:0x#{self.object_id.to_s(16)} @seeds=#{@seeds} " +
|
168
|
+
"@connected=#{@connected}>"
|
169
|
+
end
|
170
|
+
|
171
|
+
# Initiate a connection to the replica set.
|
104
172
|
def connect
|
105
|
-
|
106
|
-
|
173
|
+
log(:info, "Connecting...")
|
174
|
+
sync_synchronize(:EX) do
|
175
|
+
return if @connected
|
176
|
+
manager = PoolManager.new(self, @seeds)
|
177
|
+
manager.connect
|
107
178
|
|
108
|
-
|
109
|
-
|
110
|
-
config = check_is_master(node)
|
179
|
+
update_config(manager)
|
180
|
+
initiate_refresh_mode
|
111
181
|
|
112
|
-
if
|
113
|
-
|
182
|
+
if @require_primary && @primary.nil? #TODO: in v2.0, we'll let this be optional and do a lazy connect.
|
183
|
+
raise ConnectionFailure, "Failed to connect to primary node."
|
184
|
+
elsif !@read_pool
|
185
|
+
raise ConnectionFailure, "Failed to connect to any node."
|
114
186
|
else
|
115
|
-
|
187
|
+
@connected = true
|
116
188
|
end
|
117
189
|
end
|
190
|
+
end
|
118
191
|
|
119
|
-
|
192
|
+
# Note: this method must be called from within
|
193
|
+
# an exclusive lock.
|
194
|
+
def update_config(manager)
|
195
|
+
@arbiters = manager.arbiters.nil? ? [] : manager.arbiters.dup
|
196
|
+
@primary = manager.primary.nil? ? nil : manager.primary.dup
|
197
|
+
@secondaries = manager.secondaries.dup
|
198
|
+
@hosts = manager.hosts.dup
|
199
|
+
|
200
|
+
@primary_pool = manager.primary_pool
|
201
|
+
@read_pool = manager.read_pool
|
202
|
+
@secondary_pools = manager.secondary_pools
|
203
|
+
@tags_to_pools = manager.tags_to_pools
|
204
|
+
@seeds = manager.seeds
|
205
|
+
@manager = manager
|
206
|
+
@nodes = manager.nodes
|
207
|
+
@max_bson_size = manager.max_bson_size
|
208
|
+
end
|
120
209
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
210
|
+
# Refresh the current replica set configuration.
|
211
|
+
def refresh(opts={})
|
212
|
+
return false if !connected?
|
213
|
+
|
214
|
+
# Return if another thread is already in the process of refreshing.
|
215
|
+
return if sync_exclusive?
|
216
|
+
|
217
|
+
sync_synchronize(:EX) do
|
218
|
+
log(:info, "Refreshing...")
|
219
|
+
@background_manager ||= PoolManager.new(self, @seeds)
|
220
|
+
@background_manager.connect
|
221
|
+
update_config(@background_manager)
|
131
222
|
end
|
223
|
+
|
224
|
+
return true
|
132
225
|
end
|
133
|
-
alias :reconnect :connect
|
134
226
|
|
227
|
+
def connected?
|
228
|
+
!@primary_pool.nil? || !@read_pool.nil?
|
229
|
+
end
|
230
|
+
|
231
|
+
# @deprecated
|
135
232
|
def connecting?
|
136
|
-
|
233
|
+
warn "ReplSetConnection#connecting? is deprecated and will be removed in v2.0."
|
234
|
+
false
|
137
235
|
end
|
138
236
|
|
139
237
|
# The replica set primary's host name.
|
@@ -150,27 +248,59 @@ module Mongo
|
|
150
248
|
super
|
151
249
|
end
|
152
250
|
|
251
|
+
def nodes
|
252
|
+
warn "ReplSetConnection#nodes is DEPRECATED and will be removed in v2.0. " +
|
253
|
+
"Please use ReplSetConnection#seeds instead."
|
254
|
+
@seeds
|
255
|
+
end
|
256
|
+
|
153
257
|
# Determine whether we're reading from a primary node. If false,
|
154
258
|
# this connection connects to a secondary node and @read_secondaries is true.
|
155
259
|
#
|
156
260
|
# @return [Boolean]
|
157
261
|
def read_primary?
|
158
|
-
|
262
|
+
sync_synchronize(:SH) do
|
263
|
+
@read_pool == @primary_pool
|
264
|
+
end
|
159
265
|
end
|
160
266
|
alias :primary? :read_primary?
|
161
267
|
|
268
|
+
def read_preference
|
269
|
+
@read
|
270
|
+
end
|
271
|
+
|
162
272
|
# Close the connection to the database.
|
163
273
|
def close
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
274
|
+
sync_synchronize(:EX) do
|
275
|
+
@connected = false
|
276
|
+
super
|
277
|
+
|
278
|
+
if @refresh_thread
|
279
|
+
@refresh_thread.kill
|
280
|
+
@refresh_thread = nil
|
281
|
+
end
|
282
|
+
|
283
|
+
if @nodes
|
284
|
+
@nodes.each do |member|
|
285
|
+
member.close
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
@nodes = []
|
290
|
+
@read_pool = nil
|
291
|
+
|
292
|
+
if @secondary_pools
|
293
|
+
@secondary_pools.each do |pool|
|
294
|
+
pool.close
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
@secondaries = []
|
299
|
+
@secondary_pools = []
|
300
|
+
@arbiters = []
|
301
|
+
@tags_to_pools.clear
|
302
|
+
@sockets_to_pools.clear
|
168
303
|
end
|
169
|
-
@secondaries = []
|
170
|
-
@secondary_pools = []
|
171
|
-
@arbiters = []
|
172
|
-
@nodes_tried = []
|
173
|
-
@nodes_to_try = []
|
174
304
|
end
|
175
305
|
|
176
306
|
# If a ConnectionFailure is raised, this method will be called
|
@@ -178,15 +308,19 @@ module Mongo
|
|
178
308
|
# @deprecated
|
179
309
|
def reset_connection
|
180
310
|
close
|
181
|
-
warn "ReplSetConnection#reset_connection is now deprecated. " +
|
311
|
+
warn "ReplSetConnection#reset_connection is now deprecated and will be removed in v2.0. " +
|
182
312
|
"Use ReplSetConnection#close instead."
|
183
313
|
end
|
184
314
|
|
185
|
-
#
|
315
|
+
# Returns +true+ if it's okay to read from a secondary node.
|
316
|
+
# Since this is a replica set, this must always be true.
|
186
317
|
#
|
187
|
-
#
|
318
|
+
# This method exist primarily so that Cursor objects will
|
319
|
+
# generate query messages with a slaveOkay value of +true+.
|
320
|
+
#
|
321
|
+
# @return [Boolean] +true+
|
188
322
|
def slave_ok?
|
189
|
-
|
323
|
+
true
|
190
324
|
end
|
191
325
|
|
192
326
|
def authenticate_pools
|
@@ -205,138 +339,121 @@ module Mongo
|
|
205
339
|
|
206
340
|
private
|
207
341
|
|
208
|
-
def
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
check_set_name(config, socket)
|
217
|
-
rescue OperationFailure, SocketError, SystemCallError, IOError => ex
|
218
|
-
# It's necessary to rescue here. The #connect method will keep trying
|
219
|
-
# until it has no more nodes to try and raise a ConnectionFailure if
|
220
|
-
# it can't connect to a primary.
|
221
|
-
ensure
|
222
|
-
socket.close if socket
|
223
|
-
@nodes_tried << node
|
224
|
-
|
225
|
-
if config
|
226
|
-
nodes = []
|
227
|
-
nodes += config['hosts'] if config['hosts']
|
228
|
-
nodes += config['arbiters'] if config['arbiters']
|
229
|
-
nodes += config['passives'] if config['passives']
|
230
|
-
update_node_list(nodes)
|
231
|
-
|
232
|
-
if config['msg'] && @logger
|
233
|
-
@logger.warn("MONGODB #{config['msg']}")
|
342
|
+
def initiate_refresh_mode
|
343
|
+
if @refresh_mode == :async
|
344
|
+
return if @refresh_thread && @refresh_thread.alive?
|
345
|
+
@refresh_thread = Thread.new do
|
346
|
+
while true do
|
347
|
+
sleep(@refresh_interval)
|
348
|
+
refresh
|
234
349
|
end
|
235
350
|
end
|
236
351
|
end
|
237
352
|
|
238
|
-
|
239
|
-
end
|
240
|
-
|
241
|
-
# Primary, when connecting to a replica can, can only be a true primary node.
|
242
|
-
# (And not a slave, which is possible when connecting with the standard
|
243
|
-
# Connection class.
|
244
|
-
def is_primary?(config)
|
245
|
-
config && (config['ismaster'] == 1 || config['ismaster'] == true)
|
353
|
+
@last_refresh = Time.now
|
246
354
|
end
|
247
355
|
|
248
|
-
#
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
356
|
+
# Checkout a socket for reading (i.e., a secondary node).
|
357
|
+
# Note that @read_pool might point to the primary pool
|
358
|
+
# if no read pool has been defined.
|
359
|
+
def checkout_reader
|
360
|
+
connect unless connected?
|
361
|
+
socket = get_socket_from_pool(@read_pool)
|
254
362
|
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
config = self['admin'].command({:replSetGetStatus => 1},
|
259
|
-
:socket => socket, :check_response => false)
|
260
|
-
|
261
|
-
if !Mongo::Support.ok?(config)
|
262
|
-
raise ReplicaSetConnectionError, config['errmsg']
|
263
|
-
elsif config['set'] != @replica_set
|
264
|
-
raise ReplicaSetConnectionError,
|
265
|
-
"Attempting to connect to replica set '#{config['set']}' but expected '#{@replica_set}'"
|
266
|
-
end
|
363
|
+
if !socket
|
364
|
+
refresh
|
365
|
+
socket = get_socket_from_pool(@primary_pool)
|
267
366
|
end
|
268
|
-
end
|
269
367
|
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
if config['secondary']
|
275
|
-
host, port = *node
|
276
|
-
@secondaries << node unless @secondaries.include?(node)
|
277
|
-
@secondary_pools << Pool.new(self, host, port, :size => @pool_size, :timeout => @timeout)
|
278
|
-
elsif config['arbiterOnly']
|
279
|
-
@arbiters << node unless @arbiters.include?(node)
|
280
|
-
end
|
368
|
+
if socket
|
369
|
+
socket
|
370
|
+
else
|
371
|
+
raise ConnectionFailure.new("Could not connect to a node for reading.")
|
281
372
|
end
|
282
373
|
end
|
283
374
|
|
284
|
-
#
|
285
|
-
#
|
286
|
-
#
|
375
|
+
# Checkout a socket connected to a node with one of
|
376
|
+
# the provided tags. If no such node exists, raise
|
377
|
+
# an exception.
|
287
378
|
#
|
288
|
-
#
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
379
|
+
# NOTE: will be available in driver release v2.0.
|
380
|
+
def checkout_tagged(tags)
|
381
|
+
sync_synchronize(:SH) do
|
382
|
+
tags.each do |k, v|
|
383
|
+
pool = @tags_to_pools[{k.to_s => v}]
|
384
|
+
if pool
|
385
|
+
socket = pool.checkout
|
386
|
+
@sockets_to_pools[socket] = pool
|
387
|
+
return socket
|
388
|
+
end
|
297
389
|
end
|
298
|
-
|
299
|
-
host, port = host.split(':')
|
300
|
-
[host, port ? port.to_i : Connection::DEFAULT_PORT]
|
301
390
|
end
|
302
391
|
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
@nodes_to_try = new_nodes - @nodes_tried
|
392
|
+
raise NodeWithTagsNotFound,
|
393
|
+
"Could not find a connection tagged with #{tags}."
|
307
394
|
end
|
308
395
|
|
309
|
-
# Checkout a socket for
|
310
|
-
def
|
396
|
+
# Checkout a socket for writing (i.e., a primary node).
|
397
|
+
def checkout_writer
|
311
398
|
connect unless connected?
|
399
|
+
socket = get_socket_from_pool(@primary_pool)
|
400
|
+
|
401
|
+
if !socket
|
402
|
+
refresh
|
403
|
+
socket = get_socket_from_pool(@primary_pool)
|
404
|
+
end
|
312
405
|
|
313
|
-
if
|
314
|
-
|
406
|
+
if socket
|
407
|
+
socket
|
315
408
|
else
|
316
|
-
|
409
|
+
raise ConnectionFailure.new("Could not connect to primary node.")
|
317
410
|
end
|
318
411
|
end
|
319
412
|
|
320
|
-
|
321
|
-
|
322
|
-
|
413
|
+
def get_socket_from_pool(pool)
|
414
|
+
begin
|
415
|
+
sync_synchronize(:SH) do
|
416
|
+
if pool
|
417
|
+
socket = pool.checkout
|
418
|
+
@sockets_to_pools[socket] = pool
|
419
|
+
socket
|
420
|
+
end
|
421
|
+
end
|
323
422
|
|
324
|
-
|
423
|
+
rescue ConnectionFailure => ex
|
424
|
+
log(:info, "Failed to checkout from #{pool} with #{ex.class}; #{ex.message}")
|
425
|
+
return nil
|
426
|
+
end
|
325
427
|
end
|
326
428
|
|
327
429
|
# Checkin a socket used for reading.
|
328
430
|
def checkin_reader(socket)
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
checkin_writer(socket)
|
333
|
-
end
|
431
|
+
warn "ReplSetConnection#checkin_writer is deprecated and will be removed " +
|
432
|
+
"in driver v2.0. Use ReplSetConnection#checkin instead."
|
433
|
+
checkin(socket)
|
334
434
|
end
|
335
435
|
|
336
436
|
# Checkin a socket used for writing.
|
337
437
|
def checkin_writer(socket)
|
338
|
-
|
339
|
-
|
438
|
+
warn "ReplSetConnection#checkin_writer is deprecated and will be removed " +
|
439
|
+
"in driver v2.0. Use ReplSetConnection#checkin instead."
|
440
|
+
checkin(socket)
|
441
|
+
end
|
442
|
+
|
443
|
+
def checkin(socket)
|
444
|
+
sync_synchronize(:SH) do
|
445
|
+
if pool = @sockets_to_pools[socket]
|
446
|
+
pool.checkin(socket)
|
447
|
+
elsif socket
|
448
|
+
socket.close
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
# Refresh synchronously every @refresh_interval seconds
|
453
|
+
# if synchronous refresh mode is enabled.
|
454
|
+
if @refresh_mode == :sync &&
|
455
|
+
((Time.now - @last_refresh) > @refresh_interval)
|
456
|
+
refresh
|
340
457
|
end
|
341
458
|
end
|
342
459
|
end
|