Skip to content

Commit d919316

Browse files
authored
feat: speed up processing incoming records (#1179)
1 parent 74d7ba1 commit d919316

3 files changed

Lines changed: 51 additions & 24 deletions

File tree

src/zeroconf/_cache.pxd

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ from ._dns cimport (
1313

1414
cdef object _UNIQUE_RECORD_TYPES
1515
cdef object _TYPE_PTR
16+
cdef object _ONE_SECOND
1617

1718
cdef _remove_key(cython.dict cache, object key, DNSRecord record)
1819

@@ -22,9 +23,19 @@ cdef class DNSCache:
2223
cdef public cython.dict cache
2324
cdef public cython.dict service_cache
2425

26+
@cython.locals(
27+
records=cython.dict,
28+
record=DNSRecord,
29+
)
30+
cdef _async_all_by_details(self, object name, object type_, object class_)
31+
2532
cdef _async_add(self, DNSRecord record)
2633

2734
cdef _async_remove(self, DNSRecord record)
2835

36+
@cython.locals(
37+
record=DNSRecord,
38+
)
39+
cdef _async_mark_unique_records_older_than_1s_to_expire(self, object unique_types, object answers, object now)
2940

3041
cdef _dns_record_matches(DNSRecord record, object key, object type_, object class_)

src/zeroconf/_cache.py

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

2323
import itertools
24-
from typing import Dict, Iterable, Iterator, List, Optional, Union, cast
24+
from typing import Dict, Iterable, List, Optional, Set, Tuple, Union, cast
2525

2626
from ._dns import (
2727
DNSAddress,
@@ -34,13 +34,15 @@
3434
DNSText,
3535
)
3636
from ._utils.time import current_time_millis
37-
from .const import _TYPE_PTR
37+
from .const import _ONE_SECOND, _TYPE_PTR
3838

3939
_UNIQUE_RECORD_TYPES = (DNSAddress, DNSHinfo, DNSPointer, DNSText, DNSService)
4040
_UniqueRecordsType = Union[DNSAddress, DNSHinfo, DNSPointer, DNSText, DNSService]
4141
_DNSRecordCacheType = Dict[str, Dict[DNSRecord, DNSRecord]]
4242
_DNSRecord = DNSRecord
4343
_str = str
44+
_float = float
45+
_int = int
4446

4547

4648
def _remove_key(cache: _DNSRecordCacheType, key: _str, record: _DNSRecord) -> None:
@@ -134,19 +136,29 @@ def async_get_unique(self, entry: _UniqueRecordsType) -> Optional[DNSRecord]:
134136
return None
135137
return store.get(entry)
136138

137-
def async_all_by_details(self, name: _str, type_: int, class_: int) -> Iterator[DNSRecord]:
139+
def async_all_by_details(self, name: _str, type_: int, class_: int) -> Iterable[DNSRecord]:
138140
"""Gets all matching entries by details.
139141
140-
This function is not threadsafe and must be called from
142+
This function is not thread-safe and must be called from
143+
the event loop.
144+
"""
145+
return self._async_all_by_details(name, type_, class_)
146+
147+
def _async_all_by_details(self, name: _str, type_: int, class_: int) -> List[DNSRecord]:
148+
"""Gets all matching entries by details.
149+
150+
This function is not thread-safe and must be called from
141151
the event loop.
142152
"""
143153
key = name.lower()
144154
records = self.cache.get(key)
155+
matches: List[DNSRecord] = []
145156
if records is None:
146-
return
147-
for entry in records:
148-
if _dns_record_matches(entry, key, type_, class_):
149-
yield entry
157+
return matches
158+
for record in records:
159+
if _dns_record_matches(record, key, type_, class_):
160+
matches.append(record)
161+
return matches
150162

151163
def async_entries_with_name(self, name: str) -> Dict[DNSRecord, DNSRecord]:
152164
"""Returns a dict of entries whose key matches the name.
@@ -226,6 +238,25 @@ def names(self) -> List[str]:
226238
"""Return a copy of the list of current cache names."""
227239
return list(self.cache)
228240

241+
def async_mark_unique_records_older_than_1s_to_expire(
242+
self, unique_types: Set[Tuple[_str, _int, _int]], answers: Iterable[DNSRecord], now: _float
243+
) -> None:
244+
self._async_mark_unique_records_older_than_1s_to_expire(unique_types, answers, now)
245+
246+
def _async_mark_unique_records_older_than_1s_to_expire(
247+
self, unique_types: Set[Tuple[_str, _int, _int]], answers: Iterable[DNSRecord], now: _float
248+
) -> None:
249+
# rfc6762#section-10.2 para 2
250+
# Since unique is set, all old records with that name, rrtype,
251+
# and rrclass that were received more than one second ago are declared
252+
# invalid, and marked to expire from the cache in one second.
253+
answers_rrset = set(answers)
254+
for name, type_, class_ in unique_types:
255+
for record in self._async_all_by_details(name, type_, class_):
256+
if (now - record.created > _ONE_SECOND) and record not in answers_rrset:
257+
# Expire in 1s
258+
record.set_created_ttl(now, 1)
259+
229260

230261
def _dns_record_matches(record: _DNSRecord, key: _str, type_: int, class_: int) -> bool:
231262
return key == record.key and type_ == record.type and class_ == record.class_

src/zeroconf/_handlers.py

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
from typing import (
2727
TYPE_CHECKING,
2828
Dict,
29-
Iterable,
3029
List,
3130
NamedTuple,
3231
Optional,
@@ -421,7 +420,7 @@ def async_updates_from_response(self, msg: DNSIncoming) -> None:
421420
removes.add(record)
422421

423422
if unique_types:
424-
self._async_mark_unique_cached_records_older_than_1s_to_expire(unique_types, msg.answers, now)
423+
self.cache.async_mark_unique_records_older_than_1s_to_expire(unique_types, msg.answers, now)
425424

426425
if updates:
427426
self.async_updates(now, updates)
@@ -451,20 +450,6 @@ def async_updates_from_response(self, msg: DNSIncoming) -> None:
451450
if updates:
452451
self.async_updates_complete(new)
453452

454-
def _async_mark_unique_cached_records_older_than_1s_to_expire(
455-
self, unique_types: Set[Tuple[str, int, int]], answers: Iterable[DNSRecord], now: float
456-
) -> None:
457-
# rfc6762#section-10.2 para 2
458-
# Since unique is set, all old records with that name, rrtype,
459-
# and rrclass that were received more than one second ago are declared
460-
# invalid, and marked to expire from the cache in one second.
461-
answers_rrset = set(answers)
462-
for name, type_, class_ in unique_types:
463-
for entry in self.cache.async_all_by_details(name, type_, class_):
464-
if (now - entry.created > _ONE_SECOND) and entry not in answers_rrset:
465-
# Expire in 1s
466-
entry.set_created_ttl(now, 1)
467-
468453
def async_add_listener(
469454
self, listener: RecordUpdateListener, question: Optional[Union[DNSQuestion, List[DNSQuestion]]]
470455
) -> None:

0 commit comments

Comments
 (0)