@@ -3,9 +3,11 @@ use std::{collections::HashMap, fmt::Display};
3
3
use axum:: http:: StatusCode ;
4
4
use serde:: { ser:: SerializeStruct , Deserialize , Serialize } ;
5
5
use tonic:: Code ;
6
- use tracing:: { error, warn} ;
7
6
8
- use crate :: { clients:: ClientCode , pb:: grpc:: health:: v1:: HealthCheckResponse } ;
7
+ use crate :: {
8
+ clients:: ClientCode ,
9
+ pb:: grpc:: health:: v1:: { health_check_response:: ServingStatus , HealthCheckResponse } ,
10
+ } ;
9
11
10
12
/// Health status determined for or returned by a client service.
11
13
#[ derive( Debug , Clone , PartialEq , Serialize , Deserialize ) ]
@@ -31,50 +33,30 @@ impl Display for HealthStatus {
31
33
32
34
impl From < HealthCheckResponse > for HealthStatus {
33
35
fn from ( value : HealthCheckResponse ) -> Self {
34
- // NOTE: gRPC Health v1 status codes: 0 = UNKNOWN, 1 = SERVING, 2 = NOT_SERVING, 3 = SERVICE_UNKNOWN
35
- match value. status {
36
- 1 => Self :: Healthy ,
37
- 2 => Self :: Unhealthy ,
38
- _ => Self :: Unknown ,
36
+ match value. status ( ) {
37
+ ServingStatus :: Serving => Self :: Healthy ,
38
+ ServingStatus :: NotServing => Self :: Unhealthy ,
39
+ ServingStatus :: Unknown | ServingStatus :: ServiceUnknown => Self :: Unknown ,
39
40
}
40
41
}
41
42
}
42
43
43
44
impl From < StatusCode > for HealthStatus {
44
45
fn from ( code : StatusCode ) -> Self {
45
46
match code. as_u16 ( ) {
46
- 200 => Self :: Healthy ,
47
- 201 ..=299 => {
48
- warn ! (
49
- "Unexpected HTTP successful health check response status code: {}" ,
50
- code
51
- ) ;
52
- Self :: Healthy
53
- }
54
- 503 => Self :: Unhealthy ,
55
- 500 ..=502 | 504 ..=599 => {
56
- warn ! (
57
- "Unexpected HTTP server error health check response status code: {}" ,
58
- code
59
- ) ;
60
- Self :: Unhealthy
61
- }
62
- _ => {
63
- warn ! (
64
- "Unexpected HTTP client error health check response status code: {}" ,
65
- code
66
- ) ;
67
- Self :: Unknown
68
- }
47
+ 200 ..=299 => Self :: Healthy ,
48
+ 500 ..=599 => Self :: Unhealthy ,
49
+ _ => Self :: Unknown ,
69
50
}
70
51
}
71
52
}
72
53
73
- /// Holds health check results for all clients.
54
+ /// A cache to hold the latest health check results for each client service.
55
+ /// Orchestrator has a reference-counted mutex-protected instance of this cache.
74
56
#[ derive( Debug , Clone , Default , Serialize ) ]
75
- pub struct ClientHealth ( HashMap < String , HealthCheckResult > ) ;
57
+ pub struct HealthCheckCache ( HashMap < String , HealthCheckResult > ) ;
76
58
77
- impl ClientHealth {
59
+ impl HealthCheckCache {
78
60
pub fn new ( ) -> Self {
79
61
Self ( HashMap :: new ( ) )
80
62
}
@@ -83,82 +65,78 @@ impl ClientHealth {
83
65
Self ( HashMap :: with_capacity ( capacity) )
84
66
}
85
67
68
+ /// Returns `true` if all services are healthy or unknown.
86
69
pub fn healthy ( & self ) -> bool {
87
70
!self
88
71
. 0
89
72
. iter ( )
90
- . any ( |( _, value) | matches ! ( value. health_status , HealthStatus :: Unhealthy ) )
73
+ . any ( |( _, value) | matches ! ( value. status , HealthStatus :: Unhealthy ) )
91
74
}
92
75
}
93
76
94
- impl std:: ops:: Deref for ClientHealth {
77
+ impl std:: ops:: Deref for HealthCheckCache {
95
78
type Target = HashMap < String , HealthCheckResult > ;
96
79
97
80
fn deref ( & self ) -> & Self :: Target {
98
81
& self . 0
99
82
}
100
83
}
101
84
102
- impl std:: ops:: DerefMut for ClientHealth {
85
+ impl std:: ops:: DerefMut for HealthCheckCache {
103
86
fn deref_mut ( & mut self ) -> & mut Self :: Target {
104
87
& mut self . 0
105
88
}
106
89
}
107
90
91
+ impl Display for HealthCheckCache {
92
+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
93
+ write ! ( f, "{}" , serde_json:: to_string( self ) . unwrap( ) )
94
+ }
95
+ }
96
+
97
+ impl HealthCheckResponse {
98
+ pub fn reason ( & self ) -> Option < String > {
99
+ let status = self . status ( ) ;
100
+ match status {
101
+ ServingStatus :: Serving => None ,
102
+ _ => Some ( status. as_str_name ( ) . to_string ( ) ) ,
103
+ }
104
+ }
105
+ }
106
+
108
107
/// Result of a health check request.
109
108
#[ derive( Debug , Clone ) ]
110
109
pub struct HealthCheckResult {
111
110
/// Overall health status of client service.
112
111
/// `HEALTHY`, `UNHEALTHY`, or `UNKNOWN`.
113
- pub health_status : HealthStatus ,
112
+ pub status : HealthStatus ,
114
113
/// Response code of the latest health check request.
115
114
/// This should be omitted on serialization if the health check was successful (when the response is `HTTP 200 OK` or `gRPC 0 OK`).
116
- pub response_code : ClientCode ,
115
+ pub code : ClientCode ,
117
116
/// Optional reason for the health check result status being `UNHEALTHY` or `UNKNOWN`.
118
117
/// May be omitted overall if the health check was successful.
119
118
pub reason : Option < String > ,
120
119
}
121
120
122
- impl HealthCheckResult {
123
- pub fn reason_from_health_check_response ( response : & HealthCheckResponse ) -> Option < String > {
124
- match response. status {
125
- 0 => Some ( "from gRPC health check serving status: UNKNOWN" . to_string ( ) ) ,
126
- 1 => None ,
127
- 2 => Some ( "from gRPC health check serving status: NOT_SERVING" . to_string ( ) ) ,
128
- 3 => Some ( "from gRPC health check serving status: SERVICE_UNKNOWN" . to_string ( ) ) ,
129
- _ => {
130
- error ! (
131
- "Unexpected gRPC health check serving status: {}" ,
132
- response. status
133
- ) ;
134
- Some ( format ! (
135
- "Unexpected gRPC health check serving status: {}" ,
136
- response. status
137
- ) )
138
- }
139
- }
140
- }
141
- }
142
-
143
121
impl Serialize for HealthCheckResult {
144
122
fn serialize < S > ( & self , serializer : S ) -> Result < S :: Ok , S :: Error >
145
123
where
146
124
S : serde:: Serializer ,
147
125
{
148
- match self . health_status {
149
- HealthStatus :: Healthy => self . health_status . serialize ( serializer) ,
126
+ match self . status {
127
+ HealthStatus :: Healthy => self . status . serialize ( serializer) ,
150
128
_ => match & self . reason {
151
129
Some ( reason) => {
152
130
let mut state = serializer. serialize_struct ( "HealthCheckResult" , 3 ) ?;
153
- state. serialize_field ( "health_status " , & self . health_status ) ?;
154
- state. serialize_field ( "response_code " , & self . response_code . to_string ( ) ) ?;
131
+ state. serialize_field ( "status " , & self . status ) ?;
132
+ state. serialize_field ( "code " , & self . code . to_string ( ) ) ?;
155
133
state. serialize_field ( "reason" , reason) ?;
156
134
state. end ( )
157
135
}
158
136
None => {
159
137
let mut state = serializer. serialize_struct ( "HealthCheckResult" , 2 ) ?;
160
- state. serialize_field ( "health_status " , & self . health_status ) ?;
161
- state. serialize_field ( "response_code " , & self . response_code . to_string ( ) ) ?;
138
+ state. serialize_field ( "status " , & self . status ) ?;
139
+ state. serialize_field ( "code " , & self . code . to_string ( ) ) ?;
162
140
state. end ( )
163
141
}
164
142
} ,
@@ -169,12 +147,8 @@ impl Serialize for HealthCheckResult {
169
147
impl Display for HealthCheckResult {
170
148
fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
171
149
match & self . reason {
172
- Some ( reason) => write ! (
173
- f,
174
- "{} ({})\n \t \t \t {}" ,
175
- self . health_status, self . response_code, reason
176
- ) ,
177
- None => write ! ( f, "{} ({})" , self . health_status, self . response_code) ,
150
+ Some ( reason) => write ! ( f, "{} ({})\n \t \t \t {}" , self . status, self . code, reason) ,
151
+ None => write ! ( f, "{} ({})" , self . status, self . code) ,
178
152
}
179
153
}
180
154
}
@@ -185,14 +159,14 @@ impl From<Result<tonic::Response<HealthCheckResponse>, tonic::Status>> for Healt
185
159
Ok ( response) => {
186
160
let response = response. into_inner ( ) ;
187
161
Self {
188
- health_status : response. into ( ) ,
189
- response_code : ClientCode :: Grpc ( Code :: Ok ) ,
190
- reason : Self :: reason_from_health_check_response ( & response) ,
162
+ status : response. into ( ) ,
163
+ code : ClientCode :: Grpc ( Code :: Ok ) ,
164
+ reason : response. reason ( ) ,
191
165
}
192
166
}
193
167
Err ( status) => Self {
194
- health_status : HealthStatus :: Unknown ,
195
- response_code : ClientCode :: Grpc ( status. code ( ) ) ,
168
+ status : HealthStatus :: Unknown ,
169
+ code : ClientCode :: Grpc ( status. code ( ) ) ,
196
170
reason : Some ( format ! ( "gRPC health check failed: {}" , status) ) ,
197
171
} ,
198
172
}
@@ -205,7 +179,7 @@ impl From<Result<tonic::Response<HealthCheckResponse>, tonic::Status>> for Healt
205
179
#[ derive( Deserialize ) ]
206
180
pub struct OptionalHealthCheckResponseBody {
207
181
/// `HEALTHY`, `UNHEALTHY`, or `UNKNOWN`. Although `HEALTHY` is already implied without a body.
208
- pub health_status : HealthStatus ,
182
+ pub status : HealthStatus ,
209
183
/// Optional reason for the health check result status being `UNHEALTHY` or `UNKNOWN`.
210
184
/// May be omitted overall if the health check was successful.
211
185
#[ serde( default ) ]
0 commit comments