mongo 0.18.1 → 0.18.2

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.
@@ -16,7 +16,7 @@
16
16
 
17
17
  require 'set'
18
18
  require 'socket'
19
- require 'monitor'
19
+ require 'thread'
20
20
 
21
21
  module Mongo
22
22
 
@@ -31,12 +31,12 @@ module Mongo
31
31
  STANDARD_HEADER_SIZE = 16
32
32
  RESPONSE_HEADER_SIZE = 20
33
33
 
34
- attr_reader :logger, :size, :host, :port, :nodes, :sockets, :checked_out, :reserved_connections
34
+ attr_reader :logger, :size, :host, :port, :nodes, :sockets, :checked_out
35
35
 
36
- def slave_ok?
36
+ def slave_ok?
37
37
  @slave_ok
38
38
  end
39
-
39
+
40
40
  # Counter for generating unique request ids.
41
41
  @@current_request_id = 0
42
42
 
@@ -45,7 +45,7 @@ module Mongo
45
45
  #
46
46
  # == Connecting
47
47
  # If connecting to just one server, you may specify whether connection to slave is permitted.
48
- #
48
+ #
49
49
  # In all cases, the default host is "localhost" and the default port, is 27017.
50
50
  #
51
51
  # When specifying a pair, pair_or_host, is a hash with two keys: :left and :right. Each key maps to either
@@ -69,13 +69,15 @@ module Mongo
69
69
  # :timeout :: When all of the connections to the pool are checked out,
70
70
  # this is the number of seconds to wait for a new connection
71
71
  # to be released before throwing an exception.
72
- #
72
+ #
73
+ # Note that there are a few issues when using connection pooling with Ruby 1.9 on Windows. These
74
+ # should be resolved in the next release.
73
75
  #
74
76
  # === Examples:
75
77
  #
76
78
  # # localhost, 27017
77
79
  # Connection.new
78
- #
80
+ #
79
81
  # # localhost, 27017
80
82
  # Connection.new("localhost")
81
83
  #
@@ -85,9 +87,9 @@ module Mongo
85
87
  # # localhost, 3000, where this node may be a slave
86
88
  # Connection.new("localhost", 3000, :slave_ok => true)
87
89
  #
88
- # # A pair of servers. The driver will always talk to master.
90
+ # # A pair of servers. The driver will always talk to master.
89
91
  # # On connection errors, Mongo::ConnectionFailure will be raised.
90
- # # See http://www.mongodb.org/display/DOCS/Replica+Pairs+in+Ruby
92
+ # # See http://www.mongodb.org/display/DOCS/Replica+Pairs+in+Ruby
91
93
  # Connection.new({:left => ["db1.example.com", 27017],
92
94
  # :right => ["db2.example.com", 27017]})
93
95
  #
@@ -100,22 +102,20 @@ module Mongo
100
102
 
101
103
  # Host and port of current master.
102
104
  @host = @port = nil
103
-
105
+
104
106
  # Lock for request ids.
105
107
  @id_lock = Mutex.new
106
108
 
107
109
  # Pool size and timeout.
108
110
  @size = options[:pool_size] || 1
109
- @timeout = options[:timeout] || 1.0
110
-
111
- # Cache of reserved sockets mapped to threads
112
- @reserved_connections = {}
111
+ @timeout = options[:timeout] || 5.0
113
112
 
114
113
  # Mutex for synchronizing pool access
115
- @connection_mutex = Monitor.new
114
+ @connection_mutex = Mutex.new
115
+ @safe_mutex = Mutex.new
116
116
 
117
117
  # Condition variable for signal and wait
118
- @queue = @connection_mutex.new_cond
118
+ @queue = ConditionVariable.new
119
119
 
120
120
  @sockets = []
121
121
  @checked_out = []
@@ -123,7 +123,7 @@ module Mongo
123
123
  if options[:auto_reconnect]
124
124
  warn(":auto_reconnect is deprecated. see http://www.mongodb.org/display/DOCS/Replica+Pairs+in+Ruby")
125
125
  end
126
-
126
+
127
127
  # Slave ok can be true only if one node is specified
128
128
  @slave_ok = options[:slave_ok] && @nodes.length == 1
