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
109 changes: 108 additions & 1 deletion .github/workflows/benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,20 @@ name: Benchmark

on:
workflow_dispatch:
inputs:
engines:
description: 'Which engine benchmarks to run'
type: choice
default: all
options:
- all
- java
- ruby
- node

jobs:
benchmark-java:
if: ${{ inputs.engines == 'all' || inputs.engines == 'java' }}
runs-on: ubuntu-latest

strategy:
Expand Down Expand Up @@ -85,6 +96,7 @@ jobs:
retention-days: 30

benchmark-ruby:
if: ${{ inputs.engines == 'all' || inputs.engines == 'ruby' }}
runs-on: ubuntu-latest

strategy:
Expand Down Expand Up @@ -152,8 +164,86 @@ jobs:
path: ${{ steps.names.outputs.result_file }}
retention-days: 30

benchmark-node:
if: ${{ inputs.engines == 'all' || inputs.engines == 'node' }}
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
driver:
- configs/drivers/default/valkey-glide-node.json
- configs/drivers/default/ioredis.json
- configs/drivers/default/iovalkey.json
workload:
- configs/workloads/reference/basic-standalone-single-client-1M-reqs.json

steps:
- uses: actions/checkout@v4

# Node 22 LTS: ioredis 6 and node-redis 6 both require >= 20, and 18 is EOL.
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: npm
cache-dependency-path: node/package-lock.json

- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install -y build-essential

- name: Start Valkey server
run: |
# Use Makefile target which builds from source and configures with persistence disabled
make server-standalone-start
# Wait for server to be ready
sleep 2
# Verify server is up and persistence is disabled
work/valkey/bin/valkey-cli ping
work/valkey/bin/valkey-cli CONFIG GET save

- name: Build Node.js benchmark
run: cd node && npm ci && npm run build

- name: Extract names for result file
id: names
run: |
DRIVER_NAME=$(basename ${{ matrix.driver }} .json)
WORKLOAD_NAME=$(basename ${{ matrix.workload }} .json)
echo "driver_name=$DRIVER_NAME" >> $GITHUB_OUTPUT
echo "workload_name=$WORKLOAD_NAME" >> $GITHUB_OUTPUT
echo "result_file=results/github-runner/reference/${DRIVER_NAME}-${WORKLOAD_NAME}.ndjson" >> $GITHUB_OUTPUT

- name: Run benchmark
run: |
mkdir -p results/github-runner/reference
node node/dist/src/cli.js \
--server localhost:6379 \
--driver ${{ matrix.driver }} \
--workload ${{ matrix.workload }} \
--metrics ${{ steps.names.outputs.result_file }} \
--commit-id ${{ github.sha }}

- name: Stop Valkey server
if: always()
run: |
make server-standalone-stop || true

- name: Upload results
uses: actions/upload-artifact@v4
with:
name: benchmark-node-${{ steps.names.outputs.driver_name }}-${{ steps.names.outputs.workload_name }}
path: ${{ steps.names.outputs.result_file }}
retention-days: 30

generate-graphs:
needs: [benchmark-java, benchmark-ruby]
needs: [benchmark-java, benchmark-ruby, benchmark-node]
# A skipped `needs` job would skip this one by default, so an engine-scoped
# run would produce no graphs at all. Individual graph steps are gated on the
# same input instead.
if: ${{ !cancelled() }}
runs-on: ubuntu-latest
permissions:
contents: write
Expand Down Expand Up @@ -185,6 +275,7 @@ jobs:

# TODO - rework with matrix orchistrator
- name: Generate Java graphs - 1 Client
if: ${{ inputs.engines == 'all' || inputs.engines == 'java' }}
run: |
python scripts/generate_graphs.py \
--results "results/github-runner/reference/*-basic-standalone-single-client-1M-reqs.ndjson" \
Expand All @@ -195,6 +286,7 @@ jobs:
--commit-id ${{ github.sha }}

