From ca2eb6a4dc4be4c8809fd724cae6a334c4d46344 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Sun, 23 Jun 2024 14:48:45 +0200 Subject: [PATCH] avoid buffering the shell in memory on the server until the first component has rendered See https://github.com/lovasoa/SQLpage/issues/435 --- CHANGELOG.md | 1 + src/webserver/http.rs | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed4b3afe..b6cca987 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ - Add a `wrap` attribute to the `list` component to wrap items on multiple lines when they are too long. - New `max_pending_rows` [configuration option](https://sql.ophir.dev/configuration.md) to limit the number of messages that can be sent to the client before they are read. Usefule when sending large amounts of data to slow clients. - Update sqlite to v3.46: https://www.sqlite.org/releaselog/3_46_0.html + - Faster initial page load. SQLPage used to wait for the first component to be rendered before sending the shell to the client. We now send the shell immediately, and the first component as soon as it is ready. This can make the initial page load faster, especially when the first component requires a long computation on the database side. ## 0.23.0 (2024-06-09) diff --git a/src/webserver/http.rs b/src/webserver/http.rs index eb3cc017..24960b8e 100644 --- a/src/webserver/http.rs +++ b/src/webserver/http.rs @@ -70,6 +70,10 @@ impl ResponseWriter { if self.buffer.is_empty() { return Ok(()); } + log::trace!( + "Async flushing data to client: {}", + String::from_utf8_lossy(&self.buffer) + ); self.response_bytes .send(Ok(mem::take(&mut self.buffer).into())) .await @@ -91,6 +95,10 @@ impl Write for ResponseWriter { Ok(buf.len()) } fn flush(&mut self) -> std::io::Result<()> { + log::trace!( + "Flushing data to client: {}", + String::from_utf8_lossy(&self.buffer) + ); self.response_bytes .try_send(Ok(mem::take(&mut self.buffer).into())) .map_err(|e| @@ -115,6 +123,12 @@ async fn stream_response( mut renderer: RenderContext, ) { let mut stream = Box::pin(stream); + + if let Err(e) = &renderer.writer.async_flush().await { + log::error!("Unable to flush initial data to client: {e}"); + return; + } + while let Some(item) = stream.next().await { log::trace!("Received item from database: {item:?}"); let render_result = match item {