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

)

137142138143

def __init__(

@@ -180,6 +185,11 @@ def __init__(

180185

self.other_ttl = other_ttl

181186

self.interface_index = interface_index

182187

self._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

185195

def name(self) -> str:

@@ -191,6 +201,9 @@ def name(self, name: str) -> None:

191201

"""Replace the the name and reset the key."""

192202

self._name = name

193203

self.key = name.lower()

204+

self._dns_service_cache = None

205+

self._dns_pointer_cache = None

206+

self._dns_text_cache = None

194207195208

@property

196209

def addresses(self) -> List[bytes]:

@@ -210,6 +223,8 @@ def addresses(self, value: List[bytes]) -> None:

210223

"""

211224

self._ipv4_addresses.clear()

212225

self._ipv6_addresses.clear()

226+

self._dns_address_cache = None

227+

self._get_address_and_nsec_records_cache = None

213228214229

for address in value:

215230

try:

@@ -489,42 +504,56 @@ def dns_addresses(

489504

self,

490505

override_ttl: Optional[int] = None,

491506

version: 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

495512

name = self.server or self._name

496513

ttl = override_ttl if override_ttl is not None else self.host_ttl

497514

class_ = _CLASS_IN_UNIQUE

498515

version_value = version.value

499-

return [

516+

records = [

500517

DNSAddress(

501518

name,

502519

_TYPE_AAAA if type(ip_addr) is IPv6Address else _TYPE_A,

503520

class_,

504521

ttl,

505522

ip_addr.packed,

506-

created=created,

523+

created=0.0,

507524

)

508525

for 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(

514537

self.type,

515538

_TYPE_PTR,

516539

_CLASS_IN,

517540

override_ttl if override_ttl is not None else self.other_ttl,

518541

self._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

524553

port = self.port

525554

if TYPE_CHECKING:

526555

assert isinstance(port, int)

527-

return DNSService(

556+

record = DNSService(

528557

self._name,

529558

_TYPE_SRV,

530559

_CLASS_IN_UNIQUE,

@@ -533,23 +562,30 @@ def dns_service(self, override_ttl: Optional[int] = None, created: Optional[floa

533562

self.weight,

534563

port,

535564

self.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(

542577

self._name,

543578

_TYPE_TXT,

544579

_CLASS_IN_UNIQUE,

545580

override_ttl if override_ttl is not None else self.other_ttl,

546581

self.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."""

554590

return DNSNsec(

555591

self._name,

@@ -558,21 +594,24 @@ def dns_nsec(

558594

override_ttl if override_ttl is not None else self.host_ttl,

559595

self._name,

560596

missing_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

568605

missing_types: Set[int] = _ADDRESS_RECORD_TYPES.copy()

569606

records: 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):

571608

missing_types.discard(dns_address.type)

572609

records.add(dns_address)

573610

if missing_types:

574611

assert 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

576615

return records

577616578617

def _get_address_records_from_cache_by_type(self, zc: 'Zeroconf', _type: int) -> List[DNSAddress]: