def run(self): if not self._running: log.info("Starting PollingUpdateProcessor with request interval: " + str(self._config.poll_interval)) self._running = True while self._running: start_time = time.time() try: all_data = self._requester.get_all_data() self._store.init(all_data) if not self._ready.is_set() is True and self._store.initialized is True: log.info("PollingUpdateProcessor initialized ok") self._ready.set() except HTTPError as e: log.error('Received unexpected status code %d from polling request' % e.response.status_code) if e.response.status_code == 401: log.error('Received 401 error, no further polling requests will be made since SDK key is invalid') self.stop() break except: log.exception( 'Error: Exception encountered when updating flags.') elapsed = time.time() - start_time if elapsed < self._config.poll_interval: time.sleep(self._config.poll_interval - elapsed)
def _run_main_loop(self): log.info("Starting event processor") while True: try: message = self._inbox.get(block=True) if message.type == 'event': self._process_event(message.param) elif message.type == 'flush': self._trigger_flush() elif message.type == 'flush_users': self._user_keys.clear() elif message.type == 'diagnostic': self._send_and_reset_diagnostics() elif message.type == 'test_sync': self._flush_workers.wait() if self._diagnostic_accumulator is not None: self._diagnostic_flush_workers.wait() message.param.set() elif message.type == 'stop': self._do_shutdown() message.param.set() return except Exception: log.error('Unhandled exception in event processor', exc_info=True)
def run(self): log.info("Starting StreamingUpdateProcessor connecting to uri: " + self._uri) self._running = True while self._running: try: messages = self._connect() for msg in messages: if not self._running: break message_ok = self.process_message(self._store, self._requester, msg) if message_ok is True and self._ready.is_set() is False: log.info("StreamingUpdateProcessor initialized ok.") self._ready.set() except HTTPError as e: log.error("Received unexpected status code %d for stream connection" % e.response.status_code) if e.response.status_code == 401: log.error("Received 401 error, no further streaming connection will be made since SDK key is invalid") self.stop() break else: log.warning("Restarting stream connection after one second.") except Exception: log.warning("Caught exception. Restarting stream connection after one second.", exc_info=True) time.sleep(1)
def process_message(store, requester, msg, ready): payload = json.loads(msg.data) log.debug("Received stream event {}".format(msg.event)) if msg.event == 'put': store.init(payload) if not ready.is_set() and store.initialized: ready.set() log.info("StreamingUpdateProcessor initialized ok") elif msg.event == 'patch': key = payload['path'][1:] feature = payload['data'] log.debug("Updating feature {}".format(key)) store.upsert(key, feature) elif msg.event == "indirect/patch": key = payload['data'] store.upsert(key, requester.get_one(key)) elif msg.event == "indirect/put": store.init(requester.get_all()) if not ready.is_set() and store.initialized: ready.set() log.info("StreamingUpdateProcessor initialized ok") elif msg.event == 'delete': key = payload['path'][1:] # noinspection PyShadowingNames version = payload['version'] store.delete(key, version) else: log.warning('Unhandled event in stream processor: ' + msg.event)
def run(self): log.info("Starting MobileStreamingUpdateProcessor connecting to uri: " + self._uri) self._running = True while self._running: try: init_start = time.time() messages = self._connect() for msg in messages: if not self._running: break message_ok = self.process_message(self._store, self._requester, msg) if message_ok is True and self._ready.is_set() is False: log.info("MobileStreamingUpdateProcessor initialized ok.") init_duration = int((time.time() - init_start) * 1000) request_success.fire(request_type="ld:init", name="mobile", response_time=init_duration, response_length=0) self._ready.set() except UnsuccessfulResponseException as e: log.error(http_error_message(e.status, "stream connection")) init_duration = int((time.time() - init_start) * 1000) request_failure.fire(request_type="ld:init", name="mobile", response_time=init_duration, response_length=0, exception=e) if not is_http_error_recoverable(e.status): self._ready.set() # if client is initializing, make it stop waiting; has no effect if already inited self.stop() break except Exception as e: init_duration = int((time.time() - init_start) * 1000) request_failure.fire(request_type="ld:init", name="mobile", response_time=init_duration, response_length=0, exception=e) log.warning("Caught exception. Restarting stream connection after one second. %s" % e) # no stacktrace here because, for a typical connection error, it'll just be a lengthy tour of urllib3 internals time.sleep(1)
def _make_update_processor(self, config, store, ready, diagnostic_accumulator): if config.update_processor_class: log.info("Using user-specified update processor: " + str(config.update_processor_class)) return config.update_processor_class(config, store, ready) if config.offline or config.use_ldd: return NullUpdateProcessor(config, store, ready) if config.feature_requester_class: feature_requester = config.feature_requester_class(config) else: feature_requester = FeatureRequesterImpl(config) """ :type: FeatureRequester """ if config.stream: return StreamingUpdateProcessor(config, feature_requester, store, ready, diagnostic_accumulator) log.info("Disabling streaming API") log.warning( "You should only disable the streaming API if instructed to do so by LaunchDarkly support" ) return PollingUpdateProcessor(config, feature_requester, store, ready)
def run(self): if not self._running: log.info("Starting PollingUpdateProcessor with request interval: " + str(self._config.poll_interval)) self._running = True while self._running: start_time = time.time() try: all_data = self._requester.get_all_data() self._store.init(all_data) if not self._ready.is_set() is True and self._store.initialized is True: log.info("PollingUpdateProcessor initialized ok") self._ready.set() except UnsuccessfulResponseException as e: log.error(http_error_message(e.status, "polling request")) if not is_http_error_recoverable(e.status): self._ready.set() # if client is initializing, make it stop waiting; has no effect if already inited self.stop() break except Exception as e: log.exception( 'Error: Exception encountered when updating flags. %s' % e) elapsed = time.time() - start_time if elapsed < self._config.poll_interval: time.sleep(self._config.poll_interval - elapsed)
def run(self): log.info("Starting event consumer") self._running = True while self._running: try: self.send() except Exception: log.warning('Unhandled exception in event consumer')
def close(self): log.info("Closing LaunchDarkly client..") if self.is_offline(): return if self._event_consumer and self._event_consumer.is_alive(): self._event_consumer.stop() if self._update_processor and self._update_processor.is_alive(): self._update_processor.stop()
def close(self): """Releases all threads and network connections used by the LaunchDarkly client. Do not attempt to use the client after calling this method. """ log.info("Closing LaunchDarkly client..") self._event_processor.stop() self._update_processor.stop()
def __init__(self, sdk_key, config=None, start_wait=5): check_uwsgi() self._sdk_key = sdk_key self._config = config or Config.default() self._session = CacheControl(requests.Session()) self._queue = queue.Queue(self._config.events_max_pending) self._event_consumer = None self._lock = Lock() self._store = self._config.feature_store """ :type: FeatureStore """ if self._config.offline: self._config.events_enabled = False log.info("Started LaunchDarkly Client in offline mode") return if self._config.events_enabled: self._event_consumer = self._config.event_consumer_class( self._queue, self._sdk_key, self._config) self._event_consumer.start() if self._config.use_ldd: if self._store.__class__ == "RedisFeatureStore": log.info("Started LaunchDarkly Client in LDD mode") return log.error("LDD mode requires a RedisFeatureStore.") return if self._config.feature_requester_class: self._feature_requester = self._config.feature_requester_class( sdk_key, self._config) else: self._feature_requester = FeatureRequesterImpl(sdk_key, self._config) """ :type: FeatureRequester """ update_processor_ready = threading.Event() if self._config.update_processor_class: self._update_processor = self._config.update_processor_class( sdk_key, self._config, self._feature_requester, self._store, update_processor_ready) else: if self._config.stream: self._update_processor = StreamingUpdateProcessor( sdk_key, self._config, self._feature_requester, self._store, update_processor_ready) else: self._update_processor = PollingUpdateProcessor( sdk_key, self._config, self._feature_requester, self._store, update_processor_ready) """ :type: UpdateProcessor """ self._update_processor.start() log.info("Waiting up to " + str(start_wait) + " seconds for LaunchDarkly client to initialize...") update_processor_ready.wait(start_wait) if self._update_processor.initialized: log.info("Started LaunchDarkly Client: OK") else: log.info("Initialization timeout exceeded for LaunchDarkly Client. Feature Flags may not yet be available.")
def run(self): if not self._running: log.info("Starting PollingUpdateProcessor with request interval: " + str(self._config.poll_interval)) self._running = True while self._running: start_time = time.time() self._store.init(self._requester.get_all()) if not self._ready.is_set() is True and self._store.initialized is True: log.info("PollingUpdateProcessor initialized ok") self._ready.set() elapsed = time.time() - start_time if elapsed < self._config.poll_interval: time.sleep(self._config.poll_interval - elapsed)
def close(self): """Releases all threads and network connections used by the LaunchDarkly client. Do not attempt to use the client after calling this method. """ log.info("Closing LaunchDarkly client..") if self.is_offline(): return if self._event_processor: self._event_processor.stop() if self._config.use_ldd: return if self._update_processor and self._update_processor.is_alive(): self._update_processor.stop()
def __init__(self, url='redis://localhost:6379/0', expiration=15, capacity=1000, redis_prefix='launchdarkly'): self._url = url parsed_url = urlparse.urlparse(url) self._redis_host = parsed_url.hostname self._redis_port = parsed_url.port self._features_key = "{}:features".format(redis_prefix) self._cache = ForgetfulDict() if expiration == 0 else ExpiringDict( max_len=capacity, max_age_seconds=expiration) log.info("Created TwistedRedisFeatureStore with url: " + url + " using key: " + self._features_key)
def cb(flag): try: if not flag: log.info("Feature Flag key: " + key + " not found in Feature Store. Returning default.") send_event(default) return default return self._evaluate_and_send_events(flag, user, default) except Exception as e: log.error("Exception caught in variation: " + e.message + " for flag key: " + key + " and user: " + str(user)) return default
def __init__(self, user, sdk_key=None, config=None, start_wait=5): """Constructs a new LDClient instance. :param string sdk_key: the SDK key for your LaunchDarkly environment :param ldclient.config.Config config: optional custom configuration :param float start_wait: the number of seconds to wait for a successful connection to LaunchDarkly """ if user is None: raise Exception("user is required for mobile sdk") if config is not None and config.sdk_key is not None and sdk_key is not None: raise Exception( "LaunchDarkly client init received both sdk_key and config with sdk_key. " "Only one of either is expected") if sdk_key is not None: log.warning( "Deprecated sdk_key argument was passed to init. Use config object instead." ) self._config = MobileConfig(sdk_key=sdk_key) else: self._config = config.copy_with_new_user( user) or MobileConfig.default().copy_with_new_user(user=user) self._config._validate() self._event_processor = None self._lock = Lock() self._event_factory_default = _EventFactory(False) self._event_factory_with_reasons = _EventFactory(True) self._store = _FeatureStoreClientWrapper(self._config.feature_store) """ :type: FeatureStore """ if self._config.offline: log.info("Started LaunchDarkly Client in offline mode") if self._config.use_ldd: log.info("Started LaunchDarkly Client in LDD mode") self._event_processor = self._make_event_processor(self._config) update_processor_ready = threading.Event() self._update_processor = self._make_update_processor( self._config, self._store, update_processor_ready) self._update_processor.start() if start_wait > 0 and not self._config.offline and not self._config.use_ldd: log.info("Waiting up to " + str(start_wait) + " seconds for LaunchDarkly client to initialize...") update_processor_ready.wait(start_wait) if self._update_processor.initialized() is True: log.info("Started LaunchDarkly Client: OK") else: log.warning( "Initialization timeout exceeded for LaunchDarkly Client or an error occurred. " "Feature Flags may not yet be available.")
def run(self): log.info("Starting StreamingUpdateProcessor connecting to uri: " + self._config.stream_uri) self._running = True hdrs = _stream_headers(self._sdk_key) uri = self._config.stream_uri while self._running: try: messages = SSEClient(uri, verify=self._config.verify_ssl, headers=hdrs) for msg in messages: if not self._running: break self.process_message(self._store, self._requester, msg, self._ready) except Exception as e: log.error("Could not connect to LaunchDarkly stream: " + str(e.message) + " waiting 1 second before trying again.") time.sleep(1)
def _run_main_loop(self): log.info("Starting event processor") while True: try: message = self._queue.get(block=True) if message.type == 'event': self._process_event(message.param) elif message.type == 'flush': self._trigger_flush() elif message.type == 'flush_users': self._user_keys.clear() elif message.type == 'test_sync': self._flush_workers.wait() message.param.set() elif message.type == 'stop': self._do_shutdown() message.param.set() return except Exception: log.error('Unhandled exception in event processor', exc_info=True)
def run(self): log.info("Starting StreamingUpdateProcessor connecting to uri: " + self._uri) self._running = True while self._running: try: messages = self._connect() for msg in messages: if not self._running: break message_ok = self.process_message(self._store, self._requester, msg) if message_ok is True and self._ready.is_set() is False: log.info("StreamingUpdateProcessor initialized ok.") self._ready.set() except UnsuccessfulResponseException as e: log.error(http_error_message(e.status, "stream connection")) if not is_http_error_recoverable(e.status): self._ready.set() # if client is initializing, make it stop waiting; has no effect if already inited self.stop() break except Exception as e: log.warning("Caught exception. Restarting stream connection after one second. %s" % e) # no stacktrace here because, for a typical connection error, it'll just be a lengthy tour of urllib3 internals time.sleep(1)
def run(self): log.info("Starting StreamingUpdateProcessor connecting to uri: " + self._uri) self._running = True while self._running: try: self._es_started = int(time.time() * 1000) messages = self._connect() for msg in messages: if not self._running: break message_ok = self.process_message(self._store, self._requester, msg) if message_ok: self._record_stream_init(False) self._es_started = None if message_ok is True and self._ready.is_set() is False: log.info("StreamingUpdateProcessor initialized ok.") self._ready.set() except UnsuccessfulResponseException as e: log.error(http_error_message(e.status, "stream connection")) self._record_stream_init(True) self._es_started = None if not is_http_error_recoverable(e.status): self._ready.set( ) # if client is initializing, make it stop waiting; has no effect if already inited self.stop() break except Exception as e: log.warning( "Caught exception. Restarting stream connection after one second. %s" % e) self._record_stream_init(True) self._es_started = None # no stacktrace here because, for a typical connection error, it'll just be a lengthy tour of urllib3 internals time.sleep(1)
def run(self): if not self._running: log.info("Starting PollingUpdateProcessor with request interval: " + str(self._config.poll_interval)) self._running = True while self._running: start_time = time.time() try: all_data = self._requester.get_all_data() self._store.init(all_data) if not self._ready.is_set() is True and self._store.initialized is True: log.info("PollingUpdateProcessor initialized ok") self._ready.set() except UnsuccessfulResponseException as e: log.error(http_error_message(e.status, "polling request")) if not is_http_error_recoverable(e.status): self._ready.set() # if client is initializing, make it stop waiting; has no effect if already inited self.stop() except Exception as e: log.exception( 'Error: Exception encountered when updating flags. %s' % e) elapsed = time.time() - start_time if elapsed < self._config.poll_interval: time.sleep(self._config.poll_interval - elapsed)
def __init__(self, config: Config, start_wait: float = 5): """Constructs a new LDClient instance. :param config: optional custom configuration :param start_wait: the number of seconds to wait for a successful connection to LaunchDarkly """ check_uwsgi() self._config = config self._config._validate() self._event_processor = None self._lock = Lock() self._event_factory_default = _EventFactory(False) self._event_factory_with_reasons = _EventFactory(True) self._store = _FeatureStoreClientWrapper(self._config.feature_store) """ :type: FeatureStore """ if self._config.offline: log.info("Started LaunchDarkly Client in offline mode") if self._config.use_ldd: log.info("Started LaunchDarkly Client in LDD mode") diagnostic_accumulator = self._set_event_processor(self._config) update_processor_ready = threading.Event() self._update_processor = self._make_update_processor( self._config, self._store, update_processor_ready, diagnostic_accumulator) self._update_processor.start() if start_wait > 0 and not self._config.offline and not self._config.use_ldd: log.info("Waiting up to " + str(start_wait) + " seconds for LaunchDarkly client to initialize...") update_processor_ready.wait(start_wait) if self._update_processor.initialized() is True: log.info("Started LaunchDarkly Client: OK") else: log.warning( "Initialization timeout exceeded for LaunchDarkly Client or an error occurred. " "Feature Flags may not yet be available.")
def run(self): log.info("Starting StreamingUpdateProcessor connecting to uri: " + self._uri) self._running = True attempts = 0 while self._running: if attempts > 0: delay = self._retry_delay.next_retry_delay(time.time()) log.info("Will reconnect after delay of %fs" % delay) time.sleep(delay) attempts += 1 try: self._es_started = int(time.time() * 1000) messages = self._connect() for msg in messages: if not self._running: break self._retry_delay.set_good_since(time.time()) message_ok = self.process_message(self._store, msg) if message_ok: self._record_stream_init(False) self._es_started = None if message_ok is True and self._ready.is_set() is False: log.info("StreamingUpdateProcessor initialized ok.") self._ready.set() except UnsuccessfulResponseException as e: log.error(http_error_message(e.status, "stream connection")) self._record_stream_init(True) self._es_started = None if not is_http_error_recoverable(e.status): self._ready.set( ) # if client is initializing, make it stop waiting; has no effect if already inited self.stop() break except Exception as e: log.warning( "Unexpected error on stream connection: %s, will retry" % e) self._record_stream_init(True) self._es_started = None
def log_backoff_message(props): log.error("Streaming connection failed, will attempt to restart") log.info("Will reconnect after delay of %fs", props['wait'])
def stop(self): log.info("Stopping StreamingUpdateProcessor") self._running = False
def stop(self): log.info("Stopping StreamingUpdateProcessor") self._running = False
def run(self): log.info("Starting event consumer") self._running = True while self._running: self.send()
def __init__(self, sdk_key=None, config=None, start_wait=5): """Constructs a new LDClient instance. :param string sdk_key: the SDK key for your LaunchDarkly environment :param ldclient.config.Config config: optional custom configuration :param float start_wait: the number of seconds to wait for a successful connection to LaunchDarkly """ check_uwsgi() if config is not None and config.sdk_key is not None and sdk_key is not None: raise Exception( "LaunchDarkly client init received both sdk_key and config with sdk_key. " "Only one of either is expected") if sdk_key is not None: log.warn( "Deprecated sdk_key argument was passed to init. Use config object instead." ) self._config = Config(sdk_key=sdk_key) else: self._config = config or Config.default() self._config._validate() self._event_processor = None self._lock = Lock() self._store = _FeatureStoreClientWrapper(self._config.feature_store) """ :type: FeatureStore """ if self._config.offline or not self._config.send_events: self._event_processor = NullEventProcessor() else: self._event_processor = self._config.event_processor_class( self._config) if self._config.offline: log.info("Started LaunchDarkly Client in offline mode") return if self._config.use_ldd: log.info("Started LaunchDarkly Client in LDD mode") return update_processor_ready = threading.Event() if self._config.update_processor_class: log.info("Using user-specified update processor: " + str(self._config.update_processor_class)) self._update_processor = self._config.update_processor_class( self._config, self._store, update_processor_ready) else: if self._config.feature_requester_class: feature_requester = self._config.feature_requester_class( self._config) else: feature_requester = FeatureRequesterImpl(self._config) """ :type: FeatureRequester """ if self._config.stream: self._update_processor = StreamingUpdateProcessor( self._config, feature_requester, self._store, update_processor_ready) else: log.info("Disabling streaming API") log.warn( "You should only disable the streaming API if instructed to do so by LaunchDarkly support" ) self._update_processor = PollingUpdateProcessor( self._config, feature_requester, self._store, update_processor_ready) """ :type: UpdateProcessor """ self._update_processor.start() log.info("Waiting up to " + str(start_wait) + " seconds for LaunchDarkly client to initialize...") update_processor_ready.wait(start_wait) if self._update_processor.initialized() is True: log.info("Started LaunchDarkly Client: OK") else: log.warn( "Initialization timeout exceeded for LaunchDarkly Client or an error occurred. " "Feature Flags may not yet be available.")
def log_backoff_message(props): log.error("Streaming connection failed, will attempt to restart") log.info("Will reconnect after delay of %fs", props['wait'])
def __init__(self, sdk_key=None, config=None, start_wait=5): check_uwsgi() if config is not None and config.sdk_key is not None and sdk_key is not None: raise Exception( "LaunchDarkly client init received both sdk_key and config with sdk_key. " "Only one of either is expected") if sdk_key is not None: log.warn( "Deprecated sdk_key argument was passed to init. Use config object instead." ) self._config = Config(sdk_key=sdk_key) else: self._config = config or Config.default() self._config._validate() self._session = CacheControl(requests.Session()) self._queue = queue.Queue(self._config.events_max_pending) self._event_consumer = None self._lock = Lock() self._store = self._config.feature_store """ :type: FeatureStore """ if self._config.offline: log.info("Started LaunchDarkly Client in offline mode") return if self._config.events_enabled: self._event_consumer = self._config.event_consumer_class( self._queue, self._config) self._event_consumer.start() if self._config.use_ldd: log.info("Started LaunchDarkly Client in LDD mode") return if self._config.feature_requester_class: self._feature_requester = self._config.feature_requester_class( self._config) else: self._feature_requester = FeatureRequesterImpl(self._config) """ :type: FeatureRequester """ update_processor_ready = threading.Event() if self._config.update_processor_class: log.info("Using user-specified update processor: " + str(self._config.update_processor_class)) self._update_processor = self._config.update_processor_class( self._config, self._feature_requester, self._store, update_processor_ready) else: if self._config.stream: self._update_processor = StreamingUpdateProcessor( self._config, self._feature_requester, self._store, update_processor_ready) else: self._update_processor = PollingUpdateProcessor( self._config, self._feature_requester, self._store, update_processor_ready) """ :type: UpdateProcessor """ self._update_processor.start() log.info("Waiting up to " + str(start_wait) + " seconds for LaunchDarkly client to initialize...") update_processor_ready.wait(start_wait) if self._update_processor.initialized() is True: log.info("Started LaunchDarkly Client: OK") else: log.warn( "Initialization timeout exceeded for LaunchDarkly Client or an error occurred. " "Feature Flags may not yet be available.")
def run(self): log.info("Starting StreamingUpdateProcessor connecting to uri: " + self._uri) self._running = True while self._running: self._connect()