1111
1212
1313class ClientError (Exception ):
14- """There was an error in the microgrid API client."""
14+ """There was an error in the microgrid API client.
15+
16+ To simplify retrying, errors are classified as
17+ [retryable][frequenz.client.microgrid.ClientError.is_retryable], or not. Retryable
18+ errors might succeed if retried, while permanent errors won't. When uncertain,
19+ errors are assumed to be retryable.
20+ """
1521
1622 def __init__ (
1723 self ,
1824 * ,
1925 server_url : str ,
2026 operation : str ,
2127 description : str ,
28+ retryable : bool ,
2229 ) -> None :
2330 """Create a new instance.
2431
2532 Args:
2633 server_url: The URL of the server that returned the error.
2734 operation: The operation that caused the error.
2835 description: A human-readable description of the error.
36+ retryable: Whether retrying the operation might succeed.
2937 """
3038 super ().__init__ (
3139 f"Failed calling { operation !r} on { server_url !r} : { description } "
@@ -40,6 +48,9 @@ def __init__(
4048 self .description = description
4149 """The human-readable description of the error."""
4250
51+ self .is_retryable = retryable
52+ """Whether retrying the operation might succeed."""
53+
4354 @classmethod
4455 def from_grpc_error (
4556 cls ,
@@ -55,6 +66,7 @@ def from_grpc_error(
5566 server_url: The URL of the server that returned the error.
5667 operation: The operation that caused the error.
5768 grpc_error: The gRPC error to convert.
69+ retryable: Whether retrying the operation might succeed.
5870
5971 Returns:
6072 An instance of
@@ -97,6 +109,7 @@ def __call__(
97109 operation = operation ,
98110 description = "Got an unrecognized status code" ,
99111 grpc_error = grpc_error ,
112+ retryable = retryable ,
100113 )
101114
102115
@@ -111,13 +124,14 @@ class GrpcStatusError(ClientError):
111124 codes](https://github.com/grpc/grpc/blob/master/doc/statuscodes.md)
112125 """
113126
114- def __init__ (
127+ def __init__ ( # pylint: disable=too-many-arguments
115128 self ,
116129 * ,
117130 server_url : str ,
118131 operation : str ,
119132 description : str ,
120133 grpc_error : grpclib .GRPCError ,
134+ retryable : bool ,
121135 ) -> None :
122136 """Create a new instance.
123137
@@ -126,13 +140,15 @@ def __init__(
126140 operation: The operation that caused the error.
127141 description: A human-readable description of the error.
128142 grpc_error: The gRPC error originating this exception.
143+ retryable: Whether retrying the operation might succeed.
129144 """
130145 message = f": { grpc_error .message } " if grpc_error .message else ""
131146 details = f" ({ grpc_error .details } )" if grpc_error .details else ""
132147 super ().__init__ (
133148 server_url = server_url ,
134149 operation = operation ,
135150 description = f"{ description } <status={ grpc_error .status .name } >{ message } { details } " ,
151+ retryable = retryable ,
136152 )
137153 self .description = description
138154
@@ -158,6 +174,7 @@ def __init__(
158174 operation = operation ,
159175 description = "The operation was cancelled" ,
160176 grpc_error = grpc_error ,
177+ retryable = True ,
161178 )
162179
163180
@@ -179,6 +196,7 @@ def __init__(
179196 operation = operation ,
180197 description = "There was an error that can't be described using other statuses" ,
181198 grpc_error = grpc_error ,
199+ retryable = True , # We don't know so we assume it's retryable
182200 )
183201
184202
@@ -206,6 +224,7 @@ def __init__(
206224 operation = operation ,
207225 description = "The client specified an invalid argument" ,
208226 grpc_error = grpc_error ,
227+ retryable = False ,
209228 )
210229
211230
@@ -233,6 +252,7 @@ def __init__(
233252 description = "The time limit was exceeded while waiting for the operation "
234253 "to complete" ,
235254 grpc_error = grpc_error ,
255+ retryable = True ,
236256 )
237257
238258
@@ -259,6 +279,7 @@ def __init__(
259279 operation = operation ,
260280 description = "The requested entity was not found" ,
261281 grpc_error = grpc_error ,
282+ retryable = True , # If the entity is added later it might succeed
262283 )
263284
264285
@@ -280,6 +301,7 @@ def __init__(
280301 operation = operation ,
281302 description = "The entity that we attempted to create already exists" ,
282303 grpc_error = grpc_error ,
304+ retryable = True , # If the entity is deleted later it might succeed
283305 )
284306
285307
@@ -310,6 +332,7 @@ def __init__(
310332 description = "The caller does not have permission to execute the specified "
311333 "operation" ,
312334 grpc_error = grpc_error ,
335+ retryable = True , # If the user is granted permission it might succeed
313336 )
314337
315338
@@ -332,6 +355,7 @@ def __init__(
332355 description = "Some resource has been exhausted (for example per-user quota, "
333356 "disk space, etc.)" ,
334357 grpc_error = grpc_error ,
358+ retryable = True , # If the resource is freed it might succeed
335359 )
336360
337361
@@ -359,6 +383,7 @@ def __init__(
359383 description = "The operation was rejected because the system is not in a "
360384 "required state" ,
361385 grpc_error = grpc_error ,
386+ retryable = True , # If the system state changes it might succeed
362387 )
363388
364389
@@ -383,6 +408,7 @@ def __init__(
383408 operation = operation ,
384409 description = "The operation was aborted" ,
385410 grpc_error = grpc_error ,
411+ retryable = True ,
386412 )
387413
388414
@@ -413,6 +439,7 @@ def __init__(
413439 operation = operation ,
414440 description = "The operation was attempted past the valid range" ,
415441 grpc_error = grpc_error ,
442+ retryable = True , # If the system state changes it might succeed
416443 )
417444
418445
@@ -435,6 +462,7 @@ def __init__(
435462 description = "The operation is not implemented or not supported/enabled in "
436463 "this service" ,
437464 grpc_error = grpc_error ,
465+ retryable = False ,
438466 )
439467
440468
@@ -460,6 +488,7 @@ def __init__(
460488 description = "Some invariants expected by the underlying system have been "
461489 "broken" ,
462490 grpc_error = grpc_error ,
491+ retryable = True , # If the system state changes it might succeed
463492 )
464493
465494
@@ -485,6 +514,7 @@ def __init__(
485514 operation = operation ,
486515 description = "The service is currently unavailable" ,
487516 grpc_error = grpc_error ,
517+ retryable = True , # If the service becomes available it might succeed
488518 )
489519
490520
@@ -506,6 +536,7 @@ def __init__(
506536 operation = operation ,
507537 description = "Unrecoverable data loss or corruption" ,
508538 grpc_error = grpc_error ,
539+ retryable = False ,
509540 )
510541
511542
@@ -528,4 +559,5 @@ def __init__(
528559 description = "The request does not have valid authentication credentials "
529560 "for the operation" ,
530561 grpc_error = grpc_error ,
562+ retryable = False ,
531563 )
0 commit comments