129
129
  @logger = options[:logger] || nil
@@ -177,7 +177,7 @@ module Mongo
177
177
  # Increments and returns the next available request id.
178
178
  def get_request_id
179
179
  request_id = ''
180
- @id_lock.synchronize do
180
+ @id_lock.synchronize do
181
181
  request_id = @@current_request_id += 1
182
182
  end
183
183
  request_id
@@ -196,7 +196,7 @@ module Mongo
196
196
 
197
197
 
198
198
  ## Connections and pooling ##
199
-
199
+
200
200
  # Sends a message to MongoDB.
201
201
  #
202
202
  # Takes a MongoDB opcode, +operation+, a message of class ByteBuffer,
@@ -211,19 +211,22 @@ module Mongo
211
211
  end
212
212
 
213
213
  # Sends a message to the database, waits for a response, and raises
214
- # and exception if the operation has failed.
214
+ # an exception if the operation has failed.
215
215
  #
216
216
  # Takes a MongoDB opcode, +operation+, a message of class ByteBuffer,
217
217
  # +message+, the +db_name+, and an optional formatted +log_message+.
218
- # Sends the message to the databse, adding the necessary headers.
218
+ # Sends the message to the database, adding the necessary headers.
219
219
  def send_message_with_safe_check(operation, message, db_name, log_message=nil)
220
220
  message_with_headers = add_message_headers(operation, message)
221
221
  message_with_check = last_error_message(db_name)
222
222
  @logger.debug(" MONGODB #{log_message || message}") if @logger
223
223
  sock = checkout
224
224
  packed_message = message_with_headers.append!(message_with_check).to_s
225
- send_message_on_socket(packed_message, sock)
226
- docs, num_received, cursor_id = receive(sock)
225
+ docs = num_received = cursor_id = ''
226
+ @safe_mutex.synchronize do
227
+ send_message_on_socket(packed_message, sock)
228
+ docs, num_received, cursor_id = receive(sock)
229
+ end
227
230
  checkin(sock)
228
231
  if num_received == 1 && error = docs[0]['err']
229
232
  raise Mongo::OperationFailure, error
@@ -234,15 +237,18 @@ module Mongo
234
237
  # Sends a message to the database and waits for the response.
235
238
  #
236
239
  # Takes a MongoDB opcode, +operation+, a message of class ByteBuffer,
237
- # +message+, and an optional formatted +log_message+. This method
240
+ # +message+, and an optional formatted +log_message+. This method
238
241
  # also takes an options socket for internal use with #connect_to_master.
239
242
  def receive_message(operation, message, log_message=nil, socket=nil)
240
243
  packed_message = add_message_headers(operation, message).to_s
241
244
  @logger.debug(" MONGODB #{log_message || message}") if @logger
242
245
  sock = socket || checkout
243
246
 
244
- send_message_on_socket(packed_message, sock)
245
- result = receive(sock)
247
+ result = ''
248
+ @safe_mutex.synchronize do
249
+ send_message_on_socket(packed_message, sock)
250
+ result = receive(sock)
251
+ end
246
252
  checkin(sock)
247
253
  result
248
254
  end
@@ -275,7 +281,7 @@ module Mongo
275
281
  socket.close if socket
276
282
  false
277
283
  end
278
- end
284
+ end
279
285
  raise ConnectionFailure, "failed to connect to any given host:port" unless socket
280
286
  end
281
287
 
@@ -291,55 +297,25 @@ module Mongo
291
297
  sock.close
292
298
  end
293
299
  @host = @port = nil
294
- @sockets.clear
300
+ @sockets.clear
295
301
  @checked_out.clear
296
- @reserved_connections.clear
297
302
  end
298
303
 
299
304
  private
300
305
 
301
- # Get a socket from the pool, mapped to the current thread.
302
- def checkout
303
- #return @socket ||= checkout_new_socket if @size == 1
304
- if sock = @reserved_connections[Thread.current.object_id]
305
- sock
306
- else
307
- sock = obtain_socket
308
- @reserved_connections[Thread.current.object_id] = sock
309
- end
310
- sock
311
- end
312
-
313
306
  # Return a socket to the pool.
314
307
  def checkin(socket)
