mongo 0.18.1 → 0.18.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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