Improve Huawei LTE timeouting/stalling request behavior (#31710)

* Suppress data get timeout exceptions for 30s after notify attempts

At least SMS send failures may put the API in a state where it times out
some (but not all) data get operations for some time, e.g. 25s.

Closes https://github.com/home-assistant/home-assistant/issues/30827

* Do not pile up duplicate data requests

Do not add another request for a piece of data for which a previous
request is still in progress. For example failing SMS sends are known to
stall some (but not all) requests for some time, and firing up more is
not going to help.
pull/31735/head
Ville Skyttä 2020-02-12 03:54:19 +02:00 committed by GitHub
parent 54eb740ff6
commit d1b0ab736d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 26 additions and 0 deletions

View File

@ -5,6 +5,7 @@ from datetime import timedelta
from functools import partial
import ipaddress
import logging
import time
from typing import Any, Callable, Dict, List, Set, Tuple
from urllib.parse import urlparse
@ -65,6 +66,7 @@ from .const import (
KEY_MONITORING_STATUS,
KEY_MONITORING_TRAFFIC_STATISTICS,
KEY_WLAN_HOST_LIST,
NOTIFY_SUPPRESS_TIMEOUT,
SERVICE_CLEAR_TRAFFIC_STATISTICS,
SERVICE_REBOOT,
SERVICE_RESUME_INTEGRATION,
@ -138,9 +140,11 @@ class Router:
init=False,
factory=lambda: defaultdict(set, ((x, {"initial_scan"}) for x in ALL_KEYS)),
)
inflight_gets: Set[str] = attr.ib(init=False, factory=set)
unload_handlers: List[CALLBACK_TYPE] = attr.ib(init=False, factory=list)
client: Client
suspended = attr.ib(init=False, default=False)
notify_last_attempt: float = attr.ib(init=False, default=-1)
def __attrs_post_init__(self):
"""Set up internal state on init."""
@ -167,6 +171,10 @@ class Router:
def _get_data(self, key: str, func: Callable[[None], Any]) -> None:
if not self.subscriptions.get(key):
return
if key in self.inflight_gets:
_LOGGER.debug("Skipping already inflight get for %s", key)
return
self.inflight_gets.add(key)
_LOGGER.debug("Getting %s for subscribers %s", key, self.subscriptions[key])
try:
self.data[key] = func()
@ -189,7 +197,21 @@ class Router:
"%s requires authorization, excluding from future updates", key
)
self.subscriptions.pop(key)
except Timeout:
grace_left = (
self.notify_last_attempt - time.monotonic() + NOTIFY_SUPPRESS_TIMEOUT
)
if grace_left > 0:
_LOGGER.debug(
"%s timed out, %.1fs notify timeout suppress grace remaining",
key,
grace_left,
exc_info=True,
)
else:
raise
finally:
self.inflight_gets.discard(key)
_LOGGER.debug("%s=%s", key, self.data.get(key))
def update(self) -> None:

View File

@ -12,6 +12,7 @@ UNIT_BYTES = "B"
UNIT_SECONDS = "s"
CONNECTION_TIMEOUT = 10
NOTIFY_SUPPRESS_TIMEOUT = 30
SERVICE_CLEAR_TRAFFIC_STATISTICS = "clear_traffic_statistics"
SERVICE_REBOOT = "reboot"

View File

@ -1,6 +1,7 @@
"""Support for Huawei LTE router notifications."""
import logging
import time
from typing import Any, List
import attr
@ -57,3 +58,5 @@ class HuaweiLteSmsNotificationService(BaseNotificationService):
_LOGGER.debug("Sent to %s: %s", targets, resp)
except ResponseErrorException as ex:
_LOGGER.error("Could not send to %s: %s", targets, ex)
finally:
self.router.notify_last_attempt = time.monotonic()