315
- @connection_mutex.synchronize do
316
- @reserved_connections.delete Thread.current.object_id
308
+ @connection_mutex.synchronize do
317
309
  @checked_out.delete(socket)
318
310
  @queue.signal
319
311
  end
320
312
  true
321
313
  end
322
314
 
323
- # Releases the connection for any dead threads.
324
- # Called when the connection pool grows too large to free up more sockets.
325
- def clear_stale_cached_connections!
326
- keys = @reserved_connections.keys
327
-
328
- Thread.list.each do |thread|
329
- keys.delete(thread.object_id) if thread.alive?
330
- end
331
-
332
- keys.each do |key|
333
- next unless @reserved_connections.has_key?(key)
334
- checkin(@reserved_connections[key])
335
- @reserved_connections.delete(key)
336
- end
337
- end
338
-
339
315
  # Adds a new socket to the pool and checks it out.
340
316
  #
341
- # This method is called exclusively from #obtain_socket;
342
- # therefore, it runs within a mutex, as it must.
317
+ # This method is called exclusively from #checkout;
318
+ # therefore, it runs within a mutex.
343
319
  def checkout_new_socket
344
320
  begin
345
321
  socket = TCPSocket.new(@host, @port)
@@ -354,8 +330,8 @@ module Mongo
354
330
 
355
331
  # Checks out the first available socket from the pool.
356
332
  #
357
- # This method is called exclusively from #obtain_socket;
358
- # therefore, it runs within a mutex, as it must.
333
+ # This method is called exclusively from #checkout;
334
+ # therefore, it runs within a mutex.
359
335
  def checkout_existing_socket
360
336
  socket = (@sockets - @checked_out).first
361
337
  @checked_out << socket
@@ -365,11 +341,17 @@ module Mongo
365
341
  # Check out an existing socket or create a new socket if the maximum
366
342
  # pool size has not been exceeded. Otherwise, wait for the next
367
343
  # available socket.
368
- def obtain_socket
369
- @connection_mutex.synchronize do
370
- connect_to_master if !connected?
344
+ def checkout
345
+ connect_to_master if !connected?
346
+ start_time = Time.now
347
+ loop do
348
+ if (Time.now - start_time) > @timeout
349
+ raise ConnectionTimeoutError, "could not obtain connection within " +
350
+ "#{@timeout} seconds. The max pool size is currently #{@size}; " +
351
+ "consider increasing the pool size or timeout."
352
+ end
371
353
 
372
- loop do
354
+ @connection_mutex.synchronize do
373
355
  socket = if @checked_out.size < @sockets.size
374
356
  checkout_existing_socket
375
357
  elsif @sockets.size < @size
@@ -378,39 +360,10 @@ module Mongo
378
360
 
379
361
  return socket if socket
380
362
 
381
- # Try to clear out any stale threads to free up some connections
382
- clear_stale_cached_connections!
383
- next if @checked_out.size < @sockets.size
384
-
385
- # Otherwise, wait.
386
- if wait
387
- next
388
- else
389
-
390
- # Try to clear stale threads once more before failing.
391
- clear_stale_cached_connections!
392
- if @size == @sockets.size
393
- raise ConnectionTimeoutError, "could not obtain connection within " +
394
- "#{@timeout} seconds. The max pool size is currently #{@size}; " +
395
- "consider increasing it."
396
- end
397
- end # if
398
- end # loop
399
- end # synchronize
400
- end
401
-
402
- if RUBY_VERSION >= '1.9'
403
- # Ruby 1.9's Condition Variables don't support timeouts yet;
404
- # until they do, we'll make do with this hack.
405
- def wait
406
- Timeout.timeout(@timeout) do
407
- @queue.wait
363
+ # Otherwise, wait
364
+ @queue.wait(@connection_mutex)
408
365
  end
409
366
  end
410
- else
411
- def wait
412
- @queue.wait(@timeout)
413
- end
414
367
  end
415
368
 
416
369
  def receive(sock)
@@ -424,7 +377,7 @@ module Mongo
424
377
  header.put_array(receive_message_on_socket(16, sock).unpack("C*"))
425
378
  unless header.size == STANDARD_HEADER_SIZE
426
379
  raise "Short read for DB response header: " +
427
- "expected #{STANDARD_HEADER_SIZE} bytes, saw #{header.size}"
380
+ "expected #{STANDARD_HEADER_SIZE} bytes, saw #{header.size}"
428
381
  end
429
382
  header.rewind
430
383
  size = header.get_int
@@ -473,7 +426,7 @@ module Mongo
473
426
  message.put_array(BSON.serialize({:getlasterror => 1}, false).unpack("C*"))
474
427
  add_message_headers(Mongo::Constants::OP_QUERY, message)
475
428
  end
476
-
429
+
477
430
  # Prepares a message for transmission to MongoDB by
478
431
  # constructing a valid message header.
479
432
  def add_message_headers(operation, message)
@@ -494,7 +447,7 @@ module Mongo
494
447
  end
495
448
 
496
449
  # Low-level method for sending a message on a socket.
497
- # Requires a packed message and an available socket,
450
+ # Requires a packed message and an available socket,
498
451
  def send_message_on_socket(packed_message, socket)
499
452
  begin
500
453
  socket.send(packed_message, 0)
@@ -537,7 +490,7 @@ module Mongo
537
490
  [['localhost', DEFAULT_PORT]]
538
491
  end
539
492
  end
540
-
493
+
541
494
  # Turns an array containing a host name string and a
542
495
  # port number integer into a [host, port] pair array.
543
496
  def pair_val_to_connection(a)
@@ -1,8 +1,8 @@
1
1
  module Mongo
2
2
  module Constants
3
- OP_REPLY = 1
4
- OP_MSG = 1000
5
- OP_UPDATE = 2001
3
+ OP_REPLY = 1
4
+ OP_MSG = 1000
5
+ OP_UPDATE = 2001
6
6
  OP_INSERT = 2002
7
7
  OP_QUERY = 2004
8
8
  OP_GET_MORE = 2005
data/lib/mongo/cursor.rb CHANGED
@@ -19,7 +19,7 @@ module Mongo
19
19
  include Mongo::Conversions
20
20
  include Enumerable
21
21
 
22
- attr_reader :collection, :selector, :admin, :fields,
22
+ attr_reader :collection, :selector, :admin, :fields,
23
23
  :order, :hint, :snapshot, :timeout,
24
24
  :full_collection_name
25
25
 
@@ -49,19 +49,17 @@ module Mongo
49
49
  @query_run = false
50
50
  end
51
51
 
52
- # Return the next object or nil if there are no more. Raises an error
53
- # if necessary.
54
- def next_object
52
+ # Return the next document or nil if there are no more.
53
+ def next_document
55
54
  refill_via_get_more if num_remaining == 0
56
- o = @cache.shift
55
+ doc = @cache.shift
57
56
 
58
- if o && o['$err']
59
- err = o['$err']
57
+ if doc && doc['$err']
58
+ err = doc['$err']
60
59
 
61
60
  # If the server has stopped being the master (e.g., it's one of a
62
61
  # pair but it has died or something like that) then we close that
63
- # connection. If the db has auto connect option and a pair of
64
- # servers, next request will re-open on master server.
62
+ # connection. The next request will re-open on master server.
65
63
  if err == "not master"
66
64
  raise ConnectionFailure, err
67
65
  @connection.close
@@ -70,12 +68,17 @@ module Mongo
70
68
  raise OperationFailure, err
71
69
  end
72
70
 
73
- o
71
+ doc
72
+ end
73
+
74
+ def next_object
75
+ warn "Cursor#next_object is deprecated; please use Cursor#next_document instead."
76
+ next_document
74
77
  end
75
78
 
76
- # Get the size of the results set for this query.
79
+ # Get the size of the result set for this query.
77
80
  #
78
- # Returns the number of objects in the results set for this query. Does
81
+ # Returns the number of objects in the result set for this query. Does
79
82
  # not take limit and skip into account. Raises OperationFailure on a
80
83
  # database error.
81
84
  def count
@@ -88,17 +91,17 @@ module Mongo
88
91
  raise OperationFailure, "Count failed: #{response['errmsg']}"
89
92
  end
90
93
 
91
- # Sort this cursor's result
94
+ # Sort this cursor's results.
92
95
  #
93
96
  # Takes either a single key and a direction, or an array of [key,
94
- # direction] pairs. Directions should be specified as Mongo::ASCENDING
95
- # or Mongo::DESCENDING (or :ascending or :descending) (or :asc or :desc).
97
+ # direction] pairs. Directions should be specified as Mongo::ASCENDING / Mongo::DESCENDING
98
+ # (or :ascending / :descending, :asc / :desc).
96
99
  #
97
100
  # Raises InvalidOperation if this cursor has already been used. Raises
98
- # InvalidSortValueError if specified order is invalid.
101
+ # InvalidSortValueError if the specified order is invalid.
99
102
  #
100
103
  # This method overrides any sort order specified in the Collection#find
101
- # method, and only the last sort applied has an effect
104
+ # method, and only the last sort applied has an effect.
102
105
  def sort(key_or_list, direction=nil)
103
106
  check_modifiable
104
107
 
@@ -130,7 +133,7 @@ module Mongo
130
133
 
131
134
  # Skips the first +number_to_skip+ results of this cursor.
132
135
  # Returns the current number_to_skip if no parameter is given.
133
- #
136
+ #
134
137
  # Raises InvalidOperation if this cursor has already been used.
135
138
  #
136
139
  # This method overrides any skip specified in the Collection#find method,
@@ -151,15 +154,15 @@ module Mongo
151
154
  def each
152
155
  num_returned = 0
153
156
  while more? && (@limit <= 0 || num_returned < @limit)
154
- yield next_object()
157
+ yield next_document
155
158
  num_returned += 1
156
159
  end
157
160
  end
158
161
 
159
162
  # Return all of the documents in this cursor as an array of hashes.
160
163
  #
161
- # Raises InvalidOperation if this cursor has already been used (including
162
- # any previous calls to this method).
164
+ # Raises InvalidOperation if this cursor has already been used or if
165
+ # this methods has already been called on the cursor.
163
166
  #
164
167
  # Use of this method is discouraged - iterating over a cursor is much
165
168
  # more efficient in most cases.
@@ -168,22 +171,22 @@ module Mongo
168
171
  rows = []
169
172
  num_returned = 0
170
173
  while more? && (@limit <= 0 || num_returned < @limit)
171
- rows << next_object()
174
+ rows << next_document
172
175
  num_returned += 1
173
176
  end
174
177
  rows
175
178
  end
176
179
 
177
- # Returns an explain plan record for this cursor.
180
+ # Returns an explain plan document for this cursor.
178
181
  def explain
179
182
  c = Cursor.new(@collection, query_options_hash.merge(:limit => -@limit.abs, :explain => true))
180
- explanation = c.next_object
183
+ explanation = c.next_document
181
184
  c.close
182
185
 
183
186
  explanation
184
187
  end
185
188
 
186
- # Close the cursor.
189
+ # Closes the cursor.
187
190
  #
188
191
  # Note: if a cursor is read until exhausted (read until Mongo::Constants::OP_QUERY or
189
192
  # Mongo::Constants::OP_GETMORE returns zero for the cursor id), there is no need to
@@ -211,20 +214,20 @@ module Mongo
211
214
  # See http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol#MongoWireProtocol-Mongo::Constants::OPQUERY
212
215
  def query_opts
213
216
  timeout = @timeout ? 0 : Mongo::Constants::OP_QUERY_NO_CURSOR_TIMEOUT
214
- slave_ok = @connection.slave_ok? ? Mongo::Constants::OP_QUERY_SLAVE_OK : 0
217
+ slave_ok = @connection.slave_ok? ? Mongo::Constants::OP_QUERY_SLAVE_OK : 0
215
218
  slave_ok + timeout
216
219
  end
217
220
 
218
- # Returns the query options set on this Cursor.
221
+ # Returns the query options for this Cursor.
219
222
  def query_options_hash
220
223
  { :selector => @selector,
221
- :fields => @fields,
222
- :admin => @admin,
223
- :skip => @skip_num,
224
- :limit => @limit_num,
225
- :order => @order,
226
- :hint => @hint,
227
- :snapshot => @snapshot,
224
+ :fields => @fields,
225
+ :admin => @admin,
226
+ :skip => @skip_num,
227
+ :limit => @limit_num,
228
+ :order => @order,
229
+ :hint => @hint,
230
+ :snapshot => @snapshot,
228
231
  :timeout => @timeout }
