From 8714631c41f1f4602e8dc4c8997f75654b2554ea Mon Sep 17 00:00:00 2001 From: Alberto Geniola Date: Tue, 3 Nov 2020 20:02:00 +0100 Subject: [PATCH] Improved limiter logging --- .version | 2 +- meross_iot/manager.py | 41 ++++++++++++++++++++++++++++++++++------- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/.version b/.version index cc540022..34f9eeb6 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -0.4.0.6.post1 \ No newline at end of file +0.4.0.6.post2 \ No newline at end of file diff --git a/meross_iot/manager.py b/meross_iot/manager.py index a5f85f3a..56701acd 100644 --- a/meross_iot/manager.py +++ b/meross_iot/manager.py @@ -9,7 +9,7 @@ from asyncio import Future, AbstractEventLoop from asyncio import TimeoutError from hashlib import md5 -from typing import Optional, List, TypeVar, Iterable, Callable, Awaitable, Tuple +from typing import Optional, List, TypeVar, Iterable, Callable, Awaitable, Tuple, Dict import paho.mqtt.client as mqtt from meross_iot.controller.device import BaseDevice, HubDevice, GenericSubDevice from meross_iot.device_factory import build_meross_device, build_meross_subdevice @@ -19,7 +19,6 @@ from meross_iot.model.exception import UnconnectedError from meross_iot.model.http.device import HttpDeviceInfo from meross_iot.model.http.subdevice import HttpSubdeviceInfo -from meross_iot.model.push.bind import BindPushNotification from meross_iot.model.push.factory import parse_push_notification from meross_iot.model.push.generic import GenericPushNotification from meross_iot.model.push.unbind import UnbindPushNotification @@ -73,15 +72,34 @@ def _add_tokens(self): self._current_window_end = now + self._window_interval_seconds self._limit_hits_in_window = 0 + @property + def current_over_limit_hits(self) -> int: + """ + How many calls have been performed, within the time window, above the configured limit. + Those calls were possibly delayed, aborted. + :return: + """ + return self._limit_hits_in_window + @property def over_limit_percentace(self): + """ + Represents the percentage of the API calls over the configured limit, with respect to the maximum burst size. + If the burst size is 100, and in the current time window there were 150 api calls, then 50 o f them are over + the limit. This property will then return (50 / 100) * 100 -> 50%. + :return: + """ return (self._limit_hits_in_window / self._max_burst) * 100 @property def current_stats(self) -> str: perc = self._limit_hits_in_window / self._tokens_per_interval return f"Limiter window: {self._window_interval_seconds} seconds, " \ - f"HitRate: {self._limit_hits_in_window} ({perc}%)" + f"Over-limit hits: {self._limit_hits_in_window} ({perc}%)" + + @property + def current_window_hitrate(self) -> int: + return self._max_burst - self._remaining_tokens def check_limit_reached(self) -> bool: # Add tokens if needed @@ -120,8 +138,13 @@ def __init__(self, self._device_time_window = device_time_window self._device_tokens_per_interval = device_tokens_per_interval - def get_global_logger_stats(self) -> str: - return self._global_limiter.current_stats + @property + def global_rate_limiter(self) -> TokenBucketRateLimiter: + return self._global_limiter + + @property + def device_limiters(self) -> Dict[str, TokenBucketRateLimiter]: + return self._devices_limiters def check_limits(self, device_uuid) -> Tuple[RateLimitResult, float]: # Check the device limit first @@ -155,7 +178,7 @@ def __init__(self, ca_cert: Optional[str] = None, loop: Optional[AbstractEventLoop] = None, over_limit_delay_seconds: int = 1, - over_limit_threshold_percentage: float = 1000, + over_limit_threshold_percentage: float = 400, burst_requests_per_second_limit: int = 4, requests_per_second_limit: int = 1, *args, @@ -648,7 +671,11 @@ async def async_execute_cmd(self, # Check API rate limits. limit_result, overlimit_percentage = self._limiter.check_limits(device_uuid=destination_device_uuid) - _LIMITER.debug("Current stats:%s\nOver limit percentage: %f %%", self._limiter.get_global_logger_stats(), self._over_limit_threshold) + _LIMITER.debug("Number of API request within the last time-window: %s\n" + "Global over limit percentage: %f %%", + self._limiter.global_rate_limiter.current_window_hitrate, + self._limiter.global_rate_limiter.over_limit_percentace) + if limit_result != RateLimitResult.NotLimited: _LOGGER.debug(f"Current over-limit: {overlimit_percentage} %") # If the over-limit rate is too high, just drop the call.