class ImpressionsCountSyncTask(BaseSynchronizationTask): """Impressions synchronization task uses an asynctask.AsyncTask to send impressions.""" _PERIOD = 1800 # 30 * 60 # 30 minutes def __init__(self, synchronize_counters): """ Class constructor. :param synchronize_counters: Handler :type synchronize_counters: func """ self._task = AsyncTask(synchronize_counters, self._PERIOD, on_stop=synchronize_counters) def start(self): """Start executing the impressions synchronization task.""" self._task.start() def stop(self, event=None): """Stop executing the impressions synchronization task.""" self._task.stop(event) def is_running(self): """ Return whether the task is running or not. :return: True if the task is running. False otherwise. :rtype: bool """ return self._task.running() def flush(self): """Flush impressions in storage.""" self._task.force_execution()
class TelemetrySynchronizationTask(BaseSynchronizationTask): """Split Synchronization task class.""" def __init__(self, synchronize_telemetry, period): """ Class constructor. :param synchronize_telemetry: handler. :type synchronize_telemetry: splitio.api.telemetry.TelemetryAPI :param period: Period of task :type period: int """ self._period = period self._task = AsyncTask(synchronize_telemetry, period) def start(self): """Start the task.""" self._task.start() def stop(self, event=None): """Stop the task. Accept an optional event to set when the task has finished.""" self._task.stop(event) def is_running(self): """ Return whether the task is running. :return: True if the task is running. False otherwise. :rtype bool """ return self._task.running()
class ImpressionsSyncTask(BaseSynchronizationTask): """Impressions synchronization task uses an asynctask.AsyncTask to send impressions.""" def __init__(self, synchronize_impressions, period): """ Class constructor. :param synchronize_impressions: sender :type synchronize_impressions: func :param period: How many seconds to wait between subsequent impressions pushes to the BE. :type period: int """ self._period = period self._task = AsyncTask(synchronize_impressions, self._period, on_stop=synchronize_impressions) def start(self): """Start executing the impressions synchronization task.""" self._task.start() def stop(self, event=None): """Stop executing the impressions synchronization task.""" self._task.stop(event) def is_running(self): """ Return whether the task is running or not. :return: True if the task is running. False otherwise. :rtype: bool """ return self._task.running() def flush(self): """Flush impressions in storage.""" _LOGGER.debug('Forcing flush execution for impressions') self._task.force_execution()
class EventsSyncTask(BaseSynchronizationTask): """Events synchronization task uses an asynctask.AsyncTask to send events.""" def __init__(self, synchronize_events, period): """ Class constructor. :param synchronize_events: Events Api object to send data to the backend :type synchronize_events: splitio.api.events.EventsAPI :param period: How many seconds to wait between subsequent event pushes to the BE. :type period: int """ self._period = period self._task = AsyncTask(synchronize_events, self._period, on_stop=synchronize_events) def start(self): """Start executing the events synchronization task.""" self._task.start() def stop(self, event=None): """Stop executing the events synchronization task.""" self._task.stop(event) def flush(self): """Flush events in storage.""" _LOGGER.debug('Forcing flush execution for events') self._task.force_execution() def is_running(self): """ Return whether the task is running or not. :return: True if the task is running. False otherwise. :rtype: bool """ return self._task.running()
class TelemetrySynchronizationTask(BaseSynchronizationTask): """Split Synchronization task class.""" def __init__(self, api, storage, period): """ Class constructor. :param api: Telemetry API Client. :type api: splitio.api.telemetry.TelemetryAPI :param storage: Telemetry Storage. :type storage: splitio.storage.InMemoryTelemetryStorage """ self._logger = logging.getLogger(self.__class__.__name__) self._api = api self._period = period self._storage = storage self._task = AsyncTask(self._flush_telemetry, period) def _flush_telemetry(self): """ Send latencies, counters and gauges to split BE. :return: True if synchronization is complete. :rtype: bool """ try: latencies = self._storage.pop_latencies() if latencies: self._api.flush_latencies(latencies) except APIException: self._logger.error('Failed send telemetry/latencies to split BE.') try: counters = self._storage.pop_counters() if counters: self._api.flush_counters(counters) except APIException: self._logger.error('Failed send telemetry/counters to split BE.') try: gauges = self._storage.pop_gauges() if gauges: self._api.flush_gauges(gauges) except APIException: self._logger.error('Failed send telemetry/gauges to split BE.') def start(self): """Start the task.""" self._task.start() def stop(self, event=None): """Stop the task. Accept an optional event to set when the task has finished.""" self._task.stop(event) def is_running(self): """ Return whether the task is running. :return: True if the task is running. False otherwise. :rtype bool """ return self._task.running()
class SplitSynchronizationTask(BaseSynchronizationTask): """Split Synchronization task class.""" def __init__(self, split_api, split_storage, period, ready_flag): """ Class constructor. :param split_api: Split API Client. :type split_api: splitio.api.splits.SplitsAPI :param split_storage: Split Storage. :type split_storage: splitio.storage.InMemorySplitStorage :param ready_flag: Flag to set when splits initial sync is complete. :type ready_flag: threading.Event """ self._logger = logging.getLogger(self.__class__.__name__) self._api = split_api self._ready_flag = ready_flag self._period = period self._split_storage = split_storage self._task = AsyncTask(self._update_splits, period, self._on_start) def _update_splits(self): """ Hit endpoint, update storage and return True if sync is complete. :return: True if synchronization is complete. :rtype: bool """ till = self._split_storage.get_change_number() if till is None: till = -1 try: split_changes = self._api.fetch_splits(till) except APIException: self._logger.error('Failed to fetch split from servers') return False for split in split_changes.get('splits', []): if split['status'] == splits.Status.ACTIVE.value: self._split_storage.put(splits.from_raw(split)) else: self._split_storage.remove(split['name']) self._split_storage.set_change_number(split_changes['till']) return split_changes['till'] == split_changes['since'] def _on_start(self): """Wait until splits are in sync and set the flag to true.""" while True: ready = self._update_splits() if ready: break self._ready_flag.set() return True def start(self): """Start the task.""" self._task.start() def stop(self, event=None): """Stop the task. Accept an optional event to set when the task has finished.""" self._task.stop(event) def is_running(self): """ Return whether the task is running. :return: True if the task is running. False otherwise. :rtype bool """ return self._task.running()
class EventsSyncTask(BaseSynchronizationTask): """Events synchronization task uses an asynctask.AsyncTask to send events.""" def __init__(self, events_api, storage, period, bulk_size): """ Class constructor. :param events_api: Events Api object to send data to the backend :type events_api: splitio.api.events.EventsAPI :param storage: Events Storage :type storage: splitio.storage.EventStorage :param period: How many seconds to wait between subsequent event pushes to the BE. :type period: int :param bulk_size: How many events to send per push. :type bulk_size: int """ self._logger = logging.getLogger(self.__class__.__name__) self._events_api = events_api self._storage = storage self._period = period self._failed = queue.Queue() self._bulk_size = bulk_size self._task = AsyncTask(self._send_events, self._period, on_stop=self._send_events) def _get_failed(self): """Return up to <BULK_SIZE> events stored in the failed eventes queue.""" events = [] count = 0 while count < self._bulk_size: try: events.append(self._failed.get(False)) count += 1 except queue.Empty: # If no more items in queue, break the loop break return events def _add_to_failed_queue(self, events): """ Add events that were about to be sent to a secondary queue for failed sends. :param events: List of events that failed to be pushed. :type events: list """ for event in events: self._failed.put(event, False) def _send_events(self): """Send events from both the failed and new queues.""" to_send = self._get_failed() if len(to_send) < self._bulk_size: # If the amount of previously failed items is less than the bulk # size, try to complete with new events from storage to_send.extend( self._storage.pop_many(self._bulk_size - len(to_send))) if not to_send: return try: self._events_api.flush_events(to_send) except APIException as exc: self._logger.error( 'Exception raised while reporting events: %s -- %d', exc.message, exc.status_code) self._add_to_failed_queue(to_send) def start(self): """Start executing the events synchronization task.""" self._task.start() def stop(self, event=None): """Stop executing the events synchronization task.""" self._task.stop(event) def flush(self): """Flush events in storage.""" self._task.force_execution() def is_running(self): """ Return whether the task is running or not. :return: True if the task is running. False otherwise. :rtype: bool """ return self._task.running()