A k6 extension for interacting with MongoDB while performance testing.
- CRUD Operations: Insert, InsertMany, Find, FindOne, FindAll, UpdateOne, UpdateMany, DeleteOne, DeleteMany
- Advanced Operations: Upsert, FindOneAndUpdate, Aggregate, Distinct, CountDocuments
- Bulk Operations: BulkWrite for mixed insert/update/delete operations
- Collection Management: DropCollection
- Flexible Filters: Complex query support for all filter parameters
- Connection Management: Automatic connection verification with timeout handling
- Performance: Built-in operation timeouts and cursor management
To build a custom k6 binary with this extension, first ensure you have the prerequisites:
- Go toolchain
- Git
-
Download xk6:
go install go.k6.io/xk6/cmd/xk6@latest
-
xk6 build --with github.com/GhMartingit/xk6-mongo
This will create a k6 binary that includes the xk6-mongo extension in your local folder. This k6 binary can now run a k6 test.
To make development a little smoother, use the Makefile in the root folder. The default target will format your code, run tests, and create a k6 binary with your local code rather than from GitHub.
git clone git@github.com/GhMartingit/xk6-mongo.git
cd xk6-mongo
make buildUsing the k6 binary with xk6-mongo, run the k6 test as usual:
./k6 run test.jsThe extension includes built-in timeout handling:
- Connection timeout: 10 seconds (connection establishment and ping verification)
- Operation timeout: 30 seconds (default for all database operations)
You can configure MongoDB connection pool settings via client options:
const clientOptions = {
"max_pool_size": 100,
"min_pool_size": 10,
"max_connecting": 10
};
const client = xk6_mongo.newClientWithOptions('mongodb://localhost:27017', clientOptions);import xk6_mongo from 'k6/x/mongo';
const client = xk6_mongo.newClient('mongodb://localhost:27017');
export default ()=> {
let doc = {
correlationId: `test--mongodb`,
title: 'Perf test experiment',
url: 'example.com',
locale: 'en',
time: `${new Date(Date.now()).toISOString()}`
};
client.insert("testdb", "testcollection", doc);
}If we need to pass extra options to the driver connection we can pass a plain JavaScript object.
Snake_case and camelCase keys are automatically translated to the underlying Go driver field names. In this example, we are specifying the use of the stable api, with strict compatibility. We are also setting the application name via the app_name property as "k6-test-app", so the connection can be identified in the logs server-side.
import xk6_mongo from 'k6/x/mongo';
const clientOptions = {
"app_name": "k6-test-app",
"server_api_options": {
"server_api_version": "1",
"strict": true
}
};
const client = xk6_mongo.newClientWithOptions('mongodb://localhost:27017', clientOptions);
export default ()=> {
let doc = {
correlationId: `test--mongodb`,
title: 'Perf test experiment',
url: 'example.com',
locale: 'en',
time: `${new Date(Date.now()).toISOString()}`
};
client.insert("testdb", "testcollection", doc);
}
// When using update helpers you can provide either a full update document
// (with operators like $set) or a plain object. Plain objects are
// automatically wrapped in $set before being sent to MongoDB.import xk6_mongo from 'k6/x/mongo';
const client = xk6_mongo.newClient('mongodb://localhost:27017');
export default () => {
const result = client.findOne(
"testdb",
"testcollection",
{ score: { "$gte": 10 } }
);
console.log(result);
}The findWithOptions method provides fine-grained control over query behavior:
import xk6_mongo from 'k6/x/mongo';
const client = xk6_mongo.newClient('mongodb://localhost:27017');
export default () => {
const options = {
limit: 100,
skip: 10,
batch_size: 50, // Process results in batches of 50
sort: { createdAt: -1 },
projection: { name: 1, email: 1, _id: 0 } // Only return specific fields
};
const results = client.findWithOptions(
"testdb",
"users",
{ active: true },
options
);
console.log(`Found ${results.length} users`);
}import xk6_mongo from 'k6/x/mongo';
const client = xk6_mongo.newClient('mongodb://localhost:27017');
export default () => {
// BulkWrite returns insertedCount and modifiedCount
const [inserted, modified] = client.bulkWrite(
"testdb",
"testcollection",
[
{ insertOne: { document: { name: "Alice" } } },
{ updateOne: { filter: { name: "Bob" }, update: { $set: { age: 30 } } } },
{ deleteOne: { filter: { name: "Charlie" } } }
]
);
console.log(`Inserted: ${inserted}, Modified: ${modified}`);
}newClient(uri)- Create a new MongoDB client with default optionsnewClientWithOptions(uri, options)- Create a client with custom connection optionsdisconnect()- Close the connection to MongoDB
insert(db, collection, document)- Insert a single documentinsertMany(db, collection, documents)- Insert multiple documentsfind(db, collection, filter, sort, limit)- Find documents with basic optionsfindWithOptions(db, collection, filter, options)- Find with advanced options (batch size, projection, skip)findOne(db, collection, filter)- Find a single documentfindAll(db, collection)- Find all documents in a collectionupdateOne(db, collection, filter, update)- Update a single documentupdateMany(db, collection, filter, update)- Update multiple documentsdeleteOne(db, collection, filter)- Delete a single documentdeleteMany(db, collection, filter)- Delete multiple documents
upsert(db, collection, filter, document)- Insert or update a documentfindOneAndUpdate(db, collection, filter, update)- Find and update atomically, returns updated documentaggregate(db, collection, pipeline)- Run aggregation pipelinedistinct(db, collection, field, filter)- Get distinct values for a fieldcountDocuments(db, collection, filter)- Count documents matching filterbulkWrite(db, collection, operations)- Execute multiple write operations in one call
dropCollection(db, collection)- Drop a collection
- Use batch size for large result sets to control memory usage
- Use projections to retrieve only needed fields
- Configure connection pooling for high-concurrency tests
- Use bulk operations for multiple writes to reduce network overhead
- Add indexes to your MongoDB collections for better query performance
All operations include built-in validation and timeout handling:
const error = client.insert("testdb", "testcol", doc);
if (error) {
console.error(`Insert failed: ${error.message}`);
}We welcome contributions! Please see CONTRIBUTING.md for guidelines.
For security best practices and reporting vulnerabilities, see SECURITY.md.
Run performance benchmarks:
go test -bench=. -benchmem ./...Recent benchmark results show excellent performance:
- Input validation: ~24 ns/op with 0 allocations
- PascalCase conversion: ~230 ns/op
- Update document detection: <3 ns/op
- Pipeline detection: <1 ns/op
This project is licensed under the same license as the k6 project.
- π Documentation
- π Issue Tracker
- π¬ Discussions
- π Changelog
Built with k6 and the MongoDB Go Driver.