load test your drupal application scalability with apache jmeter: part two

user warning: Table './johnandcailincmsdb/node_counter' is marked as crashed and should be repaired query: SELECT totalcount, daycount, timestamp FROM node_counter WHERE nid = 306 in /var/www/drupal/includes/database.mysql.inc on line 172.
i recently posted an introductory article on using jmeter to load test your drupal application. if you've read this article and are curious about how to build a more sophisticated test that mimics realistic load on your site, read on.

the previous article showed you how to set up jmeter and create a basic test. to produce a more realistic test you should simulate "real world" use of your site. this typically involves simulating logged-in and logged-out users browsing and creating content. jmeter has some great functionality to help you do this.

as usual, all code and configurations have been tested on debian etch but should be useful for other *nix flavors with subtle modifications. also, although i'm discussing drupal testing, the method below really applies to any web application. if you aren't already familiar with jmeter, i'd strongly recommend that you read my first post before this one.

an overview

the http protocol exchanges for realistic tests are quite complex, and painful to manually replicate. jmeter kindly includes http-proxy functionality, that allows you to "record" browser based actions, which can be used to form the basis of your test. after recording, you can manually edit these actions to sculpt your test precisely.

our test - browsers and creators

as an example, let's create a test with two test groups: creators and browsers. creators are users that arrive at the site, stay logged out, browse a few pages, create a page and then leave. browsers, are less motivated individuals. they arrive at the site, log in, browse some content and then leave.

setting up the test - simulating creators

to create our test, fire up jmeter and do the following.

create a thread group. call it "creators". add a "http request defaults" object to the thread group. check the "retrieve all embedded resources from html files" box.

add a cookie manager to the thread group of type "compatibility". add an "http proxy server to the workbench", as follows:

modify the "content-type filter" to "text/html". your jmeter-proxy should now look like:

navigate in your browser to the start of your test e.g. your home page. clear your cookies (using the clear private data setting). open up the "connection settings option" in firefox preferences and specify a manual proxy configuration of localhost, port 8080. this should look like:

note: you can also do this using internet explorer. in ie7 go to the "connections" tab of the internet options dialog. click the "lan settings" button, and setup your proxy.

start the jmeter-proxy. record your test by performing actions in your browser: (a) browse to two pages and (b) create a page. you should see your test "writing itself". that should feel good.

now stop the jmeter-proxy. your test should look similar to:

setting up the test - simulating browsers

create another thread group above the first. call it browsers. again, add a "http request defaults" object to the thread group. check the "retrieve all embedded resources from html files" box.

add a cookie manager to the thread group of type "compatibility". start the jmeter-proxy again. record your test by performing actions: (a) login and then (b) browse three pages. your test should look like:

stop the jmeter-proxy. undo the firefox proxy.

setting up the test - cleaning up

you can now clean up the test as you see fit. i'd recommend:
  • change the number of threads and iterations on both thread-groups to simulate the load that you care about.
  • modify the login to happen only once on a thread. see the diagram below.

and optionally:

  • rename items to be more meaningful.
  • insert sensible timers between requests.
  • insert assertions to verify results.
  • add listeners to each thread group. i recommend a "graph results" and a "view results tree" listener.

your final test should look like the one below. note that i didn't clutter the example with assertion and timers:

running your test

you should now be ready to run your test. as usual, click through to the detailed results in the tree to verify that your test is doing something sensible. ideally you should do this automatically with assertions. your results should look like:


the test examples that i chose intentionally avoided logged-in users creating content. you'll probably want these users to create content, but you'll likely get tripped up by drupal's form token validation, designed to block spammers and increase security. modifying the test to work around this is beyond the scope of this article, and probably not the best way to solve the problem. if someone knows of a nice clean way to disable this in drupal temporarily, perhaps they could comment on this article.


tech blog

if you found this article useful, and you are interested in other articles on linux, drupal, scaling, performance and LAMP applications, consider subscribing to my technical blog.

Hi John, Thanks for the

Hi John,

Thanks for the great post. I was able to create tests that need to log-in a user very easily by just following your blog.

Thanks again

Couple of my pitfalls and

Couple of my pitfalls and their solutions:

1. Http Proxy Host

> in firefox preferences and specify a manual proxy configuration of localhost, port 8080.

In my case jmeter is running on vps01.example.com. I ssh -X vps01.example.com and start jmeter which causes X to launch the jmeter window on my local machine. Therefore the correct hostname for my proxy is vps01.example.com. (If I were to enter 'localhost' Firefox would give me the error: "Proxy Server Refused Connection."

2. While recording, I didn't see the webpages in my browser. Instead I got "java.net.UnknownHostException: drupal.example.com"

To debug this, I ssh'd to the vps and ran 'lynx http://drupal.example.com' and saw that lynx could not connect to my site. Ah, that's becuase there's no dns set up for that url and I'd forgotten to add the url to my /etc/hosts file.

It can also be helpful to add a View Results Tree listener as a child of Http Proxy Server, so that you can see what responses the target webserver is giving to the jmeter requests.

Thanks for the great articles John!

I've found a way to disable

I've found a way to disable temporarily checking of form_token value in drupal 5.3.
You just got to comment 1 line in form.inc :
if (isset($form['#token'])) {
if (!drupal_valid_token($form_values['form_token'], $form['#token'])) {
// Setting this error will cause the form to fail validation.
(this line) //form_set_error('form_token', t('Validation error, please try again. If this error persists, please contact the site administrator.'));

Thanks for posting that tip.

Thanks for posting that tip.

Hi John (and others), could

Hi John (and others), could you introduce a little bit how to design a jmeter test plan to simulate a lot of users "upload" files to a drupal site? thanks!

Hello, John! Thank you for

Hello, John!

Thank you for your artictes again :)

I tried to use JMeter for testing Drupal and I`ve found how to create content as logged-in user :)

You just need to add "HTML Link Parser" element as a child to "HTTP Request" that posts comment or content and modify HTTP Request`s parameters:
Name: form_token
Value: .*

So Link Parser will get form_token value and put it into request.

Hi, exe misses a couple of


exe misses a couple of crucial points in the above steps -

1. You must, in the prior sampler (or HTTP Request), touch the form you want to submit to get the initial form_token value which JMeter can then look for in the subsequent sampler (using the HTTP Link Parser). In other words you must have a sampler loading the form page without attempting any data submission first, and THEN have a second sampler with the HTML Link Parser as a child, which will submit the data.

2. In some cases the data is posted, via a redirect, to a page with a different URL to the initial form (example is the system module's module page) and in this case the HTML Link Parser approach will not work, because it doesn't make the connection between the differing URLs. In this case you need a Regular Expression Extractor (see Post Processors) on the INITIAL sampler, to extract the form_token value and save it in a variable. The Extractor settings need to look like this (for Drupal 6.x) -

Response Field to Check: Body
Reference Name: form_token
Regular Expression:
Template: $1$
Match: 0
Default Value: NOT FOUND

In the second sampler, where the data is actually submitted, the form_token parameter needs this value -

Note, approach 2 will always work, whereas exe's approach, the first one, may not.

thanks greg. good

thanks greg. good information.

Great blog. Easy to

Great blog. Easy to understand and implement (on Solaris of course!).

Hi guys - thanks for this

Hi guys - thanks for this AWESOME resource!

I have a basic question - do you have any sense of what a heavy load would look like? For example, if you get dugg, or you get slashdotted or you get tech-crunched on gigaom'd, what are we looking at in terms of traffic? Do you know what those loads typically look like, in terms of 'page requests per minute'?

Also, would love to get your thoughts on using EC2 as a back-end to deploy apps on, so scale would, in theory, be easier...


it's really hard to estimate

it's really hard to estimate what kind of load you might expect. that's definitely one of the more difficult things about planning for scale; knowing what scale to prepare for.

as for ec2. trying to scale drupal on ec2 is technically more challenging than a traditional hosting provider. it's really not set up to host database driven applications.

good luck and thanks for the feedback.

True...estimating that is

True...estimating that is tough!

Pardon my ignorance here, but why do you say ec2 is not set up for rdbmses (cf your other post), or db driven apps? Is it because of the virtual infrastructure not being able to eke out the performance you might get, or is it because the motivation behind the service is to not have always-on server-type apps?

At the outset, it looks like it would make life a lot easier to gracefully scale as load grows (using your examples of scaling via load-b heartbeats and clustered dbs).


let's continue this

let's continue this conversation on the recent amazon blog

it i's good and useful

it i's good and useful tutorial. I am planning to test drupal 5 vs new drupal 6.

unfortunately it seems that my proxy server rarely record the cookie. Can you help me with this one ?


hebiryu, did you read part

hebiryu, did you read part two of my tutorial where i discuss cookie managers etc? i'd also recommend using firefox's cookie viewer tools->options->privicy->show cookies to view and manipulate your cookies. follow the steps in the tutorial carefully, watch cookies being set and unset using firefox, and you can almost certainly debug your specific problem. good luck!

post new comment

the content of this field is kept private and will not be shown publicly.
  • web page addresses and e-mail addresses turn into links automatically.
  • allowed html tags: <h2> <h3> <h4> <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • lines and paragraphs break automatically.
  • you may post code using <code>...</code> (generic) or <?php ... ?> (highlighted php) tags.

more information about formatting options

are you human? we hope so.
copy the characters (respecting upper/lower case) from the image.