Skip to content

Commit b47fef3

Browse files
committed
Reduce chance of accidental synchronization of ServiceInfo requests
- ServiceInfo requests are frequently triggered by multicast responses to ServiceBrowser requests. When multiple instances of zeroconf are present on the same network, they can all end up sending ServiceInfo queries at the same time. We now use a random delay as described in rfc6762 sec 5.2 after the first request. Ideally we would add the delay before the first query as well, however that may break existing workflows so it was not done at this time.
1 parent 5fb3e20 commit b47fef3

1 file changed

Lines changed: 15 additions & 2 deletions

File tree

zeroconf/_services/info.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"""
2222

2323
import ipaddress
24+
import random
2425
import socket
2526
from typing import Any, Dict, List, Optional, TYPE_CHECKING, Union, cast
2627

@@ -52,6 +53,16 @@
5253
)
5354

5455

56+
# https://datatracker.ietf.org/doc/html/rfc6762#section-5.2
57+
# The most common case for calling ServiceInfo is from a
58+
# ServiceBrowser. After the first request we add a few random
59+
# milliseconds to the delay between requests to reduce the chance
60+
# that there are multiple ServiceBrowser callbacks running on
61+
# the network that are firing at the same time when they
62+
# see the same multicast response and decide to refresh
63+
# the A/AAAA/SRV records for a host.
64+
_AVOID_SYNC_DELAY_RANDOM_INTERVAL = (20, 120)
65+
5566
if TYPE_CHECKING:
5667
from .._core import Zeroconf
5768

@@ -446,15 +457,17 @@ async def async_request(
446457
if last <= now:
447458
return False
448459
if next_ <= now:
449-
out = self.generate_request_query(
450-
zc, now, question_type or DNSQuestionType.QU if first_request else DNSQuestionType.QM
460+
this_question_type = (
461+
question_type or DNSQuestionType.QU if first_request else DNSQuestionType.QM
451462
)
463+
out = self.generate_request_query(zc, now, this_question_type)
452464
first_request = False
453465
if not out.questions:
454466
return self.load_from_cache(zc)
455467
zc.async_send(out)
456468
next_ = now + delay
457469
delay *= 2
470+
next_ += random.randint(*_AVOID_SYNC_DELAY_RANDOM_INTERVAL)
458471

459472
await zc.async_wait(min(next_, last) - now)
460473
now = current_time_millis()

0 commit comments

Comments
 (0)