mongo 1.6.0 → 1.6.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +5 -8
- data/Rakefile +84 -2
- data/docs/HISTORY.md +8 -0
- data/docs/REPLICA_SETS.md +9 -1
- data/lib/mongo.rb +1 -0
- data/lib/mongo/connection.rb +32 -17
- data/lib/mongo/cursor.rb +1 -1
- data/lib/mongo/gridfs/grid.rb +1 -1
- data/lib/mongo/repl_set_connection.rb +174 -136
- data/lib/mongo/util/logging.rb +5 -2
- data/lib/mongo/util/node.rb +2 -2
- data/lib/mongo/util/pool.rb +80 -46
- data/lib/mongo/util/pool_manager.rb +8 -0
- data/lib/mongo/util/ssl_socket.rb +2 -1
- data/lib/mongo/util/tcp_socket.rb +6 -0
- data/lib/mongo/version.rb +1 -1
- data/mongo.gemspec +1 -1
- data/test/connection_test.rb +2 -2
- data/test/pool_test.rb +57 -0
- data/test/replica_sets/pooled_insert_test.rb +1 -1
- data/test/replica_sets/read_preference_test.rb +2 -2
- data/test/replica_sets/refresh_with_threads_test.rb +10 -2
- data/test/unit/connection_test.rb +31 -3
- data/test/unit/read_test.rb +5 -3
- metadata +10 -7
data/README.md
CHANGED
@@ -37,7 +37,7 @@ for much more:
|
|
37
37
|
|
38
38
|
### Ruby Versions
|
39
39
|
|
40
|
-
The driver works and is consistently tested on Ruby 1.8.
|
40
|
+
The driver works and is consistently tested on Ruby 1.8.7 and 1.9.3 as well as JRuby 1.6.6.
|
41
41
|
|
42
42
|
Note that if you're on 1.8.7, be sure that you're using a patchlevel >= 249. There
|
43
43
|
are some IO bugs in earlier versions.
|
@@ -198,7 +198,7 @@ Here is a sample primary key factory, taken from the tests:
|
|
198
198
|
|
199
199
|
class TestPKFactory
|
200
200
|
def create_pk(row)
|
201
|
-
row['_id'] ||=
|
201
|
+
row['_id'] ||= BSON::ObjectId.new
|
202
202
|
row
|
203
203
|
end
|
204
204
|
end
|
@@ -212,7 +212,7 @@ ActiveRecord-like framework for non-Rails apps) and the AR Mongo adapter code
|
|
212
212
|
def create_pk(row)
|
213
213
|
return row if row[:_id]
|
214
214
|
row.delete(:_id) # in case it exists but the value is nil
|
215
|
-
row['_id'] ||=
|
215
|
+
row['_id'] ||= BSON::ObjectId.new
|
216
216
|
row
|
217
217
|
end
|
218
218
|
end
|
@@ -283,12 +283,9 @@ To run any individual rake tasks with the C extension enabled, just pass C_EXT=t
|
|
283
283
|
|
284
284
|
$ rake test:unit C_EXT=true
|
285
285
|
|
286
|
-
If you want to test replica set, you can run the following
|
287
|
-
individually:
|
286
|
+
If you want to test replica set, you can run the following task:
|
288
287
|
|
289
|
-
$ rake test:
|
290
|
-
$ rake test:replica_set_insert
|
291
|
-
$ rake test:replica_set_query
|
288
|
+
$ rake test:rs
|
292
289
|
|
293
290
|
### Shoulda and Mocha
|
294
291
|
|
data/Rakefile
CHANGED
@@ -3,15 +3,20 @@ if RUBY_VERSION < '1.9.0'
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'rubygems/specification'
|
5
5
|
end
|
6
|
+
|
6
7
|
require 'fileutils'
|
7
8
|
require 'rake/testtask'
|
8
|
-
require 'rbconfig'
|
9
9
|
require 'rake'
|
10
|
+
|
11
|
+
begin
|
12
|
+
require 'git'
|
13
|
+
rescue LoadError
|
14
|
+
end
|
15
|
+
|
10
16
|
begin
|
11
17
|
require 'ci/reporter/rake/test_unit'
|
12
18
|
rescue LoadError
|
13
19
|
end
|
14
|
-
include Config
|
15
20
|
|
16
21
|
ENV['TEST_MODE'] = 'TRUE'
|
17
22
|
|
@@ -210,10 +215,12 @@ namespace :gem do
|
|
210
215
|
|
211
216
|
desc "Build all gems"
|
212
217
|
task :build_all do
|
218
|
+
`rm *.gem`
|
213
219
|
`gem build mongo.gemspec`
|
214
220
|
`gem build bson.gemspec`
|
215
221
|
`gem build bson.java.gemspec`
|
216
222
|
`gem build bson_ext.gemspec`
|
223
|
+
puts `ls *.gem`
|
217
224
|
end
|
218
225
|
|
219
226
|
end
|
@@ -228,6 +235,81 @@ namespace :ci do
|
|
228
235
|
end
|
229
236
|
end
|
230
237
|
|
238
|
+
# Deployment
|
239
|
+
VERSION_FILES = %w(lib/bson/version.rb lib/mongo/version.rb ext/cbson/version.h)
|
240
|
+
GEMSPECS = %w(bson.gemspec bson.java.gemspec bson_ext.gemspec mongo.gemspec)
|
241
|
+
|
242
|
+
def gem_list(version)
|
243
|
+
files = []
|
244
|
+
files << "bson-#{version}.gem"
|
245
|
+
files << "bson-#{version}-java.gem"
|
246
|
+
files << "bson_ext-#{version}.gem"
|
247
|
+
files << "mongo-#{version}.gem"
|
248
|
+
return files
|
249
|
+
end
|
250
|
+
|
251
|
+
def check_gem_list_existence(version)
|
252
|
+
gem_list(version).each do |filename|
|
253
|
+
if !File.exists?(filename)
|
254
|
+
raise "#{filename} does not exist!"
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
def check_version(version)
|
260
|
+
if !(version =~ /\d\.\d\.\d/)
|
261
|
+
raise "Must specify a valid version (e.g., x.y.z)"
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
def current_version
|
266
|
+
f = File.open("lib/mongo/version.rb")
|
267
|
+
str = f.read
|
268
|
+
str =~ /VERSION\s+=\s+([.\d"]+)$/
|
269
|
+
return $1
|
270
|
+
end
|
271
|
+
|
272
|
+
def change_version(new_version)
|
273
|
+
version = current_version
|
274
|
+
VERSION_FILES.each do |filename|
|
275
|
+
f = File.open(filename)
|
276
|
+
str = f.read
|
277
|
+
f.close
|
278
|
+
str.gsub!(version, "\"#{new_version}\"")
|
279
|
+
File.open(filename, 'w') do |f|
|
280
|
+
f.write(str)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
namespace :deploy do
|
286
|
+
task :version, [:version] do |t, args|
|
287
|
+
check_version(args[:version])
|
288
|
+
puts args[:version]
|
289
|
+
change_version(args[:version])
|
290
|
+
end
|
291
|
+
|
292
|
+
task :git_prepare, [:version] do |t, args|
|
293
|
+
g = Git.open(Dir.getwd())
|
294
|
+
version = args[:version]
|
295
|
+
check_version(version)
|
296
|
+
g.add(VERSION_FILES)
|
297
|
+
g.commit "RELEASE #{version}"
|
298
|
+
g.add_tag("#{version}")
|
299
|
+
end
|
300
|
+
|
301
|
+
task :git_push do
|
302
|
+
g = Git.open(Dir.getwd())
|
303
|
+
g.push
|
304
|
+
end
|
305
|
+
|
306
|
+
task :gems, [:version] do |t, args|
|
307
|
+
check_version(args[:version])
|
308
|
+
check_gem_list_existence(args[:version])
|
309
|
+
gem_list
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
231
313
|
task :default => :list
|
232
314
|
|
233
315
|
task :list do
|
data/docs/HISTORY.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# MongoDB Ruby Driver History
|
2
2
|
|
3
|
+
### 1.6.1
|
4
|
+
2012-03-07
|
5
|
+
|
6
|
+
* Added thread affinity to Mongo::Pool
|
7
|
+
* Added deploy tasks
|
8
|
+
* Added Travis CI support (Cyril Mougel)
|
9
|
+
* Logging warning message is only displayed for level :debug
|
10
|
+
|
3
11
|
### 1.6.0
|
4
12
|
2012-02-22
|
5
13
|
|
data/docs/REPLICA_SETS.md
CHANGED
@@ -38,8 +38,16 @@ The driver will essentially cycle through all known seed addresses until a node
|
|
38
38
|
You can now specify a refresh mode and refresh interval for a replica set connection. This will help to ensure that
|
39
39
|
changes to a replica set's configuration are quickly reflected on the driver side. In particular, if you change
|
40
40
|
the state of any secondary node, the automated refresh will ensure that this state is recorded on the client side.
|
41
|
+
|
42
|
+
There are two secenarios in which refresh is helpful and does not raise exceptions:
|
43
|
+
|
44
|
+
# You add a new secondary node to an existing replica set
|
45
|
+
# You remove an unused secondary from an existing replica set
|
46
|
+
|
47
|
+
If using MongoDB earlier than 2.0 any changes to replica set state will raise exceptions therefore refresh mode will not be useful.
|
48
|
+
|
41
49
|
If you add a secondary that responds to pings much faster than the existing nodes, then the new secondary will
|
42
|
-
be used for reads
|
50
|
+
be used for reads if :read_preference is :secondary or :secondary_only
|
43
51
|
|
44
52
|
Refresh mode is disabled by default.
|
45
53
|
|
data/lib/mongo.rb
CHANGED
data/lib/mongo/connection.rb
CHANGED
@@ -33,6 +33,8 @@ module Mongo
|
|
33
33
|
Thread.abort_on_exception = true
|
34
34
|
|
35
35
|
DEFAULT_PORT = 27017
|
36
|
+
GENERIC_OPTS = [:ssl, :auths, :pool_size, :pool_timeout, :timeout, :op_timeout, :connect_timeout, :safe, :logger, :connect]
|
37
|
+
CONNECTION_OPTS = [:slave_ok]
|
36
38
|
|
37
39
|
mongo_thread_local_accessor :connections
|
38
40
|
|
@@ -97,10 +99,18 @@ module Mongo
|
|
97
99
|
|
98
100
|
# Host and port of current master.
|
99
101
|
@host = @port = nil
|
102
|
+
|
103
|
+
# Default maximum BSON object size
|
104
|
+
@max_bson_size = Mongo::DEFAULT_MAX_BSON_SIZE
|
105
|
+
|
106
|
+
# Lock for request ids.
|
107
|
+
@id_lock = Mutex.new
|
108
|
+
|
109
|
+
# Connection pool for primay node
|
110
|
+
@primary = nil
|
111
|
+
@primary_pool = nil
|
100
112
|
|
101
|
-
|
102
|
-
@slave_ok = opts[:slave_ok]
|
103
|
-
|
113
|
+
check_opts(opts)
|
104
114
|
setup(opts)
|
105
115
|
end
|
106
116
|
|
@@ -498,32 +508,41 @@ module Mongo
|
|
498
508
|
|
499
509
|
# Check a socket back into its pool.
|
500
510
|
def checkin(socket)
|
501
|
-
if @primary_pool
|
502
|
-
|
511
|
+
if @primary_pool && socket
|
512
|
+
socket.pool.checkin(socket)
|
503
513
|
end
|
504
514
|
end
|
505
515
|
|
506
516
|
protected
|
507
517
|
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
518
|
+
def valid_opts
|
519
|
+
GENERIC_OPTS + CONNECTION_OPTS
|
520
|
+
end
|
521
|
+
|
522
|
+
def check_opts(opts)
|
523
|
+
bad_opts = opts.keys.reject { |opt| valid_opts.include?(opt) }
|
524
|
+
|
525
|
+
unless bad_opts.empty?
|
526
|
+
bad_opts.each {|opt| warn "#{opt} is not a valid option for #{self.class}"}
|
527
|
+
end
|
528
|
+
end
|
512
529
|
|
530
|
+
# Parse option hash
|
531
|
+
def setup(opts)
|
532
|
+
# slave_ok can be true only if one node is specified
|
533
|
+
@slave_ok = opts[:slave_ok]
|
534
|
+
|
513
535
|
# Determine whether to use SSL.
|
514
536
|
@ssl = opts.fetch(:ssl, false)
|
515
537
|
if @ssl
|
516
538
|
@socket_class = Mongo::SSLSocket
|
517
539
|
else
|
518
|
-
@socket_class = ::TCPSocket
|
540
|
+
@socket_class = Mongo::TCPSocket
|
519
541
|
end
|
520
542
|
|
521
543
|
# Authentication objects
|
522
544
|
@auths = opts.fetch(:auths, [])
|
523
545
|
|
524
|
-
# Lock for request ids.
|
525
|
-
@id_lock = Mutex.new
|
526
|
-
|
527
546
|
# Pool size and timeout.
|
528
547
|
@pool_size = opts[:pool_size] || 1
|
529
548
|
if opts[:timeout]
|
@@ -540,10 +559,6 @@ module Mongo
|
|
540
559
|
|
541
560
|
# Global safe option. This is false by default.
|
542
561
|
@safe = opts[:safe] || false
|
543
|
-
|
544
|
-
# Connection pool for primay node
|
545
|
-
@primary = nil
|
546
|
-
@primary_pool = nil
|
547
562
|
|
548
563
|
@logger = opts.fetch(:logger, nil)
|
549
564
|
|
data/lib/mongo/cursor.rb
CHANGED
@@ -470,7 +470,7 @@ module Mongo
|
|
470
470
|
Mongo::Constants::OP_QUERY, message, nil, sock, @command,
|
471
471
|
nil, @options & OP_QUERY_EXHAUST != 0)
|
472
472
|
rescue ConnectionFailure, OperationFailure, OperationTimeout => ex
|
473
|
-
force_checkin_socket(sock)
|
473
|
+
force_checkin_socket(sock) unless @socket
|
474
474
|
raise ex
|
475
475
|
end
|
476
476
|
checkin_socket(sock) unless @socket
|
data/lib/mongo/gridfs/grid.rb
CHANGED
@@ -68,7 +68,7 @@ module Mongo
|
|
68
68
|
# @return [BSON::ObjectId] the file's id.
|
69
69
|
def put(data, opts={})
|
70
70
|
opts = opts.dup
|
71
|
-
filename = opts
|
71
|
+
filename = opts.delete(:filename)
|
72
72
|
opts.merge!(default_grid_io_opts)
|
73
73
|
file = GridIO.new(@files, @chunks, filename, 'w', opts)
|
74
74
|
file.write(data)
|
@@ -21,6 +21,9 @@ 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, :require_primary,
|
25
|
+
:read_secondary, :rs_name, :name]
|
26
|
+
|
24
27
|
attr_reader :replica_set_name, :seeds, :refresh_interval, :refresh_mode,
|
25
28
|
:refresh_version
|
26
29
|
|
@@ -33,7 +36,7 @@ module Mongo
|
|
33
36
|
#
|
34
37
|
# @param [Array] seeds "host:port" strings
|
35
38
|
#
|
36
|
-
# @option opts [String] :
|
39
|
+
# @option opts [String] :name (nil) The name of the replica set to connect to. You
|
37
40
|
# can use this option to verify that you're connecting to the right replica set.
|
38
41
|
# @option opts [Boolean, Hash] :safe (false) Set the default safe-mode options
|
39
42
|
# propogated to DB objects instantiated off of this Connection. This
|
@@ -83,11 +86,11 @@ module Mongo
|
|
83
86
|
else
|
84
87
|
opts = {}
|
85
88
|
end
|
86
|
-
|
89
|
+
|
87
90
|
unless args.length > 0
|
88
91
|
raise MongoArgumentError, "A ReplSetConnection requires at least one seed node."
|
89
92
|
end
|
90
|
-
|
93
|
+
|
91
94
|
# This is temporary until support for the old format is dropped
|
92
95
|
@seeds = []
|
93
96
|
if args.first.last.is_a?(Integer)
|
@@ -106,51 +109,33 @@ module Mongo
|
|
106
109
|
@seeds.freeze
|
107
110
|
|
108
111
|
# Refresh
|
109
|
-
@refresh_mode = opts.fetch(:refresh_mode, false)
|
110
|
-
@refresh_interval = opts[:refresh_interval] || 90
|
111
112
|
@last_refresh = Time.now
|
112
113
|
@refresh_version = 0
|
113
114
|
|
114
115
|
# No connection manager by default.
|
115
116
|
@manager = nil
|
116
|
-
@
|
117
|
+
@old_managers = []
|
117
118
|
|
118
|
-
|
119
|
-
|
120
|
-
mode will be disabled."
|
121
|
-
elsif ![:sync, false].include?(@refresh_mode)
|
122
|
-
raise MongoArgumentError,
|
123
|
-
"Refresh mode must be either :sync or false."
|
124
|
-
end
|
125
|
-
|
126
|
-
# Are we allowing reads from secondaries?
|
127
|
-
if opts[:read_secondary]
|
128
|
-
warn ":read_secondary options has now been deprecated and will " +
|
129
|
-
"be removed in driver v2.0. Use the :read option instead."
|
130
|
-
@read_secondary = opts.fetch(:read_secondary, false)
|
131
|
-
@read = :secondary
|
132
|
-
else
|
133
|
-
@read = opts.fetch(:read, :primary)
|
134
|
-
Mongo::Support.validate_read_preference(@read)
|
135
|
-
end
|
119
|
+
# Lock for request ids.
|
120
|
+
@id_lock = Mutex.new
|
136
121
|
|
122
|
+
@pool_mutex = Mutex.new
|
137
123
|
@connected = false
|
138
124
|
|
139
|
-
|
140
|
-
|
141
|
-
warn ":rs_name option has been deprecated and will be removed in v2.0. " +
|
142
|
-
"Please use :name instead."
|
143
|
-
@replica_set_name = opts[:rs_name]
|
144
|
-
else
|
145
|
-
@replica_set_name = opts[:name]
|
146
|
-
end
|
125
|
+
@safe_mutex_lock = Mutex.new
|
126
|
+
@safe_mutexes = Hash.new {|hash, key| hash[key] = Mutex.new}
|
147
127
|
|
148
|
-
|
149
|
-
@
|
128
|
+
@connect_mutex = Mutex.new
|
129
|
+
@refresh_mutex = Mutex.new
|
150
130
|
|
131
|
+
check_opts(opts)
|
151
132
|
setup(opts)
|
152
133
|
end
|
153
134
|
|
135
|
+
def valid_opts
|
136
|
+
GENERIC_OPTS + REPL_SET_OPTS
|
137
|
+
end
|
138
|
+
|
154
139
|
def inspect
|
155
140
|
"<Mongo::ReplSetConnection:0x#{self.object_id.to_s(16)} @seeds=#{@seeds.inspect} " +
|
156
141
|
"@connected=#{@connected}>"
|
@@ -159,22 +144,26 @@ module Mongo
|
|
159
144
|
# Initiate a connection to the replica set.
|
160
145
|
def connect
|
161
146
|
log(:info, "Connecting...")
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
147
|
+
@connect_mutex.synchronize do
|
148
|
+
return if @connected
|
149
|
+
|
150
|
+
discovered_seeds = @manager ? @manager.seeds : []
|
151
|
+
@manager = PoolManager.new(self, discovered_seeds)
|
152
|
+
|
153
|
+
Thread.current[:manager] = @manager
|
154
|
+
|
155
|
+
@manager.connect
|
156
|
+
@refresh_version += 1
|
157
|
+
|
158
|
+
if @require_primary && @manager.primary.nil? #TODO: in v2.0, we'll let this be optional and do a lazy connect.
|
159
|
+
close
|
160
|
+
raise ConnectionFailure, "Failed to connect to primary node."
|
161
|
+
elsif @manager.read_pool.nil?
|
162
|
+
close
|
163
|
+
raise ConnectionFailure, "Failed to connect to any node."
|
164
|
+
else
|
165
|
+
@connected = true
|
166
|
+
end
|
178
167
|
end
|
179
168
|
end
|
180
169
|
|
@@ -211,19 +200,21 @@ module Mongo
|
|
211
200
|
def hard_refresh!
|
212
201
|
log(:info, "Initiating hard refresh...")
|
213
202
|
discovered_seeds = @manager ? @manager.seeds : []
|
214
|
-
|
215
|
-
|
203
|
+
new_manager = PoolManager.new(self, discovered_seeds | @seeds)
|
204
|
+
new_manager.connect
|
205
|
+
|
206
|
+
Thread.current[:manager] = new_manager
|
216
207
|
|
217
208
|
# TODO: make sure that connect has succeeded
|
218
|
-
|
219
|
-
@manager =
|
220
|
-
|
209
|
+
@old_managers << @manager
|
210
|
+
@manager = new_manager
|
211
|
+
|
221
212
|
@refresh_version += 1
|
222
213
|
return true
|
223
214
|
end
|
224
215
|
|
225
216
|
def connected?
|
226
|
-
@connected && (
|
217
|
+
@connected && (@manager.primary_pool || @manager.read_pool)
|
227
218
|
end
|
228
219
|
|
229
220
|
# @deprecated
|
@@ -236,14 +227,14 @@ module Mongo
|
|
236
227
|
#
|
237
228
|
# @return [String]
|
238
229
|
def host
|
239
|
-
|
230
|
+
@manager.primary_pool.host
|
240
231
|
end
|
241
232
|
|
242
233
|
# The replica set primary's port.
|
243
234
|
#
|
244
235
|
# @return [Integer]
|
245
236
|
def port
|
246
|
-
|
237
|
+
@manager.primary_pool.port
|
247
238
|
end
|
248
239
|
|
249
240
|
def nodes
|
@@ -257,7 +248,7 @@ module Mongo
|
|
257
248
|
#
|
258
249
|
# @return [Boolean]
|
259
250
|
def read_primary?
|
260
|
-
|
251
|
+
@manager.read_pool == @manager.primary_pool
|
261
252
|
end
|
262
253
|
alias :primary? :read_primary?
|
263
254
|
|
@@ -296,105 +287,84 @@ module Mongo
|
|
296
287
|
end
|
297
288
|
|
298
289
|
def authenticate_pools
|
299
|
-
|
300
|
-
|
290
|
+
primary_pool.authenticate_existing
|
291
|
+
secondary_pools.each do |pool|
|
301
292
|
pool.authenticate_existing
|
302
293
|
end
|
303
294
|
end
|
304
295
|
|
305
296
|
def logout_pools(db)
|
306
|
-
|
307
|
-
|
297
|
+
primary_pool.logout_existing(db)
|
298
|
+
secondary_pools.each do |pool|
|
308
299
|
pool.logout_existing(db)
|
309
300
|
end
|
310
301
|
end
|
311
302
|
|
312
|
-
#
|
313
|
-
#
|
314
|
-
|
315
|
-
def checkout_reader
|
303
|
+
# Generic socket checkout
|
304
|
+
# Takes a block that returns a socket from pool
|
305
|
+
def checkout(&block)
|
316
306
|
if connected?
|
317
307
|
sync_refresh
|
318
308
|
else
|
319
309
|
connect
|
320
310
|
end
|
311
|
+
|
321
312
|
begin
|
322
|
-
socket =
|
323
|
-
if !socket
|
324
|
-
connect
|
325
|
-
socket = get_socket_from_pool(self.primary_pool)
|
326
|
-
end
|
313
|
+
socket = block.call
|
327
314
|
rescue => ex
|
328
315
|
checkin(socket) if socket
|
329
316
|
raise ex
|
330
317
|
end
|
331
|
-
|
318
|
+
|
332
319
|
if socket
|
333
320
|
socket
|
334
321
|
else
|
335
|
-
|
322
|
+
@connected = false
|
323
|
+
raise ConnectionFailure.new("Could not checkout a socket.")
|
336
324
|
end
|
337
325
|
end
|
338
326
|
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
327
|
+
# Checkout a socket for reading (i.e., a secondary node).
|
328
|
+
# Note that @read_pool might point to the primary pool
|
329
|
+
# if no read pool has been defined.
|
330
|
+
def checkout_reader
|
331
|
+
checkout do
|
332
|
+
socket = get_socket_from_pool(:read)
|
333
|
+
if !socket
|
334
|
+
connect
|
335
|
+
socket = get_socket_from_pool(:primary)
|
336
|
+
end
|
337
|
+
socket
|
350
338
|
end
|
339
|
+
end
|
351
340
|
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
341
|
+
# Checkout a socket from a secondary
|
342
|
+
# For :read_preference => :secondary_only
|
343
|
+
def checkout_secondary
|
344
|
+
checkout do
|
345
|
+
get_socket_from_pool(:secondary)
|
356
346
|
end
|
357
347
|
end
|
358
348
|
|
359
349
|
# Checkout a socket for writing (i.e., a primary node).
|
360
350
|
def checkout_writer
|
361
|
-
|
362
|
-
|
363
|
-
else
|
364
|
-
connect
|
365
|
-
end
|
366
|
-
begin
|
367
|
-
socket = get_socket_from_pool(self.primary_pool)
|
368
|
-
|
369
|
-
if !socket
|
370
|
-
connect
|
371
|
-
socket = get_socket_from_pool(self.primary_pool)
|
372
|
-
end
|
373
|
-
rescue => ex
|
374
|
-
checkin(socket)
|
375
|
-
raise ex
|
376
|
-
end
|
377
|
-
|
378
|
-
if socket
|
379
|
-
socket
|
380
|
-
else
|
381
|
-
raise ConnectionFailure.new("Could not connect to primary node.")
|
351
|
+
checkout do
|
352
|
+
get_socket_from_pool(:primary)
|
382
353
|
end
|
383
354
|
end
|
384
355
|
|
385
356
|
# Checkin a socket used for reading.
|
386
357
|
def checkin_reader(socket)
|
387
|
-
if
|
388
|
-
|
389
|
-
close_socket(socket)
|
358
|
+
if socket
|
359
|
+
socket.pool.checkin(socket)
|
390
360
|
end
|
391
361
|
sync_refresh
|
392
362
|
end
|
393
363
|
|
394
364
|
# Checkin a socket used for writing.
|
395
365
|
def checkin_writer(socket)
|
396
|
-
if
|
397
|
-
|
366
|
+
if socket
|
367
|
+
socket.pool.checkin(socket)
|
398
368
|
end
|
399
369
|
sync_refresh
|
400
370
|
end
|
@@ -407,58 +377,74 @@ module Mongo
|
|
407
377
|
end
|
408
378
|
end
|
409
379
|
|
410
|
-
def get_socket_from_pool(
|
380
|
+
def get_socket_from_pool(pool_type)
|
381
|
+
if Thread.current[:manager] != @manager
|
382
|
+
Thread.current[:manager] = @manager
|
383
|
+
end
|
384
|
+
|
385
|
+
pool = case pool_type
|
386
|
+
when :primary
|
387
|
+
primary_pool
|
388
|
+
when :secondary
|
389
|
+
secondary_pool
|
390
|
+
when :read
|
391
|
+
read_pool
|
392
|
+
end
|
393
|
+
|
411
394
|
begin
|
412
395
|
if pool
|
413
|
-
|
414
|
-
socket
|
396
|
+
pool.checkout
|
415
397
|
end
|
416
398
|
rescue ConnectionFailure => ex
|
417
399
|
log(:info, "Failed to checkout from #{pool} with #{ex.class}; #{ex.message}")
|
418
400
|
return nil
|
419
401
|
end
|
420
402
|
end
|
403
|
+
|
404
|
+
def local_manager
|
405
|
+
Thread.current[:manager]
|
406
|
+
end
|
421
407
|
|
422
408
|
def arbiters
|
423
|
-
|
409
|
+
local_manager.arbiters.nil? ? [] : local_manager.arbiters
|
424
410
|
end
|
425
411
|
|
426
412
|
def primary
|
427
|
-
|
413
|
+
local_manager ? local_manager.primary : nil
|
428
414
|
end
|
429
415
|
|
430
416
|
# Note: might want to freeze these after connecting.
|
431
417
|
def secondaries
|
432
|
-
|
418
|
+
local_manager ? local_manager.secondaries : []
|
433
419
|
end
|
434
420
|
|
435
421
|
def hosts
|
436
|
-
|
422
|
+
local_manager ? local_manager.hosts : []
|
437
423
|
end
|
438
424
|
|
439
425
|
def primary_pool
|
440
|
-
|
426
|
+
local_manager ? local_manager.primary_pool : nil
|
441
427
|
end
|
442
428
|
|
443
429
|
def read_pool
|
444
|
-
|
430
|
+
local_manager ? local_manager.read_pool : nil
|
445
431
|
end
|
446
|
-
|
432
|
+
|
447
433
|
def secondary_pool
|
448
|
-
|
434
|
+
local_manager ? local_manager.secondary_pool : nil
|
449
435
|
end
|
450
436
|
|
451
437
|
def secondary_pools
|
452
|
-
|
438
|
+
local_manager ? local_manager.secondary_pools : []
|
453
439
|
end
|
454
440
|
|
455
441
|
def tag_map
|
456
|
-
|
442
|
+
local_manager ? local_manager.tag_map : {}
|
457
443
|
end
|
458
444
|
|
459
445
|
def max_bson_size
|
460
|
-
if
|
461
|
-
|
446
|
+
if local_manager && local_manager.max_bson_size
|
447
|
+
local_manager.max_bson_size
|
462
448
|
else
|
463
449
|
Mongo::DEFAULT_MAX_BSON_SIZE
|
464
450
|
end
|
@@ -466,13 +452,45 @@ module Mongo
|
|
466
452
|
|
467
453
|
private
|
468
454
|
|
469
|
-
#
|
455
|
+
# Parse option hash
|
470
456
|
def setup(opts)
|
471
|
-
|
472
|
-
@
|
473
|
-
|
457
|
+
# Require a primary node to connect?
|
458
|
+
@require_primary = opts.fetch(:require_primary, true)
|
459
|
+
|
460
|
+
# Refresh
|
461
|
+
@refresh_mode = opts.fetch(:refresh_mode, false)
|
462
|
+
@refresh_interval = opts[:refresh_interval] || 90
|
463
|
+
|
464
|
+
if @refresh_mode == :async
|
465
|
+
warn ":async refresh mode has been deprecated. Refresh
|
466
|
+
mode will be disabled."
|
467
|
+
elsif ![:sync, false].include?(@refresh_mode)
|
468
|
+
raise MongoArgumentError,
|
469
|
+
"Refresh mode must be either :sync or false."
|
470
|
+
end
|
471
|
+
|
472
|
+
# Are we allowing reads from secondaries?
|
473
|
+
if opts[:read_secondary]
|
474
|
+
warn ":read_secondary options has now been deprecated and will " +
|
475
|
+
"be removed in driver v2.0. Use the :read option instead."
|
476
|
+
@read_secondary = opts.fetch(:read_secondary, false)
|
477
|
+
@read = :secondary
|
478
|
+
else
|
479
|
+
@read = opts.fetch(:read, :primary)
|
480
|
+
Mongo::Support.validate_read_preference(@read)
|
481
|
+
end
|
482
|
+
|
483
|
+
# Replica set name
|
484
|
+
if opts[:rs_name]
|
485
|
+
warn ":rs_name option has been deprecated and will be removed in v2.0. " +
|
486
|
+
"Please use :name instead."
|
487
|
+
@replica_set_name = opts[:rs_name]
|
488
|
+
else
|
489
|
+
@replica_set_name = opts[:name]
|
490
|
+
end
|
491
|
+
|
474
492
|
opts[:connect_timeout] = opts[:connect_timeout] || 30
|
475
|
-
|
493
|
+
|
476
494
|
super opts
|
477
495
|
end
|
478
496
|
|
@@ -493,12 +511,32 @@ module Mongo
|
|
493
511
|
raise NodeWithTagsNotFound,
|
494
512
|
"Could not find a connection tagged with #{tags}."
|
495
513
|
end
|
514
|
+
|
515
|
+
def prune_managers
|
516
|
+
@old_managers.each do |manager|
|
517
|
+
if manager != @manager
|
518
|
+
if manager.closed?
|
519
|
+
@old_managers.delete(manager)
|
520
|
+
else
|
521
|
+
manager.close(:soft => true)
|
522
|
+
end
|
523
|
+
end
|
524
|
+
end
|
525
|
+
end
|
496
526
|
|
497
527
|
def sync_refresh
|
498
528
|
if @refresh_mode == :sync &&
|
499
529
|
((Time.now - @last_refresh) > @refresh_interval)
|
500
530
|
@last_refresh = Time.now
|
501
|
-
|
531
|
+
|
532
|
+
if @refresh_mutex.try_lock
|
533
|
+
begin
|
534
|
+
refresh
|
535
|
+
prune_managers
|
536
|
+
ensure
|
537
|
+
@refresh_mutex.unlock
|
538
|
+
end
|
539
|
+
end
|
502
540
|
end
|
503
541
|
end
|
504
542
|
end
|