WuKongIM Docs

wkcli bench

Run black-box validation, real workloads, capacity searches, and regression gates against a controlled cluster.

wkcli bench is WuKongIM's black-box benchmark driver. It talks to a running cluster through public HTTP, Benchmark HTTP, and WKProto gateway endpoints without importing server internals or bypassing cluster semantics. A one-node target is still a single-node cluster.

Do not use production as a load target

wkcli bench creates real users, Channels, connections, and messages and may drive a target into backpressure or unavailability. Use only an isolated, rebuildable, authorized benchmark cluster, with hard limits for rate, concurrency, duration, disk, and stopping.

Command scope

CommandPurpose
validateStatically validate target, workers, and scenario YAML plus deterministic planning, without network checks
doctorCheck target health, Benchmark API, worker control APIs, and gateway reachability
workerStart a worker control process that holds WKProto clients and executes workload shards
runExecute validate, preflight, assign, prepare, connect, warmup, run, cooldown, and report
dev-simKeep users online and sustain low-rate person/group traffic for development
capacity sendSearch maximum stable ingress send QPS against an existing cluster
capacity hot-channelSearch hot-write capacity for one fixed group Channel
capacity activate-channelsActivate and hold a fixed number of real Channel runtimes
capacity message-eventApply fixed-shape /message/event pressure and write a report
metrics classifyCompare before/after Prometheus snapshots and emit low-cardinality attribution hints
reportRender benchmark evidence and diagnostic reports

Target prerequisites

A complete workflow usually requires:

  • /healthz and /readyz;
  • /bench/v1/capabilities, /bench/v1/capacity-target, and /bench/v1/snapshot;
  • Benchmark user, Channel, and subscriber preparation endpoints;
  • a WKProto gateway advertised at an address reachable from the runner.

Explicitly enable the Benchmark API in a controlled environment:

[bench]
api_enable = true

