This document provides guidance for AI agents working with the memtier_benchmark codebase.
memtier_benchmark is a high-performance load generation and benchmarking tool for NoSQL databases (Redis and Memcached) developed by Redis. It is written in C++ and uses libevent for async I/O.
├── memtier_benchmark.cpp # Main entry point
├── client.cpp/h # Client implementation
├── cluster_client.cpp/h # Redis Cluster support
├── shard_connection.cpp/h # Connection handling
├── protocol.cpp/h # Redis/Memcached protocol handling
├── obj_gen.cpp/h # Object/key generation
├── run_stats.cpp/h # Statistics collection and reporting
├── JSON_handler.cpp/h # JSON output support
├── file_io.cpp/h # File I/O operations
├── config_types.cpp/h # Configuration types
├── deps/hdr_histogram/ # HdrHistogram library (vendored)
├── tests/ # Integration tests (Python/RLTest)
└── bash-completion/ # Shell completion scripts
- Build tool: GNU Autotools (autoconf/automake)
- Language: C++11
- Dependencies: libevent (≥2.0.10), OpenSSL (optional), zlib, pthread
# Initial setup
autoreconf -ivf
./configure
make
# Debug build (no optimizations)
./configure CXXFLAGS="-g -O0 -Wall"
# With sanitizers
./configure --enable-sanitizers # ASAN/LSAN
./configure --enable-thread-sanitizer # TSAN
./configure --enable-ubsan # UBSan
# Disable TLS
./configure --disable-tls- Uses
clang-formatfor formatting (config in.clang-format) - Run
make formatto format code - Run
make format-checkto verify formatting - CI enforces formatting on all PRs
- Always run
make formatafter modifying C++ files and before committing. Verify withmake format-checkthat no formatting issues remain. - Run
make install-hooksonce per clone to enable the.githooks/pre-commithook, which runsmake format-check-stagedautomatically on staged C/C++ blobs (the content in the index, not the working tree).
Integration tests use RLTest framework (Python-based):
# Setup virtual environment
mkdir -p .env && virtualenv .env
source .env/bin/activate
pip install -r tests/test_requirements.txt
# Run all tests (takes several minutes)
./tests/run_tests.sh
# Run specific test file
TEST=test_crash_handler_integration.py ./tests/run_tests.sh
# Run with standalone mode (default)
OSS_STANDALONE=1 ./tests/run_tests.sh
# Run with cluster mode
OSS_CLUSTER=1 ./tests/run_tests.sh
# Run with cluster mode and custom shard count (default: 3)
OSS_CLUSTER=1 SHARDS=5 ./tests/run_tests.sh
# Run with TLS
TLS=1 ./tests/run_tests.sh
# Run with sanitizers
ASAN_OPTIONS=detect_leaks=1 ./tests/run_tests.sh- Multi-threaded architecture: Uses pthreads with libevent for async I/O per thread
- Protocol support: Redis (RESP) and Memcached (text and binary)
- Statistics: Uses HdrHistogram for latency percentiles
- Cluster support: Handles Redis Cluster topology and slot-based routing
Every new CLI option must be added in all of these locations:
- Add option to the
extended_optionsenum inmemtier_benchmark.cpp - Add entry to the
long_options[]array inmemtier_benchmark.cpp - Add case handler in the
getopt_longswitch inmemtier_benchmark.cpp - Add field to
benchmark_configstruct inmemtier_benchmark.h - Initialize default value in
config_init_defaults()inmemtier_benchmark.cpp - Add help text in
usage()function inmemtier_benchmark.cpp - Update man page (
memtier_benchmark.1) - Update bash completion (
bash-completion/memtier_benchmark) — add tooptions_no_comp(takes a value) oroptions_no_args(flag) - Add tests for the new option (see below)
Verification: After adding a new flag, always confirm it appears in both --help output and memtier_benchmark.1. Run ./memtier_benchmark --help | grep <flag> and grep <flag> memtier_benchmark.1 to verify.
- Create Python test file in
tests/followingtests_oss_simple_flow.pypattern - Use RLTest decorators and
mb.pyhelper for running memtier_benchmark - Tests run against actual Redis server (started by RLTest)
All new features and bug fixes should include corresponding tests.
- Always validate structured JSON output (
mb.json) for result correctness, not just stdout text. The JSON file underALL STATScontains per-command entries (e.g.,"Sets","Gets","Scan 0s") with"Count","Ops/sec", latency metrics, etc. - Use
json.load()to parsemb.jsonand assert on the expected keys and values. - See
tests_oss_simple_flow.pyfor examples of JSON output validation patterns:results_dict['ALL STATS']['Sets'].
- When possible, prefer preloading data using memtier itself (
--ratio=1:0 --key-pattern=P:P --requests=allkeys) to match real usage patterns. Seetests_oss_simple_flow.pytest_preload_and_set_getfor the standard pattern. - Direct Redis client calls (Python
redispipeline) are acceptable when simpler — e.g., loading a small number of keys with specific prefixes or non-string data types.
- Protocol implementations are in
protocol.cpp - Each protocol (redis/memcached) has its own class hierarchy
- Connection state machine is in
shard_connection.cpp
GitHub release notes are curated, not auto-generated. Follow the pattern established from 2.2.1 onward:
# What's Changed
## New Features
- **Short title**: One-sentence description ending with the PR number (#123).
## Bug Fixes
- **Short title**: Description with `inline code` for flag/identifier names (#124).
## Packaging & Distribution
- ...
## Developer Tooling & CI Improvements
- ...
## Maintenance
- ...
## New Contributors
- @handle made their first contribution (#125)
**Full Changelog**: https://github.com/redis/memtier_benchmark/compare/<prev>...<this>Conventions:
# What's Changedheading, then##sections (only the sections that have content — drop empty ones).- Section order, when present: New Features → Bug Fixes → Packaging & Distribution → Developer Tooling & CI Improvements → Build & Configuration Improvements → Maintenance → AI → New Contributors.
- Bullet form:
- **Bold lead phrase**: Sentence-cased description ending with a period, with the PR number in parens before the period (#NNN). - Use inline backticks for CLI flags, file names, and identifiers.
- Close with the
**Full Changelog**compare link. - Do not paste the GitHub auto-generated
* by @user in <PR-link>style — it was used up to 2.2.0 but has been replaced by the curated form.
Patch releases live on a release branch (e.g. 2.3 for the 2.3.x line). The flow:
- List the candidates:
git log --oneline origin/master ^origin/<release-branch>. For each commit, decide whether it belongs in a patch release. Skip duplicates that were applied independently to the release branch (compare diffs, not just subjects — see0cb8e9avsd92b163in the 2.3.1 history). - Branch from the release branch:
git switch -c release.<X.Y.Z> origin/<release-branch>. - Cherry-pick in chronological order so the release-branch history reads forward. Resolve conflicts as they arise.
- Verify CI workflows fire on the release branch. Workflow files
(
asan.yml,ci.yml,code-style.yml,tsan.yml,ubsan.yml,release-rpm.yml) historically restrictedpull_request: branches:to[master, main]only. A PR targeting the release branch will run zero checks until the branch list includes the release branch — extend each workflow'sbranches:filter as part of the release PR if it is missing. - Run
utils/prepare_release.sh <X.Y.Z>as the last commit. See gotchas below. - Open a single PR base
<release-branch>←release.<X.Y.Z>. Don't split the cherry-picks and the version bump into separate PRs for a patch release; the atomic shape leaves the release branch self-consistent if the PR is delayed. - After merge, tag and publish a Release on the merged release-branch HEAD.
The
release: publishedevent firesdockers.yml,release-rpm.yml, andrelease.yml(APT) automatically. - Mirror any review-driven follow-ups back to master as a separate PR.
Cursor Bugbot frequently catches latent issues on release-prep PRs (e.g. the
get_total_latency()narrowing flagged on the 2.3.1 backport); do not let those fixes live only on the release branch.
utils/prepare_release.sh <version> bumps configure.ac, runs
autoreconf -ivf && ./configure && make && make rebuild-man, and commits
configure.ac + memtier_benchmark.1 as a single Release <version> commit.
Watch out for:
- Stale
version.hcorrupts the man page. The Makefile ruleversion.h: $(SHELL) $(srcdir)/version.shhas no dependencies, so onceversion.hexistsmakewon't re-runversion.sh. The script then bakes an outdated git SHA intomemtier_benchmark.1viahelp2man. Alwaysrm -f version.hbefore running the script so the SHA matches HEAD. - No branch sanity check. The script doesn't verify the current branch.
Confirm
git branch --show-currentmatches the intended release branch before running. - No version-string validation. A typo like
2.3.1.writes a brokenAC_INITline and the failure only surfaces on the next build. Sanity-check withgrep AC_INIT configure.acafter the bump commit lands. - No tag is created. The script commits but doesn't tag. After merge you
still need
git tag <X.Y.Z>on the merged release-branch tip, push the tag, and publish a GitHub Release for the workflows to fire. - Untracked-file check is dirty-state-only. The script blocks on tracked
modifications but ignores untracked files. Editor backups like
configure.ac~won't trigger the guard.
Use gh release create <tag> --generate-notes --draft as a starting point,
then rewrite the body in the curated style above before publishing. Don't
publish the auto-generated draft as-is — it doesn't match the project's
release-note conventions.
# Build with debug symbols and no optimization
./configure CXXFLAGS="-g -O0 -Wall"
make# Run under gdb
gdb --args ./memtier_benchmark -s localhost -p 6379
# Attach to running process
gdb -p $(pgrep memtier)
# Analyze core dump
gdb ./memtier_benchmark core.<pid>memtier_benchmark has a built-in crash handler that prints detailed bug reports on crashes (SIGSEGV, SIGBUS, SIGFPE, SIGILL, SIGABRT), including:
- Stack traces for all threads
- System and build information
- Active client connection states
# Enable core dumps
ulimit -c unlimited
# Check core pattern
cat /proc/sys/kernel/core_pattern
# With systemd-coredump
coredumpctl list memtier
coredumpctl gdb# AddressSanitizer (memory errors, leaks)
./configure --enable-sanitizers
make
ASAN_OPTIONS=detect_leaks=1 ./memtier_benchmark ...
# ThreadSanitizer (data races)
./configure --enable-thread-sanitizer
make
TSAN_OPTIONS="suppressions=$(pwd)/tsan_suppressions.txt" ./memtier_benchmark ...
# UndefinedBehaviorSanitizer
./configure --enable-ubsan
make
UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 ./memtier_benchmark ...Note: TSAN and ASAN are mutually exclusive; UBSan can be combined with ASAN.
All C++ source files must include this header:
/*
* Copyright (C) 2011-2026 Redis Labs Ltd.
*
* This file is part of memtier_benchmark.
*
* memtier_benchmark is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2.
*
* memtier_benchmark is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with memtier_benchmark. If not, see <http://www.gnu.org/licenses/>.
*/- Default build includes debug symbols (
-g) for crash analysis - The crash handler prints detailed reports on SIGSEGV/SIGBUS/etc.
- Core dumps require
ulimit -c unlimitedand proper kernel config - TSAN and ASAN are mutually exclusive