229
232
  end
230
233
 
@@ -245,7 +248,7 @@ module Mongo
245
248
  end
246
249
  end
247
250
 
248
- # Set query selector hash. If the selector is a Code or String object,
251
+ # Set the query selector hash. If the selector is a Code or String object,
249
252
  # the selector will be used in a $where clause.
250
253
  # See http://www.mongodb.org/display/DOCS/Server-side+Code+Execution
251
254
  def convert_selector_for_query(selector)
@@ -266,20 +269,21 @@ module Mongo
266
269
  @order || @explain || @hint || @snapshot
267
270
  end
268
271
 
272
+ # Return a number of documents remaining for this cursor.
269
273
  def num_remaining
270
274
  refill_via_get_more if @cache.length == 0
271
275
  @cache.length
272
276
  end
273
277
 
274
278
  # Internal method, not for general use. Return +true+ if there are
275
- # more records to retrieve. This methods does not check @limit;
276
- # #each is responsible for doing that.
279
+ # more records to retrieve. This method does not check @limit;
280
+ # Cursor#each is responsible for doing that.
277
281
  def more?
278
282
  num_remaining > 0
279
283
  end
280
284
 
281
285
  def refill_via_get_more
282
- return if send_query_if_needed || @cursor_id.zero?
286
+ return if send_initial_query || @cursor_id.zero?
283
287
  message = ByteBuffer.new
284
288
  # Reserved.
285
289
  message.put_int(0)
@@ -290,7 +294,7 @@ module Mongo
290
294
 
291
295
  # Number of results to return; db decides for now.
292
296
  message.put_int(0)
293
-
297
+
294
298
  # Cursor id.
295
299
  message.put_long(@cursor_id)
296
300
  results, @n_received, @cursor_id = @connection.receive_message(Mongo::Constants::OP_GET_MORE, message, "cursor.get_more()", @socket)
@@ -299,13 +303,13 @@ module Mongo
299
303
  end
300
304
 
301
305
  # Run query the first time we request an object from the wire
302
- def send_query_if_needed
306
+ def send_initial_query
303
307
  if @query_run
304
308
  false
305
309
  else
306
310
  message = construct_query_message
307
- results, @n_received, @cursor_id = @connection.receive_message(Mongo::Constants::OP_QUERY, message,
308
- (query_log_message if @connection.logger), @socket)
311
+ results, @n_received, @cursor_id = @connection.receive_message(Mongo::Constants::OP_QUERY, message,
312
+ (query_log_message if @connection.logger), @socket)
309
313
  @cache += results
310
314
  @query_run = true
311
315
  close_cursor_if_query_complete
@@ -331,7 +335,7 @@ module Mongo
331
335
 
332
336
  def query_log_message
333
337
  "#{@admin ? 'admin' : @db.name}.#{@collection.name}.find(#{@selector.inspect}, #{@fields ? @fields.inspect : '{}'})" +
334
- "#{@skip != 0 ? ('.skip(' + @skip.to_s + ')') : ''}#{@limit != 0 ? ('.limit(' + @limit.to_s + ')') : ''}"
338
+ "#{@skip != 0 ? ('.skip(' + @skip.to_s + ')') : ''}#{@limit != 0 ? ('.limit(' + @limit.to_s + ')') : ''}"
335
339
  end
336
340
 
337
341
  def selector_with_special_query_fields
@@ -349,7 +353,7 @@ module Mongo
349
353
  when String, Symbol then string_as_sort_parameters(@order)
350
354
  when Array then array_as_sort_parameters(@order)
351
355
  else
352
- raise InvalidSortValueError, "Illegal sort clause, '#{@order.class.name}'; must be of the form " +
356
+ raise InvalidSortValueError, "Illegal sort clause, '#{@order.class.name}'; must be of the form " +
353
357
  "[['field1', '(ascending|descending)'], ['field2', '(ascending|descending)']]"
354
358
  end
355
359
  end