A workload runner for testing Node.js Redis client fault tolerance against Redis database upgrades and performance testing.
- Docker
This application uses a local build of the node-redis library for testing hitless upgrades functionality. For local development without Docker, you have three options:
-
Clone the node-redis repository locally (recommended for development):
git clone --branch hitless-upgrades --single-branch https://github.com/nkaradzhov/node-redis.git node-redis cd node-redis npm install npm run build cd ..
-
Or change the Redis dependency to another version in
package.json:"redis": "5.7.0"
-
Or use the GitHub repository directly in
package.json:"redis": "github:nkaradzhov/node-redis#hitless-upgrades"
Then run
npm installto update the dependency.
When using Docker (recommended), the node-redis repository is automatically cloned and built during the container build process.
Build development image:
./run.sh build --devBuild production image (default):
./run.sh build --prod
# or simply
./run.sh buildDevelopment mode with hot reload:
./run.sh devProduction mode:
./run.sh startLocal development without Docker:
./run.sh localCustom workload and configuration:
./run.sh dev --workload workloads/custom-workload.yaml --replicas 3Run with custom workload:
./run.sh start --workload workloads/high-load-test.yamlDevelopment with multiple replicas:
./run.sh dev --replicas 5Local development with custom workload:
./run.sh local --workload workloads/high-load-test.yamlCustom run ID for tracking:
RUN_ID=performance-test-2024 ./run.sh startCombined configuration:
RUN_ID=cluster-fault-test ./run.sh dev --workload workloads/cluster-test.yaml --replicas 3Enable OpenTelemetry metrics:
ENABLE_OTEL=true ./run.sh devLocal development with OpenTelemetry:
ENABLE_OTEL=true ./run.sh localDisable pretty logging (use JSON format):
LOG_PRETTY=false ./run.sh localCustom metrics endpoint:
ENABLE_OTEL=true METRICS_EXPORTER_ENDPOINT=http://my-otel-collector:4318/v1/metrics ./run.sh startThe run.sh script supports the following options:
| Option | Description | Default Value |
|---|---|---|
--workload, -w |
Path to the workload configuration YAML file | ./workloads/example-workload.yaml |
--replicas, -r |
Number of application replicas to run | 1 |
--log-level, -l |
Log level (info or error) | info |
--help, -h |
Display help message |
These variables are used during Docker image build and must be set as build arguments:
| Variable | Description | Required | Default Value |
|---|---|---|---|
REPO_URL |
Git repository URL for node-redis library | No | https://github.com/nkaradzhov/node-redis.git |
REPO_BRANCH |
Git branch to clone from node-redis repository | No | hitless-upgrades |
These variables are used when running the application:
| Variable | Description | Required | Default Value |
|---|---|---|---|
RUN_ID |
Unique identifier for the test run | Yes | Auto-generated |
WORKLOAD |
Path to the workload configuration YAML file | Yes | ./workloads/example-workload.yaml |
INSTANCE_ID |
Instance identifier (auto-generated if not provided) | No | Auto-generated |
ENABLE_OTEL |
Enable OpenTelemetry metrics collection | No | false |
METRICS_INTERVAL_MS |
Metrics reporting interval in milliseconds | No | 1000 |
METRICS_EXPORTER_ENDPOINT |
OTLP metrics exporter endpoint | No | http://host.docker.internal:4318/v1/metrics |
APP_NAME |
Application name for metrics labeling | No | node-redis-test |
VERSION |
Application version for metrics labeling | No | 1.0.0 |
LOG_LEVEL |
Logging level (info, error) | No | info |
LOG_PRETTY |
Enable pretty-printed logs for development | No | false (JSON), true for local mode |
NODE_ENV |
Node.js environment (development, production) | No | production |
DEBUG_MAINTENANCE |
Enable debug logging for maintenance procedures | No | false |
Configuration is defined in YAML files. See workloads/example-workload.yaml for a complete example.
Note: Workload files should be placed in the workloads/ directory as this folder is mounted as a volume to the Docker container, making the configuration files accessible during execution.
| Configuration Path | Required | Description | Accepted Values | Default Value |
|---|---|---|---|---|
| Redis Configuration | ||||
runner.redis.host |
Yes | Redis server hostname | string | |
runner.redis.port |
Yes | Redis server port | number (1-65535) | |
runner.redis.username |
No | ACL username | string | |
runner.redis.password |
No | ACL password | string | |
runner.redis.database |
No | Redis database number | number (≥0) | |
runner.redis.timeout |
No | Connection timeout | ISO 8601 duration | |
| Test Configuration | ||||
runner.test.mode |
Yes | Test mode | standalone, cluster |
|
runner.test.clients |
Yes | Number of concurrent clients | number (≥1) | |
runner.test.outputFilename |
No | Output filename for test results | string | results |
runner.test.workload.type |
Yes | Workload type | get_set, redis_commands, multi, pub_sub |
|
runner.test.workload.maxDuration |
Yes | Maximum test duration | ISO 8601 duration, "endless" | |
| Workload Options | ||||
runner.test.workload.options.batchSize |
Yes | Number of operations per batch | number (≥1) | 50 |
runner.test.workload.options.getSetRatio |
Yes | Ratio of GET to SET operations | number (0.0-1.0) | 0.5 |
runner.test.workload.options.valueSize |
Yes | Size of values in bytes | number (≥1) | 100 |
runner.test.workload.options.iterationCount |
No | Total number of iterations (optional) | number (≥1) or null | |
runner.test.workload.options.delayAfterIteration |
No | Delay between iterations | ISO 8601 duration | |
runner.test.workload.options.elementsCount |
Yes | Number of elements for list operations | number (≥1) | 5 |
runner.test.workload.options.transactionSize |
Yes | Number of commands per transaction | number (≥1) | 10 |
runner.test.workload.options.keyGenerationStrategy |
Yes | Key generation strategy | random, sequential |
random |
runner.test.workload.options.keyPattern |
Yes | Pattern for key generation (use %d for number) | string with %d placeholder | key-%d |
runner.test.workload.options.keyRangeMin |
Yes | Minimum value for key range | number (≥0) | 0 |
runner.test.workload.options.keyRangeMax |
Yes | Maximum value for key range | number (≥1) | 99999 |
| Client Options | ||||
runner.clientOptions.RESP |
No | Redis protocol version | 2, 3 |
|
runner.clientOptions.commandsQueueMaxLength |
No | Max command queue length | number (≥1) | |
runner.clientOptions.disableOfflineQueue |
No | Disable offline queuing | boolean | |
runner.clientOptions.readonly |
No | Readonly mode | boolean | |
runner.clientOptions.name |
No | Client name | string | |
runner.clientOptions.disableClientInfo |
No | Disable client info | boolean | |
runner.clientOptions.clientInfoTag |
No | Tag to append to library name | string | |
runner.clientOptions.pingInterval |
No | Send PING command at interval |
ISO 8601 duration | |
| Client Socket Options | ||||
runner.clientOptions.socket.noDelay |
No | Toggle Nagle's algorithm | boolean | |
runner.clientOptions.socket.keepAlive |
No | Toggle keep-alive functionality | boolean | |
runner.clientOptions.socket.keepAliveInitialDelay |
No | Keep-alive initial delay | ISO 8601 duration | |
runner.clientOptions.socket.tls |
No | Enable TLS/SSL | boolean | |
runner.clientOptions.socket.rejectUnauthorized |
No | Verify server certificate | boolean | |
runner.clientOptions.socket.socketTimeout |
No | Socket timeout | ISO 8601 duration | |
runner.clientOptions.socket.ca |
No | Certificate Authority file path | string (file path) | |
runner.clientOptions.socket.cert |
No | Client certificate file path | string (file path) | |
runner.clientOptions.socket.key |
No | Client private key file path | string (file path) | |
runner.clientOptions.socket.passphrase |
No | Private key passphrase | string | |
| Command Options | ||||
runner.clientOptions.commandOptions.timeout |
No | Command timeout | ISO 8601 duration | |
| Redis Enterprise Maintenance Options | ||||
runner.clientOptions.maintNotifications |
No | Push notifications during maintenance | disabled, enabled, auto |
|
runner.clientOptions.maintEndpointType |
No | Moving endpoint type during maintenance | auto, internal-ip, internal-fqdn, external-ip, external-fqdn, none |
|
runner.clientOptions.maintRelaxedCommandTimeout |
No | Command timeout during maintenance | ISO 8601 duration | |
runner.clientOptions.maintRelaxedSocketTimeout |
No | Socket timeout during maintenance | ISO 8601 duration | |
| Cluster Specific Configuration | ||||
runner.clusterClientOptions.minimizeConnections |
No | Minimize connections | boolean | |
runner.clusterClientOptions.useReplicas |
No | Use replica nodes for reads | boolean | |
runner.clusterClientOptions.maxCommandRedirections |
No | Max command redirections | number (≥1) |
To view metrics locally, clone and set up the observability stack:
git clone https://github.com/redis-developer/observability-stack
cd observability-stack
# Follow the setup instructions in the repositoryOnce the observability stack is running, you can collect metrics by setting the METRICS_EXPORTER_ENDPOINT environment variable:
- For local development:
http://localhost:4318/v1/metrics - When running both this app and observability stack in Docker:
http://host.docker.internal:4318/v1/metrics - Alternatively, configure both to use the same Docker network and use the service name as hostname
All Redis testing applications send these standardized metrics:
| Metric | Type | Description |
|---|---|---|
redis_operations_total |
Counter | Total Redis operations executed |
redis_operation_duration |
Histogram | Operation latency in milliseconds |
redis_connections_total |
Counter | Connection attempts (success/failure) |
redis_reconnection_duration_ms |
Histogram | Reconnection time |
All metrics include these labels for filtering and grouping:
| Label | Description | Example Values |
|---|---|---|
app_name |
Application name | node-redis-test |
instance_id |
Unique instance identifier | abc123def456 |
version |
Application version | 1.0.0 |
run_id |
Test run identifier | performance-test-2024 |
operation |
Redis command name | GET, SET, LPUSH |
status |
Operation result | success, error |
error_type |
Error classification | timeout, connection_error, none |
After each test run, the application generates output files in the out/ directory with the following structure:
out/
└── {RUN_ID}/
└── {INSTANCE_ID}/
├── results.json # Test results and metrics
├── config.json # Workload configuration used
└── env.json # Environment variables used
| File | Description | Contents |
|---|---|---|
results.json |
Test execution results and performance metrics | Test duration, command counts, success rates, latency percentiles, throughput |
config.json |
Workload configuration that was used for the test | Complete workload YAML configuration (sensitive fields redacted) |
env.json |
Environment variables that were active during the test | All environment variables (sensitive fields redacted) |
The results filename can be customized using the runner.test.outputFilename configuration option.
The application is designed to run in Docker to support multiple instances with unique INSTANCE_ID values. The out/ directory is mounted as a volume, hence the path is not configurable.
Workarounds:
- Use meaningful
RUN_IDvalues:RUN_ID=test-$(date +%Y%m%d) ./run.sh start - Copy files after completion:
cp -r out/your-run-id/ /custom/location/