mongo 1.5.1 → 1.5.2

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -1,11 +1,16 @@
1
1
  # -*- mode: ruby; -*-
2
- require 'rubygems'
3
- require 'rubygems/specification'
2
+ if RUBY_VERSION < '1.9.0'
3
+ require 'rubygems'
4
+ require 'rubygems/specification'
5
+ end
4
6
  require 'fileutils'
5
- require 'rake'
6
7
  require 'rake/testtask'
7
- require 'rake/gempackagetask'
8
8
  require 'rbconfig'
9
+ require 'rake'
10
+ begin
11
+ require 'ci/reporter/rake/test_unit'
12
+ rescue LoadError
13
+ end
9
14
  include Config
10
15
  ENV['TEST_MODE'] = 'TRUE'
11
16
 
@@ -154,11 +159,11 @@ namespace :bamboo do
154
159
  end
155
160
 
156
161
  namespace :test do
157
- task :ruby => [:ci_reporter, "ci:setup:testunit"] do
162
+ task :ruby do
158
163
  Rake::Task['test:ruby'].invoke
159
164
  end
160
165
 
161
- task :c => [:ci_reporter, "ci:setup:testunit"] do
166
+ task :c do
162
167
  Rake::Task['gem:install_extensions'].invoke
163
168
  Rake::Task['test:c'].invoke
164
169
  end
@@ -1,5 +1,12 @@
1
1
  # MongoDB Ruby Driver History
2
2
 
3
+ ### 1.5.2
4
+ 2011-12-13
5
+
6
+ * Lots of fixes for replica set connection edge cases.
7
+ * Set default op_timeout and connect_timeout to 30 seconds.
8
+ * Support GeoHaystack indexing.
9
+
3
10
  ### 1.5.1
4
11
  2011-11-29
5
12
 
@@ -16,14 +16,13 @@
16
16
  # limitations under the License.
17
17
  # ++
18
18
 
19
- $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
20
-
21
19
  require 'mongo/version'
22
20
 
23
21
  module Mongo
24
- ASCENDING = 1
25
- DESCENDING = -1
26
- GEO2D = '2d'
22
+ ASCENDING = 1
23
+ DESCENDING = -1
24
+ GEO2D = '2d'
25
+ GEOHAYSTACK = 'geoHaystack'
27
26
 
28
27
  DEFAULT_MAX_BSON_SIZE = 4 * 1024 * 1024
29
28
 
@@ -888,7 +888,7 @@ module Mongo
888
888
  field_spec[spec.to_s] = 1
889
889
  elsif spec.is_a?(Array) && spec.all? {|field| field.is_a?(Array) }
890
890
  spec.each do |f|
891
- if [Mongo::ASCENDING, Mongo::DESCENDING, Mongo::GEO2D].include?(f[1])
891
+ if [Mongo::ASCENDING, Mongo::DESCENDING, Mongo::GEO2D, Mongo::GEOHAYSTACK].include?(f[1])
892
892
  field_spec[f[0].to_s] = f[1]
893
893
  else
894
894
  raise MongoArgumentError, "Invalid index field #{f[1].inspect}; " +
@@ -458,12 +458,13 @@ module Mongo
458
458
  @primary_pool
459
459
  end
460
460
 
461
- # The value of the read preference. Because
462
- # this is a single-node connection, the value
463
- # is +:primary+, and the connection will read
464
- # from whichever type of node it's connected to.
461
+ # The value of the read preference.
465
462
  def read_preference
466
- :primary
463
+ if slave_ok?
464
+ :secondary
465
+ else
466
+ :primary
467
+ end
467
468
  end
468
469
 
469
470
  # Close the connection to the database.
@@ -614,7 +615,13 @@ module Mongo
614
615
  socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
615
616
  end
616
617
 
617
- config = self['admin'].command({:ismaster => 1}, :socket => socket)
618
+ if @connect_timeout
619
+ Mongo::TimeoutHandler.timeout(@connect_timeout, OperationTimeout) do
620
+ config = self['admin'].command({:ismaster => 1}, :socket => socket)
621
+ end
622
+ else
623
+ config = self['admin'].command({:ismaster => 1}, :socket => socket)
624
+ end
618
625
  rescue OperationFailure, SocketError, SystemCallError, IOError => ex
619
626
  close
620
627
  ensure
@@ -86,7 +86,7 @@ module Mongo
86
86
  if(!@timeout)
87
87
  add_option(OP_QUERY_NO_CURSOR_TIMEOUT)
88
88
  end
89
- if(@connection.slave_ok?)
89
+ if(@read_preference != :primary)
90
90
  add_option(OP_QUERY_SLAVE_OK)
91
91
  end
92
92
  if(@tailable)
@@ -136,7 +136,7 @@ module Mongo
136
136
  # If the server has stopped being the master (e.g., it's one of a
137
137
  # pair but it has died or something like that) then we close that
138
138
  # connection. The next request will re-open on master server.
139
- if err == "not master"
139
+ if err.include?("not master")
140
140
  @connection.close
141
141
  raise ConnectionFailure.new(err, doc['code'], doc)
142
142
  end
@@ -463,9 +463,8 @@ module Mongo
463
463
 
464
464
  def send_initial_query
465
465
  message = construct_query_message
466
- payload = instrument_payload if @logger
467
466
  sock = @socket || checkout_socket_from_connection
468
- instrument(:find, payload) do
467
+ instrument(:find, instrument_payload) do
469
468
  begin
470
469
  results, @n_received, @cursor_id = @connection.receive_message(
471
470
  Mongo::Constants::OP_QUERY, message, nil, sock, @command,
@@ -518,13 +517,21 @@ module Mongo
518
517
  end
519
518
 
520
519
  def checkout_socket_from_connection
521
- @checkin_connection = true
522
- if @command || @read_preference == :primary
523
- @connection.checkout_writer
524
- else
525
- @read_pool = @connection.read_pool
526
- @connection.checkout_reader
520
+ socket = nil
521
+ begin
522
+ @checkin_connection = true
523
+ if @command || @read_preference == :primary
524
+ socket = @connection.checkout_writer
525
+ else
526
+ @read_pool = @connection.read_pool
527
+ socket = @connection.checkout_reader
528
+ end
529
+ rescue SystemStackError, NoMemoryError, SystemCallError => ex
530
+ @connection.close
531
+ raise ex
527
532
  end
533
+
534
+ socket
528
535
  end
529
536
 
530
537
  def checkout_socket_for_op_get_more
@@ -540,9 +547,15 @@ module Mongo
540
547
  pool.host == @read_pool.host && pool.port == @read_pool.port
541
548
  end
542
549
  if new_pool
543
- @read_pool = new_pool
544
- sock = new_pool.checkout
545
- @checkin_read_pool = true
550
+ sock = nil
551
+ begin
552
+ @read_pool = new_pool
553
+ sock = new_pool.checkout
554
+ @checkin_read_pool = true
555
+ rescue SystemStackError, NoMemoryError, SystemCallError => ex
556
+ @connection.close
557
+ raise ex
558
+ end
546
559
  return sock
547
560
  else
548
561
  raise Mongo::OperationFailure, "Failure to continue iterating " +
@@ -28,19 +28,25 @@ module Mongo
28
28
  add_message_headers(message, operation)
29
29
  packed_message = message.to_s
30
30
 
31
- if connection == :writer
32
- sock = checkout_writer
33
- else
34
- sock = checkout_reader
35
- end
36
-
31
+ sock = nil
37
32
  begin
38
- send_message_on_socket(packed_message, sock)
39
- ensure
40
33
  if connection == :writer
41
- checkin_writer(sock)
34
+ sock = checkout_writer
42
35
  else
43
- checkin_reader(sock)
36
+ sock = checkout_reader
37
+ end
38
+
39
+ send_message_on_socket(packed_message, sock)
40
+ rescue SystemStackError, NoMemoryError, SystemCallError => ex
41
+ close
42
+ raise ex
43
+ ensure
44
+ if sock
45
+ if connection == :writer
46
+ checkin_writer(sock)
47
+ else
48
+ checkin_reader(sock)
49
+ end
44
50
  end
45
51
  end
46
52
  end
@@ -66,14 +72,18 @@ module Mongo
66
72
  last_error_id = add_message_headers(last_error_message, Mongo::Constants::OP_QUERY)
67
73
 
68
74
  packed_message = message.append!(last_error_message).to_s
69
- sock = checkout_writer
75
+ sock = nil
70
76
  begin
77
+ sock = checkout_writer
71
78
  send_message_on_socket(packed_message, sock)
72
79
  docs, num_received, cursor_id = receive(sock, last_error_id)
73
80
  checkin_writer(sock)
74
81
  rescue ConnectionFailure, OperationFailure, OperationTimeout => ex
75
82
  checkin_writer(sock)
76
83
  raise ex
84
+ rescue SystemStackError, NoMemoryError, SystemCallError => ex
85
+ close
86
+ raise ex
77
87
  end
78
88
 
79
89
  if num_received == 1 && (error = docs[0]['err'] || docs[0]['errmsg'])
@@ -103,24 +113,29 @@ module Mongo
103
113
  read=:primary, exhaust=false)
104
114
  request_id = add_message_headers(message, operation)
105
115
  packed_message = message.to_s
106
- if socket
107
- sock = socket
108
- should_checkin = false
109
- else
110
- if command || read == :primary
111
- sock = checkout_writer
112
- elsif read == :secondary
113
- sock = checkout_reader
114
- else
115
- sock = checkout_tagged(read)
116
- end
117
- should_checkin = true
118
- end
119
116
 
120
117
  result = ''
118
+ sock = nil
121
119
  begin
120
+ if socket
121
+ sock = socket
122
+ should_checkin = false
123
+ else
124
+ if command || read == :primary
125
+ sock = checkout_writer
126
+ elsif read == :secondary
127
+ sock = checkout_reader
128
+ else
129
+ sock = checkout_tagged(read)
130
+ end
131
+ should_checkin = true
132
+ end
133
+
122
134
  send_message_on_socket(packed_message, sock)
123
135
  result = receive(sock, request_id, exhaust)
136
+ rescue SystemStackError, NoMemoryError, SystemCallError => ex
137
+ close
138
+ raise ex
124
139
  ensure
125
140
  if should_checkin
126
141
  if command || read == :primary
@@ -51,9 +51,8 @@ module Mongo
51
51
  # @option options [Float] :pool_timeout (5.0) When all of the connections a pool are checked out,
52
52
  # this is the number of seconds to wait for a new connection to be released before throwing an exception.
53
53
  # Note: this setting is relevant only for multi-threaded applications.
54
- # @option opts [Float] :op_timeout (nil) The number of seconds to wait for a read operation to time out.
55
- # Disabled by default.
56
- # @option opts [Float] :connect_timeout (nil) The number of seconds to wait before timing out a
54
+ # @option opts [Float] :op_timeout (30) The number of seconds to wait for a read operation to time out.
55
+ # @option opts [Float] :connect_timeout (30) The number of seconds to wait before timing out a
57
56
  # connection attempt.
58
57
  # @option opts [Boolean] :ssl (false) If true, create the connection to the server using SSL.
59
58
  # @option opts [Boolean] :refresh_mode (false) Set this to :sync to periodically update the
@@ -91,8 +90,10 @@ module Mongo
91
90
  raise MongoArgumentError, "A ReplSetConnection requires at least one seed node."
92
91
  end
93
92
 
94
- # The list of seed nodes
93
+ # The original, immutable list of seed node.
94
+ # TODO: add a method for replacing this list of node.
95
95
  @seeds = args
96
+ @seeds.freeze
96
97
 
97
98
  # TODO: get rid of this
98
99
  @nodes = @seeds.dup
@@ -152,10 +153,12 @@ module Mongo
152
153
  def connect
153
154
  log(:info, "Connecting...")
154
155
  return if @connected
155
- manager = PoolManager.new(self, @seeds)
156
- manager.connect
157
156
 
158
- update_config(manager)
157
+ discovered_seeds = @manager ? @manager.seeds : []
158
+ @manager = PoolManager.new(self, discovered_seeds)
159
+
160
+ @manager.connect
161
+ @refresh_version += 1
159
162
 
160
163
  if @require_primary && self.primary.nil? #TODO: in v2.0, we'll let this be optional and do a lazy connect.
161
164
  close
@@ -200,19 +203,21 @@ module Mongo
200
203
  # to get the refresh lock.
201
204
  def hard_refresh!
202
205
  log(:info, "Initiating hard refresh...")
203
- background_manager = PoolManager.new(self, @seeds)
206
+ discovered_seeds = @manager ? @manager.seeds : []
207
+ background_manager = PoolManager.new(self, discovered_seeds | @seeds)
204
208
  background_manager.connect
205
209
 
206
210
  # TODO: make sure that connect has succeeded
207
211
  old_manager = @manager
208
- update_config(background_manager)
212
+ @manager = background_manager
209
213
  old_manager.close(:soft => true)
214
+ @refresh_version += 1
210
215
 
211
216
  return true
212
217
  end
213
218
 
214
219
  def connected?
215
- self.primary_pool || self.read_pool
220
+ @connected && (self.primary_pool || self.read_pool)
216
221
  end
217
222
 
218
223
  # @deprecated
@@ -255,9 +260,13 @@ module Mongo
255
260
  end
256
261
 
257
262
  # Close the connection to the database.
258
- def close
263
+ def close(opts={})
264
+ if opts[:soft]
265
+ @manager.close(:soft => true) if @manager
266
+ else
267
+ @manager.close if @manager
268
+ end
259
269
  @connected = false
260
- @manager.close(:soft => true) if @manager
261
270
  end
262
271
 
263
272
  # If a ConnectionFailure is raised, this method will be called
@@ -298,12 +307,22 @@ module Mongo
298
307
  # Note that @read_pool might point to the primary pool
299
308
  # if no read pool has been defined.
300
309
  def checkout_reader
301
- connect unless connected?
302
- socket = get_socket_from_pool(self.read_pool)
303
-
304
- if !socket
310
+ if connected?
311
+ sync_refresh
312
+ else
305
313
  connect
306
- socket = get_socket_from_pool(self.primary_pool)
314
+ end
315
+
316
+ begin
317
+ socket = get_socket_from_pool(self.read_pool)
318
+
319
+ if !socket
320
+ connect
321
+ socket = get_socket_from_pool(self.primary_pool)
322
+ end
323
+ rescue => ex
324
+ checkin(socket) if socket
325
+ raise ex
307
326
  end
308
327
 
309
328
  if socket
@@ -315,12 +334,21 @@ module Mongo
315
334
 
316
335
  # Checkout a socket for writing (i.e., a primary node).
317
336
  def checkout_writer
318
- connect unless connected?
319
- socket = get_socket_from_pool(self.primary_pool)
320
-
321
- if !socket
337
+ if connected?
338
+ sync_refresh
339
+ else
322
340
  connect
341
+ end
342
+ begin
323
343
  socket = get_socket_from_pool(self.primary_pool)
344
+
345
+ if !socket
346
+ connect
347
+ socket = get_socket_from_pool(self.primary_pool)
348
+ end
349
+ rescue => ex
350
+ checkin(socket)
351
+ raise ex
324
352
  end
325
353
 
326
354
  if socket
@@ -332,8 +360,8 @@ module Mongo
332
360
 
333
361
  # Checkin a socket used for reading.
334
362
  def checkin_reader(socket)
335
- if !self.read_pool.checkin(socket) &&
336
- !self.primary_pool.checkin(socket)
363
+ if !((self.read_pool && self.read_pool.checkin(socket)) ||
364
+ (self.primary_pool && self.primary_pool.checkin(socket)))
337
365
  close_socket(socket)
338
366
  end
339
367
  sync_refresh
@@ -341,7 +369,7 @@ module Mongo
341
369
 
342
370
  # Checkin a socket used for writing.
343
371
  def checkin_writer(socket)
344
- if !self.primary_pool.checkin(socket)
372
+ if !self.primary_pool || !self.primary_pool.checkin(socket)
345
373
  close_socket(socket)
346
374
  end
347
375
  sync_refresh
@@ -349,7 +377,7 @@ module Mongo
349
377
 
350
378
  def close_socket(socket)
351
379
  begin
352
- socket.close
380
+ socket.close if socket
353
381
  rescue IOError
354
382
  log(:info, "Tried to close socket #{socket} but already closed.")
355
383
  end
@@ -372,36 +400,40 @@ module Mongo
372
400
  end
373
401
 
374
402
  def primary
375
- @manager.primary
403
+ @manager ? @manager.primary : nil
376
404
  end
377
405
 
378
406
  # Note: might want to freeze these after connecting.
379
407
  def secondaries
380
- @manager.secondaries
408
+ @manager ? @manager.secondaries : []
381
409
  end
382
410
 
383
411
  def hosts
384
- @manager.hosts
412
+ @manager ? @manager.hosts : []
385
413
  end
386
414
 
387
415
  def primary_pool
388
- @manager.primary_pool
416
+ @manager ? @manager.primary_pool : nil
389
417
  end
390
418
 
391
419
  def read_pool
392
- @manager.read_pool
420
+ @manager ? @manager.read_pool : nil
393
421
  end
394
422
 
395
423
  def secondary_pools
396
- @manager.secondary_pools
424
+ @manager ? @manager.secondary_pools : []
397
425
  end
398
426
 
399
427
  def tag_map
400
- @manager.tag_map
428
+ @manager ? @manager.tag_map : {}
401
429
  end
402
430
 
403
431
  def max_bson_size
404
- @manager.max_bson_size
432
+ if @manager && @manager.max_bson_size
433
+ @manager.max_bson_size
434
+ else
435
+ Mongo::DEFAULT_MAX_BSON_SIZE
436
+ end
405
437
  end
406
438
 
407
439
  private
@@ -437,10 +469,10 @@ module Mongo
437
469
  @pool_timeout = opts[:pool_timeout] || opts[:timeout] || 5.0
438
470
 
439
471
  # Timeout on socket read operation.
440
- @op_timeout = opts[:op_timeout] || nil
472
+ @op_timeout = opts[:op_timeout] || 30
441
473
 
442
474
  # Timeout on socket connect.
443
- @connect_timeout = opts[:connect_timeout] || nil
475
+ @connect_timeout = opts[:connect_timeout] || 30
444
476
 
445
477
  # Mutex for synchronizing pool access
446
478
  # TODO: remove this.
@@ -468,14 +500,6 @@ module Mongo
468
500
  connect if should_connect
469
501
  end
470
502
 
471
- # Given a pool manager, update this connection's
472
- # view of the replica set.
473
- def update_config(new_manager)
474
- @manager = new_manager
475
- @seeds = @manager.seeds.dup
476
- @refresh_version += 1
477
- end
478
-
479
503
  # Checkout a socket connected to a node with one of
480
504
  # the provided tags. If no such node exists, raise
481
505
  # an exception.