Skip to content

Commit 75050ad

Browse files
committed
Set key with expiry
1 parent 6cac006 commit 75050ad

File tree

6 files changed

+56
-10
lines changed

6 files changed

+56
-10
lines changed

Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "redis-rs"
33
version = "0.0.1"
44
authors = ["Pin Fang <[email protected]>"]
5-
edition = "2024"
5+
edition = "2021"
66

77
[dependencies]
88
anyhow = "1.0.59"

README.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Build Your Own Redis
22

3-
To build a toy Redis-Server clone that's capable of handling
3+
This project is to build a toy Redis-Server clone that's capable of handling
44
basic commands like `PING`, `SET` and `GET`. Also implement the event loops, the Redis protocol and more.
55

66
## Prerequisites
@@ -23,6 +23,10 @@ redis-cli ECHO hey
2323
redis-cli SET foo bar
2424
```
2525

26+
```sh
27+
redis-cli SET foo bar px 100
28+
```
29+
2630
```sh
2731
redis-cli GET foo
2832
```

src/cmd.rs

+26-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ pub enum Cmd {
88
Echo(String),
99
Get(String),
1010
Set(String, String),
11+
SetPx(String, String, u128),
12+
SetEx(String, String, u128),
1113
}
1214

1315
impl Cmd {
@@ -23,7 +25,15 @@ impl Cmd {
2325
"echo" => Cmd::Echo(cmd[1].clone()),
2426
"ping" => Cmd::Ping,
2527
"get" => Cmd::Get(cmd[1].clone()),
26-
"set" => Cmd::Set(cmd[1].clone(), cmd[2].clone()),
28+
"set" => {
29+
if cmd.len() == 5 && cmd[3] == "px" {
30+
Cmd::SetPx(cmd[1].clone(), cmd[2].clone(), cmd[4].parse().unwrap())
31+
} else if cmd.len() == 5 && cmd[3] == "ex" {
32+
Cmd::SetEx(cmd[1].clone(), cmd[2].clone(), cmd[4].parse().unwrap())
33+
} else {
34+
Cmd::Set(cmd[1].clone(), cmd[2].clone())
35+
}
36+
}
2737
_ => return Err(anyhow::anyhow!("unknown cmd {:?}", cmd[0])),
2838
})
2939
}
@@ -42,14 +52,28 @@ impl Cmd {
4252
} else {
4353
Protocol::Null
4454
})
45-
},
55+
}
4656
Cmd::Set(k, v) => {
4757
{
4858
let mut s = storage.lock().unwrap();
4959
s.set(k.clone(), v.clone());
5060
}
5161
Ok(Protocol::ok())
5262
}
63+
Cmd::SetPx(k, v, x) => {
64+
{
65+
let mut s = storage.lock().unwrap();
66+
s.setx(k.clone(), v.clone(), *x);
67+
}
68+
Ok(Protocol::ok())
69+
}
70+
Cmd::SetEx(k, v, x) => {
71+
{
72+
let mut s = storage.lock().unwrap();
73+
s.setx(k.clone(), v.clone(), *x * 1000);
74+
}
75+
Ok(Protocol::ok())
76+
}
5377
}
5478
}
5579
}

src/main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![allow(unused_imports)]
22

3-
use redis_starter_rust::server;
3+
use redis_rs::server;
44

55
use tokio::io::{AsyncReadExt, AsyncWriteExt};
66
use tokio::net::TcpListener;

src/storage.rs

+22-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
use std::collections::HashMap;
1+
use core::time;
2+
use std::{collections::HashMap, time::Instant};
23

34
pub struct Storage {
4-
set: HashMap<String, String>,
5+
// key -> (value, (insert/update time, expire milli seconds))
6+
set: HashMap<String, (String, Option<(Instant, u128)>)>,
57
}
68

79
impl Storage {
@@ -12,10 +14,26 @@ impl Storage {
1214
}
1315

1416
pub fn get(self: &Self, k: &str) -> Option<&String> {
15-
self.set.get(k)
17+
match self.set.get(k) {
18+
Some((ss, time_info)) => match time_info {
19+
Some((instant, ms)) => {
20+
if instant.elapsed().as_millis() > *ms {
21+
None
22+
} else {
23+
Some(ss)
24+
}
25+
}
26+
_ => Some(ss),
27+
},
28+
_ => None,
29+
}
1630
}
1731

1832
pub fn set(self: &mut Self, k: String, v: String) {
19-
self.set.insert(k, v);
33+
self.set.insert(k, (v, None));
34+
}
35+
36+
pub fn setx(self: &mut Self, k: String, v: String, expire_ms: u128) {
37+
self.set.insert(k, (v, Some((Instant::now(), expire_ms))));
2038
}
2139
}

0 commit comments

Comments
 (0)