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 ):
@@ -321,6 +331,7 @@ def segment_ready_task():
321331 segment_completion_thread .setDaemon (True )
322332 segment_completion_thread .start ()
323333 return SplitFactory (
334+ api_key ,
324335 storages ,
325336 cfg ['labelsEnabled' ],
326337 apis ,
@@ -330,7 +341,7 @@ def segment_ready_task():
330341 )
331342
332343
333- def _build_redis_factory (config ):
344+ def _build_redis_factory (api_key , config ):
334345 """Build and return a split factory with redis-based storage."""
335346 cfg = DEFAULT_CONFIG .copy ()
336347 cfg .update (config )
@@ -344,13 +355,14 @@ def _build_redis_factory(config):
344355 'telemetry' : RedisTelemetryStorage (redis_adapter , sdk_metadata )
345356 }
346357 return SplitFactory (
358+ api_key ,
347359 storages ,
348360 cfg ['labelsEnabled' ],
349361 impression_listener = _wrap_impression_listener (cfg ['impressionListener' ], sdk_metadata )
350362 )
351363
352364
353- def _build_uwsgi_factory (config ):
365+ def _build_uwsgi_factory (api_key , config ):
354366 """Build and return a split factory with redis-based storage."""
355367 cfg = DEFAULT_CONFIG .copy ()
356368 cfg .update (config )
@@ -364,6 +376,7 @@ def _build_uwsgi_factory(config):
364376 'telemetry' : UWSGITelemetryStorage (uwsgi_adapter )
365377 }
366378 return SplitFactory (
379+ api_key ,
367380 storages ,
368381 cfg ['labelsEnabled' ],
369382 impression_listener = _wrap_impression_listener (cfg ['impressionListener' ], sdk_metadata )
@@ -390,25 +403,47 @@ def _build_localhost_factory(config):
390403 ready_event
391404 )}
392405 tasks ['splits' ].start ()
393- return SplitFactory (storages , False , None , tasks , ready_event )
406+ return SplitFactory ('localhost' , storages , False , None , tasks , ready_event )
394407
395408
396409def get_factory (api_key , ** kwargs ):
397410 """Build and return the appropriate factory."""
398- config = kwargs .get ('config' , {})
411+ try :
412+ _INSTANTIATED_FACTORIES_LOCK .acquire ()
413+ if _INSTANTIATED_FACTORIES :
414+ if api_key in _INSTANTIATED_FACTORIES :
415+ _LOGGER .warning (
416+ "factory instantiation: You already have %d %s with this API Key. "
417+ "We recommend keeping only one instance of the factory at all times "
418+ "(Singleton pattern) and reusing it throughout your application." ,
419+ _INSTANTIATED_FACTORIES [api_key ],
420+ 'factory' if _INSTANTIATED_FACTORIES [api_key ] == 1 else 'factories'
421+ )
422+ else :
423+ _LOGGER .warning (
424+ "factory instantiation: You already have an instance of the Split factory. "
425+ "Make sure you definitely want this additional instance. "
426+ "We recommend keeping only one instance of the factory at all times "
427+ "(Singleton pattern) and reusing it throughout your application."
428+ )
399429
400- if api_key == 'localhost' :
401- return _build_localhost_factory (config )
430+ config = kwargs .get ('config' , {})
402431
403- if 'redisHost' in config or 'redisSentinels' in config :
404- return _build_redis_factory (config )
432+ if api_key == 'localhost' :
433+ return _build_localhost_factory (config )
405434
406- if 'uwsgiClient ' in config :
407- return _build_uwsgi_factory ( config )
435+ if 'redisHost' in config or 'redisSentinels ' in config :
436+ return _build_redis_factory ( api_key , config )
408437
409- return _build_in_memory_factory (
410- api_key ,
411- config ,
412- kwargs .get ('sdk_api_base_url' ),
413- kwargs .get ('events_api_base_url' )
414- )
438+ if 'uwsgiClient' in config :
439+ return _build_uwsgi_factory (api_key , config )
440+
441+ return _build_in_memory_factory (
442+ api_key ,
443+ config ,
444+ kwargs .get ('sdk_api_base_url' ),
445+ kwargs .get ('events_api_base_url' )
446+ )
447+ finally :
448+ _INSTANTIATED_FACTORIES .update ([api_key ])
449+ _INSTANTIATED_FACTORIES_LOCK .release ()
0 commit comments