This started as a toy project for "Build Your Own Redis" Challenge it is now an attempt to do a complete re-write of Redis in Rust.
The following Redis commands have been implemented:
- SET [EX|PX]
- GET
- PING
- COMMAND DOCS (always returns +OK)
- ECHO
- DEL
- MGET
- STRLEN
- APPEND
- CONFIG GET
- KEYS
- INFO
The following CLI parameters are currently supported:
- dir
- dbfilename
- replicaof
All the supported commands are defined as enums in protocol.rs.
The main.rs
tokio loop handles inbound connections:
loop {
// Asynchronously wait for an inbound TcpStream.
let (stream, _) = listener.accept().await?;
// Must clone the handler because tokio::spawn move will grab everything.
let set_command_handler_clone = set_command_actor_handle.clone();
// Spawn our handler to be run asynchronously.
// A new task is spawned for each inbound socket.
// The socket is moved to the new task and processed there.
tokio::spawn(async move {
process(stream, set_command_handler_clone)
.await
.expect("Failed to spawn process thread");
});
}
As part of the main initialization, we kick off a separate thread with an expiry channel. It listens for incoming requests to expire keys and acts accordingly.
NOTE: At the moment, this is a per redis expiry since this implementation supports a single database only.
To ensure the server can handle multiple connections at the same time, every connection spawns a new thread and gets moved there immediately:
async fn process(stream: TcpStream, set_command_actor_handle: SetCommandActorHandle) -> Result<()> {}
To avoid sharing state and dealing with mutexes, this code uses an actor model.