1use std::{collections::HashMap, sync::Arc};
91
92use serde::{Deserialize, Serialize};
93use tokio::sync::RwLock;
94
95use crate::{AirError, Result, Utility, dev_log};
96
97#[derive(Debug)]
99pub struct HealthCheckManager {
100 ServiceHealth:Arc<RwLock<HashMap<String, ServiceHealth>>>,
102
103 HealthHistory:Arc<RwLock<Vec<HealthCheckRecord>>>,
105
106 RecoveryActions:Arc<RwLock<HashMap<String, RecoveryAction>>>,
108
109 config:HealthCheckConfig,
111}
112
113#[derive(Debug, Clone, Serialize, Deserialize)]
115pub struct ServiceHealth {
116 pub ServiceName:String,
118
119 pub Status:HealthStatus,
121
122 pub LastCheck:u64,
124
125 pub LastSuccess:Option<u64>,
127
128 pub FailureCount:u32,
130
131 pub ErrorMessage:Option<String>,
133
134 pub ResponseTimeMs:Option<u64>,
136
137 pub CheckLevel:HealthCheckLevel,
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
143pub enum HealthStatus {
144 Healthy,
146
147 Degraded,
149
150 Unhealthy,
152
153 Unknown,
155}
156
157#[derive(Debug, Clone, Serialize, Deserialize)]
159pub enum HealthCheckLevel {
160 Alive,
162
163 Responsive,
165
166 Functional,
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize)]
172pub struct HealthCheckRecord {
173 pub Timestamp:u64,
175
176 pub ServiceName:String,
178
179 pub Status:HealthStatus,
181
182 pub ResponseTimeMs:Option<u64>,
184
185 pub ErrorMessage:Option<String>,
187}
188
189#[derive(Debug, Clone, Serialize, Deserialize)]
191pub struct RecoveryAction {
192 pub Name:String,
194
195 pub ServiceName:String,
197
198 pub Trigger:RecoveryTrigger,
200
201 pub Action:RecoveryActionType,
203
204 pub MaxRetries:u32,
206
207 pub RetryCount:u32,
209}
210
211#[derive(Debug, Clone, Serialize, Deserialize)]
213pub enum RecoveryTrigger {
214 ConsecutiveFailures(u32),
216
217 ResponseTimeExceeds(u64),
219
220 ServiceUnresponsive,
222}
223
224#[derive(Debug, Clone, Serialize, Deserialize)]
226pub enum RecoveryActionType {
227 RestartService,
229
230 ResetConnection,
232
233 ClearCache,
235
236 ReloadConfiguration,
238
239 Escalate,
241}
242
243#[derive(Debug, Clone, Serialize, Deserialize)]
245pub struct HealthCheckConfig {
246 pub DefaultCheckInterval:u64,
248
249 pub HistoryRetention:usize,
251
252 pub ConsecutiveFailuresThreshold:u32,
254
255 pub ResponseTimeThresholdMs:u64,
257
258 pub EnableAutoRecovery:bool,
260
261 pub RecoveryTimeoutSec:u64,
263}
264
265impl Default for HealthCheckConfig {
266 fn default() -> Self {
267 Self {
268 DefaultCheckInterval:30,
269
270 HistoryRetention:100,
271
272 ConsecutiveFailuresThreshold:3,
273
274 ResponseTimeThresholdMs:5000,
275
276 EnableAutoRecovery:true,
277
278 RecoveryTimeoutSec:60,
279 }
280 }
281}
282
283impl HealthCheckManager {
284 pub fn new(config:Option<HealthCheckConfig>) -> Self {
286 Self {
287 ServiceHealth:Arc::new(RwLock::new(HashMap::new())),
288
289 HealthHistory:Arc::new(RwLock::new(Vec::new())),
290
291 RecoveryActions:Arc::new(RwLock::new(HashMap::new())),
292
293 config:config.unwrap_or_default(),
294 }
295 }
296
297 pub async fn RegisterService(&self, ServiceName:String, CheckLevel:HealthCheckLevel) -> Result<()> {
299 let mut HealthMap = self.ServiceHealth.write().await;
300
301 HealthMap.insert(
302 ServiceName.clone(),
303 ServiceHealth {
304 ServiceName:ServiceName.clone(),
305 Status:HealthStatus::Unknown,
306 LastCheck:0,
307 LastSuccess:None,
308 FailureCount:0,
309 ErrorMessage:None,
310 ResponseTimeMs:None,
311 CheckLevel:CheckLevel.clone(),
312 },
313 );
314
315 dev_log!(
316 "lifecycle",
317 "[HealthCheck] Registered service for monitoring: {} ({:?})",
318 ServiceName,
319 CheckLevel
320 );
321
322 Ok(())
323 }
324
325 pub async fn CheckService(&self, ServiceName:&str) -> Result<HealthStatus> {
327 let StartTime = Utility::CurrentTimestamp();
328
329 let CheckTimeout = tokio::time::Duration::from_secs(10);
331
332 let (status, ErrorMessage) = tokio::time::timeout(CheckTimeout, async {
333 match ServiceName {
334 "authentication" => self.CheckAuthenticationService().await,
335 "updates" => self.CheckUpdatesService().await,
336 "downloader" => self.CheckDownloaderService().await,
337 "indexing" => self.CheckIndexingService().await,
338 "grpc" => self.CheckgRPCService().await,
339 "connections" => self.CheckConnectionsService().await,
340 _ => {
341 dev_log!("lifecycle", "warn: [HealthCheck] Unknown service: {}", ServiceName);
342
343 return (HealthStatus::Unhealthy, Some(format!("Unknown service: {}", ServiceName)));
344 },
345 }
346 })
347 .await
348 .map_err(|_| {
349 dev_log!("lifecycle", "warn: [HealthCheck] Timeout checking service: {}", ServiceName);
350
351 (
352 HealthStatus::Unhealthy,
353 Some(format!("Health check timeout for service: {}", ServiceName)),
354 )
355 })?;
356
357 let ResponseTime = Utility::CurrentTimestamp() - StartTime;
358
359 self.UpdateServiceHealth(ServiceName, status.clone(), &ErrorMessage, ResponseTime)
361 .await?;
362
363 self.RecordHealthCheck(ServiceName, status.clone(), ResponseTime, &ErrorMessage)
365 .await;
366
367 if self.config.EnableAutoRecovery {
369 self.TriggerRecoveryIfNeeded(ServiceName).await;
370 }
371
372 self.HandleCriticalAlerts(ServiceName, &status).await;
374
375 Ok(status)
376 }
377
378 async fn CheckAuthenticationService(&self) -> (HealthStatus, Option<String>) {
380 dev_log!("lifecycle", "[HealthCheck] Checking authentication service health");
381
382 let start = std::time::Instant::now();
387
388 tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
398
399 let elapsed = start.elapsed();
400
401 if elapsed.as_millis() > 1000 {
403 return (
404 HealthStatus::Degraded,
405 Some(format!(
406 "Authentication service response time too slow: {}ms",
407 elapsed.as_millis()
408 )),
409 );
410 }
411
412 dev_log!("lifecycle", "[HealthCheck] Authentication service healthy");
413
414 (HealthStatus::Healthy, None)
415 }
416
417 async fn CheckUpdatesService(&self) -> (HealthStatus, Option<String>) {
419 dev_log!("lifecycle", "[HealthCheck] Checking updates service health");
420
421 let start = std::time::Instant::now();
422
423 tokio::time::sleep(tokio::time::Duration::from_millis(30)).await;
433
434 let elapsed = start.elapsed();
435
436 if elapsed.as_millis() > 500 {
438 return (
439 HealthStatus::Degraded,
440 Some(format!("Updates service response time too slow: {}ms", elapsed.as_millis())),
441 );
442 }
443
444 dev_log!("lifecycle", "[HealthCheck] Updates service healthy");
445
446 (HealthStatus::Healthy, None)
447 }
448
449 async fn CheckDownloaderService(&self) -> (HealthStatus, Option<String>) {
451 dev_log!("lifecycle", "[HealthCheck] Checking downloader service health");
452
453 let start = std::time::Instant::now();
454
455 tokio::time::sleep(tokio::time::Duration::from_millis(40)).await;
466
467 let elapsed = start.elapsed();
468
469 if elapsed.as_millis() > 1000 {
471 return (
472 HealthStatus::Degraded,
473 Some(format!("Downloader service response time too slow: {}ms", elapsed.as_millis())),
474 );
475 }
476
477 dev_log!("lifecycle", "[HealthCheck] Downloader service healthy");
478
479 (HealthStatus::Healthy, None)
480 }
481
482 async fn CheckIndexingService(&self) -> (HealthStatus, Option<String>) {
484 dev_log!("lifecycle", "[HealthCheck] Checking indexing service health");
485
486 let start = std::time::Instant::now();
487
488 tokio::time::sleep(tokio::time::Duration::from_millis(60)).await;
499
500 let elapsed = start.elapsed();
501
502 if elapsed.as_millis() > 500 {
504 return (
505 HealthStatus::Degraded,
506 Some(format!("Indexing service response time too slow: {}ms", elapsed.as_millis())),
507 );
508 }
509
510 dev_log!("lifecycle", "[HealthCheck] Indexing service healthy");
511
512 (HealthStatus::Healthy, None)
513 }
514
515 async fn CheckgRPCService(&self) -> (HealthStatus, Option<String>) {
517 dev_log!("lifecycle", "[HealthCheck] Checking gRPC service health");
518
519 let start = std::time::Instant::now();
520
521 tokio::time::sleep(tokio::time::Duration::from_millis(20)).await;
532
533 let elapsed = start.elapsed();
534
535 if elapsed.as_millis() > 200 {
537 return (
538 HealthStatus::Degraded,
539 Some(format!("gRPC service response time too slow: {}ms", elapsed.as_millis())),
540 );
541 }
542
543 dev_log!("lifecycle", "[HealthCheck] gRPC service healthy");
544
545 (HealthStatus::Healthy, None)
546 }
547
548 async fn CheckConnectionsService(&self) -> (HealthStatus, Option<String>) {
550 dev_log!("lifecycle", "[HealthCheck] Checking connections service health");
551
552 let start = std::time::Instant::now();
553
554 tokio::time::sleep(tokio::time::Duration::from_millis(35)).await;
565
566 let elapsed = start.elapsed();
567
568 if elapsed.as_millis() > 300 {
570 return (
571 HealthStatus::Degraded,
572 Some(format!("Connections service response time too slow: {}ms", elapsed.as_millis())),
573 );
574 }
575
576 dev_log!("lifecycle", "[HealthCheck] Connections service healthy");
577
578 (HealthStatus::Healthy, None)
579 }
580
581 async fn UpdateServiceHealth(
583 &self,
584
585 ServiceName:&str,
586
587 status:HealthStatus,
588
589 ErrorMessage:&Option<String>,
590
591 ResponseTime:u64,
592 ) -> Result<()> {
593 let mut HealthMap = self.ServiceHealth.write().await;
594
595 if let Some(ServiceHealth) = HealthMap.get_mut(ServiceName) {
596 ServiceHealth.Status = status.clone();
597
598 ServiceHealth.LastCheck = Utility::CurrentTimestamp();
599
600 ServiceHealth.ResponseTimeMs = Some(ResponseTime);
601
602 match status {
603 HealthStatus::Healthy => {
604 ServiceHealth.LastSuccess = Some(Utility::CurrentTimestamp());
605
606 ServiceHealth.FailureCount = 0;
607
608 ServiceHealth.ErrorMessage = None;
609 },
610
611 HealthStatus::Degraded | HealthStatus::Unhealthy => {
612 ServiceHealth.FailureCount += 1;
613
614 ServiceHealth.ErrorMessage = ErrorMessage.clone();
615 },
616
617 HealthStatus::Unknown => {
618
619 },
621 }
622 } else {
623 return Err(AirError::Internal(format!("Service not registered: {}", ServiceName)));
624 }
625
626 dev_log!(
627 "lifecycle",
628 "[HealthCheck] Updated health for {}: {:?} ({}ms)",
629 ServiceName,
630 status,
631 ResponseTime
632 );
633
634 Ok(())
635 }
636
637 async fn RecordHealthCheck(
639 &self,
640
641 ServiceName:&str,
642
643 status:HealthStatus,
644
645 ResponseTime:u64,
646
647 ErrorMessage:&Option<String>,
648 ) {
649 let mut history = self.HealthHistory.write().await;
650
651 let record = HealthCheckRecord {
652 Timestamp:Utility::CurrentTimestamp(),
653
654 ServiceName:ServiceName.to_string(),
655
656 Status:status,
657
658 ResponseTimeMs:Some(ResponseTime),
659
660 ErrorMessage:ErrorMessage.clone(),
661 };
662
663 history.push(record);
664
665 if history.len() > self.config.HistoryRetention {
667 history.remove(0);
668 }
669 }
670
671 async fn TriggerRecoveryIfNeeded(&self, ServiceName:&str) {
673 let HealthMap = self.ServiceHealth.read().await;
674
675 if let Some(ServiceHealth) = HealthMap.get(ServiceName) {
676 if ServiceHealth.FailureCount >= self.config.ConsecutiveFailuresThreshold {
678 dev_log!(
679 "lifecycle",
680 "warn: [HealthCheck] Service {} has {} consecutive failures, triggering recovery",
681 ServiceName,
682 ServiceHealth.FailureCount
683 );
684
685 self.PerformRecoveryAction(ServiceName).await;
686 }
687
688 if let Some(ResponseTime) = ServiceHealth.ResponseTimeMs {
690 if ResponseTime > self.config.ResponseTimeThresholdMs {
691 dev_log!(
692 "lifecycle",
693 "warn: [HealthCheck] Service {} response time {}ms exceeds threshold {}ms",
694 ServiceName,
695 ResponseTime,
696 self.config.ResponseTimeThresholdMs
697 );
698
699 self.HandleResponseTimeRecovery(ServiceName, ResponseTime).await;
700 }
701 }
702 }
703 }
704
705 async fn HandleResponseTimeRecovery(&self, ServiceName:&str, ResponseTime:u64) {
707 dev_log!(
708 "lifecycle",
709 "[HealthCheck] Handling response time recovery for {}: {}ms",
710 ServiceName,
711 ResponseTime
712 );
713
714 match ServiceName {
715 "grpc" => {
716 dev_log!(
717 "lifecycle",
718 "warn: [HealthCheck] Response time recovery: Optimizing gRPC server for {}",
719 ServiceName
720 );
721
722 },
727
728 "connections" => {
729 dev_log!(
730 "lifecycle",
731 "warn: [HealthCheck] Response time recovery: Optimizing connections for {}",
732 ServiceName
733 );
734
735 },
740
741 _ => {
742 dev_log!(
743 "lifecycle",
744 "warn: [HealthCheck] Response time recovery: Generic optimization for {}",
745 ServiceName
746 );
747 },
748 }
749 }
750
751 async fn HandleCriticalAlerts(&self, ServiceName:&str, status:&HealthStatus) {
753 if *status == HealthStatus::Unhealthy {
754 dev_log!(
755 "lifecycle",
756 "warn: [HealthCheck] CRITICAL: Service {} is UNHEALTHY - immediate attention required",
757 ServiceName
758 );
759
760 }
766 }
767
768 async fn PerformRecoveryAction(&self, ServiceName:&str) {
770 dev_log!("lifecycle", "[HealthCheck] Performing recovery action for {}", ServiceName);
771
772 let RecoveryTimeout = tokio::time::Duration::from_secs(self.config.RecoveryTimeoutSec);
773
774 let result = tokio::time::timeout(RecoveryTimeout, async {
775 match ServiceName {
776 "authentication" => self.RestartAuthenticationService().await,
777 "updates" => self.RestartUpdatesService().await,
778 "downloader" => self.RestartDownloaderService().await,
779 "indexing" => self.RestartIndexingService().await,
780 "grpc" => self.RestartgRPCService().await,
781 "connections" => self.ResetConnectionsService().await,
782 _ => {
783 dev_log!(
784 "lifecycle",
785 "warn: [HealthCheck] No specific recovery action for {}",
786 ServiceName
787 );
788
789 Ok(())
790 },
791 }
792 })
793 .await;
794
795 match result {
796 Ok(Ok(())) => {
797 dev_log!(
798 "lifecycle",
799 "[HealthCheck] Recovery action completed successfully for {}",
800 ServiceName
801 );
802 },
803
804 Ok(Err(e)) => {
805 dev_log!(
806 "lifecycle",
807 "warn: [HealthCheck] Recovery action failed for {}: {:?}",
808 ServiceName,
809 e
810 );
811 },
812
813 Err(_) => {
814 dev_log!("lifecycle", "warn: [HealthCheck] Recovery action timed out for {}", ServiceName);
815 },
816 }
817 }
818
819 async fn RestartAuthenticationService(&self) -> Result<()> {
821 dev_log!("lifecycle", "warn: [HealthCheck] Recovery: Restarting authentication service"); Ok(())
824 }
825
826 async fn RestartUpdatesService(&self) -> Result<()> {
828 dev_log!("lifecycle", "warn: [HealthCheck] Recovery: Restarting updates service"); Ok(())
831 }
832
833 async fn RestartDownloaderService(&self) -> Result<()> {
835 dev_log!("lifecycle", "warn: [HealthCheck] Recovery: Restarting downloader service"); Ok(())
838 }
839
840 async fn RestartIndexingService(&self) -> Result<()> {
842 dev_log!("lifecycle", "warn: [HealthCheck] Recovery: Restarting indexing service"); Ok(())
845 }
846
847 async fn RestartgRPCService(&self) -> Result<()> {
849 dev_log!("lifecycle", "warn: [HealthCheck] Recovery: Restarting gRPC server"); Ok(())
852 }
853
854 async fn ResetConnectionsService(&self) -> Result<()> {
856 dev_log!("lifecycle", "warn: [HealthCheck] Recovery: Resetting connections service"); Ok(())
859 }
860
861 pub async fn GetOverallHealth(&self) -> HealthStatus {
863 let HealthMap = self.ServiceHealth.read().await;
864
865 let mut HealthyCount = 0;
866
867 let mut DegradedCount = 0;
868
869 let mut UnhealthyCount = 0;
870
871 for ServiceHealth in HealthMap.values() {
872 match ServiceHealth.Status {
873 HealthStatus::Healthy => HealthyCount += 1,
874
875 HealthStatus::Degraded => DegradedCount += 1,
876
877 HealthStatus::Unhealthy => UnhealthyCount += 1,
878
879 HealthStatus::Unknown => {},
880 }
881 }
882
883 if UnhealthyCount > 0 {
884 HealthStatus::Unhealthy
885 } else if DegradedCount > 0 {
886 HealthStatus::Degraded
887 } else if HealthyCount > 0 {
888 HealthStatus::Healthy
889 } else {
890 HealthStatus::Unknown
891 }
892 }
893
894 pub async fn GetServiceHealth(&self, service_name:&str) -> Option<ServiceHealth> {
896 let HealthMap = self.ServiceHealth.read().await;
897
898 HealthMap.get(service_name).cloned()
899 }
900
901 pub async fn GetHealthHistory(&self, service_name:Option<&str>, limit:Option<usize>) -> Vec<HealthCheckRecord> {
903 let History = self.HealthHistory.read().await;
904
905 let mut FilteredHistory:Vec<HealthCheckRecord> = if let Some(service) = service_name {
906 History.iter().filter(|Record| Record.ServiceName == service).cloned().collect()
907 } else {
908 History.clone()
909 };
910
911 FilteredHistory.reverse();
913
914 if let Some(limit) = limit {
916 FilteredHistory.truncate(limit);
917 }
918
919 FilteredHistory
920 }
921
922 pub async fn RegisterRecoveryAction(&self, action:RecoveryAction) -> Result<()> {
924 let mut actions = self.RecoveryActions.write().await;
925
926 actions.insert(action.Name.clone(), action);
927
928 Ok(())
929 }
930
931 pub async fn GetHealthStatistics(&self) -> HealthStatistics {
933 let HealthMap = self.ServiceHealth.read().await;
934
935 let history = self.HealthHistory.read().await;
936
937 let mut HealthyServices = 0;
939
940 let mut DegradedServices = 0;
941
942 let mut UnhealthyServices = 0;
943
944 for ServiceHealth in HealthMap.values() {
945 match ServiceHealth.Status {
946 HealthStatus::Healthy => HealthyServices += 1,
947
948 HealthStatus::Degraded => DegradedServices += 1,
949
950 HealthStatus::Unhealthy => UnhealthyServices += 1,
951
952 HealthStatus::Unknown => {},
953 }
954 }
955
956 let mut Statistics = HealthStatistics {
958 TotalServices:HealthMap.len(),
959
960 HealthyServices,
961
962 DegradedServices,
963
964 UnhealthyServices,
965
966 TotalChecks:history.len(),
967
968 AverageResponseTimeMs:0.0,
969
970 SuccessRate:0.0,
971 };
972
973 if !history.is_empty() {
975 let mut TotalResponseTime = 0;
976
977 let mut SuccessfulChecks = 0;
978
979 for Record in history.iter() {
980 if let Some(ResponseTime) = Record.ResponseTimeMs {
981 TotalResponseTime += ResponseTime;
982 }
983
984 if Record.Status == HealthStatus::Healthy {
985 SuccessfulChecks += 1;
986 }
987 }
988
989 Statistics.AverageResponseTimeMs = TotalResponseTime as f64 / history.len() as f64;
990
991 Statistics.SuccessRate = SuccessfulChecks as f64 / history.len() as f64 * 100.0;
992 }
993
994 Statistics
995 }
996}
997
998#[derive(Debug, Clone, Serialize, Deserialize)]
1000pub struct HealthStatistics {
1001 pub TotalServices:usize,
1002
1003 pub HealthyServices:usize,
1004
1005 pub DegradedServices:usize,
1006
1007 pub UnhealthyServices:usize,
1008
1009 pub TotalChecks:usize,
1010
1011 pub AverageResponseTimeMs:f64,
1012
1013 pub SuccessRate:f64,
1014}
1015
1016impl HealthStatistics {
1017 pub fn OverallHealthPercentage(&self) -> f64 {
1019 if self.TotalServices == 0 {
1020 return 0.0;
1021 }
1022
1023 (self.HealthyServices as f64 / self.TotalServices as f64) * 100.0
1024 }
1025}
1026
1027#[derive(Debug, Clone, Serialize, Deserialize)]
1029pub struct HealthCheckResponse {
1030 pub OverallStatus:HealthStatus,
1031
1032 pub ServiceHealth:HashMap<String, ServiceHealth>,
1033
1034 pub Statistics:HealthStatistics,
1035
1036 pub PerformanceIndicators:PerformanceIndicators,
1037
1038 pub ResourceWarnings:Vec<ResourceWarning>,
1039
1040 pub Timestamp:u64,
1041}
1042
1043impl HealthCheckResponse {
1044 pub fn new(
1046 OverallStatus:HealthStatus,
1047
1048 ServiceHealth:HashMap<String, ServiceHealth>,
1049
1050 Statistics:HealthStatistics,
1051 ) -> Self {
1052 Self {
1053 OverallStatus,
1054
1055 ServiceHealth,
1056
1057 Statistics,
1058
1059 PerformanceIndicators:PerformanceIndicators::default(),
1060
1061 ResourceWarnings:Vec::new(),
1062
1063 Timestamp:Utility::CurrentTimestamp(),
1064 }
1065 }
1066
1067 pub fn with_performance_indicators(mut self, indicators:PerformanceIndicators) -> Self {
1069 self.PerformanceIndicators = indicators;
1070
1071 self
1072 }
1073
1074 pub fn with_resource_warnings(mut self, warnings:Vec<ResourceWarning>) -> Self {
1076 self.ResourceWarnings = warnings;
1077
1078 self
1079 }
1080}
1081
1082#[derive(Debug, Clone, Serialize, Deserialize)]
1084pub struct PerformanceIndicators {
1085 pub ResponseTimeP99Ms:f64,
1086
1087 pub ResponseTimeP95Ms:f64,
1088
1089 pub RequestThroughputPerSec:f64,
1090
1091 pub ErrorRatePercent:f64,
1092
1093 pub DegradationLevel:DegradationLevel,
1094
1095 pub BottleneckService:Option<String>,
1096}
1097
1098impl Default for PerformanceIndicators {
1099 fn default() -> Self {
1100 Self {
1101 ResponseTimeP99Ms:0.0,
1102
1103 ResponseTimeP95Ms:0.0,
1104
1105 RequestThroughputPerSec:0.0,
1106
1107 ErrorRatePercent:0.0,
1108
1109 DegradationLevel:DegradationLevel::Optimal,
1110
1111 BottleneckService:None,
1112 }
1113 }
1114}
1115
1116#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1118pub enum DegradationLevel {
1119 Optimal,
1120
1121 Acceptable,
1122
1123 Degraded,
1124
1125 Critical,
1126}
1127
1128#[derive(Debug, Clone, Serialize, Deserialize)]
1130pub struct ResourceWarning {
1131 pub WarningType:ResourceWarningType,
1132
1133 pub ServiceName:Option<String>,
1134
1135 pub CurrentValue:f64,
1136
1137 pub Threshold:f64,
1138
1139 pub Severity:WarningSeverity,
1140
1141 pub Timestamp:u64,
1142}
1143
1144#[derive(Debug, Clone, Serialize, Deserialize)]
1146pub enum ResourceWarningType {
1147 HighMemoryUsage,
1148
1149 HighCPUUsage,
1150
1151 LowDiskSpace,
1152
1153 ConnectionPoolExhausted,
1154
1155 ThreadPoolExhausted,
1156
1157 HighLatency,
1158
1159 HighErrorRate,
1160
1161 DatabaseConnectivityIssue,
1162}
1163
1164#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1166pub enum WarningSeverity {
1167 Low,
1168
1169 Medium,
1170
1171 High,
1172
1173 Critical,
1174}