Over a million developers have joined DZone.

Being Lazy: Using the Ruby driver to connect to MongoDB

DZone's Guide to

Being Lazy: Using the Ruby driver to connect to MongoDB

· Database Zone ·
Free Resource

Running out of memory? Learn how Redis Enterprise enables large dataset analysis with the highest throughput and lowest latency while reducing costs over 75%! 

Curator's Note: The content of this post was originally written back in 2011 - please feel free to suggest constructive updates by commenting below.

One of my rake tasks failed with an interesting error this morning.

uncaught exception: error: { "$err" : "not master and slaveok=false", "code" : 13435 }

Qu’est-ce que c’est?

The problem is that I am connecting to a slave in a replica set and trying to execute a write operation that must happen on a master node. That’s because I was lazy and was executing command-line update queries, such as this one. I was being lazy. Shame on me.

        system "mongo #{db_host}:#{db_port}/#{db_name} -u #{db_user} -p#{db_password} --eval 'db.widgets.drop()'"

This is run in a Rake task. Lets replace this with some Ruby code, the way it’s ought to be.

        db = Mongo::Connection.new(db_host, db_port).db(db_name)
        db.authenticate(db_user, db_password) unless (db.user.nil? || db.user.blank?) 

It’s actually a lot cleaner, I am not sure why I was hung up on the command line thing. Unfortunately it doesn’t fix our problem. In a replica set we need to use a ReplSetConnection that will automatically load-balance requests and send writes to the master. It takes a list of hosts, something like

        db_connection = Mongo::ReplSetConnection.new(db_host_list).db(db_name)
        db_connection.authenticate(db_user, db_password)

Lets try to write something usable in all of our rake tasks. What I have is a YML file with the Heroku MongoHQ configuration. The first environment is a replica set, while the second is a single MongoDB.

         MONGOHQ_URL: "mongodb://heroku:password@replica.mongohq.com:12345/production-name"
         MONGOHQ_DATABASE: "db-name"
         MONGOHQ_HOST_LIST: "[['node0.replica.mongohq.com', 12345], ['node1.replica.mongohq.com', 12345]]"
         MONGOHQ_PASSWD: "password"
         MONGOHQ_USER: "heroku"
         config: &default
         MONGOHQ_URL: "mongodb://heroku:password@small.mongohq.com:12345/staging-name"

We can write a basic connect  method that picks up the right configuration, as a Rake task.

        namespace :mongohq do
         def heroku_config(env = Rails.env)
         @@config ||= YAML.load_file(Rails.root.join("config/heroku.yml")).symbolize_keys
         config_env = @@config[env.to_sym]
         raise "missing '#{env}' section in config/heroku.yml" if config_env.nil?
         def parse_mongohq_url(url)
         uri = URI.parse(url)
         [ uri, uri.path.gsub("/", "") ]
         # connect to a MongoDB
         def mongohq_connect(env = Rails.env)
         config = heroku_config(env)
         if ! config["MONGOHQ_HOST_LIST"].blank?
         mongohq_host_list = eval(config["MONGOHQ_HOST_LIST"])
         puts "[#{Time.now}] connecting to #{config["MONGOHQ_DATABASE"]} on #{eval(config["MONGOHQ_HOST_LIST"])}"
         db_connection = Mongo::ReplSetConnection.new(* mongohq_host_list).db(config["MONGOHQ_DATABASE"])
         db_connection.authenticate(config["MONGOHQ_USER"], config["MONGOHQ_PASSWD"])
         elsif ! config["MONGOHQ_URL"].blank?
         puts "[#{Time.now}] connecting to #{config["MONGOHQ_URL"]}"
         db, db_name = parse_mongohq_url(config["MONGOHQ_URL"])
         db_connection = Mongo::Connection.new(db.host, db.port).db(db_name)
         db_connection.authenticate(db.user, db.password) unless (db.user.nil? || db.user.blank?)
         raise "missing MONGOHQ_URL or MONGOHQ_HOST_LIST for #{env} environment"

Now, our rake tasks can call mongohq_connect(:production) or mongohq_connect(:staging) without having to worry about the kind of setup we have.


Running out of memory? Never run out of memory with Redis Enterprise databaseStart your free trial today.


Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}