@@ -71,6 +71,7 @@ class Status(Enum):
7171 NOT_INITIALIZED = 'NOT_INITIALIZED'
7272 READY = 'READY'
7373 DESTROYED = 'DESTROYED'
74+ WAITING_FORK = 'WAITING_FORK'
7475
7576
7677class TimeoutException (Exception ):
@@ -90,6 +91,7 @@ def __init__( # pylint: disable=too-many-arguments
9091 recorder ,
9192 sync_manager = None ,
9293 sdk_ready_flag = None ,
94+ preforked_initialization = False ,
9395 ):
9496 """
9597 Class constructor.
@@ -106,18 +108,29 @@ def __init__( # pylint: disable=too-many-arguments
106108 :type sdk_ready_flag: threading.Event
107109 :param recorder: StatsRecorder instance
108110 :type recorder: StatsRecorder
111+ :param preforked_initialization: Whether should be instantiated as preforked or not.
112+ :type preforked_initialization: bool
109113 """
110114 self ._apikey = apikey
111115 self ._storages = storages
112116 self ._labels_enabled = labels_enabled
113117 self ._sync_manager = sync_manager
114118 self ._sdk_internal_ready_flag = sdk_ready_flag
115- self ._sdk_ready_flag = threading .Event ()
116119 self ._recorder = recorder
120+ self ._preforked_initialization = preforked_initialization
121+ self ._start_status_updater ()
117122
123+ def _start_status_updater (self ):
124+ """
125+ Perform status updater
126+ """
127+ if self ._preforked_initialization :
128+ self ._status = Status .WAITING_FORK
129+ return
118130 # If we have a ready flag, it means we have sync tasks that need to finish
119131 # before the SDK client becomes ready.
120132 if self ._sdk_internal_ready_flag is not None :
133+ self ._sdk_ready_flag = threading .Event ()
121134 self ._status = Status .NOT_INITIALIZED
122135 # add a listener that updates the status to READY once the flag is set.
123136 ready_updater = threading .Thread (target = self ._update_status_when_ready ,
@@ -232,6 +245,38 @@ def destroyed(self):
232245 """
233246 return self ._status == Status .DESTROYED
234247
248+ def _waiting_fork (self ):
249+ """
250+ Return whether the factory is waiting to be recreated by forking or not.
251+
252+ :return: True if the factory is waiting to be recreated by forking. False otherwise.
253+ :rtype: bool
254+ """
255+ return self ._status == Status .WAITING_FORK
256+
257+ def handle_post_fork (self ):
258+ """
259+ Function in charge of starting periodic/realtime synchronization after a fork.
260+ """
261+ if not self ._waiting_fork ():
262+ _LOGGER .warning ('Cannot call handle_post_fork' )
263+ return
264+ self ._sync_manager .recreate ()
265+ sdk_ready_flag = threading .Event ()
266+ self ._sdk_internal_ready_flag = sdk_ready_flag
267+ self ._sync_manager ._ready_flag = sdk_ready_flag
268+ self ._get_storage ('telemetry' ).clear ()
269+ self ._get_storage ('impressions' ).clear ()
270+ self ._get_storage ('events' ).clear ()
271+ initialization_thread = threading .Thread (
272+ target = self ._sync_manager .start ,
273+ name = "SDKInitializer" ,
274+ )
275+ initialization_thread .setDaemon (True )
276+ initialization_thread .start ()
277+ self ._preforked_initialization = False # reset for status updater
278+ self ._start_status_updater ()
279+
235280
236281def _wrap_impression_listener (listener , metadata ):
237282 """
@@ -319,14 +364,12 @@ def _build_in_memory_factory(api_key, cfg, sdk_url=None, events_url=None, # pyl
319364
320365 synchronizer = Synchronizer (synchronizers , tasks )
321366
322- sdk_ready_flag = threading .Event ()
367+ preforked_initialization = cfg .get ('preforkedInitialization' , False )
368+
369+ sdk_ready_flag = threading .Event () if not preforked_initialization else None
323370 manager = Manager (sdk_ready_flag , synchronizer , apis ['auth' ], cfg ['streamingEnabled' ],
324371 streaming_api_base_url )
325372
326- initialization_thread = threading .Thread (target = manager .start , name = "SDKInitializer" )
327- initialization_thread .setDaemon (True )
328- initialization_thread .start ()
329-
330373 storages ['events' ].set_queue_full_hook (tasks .events_task .flush )
331374 storages ['impressions' ].set_queue_full_hook (tasks .impressions_task .flush )
332375
@@ -336,6 +379,17 @@ def _build_in_memory_factory(api_key, cfg, sdk_url=None, events_url=None, # pyl
336379 storages ['events' ],
337380 storages ['impressions' ],
338381 )
382+
383+ if preforked_initialization :
384+ synchronizer .sync_all ()
385+ synchronizer ._split_synchronizers ._segment_sync .shutdown ()
386+ return SplitFactory (api_key , storages , cfg ['labelsEnabled' ],
387+ recorder , manager , preforked_initialization = preforked_initialization )
388+
389+ initialization_thread = threading .Thread (target = manager .start , name = "SDKInitializer" )
390+ initialization_thread .setDaemon (True )
391+ initialization_thread .start ()
392+
339393 return SplitFactory (api_key , storages , cfg ['labelsEnabled' ],
340394 recorder , manager , sdk_ready_flag )
341395
@@ -387,6 +441,10 @@ def _build_uwsgi_factory(api_key, cfg):
387441 storages ['events' ],
388442 storages ['impressions' ],
389443 )
444+ _LOGGER .warning (
445+ "Beware: uwsgi-cache based operation mode is soon to be deprecated. Please consider " +
446+ "redis if you need a centralized point of syncrhonization, or in-memory (with preforking " +
447+ "support enabled) if running uwsgi with a master and several http workers)" )
390448 return SplitFactory (
391449 api_key ,
392450 storages ,
0 commit comments