add status request counters
Authored by
mfwolffe <wolffemf@dukes.jmu.edu>
- SHA
8060762e00ac3772f5daca7c903fdc48e53f8871- Parents
-
c0fb997 - Tree
736ca2f
8060762
8060762e00ac3772f5daca7c903fdc48e53f8871c0fb997
736ca2f| Status | File | + | - |
|---|---|---|---|
| M |
README.md
|
1 | 1 |
| M |
garwarp-ipc/src/lib.rs
|
34 | 3 |
| M |
garwarp/src/daemon.rs
|
4 | 0 |
| M |
garwarp/src/request.rs
|
32 | 0 |
| M |
garwarpctl/src/main.rs
|
2 | 0 |
README.mdmodified@@ -18,7 +18,7 @@ Current scaffold includes: | ||
| 18 | 18 | |
| 19 | 19 | ## Local Commands |
| 20 | 20 | 1. Start daemon: `cargo run -p garwarp -- daemon` |
| 21 | -2. Check health: `cargo run -p garwarpctl -- status` | |
| 21 | +2. Check health and request counters: `cargo run -p garwarpctl -- status` | |
| 22 | 22 | 3. Stop daemon: `cargo run -p garwarpctl -- stop` |
| 23 | 23 | 4. Verify D-Bus activation: `./scripts/test-dbus-activation.sh` |
| 24 | 24 | 5. Create mock request: `cargo run -p garwarpctl -- begin req-1 :1.2 - x11:0x2a` |
garwarp-ipc/src/lib.rsmodified@@ -198,6 +198,8 @@ pub struct StatusResponse { | ||
| 198 | 198 | pub protocol_version: u16, |
| 199 | 199 | pub health: HealthStatus, |
| 200 | 200 | pub in_flight_requests: usize, |
| 201 | + pub total_requests: usize, | |
| 202 | + pub terminal_requests: usize, | |
| 201 | 203 | } |
| 202 | 204 | |
| 203 | 205 | impl StatusResponse { |
@@ -207,6 +209,8 @@ impl StatusResponse { | ||
| 207 | 209 | protocol_version: PROTOCOL_VERSION, |
| 208 | 210 | health: HealthStatus::Healthy, |
| 209 | 211 | in_flight_requests: 0, |
| 212 | + total_requests: 0, | |
| 213 | + terminal_requests: 0, | |
| 210 | 214 | } |
| 211 | 215 | } |
| 212 | 216 | } |
@@ -240,10 +244,12 @@ impl ControlResponse { | ||
| 240 | 244 | pub fn to_line(&self) -> String { |
| 241 | 245 | match self { |
| 242 | 246 | Self::Status(status) => format!( |
| 243 | - "status protocol={} health={} in_flight={}\n", | |
| 247 | + "status protocol={} health={} in_flight={} total={} terminal={}\n", | |
| 244 | 248 | status.protocol_version, |
| 245 | 249 | status.health.as_str(), |
| 246 | - status.in_flight_requests | |
| 250 | + status.in_flight_requests, | |
| 251 | + status.total_requests, | |
| 252 | + status.terminal_requests | |
| 247 | 253 | ), |
| 248 | 254 | Self::AckStopping => "ack stopping\n".to_string(), |
| 249 | 255 | Self::RequestList { ids } => { |
@@ -284,6 +290,8 @@ impl ControlResponse { | ||
| 284 | 290 | let mut protocol_version = None; |
| 285 | 291 | let mut health = None; |
| 286 | 292 | let mut in_flight_requests = None; |
| 293 | + let mut total_requests = None; | |
| 294 | + let mut terminal_requests = None; | |
| 287 | 295 | |
| 288 | 296 | for part in parts { |
| 289 | 297 | let (key, value) = part |
@@ -310,6 +318,20 @@ impl ControlResponse { | ||
| 310 | 318 | .map_err(|_| ParseError::InvalidField(part.to_string()))?, |
| 311 | 319 | ); |
| 312 | 320 | } |
| 321 | + "total" => { | |
| 322 | + total_requests = Some( | |
| 323 | + value | |
| 324 | + .parse::<usize>() | |
| 325 | + .map_err(|_| ParseError::InvalidField(part.to_string()))?, | |
| 326 | + ); | |
| 327 | + } | |
| 328 | + "terminal" => { | |
| 329 | + terminal_requests = Some( | |
| 330 | + value | |
| 331 | + .parse::<usize>() | |
| 332 | + .map_err(|_| ParseError::InvalidField(part.to_string()))?, | |
| 333 | + ); | |
| 334 | + } | |
| 313 | 335 | _ => return Err(ParseError::InvalidField(part.to_string())), |
| 314 | 336 | } |
| 315 | 337 | } |
@@ -320,6 +342,9 @@ impl ControlResponse { | ||
| 320 | 342 | health: health.ok_or(ParseError::MissingField("health"))?, |
| 321 | 343 | in_flight_requests: in_flight_requests |
| 322 | 344 | .ok_or(ParseError::MissingField("in_flight"))?, |
| 345 | + total_requests: total_requests.ok_or(ParseError::MissingField("total"))?, | |
| 346 | + terminal_requests: terminal_requests | |
| 347 | + .ok_or(ParseError::MissingField("terminal"))?, | |
| 323 | 348 | }; |
| 324 | 349 | Ok(Self::Status(status)) |
| 325 | 350 | } |
@@ -527,6 +552,8 @@ mod tests { | ||
| 527 | 552 | protocol_version: PROTOCOL_VERSION, |
| 528 | 553 | health: HealthStatus::Healthy, |
| 529 | 554 | in_flight_requests: 7, |
| 555 | + total_requests: 10, | |
| 556 | + terminal_requests: 3, | |
| 530 | 557 | }); |
| 531 | 558 | let line = response.to_line(); |
| 532 | 559 | let parsed = ControlResponse::parse_line(&line).expect("response should parse"); |
@@ -569,11 +596,15 @@ mod tests { | ||
| 569 | 596 | assert_eq!(response.protocol_version, PROTOCOL_VERSION); |
| 570 | 597 | assert_eq!(response.health, HealthStatus::Healthy); |
| 571 | 598 | assert_eq!(response.in_flight_requests, 0); |
| 599 | + assert_eq!(response.total_requests, 0); | |
| 600 | + assert_eq!(response.terminal_requests, 0); | |
| 572 | 601 | } |
| 573 | 602 | |
| 574 | 603 | #[test] |
| 575 | 604 | fn malformed_status_is_rejected() { |
| 576 | - let parsed = ControlResponse::parse_line("status protocol=one health=healthy in_flight=0"); | |
| 605 | + let parsed = ControlResponse::parse_line( | |
| 606 | + "status protocol=one health=healthy in_flight=0 total=0 terminal=0", | |
| 607 | + ); | |
| 577 | 608 | assert!(parsed.is_err()); |
| 578 | 609 | } |
| 579 | 610 | |
garwarp/src/daemon.rsmodified@@ -117,6 +117,8 @@ fn handle_connection(stream: UnixStream, state: &mut DaemonState) -> io::Result< | ||
| 117 | 117 | protocol_version: garwarp_ipc::PROTOCOL_VERSION, |
| 118 | 118 | health: state.health, |
| 119 | 119 | in_flight_requests: state.requests.in_flight_count(), |
| 120 | + total_requests: state.requests.total_count(), | |
| 121 | + terminal_requests: state.requests.terminal_count(), | |
| 120 | 122 | }), |
| 121 | 123 | Some(ControlRequest::Stop) => { |
| 122 | 124 | state.health = HealthStatus::Stopping; |
@@ -388,6 +390,8 @@ mod tests { | ||
| 388 | 390 | ControlResponse::Status(status) => { |
| 389 | 391 | assert_eq!(status.health, HealthStatus::Healthy); |
| 390 | 392 | assert_eq!(status.in_flight_requests, 1); |
| 393 | + assert_eq!(status.total_requests, 1); | |
| 394 | + assert_eq!(status.terminal_requests, 0); | |
| 391 | 395 | } |
| 392 | 396 | _ => panic!("expected status response"), |
| 393 | 397 | } |
garwarp/src/request.rsmodified@@ -264,6 +264,19 @@ impl RequestRegistry { | ||
| 264 | 264 | .count() |
| 265 | 265 | } |
| 266 | 266 | |
| 267 | + #[must_use] | |
| 268 | + pub fn total_count(&self) -> usize { | |
| 269 | + self.entries.len() | |
| 270 | + } | |
| 271 | + | |
| 272 | + #[must_use] | |
| 273 | + pub fn terminal_count(&self) -> usize { | |
| 274 | + self.entries | |
| 275 | + .values() | |
| 276 | + .filter(|entry| entry.state.is_terminal()) | |
| 277 | + .count() | |
| 278 | + } | |
| 279 | + | |
| 267 | 280 | #[must_use] |
| 268 | 281 | pub fn state(&self, id: &str) -> Option<RequestState> { |
| 269 | 282 | self.entries.get(id).map(|entry| entry.state) |
@@ -562,6 +575,25 @@ mod tests { | ||
| 562 | 575 | assert_eq!(registry.state("req-1"), Some(RequestState::Pending)); |
| 563 | 576 | } |
| 564 | 577 | |
| 578 | + #[test] | |
| 579 | + fn count_helpers_return_expected_values() { | |
| 580 | + let now = Instant::now(); | |
| 581 | + let mut registry = RequestRegistry::new(Duration::from_secs(5)); | |
| 582 | + registry | |
| 583 | + .begin_at("req-1", owner(":1.2"), None, now) | |
| 584 | + .expect("request should be created"); | |
| 585 | + registry | |
| 586 | + .begin_at("req-2", owner(":1.3"), None, now) | |
| 587 | + .expect("request should be created"); | |
| 588 | + registry | |
| 589 | + .transition_at("req-2", &owner(":1.3"), RequestState::Cancelled, now) | |
| 590 | + .expect("request should transition"); | |
| 591 | + | |
| 592 | + assert_eq!(registry.total_count(), 2); | |
| 593 | + assert_eq!(registry.in_flight_count(), 1); | |
| 594 | + assert_eq!(registry.terminal_count(), 1); | |
| 595 | + } | |
| 596 | + | |
| 565 | 597 | #[test] |
| 566 | 598 | fn duplicate_cancel_is_idempotent() { |
| 567 | 599 | let now = Instant::now(); |
garwarpctl/src/main.rsmodified@@ -173,6 +173,8 @@ fn run(command: Command) -> io::Result<()> { | ||
| 173 | 173 | println!("protocol={}", status.protocol_version); |
| 174 | 174 | println!("health={}", status.health.as_str()); |
| 175 | 175 | println!("in_flight={}", status.in_flight_requests); |
| 176 | + println!("total={}", status.total_requests); | |
| 177 | + println!("terminal={}", status.terminal_requests); | |
| 176 | 178 | Ok(()) |
| 177 | 179 | } |
| 178 | 180 | ControlResponse::Error { code, reason } => Err(io::Error::other(format!( |