My personal site cherry.computer
htmx tailwind axum askama

fix: timeout last.fm API call if trying to return page

The last.fm API can take upwards of 5 seconds to respond and we only
call it on-demand, and the website is not getting called every 30
seconds so the cache will reliably get cold and cause it to take
responses 5 seconds to be returned too.

cherry.computer e5c93f20 25047366

verified
+31 -12
+19 -7
server/src/index.rs
··· 1 1 use crate::{scrobble::ScrobblesTemplate, scrobble_monitor::ScrobbleMonitor}; 2 2 3 + use std::time::Duration; 4 + 3 5 use askama::Template; 4 6 5 7 #[derive(Template, Debug, Clone)] 6 8 #[template(path = "index.html")] 7 9 pub struct RootTemplate { 8 - scrobble: Option<ScrobblesTemplate> 10 + scrobble: Option<ScrobblesTemplate>, 9 11 } 10 12 11 - pub async fn get_index(monitor: &mut ScrobbleMonitor) -> RootTemplate { 12 - let scrobbles_template = monitor.get_scrobble().await; 13 - if let Err(err) = scrobbles_template.as_ref() { 14 - tracing::warn!(?err, "Failed to get scrobble"); 15 - } 13 + pub async fn get_index(mut monitor: ScrobbleMonitor) -> RootTemplate { 14 + let scrobbles_template = tokio::time::timeout( 15 + Duration::from_millis(200), 16 + tokio::spawn(async move { monitor.get_scrobble().await }), 17 + ) 18 + .await; 16 19 17 20 RootTemplate { 18 - scrobble: scrobbles_template.ok() 21 + scrobble: scrobbles_template 22 + .map_err(|_| tracing::debug!("last.fm request took too long")) 23 + .and_then(|scrobble| { 24 + scrobble 25 + .map_err(|err| tracing::error!(?err, "Panicked when trying to get scrobble")) 26 + }) 27 + .and_then(|scrobble| { 28 + scrobble.map_err(|err| tracing::warn!(?err, "Failed to get scrobble")) 29 + }) 30 + .ok(), 19 31 } 20 32 }
+2 -4
server/src/main.rs
··· 49 49 Ok(()) 50 50 } 51 51 52 - async fn render_index_handler( 53 - Extension(mut monitor): Extension<ScrobbleMonitor>, 54 - ) -> impl IntoResponse { 55 - let template = get_index(&mut monitor).await; 52 + async fn render_index_handler(Extension(monitor): Extension<ScrobbleMonitor>) -> impl IntoResponse { 53 + let template = get_index(monitor).await; 56 54 template.render().map(Html).map_err(|err| { 57 55 tracing::error!("failed to render index: {err:?}"); 58 56 StatusCode::INTERNAL_SERVER_ERROR
+10 -1
server/templates/index.html
··· 86 86 /> 87 87 </div> 88 88 </div> 89 - <div class="scrobble-bar" hx-get="/scrobbles" hx-trigger="every 30s"> 89 + <div 90 + class="scrobble-bar" 91 + hx-get="/scrobbles" 92 + hx-trigger= 93 + {%- if let Some(_) = scrobble -%} 94 + "every 30s" 95 + {%- else -%} 96 + "load, every 30s" 97 + {%- endif -%} 98 + > 90 99 {% match scrobble %} {% when Some with (ScrobblesTemplate {intro, now_playing, image, srcset}) %} 91 100 {% include "scrobble.html" %} 92 101 {% else %}