Stylized Alex Ward logo


Website Benchmarking

I've recently been evaluating different website benchmarking tools, so I though I would take a moment to highlight two of them I have used recently.


ab is the Apache Benchmark tool, and it comes bundled with Apache. It's a pretty simple cli tool, used to test the throughput of a website. It has a bunch of different options you can pass to it, but the most important are -c (number of concurrent connections) and -n (number of requests). It's man page is pretty well written, so I'll let you explore the other options on your own.

So, here's a sample of testing using ab:

ab -n 200 -c 20 **http://****/**

(Make a note here, that you need to specify the protocol, and the page, otherwise ab will complain)

And the results of that benchmark:

ab -n 200 -c 20
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd,
Licensed to The Apache Software Foundation,

Benchmarking (be patient)
Completed 100 requests
Completed 200 requests
Finished 200 requests

Server Software: Apache/2.2.22
Server Hostname:
Server Port: 80

Document Path: /
Document Length: 19858 bytes

Concurrency Level: 20
Time taken for tests: 26.306 seconds
Complete requests: 200
Failed requests: 0
Write errors: 0
Total transferred: 4076928 bytes
HTML transferred: 3986634 bytes
Requests per second: 7.60 [#/sec] (mean)
Time per request: 2630.642 [ms] (mean)
Time per request: 131.532 [ms] (mean, across all concurrent requests)
Transfer rate: 151.35 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 69 2121 1728.4 1554 9394
Processing: 0 296 372.5 148 2138
Waiting: 0 63 166.3 0 897
Total: 331 2417 1776.0 1846 9684

Percentage of the requests served within a certain time (ms)
50% 1846
66% 2623
75% 3189
80% 3586
90% 4958
95% 6051
98% 8059
99% 8518
100% 9684 (longest request)

Looks like I could be doing a better job serving up web requests! Lowering the concurrency certainly helped the test, getting the request time down to < 1000 ms for 90% of requests, so I need to see what's going on with Apache when I'm serving up concurrent requests.

There's another gotcha with AB. It cannot handle ssl requests coming from a server with a self-signed cert. There does not appear to be any way to tell it to ignore ssl errors either.

Apache Jmeter

Jmeter is actually really cool, but it does have a bit of a learning curve. I've attached a couple of images which show a basic configuration.

First thing you have to add is a thread group. This is the place where you tell it how many threads to run on, and how many requests each thread is going to request. After that, you need to add HTTP Request defaults, so that you can specify the default server and the default request uri. Next, you add a HTTP Request sampler, and give it the uri you want to test. You can add as many of these as you want. Finally, you need something to read the results. I've added 2, one which shows the sample results, and another which shows the average request times over an interval.

After you hit the run button, you will see results in the resulting screen!

There's actually a pretty good intro over here:

It will give you a pretty good intro to how to do web benchmarking with it. There are a bunch of other features which are outside of the scope of this post, but it's a pretty good tool for doing all kinds of performance testing.

CiviCRM API Part 2

A very brief continuation of my CiviCRM API exploration from the other day.

I have gotten everything up and running, but there are a few more things which one needs to be aware of when querying against the search API.

Custom Fields

You can query against custom fields which you've defined for your CiviCRM contacts, but you have to query them with the name custom_{id} where ID appears to be an auto incrementing key, that you will either need to look up in the database, or querying against the CustomField api, which will allow you to figure out what the appropriate ID is.

Changing the number of records returned

As far as I can tell, this isn't documented anywhere, but the variable you want to pass is rowCount. This is useful when you want to get all your custom fields, as the default rowCount is 25.

Searching on multiple fields defaults to an OR relationship

Or, as near as I can tell. So, basically if you're wanting to query against first_name and last_name, it will give you records that match either of the fields. I'm not presently aware of how to change that behavior.

Anyhow! Finally up and running.

CiviCRM External API Access Problems

Recently I was working on getting CiviCRM to work with external API access, because I have a multi-site install which needs to be able to communitcate with the contact database across all of the sub-sites without having the plugin turned on (for reasons I'll explain in a moment). Now, API v3 does look like it is a great improvement over v2, however, it has been less-than-documented, and important information is scattered in various locations. I'll try to summarise what I've learned here to illustrate how to get an external API up and running.

Issue #1

The first problem I ran into was the external API script was having trouble finding the civicrm.settings.php file, which is located in /root/sites/default/. The reason this issue occurs is because I have added my civicrm module files to /root/modules/civicrm, instead of /root/sites/all/modules/civicrm/ where it was expecting to be. There's a patch out there that should fix the issue, but you can also get around it by adding a DEFINE('CIVICRM_CONFDIR', '/root/sites/default/'); somewhere in the include path.

Issue #2

The v3 documentation is not very clear about pointing this out, but there are 2 api tokens which you need to get CiviCRM's API to work externally. The first is the 'key' variable, and it is generated when you run the CiviCRM install script when you first install it. It's located in your civicrm.settings.php file as a defined constant. Do a search for 'KEY' and you'll see it. The second is the api_key, which is a bit harder to get at if you don't know where it's coming from. Turns out, it is generated for you each time you start an API session. You log in with a Drupal user who has the appropriate permissions to access the CiviCRM features you're needing to query, using a username and password. This works basically like HTTP basic auth, so watch out if you're doing this locally and not posting to https. (Civicrm's API explorer also generates everything for you as a GET request, so, careful of that as well). However, it'd be too easy if you just logged in with the Drupal user, which leads me to...

Issue #3

The user you are logging in with needs an api_key in the civicrm_contacts table of your CiviCRM database. There does not appear to be a way to generate this via the interface, so you have to go into the database, generate an api key, and drop it in the civicrm_contacts table for the appropriate contact which is tied to the Drupal user you are wanting to use. There may be an extension out there that makes this easier, but I was unable to locate one quickly enough while I was working on my other API issues, so I just manually inserted it.

Now everything seems to be up and running, but it was a bit more of a hassle than I was anticipating. However, I did mention one other problem that I ran into while I was using the api, and the reason I can't use the PHP interface to interact with it directly.

Api/api.php doesn't function if the module is not enabled

I believe this is because the module's autoloader doesn't get called if the module is not enabled on the site you're attempting to interact with it, and could potentially be solved by manually including some files (and this is mentioned on their API docuementation, under a standalone-Joomla! install), which I'll be investigating later. Why don't I just enable the module and be done with it? The issue here is I need to read from the CiviCRM contacts list to ensure some things are there, but I do not want to write to it when a user registers. The CiviCRM module by default implements hook_form_alter and adds the default CiviCRM contact information to the login form, and adds its own form validator and hook_user_presave, which I don't presently want running.

All in all CiviCRM is going to make my life easier, but I really wish the API v3 documentation were fleshed out a bit more, or at least easier to navigate. (Presently you only have the API explorer, which is handy if you know what you're looking for, but not so much if you don't.)