@@ -11,6 +11,8 @@ pub mod error_codes {
1111 pub const RATE_LIMITED : i64 = -32016 ;
1212 /// Backend offline error code.
1313 pub const BACKEND_OFFLINE : i64 = -32010 ;
14+ /// Backend timeout error code.
15+ pub const BACKEND_TIMEOUT : i64 = -32011 ;
1416}
1517
1618/// Error type for the Roxy proxy.
@@ -58,19 +60,24 @@ impl RoxyError {
5860 match self {
5961 Self :: RateLimited { retry_after } => ErrorPayload {
6062 code : error_codes:: RATE_LIMITED ,
61- message : format ! ( "rate limited, retry after {:?}" , retry_after ) . into ( ) ,
63+ message : format ! ( "rate limited, retry after {retry_after :?}" ) . into ( ) ,
6264 data : None ,
6365 } ,
6466 Self :: BackendOffline { backend } => ErrorPayload {
6567 code : error_codes:: BACKEND_OFFLINE ,
66- message : format ! ( "backend {} is offline" , backend ) . into ( ) ,
68+ message : format ! ( "backend {backend } is offline" ) . into ( ) ,
6769 data : None ,
6870 } ,
6971 Self :: NoHealthyBackends => ErrorPayload {
7072 code : error_codes:: BACKEND_OFFLINE ,
7173 message : "no healthy backends" . into ( ) ,
7274 data : None ,
7375 } ,
76+ Self :: BackendTimeout { backend } => ErrorPayload {
77+ code : error_codes:: BACKEND_TIMEOUT ,
78+ message : format ! ( "backend {backend} timed out" ) . into ( ) ,
79+ data : None ,
80+ } ,
7481 _ => ErrorPayload :: internal_error ( ) ,
7582 }
7683 }
@@ -88,7 +95,6 @@ mod tests {
8895
8996 use super :: * ;
9097
91- /// Test that should_failover returns the expected value for each error type.
9298 #[ rstest]
9399 #[ case:: rate_limited( RoxyError :: RateLimited { retry_after: Duration :: from_secs( 5 ) } , false ) ]
94100 #[ case:: backend_offline( RoxyError :: BackendOffline { backend: "primary" . to_string( ) } , true ) ]
@@ -100,10 +106,10 @@ mod tests {
100106 assert_eq ! ( error. should_failover( ) , expected) ;
101107 }
102108
103- /// Test that to_error_payload returns the expected error code for each error type.
104109 #[ rstest]
105110 #[ case:: rate_limited( RoxyError :: RateLimited { retry_after: Duration :: from_secs( 5 ) } , error_codes:: RATE_LIMITED ) ]
106111 #[ case:: backend_offline( RoxyError :: BackendOffline { backend: "primary" . to_string( ) } , error_codes:: BACKEND_OFFLINE ) ]
112+ #[ case:: backend_timeout( RoxyError :: BackendTimeout { backend: "slow" . to_string( ) } , error_codes:: BACKEND_TIMEOUT ) ]
107113 #[ case:: no_healthy_backends( RoxyError :: NoHealthyBackends , error_codes:: BACKEND_OFFLINE ) ]
108114 #[ case:: cache_error( RoxyError :: CacheError ( "connection failed" . to_string( ) ) , -32603 ) ]
109115 #[ case:: internal_error( RoxyError :: Internal ( "unexpected state" . to_string( ) ) , -32603 ) ]
@@ -112,7 +118,6 @@ mod tests {
112118 assert_eq ! ( payload. code, expected_code) ;
113119 }
114120
115- /// Test that error Display messages contain expected substrings.
116121 #[ rstest]
117122 #[ case:: rate_limited( RoxyError :: RateLimited { retry_after: Duration :: from_secs( 5 ) } , "rate limited" ) ]
118123 #[ case:: backend_offline( RoxyError :: BackendOffline { backend: "primary" . to_string( ) } , "primary" ) ]
@@ -129,7 +134,6 @@ mod tests {
129134 ) ;
130135 }
131136
132- /// Test that backend offline error payload message contains the backend name.
133137 #[ rstest]
134138 #[ case:: primary( "primary" ) ]
135139 #[ case:: secondary( "secondary" ) ]
@@ -144,4 +148,19 @@ mod tests {
144148 backend_name
145149 ) ;
146150 }
151+
152+ #[ rstest]
153+ #[ case:: primary( "primary" ) ]
154+ #[ case:: secondary( "secondary" ) ]
155+ #[ case:: node_1( "node-1" ) ]
156+ fn test_backend_timeout_payload_contains_name ( #[ case] backend_name : & str ) {
157+ let err = RoxyError :: BackendTimeout { backend : backend_name. to_string ( ) } ;
158+ let payload = err. to_error_payload ( ) ;
159+ assert ! (
160+ payload. message. contains( backend_name) ,
161+ "Expected payload message '{}' to contain '{}'" ,
162+ payload. message,
163+ backend_name
164+ ) ;
165+ }
147166}
0 commit comments