44
55import logging
66import threading
7- from enum import Enum
7+ from collections import Counter
88
9+ from enum import Enum
910import six
1011
1112from splitio .client .client import Client
4546 LocalhostSplitSynchronizationTask , LocalhostTelemetryStorage
4647
4748
49+ _LOGGER = logging .getLogger (__name__ )
50+ _INSTANTIATED_FACTORIES = Counter ()
51+ _INSTANTIATED_FACTORIES_LOCK = threading .RLock ()
52+
53+
4854class Status (Enum ):
4955 """Factory Status."""
5056
@@ -64,6 +70,7 @@ class SplitFactory(object): # pylint: disable=too-many-instance-attributes
6470
6571 def __init__ ( # pylint: disable=too-many-arguments
6672 self ,
73+ apikey ,
6774 storages ,
6875 labels_enabled ,
6976 apis = None ,
@@ -87,6 +94,7 @@ def __init__( # pylint: disable=too-many-arguments
8794 :param impression_listener: User custom listener to handle impressions locally.
8895 :type impression_listener: splitio.client.listener.ImpressionListener
8996 """
97+ self ._apikey = apikey
9098 self ._logger = logging .getLogger (self .__class__ .__name__ )
9199 self ._storages = storages
92100 self ._labels_enabled = labels_enabled
@@ -197,6 +205,8 @@ def _wait_for_tasks_to_stop():
197205 task .stop ()
198206 finally :
199207 self ._status = Status .DESTROYED
208+ with _INSTANTIATED_FACTORIES_LOCK :
209+ _INSTANTIATED_FACTORIES .subtract ([self ._apikey ])
200210
201211 @property
202212 def destroyed (self ):
@@ -324,6 +334,7 @@ def segment_ready_task():
324334 segment_completion_thread .setDaemon (True )
325335 segment_completion_thread .start ()
326336 return SplitFactory (
337+ api_key ,
327338 storages ,
328339 cfg ['labelsEnabled' ],
329340 apis ,
@@ -333,7 +344,7 @@ def segment_ready_task():
333344 )
334345
335346
336- def _build_redis_factory (config ):
347+ def _build_redis_factory (api_key , config ):
337348 """Build and return a split factory with redis-based storage."""
338349 cfg = DEFAULT_CONFIG .copy ()
339350 cfg .update (config )
@@ -347,13 +358,14 @@ def _build_redis_factory(config):
347358 'telemetry' : RedisTelemetryStorage (redis_adapter , sdk_metadata )
348359 }
349360 return SplitFactory (
361+ api_key ,
350362 storages ,
351363 cfg ['labelsEnabled' ],
352364 impression_listener = _wrap_impression_listener (cfg ['impressionListener' ], sdk_metadata )
353365 )
354366
355367
356- def _build_uwsgi_factory (config ):
368+ def _build_uwsgi_factory (api_key , config ):
357369 """Build and return a split factory with redis-based storage."""
358370 cfg = DEFAULT_CONFIG .copy ()
359371 cfg .update (config )
@@ -367,6 +379,7 @@ def _build_uwsgi_factory(config):
367379 'telemetry' : UWSGITelemetryStorage (uwsgi_adapter )
368380 }
369381 return SplitFactory (
382+ api_key ,
370383 storages ,
371384 cfg ['labelsEnabled' ],
372385 impression_listener = _wrap_impression_listener (cfg ['impressionListener' ], sdk_metadata )
@@ -393,25 +406,47 @@ def _build_localhost_factory(config):
393406 ready_event
394407 )}
395408 tasks ['splits' ].start ()
396- return SplitFactory (storages , False , None , tasks , ready_event )
409+ return SplitFactory ('localhost' , storages , False , None , tasks , ready_event )
397410
398411
399412def get_factory (api_key , ** kwargs ):
400413 """Build and return the appropriate factory."""
401- config = kwargs .get ('config' , {})
414+ try :
415+ _INSTANTIATED_FACTORIES_LOCK .acquire ()
416+ if _INSTANTIATED_FACTORIES :
417+ if api_key in _INSTANTIATED_FACTORIES :
418+ _LOGGER .warning (
419+ "factory instantiation: You already have %d %s with this API Key. "
420+ "We recommend keeping only one instance of the factory at all times "
421+ "(Singleton pattern) and reusing it throughout your application." ,
422+ _INSTANTIATED_FACTORIES [api_key ],
423+ 'factory' if _INSTANTIATED_FACTORIES [api_key ] == 1 else 'factories'
424+ )
425+ else :
426+ _LOGGER .warning (
427+ "factory instantiation: You already have an instance of the Split factory. "
428+ "Make sure you definitely want this additional instance. "
429+ "We recommend keeping only one instance of the factory at all times "
430+ "(Singleton pattern) and reusing it throughout your application."
431+ )
402432
403- if api_key == 'localhost' :
404- return _build_localhost_factory (config )
433+ config = kwargs .get ('config' , {})
405434
406- if 'redisHost' in config or 'redisSentinels' in config :
407- return _build_redis_factory (config )
435+ if api_key == 'localhost' :
436+ return _build_localhost_factory (config )
408437
409- if 'uwsgiClient ' in config :
410- return _build_uwsgi_factory ( config )
438+ if 'redisHost' in config or 'redisSentinels ' in config :
439+ return _build_redis_factory ( api_key , config )
411440
412- return _build_in_memory_factory (
413- api_key ,
414- config ,
415- kwargs .get ('sdk_api_base_url' ),
416- kwargs .get ('events_api_base_url' )
417- )
441+ if 'uwsgiClient' in config :
442+ return _build_uwsgi_factory (api_key , config )
443+
444+ return _build_in_memory_factory (
445+ api_key ,
446+ config ,
447+ kwargs .get ('sdk_api_base_url' ),
448+ kwargs .get ('events_api_base_url' )
449+ )
450+ finally :
451+ _INSTANTIATED_FACTORIES .update ([api_key ])
452+ _INSTANTIATED_FACTORIES_LOCK .release ()
0 commit comments