| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent f6f4a8e commit bd36427
1 file changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,14 +1,17 @@ | |||
| 1 | 1 | """Benchmarks for DNSCache.async_mark_unique_records_older_than_1s_to_expire. | |
| 2 | 2 | ||
| 3 | - Covers the RFC 6762 §10.2 paragraph 2 path that marks superseded unique | ||
| 4 | - records to expire in 1s. Today it mutates cached records in place via | ||
| 5 | - ``DNSRecord._set_created_ttl`` (see ``_cache.py`` line ~345). These | ||
| 6 | - benchmarks pin the cost of the current path so a copy-instead-of-mutate | ||
| 7 | - follow-up (issue #1780) has a baseline to compare against. | ||
| 3 | + Covers the RFC 6762 §10.2 paragraph 2 path. ``_async_set_created_ttl`` | ||
| 4 | + mutates cached records in place, so a repeated-iteration benchmark | ||
| 5 | + would only measure work on the first call. Each test uses | ||
| 6 | + ``benchmark.pedantic`` with a per-round ``setup`` that rebuilds the | ||
| 7 | + stale cache; the ``async_add_records`` cost stays outside the timed | ||
| 8 | + window. | ||
| 8 | 9 | """ | |
| 9 | 10 | ||
| 10 | 11 | from __future__ import annotations | |
| 11 | 12 | ||
| 13 | + from typing import Any | ||
| 14 | + | ||
| 12 | 15 | from pytest_codspeed import BenchmarkFixture | |
| 13 | 16 | ||
| 14 | 17 | from zeroconf import DNSAddress, DNSCache, DNSPointer, current_time_millis | |
@@ -22,89 +25,69 @@ def _ipv4_bytes(i: int) -> bytes: | |||
| 22 | 25 | ||
| 23 | 26 | ||
| 24 | 27 | def test_mark_to_expire_1000_records_all_stale(benchmark: BenchmarkFixture) -> None: | |
| 25 | - """Worst-case mark-to-expire: every cached record needs mutation. | ||
| 26 | - | ||
| 27 | - 1000 unique A records, all created > 1s ago, none in the new answer | ||
| 28 | - set — every iteration hits the ``_async_set_created_ttl`` path. | ||
| 29 | - """ | ||
| 28 | + """Worst-case mark-to-expire: 1000 stale unique A records, all mutated.""" | ||
| 30 | 29 | now = current_time_millis() | |
| 31 | 30 | name = "stale.local." | |
| 32 | - cache = DNSCache() | ||
| 33 | - cache.async_add_records( | ||
| 34 | - DNSAddress( | ||
| 35 | - name, | ||
| 36 | - _TYPE_A, | ||
| 37 | - _UNIQUE_CLASS, | ||
| 38 | - 120, | ||
| 39 | - _ipv4_bytes(i), | ||
| 40 | - created=now - 5_000, | ||
| 41 | - ) | ||
| 42 | - for i in range(1000) | ||
| 43 | - ) | ||
| 44 | 31 | unique_types = {(name, _TYPE_A, _UNIQUE_CLASS)} | |
| 45 | - # An unrelated answer keeps every cached record in the "must expire" | ||
| 32 | + # Unrelated answer keeps every cached record in the "must expire" | ||
| 46 | 33 | # branch (no membership hit short-circuits the mutation). | |
| 47 | 34 | answers = [DNSAddress(name, _TYPE_A, _UNIQUE_CLASS, 120, _ipv4_bytes(0xDEAD_BEEF))] | |
| 48 | 35 | ||
| 49 | - @benchmark | ||
| 50 | - def _mark() -> None: | ||
| 36 | + def _setup() -> tuple[tuple[Any, ...], dict[str, Any]]: | ||
| 37 | + cache = DNSCache() | ||
| 38 | + cache.async_add_records( | ||
| 39 | + DNSAddress( | ||
| 40 | + name, | ||
| 41 | + _TYPE_A, | ||
| 42 | + _UNIQUE_CLASS, | ||
| 43 | + 120, | ||
| 44 | + _ipv4_bytes(i), | ||
| 45 | + created=now - 5_000, | ||
| 46 | + ) | ||
| 47 | + for i in range(1000) | ||
| 48 | + ) | ||
| 49 | + return (cache,), {} | ||
| 50 | + | ||
| 51 | + def _mark(cache: DNSCache) -> None: | ||
| 51 | 52 | cache.async_mark_unique_records_older_than_1s_to_expire(unique_types, answers, now) | |
| 52 | 53 | ||
| 54 | + benchmark.pedantic(_mark, setup=_setup) | ||
| 53 | 55 | ||
| 54 | - def test_mark_to_expire_1000_records_none_stale(benchmark: BenchmarkFixture) -> None: | ||
| 55 | - """Same shape, but every record is fresh (created < 1s ago). | ||
| 56 | 56 | ||
| 57 | - Measures the scan + age-check overhead without paying any | ||
| 58 | - ``_async_set_created_ttl`` cost. The delta to the all-stale case is | ||
| 59 | - the mutation+re-add tax we'd avoid by switching to copy-on-expire. | ||
| 60 | - """ | ||
| 57 | + def test_mark_to_expire_1000_records_none_stale(benchmark: BenchmarkFixture) -> None: | ||
| 58 | + """Scan-only path: 1000 fresh records, no mutation.""" | ||
| 61 | 59 | now = current_time_millis() | |
| 62 | 60 | name = "fresh.local." | |
| 63 | - cache = DNSCache() | ||
| 64 | - cache.async_add_records( | ||
| 65 | - DNSAddress( | ||
| 66 | - name, | ||
| 67 | - _TYPE_A, | ||
| 68 | - _UNIQUE_CLASS, | ||
| 69 | - 120, | ||
| 70 | - _ipv4_bytes(i), | ||
| 71 | - created=now, | ||
| 72 | - ) | ||
| 73 | - for i in range(1000) | ||
| 74 | - ) | ||
| 75 | 61 | unique_types = {(name, _TYPE_A, _UNIQUE_CLASS)} | |
| 76 | 62 | answers = [DNSAddress(name, _TYPE_A, _UNIQUE_CLASS, 120, _ipv4_bytes(0xDEAD_BEEF))] | |
| 77 | 63 | ||
| 78 | - @benchmark | ||
| 79 | - def _mark() -> None: | ||
| 64 | + def _setup() -> tuple[tuple[Any, ...], dict[str, Any]]: | ||
| 65 | + cache = DNSCache() | ||
| 66 | + cache.async_add_records( | ||
| 67 | + DNSAddress( | ||
| 68 | + name, | ||
| 69 | + _TYPE_A, | ||
| 70 | + _UNIQUE_CLASS, | ||
| 71 | + 120, | ||
| 72 | + _ipv4_bytes(i), | ||
| 73 | + created=now, | ||
| 74 | + ) | ||
| 75 | + for i in range(1000) | ||
| 76 | + ) | ||
| 77 | + return (cache,), {} | ||
| 78 | + | ||
| 79 | + def _mark(cache: DNSCache) -> None: | ||
| 80 | 80 | cache.async_mark_unique_records_older_than_1s_to_expire(unique_types, answers, now) | |
| 81 | 81 | ||
| 82 | + benchmark.pedantic(_mark, setup=_setup) | ||
| 82 | 83 | ||
| 83 | - def test_mark_to_expire_many_unique_types(benchmark: BenchmarkFixture) -> None: | ||
| 84 | - """Many distinct (name, type, class) triplets, one stale record each. | ||
| 85 | 84 | ||
| 86 | - Mirrors a burst response that supersedes 100 different unique RRsets | ||
| 87 | - in one packet — the outer loop dominates, but each inner iteration | ||
| 88 | - still triggers in-place mutation. | ||
| 89 | - """ | ||
| 85 | + def test_mark_to_expire_many_unique_types(benchmark: BenchmarkFixture) -> None: | ||
| 86 | + """100 distinct (name, type, class) triplets, one stale record each.""" | ||
| 90 | 87 | now = current_time_millis() | |
| 91 | - cache = DNSCache() | ||
| 92 | - unique_types: set[tuple[str, int, int]] = set() | ||
| 93 | - for i in range(100): | ||
| 94 | - name = f"svc{i}.local." | ||
| 95 | - cache.async_add_records( | ||
| 96 | - [ | ||
| 97 | - DNSPointer( | ||
| 98 | - name, | ||
| 99 | - _TYPE_PTR, | ||
| 100 | - _UNIQUE_CLASS, | ||
| 101 | - 120, | ||
| 102 | - f"target{i}.local.", | ||
| 103 | - created=now - 5_000, | ||
| 104 | - ) | ||
| 105 | - ] | ||
| 106 | - ) | ||
| 107 | - unique_types.add((name, _TYPE_PTR, _UNIQUE_CLASS)) | ||
| 88 | + unique_types: set[tuple[str, int, int]] = { | ||
| 89 | + (f"svc{i}.local.", _TYPE_PTR, _UNIQUE_CLASS) for i in range(100) | ||
| 90 | + } | ||
| 108 | 91 | # New answers reference a different alias, so the cached entries are | |
| 109 | 92 | # not equal to anything in ``answers_rrset`` and must be expired. | |
| 110 | 93 | answers = [ | |
@@ -118,6 +101,24 @@ def test_mark_to_expire_many_unique_types(benchmark: BenchmarkFixture) -> None: | |||
| 118 | 101 | for i in range(100) | |
| 119 | 102 | ] | |
| 120 | 103 | ||
| 121 | - @benchmark | ||
| 122 | - def _mark() -> None: | ||
| 104 | + def _setup() -> tuple[tuple[Any, ...], dict[str, Any]]: | ||
| 105 | + cache = DNSCache() | ||
| 106 | + for i in range(100): | ||
| 107 | + cache.async_add_records( | ||
| 108 | + [ | ||
| 109 | + DNSPointer( | ||
| 110 | + f"svc{i}.local.", | ||
| 111 | + _TYPE_PTR, | ||
| 112 | + _UNIQUE_CLASS, | ||
| 113 | + 120, | ||
| 114 | + f"target{i}.local.", | ||
| 115 | + created=now - 5_000, | ||
| 116 | + ) | ||
| 117 | + ] | ||
| 118 | + ) | ||
| 119 | + return (cache,), {} | ||
| 120 | + | ||
| 121 | + def _mark(cache: DNSCache) -> None: | ||
| 123 | 122 | cache.async_mark_unique_records_older_than_1s_to_expire(unique_types, answers, now) | |
| 123 | + | ||
| 124 | + benchmark.pedantic(_mark, setup=_setup) | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments