use std::sync::Arc;
#[cfg(feature = "redis")]
use sqlx::PgPool;
use tonic::{Request, Response, Status};
use crate::metrics::{MetricsRecorder, NoopMetrics};
use crate::proto::udb::core::cache::services::v1 as cache_pb;
use crate::proto::udb::core::cache::services::v1::cache_service_server::CacheService;
use crate::runtime::channels::ChannelManager;
pub use crate::proto::udb::core::cache::services::v1::cache_service_server::CacheServiceServer;
use super::DataBrokerService;
mod config;
mod errors;
#[cfg(feature = "redis")]
mod events;
mod handlers;
mod keys;
#[cfg(feature = "redis")]
mod redis_engine;
#[cfg(test)]
mod tests;
mod worker;
#[cfg(feature = "redis")]
pub(crate) use config::{CACHE_INVALIDATION_BATCH, cache_invalidation_interval};
#[cfg(feature = "redis")]
pub(crate) use worker::run_cache_invalidation_worker_once;
pub struct CacheServiceImpl {
#[cfg(feature = "redis")]
pub(crate) redis: Option<redis::Client>,
#[cfg(feature = "redis")]
pub(crate) pg_pool: Option<PgPool>,
#[cfg(feature = "redis")]
pub(crate) outbox_relation: Option<String>,
pub(crate) channels: Option<ChannelManager>,
pub(crate) metrics: Arc<dyn MetricsRecorder>,
}
impl CacheServiceImpl {
pub fn new() -> Self {
Self {
#[cfg(feature = "redis")]
redis: None,
#[cfg(feature = "redis")]
pg_pool: None,
#[cfg(feature = "redis")]
outbox_relation: None,
channels: None,
metrics: Arc::new(NoopMetrics),
}
}
#[cfg(feature = "redis")]
pub(crate) fn with_redis(mut self, redis: Option<redis::Client>) -> Self {
self.redis = redis;
self
}
#[cfg(feature = "redis")]
pub(crate) fn with_postgres(mut self, pool: Option<PgPool>) -> Self {
self.pg_pool = pool;
self
}
#[cfg(feature = "redis")]
pub(crate) fn with_outbox(mut self, relation: Option<String>) -> Self {
self.outbox_relation = relation;
self
}
pub(crate) fn with_channels(mut self, channels: Option<ChannelManager>) -> Self {
self.channels = channels;
self
}
pub(crate) fn with_metrics(mut self, metrics: Arc<dyn MetricsRecorder>) -> Self {
self.metrics = metrics;
self
}
#[cfg(feature = "redis")]
pub(crate) fn require_redis(&self) -> Result<&redis::Client, Status> {
self.redis.as_ref().ok_or_else(|| {
errors::redis_capability_status(
"request_dispatch",
"redis_backend",
"cache service requires a configured Redis backend",
)
})
}
}
impl Default for CacheServiceImpl {
fn default() -> Self {
Self::new()
}
}
#[tonic::async_trait]
impl CacheService for CacheServiceImpl {
async fn get(
&self,
request: Request<cache_pb::GetRequest>,
) -> Result<Response<cache_pb::GetResponse>, Status> {
handlers::get(self, request).await
}
async fn set(
&self,
request: Request<cache_pb::SetRequest>,
) -> Result<Response<cache_pb::SetResponse>, Status> {
handlers::set(self, request).await
}
async fn delete(
&self,
request: Request<cache_pb::DeleteRequest>,
) -> Result<Response<cache_pb::DeleteResponse>, Status> {
handlers::delete(self, request).await
}
async fn scan(
&self,
request: Request<cache_pb::ScanRequest>,
) -> Result<Response<cache_pb::ScanResponse>, Status> {
handlers::scan(self, request).await
}
async fn create_namespace(
&self,
request: Request<cache_pb::CreateNamespaceRequest>,
) -> Result<Response<cache_pb::CreateNamespaceResponse>, Status> {
handlers::create_namespace(self, request).await
}
async fn delete_namespace(
&self,
request: Request<cache_pb::DeleteNamespaceRequest>,
) -> Result<Response<cache_pb::DeleteNamespaceResponse>, Status> {
handlers::delete_namespace(self, request).await
}
async fn get_namespace_stats(
&self,
request: Request<cache_pb::GetNamespaceStatsRequest>,
) -> Result<Response<cache_pb::GetNamespaceStatsResponse>, Status> {
handlers::get_namespace_stats(self, request).await
}
}
impl DataBrokerService {
pub(crate) fn build_cache_service(&self) -> CacheServiceImpl {
let runtime = self.runtime.load_full();
let channels = Some(runtime.channels().clone());
let service = CacheServiceImpl::new()
.with_channels(channels)
.with_metrics(self.metrics.clone());
#[cfg(feature = "redis")]
let service = {
let pg_pool = runtime
.native_store_pool_for_service("cache", true, "")
.ok();
let outbox = runtime.config().cdc.outbox_relation();
service
.with_redis(runtime.redis_clone())
.with_postgres(pg_pool)
.with_outbox(Some(outbox))
};
service
}
}