3737 create_diagnostic_id
3838)
3939from ldclient .impl .events .types import EventFactory
40- from ldclient .impl .listeners import Listeners
4140from ldclient .impl .model .feature_flag import FeatureFlag
4241from ldclient .impl .stubs import AsyncNullEventProcessor
4342from ldclient .impl .util import log
5352from ldclient .versioned_data_kind import FEATURES , SEGMENTS
5453
5554
56- class _NotStartedDataSystem :
57- """Placeholder data system used before start(); reports that only
58- application-provided defaults are available."""
59-
60- @property
61- def data_availability (self ) -> DataAvailability :
62- return DataAvailability .DEFAULTS
63-
64- async def stop (self ) -> None :
65- pass
66-
67-
6855class AsyncLDClient :
6956 """Async LaunchDarkly SDK client.
7057
@@ -96,23 +83,47 @@ def __init__(self, config: AsyncConfig):
9683
9784 self ._session = None
9885 self ._proxy : Optional [str ] = None
99- # Pre-start placeholders so that evaluation/ track/identify before
100- # start() degrade gracefully (defaults returned, events dropped) .
86+ # Event processor is a no-op until start(); track/identify before start()
87+ # drop events.
10188 self ._event_processor : Any = AsyncNullEventProcessor ()
102- self ._data_system : AsyncDataSystem = _NotStartedDataSystem () # type: ignore[assignment]
10389
10490 self .__hooks : List = list (config .hooks )
10591
10692 self ._event_factory_default = EventFactory (False )
10793 self ._event_factory_with_reasons = EventFactory (True )
10894
109- self ._flag_change_listeners = Listeners ()
95+ # Build the object graph here (loop-free). start() supplies the loop-bound
96+ # resources: the HTTP session (created lazily), the data source, the
97+ # big-segment poll, and the event processor. Evaluation before start()
98+ # serves whatever the store already has.
99+ self ._data_system : AsyncDataSystem = self ._make_data_system ()
100+
101+ self .__data_store_status_provider = self ._data_system .data_store_status_provider
102+ self .__data_source_status_provider = self ._data_system .data_source_status_provider
103+
104+ self .__big_segment_store_manager = AsyncBigSegmentStoreManager (self ._config .big_segments )
105+
106+ async def get_flag_fn (key ):
107+ return await self ._data_system .store .get (FEATURES , key )
108+
109+ async def get_segment_fn (key ):
110+ return await self ._data_system .store .get (SEGMENTS , key )
111+
112+ async def get_membership_fn (key ):
113+ return await self .__big_segment_store_manager .get_user_membership (key )
114+
115+ self ._evaluator = AsyncEvaluator (
116+ get_flag_fn ,
117+ get_segment_fn ,
118+ get_membership_fn ,
119+ log ,
120+ )
110121
111122 async def variation_eval_fn (key , context ):
112123 return await self .variation (key , context , None )
113124
114125 self .__flag_tracker = AsyncFlagTrackerImpl (
115- self ._flag_change_listeners ,
126+ self ._data_system . flag_change_listeners ,
116127 variation_eval_fn
117128 )
118129
@@ -208,33 +219,8 @@ async def __start_up(self, start_wait: float):
208219
209220 self .__hooks = self ._config .hooks + plugin_hooks
210221
211- self ._session = await self ._create_http_session ()
212- self ._data_system = self ._make_data_system ()
213-
214- # Expose providers and store from data system
215- self .__data_store_status_provider = self ._data_system .data_store_status_provider
216- self .__data_source_status_provider = (
217- self ._data_system .data_source_status_provider
218- )
219-
220- big_segment_store_manager = AsyncBigSegmentStoreManager (self ._config .big_segments )
221- self .__big_segment_store_manager = big_segment_store_manager
222-
223- async def get_flag_fn (key ):
224- return await self ._data_system .store .get (FEATURES , key )
225-
226- async def get_segment_fn (key ):
227- return await self ._data_system .store .get (SEGMENTS , key )
228-
229- async def get_membership_fn (key ):
230- return await big_segment_store_manager .get_user_membership (key )
231-
232- self ._evaluator = AsyncEvaluator (
233- get_flag_fn ,
234- get_segment_fn ,
235- get_membership_fn ,
236- log ,
237- )
222+ # Start the big-segment status poll now that a loop is running.
223+ self .__big_segment_store_manager .start ()
238224
239225 if self ._config .offline :
240226 log .info ("Started LaunchDarkly Client in offline mode" )
@@ -265,8 +251,16 @@ async def get_membership_fn(key):
265251 else :
266252 log .warning ("Initialization timeout exceeded for LaunchDarkly Client or an error occurred. " "Feature Flags may not yet be available." )
267253
268- async def _create_http_session (self ):
269- """Create and return the aiohttp session. Called from __start_up."""
254+ def _get_session (self ):
255+ """Return the shared aiohttp session, creating it on first use inside the
256+ event loop. Nothing creates it in offline/LDD mode, because no network
257+ component asks for it."""
258+ if self ._session is None :
259+ self ._session = self ._create_http_session ()
260+ return self ._session
261+
262+ def _create_http_session (self ):
263+ """Create and return the aiohttp session."""
270264 import ssl
271265
272266 import aiohttp
@@ -293,7 +287,7 @@ def _make_data_system(self) -> AsyncDataSystem:
293287 if datasystem_config is None :
294288 from ldclient .impl .datasystem .async_fdv1 import AsyncFDv1
295289
296- return AsyncFDv1 (self ._config , self ._select_feature_store (), self ._flag_change_listeners , self . _session , self . _proxy )
290+ return AsyncFDv1 (self ._config , self ._select_feature_store (), self ._get_session )
297291
298292 raise NotImplementedError ("FDv2 is not yet supported in the async client" )
299293
@@ -319,7 +313,7 @@ def _set_event_processor(self, config):
319313 if not config .event_processor_class :
320314 diagnostic_id = create_diagnostic_id (config )
321315 diagnostic_accumulator = None if config .diagnostic_opt_out else _DiagnosticAccumulator (diagnostic_id )
322- self ._event_processor = DefaultAsyncEventProcessor (config , self ._session , diagnostic_accumulator = diagnostic_accumulator )
316+ self ._event_processor = DefaultAsyncEventProcessor (config , self ._get_session () , diagnostic_accumulator = diagnostic_accumulator )
323317 return diagnostic_accumulator
324318 self ._event_processor = config .event_processor_class (config )
325319 return None
0 commit comments