Skip to content

Commit 844715d

Browse files
authored
Add explicit timeout for subscriber creation (#1029)
* Add explicit timeout for subscriber creation * Bump version to 4.0.5
1 parent 9ead159 commit 844715d

File tree

3 files changed

+53
-5
lines changed

3 files changed

+53
-5
lines changed

google_nest_sdm/google_nest_subscriber.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@
5959

6060
MESSAGE_ACK_TIMEOUT_SECONDS = 30.0
6161

62+
NEW_SUBSCRIBER_TIMEOUT_SECONDS = 30.0
63+
6264
# Note: Users of non-prod instances will have to manually configure a topic
6365
TOPIC_FORMAT = "projects/sdm-prod/topics/enterprise-{project_id}"
6466

@@ -294,14 +296,17 @@ def callback_wrapper(message: pubsub_v1.subscriber.message.Message) -> None:
294296
callback_wrapper,
295297
)
296298

299+
297300
def _new_subscriber(
298301
self,
299302
creds: Credentials,
300303
subscription_name: str,
301304
callback_wrapper: Callable[[pubsub_v1.subscriber.message.Message], None],
302305
) -> pubsub_v1.subscriber.futures.StreamingPullFuture:
303306
"""Issue a command to verify subscriber creds are correct."""
307+
_LOGGER.debug("Creating subscriber '%s'", subscription_name)
304308
creds = refresh_creds(creds)
309+
_LOGGER.debug("Subscriber credentials refreshed")
305310
subscriber = pubsub_v1.SubscriberClient(credentials=creds)
306311
subscription = subscriber.get_subscription(subscription=subscription_name)
307312
if subscription.topic:
@@ -311,6 +316,7 @@ def _new_subscriber(
311316
subscription_name,
312317
subscription.topic,
313318
)
319+
_LOGGER.debug("Starting subscriber '%s'", subscription_name)
314320
return subscriber.subscribe(subscription_name, callback_wrapper)
315321

316322

@@ -432,11 +438,17 @@ async def start_async(self) -> None:
432438
raise AuthException(f"Access token failure: {err}") from err
433439

434440
try:
435-
self._subscriber_future = (
436-
await self._subscriber_factory.async_new_subscriber(
437-
creds, self._subscriber_id, self._loop, self._async_message_callback_with_timeout
441+
async with asyncio.timeout(NEW_SUBSCRIBER_TIMEOUT_SECONDS):
442+
self._subscriber_future = (
443+
await self._subscriber_factory.async_new_subscriber(
444+
creds, self._subscriber_id, self._loop, self._async_message_callback_with_timeout
445+
)
438446
)
439-
)
447+
except asyncio.TimeoutError as err:
448+
DIAGNOSTICS.increment("start.timeout_error")
449+
raise SubscriberException(
450+
f"Failed to create subscriber '{self._subscriber_id}': {err}"
451+
) from err
440452
except NotFound as err:
441453
DIAGNOSTICS.increment("start.not_found_error")
442454
raise ConfigurationException(

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = google_nest_sdm
3-
version = 4.0.4
3+
version = 4.0.5
44
description = Library for the Google Nest SDM API
55
long_description = file: README.md
66
long_description_content_type = text/markdown

tests/test_google_nest_subscriber.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,42 @@ async def task2() -> None:
307307
subscriber.stop_async()
308308

309309

310+
async def test_subscriber_timeout(
311+
app: aiohttp.web.Application,
312+
device_handler: DeviceHandler,
313+
structure_handler: StructureHandler,
314+
subscriber_client: Callable[
315+
[Optional[AbstractSubscriberFactory]], Awaitable[GoogleNestSubscriber]
316+
],
317+
) -> None:
318+
class FailingFactory(FakeSubscriberFactory):
319+
async def async_new_subscriber(
320+
self,
321+
creds: Credentials,
322+
subscription_name: str,
323+
loop: asyncio.AbstractEventLoop,
324+
async_callback: Callable[
325+
[pubsub_v1.subscriber.message.Message], Awaitable[None]
326+
],
327+
) -> pubsub_v1.subscriber.futures.StreamingPullFuture:
328+
raise asyncio.TimeoutError("Some error")
329+
330+
subscriber = await subscriber_client(FailingFactory())
331+
332+
with pytest.raises(SubscriberException):
333+
await subscriber.start_async()
334+
subscriber.stop_async()
335+
336+
assert_diagnostics(
337+
diagnostics.get_diagnostics(),
338+
{
339+
"subscriber": {
340+
"start": 1,
341+
"start.timeout_error": 1,
342+
"stop": 1,
343+
},
344+
},
345+
)
310346
async def test_subscriber_error(
311347
app: aiohttp.web.Application,
312348
device_handler: DeviceHandler,

0 commit comments

Comments
 (0)