feat: cache construction of records used to answer queries from the s… · python-zeroconf/python-zeroconf@0890f62
@@ -133,6 +133,11 @@ class ServiceInfo(RecordUpdateListener):
133133"other_ttl",
134134"interface_index",
135135"_new_records_futures",
136+"_dns_pointer_cache",
137+"_dns_service_cache",
138+"_dns_text_cache",
139+"_dns_address_cache",
140+"_get_address_and_nsec_records_cache",
136141 )
137142138143def __init__(
@@ -180,6 +185,11 @@ def __init__(
180185self.other_ttl = other_ttl
181186self.interface_index = interface_index
182187self._new_records_futures: Set[asyncio.Future] = set()
188+self._dns_address_cache: Optional[List[DNSAddress]] = None
189+self._dns_pointer_cache: Optional[DNSPointer] = None
190+self._dns_service_cache: Optional[DNSService] = None
191+self._dns_text_cache: Optional[DNSText] = None
192+self._get_address_and_nsec_records_cache: Optional[Set[DNSRecord]] = None
183193184194@property
185195def name(self) -> str:
@@ -191,6 +201,9 @@ def name(self, name: str) -> None:
191201"""Replace the the name and reset the key."""
192202self._name = name
193203self.key = name.lower()
204+self._dns_service_cache = None
205+self._dns_pointer_cache = None
206+self._dns_text_cache = None
194207195208@property
196209def addresses(self) -> List[bytes]:
@@ -210,6 +223,8 @@ def addresses(self, value: List[bytes]) -> None:
210223 """
211224self._ipv4_addresses.clear()
212225self._ipv6_addresses.clear()
226+self._dns_address_cache = None
227+self._get_address_and_nsec_records_cache = None
213228214229for address in value:
215230try:
@@ -489,42 +504,56 @@ def dns_addresses(
489504self,
490505override_ttl: Optional[int] = None,
491506version: IPVersion = IPVersion.All,
492-created: Optional[float] = None,
493507 ) -> List[DNSAddress]:
494508"""Return matching DNSAddress from ServiceInfo."""
509+cacheable = version is IPVersion.All and override_ttl is None
510+if self._dns_address_cache is not None and cacheable:
511+return self._dns_address_cache
495512name = self.server or self._name
496513ttl = override_ttl if override_ttl is not None else self.host_ttl
497514class_ = _CLASS_IN_UNIQUE
498515version_value = version.value
499-return [
516+records = [
500517DNSAddress(
501518name,
502519_TYPE_AAAA if type(ip_addr) is IPv6Address else _TYPE_A,
503520class_,
504521ttl,
505522ip_addr.packed,
506-created=created,
523+created=0.0,
507524 )
508525for ip_addr in self._ip_addresses_by_version_value(version_value)
509526 ]
527+if cacheable:
528+self._dns_address_cache = records
529+return records
510530511-def dns_pointer(self, override_ttl: Optional[int] = None, created: Optional[float] = None) -> DNSPointer:
531+def dns_pointer(self, override_ttl: Optional[int] = None) -> DNSPointer:
512532"""Return DNSPointer from ServiceInfo."""
513-return DNSPointer(
533+cacheable = override_ttl is None
534+if self._dns_pointer_cache is not None and cacheable:
535+return self._dns_pointer_cache
536+record = DNSPointer(
514537self.type,
515538_TYPE_PTR,
516539_CLASS_IN,
517540override_ttl if override_ttl is not None else self.other_ttl,
518541self._name,
519-created,
542+0.0,
520543 )
544+if cacheable:
545+self._dns_pointer_cache = record
546+return record
521547522-def dns_service(self, override_ttl: Optional[int] = None, created: Optional[float] = None) -> DNSService:
548+def dns_service(self, override_ttl: Optional[int] = None) -> DNSService:
523549"""Return DNSService from ServiceInfo."""
550+cacheable = override_ttl is None
551+if self._dns_service_cache is not None and cacheable:
552+return self._dns_service_cache
524553port = self.port
525554if TYPE_CHECKING:
526555assert isinstance(port, int)
527-return DNSService(
556+record = DNSService(
528557self._name,
529558_TYPE_SRV,
530559_CLASS_IN_UNIQUE,
@@ -533,23 +562,30 @@ def dns_service(self, override_ttl: Optional[int] = None, created: Optional[floa
533562self.weight,
534563port,
535564self.server or self._name,
536-created,
565+0.0,
537566 )
567+if cacheable:
568+self._dns_service_cache = record
569+return record
538570539-def dns_text(self, override_ttl: Optional[int] = None, created: Optional[float] = None) -> DNSText:
571+def dns_text(self, override_ttl: Optional[int] = None) -> DNSText:
540572"""Return DNSText from ServiceInfo."""
541-return DNSText(
573+cacheable = override_ttl is None
574+if self._dns_text_cache is not None and cacheable:
575+return self._dns_text_cache
576+record = DNSText(
542577self._name,
543578_TYPE_TXT,
544579_CLASS_IN_UNIQUE,
545580override_ttl if override_ttl is not None else self.other_ttl,
546581self.text,
547-created,
582+0.0,
548583 )
584+if cacheable:
585+self._dns_text_cache = record
586+return record
549587550-def dns_nsec(
551-self, missing_types: List[int], override_ttl: Optional[int] = None, created: Optional[float] = None
552- ) -> DNSNsec:
588+def dns_nsec(self, missing_types: List[int], override_ttl: Optional[int] = None) -> DNSNsec:
553589"""Return DNSNsec from ServiceInfo."""
554590return DNSNsec(
555591self._name,
@@ -558,21 +594,24 @@ def dns_nsec(
558594override_ttl if override_ttl is not None else self.host_ttl,
559595self._name,
560596missing_types,
561-created,
597+0.0,
562598 )
563599564-def get_address_and_nsec_records(
565-self, override_ttl: Optional[int] = None, created: Optional[float] = None
566- ) -> Set[DNSRecord]:
600+def get_address_and_nsec_records(self, override_ttl: Optional[int] = None) -> Set[DNSRecord]:
567601"""Build a set of address records and NSEC records for non-present record types."""
602+cacheable = override_ttl is None
603+if self._get_address_and_nsec_records_cache is not None and cacheable:
604+return self._get_address_and_nsec_records_cache
568605missing_types: Set[int] = _ADDRESS_RECORD_TYPES.copy()
569606records: Set[DNSRecord] = set()
570-for dns_address in self.dns_addresses(override_ttl, IPVersion.All, created):
607+for dns_address in self.dns_addresses(override_ttl, IPVersion.All):
571608missing_types.discard(dns_address.type)
572609records.add(dns_address)
573610if missing_types:
574611assert self.server is not None, "Service server must be set for NSEC record."
575-records.add(self.dns_nsec(list(missing_types), override_ttl, created))
612+records.add(self.dns_nsec(list(missing_types), override_ttl))
613+if cacheable:
614+self._get_address_and_nsec_records_cache = records
576615return records
577616578617def _get_address_records_from_cache_by_type(self, zc: 'Zeroconf', _type: int) -> List[DNSAddress]: