diff options
| author | omagdy <omar.professional8777@gmail.com> | 2025-07-23 04:06:13 +0300 |
|---|---|---|
| committer | omagdy <omar.professional8777@gmail.com> | 2025-07-23 04:06:13 +0300 |
| commit | 1f2f3a241c59f467df5bf16fbde872f5083a174f (patch) | |
| tree | 37af0cc34a97e3efd350b1ee1b872e621e25dca9 | |
| parent | 8717ae6ab2335cf1a5d34283f272ad728da88667 (diff) | |
| download | redis-rust-1f2f3a241c59f467df5bf16fbde872f5083a174f.tar.xz redis-rust-1f2f3a241c59f467df5bf16fbde872f5083a174f.zip | |
feat: Added syncing with empty RDB file
| -rw-r--r-- | Cargo.lock | 7 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | src/lib.rs | 6 | ||||
| -rw-r--r-- | src/main.rs | 48 | ||||
| -rw-r--r-- | src/resp_commands.rs | 1 |
5 files changed, 56 insertions, 7 deletions
@@ -54,6 +54,12 @@ dependencies = [ ] [[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -82,6 +88,7 @@ name = "codecrafters-redis" version = "0.1.0" dependencies = [ "anyhow", + "base64", "bytes", "regex", "thiserror", @@ -6,6 +6,7 @@ edition = "2021" [dependencies] anyhow = "1.0.59" # error handling +base64 = "0.22.1" bytes = "1.3.0" # helps manage buffers regex = "1.11.1" thiserror = "1.0.32" # error handling @@ -67,21 +67,21 @@ fn handshake_process(slave: &RedisServer) -> Result<(), String> { // PING send_command(&resp_bytes!(array => [resp!(bulk "PING")]))?; - // REPLCONF listening-port + // REPLCONF listening-port <PORT> send_command(&resp_bytes!(array => [ resp!(bulk "REPLCONF"), resp!(bulk "listening-port"), resp!(bulk slave.port.clone()) ]))?; - // REPLCONF capa + // REPLCONF capa psync2 send_command(&resp_bytes!(array => [ resp!(bulk "REPLCONF"), resp!(bulk "capa"), resp!(bulk "psync2") ]))?; - // PSYNC + // PSYNC <REPL_ID> <REPL_OFFSSET> send_command(&resp_bytes!(array => [ resp!(bulk "PSYNC"), resp!(bulk "?"), diff --git a/src/main.rs b/src/main.rs index 3186b6c..78b9c24 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ use core::time; use std::{ collections::HashMap, env, + fmt::format, io::{Read, Write}, net::{TcpListener, TcpStream}, sync::{Arc, Mutex}, @@ -12,6 +13,7 @@ use std::{ use codecrafters_redis::{ rdb::{KeyExpiry, ParseError, RDBFile, RedisValue}, + resp_bytes, shared_cache::*, }; use codecrafters_redis::{resp_commands::RedisCommands, Config}; @@ -38,6 +40,23 @@ fn spawn_cleanup_thread(cache: SharedCache) { }); } +use base64::{engine::general_purpose, Engine as _}; + +fn write_rdb_to_stream<W: Write>(writer: &mut W) -> Result<(), Box<dyn std::error::Error>> { + let hardcoded_rdb = "UkVESVMwMDEx+glyZWRpcy12ZXIFNy4yLjD6CnJlZGlzLWJpdHPAQPoFY3RpbWXCbQi8ZfoIdXNlZC1tZW3CsMQQAPoIYW9mLWJhc2XAAP/wbjv+wP9aog=="; + + let bytes = general_purpose::STANDARD.decode(hardcoded_rdb)?; + + let mut response = format!("${}\r\n", bytes.len()).into_bytes(); + response.extend_from_slice(&bytes); + + // Write the binary RDB data + writer.write_all(&response)?; + + Ok(()) +} + +// TODO: This should return a Result to handle the plethora of different errors fn handle_client(mut stream: TcpStream, cache: SharedCache, config: SharedConfig) { let mut buffer = [0; 512]; @@ -48,11 +67,32 @@ fn handle_client(mut stream: TcpStream, cache: SharedCache, config: SharedConfig Err(_) => return, // error occurred }; - let parsed_resp = parse(&buffer).unwrap(); - let response = RedisCommands::from(parsed_resp.0).execute(cache.clone(), config.clone()); + let request = parse(&buffer).unwrap(); + let response = + RedisCommands::from(request.0.clone()).execute(cache.clone(), config.clone()); + + let mut request_command = "".to_string(); - // write respose back to the client - stream.write(&response).unwrap(); + // FIXME: Find a solution for this mess!! + match &request.0 { + RespType::Array(arr) => { + if let RespType::BulkString(s) = arr[0].clone() { + request_command = String::from_utf8(s).unwrap(); + } + } + _ => {} + } + + // if this true immediately write and send back rdb file after response + // HACK: This just feels wrong I feel this shouldn't be handled here and should be handled + // in the exexute command + if request_command.starts_with("PSYNC") { + stream.write(&response).unwrap(); + let _ = write_rdb_to_stream(&mut stream); + } else { + // write respose back to the client + stream.write(&response).unwrap(); + } } } diff --git a/src/resp_commands.rs b/src/resp_commands.rs index 04d304c..3c18b07 100644 --- a/src/resp_commands.rs +++ b/src/resp_commands.rs @@ -266,6 +266,7 @@ impl RedisCommands { "FULLRESYNC {} 0", server.master_replid.unwrap_or("".to_string()), ); + resp_bytes!(response) } RC::Invalid => todo!(), |
