Class: Mongo::Connection

Inherits:
Object show all
Defined in:
lib/mongo/connection.rb

Overview

Instantiates and manages connections to MongoDB.

Constant Summary collapse

DEFAULT_PORT =
27017
STANDARD_HEADER_SIZE =
16
RESPONSE_HEADER_SIZE =
20
MONGODB_URI_MATCHER =
/(([.\w\d]+):([\w\d]+)@)?([.\w\d]+)(:([\w\d]+))?(\/([-\d\w]+))?/
MONGODB_URI_SPEC =
"mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/database]"
@@current_request_id =

Counter for generating unique request ids.

0

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(pair_or_host = nil, port = nil, options = {}) ⇒ Connection

Create a connection to MongoDB. Specify either one or a pair of servers, along with a maximum connection pool size and timeout.

If connecting to just one server, you may specify whether connection to slave is permitted. In all cases, the default host is “localhost” and the default port is 27017.

When specifying a pair, pair_or_host, is a hash with two keys: :left and :right. Each key maps to either

  • a server name, in which case port is 27017,

  • a port number, in which case the server is “localhost”, or

  • an array containing [server_name, port_number]

Note that there are a few issues when using connection pooling with Ruby 1.9 on Windows. These should be resolved in the next release.

Examples:

localhost, 27017

Connection.new

localhost, 27017

Connection.new("localhost")

localhost, 3000, max 5 connections, with max 5 seconds of wait time.

Connection.new("localhost", 3000, :pool_size => 5, :timeout => 5)

localhost, 3000, where this node may be a slave

Connection.new("localhost", 3000, :slave_ok => true)

DEPRECATED. To initialize a paired connection, use Connection.paired instead.

Connection.new({:left  => ["db1.example.com", 27017],
               :right => ["db2.example.com", 27017]})

DEPRECATED. To initialize a paired connection, use Connection.paired instead.

Connection.new({:left  => ["db1.example.com", 27017],
                :right => ["db2.example.com", 27017]}, nil,
                :pool_size => 20, :timeout => 5)

Parameters:

  • pair_or_host (String, Hash) (defaults to: nil)

    See explanation above.

  • port (Integer) (defaults to: nil)

    specify a port number here if only one host is being specified. Leave nil if specifying a pair of servers in pair_or_host.

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • :slave_ok (Boolean) — default: false

    Must be set to true when connecting to a single, slave node.

  • :logger (Logger, #debug) — default: nil

    Logger instance to receive driver operation log.

  • :auto_reconnect (Boolean)
  • :pool_size (Integer) — default: 1

    The maximum number of socket connections that can be opened to the database.

  • :timeout (Float) — default: 5.0

    When all of the connections to the pool are checked out, this is the number of seconds to wait for a new connection to be released before throwing an exception.

See Also:



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/mongo/connection.rb', line 92

def initialize(pair_or_host=nil, port=nil, options={})
  @auths        = []

  if block_given?
    @nodes = yield self
  else
    @nodes = format_pair(pair_or_host, port)
  end

  # Host and port of current master.
  @host = @port = nil

  # Lock for request ids.
  @id_lock = Mutex.new

  # Pool size and timeout.
  @size      = options[:pool_size] || 1
  @timeout   = options[:timeout]   || 5.0

  # Mutex for synchronizing pool access
  @connection_mutex = Mutex.new
  @safe_mutex = Mutex.new

  # Condition variable for signal and wait
  @queue = ConditionVariable.new

  @sockets      = []
  @checked_out  = []

  if options[:auto_reconnect]
    warn(":auto_reconnect is deprecated. see http://www.mongodb.org/display/DOCS/Replica+Pairs+in+Ruby")
  end

  # slave_ok can be true only if one node is specified
  @slave_ok = options[:slave_ok] && @nodes.length == 1
  @logger   = options[:logger] || nil
  @options  = options

  should_connect = options[:connect].nil? ? true : options[:connect]
  connect_to_master if should_connect
end

Instance Attribute Details

#authsObject (readonly)

Returns the value of attribute auths.



36
37
38
# File 'lib/mongo/connection.rb', line 36

def auths
  @auths
end

#checked_outObject (readonly)

Returns the value of attribute checked_out.



36
37
38
# File 'lib/mongo/connection.rb', line 36

def checked_out
  @checked_out
end

#hostObject (readonly)

Returns the value of attribute host.



36
37
38
# File 'lib/mongo/connection.rb', line 36

def host
  @host
end

#loggerObject (readonly)

Returns the value of attribute logger.



36
37
38
# File 'lib/mongo/connection.rb', line 36

def logger
  @logger
end

#nodesObject (readonly)

Returns the value of attribute nodes.



36
37
38
# File 'lib/mongo/connection.rb', line 36

def nodes
  @nodes
end

#portObject (readonly)

Returns the value of attribute port.



36
37
38
# File 'lib/mongo/connection.rb', line 36

def port
  @port
end

#sizeObject (readonly)

Returns the value of attribute size.



36
37
38
# File 'lib/mongo/connection.rb', line 36

def size
  @size
end

#socketsObject (readonly)

Returns the value of attribute sockets.



36
37
38
# File 'lib/mongo/connection.rb', line 36

def sockets
  @sockets
end

Class Method Details

.from_uri(uri, opts = {}) ⇒ Mongo::Connection

Initialize a connection to MongoDB using the MongoDB URI spec:

Parameters:

Returns:



168
169
170
171
172
# File 'lib/mongo/connection.rb', line 168

def self.from_uri(uri, opts={})
  new(nil, nil, opts) do |con|
    con.parse_uri(uri)
  end
end

.paired(nodes, opts = {}) ⇒ Mongo::Connection

Initialize a paired connection to MongoDB.

Examples:

Connection.new([["db1.example.com", 27017],
                ["db2.example.com", 27017]])
Connection.new([["db1.example.com", 27017],
                ["db2.example.com", 27017]],
                :pool_size => 20, :timeout => 5)

Parameters:

  • nodes (Array)

    An array of arrays, each of which specified a host and port.

  • opts (defaults to: {})

    Takes the same options as Connection.new

Returns:



149
150
151
152
153
154
155
156
157
158
# File 'lib/mongo/connection.rb', line 149

def self.paired(nodes, opts={})
  unless nodes.length == 2 && nodes.all? {|n| n.is_a? Array}
    raise MongoArgumentError, "Connection.paired requires that exactly two nodes be specified."
  end
  # Block returns an array, the first element being an array of nodes and the second an array
  # of authorizations for the database.
  new(nil, nil, opts) do |con|
    [con.pair_val_to_connection(nodes[0]), con.pair_val_to_connection(nodes[1])]
  end
end

Instance Method Details

#[](db_name) ⇒ Mongo::DB

Shortcut for returning a database. Use DB#db to accept options.

Parameters:

  • db_name (String)

    a valid database name.

Returns:



267
268
269
# File 'lib/mongo/connection.rb', line 267

def [](db_name)
  DB.new(db_name, self, :logger => @logger)
end

#add_auth(db_name, username, password) ⇒ Hash

Save an authentication to this connection. When connecting, the connection will attempt to re-authenticate on every db specificed in the list of auths.

Parameters:

  • db_name (String)
  • username (String)
  • password (String)

Returns:

  • (Hash)

    a hash representing the authentication just added.



198
199
200
201
202
203
204
205
206
# File 'lib/mongo/connection.rb', line 198

def add_auth(db_name, username, password)
  remove_auth(db_name)
  auth = {}
  auth['db_name']  = db_name
  auth['username'] = username
  auth['password'] = password
  @auths << auth
  auth
end

#apply_saved_authenticationBoolean

Apply each of the saved database authentications.

Returns:

  • (Boolean)

    returns true if authentications exist and succeeed, false if none exists.

Raises:



181
182
183
184
185
186
187
# File 'lib/mongo/connection.rb', line 181

def apply_saved_authentication
  return false if @auths.empty?
  @auths.each do |auth|
    self[auth['db_name']].authenticate(auth['username'], auth['password'], false)
  end
  true
end

#clear_authstrue

Remove all authenication information stored in this connection.

Returns:

  • (true)

    this operation return true because it always succeeds.



225
226
227
228
# File 'lib/mongo/connection.rb', line 225

def clear_auths
  @auths = []
  true
end

#closeObject

Close the connection to the database.



449
450
451
452
453
454
455
456
# File 'lib/mongo/connection.rb', line 449

def close
  @sockets.each do |sock|
    sock.close
  end
  @host = @port = nil
  @sockets.clear
  @checked_out.clear
end

#connect_to_masterObject

Create a new socket and attempt to connect to master. If successful, sets host and port to master and returns the socket.

Raises:



410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
# File 'lib/mongo/connection.rb', line 410

def connect_to_master
  close
  @host = @port = nil
  for node_pair in @nodes
    host, port = *node_pair
    begin
      socket = TCPSocket.new(host, port)
      socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)

      # If we're connected to master, set the @host and @port
      result = self['admin'].command({:ismaster => 1}, false, false, socket)
      if result['ok'] == 1 && ((is_master = result['ismaster'] == 1) || @slave_ok)
        @host, @port = host, port
        apply_saved_authentication
      end

      # Note: slave_ok can be true only when connecting to a single node.
      if @nodes.length == 1 && !is_master && !@slave_ok
        raise ConfigurationError, "Trying to connect directly to slave; " +
          "if this is what you want, specify :slave_ok => true."
      end

      break if is_master || @slave_ok
    rescue SocketError, SystemCallError, IOError => ex
      socket.close if socket
      close
      false
    end
  end
  raise ConnectionFailure, "failed to connect to any given host:port" unless socket
end

#connected?Boolean

Are we connected to MongoDB? This is determined by checking whether host and port have values, since they’re set to nil on calls to #close.

Returns:

  • (Boolean)


444
445
446
# File 'lib/mongo/connection.rb', line 444

def connected?
  @host && @port
end

#copy_database(from, to, from_host = "localhost") ⇒ Object

Copy the database from on the local server to to on the specified host. host defaults to ‘localhost’ if no value is provided.

Parameters:

  • from (String)

    name of the database to copy from.

  • to (String)

    name of the database to copy to.

  • from_host (String) (defaults to: "localhost")

    host of the ‘from’ database.



284
285
286
287
288
289
290
291
# File 'lib/mongo/connection.rb', line 284

def copy_database(from, to, from_host="localhost")
  oh = OrderedHash.new
  oh[:copydb]   = 1
  oh[:fromhost] = from_host
  oh[:fromdb]   = from
  oh[:todb]     = to
  self["admin"].command(oh, false, true)
end

#database_infoHash

Return a hash with all database names and their respective sizes on disk.

Returns:

  • (Hash)


234
235
236
237
238
239
# File 'lib/mongo/connection.rb', line 234

def database_info
  doc = self['admin'].command({:listDatabases => 1}, false, true)
  returning({}) do |info|
    doc['databases'].each { |db| info[db['name']] = db['sizeOnDisk'].to_i }
  end
end

#database_namesArray

Return an array of database names.

Returns:

  • (Array)


244
245
246
# File 'lib/mongo/connection.rb', line 244

def database_names
  database_info.keys
end

#db(db_name, options = {}) ⇒ Mongo::DB

Return a database with the given name. See DB#new for valid options hash parameters.

Parameters:

  • db_name (String)

    a valid database name.

Returns:



256
257
258
# File 'lib/mongo/connection.rb', line 256

def db(db_name, options={})
  DB.new(db_name, self, options.merge(:logger => @logger))
end

#drop_database(name) ⇒ Object

Drop a database.

Parameters:

  • name (String)

    name of an existing database.



274
275
276
# File 'lib/mongo/connection.rb', line 274

def drop_database(name)
  self[name].command(:dropDatabase => 1)
end

#format_pair(pair_or_host, port) ⇒ Object

Returns an array of host-port pairs.



463
464
465
466
467
468
469
470
471
472
473
474
475
476
# File 'lib/mongo/connection.rb', line 463

def format_pair(pair_or_host, port)
  case pair_or_host
    when String
      [[pair_or_host, port ? port.to_i : DEFAULT_PORT]]
    when Hash
     warn "Initializing a paired connection with Connection.new is deprecated. Use Connection.pair instead."
     connections = []
     connections << pair_val_to_connection(pair_or_host[:left])
     connections << pair_val_to_connection(pair_or_host[:right])
     connections
    when nil
      [['localhost', DEFAULT_PORT]]
  end
end

#get_request_idObject

Increment and return the next available request id.

return [Integer]



296
297
298
299
300
301
302
# File 'lib/mongo/connection.rb', line 296

def get_request_id
  request_id = ''
  @id_lock.synchronize do
    request_id = @@current_request_id += 1
  end
  request_id
end

#pair_val_to_connection(a) ⇒ Object

Convert an argument containing a host name string and a port number integer into a [host, port] pair array.



482
483
484
485
486
487
488
489
490
491
492
493
# File 'lib/mongo/connection.rb', line 482

def pair_val_to_connection(a)
  case a
  when nil
    ['localhost', DEFAULT_PORT]
  when String
    [a, DEFAULT_PORT]
  when Integer
    ['localhost', a]
  when Array
    a
  end
end

#parse_uri(string) ⇒ Object

Parse a MongoDB URI. This method is used by Connection.from_uri. Returns an array of nodes and an array of db authorizations, if applicable.



499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
# File 'lib/mongo/connection.rb', line 499

