Skip to content

Commit

Permalink
Keys sorting
Browse files Browse the repository at this point in the history
  • Loading branch information
gillchristian committed Oct 20, 2024
1 parent 51b7f19 commit 38fd8a4
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 11 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ strum = "0.26.2"
strum_macros = "0.26.2"
clap = { version = "4.5.7", features = ["derive", "env"] }
num-traits = "0.2.19"
itertools = "0.13.0"

[dev-dependencies]
rand = "0.8.5"
Expand Down
12 changes: 7 additions & 5 deletions src/commands/keys.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use bytes::Bytes;
use glob_match::glob_match;
use itertools::Itertools;

use crate::commands::executable::Executable;
use crate::commands::CommandParser;
Expand All @@ -20,11 +21,12 @@ pub struct Keys {
impl Executable for Keys {
fn exec(self, store: Store) -> Result<Frame, Error> {
let store = store.lock();
let matching_keys: Vec<Frame> = store
.keys()
.filter(|key| glob_match(self.pattern.as_str(), key))
.map(|key| Frame::Bulk(Bytes::from(key.to_string())))
.collect();
let matching_keys = store
.iter()
.filter(|(key, _)| glob_match(self.pattern.as_str(), key))
.sorted_by(|(_, a), (_, b)| b.inserted_at.cmp(&a.inserted_at))
.map(|(key, _)| Frame::Bulk(Bytes::from(key.to_string())))
.collect::<Vec<_>>();

Ok(Frame::Array(matching_keys))
}
Expand Down
15 changes: 11 additions & 4 deletions src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,23 +59,29 @@ pub struct InnerStoreLocked<'a> {
impl<'a> InnerStoreLocked<'a> {
pub fn set(&mut self, key: String, data: Bytes) {
// Ensure any previous TTL is removed.
self.remove(&key);
let removed = self.remove(&key);

let inserted_at = removed.map(|v| v.inserted_at).unwrap_or(Instant::now());

let value = Value {
data,
expires_at: None,
inserted_at,
};
self.state.keys.insert(key, value);
}

pub fn set_with_ttl(&mut self, key: Key, data: Bytes, ttl: Duration) {
// Ensure any previous TTL is removed.
self.remove(&key);
let removed = self.remove(&key);

let inserted_at = removed.map(|v| v.inserted_at).unwrap_or(Instant::now());

let expires_at = Instant::now() + ttl;
let value = Value {
data,
expires_at: Some(expires_at),
inserted_at,
};

self.state.keys.insert(key.clone(), value);
Expand Down Expand Up @@ -142,11 +148,11 @@ impl<'a> InnerStoreLocked<'a> {
self.state.keys.keys()
}

pub fn iter(&self) -> impl Iterator<Item = (&String, &Bytes)> {
pub fn iter(&self) -> impl Iterator<Item = (&String, &Value)> {
self.state
.keys
.iter()
.map(|(key, value)| (key, &value.data))
.map(|(key, value)| (key, value))
}

pub fn incr_by<T, R>(&mut self, key: &str, increment: T) -> Result<R, String>
Expand Down Expand Up @@ -232,6 +238,7 @@ type Key = String;
pub struct Value {
pub data: Bytes,
pub expires_at: Option<Instant>,
pub inserted_at: Instant,
}

pub struct State {
Expand Down
6 changes: 4 additions & 2 deletions tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,9 +349,11 @@ async fn test_getrange() {
#[tokio::test]
#[serial]
async fn test_keys() {
// TODO: The response order from the server is not guaranteed, to ensure accurate comparison
// with the expected result, we need to sort the response before performing the comparison.
test_compare::<Vec<Value>>(|p| {
// Redis keys order is deterministice but not guaranteed.
// We sort the keys by insertion order to make the test deterministic.
// Testing with a different set of keys produces different results,
// but matching the implementation is out of the scope of the project.
p.cmd("SET").arg("keys_key_1").arg("Argentina");
p.cmd("SET").arg("keys_key_2").arg("Spain");
p.cmd("SET").arg("keys_key_3").arg("Netherlands");
Expand Down

0 comments on commit 38fd8a4

Please sign in to comment.