Skip to content

perf(registry): use insertion-ordered dicts for O(1) removal#1786

Draft
bluetoothbot wants to merge 2 commits into
python-zeroconf:masterfrom
bluetoothbot:koan/fix-issue-1781
Draft

perf(registry): use insertion-ordered dicts for O(1) removal#1786
bluetoothbot wants to merge 2 commits into
python-zeroconf:masterfrom
bluetoothbot:koan/fix-issue-1781

Conversation

@bluetoothbot

@bluetoothbot bluetoothbot commented May 26, 2026

Copy link
Copy Markdown
Contributor

Summary

ServiceRegistry._remove was O(n) per call because the per-type and per-server indices were stored as list[str], so bulk async_remove of N services sharing a type/server degraded to O(N²) — visible at shutdown for deployments with many entries under one _type._tcp.local.. Switch the value type to dict[str, None], which preserves insertion order (so async_get_infos_type / async_get_infos_server still return entries in registration order) while giving O(1) add and remove. Also delete empty buckets so long-lived Zeroconf instances with churning type/server names don't leak dict keys.

Closes #1781

Changes

  • src/zeroconf/_services/registry.py: types and servers are now dict[str, dict[str, None]]; _add uses setdefault(...)[key] = None; _remove does del bucket[key] and cleans up the bucket when empty.
  • src/zeroconf/_services/registry.pxd: updated cython.locals to reflect the new dict-of-dicts shape.
  • tests/services/test_registry.py: added three tests pinning empty-bucket cleanup, insertion-order preservation under bulk removal, and add-after-bucket-deletion.

Test plan

  • poetry run pytest tests/services/test_registry.py -v — 8 passed (3 new tests fail without the fix).
  • poetry run pytest tests/ --timeout=60 -q — 395 passed, 2 skipped, 3 xfailed, 4 xpassed.
  • poetry run ruff check + ruff format --check on the touched files — clean.
  • cythonize src/zeroconf/_services/registry.py — compiles with no new warnings.

Generated by Kōan


Quality Report

Changes: 3 files changed, 104 insertions(+), 12 deletions(-)

Code scan: clean

Tests: failed (4 failed, 4 PASSED)

Branch hygiene: clean

Generated by Kōan post-mission quality pipeline

ServiceRegistry's per-type and per-server indices were stored as
list[str], so each list.remove(...) call in _remove was an O(n)
linear scan. Bulk async_remove of N services sharing a type or
server therefore degraded to O(N**2) — visible at shutdown for
deployments with many entries under one _type._tcp.local.

Switch the value type to dict[str, None], which preserves insertion
order (so async_get_infos_type / async_get_infos_server still return
entries in registration order) while giving O(1) add and remove.

Also delete empty buckets once their last entry is removed so that
long-lived Zeroconf instances with churning type / server names
don't leak dict keys.
@codecov

codecov Bot commented May 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.77%. Comparing base (44433dd) to head (a4e0bdc).

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #1786   +/-   ##
=======================================
  Coverage   99.77%   99.77%           
=======================================
  Files          33       33           
  Lines        3536     3544    +8     
  Branches      498      500    +2     
=======================================
+ Hits         3528     3536    +8     
  Misses          5        5           
  Partials        3        3           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@codspeed-hq

codspeed-hq Bot commented May 26, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 18 untouched benchmarks


Comparing bluetoothbot:koan/fix-issue-1781 (a4e0bdc) with master (44433dd)

Open in CodSpeed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

1 participant