A Rails Cloud Implementation Using MongoDB and Heroku

Learn how to use the non-relational MongoDB datastore as the backend for your Heroku-deployed Rails application. More importantly, learn why you would choose it over the likes of PostgreSQL.

When you have mastered how to use the Heroku platform to deploy and manage Rails web applications, you can choose which database or data store to use on the backend. Using a simple Rails app, Note Taker with Search (see the previous article in this series, "Deploying a Rails Application to Heroku"), I will demonstrate how to use the non-relational MongoDB datastore. Why MongoDB? Next to the PostgreSQL relational database (and occasionally the PostGIS geospatial/geolocation extensions), MongoDB is the data storage and management tool that I most often use (with the possible exception of the Sesame RDF data store).

The nosql-databases.org web site provides links to most non-relational data store systems (leaving out only RDF data stores). Check it out if you want to explore other options besides MongoDB. (My next article in this series covers another good NoSQL alternative: CouchDB.) In a nutshell, NoSQL data stores trade off immediate data consistency for greater data storage capacity and resiliency in the face of network partitioning (the "CAP Theorem"). The following two PDFs make a good case for why this tradeoff is sometimes a good design decision:

Save these papers for reference and, after working through the rest of this article, read through them for a better understanding of NoSQL data stores.

The minimal Heroku deployment option (5 megabytes of data storage and one compute unit) is free. For larger deployments, deploying on Heroku is definitely more expensive than managing your own Amazon EC2 instances. For many projects, however, the higher deployment costs can be more than offset by the reduction in development and administration costs. I do a lot of "bare metal" EC2 deployments, and I can attest to the cost and time savings of Heroku because "bare metal" EC2 deployments usually require me to:

Manage persistent data stores by attaching EC2 volumes before starting any services that require persistent disk storage (not so easy, and time consuming to set up)

Manage the automatic binding of EC2 instances to Elastic IP addresses

Bring more servers online quickly when I get traffic spikes (difficult to do)

Back up EBS volumes and database dumps to secure S3 storage (easy to do, but does take a little time to set up)

The use case I cover in this article (using MongoDB data stores) requires an external server, but you can find some third-party services that provide managed MongoDB services. I prefer to run MongoDB on my own managed EC2 instance. Note that if you decide to mix Rails deployments on Heroku with your own services running on EC2, you will lose some of the hassle-free admin and deployment advantages of using Heroku.

Replacing PostgreSQL with MongoDB

This section walks you through replacing PostgreSQL with MongoDB a simple Rails app: Note Taker with Search. The code download for this article contains all the examples in the directory note_taker_mongodb, and you should extract them and work along with me through every example. As you will see, the implementation is not as easy as simply using ActiveRecord and a relational database. However, it's worth the effort, because document data stores are a natural fit for handling structured data and they tend to scale well. You usually sacrifice immediate consistency, though (i.e., data store write operations may not be available for reading for a small time interval).

To start the PostgreSQL-to-MongoDB replacement, make a copy of the note_taker_pg directory and name it note_taker_mongodb. Then, edit the .gems file to specify the gems:

mongo
mongo_ext
mongo_record

You will also need to install these gems on your system. I then edited the environment.rb file making two changes. The first is to specify required gems:

config.gem 'mongo'
config.gem 'mongo_record'

The next thing you need to address is how you will manage allowed user names and passwords. I considered storing these in MongoDB, but that is probably not a feasible scenario for a real application. While great for storing structured data, document-centric data stores like MongoDB and CouchDB should often be used with relational databases, in my opinion. You use the relational database to store information that is not deeply structured and/or needs transaction support. So, at the risk of slightly complicating this example, let's use PostgreSQL to store user names and passwords and MongoDB to store notes.

Set up your MongoDB connections to use two environment variables. If these environment variables are not set, then the connection defaults to localhost and uses the default MongoDB port. Here is the setup that I added to the bottom of environment.rb:

I used four environment variables to specify the server information and the account name and password for authentication. This lets me easily switch between using a MongoDB on my laptop, using a commercial service like MongoHQ.com, or on one of my EC2 instances.

If multiple mongrels are used to run this web application (or, in the case of deploying to Heroku, multiple dynos), then each will create one connection. For each note, I collect all the words used in the note and add the list of words to the document.

The following code snippet shows how to use the word list in each note document to implement search:

I implemented a class method search that you will use in the Notes controller class. The class MongoRecord::Base provides a query API that is similar to that provided by ActiveRecord. The program looks for all note documents that contain words in the search query. Notice that the regular expression that prefix-matches words in the document. For example, if a document contains the word "container", then searches for "contain", "cont", etc. will all match.

Except for the changes I discussed in this section for MongoDB, this is a plain scaffold Rails application.

MongoDB on Your Own Server or a Hosted MongoDB Service?

A good alternative to running MongoDB on your own server is to avoid installing MongoDB all together and use a commercial service like MongoHQ.com.

I usually run MongoDB on my MacBook and on two of my servers. (I always keep MongoDB always running on my MacBook; it starts as a service.) I change the following environment variables to switch between available servers:

Wrapup

I enjoy doing "bare metal" deployments to leased servers or VPS solutions like Amazon EC2, RimuHosting, Slicehost, etc. That said, sometimes it simply does not make economic sense to create custom deployments and administer your own servers. In those cases, three options are available:

Use your own servers

Use cloud deployment servers

Use a hybrid of cloud services and your own servers

I hope this article provided you with the information necessary to make a wise deployment choice. You also have a few deployment and MongoDB tricks in your toolbox now.

About the Author

Mark Watson is a consultant living in the mountains of Central Arizona with his wife Carol and a very feisty Meyers Parrot. He specializes in web applications, text mining, and artificial intelligence development. He is the author of 16 books and writes both a technology blog and an artificial intelligence blog.

Advertiser Disclosure:
Some of the products that appear on this site are from companies from which QuinStreet receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. QuinStreet does not include all companies or all types of products available in the marketplace.