use std::sync::Arc;
use tracing::debug;
use tracing::trace;
use tracing::warn;
use tracing::error;
use futures_util::io::AsyncRead;
use futures_util::io::AsyncWrite;
use tokio::select;
use event_listener::Event;
use tokio::sync::broadcast::RecvError;
use fluvio_future::zero_copy::ZeroCopyWrite;
use fluvio_future::task::spawn;
use fluvio_socket::InnerFlvSink;
use fluvio_socket::InnerExclusiveFlvSink;
use fluvio_socket::FlvSocketError;
use dataplane::api::{RequestMessage, RequestHeader};
use dataplane::{Offset, Isolation, ReplicaKey};
use dataplane::fetch::FilePartitionResponse;
use fluvio_spu_schema::server::stream_fetch::FileStreamFetchRequest;
use fluvio_spu_schema::server::stream_fetch::StreamFetchResponse;
use crate::core::DefaultSharedGlobalContext;
pub struct StreamFetchHandler<S> {
ctx: DefaultSharedGlobalContext,
replica: ReplicaKey,
isolation: Isolation,
max_bytes: u32,
header: RequestHeader,
kf_sink: InnerExclusiveFlvSink<S>,
end_event: Arc<Event>,
}
impl<S> StreamFetchHandler<S>
where
S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
InnerFlvSink<S>: ZeroCopyWrite,
{
pub fn handle_stream_fetch(
request: RequestMessage<FileStreamFetchRequest>,
ctx: DefaultSharedGlobalContext,
kf_sink: InnerExclusiveFlvSink<S>,
end_event: Arc<Event>,
) {
let (header, msg) = request.get_header_request();
let current_offset = msg.fetch_offset;
let isolation = msg.isolation;
let replica = ReplicaKey::new(msg.topic, msg.partition);
let max_bytes = msg.max_bytes as u32;
debug!(
"conn: {}, start continuous fetch replica: {} offset: {}, max_bytes: {}",
kf_sink.id(),
replica,
current_offset,
max_bytes
);
let handler = Self {
ctx,
isolation,
replica,
header,
max_bytes,
kf_sink,
end_event,
};
spawn(async move { handler.process(current_offset).await });
}
async fn process(mut self, starting_offset: Offset) -> Result<(), FlvSocketError> {
let mut current_offset =
if let Some(offset) = self.send_back_records(starting_offset).await? {
offset
} else {
debug!(
"conn: {}, no records, finishing processing",
self.kf_sink.id()
);
return Ok(());
};
let mut receiver = self.ctx.offset_channel().receiver();
let mut counter: i32 = 0;
loop {
counter += 1;
debug!(
"conn: {}, waiting event, counter: {}",
self.kf_sink.id(),
counter
);
select! {
_ = self.end_event.listen() => {
debug!("stream fetch: {}, connection has been terminated, terminating",self.kf_sink.id());
break;
},
offset_event_res = receiver.recv() => {
match offset_event_res {
Ok(offset_event) => {
debug!("conn: {}, received offset event connection: {:#?}", self.kf_sink.id(),offset_event);
if offset_event.replica_id == self.replica {
let update_offset = match self.isolation {
Isolation::ReadCommitted => offset_event.hw,
Isolation::ReadUncommitted => offset_event.leo
};
debug!("conn: {}, update offset: {}",self.kf_sink.id(),update_offset);
if update_offset != current_offset {
debug!("conn: {}, updated offset replica: {} offset: {} diff from prev: {}",self.kf_sink.id(), self.replica,update_offset,current_offset);
if let Some(offset) = self.send_back_records(current_offset).await? {
debug!("conn: {}, replica: {} read offset: {}",self.kf_sink.id(), self.replica,offset);
current_offset = offset;
} else {
debug!("conn: {}, no more replica: {} records can be read", self.kf_sink.id(),self.replica);
break;
}
} else {
debug!("conn: {}, no changed in offset: {} offset: {} ignoring",self.kf_sink.id(), self.replica,update_offset);
}
} else {
debug!("conn: {}, ignoring event because replica does not match",self.kf_sink.id());
}
},
Err(err) => {
match err {
RecvError::Closed => {
warn!("conn: {}, lost connection to leader controller",self.kf_sink.id());
},
RecvError::Lagged(lag) => {
error!("conn: {}, lagging: {}",self.kf_sink.id(),lag);
}
}
}
}
},
}
}
debug!(
"conn: {}, done with stream fetch loop exiting",
self.kf_sink.id()
);
Ok(())
}
async fn send_back_records(
&mut self,
offset: Offset,
) -> Result<Option<Offset>, FlvSocketError> {
let mut partition_response = FilePartitionResponse::default();
partition_response.partition_index = self.replica.partition;
if let Some((hw, leo)) = self
.ctx
.leaders_state()
.read_records(
&self.replica,
offset,
self.max_bytes,
self.isolation.clone(),
&mut partition_response,
)
.await
{
debug!(
"conn: {}, retrieved slice len: {} replica: {}, from: {} to hw: {}, leo: {}",
partition_response.records.len(),
self.kf_sink.id(),
self.replica,
offset,
hw,
leo,
);
let response = StreamFetchResponse {
topic: self.replica.topic.clone(),
partition: partition_response,
};
let response = RequestMessage::<FileStreamFetchRequest>::response_with_header(
&self.header,
response,
);
trace!(
"conn: {}, sending back file fetch response: {:#?}",
self.kf_sink.id(),
response
);
let mut inner_sink = self.kf_sink.lock().await;
inner_sink
.encode_file_slices(&response, self.header.api_version())
.await?;
trace!("conn: {}, finish sending fetch response", self.kf_sink.id());
let next_offset = match self.isolation {
Isolation::ReadCommitted => hw,
Isolation::ReadUncommitted => leo,
};
Ok(Some(next_offset))
} else {
debug!(
"conn: {} unable to retrieve records from replica: {}, from: {}",
self.kf_sink.id(),
self.replica,
offset
);
Ok(None)
}
}
}