Bläddra i källkod

Revert "feat: remove redis cache (#1096)" (#1129)

This reverts commit be7c83689d2444ba9028e39c9b4a0416eec13a99.
thesimplekid 1 månad sedan
förälder
incheckning
676463f730

+ 4 - 3
.github/workflows/ci.yml

@@ -103,7 +103,8 @@ jobs:
             # HTTP/API layer - consolidated
             -p cdk-axum,
             -p cdk-axum --no-default-features,
-            -p cdk-axum --no-default-features --features swagger,
+            -p cdk-axum --no-default-features --features redis,
+            -p cdk-axum --no-default-features --features "redis swagger",
             
             # Lightning backends
             -p cdk-cln,
@@ -126,7 +127,7 @@ jobs:
             --bin cdk-cli --features sqlcipher,
             --bin cdk-cli --features redb,
             --bin cdk-mintd,
-
+            --bin cdk-mintd --features redis,
             --bin cdk-mintd --features sqlcipher,
             --bin cdk-mintd --no-default-features --features lnd --features sqlite,
             --bin cdk-mintd --no-default-features --features cln --features postgres,
@@ -337,7 +338,7 @@ jobs:
             -p cdk --no-default-features --features "wallet auth",
             -p cdk --no-default-features --features "http_subscription",
             -p cdk-axum,
-
+            -p cdk-axum --no-default-features --features redis,
             -p cdk-lnbits,
             -p cdk-fake-wallet,
             -p cdk-cln,

+ 4 - 0
crates/cdk-axum/Cargo.toml

@@ -12,6 +12,7 @@ readme = "README.md"
 
 [features]
 default = ["auth"]
+redis = ["dep:redis"]
 swagger = ["cdk/swagger", "dep:utoipa"]
 auth = ["cdk/auth"]
 prometheus = ["dep:cdk-prometheus"]
@@ -33,6 +34,9 @@ paste = "1.0.15"
 serde.workspace = true
 uuid.workspace = true
 sha2 = "0.10.8"
+redis = { version = "0.31.0", features = [
+    "tokio-rustls-comp",
+], optional = true }
 
 [target.'cfg(target_arch = "wasm32")'.dependencies]
 uuid = { workspace = true, features = ["js"] }

+ 4 - 0
crates/cdk-axum/src/cache/backend/mod.rs

@@ -1,3 +1,7 @@
 mod memory;
+#[cfg(feature = "redis")]
+mod redis;
 
 pub use self::memory::InMemoryHttpCache;
+#[cfg(feature = "redis")]
+pub use self::redis::{Config as RedisConfig, HttpCacheRedis};

+ 96 - 0
crates/cdk-axum/src/cache/backend/redis.rs

@@ -0,0 +1,96 @@
+use std::time::Duration;
+
+use redis::AsyncCommands;
+use serde::{Deserialize, Serialize};
+
+use crate::cache::{HttpCacheKey, HttpCacheStorage};
+
+/// Redis cache storage for the HTTP cache.
+///
+/// This cache storage backend uses Redis to store the cache.
+pub struct HttpCacheRedis {
+    cache_ttl: Duration,
+    prefix: Option<Vec<u8>>,
+    client: redis::Client,
+}
+
+/// Configuration for the Redis cache storage.
+#[derive(Debug, Clone, Serialize, Deserialize, Default)]
+pub struct Config {
+    /// Commong key prefix
+    pub key_prefix: Option<String>,
+
+    /// Connection string to the Redis server.
+    pub connection_string: String,
+}
+
+impl HttpCacheRedis {
+    /// Create a new Redis cache.
+    pub fn new(client: redis::Client) -> Self {
+        Self {
+            client,
+            prefix: None,
+            cache_ttl: Duration::from_secs(60),
+        }
+    }
+
+    /// Set a prefix for the cache keys.
+    ///
+    /// This is useful to have all the HTTP cache keys under a common prefix,
+    /// some sort of namespace, to make management of the database easier.
+    pub fn set_prefix(mut self, prefix: Vec<u8>) -> Self {
+        self.prefix = Some(prefix);
+        self
+    }
+}
+
+#[async_trait::async_trait]
+impl HttpCacheStorage for HttpCacheRedis {
+    fn set_expiration_times(&mut self, cache_ttl: Duration, _cache_tti: Duration) {
+        self.cache_ttl = cache_ttl;
+    }
+
+    async fn get(&self, key: &HttpCacheKey) -> Option<Vec<u8>> {
+        let mut conn = self
+            .client
+            .get_multiplexed_tokio_connection()
+            .await
+            .map_err(|err| {
+                tracing::error!("Failed to get redis connection: {:?}", err);
+                err
+            })
+            .ok()?;
+
+        let mut db_key = self.prefix.clone().unwrap_or_default();
+        db_key.extend(&**key);
+
+        conn.get(db_key)
+            .await
+            .map_err(|err| {
+                tracing::error!("Failed to get value from redis: {:?}", err);
+                err
+            })
+            .ok()?
+    }
+
+    async fn set(&self, key: HttpCacheKey, value: Vec<u8>) {
+        let mut db_key = self.prefix.clone().unwrap_or_default();
+        db_key.extend(&*key);
+
+        let mut conn = match self.client.get_multiplexed_tokio_connection().await {
+            Ok(conn) => conn,
+            Err(err) => {
+                tracing::error!("Failed to get redis connection: {:?}", err);
+                return;
+            }
+        };
+
+        let _: Result<(), _> = conn
+            .set_ex(db_key, value, self.cache_ttl.as_secs())
+            .await
+            .map_err(|err| {
+                tracing::error!("Failed to set value in redis: {:?}", err);
+                err
+            });
+    }
+}

+ 34 - 0
crates/cdk-axum/src/cache/config.rs

@@ -2,6 +2,11 @@ use serde::{Deserialize, Serialize};
 
 pub const ENV_CDK_MINTD_CACHE_BACKEND: &str = "CDK_MINTD_CACHE_BACKEND";
 
+#[cfg(feature = "redis")]
+pub const ENV_CDK_MINTD_CACHE_REDIS_URL: &str = "CDK_MINTD_CACHE_REDIS_URL";
+#[cfg(feature = "redis")]
+pub const ENV_CDK_MINTD_CACHE_REDIS_KEY_PREFIX: &str = "CDK_MINTD_CACHE_REDIS_KEY_PREFIX";
+
 pub const ENV_CDK_MINTD_CACHE_TTI: &str = "CDK_MINTD_CACHE_TTI";
 pub const ENV_CDK_MINTD_CACHE_TTL: &str = "CDK_MINTD_CACHE_TTL";
 
@@ -11,12 +16,27 @@ pub const ENV_CDK_MINTD_CACHE_TTL: &str = "CDK_MINTD_CACHE_TTL";
 pub enum Backend {
     #[default]
     Memory,
+    #[cfg(feature = "redis")]
+    Redis(super::backend::RedisConfig),
 }
 
 impl Backend {
     pub fn from_env_str(backend_str: &str) -> Option<Self> {
         match backend_str.to_lowercase().as_str() {
             "memory" => Some(Self::Memory),
+            #[cfg(feature = "redis")]
+            "redis" => {
+                // Get Redis configuration from environment
+                let connection_string = std::env::var(ENV_CDK_MINTD_CACHE_REDIS_URL)
+                    .unwrap_or_else(|_| "redis://127.0.0.1:6379".to_string());
+
+                let key_prefix = std::env::var(ENV_CDK_MINTD_CACHE_REDIS_KEY_PREFIX).ok();
+
+                Some(Self::Redis(super::backend::RedisConfig {
+                    connection_string,
+                    key_prefix,
+                }))
+            }
             _ => None,
         }
     }
@@ -46,6 +66,20 @@ impl Config {
         if let Ok(backend_str) = env::var(ENV_CDK_MINTD_CACHE_BACKEND) {
             if let Some(backend) = Backend::from_env_str(&backend_str) {
                 self.backend = backend;
+
+                // If Redis backend is selected, parse Redis configuration
+                #[cfg(feature = "redis")]
+                if matches!(self.backend, Backend::Redis(_)) {
+                    let connection_string = env::var(ENV_CDK_MINTD_CACHE_REDIS_URL)
+                        .unwrap_or_else(|_| "redis://127.0.0.1:6379".to_string());
+
+                    let key_prefix = env::var(ENV_CDK_MINTD_CACHE_REDIS_KEY_PREFIX).ok();
+
+                    self.backend = Backend::Redis(super::backend::RedisConfig {
+                        connection_string,
+                        key_prefix,
+                    });
+                }
             }
         }
 

+ 18 - 1
crates/cdk-axum/src/cache/mod.rs

@@ -7,7 +7,7 @@
 //! idempotent operations.
 //!
 //! This mod also provides common backend implementations as well, such as In
-//! Memory (default).
+//! Memory (default) and Redis.
 use std::ops::Deref;
 use std::sync::Arc;
 use std::time::Duration;
@@ -89,6 +89,23 @@ impl From<config::Config> for HttpCache {
                 Duration::from_secs(config.tti.unwrap_or(DEFAULT_TTI_SECS)),
                 None,
             ),
+            #[cfg(feature = "redis")]
+            config::Backend::Redis(redis_config) => {
+                let client = redis::Client::open(redis_config.connection_string)
+                    .expect("Failed to create Redis client");
+                let storage = HttpCacheRedis::new(client).set_prefix(
+                    redis_config
+                        .key_prefix
+                        .unwrap_or_default()
+                        .as_bytes()
+                        .to_vec(),
+                );
+                Self::new(
+                    Duration::from_secs(config.ttl.unwrap_or(DEFAULT_TTL_SECS)),
+                    Duration::from_secs(config.tti.unwrap_or(DEFAULT_TTI_SECS)),
+                    Some(Box::new(storage)),
+                )
+            }
         }
     }
 }

+ 1 - 0
crates/cdk-mintd/Cargo.toml

@@ -26,6 +26,7 @@ grpc-processor = ["dep:cdk-payment-processor", "cdk-signatory/grpc"]
 sqlcipher = ["sqlite", "cdk-sqlite/sqlcipher"]
 # MSRV is not committed to with swagger enabled
 swagger = ["cdk-axum/swagger", "dep:utoipa", "dep:utoipa-swagger-ui"]
+redis = ["cdk-axum/redis"]
 auth = ["cdk/auth", "cdk-axum/auth", "cdk-sqlite?/auth", "cdk-postgres?/auth"]
 prometheus = ["cdk/prometheus", "dep:cdk-prometheus", "cdk-sqlite?/prometheus", "cdk-axum/prometheus"]
 

+ 4 - 1
crates/cdk-mintd/example.config.toml

@@ -33,10 +33,13 @@ enabled = false
 #port = 9090
 # 
 [info.http_cache]
-# backend type: memory (default)
+# memory or redis
 backend = "memory"
 ttl = 60
 tti = 60
+# `key_prefix` and `connection_string` required for redis
+# key_prefix = "mintd"
+# connection_string = "redis://localhost"
 
 # NOTE: If [mint_management_rpc] is enabled these values will only be used on first start up.
 # Further changes must be made through the rpc.

+ 25 - 1
docker-compose.yaml

@@ -58,6 +58,9 @@ services:
       # - CDK_MINTD_DATABASE_URL=postgresql://cdk_user:cdk_password@postgres:5432/cdk_mint
       # Cache configuration
       - CDK_MINTD_CACHE_BACKEND=memory
+      # For Redis cache (requires redis service, enable with: docker-compose --profile redis up):
+      # - CDK_MINTD_CACHE_REDIS_URL=redis://redis:6379
+      # - CDK_MINTD_CACHE_REDIS_KEY_PREFIX=cdk-mintd
       - CDK_MINTD_PROMETHEUS_ENABLED=true
       - CDK_MINTD_PROMETHEUS_ADDRESS=0.0.0.0
       - CDK_MINTD_PROMETHEUS_PORT=9000
@@ -131,13 +134,34 @@ services:
   #     timeout: 5s
   #     retries: 5
 
-
+  # Redis cache service (optional)
+  # Enable with: docker-compose --profile redis up
+#   redis:
+#     image: redis:7-alpine
+#     container_name: mint_redis
+#     restart: unless-stopped
+#     profiles:
+#       - redis
+#     ports:
+#       - "6379:6379"
+#     volumes:
+#       - redis_data:/data
+#     command: redis-server --save 60 1 --loglevel warning
+#     healthcheck:
+#       test: ["CMD", "redis-cli", "ping"]
+#       interval: 10s
+#       timeout: 3s
+#       retries: 5
 
 volumes:
   postgres_data:
     driver: local
   ldk_node_data:
     driver: local
+# redis_data:
+#   driver: local
+
+
 
 networks:
   cdk: