fix(sql-file): throttle progress events
This commit is contained in:
1 parent
25e799c34b
commit
2188db3a26
4 files changed
+247
-54
No files matched your search
@@ -8,6 +8,7 @@ use dbx_core::sql;
|
||||
use dbx_core::sql::{SqlFileProgress, SqlFileRequest, SqlFileStatus};
|
||||
use dbx_core::sql_file_import::{
|
||||
execute_sql_file_content, sql_file_error_progress, sql_file_progress as build_sql_file_progress,
|
||||
SqlFileProgressEmitter,
|
||||
};
|
||||
use futures::stream::Stream;
|
||||
use serde::Deserialize;
|
||||
@@ -92,21 +93,32 @@ pub async fn execute_sql_file(
|
||||
|
||||
tokio::spawn(async move {
|
||||
let started_at = std::time::Instant::now();
|
||||
let mut progress_emitter = SqlFileProgressEmitter::new(|progress| {
|
||||
send_sql_file_progress(&tx, progress);
|
||||
});
|
||||
progress_emitter.emit(build_sql_file_progress(
|
||||
&req.execution_id,
|
||||
SqlFileStatus::Started,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
started_at,
|
||||
"",
|
||||
None,
|
||||
));
|
||||
match std::fs::metadata(&file_path) {
|
||||
Ok(meta) if meta.len() > 200 * 1024 * 1024 => {
|
||||
send_sql_file_progress(
|
||||
&tx,
|
||||
sql_file_error_progress(
|
||||
&req.execution_id,
|
||||
started_at,
|
||||
format!("File too large: {} bytes (max {} bytes)", meta.len(), 200 * 1024 * 1024),
|
||||
),
|
||||
);
|
||||
progress_emitter.emit(sql_file_error_progress(
|
||||
&req.execution_id,
|
||||
started_at,
|
||||
format!("File too large: {} bytes (max {} bytes)", meta.len(), 200 * 1024 * 1024),
|
||||
));
|
||||
cleanup_sql_file_execution(&state_clone, &req.execution_id).await;
|
||||
return;
|
||||
}
|
||||
Err(e) => {
|
||||
send_sql_file_progress(&tx, sql_file_error_progress(&req.execution_id, started_at, e.to_string()));
|
||||
progress_emitter.emit(sql_file_error_progress(&req.execution_id, started_at, e.to_string()));
|
||||
cleanup_sql_file_execution(&state_clone, &req.execution_id).await;
|
||||
return;
|
||||
}
|
||||
@@ -119,19 +131,14 @@ pub async fn execute_sql_file(
|
||||
}) {
|
||||
Ok(content) => content,
|
||||
Err(e) => {
|
||||
send_sql_file_progress(&tx, sql_file_error_progress(&req.execution_id, started_at, e.to_string()));
|
||||
progress_emitter.emit(sql_file_error_progress(&req.execution_id, started_at, e.to_string()));
|
||||
cleanup_sql_file_execution(&state_clone, &req.execution_id).await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
send_sql_file_progress(
|
||||
&tx,
|
||||
build_sql_file_progress(&req.execution_id, SqlFileStatus::Started, 0, 0, 0, 0, started_at, "", None),
|
||||
);
|
||||
|
||||
let _ = execute_sql_file_content(&app, &req, &file_content, token, started_at, |progress| {
|
||||
send_sql_file_progress(&tx, progress);
|
||||
progress_emitter.emit(progress);
|
||||
})
|
||||
.await;
|
||||
|
||||
@@ -182,7 +189,7 @@ pub async fn sql_file_progress(
|
||||
let tx = channels.get(&execution_id).ok_or_else(|| AppError("Execution not found".to_string()))?;
|
||||
let rx = tx.subscribe();
|
||||
drop(channels);
|
||||
Ok(crate::sse::sse_from_channel(rx))
|
||||
Ok(crate::sse::sse_from_lossy_channel(rx))
|
||||
}
|
||||
|
||||
pub async fn cancel_sql_file(
|
||||
|
||||
@@ -1,13 +1,33 @@
|
||||
use axum::response::sse::{Event, KeepAlive, Sse};
|
||||
use futures::stream::Stream;
|
||||
use tokio::sync::broadcast;
|
||||
use tokio::sync::broadcast::{self, error::RecvError};
|
||||
|
||||
pub fn sse_from_channel(
|
||||
rx: broadcast::Receiver<String>,
|
||||
) -> Sse<impl Stream<Item = Result<Event, std::convert::Infallible>>> {
|
||||
sse_from_channel_with_lag_policy(rx, false)
|
||||
}
|
||||
|
||||
pub fn sse_from_lossy_channel(
|
||||
rx: broadcast::Receiver<String>,
|
||||
) -> Sse<impl Stream<Item = Result<Event, std::convert::Infallible>>> {
|
||||
sse_from_channel_with_lag_policy(rx, true)
|
||||
}
|
||||
|
||||
fn sse_from_channel_with_lag_policy(
|
||||
mut rx: broadcast::Receiver<String>,
|
||||
recover_from_lag: bool,
|
||||
) -> Sse<impl Stream<Item = Result<Event, std::convert::Infallible>>> {
|
||||
let stream = async_stream::stream! {
|
||||
while let Ok(data) = rx.recv().await {
|
||||
yield Ok(Event::default().data(data));
|
||||
loop {
|
||||
match rx.recv().await {
|
||||
Ok(data) => yield Ok(Event::default().data(data)),
|
||||
// Only cumulative progress streams may skip stale snapshots; token and
|
||||
// data streams retain the previous fail-closed behavior on message loss.
|
||||
Err(RecvError::Lagged(_)) if recover_from_lag => continue,
|
||||
Err(RecvError::Lagged(_)) => break,
|
||||
Err(RecvError::Closed) => break,
|
||||
}
|
||||
}
|
||||
};
|
||||
Sse::new(stream).keep_alive(KeepAlive::default())
|
||||
|
||||
Reference in new issue
Block a user