#![allow(dead_code)]
use std::sync::Arc;
use arrow::array::{ArrayRef, Int32Array};
use arrow::datatypes::DataType;
use arrow::record_batch::RecordBatch;
use arrow_flight::error::FlightError;
use arrow_flight::flight_service_server::FlightService;
use arrow_flight::{
Action, ActionType, Criteria, Empty, FlightData, FlightDescriptor, FlightEndpoint, FlightInfo,
HandshakeRequest, HandshakeResponse, PollInfo, PutResult, SchemaResult, Ticket,
encode::{DictionaryHandling, FlightDataEncoderBuilder},
};
use futures::stream::{self, BoxStream, TryStreamExt};
use minarrow::ffi::arrow_dtype::CategoricalIndexType;
use minarrow::{ArrowType, Table};
use tonic::{Request, Response, Status, Streaming};
pub const FLIGHT_HTTP2_WINDOW: u32 = 8 * 1024 * 1024;
pub fn assert_export_parity(table: &Table, batch: &RecordBatch) {
assert_eq!(
batch.num_rows(),
table.n_rows,
"row count drift in the Arrow export"
);
assert_eq!(
batch.num_columns(),
table.n_cols(),
"column count drift in the Arrow export"
);
for (field, arrow_field) in table.schema().iter().zip(batch.schema().fields()) {
assert_eq!(
arrow_field.name(),
&field.name,
"column name drift in the Arrow export"
);
let expected = match field.dtype {
ArrowType::Int32 => DataType::Int32,
ArrowType::Int64 => DataType::Int64,
ArrowType::Float32 => DataType::Float32,
ArrowType::Float64 => DataType::Float64,
ArrowType::String => DataType::Utf8,
#[cfg(not(feature = "default_categorical_8"))]
ArrowType::Dictionary(CategoricalIndexType::UInt32) => {
DataType::Dictionary(Box::new(DataType::UInt32), Box::new(DataType::Utf8))
}
#[cfg(feature = "default_categorical_8")]
ArrowType::Dictionary(CategoricalIndexType::UInt8) => {
DataType::Dictionary(Box::new(DataType::UInt8), Box::new(DataType::Utf8))
}
ref other => panic!("bench shapes do not cover the {:?} column type", other),
};
assert_eq!(
arrow_field.data_type(),
&expected,
"type drift in the Arrow export for column {}",
field.name
);
}
}
#[derive(Clone)]
pub struct BenchFlightService {
pub batch: Arc<RecordBatch>,
}
#[tonic::async_trait]
impl FlightService for BenchFlightService {
type HandshakeStream = BoxStream<'static, Result<HandshakeResponse, Status>>;
type ListFlightsStream = BoxStream<'static, Result<FlightInfo, Status>>;
type DoGetStream = BoxStream<'static, Result<FlightData, Status>>;
type DoPutStream = BoxStream<'static, Result<PutResult, Status>>;
type DoActionStream = BoxStream<'static, Result<arrow_flight::Result, Status>>;
type ListActionsStream = BoxStream<'static, Result<ActionType, Status>>;
type DoExchangeStream = BoxStream<'static, Result<FlightData, Status>>;
async fn handshake(
&self,
_request: Request<Streaming<HandshakeRequest>>,
) -> Result<Response<Self::HandshakeStream>, Status> {
Err(Status::unimplemented("handshake not implemented"))
}
async fn list_flights(
&self,
_request: Request<Criteria>,
) -> Result<Response<Self::ListFlightsStream>, Status> {
Err(Status::unimplemented("list_flights not implemented"))
}
async fn get_flight_info(
&self,
request: Request<FlightDescriptor>,
) -> Result<Response<FlightInfo>, Status> {
let descriptor = request.into_inner();
let bytes: [u8; 17] = descriptor
.cmd
.as_ref()
.try_into()
.map_err(|_| Status::invalid_argument("flight descriptor must be 17 bytes"))?;
let batches_per_endpoint = u64::from_le_bytes(bytes[0..8].try_into().unwrap());
let endpoint_count = u64::from_le_bytes(bytes[8..16].try_into().unwrap());
if endpoint_count == 0 {
return Err(Status::invalid_argument(
"flight descriptor must request at least one endpoint",
));
}
let endpoints = (0..endpoint_count)
.map(|idx| {
let mut ticket = Vec::with_capacity(17);
ticket.extend_from_slice(&batches_per_endpoint.to_le_bytes());
ticket.extend_from_slice(&idx.to_le_bytes());
ticket.push(bytes[16]);
FlightEndpoint::new().with_ticket(Ticket::new(ticket))
})
.collect();
let total_records = batches_per_endpoint
.checked_mul(endpoint_count)
.and_then(|n| n.checked_mul(self.batch.num_rows() as u64))
.and_then(|n| i64::try_from(n).ok())
.ok_or_else(|| Status::invalid_argument("flight record count exceeds i64"))?;
let info = FlightInfo::new()
.try_with_schema(self.batch.schema().as_ref())
.map_err(|e| Status::internal(format!("encode flight schema: {e}")))?
.with_descriptor(descriptor)
.with_endpoints(endpoints)
.with_total_records(total_records)
.with_ordered(true);
Ok(Response::new(info))
}
async fn poll_flight_info(
&self,
_request: Request<FlightDescriptor>,
) -> Result<Response<PollInfo>, Status> {
Err(Status::unimplemented("poll_flight_info not implemented"))
}
async fn get_schema(
&self,
_request: Request<FlightDescriptor>,
) -> Result<Response<SchemaResult>, Status> {
Err(Status::unimplemented("get_schema not implemented"))
}
async fn do_get(
&self,
request: Request<Ticket>,
) -> Result<Response<Self::DoGetStream>, Status> {
let ticket = request.into_inner();
let (n, start_seq) = match ticket.ticket.len() {
8 => (u64::from_le_bytes(ticket.ticket.as_ref().try_into().unwrap()), None),
17 => {
let n = u64::from_le_bytes(ticket.ticket[0..8].try_into().unwrap());
let idx = u64::from_le_bytes(ticket.ticket[8..16].try_into().unwrap());
(n, Some(idx * n))
}
_ => return Err(Status::invalid_argument("flight ticket must be 8 or 17 bytes")),
};
let batch = Arc::clone(&self.batch);
let batch_stream = stream::iter((0..n).map(move |b| match start_seq {
None => Ok((*batch).clone()),
Some(start) => {
let seq = start + b;
let rows = batch.num_rows();
let first = Int32Array::from_iter_values(
(0..rows).map(|i| (seq as i32).wrapping_add(i as i32)),
);
let mut cols = batch.columns().to_vec();
cols[0] = Arc::new(first) as ArrayRef;
RecordBatch::try_new(batch.schema(), cols).map_err(FlightError::from)
}
}));
let builder = FlightDataEncoderBuilder::new()
.with_dictionary_handling(DictionaryHandling::Resend);
let flight_data = builder
.build(batch_stream)
.map_err(|err| Status::internal(format!("flight encode failure: {err}")));
Ok(Response::new(Box::pin(flight_data)))
}
async fn do_put(
&self,
_request: Request<Streaming<FlightData>>,
) -> Result<Response<Self::DoPutStream>, Status> {
Err(Status::unimplemented("do_put not implemented"))
}
async fn do_action(
&self,
_request: Request<Action>,
) -> Result<Response<Self::DoActionStream>, Status> {
Err(Status::unimplemented("do_action not implemented"))
}
async fn list_actions(
&self,
_request: Request<Empty>,
) -> Result<Response<Self::ListActionsStream>, Status> {
Err(Status::unimplemented("list_actions not implemented"))
}
async fn do_exchange(
&self,
_request: Request<Streaming<FlightData>>,
) -> Result<Response<Self::DoExchangeStream>, Status> {
Err(Status::unimplemented("do_exchange not implemented"))
}
}