mini-redis
is an incomplete, idiomatic implementation of a
Redis client and server built with
Tokio.
The intent of this project is to provide a larger example of writing a Tokio application.
Disclaimer Don't even think about trying to use this in production... just don't.
The primary goal of this project is teaching Tokio. Doing this requires a project with a wide range of features with a focus on implementation simplicity. Redis, an in-memory database, provides a wide range of features and uses a simple wire protocol. The wide range of features allows demonstrating many Tokio patterns in a "real world" context.
The Redis wire protocol documentation can be found here.
The set of commands Redis provides can be found here.
The repository provides a server, client library, and some client executables for interacting with the server.
Start the server:
RUST_LOG=debug cargo run --bin server
The tracing
crate is used to provide structured logs.
You can substitute debug
with the desired log level.
Then, in a different terminal window, the various client examples can be executed. For example:
cargo run --example hello_world
Additionally, a CLI client is provided to run arbitrary commands from the terminal. With the server running, the following works:
cargo run --bin cli set foo bar
cargo run --bin cli get foo
mini-redis
currently supports the following commands.
The Redis wire protocol specification can be found here.
There is no support for persistence yet.
The project demonstrates a number of useful patterns, including:
server.rs
starts a TCP server that accepts connections,
and spawns a new task per connection. It gracefully handles accept
errors.
client.rs
shows how to model an asynchronous client. The
various capabilities are exposed as async
methods.
The server maintains a Db
instance that is accessible from all connected
connections. The Db
instance manages the key-value state as well as pub/sub
capabilities.
connection.rs
and frame.rs
show how to
idiomatically implement a wire protocol. The protocol is modeled using an
intermediate representation, the Frame
structure. Connection
takes a
TcpStream
and exposes an API that sends and receives Frame
values.
The server implements graceful shutdown. tokio::signal
is used to listen for
a SIGINT. Once the signal is received, shutdown begins. The server stops
accepting new connections. Existing connections are notified to shutdown
gracefully. In-flight work is completed, and the connection is closed.
The server uses a Semaphore
limits the maximum number of concurrent
connections. Once the limit is reached, the server stops accepting new
connections until an existing one terminates.
The server implements non-trivial pub/sub capability. The client may subscribe
to multiple channels and update its subscription at any time. The server
implements this using one broadcast channel per channel and a
StreamMap
per connection. Clients are able to send subscription commands to
the server to update the active subscriptions.
The server uses a std::sync::Mutex
and not a Tokio mutex to synchronize
access to shared state. See db.rs
for more details.
In tests/server.rs
, there are tests for key expiration.
These tests depend on time passing. In order to make the tests deterministic,
time is mocked out using Tokio's testing utilities.
Contributions to mini-redis
are welcome. Keep in mind, the goal of the project
is not to reach feature parity with real Redis, but to demonstrate
asynchronous Rust patterns with Tokio.
Commands or other features should only be added if doing so is useful to demonstrate a new pattern.
Contributions should come with extensive comments targetted to new Tokio users.
Contributions that only focus on clarifying and improving comments are very welcome.
No.
This project is licensed under the MIT license.
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in mini-redis
by you, shall be licensed as MIT, without any
additional terms or conditions.