MongoDB Performance Testing With JMeter
Let’s learn a bit more about MongoDB, and then, let's learn to build a load testing script for it.
Join the DZone community and get the full member experience.
Join For FreeDatabases are a crucial element for most application environments. Where and how you store your data has a great impact on the performance of the whole system. Therefore, choosing a database is, certainly, one of the most impactful decisions one has to make before starting development. Performance testing your database can help you reach this decision, and is also an important task during your development process.
- This blog post will teach you how to test the open-source MongoDB database with Apache JMeter™. We will see how to:
- Connect to MongoDB
- Write documents to MongoDB
- Read documents from MongoDB
- Update documents in MongoDB
- Delete documents from MongoDB
Performance Testing Databases With JMeter
If your application suffers from performance issues, it may be due to a poorly optimized query to the database or an insufficient database server. If you have a relational database, JMeter’s JDBC Request Sampler allows you to execute an SQL query and evaluate its performance. But, sometimes, a non-relational database is a more viable choice for your needs, so you need to find a different way to load test it with JMeter.
MongoDB is a highly popular non-relational database, which stores data in "document" structures. To test it, JMeter has the MongoDB Sampler, to send a query to the MongoDB instance. However, its implementation locks the database access when one query is being executed. This limits you to one request at a time and is not enough for the performance testing.
Fortunately, by using a JSR223 Sampler and MongoDB Java Driver library, you can test your MongoDB instance, writing your requests in Java. Let’s learn a bit more about MongoDB, and then, let's learn to build a load testing script for it.
What Is MongoDB?
MongoDB is a free, open-source, cross-platform, non-relational, document-oriented database, which stores data in a JSON-like document:
{
firstName: "Tester",
lastName: "Testovsky",
age: 30,
occupation: "QA engineer",
skills: [
"JMeter",
"Load Testing",
"Bad Puns"
]
address: [{
city: "Quality-city",
street: "Performance ave.",
house: 12
}]
}
A document is a set of field-value pairs, where the value can be any of the BSON data types, arrays, other documents and arrays of documents.
In MongoDB, documents are stored inside so-called “collections” (similar to tables in relational databases). Collections are stored in databases, and each MongoDB server may contain a number of databases.
The MongoDB Java Driver
It is possible to control your MongoDB instance through Java code by using the powerful MongoDB Java Driver. This library provides you with the ability to connect to a MongoDB instance; create, read, update and delete documents, and much more. The full API documentation for version 3.8 can be found here. There is also an extremely useful reference guide with examples and tutorials.
In order to use MongoDB Java Driver in your JMeter scripts, download the latest version of the mongo-java-driver jar file and put it to lib/ext folder under your JMeter home folder.
Attention: as for now, JMeter distribution has an older version of mongo-java-driver library already presented in the lib folder. This will cause a number of compatibility issues, so, to avoid them, delete the older mongo-java-driver .jar file from the lib folder.
Let’s see, how we can use this driver in a JSR233 sampler to perform basic operations to evaluate the performance of our database.
Connecting JMeter to the MongoDB Database
In order to test the performance of your database, you need to connect to the database first through your JMeter script. This can be achieved via JMeter JSR223 Sampler. You can use this sampler to evaluate the performance of a connection process and then use this established connection to check the performance of querying DB entries later. Depending on the configuration of your database system, it may be required to perform specific actions during the connection process. MongoDB Java Driver supports a wide range of ways to create the connection. Here, we'll look at basic ones.
To connect to the MongoDB client on the localhost with an assigned port 27017:
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoClient;
MongoClient mongoClient = MongoClients.create();
You may specify a connection string as a parameter for a MongoClients.create()
method:
MongoClient mongoClient = MongoClients.create("mongodb://mongohost:27017");
If you need to provide credentials for the connection to the MongoDB client:
MongoClient mongoClient = MongoClients.create("mongodb://user:password@mongohost/?authSource=userdb&ssl=true");
Often, you may want to use JMeter variables as parameters for a MongoClients.create()
method. To keep your scripts readable, you can use a MongoClientSettings
class. For example:
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoClient;
import com.mongodb.MongoClientSettings;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import java.util.Arrays;
String mongoUser = vars.get("mongoUser");
String userDB = vars.get("userDB");
char[] password = vars.get("password").toCharArray();
MongoCredential credential = MongoCredential.createCredential(mongoUser, userDB, password);
MongoClientSettings settings = MongoClientSettings.builder()
.applyToClusterSettings {builder ->
builder.hosts(Arrays.asList(new ServerAddress(vars.get("mongoHost"),vars.get("mongoPort").toInteger())))}
.build();
MongoClient mongoClient = MongoClients.create(settings);
After you have a connection to the client, you can access databases and collections:
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;
MongoDatabase database = mongoClient.getDatabase("jmeter_test");
MongoCollection<Document> collection = database.getCollection("blazemeter_tutorial");
1. Here is the full code to connect to a database defined in JMeter variables “mongoHost," “databaseName,” and “collectionName.” We will use it later in our JMeter script.
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoClient;
import com.mongodb.MongoClientSettings;
import com.mongodb.ServerAddress;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;
import java.util.Arrays;
try {
MongoClientSettings settings = MongoClientSettings.builder()
.applyToClusterSettings {builder ->
builder.hosts(Arrays.asList(new ServerAddress(vars.get("mongoHost"),vars.get("mongoPort").toInteger())))}
.build();
MongoClient mongoClient = MongoClients.create(settings);
MongoDatabase database = mongoClient.getDatabase(vars.get("databaseName"));
MongoCollection<Document> collection = database.getCollection(vars.get("collectionName"));
vars.putObject("collection", collection);
return "Connected to " + vars.get("collectionName");
}
catch (Exception e) {
SampleResult.setSuccessful(false);
SampleResult.setResponseCode("500");
SampleResult.setResponseMessage("Exception: " + e);
}
Now, when you have a MongoCollection
object, you can finally start to work with documents, i.e storing data in the database.
How to Create a Document and Insert it Into the MongoDB Database With JMeter
If your application creates new documents and inserts them to a database, then it’s worthwhile to check the performance of the process of inserting a new document to your database. As with the previous example, we will use JSR223 Sampler.
First, let’s import the necessary libraries:
import com.mongodb.client.MongoCollection;
import org.bson.Document;
import java.util.Arrays;
Now, we’ll create a Document
object and set its fields and values:
Document document = new Document("firstName", "Expert")
.append("lastName", "Protocolson")
.append("age", 37)
.append("occupation", "DevOps")
.append("skills", Arrays.asList("System Administration", "Linux"))
.append("address", new Document("city", "Systemberg")
.append("street", "Data Line")
.append("house", 42));
Here, we are inserting the created document to our collection:
collection.insertOne(document);
Each MongoDB document should have "_id" field with a unique value. If there is no such field or value provided by the time the document is created, then the Java Driver will automatically add "_id" field with a unique value to documents inserted into the collection. It’s rarely (if ever) necessary to provide the "_id" value manually.
Create a list of documents and insert them into the collection:
import com.mongodb.client.MongoCollection;
import org.bson.Document;
import java.util.List;
import java.util.ArrayList;
List<Document> documents = new ArrayList<Document>();
documents.add(document1);
documents.add(document2);
collection.insertMany(documents);
2. Here is the full code for a process of creating a new document and inserting it to a collection. We will use it later in our JMeter script.
import com.mongodb.client.MongoCollection;
import org.bson.Document;
import java.util.Arrays;
try {
MongoCollection<Document> collection = vars.getObject("collection");
Document document = new Document("firstName", "Expert")
.append("lastName", "Protocolson")
.append("age", 37)
.append("occupation", "DevOps")
.append("skills", Arrays.asList("System Administration", "Linux"))
.append("adress", new Document("city", "Systemberg")
.append("street", "Data Line")
.append("house", 42));
collection.insertOne(document);
return "Document inserted";
}
catch (Exception e) {
SampleResult.setSuccessful(false);
SampleResult.setResponseCode("500");
SampleResult.setResponseMessage("Exception: " + e);
}
Querying Documents
In order to get documents from the collection, you should utilize a find()
method of the MongoCollection
object. As always, we will put our code in JSR223 sampler. For example, the following code:
import com.mongodb.client.MongoCollection;
import org.bson.Document;
import java.util.List;
List<Document> result = collection.find();
will find all the documents in the collection, and write them to the result
list.
You may pass a Document
object as a filter to a find()
method:
List<Document> result = collection.find(new Document("age", new Document("$gte", 18)
.append("$lt", 66))
.append("occupation", "Developer"));
Here, we find all the developers of the age greater or equal to 18 and lower than 66 years old. The same list can be retrieved by using the Filters
helper methods:
import static com.mongodb.client.model.Filters.*;
List<Document> result = collection.find(and(gte("age", 2), lt("age", 5), eq("occupation", "Developer")));
3. Here is the full code for finding a document in our collection. We will use it later in our JMeter script.
import com.mongodb.client.MongoCollection;
import static com.mongodb.client.model.Filters.*;
import org.bson.Document;
import org.bson.types.ObjectId;
try {
MongoCollection<Document> collection = vars.getObject("collection");
Document result = collection.find(eq("firstName", "Expert")).first();
vars.put("exampleDocumentId", result.get("_id").toString());
return "Document with id=" + result.get("_id") + " found";
}
catch (Exception e) {
SampleResult.setSuccessful(false);
SampleResult.setResponseCode("500");
SampleResult.setResponseMessage("Exception: " + e);
}
You can find a list of useful filter operations here.
Updating a Document in the Database
To update the document in the collection, you can use the updateOne()
method of the MongoCollection
object. The same approach is used to query the document for updating as described in the previous paragraph. For example, the code in JSR223 sampler:
import com.mongodb.client.MongoCollection;
import static com.mongodb.client.model.Filters.*;
import static com.mongodb.client.model.Updates.*;
import org.bson.types.ObjectId;
collection.updateOne(
eq("_id", new ObjectId("5bb43f18ce8cdca890b72422")),
combine(set("occupation", "Project Manager"), set("address.city", "New Codeshire"), currentDate("lastModified")));
changes the "occupation" field value, the "address.city" value and sets "lastModified" field to the current date for the document in the collection with "_id" equal to "57506d62f57802807471dd41".
If you need to edit several documents, you may use the updateMany()
method:
collection.updateOne(
and(eq("address.city", "Saint Java"), eq("address.street", "Bugs street")),
combine(set("address.street", "Features blvd."), currentDate("lastModified")));
The code above changes the street name of all people living in Saint Java city on Bugs street to Features blvd.
4. Here is the full code of updating values of our document. We will use it later in our JMeter script.
import com.mongodb.client.MongoCollection;
import static com.mongodb.client.model.Filters.*;
import static com.mongodb.client.model.Updates.*;
import org.bson.Document;
import org.bson.types.ObjectId;
try {
MongoCollection<Document> collection = vars.getObject("collection");
collection.updateOne(
eq("_id", new ObjectId(vars.get("exampleDocumentId"))),
combine(set("occupation", "Project Manager"), set("adress.city", "New Codeshire"), currentDate("lastModified")));
return "Document with id=" + vars.get("exampleDocumentId") + " modified";
}
catch (Exception e) {
SampleResult.setSuccessful(false);
SampleResult.setResponseCode("500");
SampleResult.setResponseMessage("Exception: " + e);
}
The list of all update operations can be found here.
Deleting Documents From the Database
Deleting documents from the collection is very similar to finding them. Use the deleteOne()
method of the MongoCollection
object to delete the first document matching specified filters or use deleteMany()
to delete all the matching documents. We will use JSR223 sampler.
5. Here is how you can delete a document from your collection (that’s right, we will use it later in our JMeter script):
import com.mongodb.client.MongoCollection;
import static com.mongodb.client.model.Filters.*;
import org.bson.Document;
try {
MongoCollection<Document> collection = vars.getObject("collection");
collection.deleteOne(eq("occupation", "Project Manager"));
return "Document deleted";
}
catch (Exception e) {
SampleResult.setSuccessful(false);
SampleResult.setResponseCode("500");
SampleResult.setResponseMessage("Exception: " + e);
}
Creating Your JMeter Test Plan
Now, let’s try to write a simple JMeter script to evaluate the performance of our MongoDB deployment. In our script, we will create one JSR223 sampler for each operation of the connection to the database: inserting, reading, updating, and deleting documents.
Prerequisites: you have a MongoDB client installed and running on your local machine with the default port 27017 used for a connection. There is one empty database "jmeter_test" with one empty collection "blazemeter_tutorial".
1. In the Test Plan or User Defined Variables, specify the necessary variables:
- mongoHost: localhost
- mongoPort: 27017
- databaseName: jmeter_test
- collectionName: blazemeter_tutorial
2. Add a Thread Group to your Test Plan.
Right Click->Add->Threads (Users)->Thread Group
In the following steps, we’ll look into making our samplers to test the basic MongoDB operations:
- connect to a database
- create a document
- read this document
- modify the document
- delete this document
All these steps assume that the previous step was executed successfully, so if we encounter an error on any step, we should abort the execution of our thread to prevent further errors. To do this, we need to set “Action to be taken after a Sampler error” to “Stop Thread” in our Thread Group.
Writing a JMeter MongoDB Sampler
3. Add a JSR223 Sampler to your Thread Group. These are the MongoDB Samplers you are creating.
Right Click->Add->Sampler->JSR223 Sampler
4. Name this sampler “Connect to DB” and put the code from the “Connecting JMeter to the MongoDB Database” section, marked as 1 into this sampler.
5. Add another JSR223 Sampler, name it “Write to the DB” and put the code from the “How to Create a Document and Insert it into the MongoDB Database with JMeter” section, marked as 2 into this sampler.
6. Add another JSR223 Sampler and name it “Read from DB” and put the code from the “Querying Documents” section, marked as 3 into this sampler.
7. Add another JSR223 Sampler and name it “Update the Document” and put the code from the “Updating a Document in the Database” section, marked as 4 into this sampler.
8. Add another JSR223 Sampler and name it “Delete a Document” and put the code from the “Deleting Documents from the Database” section, marked as 5 into this sampler.
9. Add a View Results Tree Listener.
Right Click->Add->Listener->View Results Tree
Run the script and see the results in the listener:
Here we see that our “Connect to DB” sampler has successfully returned a “Connected to blazemeter_tutorial” response.
The “Write to a DB” sampler has returned a successful “Document inserted” response.
Here, we see in the response that the requested document was found.
This response shows us that our document was modified.
And, finally, we see that the document was deleted from the database.
All of our samplers have done their job.
Now, in order to evaluate the performance of our MongoDB deployment, we can increase the number of threads, increase the number and complexity of our documents and queries, use the Simple Data Writer listener instead of the View Results Tree listener, and run our scripts from the command line.
Though in this example, we have used a local deployment with a rather basic configuration; in your performance tests, you should use a deployment configuration resembling the actual environment of your project. Also, your test documents and queries should be similar to what you expect to see on the working application.
Using the Java Request Sampler
In the previous example, we used JSR223 sampler to emulate requests to the MongoDB. You may consider using a Java Request sampler instead. The same methods we used to access a database and manipulate documents can be used in Java classes for Java Request sampler.
Also, there is a framework like Morphia ODM (Object-Document Mapper) that can be used to make the creation of documents much simpler.
As we just saw, it’s quite easy to work with the MongoDB using JMeter samplers. But remember, nothing replaces planning your test environment and test data is a very important step to getting a useful analysis on the performance of your MongoDB deployment.
Load Testing With BlazeMeter
Once you created your JMeter script, upload it to BlazeMeter and run your test smoothly in the cloud. Use a SaaS interface to scale and run your tests more easily, collaborate with colleagues, and get advanced reporting.
To learn more, start testing now. Just put your URL in the box below and your test will start in minutes.
Published at DZone with permission of Konstantin Tonkov, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments