use std::cmp;
use std::sync::Arc;
use anyhow::Result;
use tokio::sync::mpsc;
use crate::config::StreamConfig;
use crate::types::QueryResponse;
use crate::Client;
use hypersync_solana_net_types::query::SolanaQuery;
pub fn stream_arrow(
client: Arc<Client>,
query: SolanaQuery,
config: StreamConfig,
) -> mpsc::Receiver<Result<QueryResponse>> {
let (tx, rx) = mpsc::channel(config.concurrency * 2);
tokio::task::spawn(async move {
let from = query.from_slot;
let to = query.to_slot.unwrap_or(u64::MAX);
let mut current = from;
let mut batch_size = config.batch_size;
while current < to {
let mut futs = Vec::with_capacity(config.concurrency);
let mut chunk_end = current;
for _ in 0..config.concurrency {
if chunk_end >= to {
break;
}
let end = cmp::min(to, chunk_end + batch_size);
let mut q = query.clone();
q.from_slot = chunk_end;
q.to_slot = Some(end);
let client = client.clone();
futs.push(tokio::spawn(async move { client.get_arrow(&q).await }));
chunk_end = end;
}
if futs.is_empty() {
break;
}
for fut in futs {
let result = match fut.await {
Ok(r) => r,
Err(e) => Err(anyhow::anyhow!("task join error: {}", e)),
};
match &result {
Ok(resp) => {
let bytes = resp.response_bytes as u64;
if bytes > config.response_bytes_ceiling
&& batch_size > config.min_batch_size
{
batch_size = cmp::max(config.min_batch_size, batch_size / 2);
tracing::debug!(
batch_size,
response_bytes = bytes,
"Shrunk batch size (response too large)"
);
} else if bytes < config.response_bytes_floor
&& batch_size < config.max_batch_size
{
batch_size = cmp::min(config.max_batch_size, batch_size * 2);
tracing::debug!(
batch_size,
response_bytes = bytes,
"Grew batch size (response too small)"
);
}
current = resp.next_slot;
}
Err(_) => {
}
}
let is_err = result.is_err();
if tx.send(result).await.is_err() {
tracing::warn!("Stream receiver dropped");
return;
}
if is_err {
return;
}
}
}
});
rx
}