Blog

Feature-flipping with node.js and MongoDB

July 25, 2012 9:08 am / by / 1 comment

Continuous Deployment means releasing newly-committed code to production very frequently after running an extensive suite of automated tests. However, there are inevitably differences between the test environment and production. Many organizations also want to have someone do at least a cursory examination of the new functionality before it goes truly ‘live. Feature flipping is one way to address these needs.

Feature flipping means adding code to your application to flip between the old version of a particular feature and the new feature based on a database parameter. This also enables developers to work on new features without having to create branches. There are many ways to determine if a particular user will see the new version or the old version of a feature – one of the most common and most coarse-grained is to show the new version of the feature to internal users (ie employees) while external users continue to see the old version. As your site grows in popularity, you may then wish to roll out the new feature to a random % of users, to a % of users by geography, by user type, etc. A good implementation of feature-flipping will allow you to build out these different methods of feature rollout as they become necessary.

We continuously deploy StriderApp.com – our hosted continuous deployment platform for node.js and Python – so in relatively short order we needed feature-flipping ourselves. Matheus Mendes from Yahoo has written feature-flipper.js which looks like a great option for node.js but didn’t quite meet our needs, so we decided just to write what we needed. (We are aiming to package up our code as an npm module at some point soon.)

Our goals for feature-flipping were to be able to quickly add in feature-flipping code for new features, to have useful defaults, and to abstract away the logic of whether the feature is on or off so that we can add more complex methods in the future. We decided that the most useful default for us would be for a feature to be ‘on’ for admin (ie internal) users. This is both our most common test scenario at this point and also meant that when we push code out to production, the feature won’t be live for our customers until we actively make a change in the production database.

Here’s an example of the library in action.

Controller

First, the javascript controller checks to see if the feature should be on and passes that parameter to the template:

var feature = require('feature');
 
feature("deactivate", req.user, function(err, is_enabled) {
  res.render('project_config', {repo: trepo, deactivate_enabled: deactivate_enabled});
});

Jade template

Then in the jade template, we just have an ‘if’ statement around the HTML in question:

if (deactivate_enabled)
  #deactivate_project.span8
    strong Deactivate / Activate Project
    p Temporarily turn off continuous integration and deployment. Tests and deployments will not be triggered by commits when a project is deactivated.
    a.#deactivate.btn.btn-danger Deactivate Project

Feature Module

In the feature module, we first check to see if the user is admin, in which case we don’t even need to look at the feature record:

// current logic - if user is admin, feature is ALWAYS on, whether it is in the db or not
if (self.user_obj.account_level != undefined && self.user_obj.account_level > 0) {
  console.debug("Feature.is_enabled() - %s is admin. Feature is enabled.",
      self.feature_name,self.user_obj.email);
  return callback(null, true);
}

If the user is NOT admin, we then retrieve the feature record from the database:

