Skip to content

About

A lightweight, multi-threaded Redis-compatible database server built from scratch in Java.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Java Redis Server

CI Java 21

A Redis-compatible, in-memory key-value server written from scratch in Java 21, with no dependencies. It speaks the real Redis protocol (RESP2), so the official redis-cli and redis-benchmark tools work against it unchanged.

Features

  • RESP2 protocol parser that streams commands from the socket, so it correctly handles commands split across TCP packets and thousands of pipelined commands in one packet. Inline (plain-text) commands work too.
  • 30+ commands: GET SET (EX PX EXAT PXAT NX XX KEEPTTL GET) SETNX MSET MGET DEL UNLINK EXISTS INCR DECR INCRBY DECRBY APPEND STRLEN EXPIRE PEXPIRE PEXPIREAT PERSIST TTL PTTL TYPE KEYS DBSIZE FLUSHALL FLUSHDB PING ECHO SELECT INFO CONFIG GET QUIT
  • Key expiry the way Redis does it: lazily when a key is read, plus an active expiry cycle 10 times a second that samples keys with a TTL and deletes expired ones.
  • Append-only file (AOF) persistence with fsync every second. Relative TTLs are logged as absolute timestamps so a restart never extends a key's life, and a file damaged by a crash mid-write is repaired automatically by truncating the partial command.
  • Concurrency: one Java 21 virtual thread per client; lock-free reads on a ConcurrentHashMap; atomic read-modify-write (INCR, APPEND, SET NX). With persistence on, 64 striped locks keep the AOF in the same order the writes were applied.
  • Pipelining-aware output: replies are buffered and flushed once per batch of pipelined commands, not once per command.

Benchmarks

Measured in CI with the official redis-benchmark on a standard GitHub Actions ubuntu-latest runner, after JIT warm-up. Real Redis was run on the same runner, with the same settings, for comparison. The benchmark client shares the machine with the server.

Scenario This server Redis (same runner) Ratio
SET, 50 clients ~49,000 req/s ~73,000 req/s 67%
GET, 50 clients ~49,000 req/s ~72,000 req/s 69%
SET, 50 clients, pipeline 16 ~663,000 req/s ~1,053,000 req/s 63%
GET, 50 clients, pipeline 16 ~672,000 req/s ~1,187,000 req/s 57%
GET, 1,000 concurrent clients ~44,000 req/s ~71,000 req/s 62%

Numbers vary by machine and from run to run. Reproduce them with bash bench/run.sh; CI runs it on every push, shows the table in the job summary and uploads it as the benchmark-results artifact.

Tests

39 JUnit 5 tests run in GitHub Actions on every push:

  • Protocol: pipelined input, byte-at-a-time delivery, binary-safe values, malformed input.
  • Data store: TTL edge cases with an injectable clock, active expiry, NX/XX/KEEPTTL, overflow.
  • Concurrency: 64 threads × 10,000 INCR with no lost updates; exactly one winner for SET NX.
  • Persistence: restart recovery, TTLs surviving downtime correctly, crash-damaged file repair.
  • End-to-end over TCP: 10,000 pipelined commands answered in order, 200 concurrent clients, a protocol error on one connection not affecting others, data surviving a server restart.
mvn verify          # build, run all tests, write a coverage report to target/site/jacoco

Run it

mvn -q package -DskipTests
java -jar target/codecrafters-redis.jar --port 6379 --appendonly yes --dir ./data
redis-cli SET greeting hello EX 60
redis-cli GET greeting

With Docker

docker build -t java-redis-server .
docker run -p 6379:6379 -v redis-data:/data java-redis-server

The container runs as a non-root user with persistence enabled, storing data in the /data volume. It also honours a PORT environment variable, which hosting platforms set automatically.

Project structure

src/main/java/
├── Main.java                  # entry point and command-line options
└── redis/
    ├── RedisServer.java       # TCP accept loop, virtual thread per client, background tasks
    ├── RespParser.java        # streaming RESP2 parser
    ├── Resp.java              # reply encoding
    ├── CommandHandler.java    # command dispatch, AOF logging, write ordering
    ├── DataStore.java         # concurrent key-value store with expiry
    ├── AppendOnlyFile.java    # AOF persistence and crash recovery
    └── Glob.java              # KEYS pattern matching
src/test/java/redis/           # unit, concurrency, persistence and integration tests
bench/run.sh                   # redis-benchmark script
.github/workflows/ci.yml       # tests, benchmark and Docker build on every push

Started as a CodeCrafters "Build your own Redis" challenge, then extended with persistence, expiry, pipelining, tests and benchmarks.

About

A lightweight, multi-threaded Redis-compatible database server built from scratch in Java.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages