swiftide_integrations/redis/
node_cache.rs1use anyhow::Result;
2use async_trait::async_trait;
3
4use swiftide_core::indexing::{Node, NodeCache};
5
6use super::Redis;
7
8#[allow(dependency_on_unit_never_type_fallback)]
9#[async_trait]
10impl NodeCache for Redis {
11 #[tracing::instrument(skip_all, name = "node_cache.redis.get", fields(hit))]
25 async fn get(&self, node: &Node) -> bool {
26 let cache_result = if let Some(mut cm) = self.lazy_connect().await {
27 let result = redis::cmd("EXISTS")
28 .arg(self.cache_key_for_node(node))
29 .query_async(&mut cm)
30 .await;
31
32 match result {
33 Ok(1) => true,
34 Ok(0) => false,
35 Err(e) => {
36 tracing::error!("Failed to check node cache: {}", e);
37 false
38 }
39 _ => {
40 tracing::error!("Unexpected response from redis");
41 false
42 }
43 }
44 } else {
45 false
46 };
47
48 tracing::Span::current().record("hit", cache_result);
49
50 cache_result
51 }
52
53 #[tracing::instrument(skip_all, name = "node_cache.redis.get")]
63 async fn set(&self, node: &Node) {
64 if let Some(mut cm) = self.lazy_connect().await {
65 let result: Result<(), redis::RedisError> = redis::cmd("SET")
66 .arg(self.cache_key_for_node(node))
67 .arg(1)
68 .query_async(&mut cm)
69 .await;
70
71 if let Err(e) = result {
72 tracing::error!("Failed to set node cache: {}", e);
73 }
74 }
75 }
76
77 async fn clear(&self) -> Result<()> {
78 if self.cache_key_prefix.is_empty() {
79 return Err(anyhow::anyhow!(
80 "No cache key prefix set; not flushing cache"
81 ));
82 }
83
84 if let Some(mut cm) = self.lazy_connect().await {
85 redis::cmd("DEL")
86 .arg(format!("{}*", self.cache_key_prefix))
87 .query_async::<()>(&mut cm)
88 .await?;
89
90 Ok(())
91 } else {
92 anyhow::bail!("Failed to connect to Redis");
93 }
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100
101 use testcontainers::runners::AsyncRunner;
102
103 #[test_log::test(tokio::test)]
105 async fn test_redis_cache() {
106 let redis = testcontainers::GenericImage::new("redis", "7.2.4")
107 .with_exposed_port(6379.into())
108 .with_wait_for(testcontainers::core::WaitFor::message_on_stdout(
109 "Ready to accept connections",
110 ))
111 .start()
112 .await
113 .expect("Redis started");
114
115 let host = redis.get_host().await.unwrap();
116 let port = redis.get_host_port_ipv4(6379).await.unwrap();
117 let cache = Redis::try_from_url(format!("redis://{host}:{port}"), "test")
118 .expect("Could not build redis client");
119 cache.reset_cache().await;
120
121 let node = Node::new("chunk");
122
123 let before_cache = cache.get(&node).await;
124 assert!(!before_cache);
125
126 cache.set(&node).await;
127 let after_cache = cache.get(&node).await;
128 assert!(after_cache);
129 }
130}