Server tools to backfill, tail, mirror, and verify PLC logs
49
fork

Configure Feed

Select the types of activity you want to include in your feed.

cache upstream status too

and add some log traces

phil 94b80a2d 6b2fc8cd

+49 -17
+16 -1
src/cached_value.rs
··· 16 16 impl<T: Clone> ExpiringValue<T> { 17 17 fn get(&self, now: Instant) -> Option<T> { 18 18 if now <= self.expires { 19 + log::trace!("returning val (fresh for {:?})", self.expires - now); 19 20 Some(self.value.clone()) 20 21 } else { 22 + log::trace!("hiding expired val"); 21 23 None 22 24 } 23 25 } ··· 48 50 if let Some(v) = val.as_ref().and_then(|v| v.get(now)) { 49 51 return Ok(v); 50 52 } 51 - let new = self.fetcher.fetch().await?; 53 + log::debug!( 54 + "value {}, fetching...", 55 + if val.is_some() { 56 + "expired" 57 + } else { 58 + "not present" 59 + } 60 + ); 61 + let new = self 62 + .fetcher 63 + .fetch() 64 + .await 65 + .inspect_err(|e| log::warn!("value fetch failed, next access will retry: {e}"))?; 66 + log::debug!("fetched ok, saving a copy for cache."); 52 67 *val = Some(ExpiringValue { 53 68 value: new.clone(), 54 69 expires: now + self.validitiy,
+33 -16
src/mirror.rs
··· 17 17 client: Client, 18 18 plc: Url, 19 19 upstream: Url, 20 - latest_at: CachedValue<Dt, LatestAt>, 21 - } 22 - 23 - #[derive(Clone)] 24 - struct LatestAt(Db); 25 - impl Fetcher<Dt> for LatestAt { 26 - async fn fetch(&self) -> Result<Dt, Box<dyn std::error::Error>> { 27 - let now = self.0.get_latest().await?.ok_or(anyhow::anyhow!( 28 - "expected to find at least one thing in the db" 29 - ))?; 30 - Ok(now) 31 - } 20 + latest_at: CachedValue<Dt, GetLatestAt>, 21 + upstream_status: CachedValue<PlcStatus, CheckUpstream>, 32 22 } 33 23 34 24 #[handler] ··· 89 79 ) 90 80 } 91 81 92 - async fn plc_status(url: &Url, client: &Client) -> (bool, serde_json::Value) { 82 + type PlcStatus = (bool, serde_json::Value); 83 + 84 + async fn plc_status(url: &Url, client: &Client) -> PlcStatus { 93 85 use serde_json::json; 94 86 95 87 let mut url = url.clone(); ··· 125 117 } 126 118 } 127 119 120 + #[derive(Clone)] 121 + struct GetLatestAt(Db); 122 + impl Fetcher<Dt> for GetLatestAt { 123 + async fn fetch(&self) -> Result<Dt, Box<dyn std::error::Error>> { 124 + let now = self.0.get_latest().await?.ok_or(anyhow::anyhow!( 125 + "expected to find at least one thing in the db" 126 + ))?; 127 + Ok(now) 128 + } 129 + } 130 + 131 + #[derive(Clone)] 132 + struct CheckUpstream(Url, Client); 133 + impl Fetcher<PlcStatus> for CheckUpstream { 134 + async fn fetch(&self) -> Result<PlcStatus, Box<dyn std::error::Error>> { 135 + Ok(plc_status(&self.0, &self.1).await) 136 + } 137 + } 138 + 128 139 #[handler] 129 140 async fn health( 130 141 Data(State { 131 142 plc, 132 143 client, 133 - upstream, 134 144 latest_at, 145 + upstream_status, 146 + .. 135 147 }): Data<&State>, 136 148 ) -> impl IntoResponse { 137 149 let mut overall_status = StatusCode::OK; ··· 139 151 if !ok { 140 152 overall_status = StatusCode::BAD_GATEWAY; 141 153 } 142 - let (ok, upstream_status) = plc_status(upstream, client).await; 154 + let (ok, upstream_status) = upstream_status.get().await.expect("plc_status infallible"); 143 155 if !ok { 144 156 overall_status = StatusCode::BAD_GATEWAY; 145 157 } ··· 233 245 .build() 234 246 .expect("reqwest client to build"); 235 247 236 - let latest_at = CachedValue::new(LatestAt(db), Duration::from_secs(1)); 248 + let latest_at = CachedValue::new(GetLatestAt(db), Duration::from_secs(2)); 249 + let upstream_status = CachedValue::new( 250 + CheckUpstream(upstream.clone(), client.clone()), 251 + Duration::from_secs(6), 252 + ); 237 253 238 254 let state = State { 239 255 client, 240 256 plc, 241 257 upstream: upstream.clone(), 242 258 latest_at, 259 + upstream_status, 243 260 }; 244 261 245 262 let app = Route::new()