mongo 1.2.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -129,7 +129,7 @@ The driver is thread-safe.
129
129
 
130
130
  ## Connection Pooling
131
131
 
132
- As of v0.18, the driver implements connection pooling. By default, only one
132
+ The driver implements connection pooling. By default, only one
133
133
  socket connection will be opened to MongoDB. However, if you're running a
134
134
  multi-threaded application, you can specify a maximum pool size and a maximum
135
135
  timeout for waiting for old connections to be released to the pool.
@@ -149,12 +149,24 @@ processes will create a new connection to the database. In Passenger, this can b
149
149
  if defined?(PhusionPassenger)
150
150
  PhusionPassenger.on_event(:starting_worker_process) do |forked|
151
151
  if forked
152
- # Create new connection here
152
+ # Reset all connection objects. How you do this depends on where
153
+ # you keep your connection object. In any case, call the #connect
154
+ # method on the connection object. For example:
155
+ # CONN.connect
156
+ #
157
+ # If you're using MongoMapper:
158
+ # MongoMapper.connection.connect
153
159
  end
154
160
  end
155
161
  end
156
162
 
157
- The above code should be put into a Rails initializer or other initialization script.
163
+ In Unicorn, add this to your unicorn.rb file:
164
+
165
+ after_fork do |server, worker|
166
+ # Handle reconnection
167
+ end
168
+
169
+ The above code should be put into a Rails initializer or similar initialization script.
158
170
 
159
171
  ## String Encoding
160
172
 
data/docs/FAQ.md CHANGED
@@ -110,3 +110,7 @@ Without further investigation, it's impossible to know exactly what has caused t
110
110
  Because of the indeterminacy involved, the MongoDB drivers will not retry operations on connection failure. How connection failures should be handled is entirely dependent on the application. Therefore, we leave it to the application developers to make the best decision in this case.
111
111
 
112
112
  The drivers will reconnect on the subsequent operation.
113
+
114
+ #### I ocassionally get an error saying that responses are out of order. What's happening?
115
+
116
+ See (this JIRA issue)[http://jira.mongodb.org/browse/RUBY-221].
data/docs/HISTORY.md CHANGED
@@ -1,14 +1,21 @@
1
1
  # MongoDB Ruby Driver History
2
2
 
3
- 1.2.0
3
+ ### 1.2.1
4
4
  2011-1-18
5
+
6
+ * Enable authentication with connection pooling.
7
+ * Allow custom logging with Connection#instrument (CodeMonkeySteve)
8
+ * Minor fixes and doc improvements.
9
+
10
+ ### 1.2.0
11
+ 2011-1-18
12
+
5
13
  * Some minor improvements. See commit history.
6
- * Since nothing major was reported for the RC, we're releasing.
7
14
 
8
- 1.2.rc0
15
+ ### 1.2.rc0
9
16
  2011-1-5
10
17
 
11
- Lots of cleanp and minor bug fixes.
18
+ Lots of cleanup and minor bug fixes.
12
19
  * Issues resolved: http://jira.mongodb.org/browse/RUBY/fixforversion/10222
13
20
  * Updated Java BSON to Java driver 2.4.
14
21
  * Platform gem for JRuby bson.
data/lib/mongo.rb CHANGED
@@ -19,7 +19,7 @@
19
19
  $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
20
20
 
21
21
  module Mongo
22
- VERSION = "1.2.0"
22
+ VERSION = "1.2.1"
23
23
  end
24
24
 
25
25
  module Mongo
@@ -79,7 +79,6 @@ module Mongo
79
79
 
80
80
  @db, @name = db, name
81
81
  @connection = @db.connection
82
- @logger = @connection.logger
83
82
  @cache_time = @db.cache_time
84
83
  @cache = Hash.new(0)
85
84
  unless pk_factory
@@ -322,12 +321,13 @@ module Mongo
322
321
  message.put_int(0)
323
322
  message.put_binary(BSON::BSON_CODER.serialize(selector, false, true).to_s)
324
323
 
325
- @logger.debug("MONGODB #{@db.name}['#{@name}'].remove(#{selector.inspect})") if @logger
326
- if safe
327
- @connection.send_message_with_safe_check(Mongo::Constants::OP_DELETE, message, @db.name, nil, safe)
328
- else
329
- @connection.send_message(Mongo::Constants::OP_DELETE, message)
330
- true
324
+ @connection.instrument(:remove, :database => @db.name, :collection => @name, :selector => selector) do
325
+ if safe
326
+ @connection.send_message_with_safe_check(Mongo::Constants::OP_DELETE, message, @db.name, nil, safe)
327
+ else
328
+ @connection.send_message(Mongo::Constants::OP_DELETE, message)
329
+ true
330
+ end
331
331
  end
332
332
  end
333
333
 
@@ -367,11 +367,13 @@ module Mongo
367
367
  message.put_int(update_options)
368
368
  message.put_binary(BSON::BSON_CODER.serialize(selector, false, true).to_s)
369
369
  message.put_binary(BSON::BSON_CODER.serialize(document, false, true).to_s)
370
- @logger.debug("MONGODB #{@db.name}['#{@name}'].update(#{selector.inspect}, #{document.inspect})") if @logger
371
- if safe
372
- @connection.send_message_with_safe_check(Mongo::Constants::OP_UPDATE, message, @db.name, nil, safe)
373
- else
374
- @connection.send_message(Mongo::Constants::OP_UPDATE, message, nil)
370
+
371
+ @connection.instrument(:update, :database => @db.name, :collection => @name, :selector => selector, :document => document) do
372
+ if safe
373
+ @connection.send_message_with_safe_check(Mongo::Constants::OP_UPDATE, message, @db.name, nil, safe)
374
+ else
375
+ @connection.send_message(Mongo::Constants::OP_UPDATE, message, nil)
376
+ end
375
377
  end
376
378
  end
377
379
 
@@ -570,9 +572,9 @@ module Mongo
570
572
  # set to true.
571
573
  #
572
574
  # @return [Array] the command response consisting of grouped items.
573
- def group(key, condition={}, initial={}, reduce=nil, finalize=nil)
574
- if key.is_a?(Hash)
575
- return new_group(key)
575
+ def group(opts, condition={}, initial={}, reduce=nil, finalize=nil)
576
+ if opts.is_a?(Hash)
577
+ return new_group(opts)
576
578
  else
577
579
  warn "Collection#group no longer take a list of paramters. This usage is deprecated." +
578
580
  "Check out the new API at http://api.mongodb.org/ruby/current/Mongo/Collection.html#group-instance_method"
@@ -589,19 +591,19 @@ module Mongo
589
591
  }
590
592
  }
591
593
 
592
- if key.is_a?(Symbol)
594
+ if opts.is_a?(Symbol)
593
595
  raise MongoArgumentError, "Group takes either an array of fields to group by or a JavaScript function" +
594
596
  "in the form of a String or BSON::Code."
595
597
  end
596
598
 
597
- unless key.nil?
598
- if key.is_a? Array
599
+ unless opts.nil?
600
+ if opts.is_a? Array
599
601
  key_type = "key"
600
602
  key_value = {}
601
- key.each { |k| key_value[k] = 1 }
603
+ opts.each { |k| key_value[k] = 1 }
602
604
  else
603
605
  key_type = "$keyf"
604
- key_value = key.is_a?(BSON::Code) ? key : BSON::Code.new(key)
606
+ key_value = opts.is_a?(BSON::Code) ? opts : BSON::Code.new(opts)
605
607
  end
606
608
 
607
609
  group_command["group"][key_type] = key_value
@@ -838,11 +840,12 @@ module Mongo
838
840
  end
839
841
  raise InvalidOperation, "Exceded maximum insert size of 16,000,000 bytes" if message.size > 16_000_000
840
842
 
841
- @logger.debug("MONGODB #{@db.name}['#{collection_name}'].insert(#{documents.inspect})") if @logger
842
- if safe
843
- @connection.send_message_with_safe_check(Mongo::Constants::OP_INSERT, message, @db.name, nil, safe)
844
- else
845
- @connection.send_message(Mongo::Constants::OP_INSERT, message, nil)
843
+ @connection.instrument(:insert, :database => @db.name, :collection => collection_name, :documents => documents) do
844
+ if safe
845
+ @connection.send_message_with_safe_check(Mongo::Constants::OP_INSERT, message, @db.name, nil, safe)
846
+ else
847
+ @connection.send_message(Mongo::Constants::OP_INSERT, message, nil)
848
+ end
846
849
  end
847
850
  documents.collect { |o| o[:_id] || o['_id'] }
848
851
  end
@@ -35,7 +35,7 @@ module Mongo
35
35
  STANDARD_HEADER_SIZE = 16
36
36
  RESPONSE_HEADER_SIZE = 20
37
37
 
38
- attr_reader :logger, :size, :auths, :primary, :safe, :primary_pool, :host_to_try
38
+ attr_reader :logger, :size, :auths, :primary, :safe, :primary_pool, :host_to_try, :pool_size
39
39
 
40
40
  # Counter for generating unique request ids.
41
41
  @@current_request_id = 0
@@ -61,7 +61,8 @@ module Mongo
61
61
  # on initialization.
62
62
  # @option opts [Boolean] :slave_ok (false) Must be set to +true+ when connecting
63
63
  # to a single, slave node.
64
- # @option opts [Logger, #debug] :logger (nil) Logger instance to receive driver operation log.
64
+ # @option opts [Logger, #debug] :logger (nil) A Logger instance for debugging driver ops. Note that
65
+ # logging negatively impacts performance; therefore, it should not be used for high-performance apps.
65
66
  # @option opts [Integer] :pool_size (1) The maximum number of socket connections allowed per
66
67
  # connection pool. Note: this setting is relevant only for multi-threaded applications.
67
68
  # @option opts [Float] :timeout (5.0) When all of the connections a pool are checked out,
@@ -188,10 +189,11 @@ module Mongo
188
189
  #
189
190
  # @raise [AuthenticationError] raises an exception if any one
190
191
  # authentication fails.
191
- def apply_saved_authentication
192
+ def apply_saved_authentication(opts={})
192
193
  return false if @auths.empty?
193
194
  @auths.each do |auth|
194
- self[auth['db_name']].authenticate(auth['username'], auth['password'], false)
195
+ self[auth['db_name']].issue_authentication(auth['username'], auth['password'], false,
196
+ :socket => opts[:socket])
195
197
  end
196
198
  true
197
199
  end
@@ -241,6 +243,14 @@ module Mongo
241
243
  true
242
244
  end
243
245
 
246
+ def authenticate_pools
247
+ @primary_pool.authenticate_existing
248
+ end
249
+
250
+ def logout_pools(db)
251
+ @primary_pool.logout_existing(db)
252
+ end
253
+
244
254
  # Return a hash with all database names
245
255
  # and their respective sizes on disk.
246
256
  #
@@ -411,7 +421,13 @@ module Mongo
411
421
  request_id = add_message_headers(message, operation)
412
422
  packed_message = message.to_s
413
423
  begin
414
- sock = socket || (command ? checkout_writer : checkout_reader)
424
+ if socket
425
+ sock = socket
426
+ checkin = false
427
+ else
428
+ sock = (command ? checkout_writer : checkout_reader)
429
+ checkin = true
430
+ end
415
431
 
416
432
  result = ''
417
433
  @safe_mutexes[sock].synchronize do
@@ -419,7 +435,9 @@ module Mongo
419
435
  result = receive(sock, request_id)
420
436
  end
421
437
  ensure
422
- command ? checkin_writer(sock) : checkin_reader(sock)
438
+ if checkin
439
+ command ? checkin_writer(sock) : checkin_reader(sock)
440
+ end
423
441
  end
424
442
  result
425
443
  end
@@ -451,6 +469,7 @@ module Mongo
451
469
  raise ConnectionFailure, "Failed to connect to a master node at #{@host_to_try[0]}:#{@host_to_try[1]}"
452
470
  end
453
471
  end
472
+ alias :reconnect :connect
454
473
 
455
474
  def connecting?
456
475
  @nodes_to_try.length > 0
@@ -517,6 +536,15 @@ module Mongo
517
536
  end
518
537
  end
519
538
 
539
+ # Execute the block and log the operation described by name
540
+ # and payload.
541
+ # TODO: Not sure if this should take a block.
542
+ def instrument(name, payload = {}, &blk)
543
+ res = yield
544
+ log_operation(name, payload)
545
+ res
546
+ end
547
+
520
548
  protected
521
549
 
522
550
  # Generic initialization code.
@@ -548,7 +576,12 @@ module Mongo
548
576
  @primary = nil
549
577
  @primary_pool = nil
550
578
 
551
- @logger = opts[:logger] || nil
579
+ @logger = opts[:logger] || nil
580
+
581
+ if @logger
582
+ @logger.debug("MongoDB logging. Please note that logging negatively impacts performance " +
583
+ "and should be disabled for high-performance production apps.")
584
+ end
552
585
 
553
586
  should_connect = opts.fetch(:connect, true)
554
587
  connect if should_connect
@@ -570,6 +603,18 @@ module Mongo
570
603
  end
571
604
  end
572
605
 
606
+ ## Logging methods
607
+
608
+ def log_operation(name, payload)
609
+ return unless @logger
610
+ msg = "#{payload[:database]}['#{payload[:collection]}'].#{name}("
611
+ msg += payload.values_at(:selector, :document, :documents, :fields ).compact.map(&:inspect).join(', ') + ")"
612
+ msg += ".skip(#{payload[:skip]})" if payload[:skip]
613
+ msg += ".limit(#{payload[:limit]})" if payload[:limit]
614
+ msg += ".sort(#{payload[:sort]})" if payload[:sort]
615
+ @logger.debug "MONGODB #{msg}"
616
+ end
617
+
573
618
  private
574
619
 
575
620
  ## Methods for establishing a connection:
@@ -588,7 +633,7 @@ module Mongo
588
633
  socket = TCPSocket.new(host, port)
589
634
  socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
590
635
 
591
- config = self['admin'].command({:ismaster => 1}, :sock => socket)
636
+ config = self['admin'].command({:ismaster => 1}, :socket => socket)
592
637
  rescue OperationFailure, SocketError, SystemCallError, IOError => ex
593
638
  close
594
639
  ensure
@@ -598,16 +643,13 @@ module Mongo
598
643
  config
599
644
  end
600
645
 
601
- # Set the specified node as primary, and
602
- # apply any saved authentication credentials.
646
+ # Set the specified node as primary.
603
647
  def set_primary(node)
604
648
  host, port = *node
605
649
  @primary = [host, port]
606
650
  @primary_pool = Pool.new(self, host, port, :size => @pool_size, :timeout => @timeout)
607
- apply_saved_authentication
608
651
  end
609
652
 
610
-
611
653
  ## Low-level connection methods.
612
654
 
613
655
  def receive(sock, expected_response)
data/lib/mongo/cursor.rb CHANGED
@@ -373,18 +373,21 @@ module Mongo
373
373
  end
374
374
 
375
375
  # Run query the first time we request an object from the wire
376
+ # TODO: should we be calling instrument_payload even if logging
377
+ # is disabled?
376
378
  def send_initial_query
377
379
  if @query_run
378
380
  false
379
381
  else
380
382
  message = construct_query_message
381
- @logger.debug query_log_message if @logger
382
- results, @n_received, @cursor_id = @connection.receive_message(
383
- Mongo::Constants::OP_QUERY, message, nil, @socket, @command)
384
- @returned += @n_received
385
- @cache += results
386
- @query_run = true
387
- close_cursor_if_query_complete
383
+ @connection.instrument(:find, instrument_payload) do
384
+ results, @n_received, @cursor_id = @connection.receive_message(
385
+ Mongo::Constants::OP_QUERY, message, nil, @socket, @command)
386
+ @returned += @n_received
387
+ @cache += results
388
+ @query_run = true
389
+ close_cursor_if_query_complete
390
+ end
388
391
  true
389
392
  end
390
393
  end
@@ -401,10 +404,13 @@ module Mongo
401
404
  message
402
405
  end
403
406
 
404
- def query_log_message
405
- "#{@db.name}['#{@collection.name}'].find(#{@selector.inspect}, #{@fields ? @fields.inspect : '{}'})" +
406
- "#{@skip != 0 ? ('.skip(' + @skip.to_s + ')') : ''}#{@limit != 0 ? ('.limit(' + @limit.to_s + ')') : ''}" +
407
- "#{@order ? ('.sort(' + @order.inspect + ')') : ''}"
407
+ def instrument_payload
408
+ log = { :database => @db.name, :collection => @collection.name, :selector => selector }
409
+ log[:fields] = @fields if @fields
410
+ log[:skip] = @skip if @skip && (@skip > 0)
411
+ log[:limit] = @limit if @limit && (@limit > 0)
412
+ log[:order] = @order if @order
413
+ log
408
414
  end
409
415
 
410
416
  def construct_query_spec
data/lib/mongo/db.rb CHANGED
@@ -92,7 +92,8 @@ module Mongo
92
92
  # @param [String] password
93
93
  # @param [Boolean] save_auth
94
94
  # Save this authentication to the connection object using Connection#add_auth. This
