Skip to content

Commit 8e3eb79

Browse files
add Redis support and configuration
1 parent ea99b64 commit 8e3eb79

13 files changed

Lines changed: 1632 additions & 136 deletions

Cargo.lock

Lines changed: 1409 additions & 124 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,25 @@ rust-version = "1.85"
99

1010
[features]
1111
default = ["caching"]
12-
release = ["kms-aws", "middleware", "key_custodian", "limit", "kms-hashicorp-vault", "caching", "external_key_manager", "vergen"]
12+
release = ["kms-aws", "middleware", "key_custodian", "limit", "kms-hashicorp-vault", "caching", "external_key_manager", "vergen", "redis"]
1313
kms-aws = ["dep:aws-config", "dep:aws-sdk-kms"]
1414
kms-hashicorp-vault = ["dep:vaultrs"]
1515
limit = []
1616
middleware = []
1717
key_custodian = []
1818
caching = ["dep:moka"]
19+
redis = ["dep:hyperswitch_redis_interface"]
1920
console = ["tokio/tracing", "dep:console-subscriber"]
2021
external_key_manager = ["reqwest/rustls-tls"]
2122
vergen = ["build_info/vergen-gix"]
2223

2324
[dependencies]
24-
async-trait = "0.1.81"
25+
async-trait = "0.1.88"
2526
aws-config = { version = "1.5.5", optional = true }
2627
aws-sdk-kms = { version = "1.40.0", optional = true }
2728
base64 = "0.22.1"
2829
bytes = "1.7.1"
29-
futures = "0.3.30"
30+
futures = "0.3.31"
3031
gethostname = "0.5.0"
3132
rustc-hash = "2.1"
3233
once_cell = "1.19.0"
@@ -46,24 +47,25 @@ tracing-subscriber = { version = "0.3.18", default-features = true, features = [
4647
console-subscriber = { version = "0.4.0", optional = true }
4748
http-body-util = "0.1.2"
4849

49-
diesel = { version = "2.2.3", features = ["postgres", "serde_json", "time"] }
50+
diesel = { version = "2.2.10", features = ["postgres", "serde_json", "time"] }
5051
diesel-async = { version = "0.5.0", features = ["postgres", "deadpool"] }
5152

5253
serde = { version = "1.0.228", features = ["derive"] }
5354
serde_json = "1.0.140"
5455
josekit = "0.8.7"
5556

56-
thiserror = "1.0.63"
57+
thiserror = "1.0.69"
5758
config = "0.14.0"
5859
serde_path_to_error = "0.1.16"
5960
error-stack = "0.5.0"
6061
futures-util = "0.3.30"
6162
digest = "0.10"
6263
hyperswitch_masking = { version = "0.0.1", features = ["diesel", "serde"] }
63-
ring = { version = "0.17.8", features = ["std"] }
64+
hyperswitch_redis_interface = { git = "https://github.com/juspay/hyperswitch", tag = "2026.06.12.0", package = "redis_interface", default-features = false, features = ["fred"], optional = true }
65+
ring = { version = "0.17.14", features = ["std"] }
6466
hex = "0.4.3"
6567
time = { version = "0.3.45", features = ["serde"] }
66-
uuid = { version = "1.10.0", features = ["v7", "fast-rng"] }
68+
uuid = { version = "1.20.0", features = ["v7", "fast-rng"] }
6769
moka = { version = "0.12.8", features = ["future"], optional = true }
6870
reqwest = { version = "0.12.7", features = ["json", "__rustls"] }
6971

config/config.example.toml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,24 @@ mode = "disabled" # Options: "disabled", "enabled", "enabled_with_mtls"
8383
# -----END CERTIFICATE-----
8484
# """
8585

86+
# Redis configuration (only used with the `redis` feature)
87+
[redis]
88+
host = "127.0.0.1" # Redis server host
89+
port = 6379 # Redis server port
90+
pool_size = 5 # number of connections in the pool
91+
reconnect_max_attempts = 0 # max reconnection attempts (0 = retry forever)
92+
reconnect_delay = 200 # delay between reconnection attempts (milliseconds)
93+
default_ttl = 300 # default key TTL (seconds)
94+
default_hash_ttl = 900 # default hash-table TTL (seconds)
95+
use_legacy_version = false # use RESP2 instead of RESP3
96+
stream_read_count = 1 # entries read per stream read
97+
auto_pipeline = true # automatically pipeline commands
98+
broadcast_channel_capacity = 32 # pub/sub broadcast channel capacity
99+
disable_auto_backpressure = false # disable automatic backpressure
100+
max_in_flight_commands = 5000 # max in-flight commands before backpressure (0 = disabled)
101+
default_command_timeout = 30 # command timeout (seconds)
102+
unresponsive_timeout = 10 # mark a connection unresponsive after (seconds)
103+
unresponsive_check_interval = 2 # how often to check for unresponsiveness (seconds)
104+
max_feed_count = 200 # max commands fed to the server at once
105+
max_failure_threshold_seconds = 5 # max seconds Redis may be unreachable before it's failed
106+

config/development.toml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,23 @@ public = { master_key = "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9
3030

3131
[external_key_manager]
3232
mode = "disabled"
33+
34+
[redis]
35+
host = "127.0.0.1"
36+
port = 6379
37+
pool_size = 5
38+
reconnect_max_attempts = 0
39+
reconnect_delay = 200
40+
default_ttl = 300
41+
default_hash_ttl = 900
42+
use_legacy_version = false
43+
stream_read_count = 1
44+
auto_pipeline = true
45+
broadcast_channel_capacity = 32
46+
disable_auto_backpressure = false
47+
max_in_flight_commands = 5000
48+
default_command_timeout = 30
49+
unresponsive_timeout = 10
50+
unresponsive_check_interval = 2
51+
max_feed_count = 200
52+
max_failure_threshold_seconds = 5

config/docker-configuration.toml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,23 @@ identity = ""
3232
[external_key_manager]
3333
url = "http://localhost:5000"
3434
cert = ""
35+
36+
[redis]
37+
host = "127.0.0.1"
38+
port = 6379
39+
pool_size = 5
40+
reconnect_max_attempts = 0
41+
reconnect_delay = 200
42+
default_ttl = 300
43+
default_hash_ttl = 900
44+
use_legacy_version = false
45+
stream_read_count = 1
46+
auto_pipeline = true
47+
broadcast_channel_capacity = 32
48+
disable_auto_backpressure = false
49+
max_in_flight_commands = 5000
50+
default_command_timeout = 30
51+
unresponsive_timeout = 10
52+
unresponsive_check_interval = 2
53+
max_feed_count = 200
54+
max_failure_threshold_seconds = 5

src/app.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ pub struct TenantAppState {
4343
pub db: Storage,
4444
pub config: config::TenantConfig,
4545
pub api_client: ApiClient,
46+
#[cfg(feature = "redis")]
47+
pub redis: Option<storage::redis::RedisStore>,
4648
}
4749

4850
#[allow(clippy::expect_used)]
@@ -54,6 +56,8 @@ impl TenantAppState {
5456
global_config: &GlobalConfig,
5557
tenant_config: TenantConfig,
5658
api_client: ApiClient,
59+
#[cfg(feature = "redis")]
60+
shared_redis: Option<&storage::redis::RedisStore>,
5761
) -> error_stack::Result<Self, error::ConfigurationError> {
5862
#[allow(clippy::map_identity)]
5963
let db = storage::Storage::new(
@@ -72,6 +76,8 @@ impl TenantAppState {
7276
Ok(Self {
7377
db,
7478
api_client,
79+
#[cfg(feature = "redis")]
80+
redis: shared_redis.map(|store| store.clone_with_prefix(&tenant_config.tenant_id)),
7581
config: tenant_config,
7682
})
7783
}

src/config.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ use error_stack::ResultExt;
88
use hyperswitch_masking::ExposeInterface;
99
#[cfg(feature = "external_key_manager")]
1010
use hyperswitch_masking::Secret;
11+
#[cfg(feature = "redis")]
12+
use hyperswitch_redis_interface::RedisSettings;
1113

1214
use crate::{
1315
api_client::ApiClientConfig,
@@ -36,6 +38,8 @@ pub struct GlobalConfig {
3638
pub api_client: ApiClientConfig,
3739
#[serde(default)]
3840
pub external_key_manager: ExternalKeyManagerConfig,
41+
#[cfg(feature = "redis")]
42+
pub redis: Option<RedisSettings>,
3943
}
4044

4145
#[derive(Clone, Debug)]

src/routes/health.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ pub struct Diagnostics {
3636
database: DatabaseHealth,
3737
#[cfg(feature = "external_key_manager")]
3838
keymanager_status: HealthState,
39+
#[cfg(feature = "redis")]
40+
redis_status: HealthState,
3941
}
4042

4143
#[derive(Debug, serde::Serialize, Default)]
@@ -51,7 +53,7 @@ pub enum HealthState {
5153
Working,
5254
#[default]
5355
Failing,
54-
#[cfg(feature = "external_key_manager")]
56+
#[cfg(any(feature = "external_key_manager", feature = "redis"))]
5557
Disabled,
5658
}
5759

@@ -109,10 +111,24 @@ pub async fn diagnostics(TenantStateResolver(state): TenantStateResolver) -> Jso
109111
}
110112
};
111113

114+
#[cfg(feature = "redis")]
115+
let redis_status = match &state.redis {
116+
None => HealthState::Disabled,
117+
Some(redis) => match redis.test().await {
118+
Ok(()) => HealthState::Working,
119+
Err(err) => {
120+
crate::logger::error!(redis_err=?err);
121+
HealthState::Failing
122+
}
123+
},
124+
};
125+
112126
axum::Json(Diagnostics {
113127
key_custodian_locked: false,
114128
database: db_health,
115129
#[cfg(feature = "external_key_manager")]
116130
keymanager_status,
131+
#[cfg(feature = "redis")]
132+
redis_status,
117133
})
118134
}

src/routes/key_custodian.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ pub async fn decrypt(
118118
&global_app_state.global_config,
119119
tenant_config,
120120
global_app_state.api_client.clone(),
121+
#[cfg(feature = "redis")]
122+
global_app_state.redis_store.as_ref(),
121123
)
122124
.await
123125
.change_context(error::ApiError::TenantError(

src/storage.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ use crate::{
2020
pub mod caching;
2121
pub mod consts;
2222
pub mod db;
23+
#[cfg(feature = "redis")]
24+
pub mod redis;
2325
pub mod schema;
2426
pub mod storage_v2;
2527
pub mod types;

0 commit comments

Comments
 (0)