dns

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 2, 2026 License: MIT Imports: 4 Imported by: 0

Documentation

Overview

Package dns is the host-resolution cache: it maps a host to its resolved IP, honors TTLs, and feeds the per-IP politeness bucket so two hosts behind one address do not dodge rate limits.

The point of the package is to keep DNS off the dispatch hot path. Resolution is slow and bursty, so the dispatcher never blocks on a lookup: it asks the Cache for a host with Lookup, and on a miss it queues the host with Prefetch and moves on. A bounded worker pool resolves queued hosts in the background and fills the positive cache. A name that fails to resolve lands in the negative cache and is suppressed for a while so a dead host does not get retried on every dispatch.

The real resolver runs over the pure-Go net resolver (PreferGo: true). The cgo resolver blocks an OS thread per lookup and exhausts the thread limit at crawl scale; the pure-Go resolver uses goroutines instead.

DialContext rewrites a dial address to the cached IP so a connection goes straight to the resolved address while the hostname still rides in the Host header and TLS SNI. That keeps the per-IP politeness bucket keyed on exactly the IP we dialed.

Index

Constants

View Source
const DefaultTTL = 5 * time.Minute

DefaultTTL is the lifetime SystemResolver reports for a record. The Go resolver does not surface the DNS TTL, so we report a sane default and let the cache clamp it into [MinTTL, MaxTTL].

Variables

This section is empty.

Functions

This section is empty.

Types

type Cache

type Cache struct {
	// contains filtered or unexported fields
}

Cache is the resolve cache: a positive cache (host -> ip, expiry) and a negative cache (host -> fail-until), refreshed by a bounded prefetch pool.

func NewCache

func NewCache(r Resolver, now func() time.Time, opts ...Option) *Cache

NewCache builds a cache over r. now is an injectable clock for tests: pass a func returning the current time; nil means time.Now.

func (*Cache) Close

func (c *Cache) Close()

Close stops the worker pool. It is safe to call more than once.

func (*Cache) DialContext

func (c *Cache) DialContext(base *net.Dialer) func(ctx context.Context, network, addr string) (net.Conn, error)

DialContext returns a net.Dialer DialContext func that dials the cached IP for the request's host directly (the per-IP politeness bucket is keyed on exactly this IP), falling back to the system dial when the host is uncached. The hostname still goes in the Host header and TLS SNI upstream; this only rewrites the dial address to ip:port.

func (*Cache) Failures

func (c *Cache) Failures(host string) int

Failures returns the current consecutive failure count for host, zero when the host is not suppressed. A run of failures is the signal to flag a dead host.

func (*Cache) Lookup

func (c *Cache) Lookup(host string) (ip [16]byte, ok bool)

Lookup returns the cached IP for host when present and unexpired. ok is false on a miss or an expired entry (the caller should Prefetch and defer the host, never resolve synchronously on the dispatch path).

func (*Cache) Prefetch

func (c *Cache) Prefetch(hosts ...string)

Prefetch enqueues hosts for asynchronous resolution off the dispatch path. Hosts already fresh in the positive cache or still suppressed by the negative cache are skipped. It returns immediately; resolution happens in the pool.

func (*Cache) Resolve

func (c *Cache) Resolve(ctx context.Context, host string) (ip [16]byte, ok bool)

Resolve does a synchronous resolve-and-cache, for the rare path that needs it (and for tests). It honors the negative cache and the TTL clamps.

func (*Cache) Stats

func (c *Cache) Stats() Stats

Stats snapshots the cache sizes. Pending counts hosts reserved for or under resolution right now.

func (*Cache) Suppressed

func (c *Cache) Suppressed(host string) bool

Suppressed reports whether host is currently negatively cached (a recent failure), so the caller can flag a dead host after repeated suppression.

func (*Cache) Wait

func (c *Cache) Wait()

Wait blocks until the prefetch pool has drained all queued work. For tests and graceful shutdown.

type Option

type Option func(*config)

Option configures a Cache.

func WithMaxTTL

func WithMaxTTL(d time.Duration) Option

WithMaxTTL caps a huge TTL so the cache eventually re-resolves.

func WithMinTTL

func WithMinTTL(d time.Duration) Option

WithMinTTL floors a tiny TTL so a short-lived record does not churn the cache.

func WithNegativeTTL

func WithNegativeTTL(d time.Duration) Option

WithNegativeTTL sets how long a failed name is suppressed before a retry.

func WithWorkers

func WithWorkers(n int) Option

WithWorkers sets the prefetch pool concurrency. A value below 1 is ignored so the pool always has at least one worker.

type Resolver

type Resolver interface {
	Resolve(ctx context.Context, host string) (ip [16]byte, ttl time.Duration, err error)
}

Resolver resolves a host name to an IP and the record's TTL. The production implementation is SystemResolver over the pure-Go net resolver; tests pass a stub. It must be safe for concurrent use.

type Stats

type Stats struct{ Positive, Negative, Pending int }

Stats reports cache state for the gate and tests.

type SystemResolver

type SystemResolver struct {
	// contains filtered or unexported fields
}

SystemResolver resolves over &net.Resolver{PreferGo: true}. Note: the Go resolver does not surface the record TTL, so Resolve returns DefaultTTL; the cache clamps TTLs into [MinTTL, MaxTTL] regardless.

func (SystemResolver) Resolve

func (s SystemResolver) Resolve(ctx context.Context, host string) ([16]byte, time.Duration, error)

Resolve looks up host and returns its first IP, IPv4-mapped into 16 bytes. It always uses the pure-Go resolver so a lookup costs a goroutine, not an OS thread.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL