@@ -46,9 +46,9 @@ def on_flag_change(self, notice: FlagChangedNotice):
4646class FeatBitProvider (AbstractProvider ):
4747 """OpenFeature provider backed by an existing thread-safe ``FBClient``.
4848
49- The provider does not own the client. ``shutdown`` removes the listeners
50- installed by the provider, but the application remains responsible for
51- calling ``FBClient.stop`` when it no longer needs the SDK .
49+ The provider owns the injected client's lifecycle . ``shutdown`` removes
50+ its listeners and stops the client so queued events and background
51+ resources are released with the OpenFeature lifecycle .
5252 """
5353
5454 def __init__ (self , client : FBClient , initialization_timeout : float = 15.0 ):
@@ -72,6 +72,9 @@ def client(self) -> FBClient:
7272 return self ._client
7373
7474 def initialize (self , evaluation_context : EvaluationContext ) -> None :
75+ # Register before waiting so a client that connects just after an
76+ # initialization timeout can still publish its recovery to OpenFeature.
77+ self ._register_listeners ()
7578 if not self ._client .initialize :
7679 ready = self ._client .update_status_provider .wait_for_OKState (
7780 self ._initialization_timeout
@@ -83,8 +86,6 @@ def initialize(self, evaluation_context: EvaluationContext) -> None:
8386 raise ProviderFatalError (message )
8487 raise ProviderNotReadyError (message )
8588
86- self ._register_listeners ()
87-
8889 def shutdown (self ) -> None :
8990 with self ._lifecycle_lock :
9091 flag_listener = self ._flag_change_listener
@@ -111,6 +112,13 @@ def shutdown(self) -> None:
111112 with self ._lifecycle_lock :
112113 self ._status_listener_registered = False
113114
115+ try :
116+ self ._client .stop ()
117+ except Exception :
118+ # Provider shutdown is application lifecycle code; an extension
119+ # failure in the underlying SDK must not escape into OpenFeature.
120+ logger .exception ("Failed to stop FeatBit client" )
121+
114122 def get_metadata (self ) -> Metadata :
115123 return Metadata ("featbit-openfeature-server" )
116124
@@ -292,12 +300,17 @@ def _handle_data_update_status(self, state: State) -> None:
292300 ProviderEventDetails (message = self ._state_message (state ))
293301 )
294302 elif state .state_type == StateType .OFF :
295- self .emit_provider_error (
296- ProviderEventDetails (
297- error_code = ErrorCode .PROVIDER_FATAL ,
298- message = self ._state_message (state ),
303+ if state .error_track is not None :
304+ self .emit_provider_error (
305+ ProviderEventDetails (
306+ error_code = ErrorCode .PROVIDER_FATAL ,
307+ message = self ._state_message (state ),
308+ )
299309 )
300- )
310+ # A normal OFF is produced by FBClient.stop(). Provider
311+ # shutdown unregisters this listener before stopping the
312+ # client; OpenFeature then removes the provider status, whose
313+ # standard fallback is NOT_READY. Do not misreport it FATAL.
301314 except Exception :
302315 logger .exception ("Failed to emit OpenFeature provider status event" )
303316
0 commit comments