- name: Generate Java graphs - 10 Clients
if: ${{ inputs.engines == 'all' || inputs.engines == 'java' }}
run: |
python scripts/generate_graphs.py \
--results "results/github-runner/reference/*-basic-standalone-10-clients.ndjson" \
Expand All @@ -205,6 +297,7 @@ jobs:
--commit-id ${{ github.sha }}

- name: Generate Java graphs - 100 Clients
if: ${{ inputs.engines == 'all' || inputs.engines == 'java' }}
run: |
python scripts/generate_graphs.py \
--results "results/github-runner/reference/*-basic-standalone-100-clients.ndjson" \
Expand All @@ -216,6 +309,7 @@ jobs:

# Ruby graphs — per concurrency level
- name: Generate Ruby graphs - 1 Client
if: ${{ inputs.engines == 'all' || inputs.engines == 'ruby' }}
run: |
python scripts/generate_graphs.py \
--results "results/github-runner/reference/*-basic-standalone-single-client-1M-reqs.ndjson" \
Expand All @@ -226,6 +320,7 @@ jobs:
--commit-id ${{ github.sha }}

- name: Generate Ruby graphs - 10 Clients
if: ${{ inputs.engines == 'all' || inputs.engines == 'ruby' }}
run: |
python scripts/generate_graphs.py \
--results "results/github-runner/reference/*-basic-standalone-10-clients.ndjson" \
Expand All @@ -236,6 +331,7 @@ jobs:
--commit-id ${{ github.sha }}

- name: Generate Ruby graphs - 100 Clients
if: ${{ inputs.engines == 'all' || inputs.engines == 'ruby' }}
run: |
python scripts/generate_graphs.py \
--results "results/github-runner/reference/*-basic-standalone-100-clients.ndjson" \
Expand All @@ -245,6 +341,17 @@ jobs:
--workload "Ruby - 100 Clients" \
--commit-id ${{ github.sha }}

- name: Generate Node.js graphs - 1 Client
if: ${{ inputs.engines == 'all' || inputs.engines == 'node' }}
run: |
python scripts/generate_graphs.py \
--results "results/github-runner/reference/*-basic-standalone-single-client-1M-reqs.ndjson" \
--output graphs/node/1-client/ \
--phase STEADY \
--language node \
--workload "Node.js - 1 Client" \
--commit-id ${{ github.sha }}

- name: Upload graphs
uses: actions/upload-artifact@v4
with:
Expand Down
65 changes: 62 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ WORK_DIR=$(shell pwd)/work
python-build python-test python-run python-clean \
ruby-build ruby-test ruby-run ruby-clean ruby-info \
csharp-build csharp-test csharp-run csharp-clean csharp-info \
node-build node-deps node-test node-unit-test node-integration-test \
node-run node-clean node-info \
config-editor-build config-editor-dev

# ============================================================================
Expand Down Expand Up @@ -74,6 +76,13 @@ help:
@echo " make csharp-clean Clean C# build artifacts"
@echo " make csharp-info Show supported C# drivers and commands"
@echo ""
@echo "Node.js Engine:"
@echo " make node-build Install deps and compile the Node.js engine"
@echo " make node-test Run Node.js tests (unit + integration)"
@echo " make node-run Run Node.js benchmark (requires DRIVER and WORKLOAD)"
@echo " make node-clean Clean Node.js build artifacts"
@echo " make node-info Show supported Node.js drivers and commands"
@echo ""
@echo "Config Editor:"
@echo " make config-editor-build Build config editor UI"
@echo " make config-editor-dev Run config editor in development mode"
Expand Down Expand Up @@ -387,6 +396,56 @@ csharp-clean:
csharp-info: csharp-build
dotnet run --project $(CSHARP_PROJECT) -c Release -- --info

# ============================================================================
# Node.js Engine
# ============================================================================

# tsc emits into node/dist mirroring the source tree, so src/cli.ts -> dist/src/cli.js
NODE_CLI=node/dist/src/cli.js