/bench/v1/* is not a public product API and must not be exposed to a public network. capacity message-event is the exception: it uses product /channel, /message/send, /message/event, and /metrics endpoints and does not need the Benchmark API. It still writes generated Channels and messages, so it also requires a controlled target.

Minimum validation flow

Download target.yaml, workers.yaml, and scenario.yaml. This smoke workload has 20 online users and two 10-member groups sending 5 messages/second each. It measures 60 seconds, plus 10 seconds each for warmup and cooldown; it is not a production capacity claim.

First install wkcli. The following commands use the installed binary and do not require a source checkout or Go. Download the configurations into a working directory:

mkdir -p ./tmp/docs-wkbench
for file in target workers scenario; do
  curl --fail --location "https://docs.githubim.com/examples/wkbench/${file}.yaml" \
    --output "./tmp/docs-wkbench/${file}.yaml"
done

Edit target.yaml with the isolated test cluster's API, Gateway, and metrics endpoints. Defaults assume the test node, worker, and coordinator share a host; for separate hosts, make every endpoint reachable from the process that uses it.

Use the same worker control token in both terminals. The coordinator's API token must match bench.api_token on the test node, which also needs bench.api_enable and observability.metrics_enable. Do not use production credentials here.

Terminal A, start the worker:

export WK_BENCH_WORKER_TOKEN='replace-with-test-worker-secret'
wkcli bench worker \
  --listen 127.0.0.1:19090 \
  --work-dir ./tmp/docs-wkbench/worker

Terminal B, configure this run. Change WK_BENCH_RUN_ID for every run; do not reuse an existing identity:

export WK_BENCH_WORKER_TOKEN='replace-with-test-worker-secret'
export WK_BENCH_API_TOKEN='replace-with-test-api-secret'
export WK_BENCH_RUN_ID="docs-smoke-$(date -u +%Y%m%dT%H%M%SZ)"
wkcli bench validate \
  --target ./tmp/docs-wkbench/target.yaml \
  --workers ./tmp/docs-wkbench/workers.yaml \
  --scenario ./tmp/docs-wkbench/scenario.yaml

validate does not access the network. After it passes, run preflight. If preflight fails, fix the target, tokens, or endpoints before running traffic:

wkcli bench doctor \
  --target ./tmp/docs-wkbench/target.yaml \
  --workers ./tmp/docs-wkbench/workers.yaml \
  --scenario ./tmp/docs-wkbench/scenario.yaml

Once preflight passes, execute the bounded workload:

wkcli bench run \
  --target ./tmp/docs-wkbench/target.yaml \
  --workers ./tmp/docs-wkbench/workers.yaml \
  --scenario ./tmp/docs-wkbench/scenario.yaml

The example fails on any connection, send-acknowledgement, receive-verification, or worker error, and also fails when P99 exceeds the configured limits. Check the exit code and report, then stop Terminal A's worker with Ctrl+C. cleanup.strategy=keep_data retains generated users, groups, and messages. Handle them through your isolated test environment's retention plan; do not delete cluster data directories to clean up one run.

Design a representative workload

  • Model realistic online-user count, Channel cardinality and types, group size, payload size, acknowledgements, and reconnect behavior.
  • Separate connection ramp, warmup, measurement, and cooldown; warmup counters must not enter the measured window.
  • Test high Channel cardinality, one hot Channel, message events, and connection pressure separately rather than reducing every bottleneck to one QPS number.
  • For 100,000-member groups, high message rates, many Channels, and many online users, account explicitly for CPU, memory, allocations, contention, bounded queues, backpressure, and fanout.
  • Fix the random seed or generation rule and retain scenario YAML, tool/server revisions, hardware, topology, and configuration.
wkcli bench capacity send \
  --api http://127.0.0.1:5001 \
  --profile mixed \
  --start-qps 100 \
  --max-qps 5000 \
  --stable-p99 200ms \
  --duration 30s \
  --group-members 10

capacity send discovers gateways, starts a temporary local worker, and searches for a stable ingress rate under the supplied gates. It does not start or stop the cluster, build images, or remove data. Use separate capacity subcommands for a hot Channel, Channel-runtime cardinality, and message events.

“Maximum stable” applies only to this revision, hardware, topology, scenario, and gate. Plan below the first failure point with headroom. Actual QPS below offered QPS, tail latency, error rate, queues, disk, and recovery time are all results; never retain only the highest number.

Results and stop conditions

A report should retain at least:

  • Git revision, binary digests, configuration, and cluster topology.
  • Target/worker/scenario files and the complete command.
  • Timing and status for prepare, connect, warmup, run, and cooldown.
  • Offered/actual QPS, throughput, p50/p95/p99, typed operation errors, and timeouts.
  • Per-node CPU, memory, goroutines, FDs, network, disk IO, queues, and replica/leader skew.
  • Before/after /readyz, backlog recovery time, generated-data location, and cleanup result.

Stop on any hard gate, readiness, disk-headroom, error-rate, tail-latency, or worker-state failure. Do not raise limits merely to obtain a larger number. See Diagnostics for evidence selection.

Cleanup

Stop workers and simulators, revoke temporary credentials, disable the Benchmark API, archive reports, remove generated data, and verify the cluster returns to its idle baseline. If worker stop or target recovery cannot be confirmed, treat the run as an unfinished incident, not a successful benchmark.

Read the report

In the report directory selected by run.report_dir, read summary.md and report.json first, then use the saved configurations, plan.json, and metrics/ to explain the result. Before comparing runs, separately record server version or digest, SDK/tool version, CPU/memory/disk type per node, node count, replicas, and network conditions. Tool output does not replace a complete hardware inventory.

The following uses illustrative values, not measured benchmark results: the same server and tool versions, one 4-vCPU / 8-GiB / SSD single-node cluster with the default 256 hash slots, and the smoke scenario above.

Report itemIllustrative observationInterpretation
Planned rate2 groups × 5 messages/second = 10/secondTarget send rate, not achieved successful throughput
summary.ingress_qps9.8Below the target; inspect latency, errors, and scheduling
summary.sendack_max_worker_p99180 msBelow the example 200-ms limit; the maximum worker-local P99, not a merged percentile across all messages
summary.recv_verify_error_rate0This example samples 2 recipients per message, so it does not prove delivery to every member
Delivery queueGrows during measurement and remains after cooldownPassing send metrics alone does not prove sustainable capacity

Go time.Duration fields in JSON use nanoseconds; 180 ms is 180000000. Prefer formatted latency values in summary.md. Increase load only after the measurement window, achieved throughput, errors, tail latency, recipient coverage, queue recovery, and resource headroom all satisfy your targets.

On this page