4.5 The Cache

The cache's key and lifetimes are set by PSPU §6.7. This article is how resolvd computes them and what it does at the edges.

4.5.1 The cache key #

  • An entry is keyed by the candidate in lower case, the record type, and the scope key (§1.3). The class is not part of the key; every question resolvd asks is class IN. The stub rejects other classes before consulting the cache (§6.1), even when a matching IN answer is held.
  • The same name asked through two scopes is two entries, and a single label's expansions are each cached under the expanded name.

4.5.2 What is stored #

Only replies a server sent with response code NOERROR or NXDOMAIN are stored, under the candidate and the scope the candidate was routed to:

ReplyStored asLifetime
NOERROR with relevant answer records of class IN (§4.8)found with those recordsThe least TTL among those records, capped at 86 400 s
NOERROR with no relevant answer records of class INfound with noneNegative lifetime
NXDOMAINnotfoundNegative lifetime

4.5.2.1 The negative lifetime #

  • The negative lifetime comes from the first IN SOA record in the reply's authority section whose owner is the effective name or an enclosing domain. The effective name is the candidate after following its relevant CNAME path (§4.8); suffix matching respects label boundaries. Use the lesser of the SOA's MINIMUM field and its own TTL, capped at 300 s. When an alias leads to NXDOMAIN, also cap the lifetime at the least TTL of that alias path.
  • A reply with no qualifying SOA in its authority section has a negative lifetime of zero.

4.5.2.2 A zero lifetime is not stored #

An entry whose lifetime is zero is not stored. A positive answer with a zero TTL, and a negative answer without an SOA, are therefore never cached.

4.5.2.3 What is never stored #

Never stored: synthetic answers, unavailable, transport failures, a UDP reply with TC set (the reply to its TCP retry is the one considered), and replies with any other response code.

4.5.2.4 The TTLs inside an entry #

The records in an entry are kept with the TTLs the server sent. Only the entry's lifetime is capped.

4.5.3 Hits #

  • An entry is live while its expiry is in the future. A live entry is returned with each record's TTL lowered to the whole number of seconds the entry has left, rounded down, when that is less than the record's own.
  • A notfound hit with further candidates waiting moves to the next candidate, as a notfound from a server would (§4.1).

4.5.4 Bypassing the cache #

  • A resolve request with no_cache set skips the lookup for each of its candidates. The reply it gets is still stored.
  • Stub queries, lookup and reverse always consult the cache.

4.5.5 Capacity and eviction #

  • The cache holds at most 8 192 entries.
  • Expiry alone does not remove an entry. Looking up an expired key removes it immediately, before any replacement answer arrives. Otherwise it stays until overwritten, flushed, or removed during admission at capacity. The cache_entries count in status includes expired entries that have not yet been removed.

4.5.5.1 Eviction #

Storing an entry under a key the cache does not already hold, when it already holds 8 192 entries, first evicts:

  1. every entry whose expiry is at or before the current time;
  2. then, if the cache still holds 8 192 entries, the one entry with the earliest expiry — any one of them, when several share it.

4.5.5.2 A long-lived answer preserves other live entries #

The sweep compares against the current time, independently of the new answer's lifetime. A day-long answer arriving at a full cache of live, shorter-lived answers replaces only the entry with the earliest expiry; the cache still holds 8 192 entries. If removing expired entries makes room, no live entry is evicted.

Older builds compared against the incoming answer's expiry and could empty a cache of live answers.

4.5.5.3 Overwriting a held key #

Storing under a key the cache already holds replaces that entry, live or expired, and evicts nothing else.

4.5.6 Flushes #

EventDiscards
A snapshot in which a scope is new, has a different server list, or is missing (§3.3)That scope's entries
A registry change to FallbackServers in content or order (§2.3)The fallback scope's entries
A flush request (§5.3)Everything
A restartEverything

Nothing else discards entries. Changes to static names, search domains, the hostname, ControlSecurity, scope flags and metrics leave the cache as it is.

4.5.6.1 A reply after its scope was flushed #

In source b4f7085, a reply that arrives after its scope was flushed is still stored, under the same scope key.

4.5.6.2 Proposed admission rule for invalidated tasks #

The proposed source correction also invalidates pending tasks' permission to store answers. A global flush affects every pending task. Removing an interface scope or changing its server list affects tasks currently associated with that scope; changing the fallback-server list affects tasks associated with the fallback scope.

An invalidated task still completes its original requester normally, but cannot store any further answers. This restriction persists through UDP-to-TCP fallback, timeout retries and later search candidates, even if a later attempt uses a new server list. Removing and re-adding a scope does not restore the task's permission. Fresh requests can cache normally.

Changes only to metrics, search domains or other scope properties with unchanged server lists do not invalidate admission; unrelated scopes' pending tasks are unaffected. This changes cache admission, not request cancellation or routing. Cache keys, TTLs and eviction policy are unchanged. It describes proposed source behavior, not a released package or an installed resolver; check the installed source revision before relying on it.

Edit this page