CLI tool
tokio-rs/mini-redis avatar
tokio-rs/mini-redis

mini-redis: five commands and nine Tokio patterns, on purpose

Incomplete Redis client and server implementation using Tokio - for learning purposes only

4,811 stars587 forksRustMIT

At a glance

What is it?
mini-redis is an incomplete Redis client and server in Rust, written to demonstrate asynchronous Tokio patterns rather than to be usable in production. It implements PING, GET, SET, PUBLISH and SUBSCRIBE, has no persistence, and every omission is deliberate.
Who is it for?
Use mini-redis if you are learning asynchronous Rust and want one readable codebase that shows a TCP server, a wire protocol framed through an intermediate type, graceful shutdown, a connection semaphore, pub/sub and mocked time in tests. Do not use it as a Redis client, a Redis server or a protocol reference: it supports five commands, omits parts of the wire protocol by design, has no persistence, and the README asks you to use a fully featured alternative instead.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Activity is slowing. The repository last received commits 7 months ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 13, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • tokio-rs/mini-redis4,811 ★ · +23
Star history
Repository2026-09-232026-11-13
tokio-rs/mini-redis4.8k4.8k

23 Stars in the last 51 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

The disclaimer is the specification

mini-redis describes itself as an incomplete, idiomatic implementation of a Redis client and server built with Tokio, and the intent is stated before anything else: it exists to be a larger example of writing a Tokio application.

The omissions are the design. Parts of the Redis protocol are left out because implementing them would not introduce any new concepts, and the project says it will not add features because you need them in your project. If you want those, the instruction is to use one of the fully featured alternatives.

The contributing rules follow the same logic. The goal is not feature parity with real Redis but demonstrating asynchronous Rust patterns, so a command or feature should be added only if it teaches a new pattern, contributions are expected to carry extensive comments aimed at new Tokio users, and contributions that only clarify and improve comments are explicitly welcome. Redis itself was chosen because it has a wide feature range and a simple wire protocol, which is what allows many patterns to appear in one small codebase.

Two binaries, four examples, five commands

Running it is three commands. The server comes first, with structured logs through the `tracing` crate, where `debug` can be swapped for any other level:

code
RUST_LOG=debug cargo run --bin mini-redis-server

Then, from another terminal, either an example or the CLI client:

code
cargo run --example hello_world
code
cargo run --bin mini-redis-cli set foo bar

cargo run --bin mini-redis-cli get foo

The examples directory holds `hello_world.rs`, `pub.rs`, `sub.rs` and `chat.rs`, which between them cover the client library and the pub/sub side of the server.

The supported command set is the short part to remember: PING, GET, SET, PUBLISH and SUBSCRIBE. There is no persistence support yet, so anything the server holds is lost when it stops.

Frame is the intermediate representation, not the socket

The framing code is the part worth reading twice. `src/connection.rs` and `src/frame.rs` model the wire protocol through an intermediate representation, the `Frame` structure, and `Connection` takes a `TcpStream` and exposes an API that sends and receives `Frame` values.

That separation is what makes the protocol readable. It is also the pattern most likely to transfer to your own project, because almost every line protocol benefits from a parse step that produces a typed value before any handling happens.

The client side follows the same discipline in a different file. `src/clients/client.rs` models an asynchronous client whose capabilities are exposed as `async` methods, which is the shape a Tokio application usually wants.

SIGINT starts the shutdown and a Semaphore caps the connections

Two operational patterns sit in the server. The first is graceful shutdown, driven by `tokio::signal` listening for SIGINT. Once the signal arrives, the server stops accepting new connections, notifies existing connections to shut down, lets in-flight work finish, and then closes the connection. Nothing is dropped mid-write, and the ordering is spelled out in the README rather than left to the reader.

The second is concurrent connection limiting. A `Semaphore` caps the number of concurrent connections, and once the limit is reached the server stops accepting until an existing connection terminates.

`src/server.rs` also spawns a new task per connection and handles `accept` errors gracefully rather than unwinding. That last detail is the one people copy without noticing: an accept loop that panics on a transient error takes the whole server with it, and this one does not.

One broadcast channel per channel, one StreamMap per connection

Pub/sub is described as the non-trivial part of the implementation, and the mechanism is two data structures from the Tokio ecosystem.

The server runs one broadcast channel per channel, so a published message reaches every current subscriber without the server keeping a list of who is listening. Each connection holds a `StreamMap`, which maps that connection's active subscriptions to the streams it should read from. Because the map is per connection and mutable, a client can subscribe to several channels and change its subscription at any time by sending further subscription commands; the server does not need to be told in advance.

The shared state that ties this together is a `Db` instance in `src/db.rs`, reachable from every connected connection. It manages both the key-value state and the pub/sub capabilities, which is the reason the file is worth reading even if you never subscribe to anything.

A std::sync::Mutex, and a clock the tests control

Two smaller decisions are as instructive as the big ones.

The server synchronises access to shared state with a `std::sync::Mutex` and explicitly not a Tokio mutex, with the reasoning pointed at `db.rs`.

The tests take the opposite approach to time. `tests/server.rs` contains tests for key expiration, which depend on time passing, and to keep them deterministic the clock is mocked with Tokio's testing utilities. The `dev-dependencies` section enables tokio's `test-util` feature for exactly this, in dev mode only, so a production build does not carry it.

Together those two choices are a lesson in measurement: pick the primitive that fits the section, and control the one source of nondeterminism that would otherwise make a test flaky.

The otel feature swaps tracing for tracing-opentelemetry

Tracing is `tracing` and `tracing-subscriber` with the `env-filter` feature, which is why `RUST_LOG` works. Turning on OpenTelemetry is a compile-time switch rather than a runtime setting:

code
RUST_LOG=debug cargo run --bin mini-redis-server --features otel

That swaps `tracing` over to `tracing-opentelemetry`, and it requires an AWSOtelCollector running on the same host. The reasoning is stated in the README: OpenTelemetry is an open standard with one data format, so the same traces can go to Prometheus, Jaeger, DataDog, Honeycomb, AWS X-Ray or anything else, which removes the risk of vendor lock-in.

The feature pulls four optional dependencies: `opentelemetry` 0.20.0 for the spec types, `tracing-opentelemetry` 0.21.0, `opentelemetry-aws` 0.8.0 for the propagator that carries an X-Ray id between services, and `opentelemetry-otlp` 0.13.0 for talking to the collector.

The rest of `Cargo.toml` is ordinary: edition 2018, tokio 1 with the `full` feature, `bytes`, `async-stream`, `atoi`, and clap 4.2.7 with derive for the CLI. The package is at version 0.4.1 while the documentation link in the manifest still points at docs.rs for 0.4.0, which is the kind of detail worth knowing before you file an issue. The licence is MIT, the repository has no GitHub releases, and the last push was on 2026-04-15.

Editorial conclusion

Use mini-redis if you are learning asynchronous Rust and want one readable codebase that shows a TCP server, a wire protocol framed through an intermediate type, graceful shutdown, a connection semaphore, pub/sub and mocked time in tests. Do not use it as a Redis client, a Redis server or a protocol reference: it supports five commands, omits parts of the wire protocol by design, has no persistence, and the README asks you to use a fully featured alternative instead. Before you start, run the server with `RUST_LOG=debug cargo run --bin mini-redis-server` and the `hello_world` example, and if you add anything, add it only when it demonstrates a Tokio pattern the project does not already show.

Frequently asked questions

what is mini redis

An incomplete, idiomatic implementation of a Redis client and server in Rust built with Tokio, kept as a learning resource for writing asynchronous Rust. It supports PING, GET, SET, PUBLISH and SUBSCRIBE, has no persistence, omits parts of the Redis protocol on purpose, and its README asks you not to use it in production.

How do I run the mini-redis server?

Run `RUST_LOG=debug cargo run --bin mini-redis-server`. Structured logs come from the `tracing` crate, and you can replace `debug` with any other log level the `tracing-subscriber` filter accepts.

How do I send a command to mini-redis from a terminal?

With the server running, use the CLI binary: `cargo run --bin mini-redis-cli set foo bar` followed by `cargo run --bin mini-redis-cli get foo`. The `examples/` directory also holds `hello_world.rs`, `pub.rs`, `sub.rs` and `chat.rs`, run with `cargo run --example hello_world` and its siblings.

Which Redis commands does mini-redis support?

Five: PING, GET, SET, PUBLISH and SUBSCRIBE. There is no persistence support yet, and other parts of the Redis wire protocol are deliberately omitted because implementing them would not add a new concept.

How do I enable OpenTelemetry tracing in mini-redis?

Build the server with `RUST_LOG=debug cargo run --bin mini-redis-server --features otel`, which switches `tracing` to `tracing-opentelemetry`. You then need an AWSOtelCollector running on the same host. The `otel` feature pulls in `opentelemetry`, `tracing-opentelemetry`, `opentelemetry-aws` and `opentelemetry-otlp`.

Which Tokio patterns does mini-redis demonstrate?

A TCP server spawning a task per connection, a shared `Db` across sockets, a `Frame` intermediate representation for the wire protocol, graceful shutdown on SIGINT, a `Semaphore` capping concurrent connections, pub/sub built from a broadcast channel per channel and a `StreamMap` per connection, a `std::sync::Mutex` in async code, and mocked time in `tests/server.rs` for key expiration tests.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. tokio-rs/mini-redis on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/tokio-rs-mini-redis.svg)](https://hysenlabs.com/projects/tokio-rs-mini-redis)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/tokio-rs-mini-redis.svg)](https://hysenlabs.com/projects/tokio-rs-mini-redis)

Was this useful?