77
88from typing import Iterator , Optional
99
10+ import grpc
1011from azure .core .credentials import TokenCredential
1112from azure .identity import ManagedIdentityCredential
1213
@@ -45,6 +46,7 @@ def __init__(self) -> None:
4546 resolved_secure_channel = _resolve_secure_channel (resolved_host_address )
4647 resolved_token_credential = _resolve_token_credential ()
4748 resolved_max_concurrent_activities = _resolve_max_concurrent_activities ()
49+ resolved_dts_sandbox_identifier = _resolve_dts_sandbox_identifier ()
4850 resolved_sandbox_provider = _resolve_sandbox_provider ()
4951 concurrency_options = ConcurrencyOptions (
5052 maximum_concurrent_activity_work_items = resolved_max_concurrent_activities )
@@ -66,7 +68,7 @@ def __init__(self) -> None:
6668 self ._sandbox_activities : list [SandboxActivity ] = []
6769 self ._sandbox_max_activities = resolved_max_concurrent_activities
6870 self ._sandbox_provider = resolved_sandbox_provider
69- self ._sandbox_dts_sandbox_identifier = os . getenv ( "DTS_SANDBOX_ID" )
71+ self ._sandbox_dts_sandbox_identifier = resolved_dts_sandbox_identifier
7072 self ._sandbox_heartbeat_interval_seconds = 2.0
7173 self ._sandbox_registration_stop = threading .Event ()
7274 self ._sandbox_registration_thread : Optional [threading .Thread ] = None
@@ -143,6 +145,11 @@ def _run_sandbox_registration_loop(self) -> None:
143145 except Exception as ex :
144146 if self ._sandbox_registration_stop .is_set ():
145147 break
148+ if not _is_retriable_registration_failure (ex ):
149+ self ._sandbox_logger .error (
150+ "Sandbox activity worker registration failed permanently: %s" , ex )
151+ self ._sandbox_registration_stop .set ()
152+ break
146153 self ._sandbox_logger .warning ("Sandbox activity worker registration failed: %s" , ex )
147154 delay = random .uniform (0 , retry_delay )
148155 self ._sandbox_registration_stop .wait (delay )
@@ -216,19 +223,59 @@ def _resolve_token_credential() -> TokenCredential | None:
216223 return ManagedIdentityCredential (client_id = client_id .strip ())
217224
218225
219- def _resolve_sandbox_provider () -> str :
226+ def _resolve_sandbox_provider () -> Optional [ str ] :
220227 sandbox_provider = os .getenv ("DTS_SANDBOX_PROVIDER" )
221- if not sandbox_provider :
228+ if not sandbox_provider or not sandbox_provider .strip ():
229+ return None
230+ return sandbox_provider .strip ()
231+
232+
233+ def _resolve_dts_sandbox_identifier () -> str :
234+ dts_sandbox_identifier = os .getenv ("DTS_SANDBOX_ID" )
235+ if not dts_sandbox_identifier or not dts_sandbox_identifier .strip ():
222236 raise ValueError (
223- "Sandbox worker requires DTS_SANDBOX_PROVIDER to be injected in the "
237+ "Sandbox worker requires DTS_SANDBOX_ID to be injected in the "
224238 "sandbox environment." )
225239
226- normalized = sandbox_provider .strip ()
227- if normalized .lower () not in ("sandbox" , "acasessionpool" ):
228- raise ValueError (
229- "Sandbox worker requires DTS_SANDBOX_PROVIDER to be Sandbox or AcaSessionPool." )
240+ return dts_sandbox_identifier .strip ()
241+
242+
243+ _RETRIABLE_REGISTRATION_STATUS_CODES = {
244+ grpc .StatusCode .CANCELLED ,
245+ grpc .StatusCode .DEADLINE_EXCEEDED ,
246+ grpc .StatusCode .INTERNAL ,
247+ grpc .StatusCode .RESOURCE_EXHAUSTED ,
248+ grpc .StatusCode .UNAVAILABLE ,
249+ grpc .StatusCode .UNKNOWN ,
250+ }
251+
252+ _PERMANENT_FAILED_PRECONDITION_DETAILS = (
253+ "worker profile" ,
254+ "registered activit" ,
255+ "max activit" ,
256+ "sandbox identifier" ,
257+ "sandbox id" ,
258+ )
259+
260+
261+ def _is_retriable_registration_failure (ex : Exception ) -> bool :
262+ if isinstance (ex , grpc .RpcError ):
263+ status_code = ex .code ()
264+ if status_code in _RETRIABLE_REGISTRATION_STATUS_CODES :
265+ return True
266+ if status_code == grpc .StatusCode .FAILED_PRECONDITION :
267+ details = _rpc_error_details (ex ).casefold ()
268+ return not any (
269+ detail in details
270+ for detail in _PERMANENT_FAILED_PRECONDITION_DETAILS )
271+ return False
272+
273+ return isinstance (ex , OSError )
274+
230275
231- return normalized
276+ def _rpc_error_details (ex : grpc .RpcError ) -> str :
277+ details = ex .details ()
278+ return details if isinstance (details , str ) else ""
232279
233280
234281def _resolve_max_concurrent_activities () -> int :
0 commit comments