def parse_uri(string)
  if string =~ /^mongodb:\/\//
    string = string[10..-1]
  else
    raise MongoArgumentError, "MongoDB URI must match this spec: #{MONGODB_URI_SPEC}"
  end

  nodes = []
  auths = []
  specs = string.split(',')
  specs.each do |spec|
    matches  = MONGODB_URI_MATCHER.match(spec)
    if !matches
      raise MongoArgumentError, "MongoDB URI must match this spec: #{MONGODB_URI_SPEC}"
    end

    uname = matches[2]
    pwd   = matches[3]
    host  = matches[4]
    port  = matches[6] || DEFAULT_PORT
    if !(port.to_s =~ /^\d+$/)
      raise MongoArgumentError, "Invalid port #{port}; port must be specified as digits."
    end
    port  = port.to_i
    db    = matches[8]

    if (uname || pwd || db) && !(uname && pwd && db)
      raise MongoArgumentError, "MongoDB URI must include all three of username, password, " +
        "and db if any one of these is specified."
    else
      add_auth(db, uname, pwd)
    end

    nodes << [host, port]
  end

  nodes
end

#receive_message(operation, message, log_message = nil, socket = nil) ⇒ Array

Sends a message to the database and waits for the response.

Parameters:

  • operation (Integer)

    a MongoDB opcode.

  • message (ByteBuffer)

    a message to send to the database.

  • log_message (String) (defaults to: nil)

    text version of message for logging.

  • socket (Socket) (defaults to: nil)

    a socket to use in lieu of checking out a new one.

Returns:

  • (Array)

    An array whose indexes include [0] documents returned, [1] number of document received, and [3] a cursor_id.



389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
# File 'lib/mongo/connection.rb', line 389

def receive_message(operation, message, log_message=nil, socket=nil)
  packed_message = add_message_headers(operation, message).to_s
  @logger.debug("  MONGODB #{log_message || message}") if @logger
  begin
    sock = socket || checkout

    result = ''
    @safe_mutex.synchronize do
      send_message_on_socket(packed_message, sock)
      result = receive(sock)
    end
  ensure
    checkin(sock)
  end
  result
end

#remove_auth(db_name) ⇒ Boolean

Remove a saved authentication for this connection.

Parameters:

  • db_name (String)

Returns:

  • (Boolean)


213
214
215
216
217
218
219
220
# File 'lib/mongo/connection.rb', line 213

def remove_auth(db_name)
  return unless @auths
  if @auths.reject! { |a| a['db_name'] == db_name }
    true
  else
    false
  end
end

#send_message(operation, message, log_message = nil) ⇒ True

Send a message to MongoDB, adding the necessary headers.

Parameters:

  • operation (Integer)

    a MongoDB opcode.

  • message (ByteBuffer)

    a message to send to the database.

  • log_message (String) (defaults to: nil)

    text version of message for logging.

Returns:

  • (True)


336
337
338
339
340
341
342
343
344
345
# File 'lib/mongo/connection.rb', line 336

def send_message(operation, message, log_message=nil)
  @logger.debug("  MONGODB #{log_message || message}") if @logger
  begin
    packed_message = add_message_headers(operation, message).to_s
    socket = checkout
    send_message_on_socket(packed_message, socket)
  ensure
    checkin(socket)
  end
end

#send_message_with_safe_check(operation, message, db_name, log_message = nil) ⇒ Array

Sends a message to the database, waits for a response, and raises an exception if the operation has failed.

Parameters:

  • operation (Integer)

    a MongoDB opcode.

  • message (ByteBuffer)

    a message to send to the database.

  • db_name (String)

    the name of the database. used on call to get_last_error.

  • log_message (String) (defaults to: nil)

    text version of message for logging.

Returns:

  • (Array)

    An array whose indexes include [0] documents returned, [1] number of document received, and [3] a cursor_id.



358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
# File 'lib/mongo/connection.rb', line 358

def send_message_with_safe_check(operation, message, db_name, log_message=nil)
  message_with_headers = add_message_headers(operation, message)
  message_with_check   = last_error_message(db_name)
  @logger.debug("  MONGODB #{log_message || message}") if @logger
  begin
    sock = checkout
    packed_message = message_with_headers.append!(message_with_check).to_s
    docs = num_received = cursor_id = ''
    @safe_mutex.synchronize do
      send_message_on_socket(packed_message, sock)
      docs, num_received, cursor_id = receive(sock)
    end
  ensure
    checkin(sock)
  end
  if num_received == 1 && error = docs[0]['err']
    raise Mongo::OperationFailure, error
  end
  [docs, num_received, cursor_id]
end

#server_infoHash

Get the build information for the current connection.

Returns:

  • (Hash)


307
308
309
# File 'lib/mongo/connection.rb', line 307

def server_info
  self["admin"].command({:buildinfo => 1}, false, true)
end

#server_versionMongo::ServerVersion

Get the build version of the current server.

Returns:



315
316
317
# File 'lib/mongo/connection.rb', line 315

def server_version
  ServerVersion.new(server_info["version"])
end

#slave_ok?Boolean

Is it okay to connect to a slave?

Returns:

  • (Boolean)


322
323
324
# File 'lib/mongo/connection.rb', line 322

def slave_ok?
  @slave_ok
end