fix(cache): refresh honors forwarding rules (#147) #150

Merged
razvandimescu merged 1 commits from fix/refresh-honors-forwarding-rules into main 2026-04-25 23:26:48 +08:00
razvandimescu commented 2026-04-25 00:04:19 +08:00 (Migrated from github.com)

Summary

  • refresh_entry now consults match_forwarding_rule before falling through to recursive/default-upstream resolution, matching the precedence already enforced in resolve_query.
  • Without this, the first cache refresh for a forwarded name (NearExpiry at 90% TTL, or Stale within the 3600s serve-stale window) re-resolved the private zone through the public default upstream, which returned NXDOMAIN/NODATA and poisoned the cache for at least cache.min_ttl (60s default). On a busy resolver the cycle was continuous; a restart restored service until the next refresh. Reporter in #147 observed this for both a private TLD and its in-addr.arpa zone, and worked around it with hardcoded zone entries (which win over cache in the pipeline).
  • Added two regression tests: refresh_entry_honors_forwarding_rule (default pool points at a blackhole — only the rule's mock upstream can satisfy the refresh) and refresh_entry_prefers_forwarding_rule_over_recursive (Recursive mode with empty root_hints — recursion would fail immediately, proving the rule branch fired).
  • Extracted testutil::a_record_response() for the mock-response boilerplate and migrated six call sites (two new + four adjacent tests).

Test plan

  • cargo test --lib ctx::tests — 33/33 pass, including both new regression tests
  • make all — fmt, clippy, audit, build, full suite (356 unit + 1 integration)
  • Verified by having a long-running instance under load with a forwarding rule for a private zone keeps resolving past the first TTL expiry (no cache poisoning)

Fixes #147

## Summary - `refresh_entry` now consults `match_forwarding_rule` before falling through to recursive/default-upstream resolution, matching the precedence already enforced in `resolve_query`. - Without this, the first cache refresh for a forwarded name (NearExpiry at 90% TTL, or Stale within the 3600s serve-stale window) re-resolved the private zone through the public default upstream, which returned NXDOMAIN/NODATA and poisoned the cache for at least `cache.min_ttl` (60s default). On a busy resolver the cycle was continuous; a restart restored service until the next refresh. Reporter in #147 observed this for both a private TLD and its `in-addr.arpa` zone, and worked around it with hardcoded zone entries (which win over cache in the pipeline). - Added two regression tests: `refresh_entry_honors_forwarding_rule` (default pool points at a blackhole — only the rule's mock upstream can satisfy the refresh) and `refresh_entry_prefers_forwarding_rule_over_recursive` (Recursive mode with empty `root_hints` — recursion would fail immediately, proving the rule branch fired). - Extracted `testutil::a_record_response()` for the mock-response boilerplate and migrated six call sites (two new + four adjacent tests). ## Test plan - [x] `cargo test --lib ctx::tests` — 33/33 pass, including both new regression tests - [x] `make all` — fmt, clippy, audit, build, full suite (356 unit + 1 integration) - [x] Verified by having a long-running instance under load with a forwarding rule for a private zone keeps resolving past the first TTL expiry (no cache poisoning) Fixes #147
Sign in to join this conversation.