# Stamp file so `npm ci` runs only when the manifests actually change. The matrix
# runner invokes `make node-run` once per cell, and `npm ci` deletes and
# reinstalls node_modules every time it runs — on a 75-cell sweep that is ~15
# minutes of pure reinstall plus 75 chances for a network blip mid-sweep. The
# stamp lives inside node_modules, so wiping that directory correctly forces a
# reinstall. `npm run build` stays on every invocation: tsc is incremental and
# no-ops in well under a second.
NODE_DEPS_STAMP=node/node_modules/.resp-bench-deps-stamp

$(NODE_DEPS_STAMP): node/package.json node/package-lock.json
cd node && npm ci
@touch $@

node-deps: $(NODE_DEPS_STAMP)

node-build: node-deps
cd node && npm run build

node-test: node-unit-test node-integration-test

node-unit-test: node-build
cd node && node --test dist/test/unit/

# The live-server tests skip themselves unless VALKEY_HOST is set, so the server
# has to be up before this runs.
node-integration-test: node-build server-standalone-start
sleep 1
cd node && VALKEY_HOST=localhost VALKEY_PORT=6379 node --test dist/test/integration/
$(MAKE) server-standalone-stop

node-run: node-build
node $(NODE_CLI) \
--server $(SERVER) \
--driver $(DRIVER) \
--workload $(WORKLOAD) \
--metrics $(METRICS_OUTPUT)

node-info: node-build
node $(NODE_CLI) --info

node-clean:
cd node && rm -rf dist node_modules coverage

# ============================================================================
# Config Editor
# ============================================================================
Expand Down Expand Up @@ -447,8 +506,8 @@ test-scripts-all: java-build
# All Languages
# ============================================================================

build-all: java-build ruby-build csharp-build python-build
build-all: java-build ruby-build csharp-build node-build python-build

test-all: java-test ruby-test csharp-test python-test
test-all: java-test ruby-test csharp-test node-test python-test

clean-all: java-clean ruby-clean csharp-clean python-clean clean
clean-all: java-clean ruby-clean csharp-clean node-clean python-clean clean
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ A multi-language benchmark suite for RESP protocol (Redis/Valkey) compatible dat
### Prerequisites

- Python 3.8+, Java 21+, Maven
- Node.js 20+ (for the Node.js engine)
- Make
- A server CLI (`valkey-cli`) for the matrix runner's readiness probe and per-cell
FLUSHALL — the Makefile's `server-*` targets build one into
Expand Down Expand Up @@ -103,6 +104,7 @@ Thread-based system metrics collector that runs alongside benchmarks, collecting
| Java | ✅ Ready | Jedis, Lettuce, Valkey-Glide, Redisson, Spring Data Valkey/Redis |
| Ruby | ✅ Ready | redis-rb, valkey-glide-ruby |
| C# | ✅ Ready | valkey-glide-csharp, StackExchange.Redis |
| Node.js | ✅ Ready | valkey-glide-node, ioredis, iovalkey |
| Python | 🚧 Planned | redis-py, aioredis, valkey-glide |

## Project Structure
Expand Down Expand Up @@ -133,14 +135,16 @@ resp-bench/
├── java/ # Java benchmark engine
├── ruby/ # Ruby benchmark engine
├── csharp/ # C# (.NET 10) benchmark engine
├── node/ # Node.js (TypeScript) benchmark engine
├── docs/
│ ├── ARCHITECTURE.md # System architecture
│ ├── BENCHMARK_MATRIX.md # Matrix orchestrator docs
│ ├── INTERACTIVE_GRAPHS.md # Graph generator docs
│ ├── CONFIG_SPECIFICATION.md # Configuration format spec
│ ├── BENCHMARKS_JAVA.md # Java benchmark details
│ ├── BENCHMARKS_CSHARP.md # C# benchmark details
│ └── BENCHMARKS_RUBY.md # Ruby benchmark details
│ ├── BENCHMARKS_RUBY.md # Ruby benchmark details
│ └── BENCHMARKS_NODE.md # Node.js benchmark details
└── graphs/interactive/ # Generated HTML graphs
```

Expand Down Expand Up @@ -197,6 +201,7 @@ See [docs/CONFIG_SPECIFICATION.md](docs/CONFIG_SPECIFICATION.md) for full detail
| `make java-test` | Run Java unit tests |
| `make ruby-test` | Run Ruby tests |
| `make csharp-test` | Run C# tests |
| `make node-test` | Run Node.js tests (unit + integration) |

### Engines

Expand All @@ -205,8 +210,10 @@ See [docs/CONFIG_SPECIFICATION.md](docs/CONFIG_SPECIFICATION.md) for full detail
| `make java-run` | Run Java engine (DRIVER, WORKLOAD, SERVER) |
| `make ruby-run` | Run Ruby engine (DRIVER, WORKLOAD, SERVER) |
| `make csharp-run` | Run C# engine (DRIVER, WORKLOAD, SERVER) |
| `make node-run` | Run Node.js engine (DRIVER, WORKLOAD, SERVER) |
| `make java-build` | Build Java JAR |
| `make csharp-build` | Build C# executable |
| `make node-build` | Install deps and compile the Node.js engine |

### Server Management

Expand Down
7 changes: 6 additions & 1 deletion config-editor/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,12 @@ interface CommandConfig {
data_size_bytes?: number
}

const DRIVERS = ['jedis', 'lettuce', 'valkey-glide', 'redisson', 'spring-data-valkey', 'spring-data-redis']
const DRIVERS = [
// Java
'jedis', 'lettuce', 'valkey-glide', 'redisson', 'spring-data-valkey', 'spring-data-redis',
// Node.js
'valkey-glide-node', 'ioredis', 'iovalkey',
]
const COMMANDS = ['set', 'get', 'ping']
const ALGORITHMS = ['sequential_int', 'uniform_rand']

Expand Down
7 changes: 7 additions & 0 deletions configs/drivers/default/ioredis.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"schema_version": "1.0",
"description": "ioredis client - default configuration",
"driver_id": "ioredis",
"mode": "standalone",
"specific_driver_config": {}
}
7 changes: 7 additions & 0 deletions configs/drivers/default/iovalkey.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"schema_version": "1.0",
"description": "iovalkey client - default configuration",
"driver_id": "iovalkey",
"mode": "standalone",
"specific_driver_config": {}
}
7 changes: 7 additions & 0 deletions configs/drivers/default/valkey-glide-node.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"schema_version": "1.0",
"description": "Valkey GLIDE for Node.js client - default configuration",
"driver_id": "valkey-glide-node",
"mode": "standalone",
"specific_driver_config": {}
Comment thread
Aryex marked this conversation as resolved.
}
7 changes: 7 additions & 0 deletions configs/drivers/example-ioredis-standalone.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"schema_version": "1.0",
"description": "ioredis client, standalone mode",
"driver_id": "ioredis",
"mode": "standalone",
"specific_driver_config": {}
}
7 changes: 7 additions & 0 deletions configs/drivers/example-iovalkey-standalone.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"schema_version": "1.0",
"description": "iovalkey client, standalone mode",
"driver_id": "iovalkey",
"mode": "standalone",
"specific_driver_config": {}
}
7 changes: 7 additions & 0 deletions configs/drivers/example-valkey-glide-node-standalone.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"schema_version": "1.0",
"description": "Valkey GLIDE for Node.js client, standalone mode",
"driver_id": "valkey-glide-node",
"mode": "standalone",
"specific_driver_config": {}
}
8 changes: 8 additions & 0 deletions configs/drivers/high-throughput/ioredis.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"schema_version": "1.0",
"description": "ioredis client - high-throughput configuration",
"driver_id": "ioredis",
"mode": "standalone",
"specific_driver_config": {},
"command_timeout_ms": 10000
}
Loading
Loading