Step(
  function findFeature() {
    FeatureModel.findOne({name: self.feature_name}, this);
  },
  function foundFeature(err, feature_obj) {
 
    if (err) throw err;

If ‘global_enabled’ is true, then the feature is enabled:

if (feature_obj != undefined && feature_obj.global_enabled) {
  console.debug("Feature.is_enabled() - global_enabled flag enabled for feature '%s'.", self.feature_name, self.user_obj.email);
  return callback(null, true);
}

If the user is in the list of users for this feature, then the feature is enabled:

if (feature_obj != undefined && feature_obj.users_enabled !== undefined
  && typeof(feature_obj.users_enabled) === 'object'
  && _.indexOf(feature_obj.users_enabled, self.user_obj._id) !== -1) {
    console.debug("Feature.is_enabled() - user %s is in users_enabled list for feature '%s'.",
      self.user_obj.email, self.feature_name);
  return callback(null, true);
}

If none of the above were true, then the feature is disabled:

console.debug("Feature.is_enabled() - user is not admin, feature is not global enabled, user is not in user list. Default to disabled.");
return callback(null, false);

And that’s it.


Next Steps:

In an ideal world, we would not need to add conditionals to both the controller and to the template. Also, we use client-side javascript (using the Backbone framework as well as the Apres framework) for much of our front-end and would like to have an easy way to write feature-flipping code on the client that does not require an additional request nor modifying the node.js controller code. One way to do this would be to write middleware which loads all of the feature-flip flags for the currently logged-in user and sends this to the jade template which ensures that it is always available as a client-side global variable. This isn’t a particularly scalable solution however so for now we are sticking with the current process.

Have you come up with a way to implement feature-flipping that minimizes changes to both server and client-side code? Share in the comments.


Stephen Bronstein is a co-founder of BeyondFog, the creators of StriderCD.com, a hosted continuous integration and deployment platform for Python and node.js.

 

Testing Strategies for Continuous Deployment of a Node.JS & MongoDB app to Heroku/MongoLab

May 14, 2012 8:24 pm / by / 3 comments

Poang (github) is a very basic Node.js/MongoDB app that will run on Heroku & MongoLab. This app demonstrates a few of the ways that we write tests in Node.js for Strider. (Strider is our hosted continuous integration and deployment platform for Node.js and Python. Learn more at StriderCD.com)

Poang was built using the Express framework. It uses Everyauth for local authentication, Mongoose-Auth to connect Everyauth to MongoDB (and Mongoose as the ODM) for account persistence, and Connect-Mongo as a session store. Much of the code in app.js was generated by Express and all of the code in auth.js after the Comment schema definition is straight from the Mongoose-Auth docs.

For testing, Poang uses the Mocha test framework, should for assertions, Sinon.JS for mocks & stubs, and Zombie.js for lightweight integration testing.

Unit Testing

It can be challenging to create simple unit tests with a basic web/database application because so much of the code relates to database reads and writes. Poang didn’t have any functions that were simple enough, so I added a function for this purpose – timesTwo(). As you might expect, timesTwo() takes a number as an input and returns that number…times two.

Here is the unit test for timesTwo():

describe('index', function() {
  describe('#timesTwo()', function() {
    it('should multiply by two', function() {
      var x = 5;
      var xTimesTwo = index.timesTwo(x);
      xTimesTwo.should.equal(10);
    });
  });
});

The first three lines of the test are Mocha setup – first we specify the file, then the function, then the behavior that we expect. (I will exclude these lines in subsequent code snippets)

The main body of the test is very simple – it executes timesTwo() with an input of 5, and then uses should.js to verify that the output is 10.

Using a Sinon spy to verify middleware functionality

Next, I wanted to verify that my middleware function requires user login to do anything in the app. I created ‘mock_req’ and ‘mock_res’ objects that I could pass to the middleware. I used Sinon’s spy function to watch the redirect function within the mock response object, and then called the middleware with mock_req, mock_res, and the ‘next’ function (which in this case just sets the http status to 200).

var mock_req = {session: {}};
var mock_res = {redirect: function() {}, end: function() {}};
 
sinon.spy(mock_res,"redirect");
 
middleware.require_auth_browser(mock_req, mock_res, function() {
  mock_res.statusCode = 200;
});

The middleware determines if the user is logged in by checking to see if there is a user object in the request object. Since ‘mock_req’ didn’t include a user object, the middleware should return a status code of 401 (Authorization Required) and should redirect to ‘/login’:

mock_res.statusCode.should.eql(401);
mock_res.redirect.getCall(0).args[0].should.equal('/login');

I then created a new ‘mock_req’ which includes an empty user object and again call the middleware function. This time it should call the ‘next’ function:

mock_req = {user: {}};
middleware.require_auth_browser(mock_req, mock_res, function() { 
  mock_res.statusCode = 200;
});
 
mock_res.statusCode.should.eql(200);

Zombie for lightweight browser integration testing

Next I wanted to add a few lightweight integration tests using Zombie.js. Since Zombie is a headless browser, for these tests we need to startup an instance of Poang in the ‘before’ block, using a random number between 4096 and 65535 for the server port. (Ideally the code would also check to make sure that port is open before listening on it)

var TEST_PORT = Math.floor(Math.random()*61439 + 4096);
before(function() {
   var server = app.init(config);
 
   // should check to see if something is listening on the port first
   server.listen(TEST_PORT);
   console.log('Server is listening on port %s', TEST_PORT);
 });

The first Zombie test just checks to make sure the front page loads. As you can see, it’s very little code:

var browser = new zombie();
browser.visit(base_url, function () {
  browser.success.should.be.ok;
  if (browser.error) {
    console.dir('errors reported:', browser.errors);
  }
done();
});

The next test checks the title of the front page. The only difference here is that the ‘browser.success’ line was replaced with the following:

browser.text("title").should.eql("Poang");

The last test goes through the registration process and verifies that Poang then redirects to ‘/’:

var browser = new zombie();
browser.visit(base_url + "register", function () {
  browser.query("#register").should.be.ok;
  // Fill email, password and submit form
  browser.
    fill("email", test_email).
    fill("password", "secret").
    pressButton("register", function() {
 
      // Form submitted, new page loaded.
      browser.success.should.be.ok;
      browser.location.pathname.should.eql("/");
      done();
    });    
});

Because the last test attempts to register with the same information every time, we need to delete at least this record from the database or else the test will fail the next time around. The cleanest thing to do is to drop the entire test db. This is done in the after() function:

after(function(done) {
  var db_uri = process.env.MONGOLAB_URI || process.env.MONGODB_URI || config.default_db_uri;
  mongoose.connect(db_uri);
 
  // drop database
  mongoose.connection.db.executeDbCommand( {dropDatabase:1}, function() {
    console.log("Dropped test database");
    done();     
  });
})

npm test

Strider will work with any test framework, assertion libraries, etc. The only requirement is that Strider needs to be able to start a test run by executing ‘npm test’. To do this, you need to add a line to package.json with your desired test framework and any necessary arguments. Strider works best with results in TAP so we have added that as an argument:

"scripts": { "test": "mocha -R tap" }

After I added the Zombie tests, when I ran ‘npm test’, Mocha failed on the first Zombie test with the following error:

Error: global leaks detected: o, section, data, m, k, i

To resolve this, I added those globals to the test command, which now looks like this:

"test": "mocha -R tap --globals o,section,data,m,k,i"

Another way to resolve this would be to add –ignore-leaks at the end of the mocha command, however, by instead allowing only these particular globals, Mocha will fail again if any new global variables appear in the code.

Test Driven Development (TDD) and the importance of done()

When I first wrote these Zombie tests, they were all succeeding. I eventually determined that one of them should be failing, and couldn’t figure out why until Niall reminded me that I needed to specify done as the callback and then invoke done() at the completion of each test. Had I been doing proper TDD I would have discovered this right at the start!

Mongod configuration – no preallocate

After I started dropping the test database after each run, the test run would start failing pretty regularly due to one or more of the tests timing out. The reason for this is that MongoDB pre-allocates space when first creating a new database and that pre-allocation can take a while. To avoid pre-allocation, start your dev MongoDB instance with the following arguments:

mongod --noprealloc --nojournal

Running Poang on Heroku/MongoLab

Procfile

Heroku requires a Procfile that specifies how to start the web application. Poang’s Procfile is one line:

web: node app.js

Environment Variables for Database Connectivity

Heroku provides UNIX environment variables to connect to MongoLab. Strider also provides UNIX environment variables for database connectivity. In order to work with both, Poang first checks to see if the Heroku-provided environment variable is present, if it isn’t there, it then looks for the Strider-provided environment variable, and if neither is present, it uses the local dev database as specified in the config file. Here’s the relevant code:

var db_uri=process.env.MONGOLAB_URI || process.env.MONGODB_URI || config.default_db_uri;

Add MongoLab module to Heroku via Heroku Toolbelt

After you create your application on Heroku, you need to add the MongoLab add-on to the application before it will run. The mongolab:starter plan is free.

A) If you are using Strider to test and deploy Poang, you will need to run the following command from your command line (using the Heroku Toolbelt) after Strider creates the Heroku app:

heroku addons:add mongolab:starter --app [your_app_name]

B) If you created the heroku app yourself using the Heroku Toolbelt and you are in the local directory for that app, you may not need to specify the app name:

heroku addons:add mongolab:starter

That’s it, your app should now be up and running on Heroku and MongoLab!


Stephen Bronstein is a co-founder of BeyondFog, the creators of StriderCD.com, a hosted continuous integration and deployment platform for Python and node.js.

 

7 Tips for Python & Django on Heroku, Testing, and more from a Ruby on Rails developer

May 3, 2012 10:41 am / by / 8 comments

While Niall has been coding in Python for years and has worked extensively with both Django and Pyramid, I am new to Python. We have been building Strider primarily in node.js (Strider is our hosted continuous deployment platform for Python and node.js – learn more at StriderCD.com), and in the past I have done some coding in Rails and further back in both Perl and Java, but no Python. To kick things off with Python, I decided to build a basic Django project. The project is called Klingsbo (github).

I started by walking through the excellent Django Project tutorial. The tutorial guides you through the process of creating a very basic project with one app – polls. The tutorial does a great job getting a new user up and running with Django in short order. There were, however, a few additional things that I needed to implement for this sample project to meet my needs.

1. Relative Paths for Templates

One thing that I found surprising in the tutorial was the use of absolute paths for the templates. I can’t imagine why anyone would want to use absolute paths in a project configuration. After searching around a bit, I found the following code to put the templates directory inside the project directory (ie the same directory as settings.py):

TEMPLATE_DIRS = (
  os.path.join(SITE_ROOT, 'templates')
)

2. Testing

There is nothing in the tutorial about automated testing with Django. After some more searching, I managed to find this excellent pair of blog posts by Daniel Lindsley. You can find them here: part one & part 2. Daniel goes further than I did in my project; if you are building a Django project yourself, I would recommend walking through the entirety of both of his posts.

3. Initial data

The tests used fixtures for sample data and I also wanted to load some initial data when the project is first setup. Django will let you provide initial data via fixtures (just like the test data) or via SQL. I opted for fixtures. I created a json file called ‘initial_data.json’ which I put inside the fixtures directory of my app. (Note that this sample data will be re-added every time you run syncdb so you should not put data in here that will be changing. If you want to load data once but not upon subsequent execution of ‘syncdb’, then you should name the file something else and explicitly run ‘manage.py loaddata [filename]’ the first time around).

See the Django documentation on Providing initial data for models for further details.

4. Non-interactive setup

By default, the first time you run ‘manage.py syncdb’, it will ask you interactively to create a superuser. In order for Strider to be able to setup the project programmatically, I needed to turn off the interactivity, but yet still create a superuser. There are a couple of ways to do this. I came across this blog post which recommends dumping the data from the db to json and then adding the superuser json section to an ‘initial_data.json’ file. This is how I am creating the superuser in Klingsbo. (Note: when I tried to put the ‘initial_data.json’ file in the klingsbo (interior) project folder, it didn’t load, so I ended up appending the superuser initial data to the initial data for the polls, ie the initial_data.json file located in ‘polls/fixtures’. There is most likely a better way to do this.)

Another way to create a superuser non-interactively is via the following sequence of commands. For a production app, this might be preferable to creating the superuser via the initial data load:

python manage.py syncdb
python django-admin.py createsuperuser --username admin --email admin@exmaple.com
python django-admin.py changepassword --username admin [password]

5. Data migrations

I was a bit surprised that Django does not have built-in support for data migrations (as Rails does) but it was easy enough to get up and running with South, which seems to be the most popular of the Django migration alternatives. Since I had already created a project and run ‘syncdb’, I followed their instructions on Converting an Existing App. Basically I just had to add ‘south’ to my list of installed apps and then execute the following command:

python manage.py convert_to_south polls

And that’s it. In the future, when I change a model, I will need to run this command before I commit to create a new migration:

python manage.py schemamigration [model] --auto

And then of course after pushing that commit, I will need to run ‘migrate’ on my production server:

python manage.py migrate

6. Deploying to Heroku

Next, I wanted to deploy my project to Heroku. Heroku has a great guide to running Django projects that is very detailed (Getting Started with Django on Heroku/Cedar). The key steps are that you need to install psycopg2 (even if you are using sqlite locally) as well as gunicorn, and you need to create a Procfile. My Procfile looks like this:

web: gunicorn klingsbo.wsgi -b 0.0.0.0:$PORT

Once you have those three items in place, you can then deploy your app to Heroku from the command line (or via Strider of course). After you have deployed your project, you will need to execute two commands via the Heroku toolbelt (cli):

1. heroku run python manage.py syncdb
2. heroku run python manage.py migrate

That’s it! Heroku will add a bunch of stuff to your settings.py to handle the production db configuration so you don’t need to worry about that.

For more details on configuring a Django project for Heroku, see Getting Started with Django on Heroku/Cedar.

7. Converting from sqlite to PostgreSQL

As I noted above, the project can use sqlite locally while Heroku will configure and run PostgreSQL in production. However, I needed to configure the project to run PostgreSQL locally so that I could confirm that Strider would work properly using PostgreSQL as the database. For this I had to change the settings.py DATABASE ENGINE to ‘django.db.backends.postgresql_psycopg2’, and I had to specify a username and password for the database. I also had to explicitly specify ‘localhost’ as the HOST even though the comment says that localhost is the default.

Lastly, one of my tests started failing after I cut over to PostgreSQL. I eventually determined that this is because sqlite always returns results from a table ordered by primary key whereas PostgreSQL does not unless explicitly ordered. I added the following to my polls model to fix this:

class Meta:     
  ordering = ['id']

[UPDATE: As Erik rightly points out in the comments, the reason I needed to add this ordering clause was because in my test, I am querying the db and storing the results in an array (via the ORM), and then checking the elements in the array *by their order in the array* to confirm that values in the other fields of each row are correct. In a real-world application, it is unlikely that the order of the results would matter in this way, so you should not have a test like this, in which case you would not need to add this ordering to the model which will order the results for each and every query. So, in summary, only add this ordering if you in fact really need it for at least many if not most of your queries.]

And that’s it! Do you have a tip for new users that you didn’t see on this list? Add it in the comments.


Stephen Bronstein is a co-founder of BeyondFog, the creators of StriderCD.com, an open source continuous delivery platform for Python and node.js.

 

Slides from Node.js SF Meetup

April 18, 2012 9:27 pm / by / 1 comment

Steve and I were at the Node.js SF meetup this evening. I gave a talk on “Testing in Node.js for Continuous Deployment Win”. It was a lot of fun. You can download the slides to my talk [PDF].

If you are interested in an invite for our hosted continuous deployment product, enter your email on the form at StriderApp.com. Thanks!

 

Websockets and More: 20 awesome node.js npm modules we use every day

April 17, 2012 8:17 am / by / 16 comments

Strider, our continuous deployment platform for node.js and Python, is in fact written in node.js and Python and it was the first project that we started testing and deploying via Strider. Yes, we are in fact continuously deploying Strider via Strider itself. It’s our own small attempt to bootstrap the Singularity. (Continuous deployment means that we automatically test after every commit, and then immediately deploy that code to production if the tests pass.)

So anyway. We use node.js for our web application server (along with backbone.js on the client), and the workers (which run the actual tests) are written in Python. While node.js is best known for its real-time capabilities with websockets, it is very easy at this point to build your entire web app in node.js. Node.js has a great package manager in npm and a strong ecosystem of modules. One of our biggest challenges was figuring out which modules would work best. Here’s what we chose:

