1 unstable release
new 0.1.0 | Jan 29, 2025 |
---|
#1211 in Database interfaces
535KB
16K
SLoC
Bradis
Bradis is a partial implementation of Redis in Rust as a learning exercise. It's a serious attempt at recreating Redis functionality, but not intended for serious use.
Goals
Idiomatic Rust
The number one goal of this project is to learn how to implement Redis with Rust. Writing readable, idiomatic, documented Rust comes before all other goals.
Redis data structures
The second goal is to learn about Redis data structures and how they work together to create a solution to real problems. Some of the things I've implemented might be available as off the shelf crates, but writing them myself is the whole point of this exercise, so I'm doing that!
Idiomatic async Rust
Reading requests and writing responses is a great opportunity for using the asynchronous features of Rust. Learning to implement parallel processing of requests and responses is difficult and information about strategies for doing so are few and far between. I'd like to come up with my own ideas about it!
Match the latest Redis release
My target for functionality is generally the latest Redis release. This project will always lag behind, but that's what I'm shooting for.
Iteratively improve performance
Measuring, profiling, and improving the performance of a code path is difficult and requires many tools. Making that loop tighter is an explicit goal of mine here.
Non-Goals
Implement all of Redis
Some parts of Redis are more interesting to me than others. I'll be doing all the parts that I'm interested in and not the others.
Contribute to C Redis
I have no desire to replace or contribute to C Redis. This is about writing Rust, not C.
Performance at the cost of understanding
I'd like to achieve performance comparable to C Redis, but only if it's easily understandable and idiomatic. I don't care about squeezing out every tiny bit of performance.
Architecture
Redis is often described as being "single threaded", referring to the way commands are processed sequentially. However, there is a great deal of work that can be done in parallel. Currently, I've split that work roughly into the following tasks.
Waiting for connections
The Server
waits for connections and spawns new clients.
Reading commands
Each Client
spawns a task for reading commands. It waits for bytes to arrive,
parses them into arguments, and sends them through a channel along with
separators to denote command boundaries.
Writing responses
Each Client
also spawns a Replier
to accept replies through a channel
and write them to a socket one at a time.
Waiting for commands
The Client
needs to wait for a full command to arrive before sending a
ready message to the store. Also, if the client is killed it should stop
waiting, send a disconnect message to the server, stop reading commands,
and drop itself. The replier will be dropped as soon as all replies have
been sent.
Freeing memory
Redis can free memory asynchronously (e.g. UNLINK
) and we need a task
to accept values and free them on a separate thread.
Procesing commands
Commands are processed one at a time by sending clients that are ready to a channel that owns the store and running them one at a time.
Dependencies
~8–15MB
~158K SLoC