gardesk/garwarp / 8060762

Browse files

add status request counters

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
8060762e00ac3772f5daca7c903fdc48e53f8871
Parents
c0fb997
Tree
736ca2f

5 changed files

StatusFile+-
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
 ## Local Commands
19
 ## Local Commands
20
 1. Start daemon: `cargo run -p garwarp -- daemon`
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
 3. Stop daemon: `cargo run -p garwarpctl -- stop`
22
 3. Stop daemon: `cargo run -p garwarpctl -- stop`
23
 4. Verify D-Bus activation: `./scripts/test-dbus-activation.sh`
23
 4. Verify D-Bus activation: `./scripts/test-dbus-activation.sh`
24
 5. Create mock request: `cargo run -p garwarpctl -- begin req-1 :1.2 - x11:0x2a`
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
     pub protocol_version: u16,
198
     pub protocol_version: u16,
199
     pub health: HealthStatus,
199
     pub health: HealthStatus,
200
     pub in_flight_requests: usize,
200
     pub in_flight_requests: usize,
201
+    pub total_requests: usize,
202
+    pub terminal_requests: usize,
201
 }
203
 }
202
 
204
 
203
 impl StatusResponse {
205
 impl StatusResponse {
@@ -207,6 +209,8 @@ impl StatusResponse {
207
             protocol_version: PROTOCOL_VERSION,
209
             protocol_version: PROTOCOL_VERSION,
208
             health: HealthStatus::Healthy,
210
             health: HealthStatus::Healthy,
209
             in_flight_requests: 0,
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
     pub fn to_line(&self) -> String {
244
     pub fn to_line(&self) -> String {
241
         match self {
245
         match self {
242
             Self::Status(status) => format!(
246
             Self::Status(status) => format!(
243
-                "status protocol={} health={} in_flight={}\n",
247
+                "status protocol={} health={} in_flight={} total={} terminal={}\n",
244
                 status.protocol_version,
248
                 status.protocol_version,
245
                 status.health.as_str(),
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
             Self::AckStopping => "ack stopping\n".to_string(),
254
             Self::AckStopping => "ack stopping\n".to_string(),
249
             Self::RequestList { ids } => {
255
             Self::RequestList { ids } => {
@@ -284,6 +290,8 @@ impl ControlResponse {
284
                 let mut protocol_version = None;
290
                 let mut protocol_version = None;
285
                 let mut health = None;
291
                 let mut health = None;
286
                 let mut in_flight_requests = None;
292
                 let mut in_flight_requests = None;
293
+                let mut total_requests = None;
294
+                let mut terminal_requests = None;
287
 
295
 
288
                 for part in parts {
296
                 for part in parts {
289
                     let (key, value) = part
297
                     let (key, value) = part
@@ -310,6 +318,20 @@ impl ControlResponse {
310
                                     .map_err(|_| ParseError::InvalidField(part.to_string()))?,
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
                         _ => return Err(ParseError::InvalidField(part.to_string())),
335
                         _ => return Err(ParseError::InvalidField(part.to_string())),
314
                     }
336
                     }
315
                 }
337
                 }
@@ -320,6 +342,9 @@ impl ControlResponse {
320
                     health: health.ok_or(ParseError::MissingField("health"))?,
342
                     health: health.ok_or(ParseError::MissingField("health"))?,
321
                     in_flight_requests: in_flight_requests
343
                     in_flight_requests: in_flight_requests
322
                         .ok_or(ParseError::MissingField("in_flight"))?,
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
                 Ok(Self::Status(status))
349
                 Ok(Self::Status(status))
325
             }
350
             }
@@ -527,6 +552,8 @@ mod tests {
527
             protocol_version: PROTOCOL_VERSION,
552
             protocol_version: PROTOCOL_VERSION,
528
             health: HealthStatus::Healthy,
553
             health: HealthStatus::Healthy,
529
             in_flight_requests: 7,
554
             in_flight_requests: 7,
555
+            total_requests: 10,
556
+            terminal_requests: 3,
530
         });
557
         });
531
         let line = response.to_line();
558
         let line = response.to_line();
532
         let parsed = ControlResponse::parse_line(&line).expect("response should parse");
559
         let parsed = ControlResponse::parse_line(&line).expect("response should parse");
@@ -569,11 +596,15 @@ mod tests {
569
         assert_eq!(response.protocol_version, PROTOCOL_VERSION);
596
         assert_eq!(response.protocol_version, PROTOCOL_VERSION);
570
         assert_eq!(response.health, HealthStatus::Healthy);
597
         assert_eq!(response.health, HealthStatus::Healthy);
571
         assert_eq!(response.in_flight_requests, 0);
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
     #[test]
603
     #[test]
575
     fn malformed_status_is_rejected() {
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
         assert!(parsed.is_err());
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
             protocol_version: garwarp_ipc::PROTOCOL_VERSION,
117
             protocol_version: garwarp_ipc::PROTOCOL_VERSION,
118
             health: state.health,
118
             health: state.health,
119
             in_flight_requests: state.requests.in_flight_count(),
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
         Some(ControlRequest::Stop) => {
123
         Some(ControlRequest::Stop) => {
122
             state.health = HealthStatus::Stopping;
124
             state.health = HealthStatus::Stopping;
@@ -388,6 +390,8 @@ mod tests {
388
             ControlResponse::Status(status) => {
390
             ControlResponse::Status(status) => {
389
                 assert_eq!(status.health, HealthStatus::Healthy);
391
                 assert_eq!(status.health, HealthStatus::Healthy);
390
                 assert_eq!(status.in_flight_requests, 1);
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
             _ => panic!("expected status response"),
396
             _ => panic!("expected status response"),
393
         }
397
         }
garwarp/src/request.rsmodified
@@ -264,6 +264,19 @@ impl RequestRegistry {
264
             .count()
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
     #[must_use]
280
     #[must_use]
268
     pub fn state(&self, id: &str) -> Option<RequestState> {
281
     pub fn state(&self, id: &str) -> Option<RequestState> {
269
         self.entries.get(id).map(|entry| entry.state)
282
         self.entries.get(id).map(|entry| entry.state)
@@ -562,6 +575,25 @@ mod tests {
562
         assert_eq!(registry.state("req-1"), Some(RequestState::Pending));
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
     #[test]
597
     #[test]
566
     fn duplicate_cancel_is_idempotent() {
598
     fn duplicate_cancel_is_idempotent() {
567
         let now = Instant::now();
599
         let now = Instant::now();
garwarpctl/src/main.rsmodified
@@ -173,6 +173,8 @@ fn run(command: Command) -> io::Result<()> {
173
                     println!("protocol={}", status.protocol_version);
173
                     println!("protocol={}", status.protocol_version);
174
                     println!("health={}", status.health.as_str());
174
                     println!("health={}", status.health.as_str());
175
                     println!("in_flight={}", status.in_flight_requests);
175
                     println!("in_flight={}", status.in_flight_requests);
176
+                    println!("total={}", status.total_requests);
177
+                    println!("terminal={}", status.terminal_requests);
176
                     Ok(())
178
                     Ok(())
177
                 }
179
                 }
178
                 ControlResponse::Error { code, reason } => Err(io::Error::other(format!(
180
                 ControlResponse::Error { code, reason } => Err(io::Error::other(format!(