Web Framework: Express is our node.js web framework of choice. Express is built on top of Connect, the definitive middleware framework for node.js.

Templating: Jade is the favored templating engine of the Express.js core team and it is also our favorite. After using jade for a while, it now actually pains me whenever I need to fully type out html. The amount of typing that jade eliminates dwarfs that involved in the recent (ongoing?) semicolon debate.

Auth: Everyauth integrates tightly with Express and provides excellent authentication and authorization support both internally and against a long list of external services. To date, we have only used it for internal auth and for oauth against github.

Database: We use MongoDB as our primary datastore. To connect to MongoDB, we use Mongoose as well as Mongoose-Auth for Everyauth/MongoDB integration. This combination has worked well although it is less than ideal that every data object comes out of the auth module.

Session Store: We use connect-mongo as our session store.

Email: We send outgoing email via Sendgrid and took their recommendation to use the node_mailer module. It hasn’t been updated in a while but has worked well for us so far.

Control-Flow: Step has proven very useful in certain situations. For example, since MongoDB has limited joins within the db, on occasion we may need to execute one query, and then upon receiving the result, execute a sequence of additional queries to perform a ‘software join’. Step significantly simplified the code required for this flow.

Testing: We use a number of modules for testing, including mock-request for mocking req/res pairs, Sinon.JS for other stubs and mocks, sanboxed-module for dependency injection, and should.js for assertions. Niall’s recent blog post talks about the testing modules in a bit more depth, and in a future post we will walk through an example app that uses all of them.

Logging: We send our logs to loggly and store them locally. We patched console.log/debug/warn/error to go into Winston at equiv levels.

HTTP Request: Request is the gold standard HTTP client for node.

Password Hashing: Bcrypt of course.

Websockets: Everyone uses socket.io and for good reason.

String Validation: We use node-validator to validate email addresses and whatnot.

Other Libraries:

  • Humane Dates is very helpful for more readable dates – “2 hours ago” or “5 days ago” instead of just the raw date.
  • Nibbler encodes and decodes our invite codes to and from base32.

That’s it for us. Have a favorite node.js module that isn’t on our list? Let us know in the comments.

 

MongoDB – How To Benchmark and Test Performance Using mongo-perf

January 16, 2012 9:36 pm / by / 3 comments

Benchmarking MongoDB

Here at BeyondFog, we are very interested in how MongoDB performs – and how to make it faster! While performance testing in general is a tricky thing, depending heavily upon the details of your application, some scenarios are easy enough to benchmark. Imagine you wished to compare MongoDB performance on the EXT4FS filesystem with MongoDB performance on the XFS filesystem. A tool which can generate reproducible load on the database and record response time is perfect for this situation. This kind of tool is called a synthetic benchmark.

Mongo-Perf

Mongo Perf Graph

Fortunately, such a synthetic benchmarking tool already exists for MongoDB in the form of 10gen’s mongo-perf utility. Less fortunately, this tool is not very well documented and can be a little tricky to get running. This article hopes to help you set it up.

mongo-perf comprises a C++ program and a couple of Python harnesses. The C++ program needs to be linked against the mongoclient library, which is not included in standard MongoDB binary distributions. For this reason, you’ll need to build MongoDB from source, enabling the mongoclient headers and library binaries. In this blog post, we will provide instructions for doing this under Mac OS X 10.7 (Lion) and Ubuntu 11.10 (Oneric).

Getting Mongo-Perf Dependencies for Mac OS X 10.7

There are three major dependencies required to build MongoDB on OS X: XCode, the Boost family of C++ libraries and the SCons build tool. XCode is available from Apple. Both Boost and SCons are installable via MacPorts and Homebrew. It is your choice which package manager to use.

Option A – Mac Ports

To install MacPorts simply download the Lion installer and run it. For more detailed instructions see the MacPorts install page. Once you have MacPorts installed, you can simply run:

port install boost scons

Option B – Homebrew

For complete Homebrew install instructions, see this Wiki page. The short version is to run the command:

/usr/bin/ruby -e "$(curl -fsSL https://raw.github.com/gist/323731)"

Once you have Homebrew, you can install Boost and SCons with the commands:

brew install boost; brew install scons

Getting Mongo-Perf Dependencies for Ubuntu 11.10

On Ubuntu, you can simply use apt-get to install the requisite packages. From 10gen’s Linux build page:

apt-get -y install tcsh git-core scons g++
apt-get -y install libpcre++-dev libboost-dev libreadline-dev
apt-get -y install libboost-program-options-dev libboost-thread-dev libboost-filesystem-dev libboost-date-time-dev

Building MongoDB

Now that you have the various MongoDB dependencies on your system, you can build MongoDB itself. The first step is checking out the source code from Github:

git clone https://github.com/mongodb/mongo.git

Note: You could just as easily use a source tarball from http://www.mongodb.org/downloads.

Now you should be able to cd into the root of the source directory and build it. Before building it, you should choose a prefix. We recommend /opt/local as it simplifies building mongo-perf itself on OS X later. We have some shell magic in the scons snippets below to automatically add the –prefix=/opt/local on OS X as needed.

cd mongo; scons $(test $(uname) = "Darwin" && echo -n --prefix=/opt/local) --full all

Depending on how fast your machine is, this could take some time. We suggest using this as an opportunity to prepare a beverage. Once this has completed successfully, install the binaries, headers, libraries and so on using the command:

sudo scons $(test $(uname) = "Darwin" && echo -n --prefix=/opt/local) --full install

Building Mongo-Perf

Now you are ready to build Mongo-Perf. To fetch the mongo-perf source from GitHub, run:

git clone https://github.com/mongodb/mongo-perf.git

If all the dependencies were installed in the correct places, you should be able to build immediately:

cd mongo-perf; scons

To run Mongo-Perf, you will need a Python environment with the pymongo driver available. The easiest way to set this up is to create a “virtualenv” and install pymongo into it. To get the virtualenv tool, simply download it from https://raw.github.com/pypa/virtualenv/master/virtualenv.py.

wget --no-check-certificate https://raw.github.com/pypa/virtualenv/master/virtualenv.py

Now to create a virtualenv and install pymongo, run:

python virtualenv.py env
source env/bin/activate
pip install pymongo

Using Mongo-Perf

Mongo-Perf benchmarks are kicked off by the Python program “runner.py”. You can either launch this against an already-running MongoDB server on your local system, or you can have runner.py fetch and build a version of MongoDB from source (at any revision you want) and use that. There is also an option to run it against a mongos router to benchmark a sharded configuration.

For this article, we’ll assume you already have a MongoDB server running on localhost port 27017. If you don’t have MongoDB running locally just yet, see 10gen’s MongoDB Quickstart OS X for OS X install instructions or their Ubuntu and Debian Packages page for Ubuntu install instructions.

To start a Mongo-Perf benchmark against this, run:

python runner.py --nolaunch --port 27017

You should immediately see output from the benchmark tool about which load test is being run. Upon completion, the program will emit a JSON dump of the test results to the console, and also write it to the local MongoDB in a database called “bench_results”.

The easiest way to view the benchmark results is through the included web app, server.py. To start the web app, run:

python server.py

Once that is running, point your browser to http://localhost:8080 to view the results. As you will see, there are a number of different kinds of tests – inserts, reads and updates. The tests are run with varying levels of concurrency which helps evaluate performance with multiple clients.

That should be enough to get you started with your own synthetic MongoDB benchmarks. In future posts we will be talking about some of our results and how to tune your database for better performance. If you are interested in more cloud and database tips & tricks, sign up for our newsletter! Also, if you are using MongoDB in your business we’d love to hear from you.