Skip to content

Commit 1cc9f14

Browse files
authored
Simplify string caching (#1158)
Since `StringCacheValue` is always a String, there's not much point in having a separate type for it. This adds a marker trait that gets implemented in the macro so that we can have still have type safety around cache keys that store strings, but otherwise change the relevant cache methods to just take/return `&str`/`String` as appropriate.
1 parent 3579efe commit 1cc9f14

File tree

4 files changed

+36
-70
lines changed

4 files changed

+36
-70
lines changed

server/svix-server/src/core/cache/memory.rs

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ fn check_is_expired(vw: &ValueWrapper) -> bool {
106106
#[cfg(test)]
107107
mod tests {
108108
use super::{
109-
super::{kv_def, CacheValue, StringCacheValue},
109+
super::{kv_def, CacheValue},
110110
*,
111111
};
112112
use crate::core::cache::string_kv_def;
@@ -134,26 +134,13 @@ mod tests {
134134

135135
#[derive(Deserialize, Serialize, Debug, PartialEq)]
136136
struct StringTestVal(String);
137-
string_kv_def!(StringTestKey, StringTestVal);
137+
string_kv_def!(StringTestKey);
138138
impl StringTestKey {
139139
fn new(id: String) -> StringTestKey {
140140
StringTestKey(format!("SVIX_TEST_KEY_STRING_{id}"))
141141
}
142142
}
143143

144-
impl std::fmt::Display for StringTestVal {
145-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
146-
write!(f, "{}", self.0)
147-
}
148-
}
149-
150-
impl TryFrom<String> for StringTestVal {
151-
type Error = crate::error::Error;
152-
fn try_from(s: String) -> crate::error::Result<Self> {
153-
Ok(StringTestVal(s))
154-
}
155-
}
156-
157144
#[tokio::test]
158145
async fn test_cache_crud_no_ttl() {
159146
let cache = new();
@@ -167,8 +154,8 @@ mod tests {
167154
);
168155
let (third_key, third_val_a, third_val_b) = (
169156
StringTestKey::new("1".to_owned()),
170-
StringTestVal("1".to_owned()),
171-
StringTestVal("2".to_owned()),
157+
"1".to_owned(),
158+
"2".to_owned(),
172159
);
173160

174161
// Create
@@ -223,10 +210,7 @@ mod tests {
223210
// Confirm deletion
224211
assert_eq!(cache.get::<TestValA>(&first_key).await.unwrap(), None);
225212
assert_eq!(cache.get::<TestValB>(&second_key).await.unwrap(), None);
226-
assert_eq!(
227-
cache.get_string::<StringTestVal>(&third_key).await.unwrap(),
228-
None
229-
);
213+
assert_eq!(cache.get_string(&third_key).await.unwrap(), None);
230214
}
231215

232216
#[tokio::test]

server/svix-server/src/core/cache/mod.rs

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ type Result<T> = std::result::Result<T, Error>;
4040
/// A valid key value for the cache -- usually just a wrapper around a [`String`]
4141
pub trait CacheKey: AsRef<str> + Send + Sync {}
4242

43+
/// A cache key for setting/getting raw [`String`]s -- this is just a marker
44+
/// trait added in the `string_kv_def macro`
45+
pub trait StringCacheKey: AsRef<str> + Send + Sync {}
46+
4347
/// Any (de)serializable structure usable as a value in the cache -- it is associated with a
4448
/// given key type to ensure type checking on creation or reading of values from the cache
4549
pub trait CacheValue: DeserializeOwned + Serialize + Send + Sync {
@@ -85,7 +89,7 @@ pub(crate) use kv_def;
8589
/// but it can't be made private or else it couldn't be used in the outer macro.
8690
#[allow(unused_macros)]
8791
macro_rules! string_kv_def_inner {
88-
($key_id:ident, $val_struct:ident) => {
92+
($key_id:ident) => {
8993
#[derive(Clone, Debug)]
9094
pub struct $key_id(String);
9195

@@ -94,10 +98,6 @@ macro_rules! string_kv_def_inner {
9498
&self.0
9599
}
96100
}
97-
98-
impl StringCacheValue for $val_struct {
99-
type Key = $key_id;
100-
}
101101
};
102102
}
103103
#[allow(unused_imports)]
@@ -106,10 +106,12 @@ pub(crate) use string_kv_def_inner;
106106
// Used downstream and for testing:
107107
#[allow(unused_macros)]
108108
macro_rules! string_kv_def {
109-
($key_id:ident, $val_struct:ident) => {
110-
crate::core::cache::string_kv_def_inner!($key_id, $val_struct);
109+
($key_id:ident) => {
110+
crate::core::cache::string_kv_def_inner!($key_id);
111111

112-
impl CacheKey for $key_id {}
112+
impl crate::core::cache::StringCacheKey for $key_id {}
113+
// so key can work w/ other methods, like delete:
114+
impl crate::core::cache::CacheKey for $key_id {}
113115
};
114116
}
115117
#[allow(unused_imports)]
@@ -159,16 +161,12 @@ pub trait CacheBehavior: Sync + Send {
159161

160162
async fn get_raw(&self, key: &[u8]) -> Result<Option<Vec<u8>>>;
161163

162-
async fn get_string<T: StringCacheValue>(&self, key: &T::Key) -> Result<Option<T>> {
164+
async fn get_string<T: StringCacheKey>(&self, key: &T) -> Result<Option<String>> {
163165
run_with_retries(
164166
|| async move {
165167
self.get_raw(key.as_ref().as_bytes())
166168
.await?
167-
.map(|x| {
168-
String::from_utf8(x)
169-
.map_err(|e| e.into())
170-
.and_then(|x| x.try_into().map_err(|_| Error::DeserializationOther))
171-
})
169+
.map(|x| String::from_utf8(x).map_err(|e| e.into()))
172170
.transpose()
173171
},
174172
|e| self.should_retry(e),
@@ -195,10 +193,10 @@ pub trait CacheBehavior: Sync + Send {
195193

196194
async fn set_raw(&self, key: &[u8], value: &[u8], ttl: Duration) -> Result<()>;
197195

198-
async fn set_string<T: StringCacheValue>(
196+
async fn set_string<T: StringCacheKey>(
199197
&self,
200-
key: &T::Key,
201-
value: &T,
198+
key: &T,
199+
value: &str,
202200
ttl: Duration,
203201
) -> Result<()> {
204202
run_with_retries(
@@ -237,10 +235,10 @@ pub trait CacheBehavior: Sync + Send {
237235

238236
async fn set_raw_if_not_exists(&self, key: &[u8], value: &[u8], ttl: Duration) -> Result<bool>;
239237

240-
async fn set_string_if_not_exists<T: StringCacheValue>(
238+
async fn set_string_if_not_exists<T: StringCacheKey>(
241239
&self,
242-
key: &T::Key,
243-
value: &T,
240+
key: &T,
241+
value: &str,
244242
ttl: Duration,
245243
) -> Result<bool> {
246244
run_with_retries(

server/svix-server/src/core/cache/none.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::time::Duration;
55

66
use axum::async_trait;
77

8-
use super::{Cache, CacheBehavior, CacheKey, CacheValue, Result, StringCacheValue};
8+
use super::{Cache, CacheBehavior, CacheKey, CacheValue, Result, StringCacheKey};
99

1010
pub fn new() -> Cache {
1111
tracing::warn!("Running with caching disabled will negatively affect performance. Idempotency is not supported without a cache.");
@@ -29,7 +29,7 @@ impl CacheBehavior for NoCache {
2929
Ok(None)
3030
}
3131

32-
async fn get_string<T: StringCacheValue>(&self, _key: &T::Key) -> Result<Option<T>> {
32+
async fn get_string<T: StringCacheKey>(&self, _key: &T) -> Result<Option<String>> {
3333
Ok(None)
3434
}
3535

@@ -41,10 +41,10 @@ impl CacheBehavior for NoCache {
4141
Ok(())
4242
}
4343

44-
async fn set_string<T: StringCacheValue>(
44+
async fn set_string<T: StringCacheKey>(
4545
&self,
46-
_key: &T::Key,
47-
_value: &T,
46+
_key: &T,
47+
_value: &str,
4848
_ttl: Duration,
4949
) -> Result<()> {
5050
Ok(())
@@ -72,10 +72,10 @@ impl CacheBehavior for NoCache {
7272
Ok(false)
7373
}
7474

75-
async fn set_string_if_not_exists<T: StringCacheValue>(
75+
async fn set_string_if_not_exists<T: StringCacheKey>(
7676
&self,
77-
_key: &T::Key,
78-
_value: &T,
77+
_key: &T,
78+
_value: &str,
7979
_ttl: Duration,
8080
) -> Result<bool> {
8181
Ok(false)

server/svix-server/src/core/cache/redis.rs

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ impl CacheBehavior for RedisCache {
7676
#[cfg(test)]
7777
mod tests {
7878
use super::{
79-
super::{kv_def, string_kv_def, CacheValue, StringCacheValue},
79+
super::{kv_def, string_kv_def, CacheValue},
8080
*,
8181
};
8282
use serde::{Deserialize, Serialize};
@@ -105,26 +105,13 @@ mod tests {
105105

106106
#[derive(Deserialize, Serialize, Debug, PartialEq)]
107107
struct StringTestVal(String);
108-
string_kv_def!(StringTestKey, StringTestVal);
108+
string_kv_def!(StringTestKey);
109109
impl StringTestKey {
110110
fn new(id: String) -> StringTestKey {
111111
StringTestKey(format!("SVIX_TEST_KEY_STRING_{id}"))
112112
}
113113
}
114114

115-
impl std::fmt::Display for StringTestVal {
116-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117-
write!(f, "{}", self.0)
118-
}
119-
}
120-
121-
impl TryFrom<String> for StringTestVal {
122-
type Error = crate::error::Error;
123-
fn try_from(s: String) -> crate::error::Result<Self> {
124-
Ok(StringTestVal(s))
125-
}
126-
}
127-
128115
async fn get_pool(redis_dsn: &str, cfg: &crate::cfg::Configuration) -> RedisPool {
129116
match cfg.cache_type {
130117
CacheType::RedisCluster => crate::redis::new_redis_pool_clustered(redis_dsn, cfg).await,
@@ -149,8 +136,8 @@ mod tests {
149136
);
150137
let (third_key, third_val_a, third_val_b) = (
151138
StringTestKey::new("1".to_owned()),
152-
StringTestVal("1".to_owned()),
153-
StringTestVal("2".to_owned()),
139+
"1".to_owned(),
140+
"2".to_owned(),
154141
);
155142

156143
// Create
@@ -205,10 +192,7 @@ mod tests {
205192
// Confirm deletion
206193
assert_eq!(cache.get::<TestValA>(&first_key).await.unwrap(), None);
207194
assert_eq!(cache.get::<TestValB>(&second_key).await.unwrap(), None);
208-
assert_eq!(
209-
cache.get_string::<StringTestVal>(&third_key).await.unwrap(),
210-
None
211-
);
195+
assert_eq!(cache.get_string(&third_key).await.unwrap(), None);
212196
}
213197

214198
#[tokio::test]

0 commit comments

Comments
 (0)