Skip to content

optimization: ServiceRegistry removal is O(n) per call because types/servers are lists #1781

Description

@bluetoothbot

Problem

self.types: dict[str, list] and self.servers: dict[str, list] store the per-key membership as Python lists. _remove calls self.types[old_service_info.type.lower()].remove(info.key) and self.servers[old_service_info.server_key].remove(info.key), each of which is an O(n) linear scan. For deployments registering many services under the same _type._tcp.local. (e.g. Home Assistant printing ~100 devices, or Avahi-style mass advertisements), bulk async_remove([...]) of N services degrades to O(N²) — measurable at shutdown / restart, and exactly the moment users notice slow teardown. The _add side is fine (setdefault(...).append(...) is O(1)), so the asymmetry is purely on the removal hot path. Also: when the last entry under a type / server is removed, the empty list lingers in the dict forever (no del self.types[...] cleanup), so long-lived Zeroconf instances with churning type names grow these dicts unboundedly.

Why This Matters

The registry sits behind every service announcement and removal; quadratic removal is a real latency hit during shutdowns and at any large-scale rotation. The dict-leak is small but unbounded in the wrong workload.

Suggested Fix

Switch the value type to dict[str, set[str]] (or dict[str, dict[str, None]] if insertion-order iteration matters anywhere — check call sites of async_get_infos_type / async_get_infos_server). _add becomes self.types.setdefault(...).add(info.key); _remove becomes bucket = self.types[key]; bucket.discard(info.key); if not bucket: del self.types[key]. The same change for self.servers. Both set.discard and dict-key removal are O(1). _async_get_by_index keeps returning a list[ServiceInfo] — just iterate the set.

Details

Severity 🟡 Medium
Category optimization
Location src/zeroconf/_services/registry.py:38-112
Effort ⚡ Quick fix

🤖 Created by Kōan from audit session

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions