use crate::AppState;
use actix_web::web::Data;
use serde_json::Value;
use tokio::time::error::Elapsed;
use crate::utils::redis_client::{
GLOBAL_REDIS, note_redis_failure_and_start_cooldown, note_redis_success,
redis_operation_timeout, should_bypass_redis_temporarily,
};
use std::time::Instant;
const RAW_CACHE_KEY_SUFFIX: &str = "__raw_json";
fn raw_cache_key(cache_key: &str) -> String {
format!("{cache_key}:{RAW_CACHE_KEY_SUFFIX}")
}
pub async fn hydrate_cache_and_return_json(
app_state: Data<AppState>,
cache_key: String,
json_body: Vec<Value>,
) -> Vec<Value> {
hydrate_cache_and_return_json_with_write_metric(
app_state,
cache_key,
json_body,
"gateway_fetch_cache_write",
)
.await
}
pub async fn hydrate_cache_and_return_json_with_write_metric(
app_state: Data<AppState>,
cache_key: String,
json_body: Vec<Value>,
write_metric: &str,
) -> Vec<Value> {
let value_to_cache: Value = Value::Array(json_body.clone());
let raw_body: Option<String> = match &value_to_cache {
Value::Array(arr) if arr.len() == 1 => serde_json::to_string(&arr[0]).ok(),
_ => serde_json::to_string(&value_to_cache).ok(),
};
app_state
.cache
.insert(cache_key.clone(), value_to_cache)
.await;
if let Some(raw_body) = raw_body {
app_state
.cache
.insert(raw_cache_key(&cache_key), Value::String(raw_body))
.await;
}
if let Some(redis) = GLOBAL_REDIS.get() {
if should_bypass_redis_temporarily() {
return json_body;
}
let redis_ttl_secs: u64 = app_state
.cache
.policy()
.time_to_live()
.map(|ttl| ttl.as_secs())
.filter(|ttl| *ttl > 0)
.unwrap_or(900u64);
let redis_write_started_at: Instant = Instant::now();
let redis_write_result: Result<Result<(), String>, Elapsed> = tokio::time::timeout(
redis_operation_timeout(),
redis.set_with_ttl(&cache_key, &Value::Array(json_body.clone()), redis_ttl_secs),
)
.await;
match redis_write_result {
Ok(Ok(())) => {
note_redis_success();
app_state.metrics_state.record_management_mutation(
write_metric,
"redis_set_ok",
redis_write_started_at.elapsed().as_secs_f64(),
);
tracing::debug!(cache_key = %cache_key, ttl_secs = redis_ttl_secs, "Redis set succeeded during cache hydration");
}
Ok(Err(err)) => {
note_redis_failure_and_start_cooldown();
app_state.metrics_state.record_management_mutation(
write_metric,
"redis_set_error",
redis_write_started_at.elapsed().as_secs_f64(),
);
tracing::warn!(error = %err, cache_key = %cache_key, ttl_secs = redis_ttl_secs, "Redis set failed during cache hydration");
}
Err(_) => {
note_redis_failure_and_start_cooldown();
app_state.metrics_state.record_management_mutation(
write_metric,
"redis_set_timeout",
redis_write_started_at.elapsed().as_secs_f64(),
);
tracing::warn!(cache_key = %cache_key, ttl_secs = redis_ttl_secs, "Redis set timed out during cache hydration");
}
}
}
json_body
}