So how would I decide on which method to implement? Well, lets just choose the one that performs the best. Ok, how do I do that? Hmmm. I could download and install some of the tools out there, or I could just wrap the shell code in a function and add some timings. OR, I could use the same tool that I use to performance test everything else; JMeter. To me it was a no brainer.
So how do we do it?
There is a full tutorial here.
Simply put, you need to do the following:
- Create a Sampler class.
- Create a BeanInfo class.
- Create a properties file.
- Bundle up into a jar and drop into the apache-jmeter-X.X\lib\ext folder
- Update search_paths=../lib/ext/mongodb.jar in jmeter.properties if you place the jar anywhere else.
How I did it
I tend to have a scratch pad project set up in my IDE, so I decided just to go with that. Just to be on the safe side, I imported all the dependencies from:
- apache-jmeter-X.X\lib
- apache-jmeter-X.X\lib\ext
- apache-jmeter-X.X\lib\junit
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.ArrayList; | |
import org.apache.jmeter.samplers.AbstractSampler; | |
import org.apache.jmeter.samplers.Entry; | |
import org.apache.jmeter.samplers.SampleResult; | |
import org.apache.jmeter.testbeans.TestBean; | |
import org.apache.jorphan.logging.LoggingManager; | |
import org.apache.log.Logger; | |
import com.mongodb.DB; | |
import com.mongodb.Mongo; | |
import com.mongodb.ServerAddress; | |
public class Sampler | |
extends AbstractSampler | |
implements TestBean { | |
private static final long serialVersionUID = -678108159079724396L; | |
private static final Logger log = LoggingManager.getLoggerForClass(); | |
public final static String SCRIPT = "Sampler.script"; //$NON-NLS-1$ | |
private static int classCount = 0; // keep track of classes created | |
public Sampler() { | |
classCount++; | |
trace("Sampler()"); | |
} | |
public SampleResult sample(Entry e) { | |
trace("sample()"); | |
SampleResult res = new SampleResult(); | |
String data = getScript(); // Sampler script | |
String response = null; | |
Mongo m = null; | |
res.setSampleLabel(getTitle()); | |
res.sampleStart(); | |
try { | |
ArrayList<ServerAddress> addrs = new ArrayList<ServerAddress>(); | |
addrs.add(new ServerAddress(YOUR_REPLICASET_BOX_1, YOUR_REPLICASET_PORT_1)); | |
addrs.add(new ServerAddress(YOUR_REPLICASET_BOX_2, YOUR_REPLICASET_PORT_1)); | |
m = new Mongo(addrs); | |
DB db = m.getDB(YOUR_DB_NAME); | |
boolean authenticated = db.authenticate(YOUR_DB_USERNAME, YOUR_DB_PASSWORD.toCharArray()); | |
db.eval(getScript()); | |
response = Thread.currentThread().getName(); | |
res.setSamplerData(data); | |
res.setResponseData(response.getBytes()); | |
res.setDataType(SampleResult.TEXT); | |
res.setResponseCodeOK(); | |
res.setResponseCode("200"); | |
res.setSuccessful(true); | |
res.setResponseMessage("OK");// $NON-NLS-1$ | |
} | |
catch (Exception ex) { | |
log.debug("", ex); | |
res.setResponseCode("500");// $NON-NLS-1$ | |
res.setSuccessful(false); | |
res.setResponseMessage(ex.toString()); | |
} | |
finally { | |
res.sampleEnd(); | |
m.close(); | |
} | |
return res; | |
} | |
private String getTitle() { | |
return this.getName(); | |
} | |
public String getScript() { | |
return getPropertyAsString(SCRIPT); | |
} | |
public void setScript(String script) { | |
setProperty(SCRIPT, script); | |
} | |
/* | |
* Helper method | |
*/ | |
private void trace(String s) { | |
String tl = getTitle(); | |
String tn = Thread.currentThread().getName(); | |
String th = this.toString(); | |
log.debug(tn + " (" + classCount + ") " + tl + " " + s + " " + th); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.beans.PropertyDescriptor; | |
import org.apache.jmeter.testbeans.BeanInfoSupport; | |
import org.apache.jmeter.testbeans.gui.TextAreaEditor; | |
public class SamplerBeanInfo extends BeanInfoSupport { | |
public SamplerBeanInfo() { | |
super(Sampler.class); | |
createPropertyGroup("sampler", new String[]{"script"}); | |
PropertyDescriptor pd = property("script"); | |
pd.setValue(NOT_UNDEFINED, Boolean.FALSE); | |
pd.setValue(DEFAULT, ""); | |
pd.setValue(NOT_EXPRESSION, Boolean.TRUE); | |
pd.setPropertyEditorClass(TextAreaEditor.class); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
displayName=MongoDB Script Sampler | |
sampler.displayName=Script | |
script.displayName=The script to run | |
script.shortDescription=Add your mongo shell script as you would via the mongo shell. |
Go through the normal steps to set the test plan up:
- Right click Test Plan and add a Thread Group.
- Right click the Thread Group and add a Sampler, in this case a MongoDB Script Sampler.
- Add your script to the textarea; db.YOUR_COLLECTION_NAME.insert({"jan" : "thinks he is great"})
- Run the test
Happy days. You can then use JMeter as you would for any other sampler.
Future enhancements
This is just a hack that took me 37 minutes to get running, plus 24 minutes if you include this post. This can certainly be extended to allow you to enter the replicaset config details for instance and to pull the creation of the connection out so we're not initiating this each time run a test.