Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 50 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ examples.
| [`setup`](#setup) | Provision an isolated VPC + EC2 fleet and pre-install every engine. | mutates |
| [`run`](#run) | Execute a `memtier_benchmark` pass (N trials, all engines) against an existing setup. | mutates |
| [`run-commands`](#run-commands) | Benchmark one Redis command (SET, GET, LPUSH, ...) per invocation against an existing setup. | mutates |
| [`run-commands-dfly`](#run-commands-dfly) | Same as `run-commands`, but drives load with `dfly_bench` instead of memtier. | mutates |
| [`run-replication`](#run-replication) | Execute the Dragonfly master/replica sweep against a setup with `--replicas >= 1`. | mutates |
| [`quickstart`](#quickstart) | One-shot: `setup` + `run` + `destroy` in a single invocation. | mutates |
| [`compare`](#compare) | Diff two result files (auto-detects memtier vs replication) and exit non-zero on regression. | read-only |
Expand Down Expand Up @@ -623,6 +624,54 @@ benchmark. 100M x 256B assumes a ~128GB server; on smaller instances
lower it proportionally or the engines will evict or OOM during
preload.

### `run-commands-dfly`

The `dfly_bench` counterpart to `run-commands`: it drives the same
per-command flush/preload/warmup/trials cycle, but with Dragonfly's own
load generator instead of memtier. `dfly_bench` is built from the
`dragonflydb/dragonfly` source and installed on the client at `dfbench
setup` time (alongside memtier); override the build with
`--dfly-bench-ref`. Results use the same schema as `run-commands` (tagged
`tool="dfly_bench"`) and land in `~/.dfbench/runs/<id>/commands/<timestamp>/`,
so a memtier file and a `dfly_bench` file for the same command diff with
`dfbench compare`.

Because `dfly_bench` uses its own key prefix (`--key_prefix`, default
`key:`), read commands (GET, ...) are preloaded by `dfly_bench` itself
(sequential distribution, one connection per thread covering the whole
`--key-maximum` range) rather than reusing a memtier preload.

| Flag | Default | Notes |
| ---- | ------- | ----- |
| `--id` | (required) | Setup previously created by `dfbench setup`. |
| `--command` | (required) | Single command name, e.g. `SET`, `GET`, `LRANGE`. |
| `--engines` | `dragonfly` | Subset of the setup's installed engines (`dfly_bench` speaks RESP, so Redis/Valkey work too). |
| `--dfly-threads` | `32` | `dfly_bench --proactor_threads` (analogous to memtier threads). Total connections = threads * conns. |
| `--dfly-conns` | `5` | `dfly_bench -c`, connections per thread (analogous to memtier clients). |
| `--dfly-pipeline` | `20` | `dfly_bench --pipeline`: max pending requests per connection. |
| `--dfly-data-size` | `256` | `dfly_bench -d`: value size in bytes (`__data__` placeholders). |
| `--key-maximum` | `100000000` | Key range (`--key_maximum`). Structured preloads use a built-in 1M-key range. |
| `--key-dist` | `U` | `dfly_bench --key_dist` for the measured pass: `U` (uniform, the memtier random `R` equivalent), `N`, `Z`, `S`. |
| `--qps` | `0` | `dfly_bench --qps` per connection; `0` = closed-loop max load (coordinated omission). |
| `--test-time` | `300` | Seconds of measured load per trial. |
| `--warmup-time` | `10` | Seconds of discarded warmup load per engine. `0` disables. |
| `--trials` | `3` | Measured trials per engine (median with [min..max]). |
| `--dfly-bench-ref` | `main` | Git ref of `dragonflydb/dragonfly` to build `dfly_bench` from if it isn't already installed. |
| `--dragonfly_num_shards`, `--dragonfly_conn_use_incoming_cpu`, `--dragonfly_version` | (as in `run-commands`) | Server-side Dragonfly tuning; identical semantics to `run-commands`. |
| `--redis_io_threads`, `--valkey_io_threads`, `--redis_pin_network`, `--valkey_pin_network` | (as in `run-commands`) | Redis/Valkey tuning. |
| `--output` | `table` | `table` / `json` / `csv`. |

`dfly_bench`'s `--json_out_file` writes a memtier-compatible latency
report, so the same parser produces the throughput / p50 / p99 / p99.9 /
avg columns for both tools.

```bash
dfbench run-commands-dfly --id percmd \
--engines dragonfly \
--command GET \
--key-maximum 1000000 --test-time 15 --trials 1
```

### `run-replication`

Execute the Dragonfly master/replica benchmark scenario against an
Expand Down Expand Up @@ -1106,7 +1155,7 @@ their keyspace can empty mid-trial. A `results.csv` with one row per
| `run-replication` errors: `ERR Out of memory` during preload | Default `--preload-keys` and `--value-size` exceed the instance's memory budget. | Set `--master-flags "--maxmemory=25gb"` (or larger), lower `--preload-keys`, or shrink `--value-size`. Dragonfly will evict rather than OOM. |
| Maxload case appears to run at ~20 req/sec | `dfly_bench --qps` defaults to 20 when omitted. dfbench explicitly emits `--qps=0` for `maxload`; if you see this, verify `--dfly-ref` resolves to a revision whose `dfly_bench` treats 0 as unbounded. | See `BuildDflyBenchArgs` in [`bench/replication/dflybench.go`](bench/replication/dflybench.go). |
| `run` refuses to start with `setup "foo" is not ready (status="benchmarking")` | Previous run crashed or was killed before the deferred status-restore ran. | Rare now; `run` and `run-replication` wrap their context with `signal.NotifyContext(Interrupt, SIGTERM)` so `Ctrl+C` unwinds cleanly. If it happens: `dfbench destroy --id ...` and start over. |
| `dfly_bench` build fails on the client: missing `helio/blaze.sh` | Submodule population failed. | `InstallDflyBench` falls back to a full clone + `git submodule update --init --recursive`. If it still fails, check the error output for missing apt packages (`bison`, `libboost-fiber-dev`, `libunwind-dev`). |
| `dfly_bench` build fails on the client: missing `helio/blaze.sh` | Submodule population failed. | `InstallDflyBench` in [`bench/dflybench.go`](bench/dflybench.go) falls back to a full clone + `git submodule update --init --recursive`. If it still fails, check the error output for missing apt packages (`bison`, `libboost-fiber-dev`, `libunwind-dev`). |
| `destroy --id foo` returns `setup "foo" not found` | Typo, or state file moved / deleted. | `dfbench list` to see what IDs exist. Old versions leaked the internal path; the current message is deliberately terse. |

---
Expand Down
6 changes: 5 additions & 1 deletion bench/commands/results.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ const ScenarioTag = "commands"
// CommandRunResults is the results.json schema for one dfbench
// run-commands invocation.
type CommandRunResults struct {
Scenario string `json:"scenario"` // always "commands"
Scenario string `json:"scenario"` // always "commands"
// Tool is the load generator that produced these numbers: "memtier"
// (dfbench run-commands) or "dfly_bench" (dfbench run-commands-dfly).
// It lets 'dfbench compare' diff a memtier file against a dfly_bench one.
Tool string `json:"tool,omitempty"`
RunID string `json:"run_id"`
SetupID string `json:"setup_id"`
CreatedAt time.Time `json:"created_at"`
Expand Down
15 changes: 15 additions & 0 deletions bench/commands/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,20 @@ type Config struct {
// dfbench setup installed. Redis/Valkey ignore it.
DflyVersion string

// The fields below are only read by RunDfly (dfbench run-commands-dfly).
// The memtier Run ignores them.

// KeyDist is dfly_bench --key_dist: U (uniform, the memtier "R"
// equivalent), N (normal), Z (zipfian), S (sequential). Empty defaults
// to U for the measured pass.
KeyDist string
// QPS is dfly_bench --qps, a per-connection rate cap; 0 means closed-loop
// max load (coordinated omission).
QPS int
// DflyBenchRef is the dragonflydb/dragonfly git ref used to (re)build
// dfly_bench on the client if it isn't already installed.
DflyBenchRef string

OutputDir string
}

Expand Down Expand Up @@ -102,6 +116,7 @@ func Run(

run := &CommandRunResults{
Scenario: ScenarioTag,
Tool: "memtier",
RunID: time.Now().UTC().Format("20060102-150405"),
SetupID: state.ID,
CreatedAt: time.Now().UTC(),
Expand Down
Loading
Loading