use std::sync::Arc;
use sqlx::PgPool;
use tonic::{Request, Response, Status};
use crate::metrics::{MetricsRecorder, NoopMetrics};
use crate::proto::udb::core::search::services::v1 as search_pb;
use crate::proto::udb::core::search::services::v1::search_service_server::SearchService;
use crate::runtime::DataBrokerRuntime;
use crate::runtime::catalog::CatalogManager;
use crate::runtime::channels::ChannelManager;
pub use crate::proto::udb::core::search::services::v1::search_service_server::SearchServiceServer;
use super::DataBrokerService;
mod config;
mod errors;
mod events;
mod fusion;
mod handlers;
mod model;
mod store;
#[cfg(test)]
mod tests;
mod workers;
pub(crate) use config::{
SEARCH_FRESHNESS_BATCH, SEARCH_REINDEX_BATCH, search_freshness_interval,
search_reindex_interval,
};
pub(crate) use workers::{run_index_freshness_consumer, run_search_reindex_once};
pub struct SearchServiceImpl {
pub(crate) pg_pool: Option<PgPool>,
pub(crate) runtime: Option<Arc<DataBrokerRuntime>>,
pub(crate) catalog: Option<Arc<CatalogManager>>,
pub(crate) outbox_relation: Option<String>,
pub(crate) channels: Option<ChannelManager>,
pub(crate) metrics: Arc<dyn MetricsRecorder>,
}
impl SearchServiceImpl {
pub fn new() -> Self {
Self {
pg_pool: None,
runtime: None,
catalog: None,
outbox_relation: None,
channels: None,
metrics: Arc::new(NoopMetrics),
}
}
pub fn with_postgres(mut self, pool: Option<PgPool>) -> Self {
self.pg_pool = pool;
self
}
pub(crate) fn with_runtime(mut self, runtime: Option<Arc<DataBrokerRuntime>>) -> Self {
self.runtime = runtime;
self
}
pub(crate) fn with_catalog(mut self, catalog: Option<Arc<CatalogManager>>) -> Self {
self.catalog = catalog;
self
}
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
}
pub(crate) fn require_runtime(&self) -> Result<&DataBrokerRuntime, Status> {
self.runtime.as_deref().ok_or_else(|| {
errors::search_capability_status(
"native_entity_dispatch",
"runtime_native_entity_dispatch",
"search service requires runtime native-entity dispatch (no runtime configured)",
)
})
}
pub(crate) fn require_catalog(&self) -> Result<&CatalogManager, Status> {
self.catalog.as_deref().ok_or_else(|| {
errors::search_capability_status(
"catalog_lookup",
"active_catalog",
"search service requires the active catalog (no catalog configured)",
)
})
}
}
impl Default for SearchServiceImpl {
fn default() -> Self {
Self::new()
}
}
#[tonic::async_trait]
impl SearchService for SearchServiceImpl {
async fn create_index(
&self,
request: Request<search_pb::CreateIndexRequest>,
) -> Result<Response<search_pb::CreateIndexResponse>, Status> {
handlers::create_index(self, request).await
}
async fn delete_index(
&self,
request: Request<search_pb::DeleteIndexRequest>,
) -> Result<Response<search_pb::DeleteIndexResponse>, Status> {
handlers::delete_index(self, request).await
}
async fn list_indexes(
&self,
request: Request<search_pb::ListIndexesRequest>,
) -> Result<Response<search_pb::ListIndexesResponse>, Status> {
handlers::list_indexes(self, request).await
}
async fn search(
&self,
request: Request<search_pb::SearchRequest>,
) -> Result<Response<search_pb::SearchResponse>, Status> {
handlers::search(self, request).await
}
async fn reindex(
&self,
request: Request<search_pb::ReindexRequest>,
) -> Result<Response<search_pb::ReindexResponse>, Status> {
handlers::reindex(self, request).await
}
}
impl DataBrokerService {
pub(crate) fn build_search_service(&self) -> SearchServiceImpl {
let runtime = self.runtime.load_full();
let pg_pool = runtime
.native_store_pool_for_service("search", true, "")
.ok();
let outbox = runtime.config().cdc.outbox_relation();
let channels = Some(runtime.channels().clone());
SearchServiceImpl::new()
.with_postgres(pg_pool)
.with_runtime(Some(runtime))
.with_catalog(Some(self.catalog.clone()))
.with_outbox(Some(outbox))
.with_channels(channels)
.with_metrics(self.metrics.clone())
}
}