(&self, state: &mut EngineState)
| 183 | type Error = EngineTaskErrors; |
| 184 | |
| 185 | async fn execute(&self, state: &mut EngineState) -> Result<(), Self::Error> { |
| 186 | // Wall-clock duration of the entire retry loop (not per attempt), so the |
| 187 | // difference against `engine_method_request_duration{method}` isolates pure |
| 188 | // CL retry/yield overhead. Records on drop regardless of outcome |
| 189 | // (success / critical / reset / flush). |
| 190 | let label = self.task_metrics_label(); |
| 191 | let _task_timer = base_metrics::timed!(Metrics::engine_task_duration(label)); |
| 192 | |
| 193 | // Retry the task until it succeeds or a critical error occurs. |
| 194 | while let Err(e) = self.execute_inner(state).await { |
| 195 | let severity = e.severity(); |
| 196 | |
| 197 | Metrics::engine_task_failure(self.task_metrics_label(), severity.as_label()) |
| 198 | .increment(1); |
| 199 | |
| 200 | match severity { |
| 201 | EngineTaskErrorSeverity::Temporary => { |
| 202 | trace!(target: "engine", error = %e, "Temporary engine error"); |
| 203 | |
| 204 | // Yield the task to allow other tasks to execute to avoid starvation. |
| 205 | yield_now().await; |
| 206 | |
| 207 | continue; |
| 208 | } |
| 209 | EngineTaskErrorSeverity::Critical => { |
| 210 | error!(target: "engine", error = %e, "Critical engine error"); |
| 211 | return Err(e); |
| 212 | } |
| 213 | EngineTaskErrorSeverity::Reset => { |
| 214 | warn!(target: "engine", "Engine requested derivation reset"); |
| 215 | return Err(e); |
| 216 | } |
| 217 | EngineTaskErrorSeverity::Flush => { |
| 218 | warn!(target: "engine", "Engine requested derivation flush"); |
| 219 | return Err(e); |
| 220 | } |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | Metrics::engine_task_count(self.task_metrics_label()).increment(1); |
| 225 | |
| 226 | Ok(()) |
| 227 | } |
| 228 | } |
no test coverage detected