mongo 1.3.1 → 1.4.0
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 +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
|