Skip to content

Commit 8d3ec79

Browse files
authored
feat: optimize cache implementation (#1252)
1 parent 235d528 commit 8d3ec79

3 files changed

Lines changed: 29 additions & 26 deletions

File tree

src/zeroconf/_cache.pxd

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ from ._dns cimport (
44
DNSAddress,
55
DNSEntry,
66
DNSHinfo,
7+
DNSNsec,
78
DNSPointer,
89
DNSRecord,
910
DNSService,
@@ -13,7 +14,7 @@ from ._dns cimport (
1314

1415
cdef object _UNIQUE_RECORD_TYPES
1516
cdef object _TYPE_PTR
16-
cdef object _ONE_SECOND
17+
cdef cython.uint _ONE_SECOND
1718

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

@@ -27,23 +28,37 @@ cdef class DNSCache:
2728

2829
cpdef async_remove_records(self, object entries)
2930

31+
@cython.locals(
32+
store=cython.dict,
33+
)
3034
cpdef async_get_unique(self, DNSRecord entry)
3135

36+
@cython.locals(
37+
record=DNSRecord,
38+
)
39+
cpdef async_expire(self, float now)
40+
3241
@cython.locals(
3342
records=cython.dict,
3443
record=DNSRecord,
3544
)
36-
cdef _async_all_by_details(self, object name, object type_, object class_)
45+
cpdef async_all_by_details(self, str name, object type_, object class_)
3746

47+
cpdef async_entries_with_name(self, str name)
48+
49+
cpdef async_entries_with_server(self, str name)
50+
51+
@cython.locals(
52+
store=cython.dict,
53+
)
3854
cdef _async_add(self, DNSRecord record)
3955

4056
cdef _async_remove(self, DNSRecord record)
4157

42-
cpdef async_mark_unique_records_older_than_1s_to_expire(self, object unique_types, object answers, object now)
43-
4458
@cython.locals(
4559
record=DNSRecord,
60+
created_float=cython.float,
4661
)
47-
cdef _async_mark_unique_records_older_than_1s_to_expire(self, object unique_types, object answers, object now)
62+
cpdef async_mark_unique_records_older_than_1s_to_expire(self, cython.set unique_types, object answers, float now)
4863

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

src/zeroconf/_cache.py

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
USA
2121
"""
2222

23-
import itertools
2423
from typing import Dict, Iterable, List, Optional, Set, Tuple, Union, cast
2524

2625
from ._dns import (
@@ -115,12 +114,12 @@ def async_remove_records(self, entries: Iterable[DNSRecord]) -> None:
115114
for entry in entries:
116115
self._async_remove(entry)
117116

118-
def async_expire(self, now: float) -> List[DNSRecord]:
117+
def async_expire(self, now: _float) -> List[DNSRecord]:
119118
"""Purge expired entries from the cache.
120119
121120
This function must be run in from event loop.
122121
"""
123-
expired = [record for record in itertools.chain(*self.cache.values()) if record.is_expired(now)]
122+
expired = [record for records in self.cache.values() for record in records if record.is_expired(now)]
124123
self.async_remove_records(expired)
125124
return expired
126125

@@ -136,15 +135,7 @@ def async_get_unique(self, entry: _UniqueRecordsType) -> Optional[DNSRecord]:
136135
return None
137136
return store.get(entry)
138137

139-
def async_all_by_details(self, name: _str, type_: int, class_: int) -> Iterable[DNSRecord]:
140-
"""Gets all matching entries by details.
141-
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]:
138+
def async_all_by_details(self, name: _str, type_: _int, class_: _int) -> List[DNSRecord]:
148139
"""Gets all matching entries by details.
149140
150141
This function is not thread-safe and must be called from
@@ -240,20 +231,16 @@ def names(self) -> List[str]:
240231

241232
def async_mark_unique_records_older_than_1s_to_expire(
242233
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
248234
) -> None:
249235
# rfc6762#section-10.2 para 2
250236
# Since unique is set, all old records with that name, rrtype,
251237
# and rrclass that were received more than one second ago are declared
252238
# invalid, and marked to expire from the cache in one second.
253239
answers_rrset = set(answers)
254240
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:
241+
for record in self.async_all_by_details(name, type_, class_):
242+
created_float = record.created
243+
if (now - created_float > _ONE_SECOND) and record not in answers_rrset:
257244
# Expire in 1s
258245
record.set_created_ttl(now, 1)
259246

src/zeroconf/_handlers/record_manager.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,9 @@ def async_updates_from_response(self, msg: DNSIncoming) -> None:
8686
now_float = now
8787
unique_types: Set[Tuple[str, int, int]] = set()
8888
cache = self.cache
89+
answers = msg.answers
8990

90-
for record in msg.answers:
91+
for record in answers:
9192
# Protect zeroconf from records that can cause denial of service.
9293
#
9394
# We enforce a minimum TTL for PTR records to avoid
@@ -127,7 +128,7 @@ def async_updates_from_response(self, msg: DNSIncoming) -> None:
127128
removes.add(record)
128129

129130
if unique_types:
130-
cache.async_mark_unique_records_older_than_1s_to_expire(unique_types, msg.answers, now)
131+
cache.async_mark_unique_records_older_than_1s_to_expire(unique_types, answers, now)
131132

132133
if updates:
133134
self.async_updates(now, updates)

0 commit comments

Comments
 (0)