use std::net::SocketAddr;
use async_trait::async_trait;
use datafusion::execution::context::{SessionContext, SessionState};
use datafusion_flight_sql_server::service::FlightSqlService;
use datafusion_flight_sql_server::session::SessionStateProvider;
use jammi_db::tenant::TenantContext;
use jammi_db::tenant_scope::TenantBinding;
use tonic::transport::Server;
use tonic::{Request, Status};
use std::sync::Arc;
use tower::Layer;
use crate::grpc::catalog::CatalogServer;
use crate::grpc::proto::catalog::catalog_service_server::CatalogServiceServer;
use crate::grpc::session::{SessionIdTenantResolver, SessionStore, TenantResolver, TenantScope};
use crate::tenant_resolver_layer::TenantResolverLayer;
pub async fn serve_flight(
ctx: &SessionContext,
addr: SocketAddr,
) -> Result<(), Box<dyn std::error::Error>> {
let service = FlightSqlService::new(ctx.state());
tracing::info!("Flight SQL server listening on {addr}");
service.serve(addr.to_string()).await
}
pub async fn serve_flight_with_catalog_service(
base_ctx: &SessionContext,
base_tenant_binding: TenantBinding,
addr: SocketAddr,
store: SessionStore,
) -> Result<(), Box<dyn std::error::Error>> {
let resolver = SessionIdTenantResolver::arc(store.clone());
let provider = TenantBoundProvider::new(
base_ctx.state(),
base_tenant_binding.clone(),
Arc::clone(&resolver),
);
let flight = FlightSqlService::new_with_provider(Box::new(provider));
let flight_svc = arrow_flight::flight_service_server::FlightServiceServer::new(flight);
let catalog_svc =
TenantResolverLayer::new(resolver).layer(CatalogServiceServer::new(CatalogServer::new(
store,
crate::tiers::TierSet::resolve(std::iter::empty())?,
None,
)));
tracing::info!(
"Flight SQL + CatalogService listening on {addr} \
(tenant binding via jammi-session-id header)"
);
Server::builder()
.add_service(flight_svc)
.add_service(catalog_svc)
.serve(addr)
.await?;
Ok(())
}
pub struct TenantBoundProvider {
base_state: SessionState,
binding: TenantBinding,
resolver: Arc<dyn TenantResolver>,
}
impl TenantBoundProvider {
pub fn new(
base_state: SessionState,
binding: TenantBinding,
resolver: Arc<dyn TenantResolver>,
) -> Self {
Self {
base_state,
binding,
resolver,
}
}
}
#[async_trait]
impl SessionStateProvider for TenantBoundProvider {
async fn new_context(&self, request: &Request<()>) -> Result<SessionState, Status> {
let ctx = match self.resolver.resolve(request.metadata()).await? {
TenantScope::Tenant(t) => TenantContext::Scoped(t),
TenantScope::Global => TenantContext::Unscoped,
};
self.binding.set_shared(ctx);
Ok(self.base_state.clone())
}
}