5 views
Node’s default DNS behavior is a silent reliability tax: dns.lookup hits the OS resolver per new connection family, and a 200ms CoreDNS blip becomes a thundering herd of ENOTFOUND / ETIMEOUT across every undici Agent. The unfair advantage is an application-level DNS cache with negative caching bounds, stale-while-revalidate, and explicit timeouts that fail individual lookups — not entire request batches.
⚡ TL;DR: PreferCacheableLookup(or a thin TTL cache arounddns.promises.lookup) wired into undici/http.Agentlookup; set positive TTL ≤ DNS TTL; negative TTL short (1–5s); never cache forever. Pair with Node undici Dispatcher Pools and HTTP/2 in Node Behind ALB.
Node DNS Caching: production guidance
typescript
// src/dns-agent.ts
import CacheableLookup from "cacheable-lookup";
import { Agent, setGlobalDispatcher } from "undici";
import { Resolver } from "node:dns";
const resolver = new Resolver();
resolver.setServers(process.env.DNS_SERVERS?.split(",") ?? ["10.0.0.2"]);
const cacheable = new CacheableLookup({
maxTtl: 30, // ✅ clamp runaway TTLs
errorTtl: 2, // ✅ short negative cache
resolver,
});
export const dispatcher = new Agent({
connect: {
lookup: cacheable.lookup.bind(cacheable) as any,
timeout: 3_000,
},
keepAliveTimeout: 30_000,
connections: 32,
});
setGlobalDispatcher(dispatcher);
typescript
// ❌ New Agent per request + default lookup — amplifies every blip
const res = await fetch(url); // default DNS, no shared cache
Stale-while-revalidate for critical peers
For in-mesh services with stable IPs, serve last-known-good briefly while refreshing.typescript
// src/stale-dns.ts
type Entry = { address: string; family: 4 | 6; expires: number; staleUntil: number };
const map = new Map<string, Entry>();
export async function lookupCached(host: string, lookup: typeof import("node:dns").promises.lookup) {
const now = Date.now();
const hit = map.get(host);
if (hit && hit.expires > now) return { address: hit.address, family: hit.family };
try {
const r = await lookup(host, { verbatim: true });
map.set(host, {
address: r.address,
family: r.family as 4 | 6,
expires: now + 15_000,
staleUntil: now + 60_000,
});
return r;
} catch (err) {
// ✅ Serve stale for a bounded window instead of failing open forever
if (hit && hit.staleUntil > now) {
metrics.increment("dns.stale_served");
return { address: hit.address, family: hit.family };
}
throw err;
}
}
Timeouts and herd control
| Knob | Suggested start |
|---|---|
| Positive max TTL | 15–30s (or DNS TTL) |
| Negative TTL | 1–5s |
| Lookup timeout | 1–3s |
| Stale window | ≤60s for mesh peers only |
| Max concurrent lookups/host | 1 in-flight + waiters |
bash
# ✅ Prove resolver latency in staging
dig @$COREDNS api.internal.svc.cluster.local +stats
# Alarm on dns.lookup error rate and p99 duration
Closing checklist
✅ Dos – ✅ Share one cacheable lookup across the process dispatcher – ✅ Bound negative caching so recoveries are fast – ✅ Prefer stale-while-revalidate for critical mesh hosts – ✅ Alarm DNS error rate separately from upstream 5xx – ✅ Test behavior when CoreDNS is paused for 5s ❌ Don’ts – ❌ Don’t cache DNS forever “for performance” – ❌ Don’t ignore IPv6/verbatim surprises behind ALB
– ❌ Don’t create a new Agent (and lookup path) per request
– ❌ Don’t treat every ENOTFOUND as an application bug
Related reading
- Node undici Dispatcher Pools: Kill Keep-Alive Storms Under Spikes
- HTTP/2 in Node Behind ALB: Multiplexing Pitfalls That Reset Streams
- Node Fetch vs Got vs Undici: Throughput Under Real TLS Churn
- Graceful Shutdown for Node: Drain Correctly on Kubernetes and ECS
Watch: 100 Days of AI on YouTube
Short videos from CheatCoders, one AI engineering topic per day.
Most viewed
- Python Decorators Explained: From Simple Wrappers to Production Patterns
- Claude Code vs Cursor vs GitHub Copilot: Choose by Workflow, and Set Up One Repo That Works With All Three
- AI Agent Frameworks in 2025: LangGraph vs CrewAI vs AutoGen vs Raw API
- Python String Methods: Every str Method With Real Production Examples
- Css Animation Performance: Performance-Optimized Motion for Modern UIs
Newly added
- AWS Systems Manager Parameter Store: Hierarchical Runtime Config for Coding Agents
- Amazon Cognito Identity Pools: Federate Coding-Agent Tool Callers Without Long-Lived Keys
- Amazon Athena for Coding Agents: Query Tool Audit Logs and Eval Traces on S3 Without a Warehouse
- AWS Batch for Coding Agents: Parallel Overnight PR Reviews and Long-Running Jobs Without Lambda Timeouts
- Amazon Bedrock Prompt Management: Version and A/B Test Coding-Agent System Prompts
Deep-dive PDF
Get the expanded guide for this post — extra diagrams-style checklists, failure modes, and a production walkthrough. Free when you subscribe to CheatCoders.
Already subscribed? or open the subscribe page.
Discover more from CheatCoders
Subscribe to get the latest posts sent to your email.