95
- # will ensure that the authentication will be applied on database reconnect.
95
+ # will ensure that the authentication will be applied on database reconnect. Note
96
+ # that this value must be true when using connection pooling.
96
97
  #
97
98
  # @return [Boolean]
98
99
  #
@@ -100,8 +101,19 @@ module Mongo
100
101
  #
101
102
  # @core authenticate authenticate-instance_method
102
103
  def authenticate(username, password, save_auth=true)
103
- doc = command({:getnonce => 1}, :check_response => false)
104
- raise "error retrieving nonce: #{doc}" unless ok?(doc)
104
+ if @connection.pool_size > 1
105
+ if !save_auth
106
+ raise MongoArgumentError, "If using connection pooling, :save_auth must be set to true."
107
+ end
108
+ @connection.authenticate_pools
109
+ end
110
+
111
+ issue_authentication(username, password, save_auth)
112
+ end
113
+
114
+ def issue_authentication(username, password, save_auth=true, opts={})
115
+ doc = command({:getnonce => 1}, :check_response => false, :socket => opts[:socket])
116
+ raise MongoDBError, "Error retrieving nonce: #{doc}" unless ok?(doc)
105
117
  nonce = doc['nonce']
106
118
 
107
119
  auth = BSON::OrderedHash.new
@@ -109,7 +121,7 @@ module Mongo
109
121
  auth['user'] = username
110
122
  auth['nonce'] = nonce
111
123
  auth['key'] = Mongo::Support.auth_key(username, password, nonce)
112
- if ok?(self.command(auth, :check_response => false))
124
+ if ok?(self.command(auth, :check_response => false, :socket => opts[:socket]))
113
125
  if save_auth
114
126
  @connection.add_auth(@name, username, password)
115
127
  end
@@ -121,7 +133,7 @@ module Mongo
121
133
 
122
134
  # Adds a stored Javascript function to the database which can executed
123
135
  # server-side in map_reduce, db.eval and $where clauses.
124
- #
136
+ #
125
137
  # @param [String] function_name
126
138
  # @param [String] code
127
139
  #
@@ -179,14 +191,22 @@ module Mongo
179
191
  end
180
192
 
181
193
  # Deauthorizes use for this database for this connection. Also removes
182
- # any saved authorization in the connection class associated with this
194
+ # any saved authentication in the connection class associated with this
183
195
  # database.
184
196
  #
185
197
  # @raise [MongoDBError] if logging out fails.
186
198
  #
187
199
  # @return [Boolean]
188
- def logout
189
- doc = command(:logout => 1)
200
+ def logout(opts={})
201
+ if @connection.pool_size > 1
202
+ @connection.logout_pools(@name)
203
+ end
204
+
205
+ issue_logout(opts)
206
+ end
207
+
208
+ def issue_logout(opts={})
209
+ doc = command({:logout => 1}, :socket => opts[:socket])
190
210
  if ok?(doc)
191
211
  @connection.remove_auth(@name)
192
212
  true
@@ -455,14 +475,14 @@ module Mongo
455
475
  #
456
476
  # @option opts [Boolean] :check_response (true) If +true+, raises an exception if the
457
477
  # command fails.
458
- # @option opts [Socket] :sock a socket to use for sending the command. This is mainly for internal use.
478
+ # @option opts [Socket] :socket a socket to use for sending the command. This is mainly for internal use.
459
479
  #
460
480
  # @return [Hash]
461
481
  #
462
482
  # @core commands command_instance-method
463
483
  def command(selector, opts={})
464
484
  check_response = opts.fetch(:check_response, true)
465
- sock = opts[:sock]
485
+ socket = opts[:socket]
466
486
  raise MongoArgumentError, "command must be given a selector" unless selector.is_a?(Hash) && !selector.empty?
467
487
  if selector.keys.length > 1 && RUBY_VERSION < '1.9' && selector.class != BSON::OrderedHash
468
488
  raise MongoArgumentError, "DB#command requires an OrderedHash when hash contains multiple keys"
@@ -470,7 +490,7 @@ module Mongo
470
490
 
471
491
  begin
472
492
  result = Cursor.new(system_command_collection,
473
- :limit => -1, :selector => selector, :socket => sock).next_document
493
+ :limit => -1, :selector => selector, :socket => socket).next_document
474
494
  rescue OperationFailure => ex
475
495
  raise OperationFailure, "Database command '#{selector.keys.first}' failed: #{ex.message}"
476
496
  end