Skip to content

DNS Cache Example ​

Demonstrates HTTPX's thread-safe in-memory DNS caching subsystem with positive/negative TTL and single-flight stampede prevention.

How DNS Caching Works ​

  1. Automatic Client Caching: By default, httpx.Client initializes an internal Cache that caches successful lookups for 60,000 ms (60 seconds) and failed lookups for 5,000 ms (5 seconds).
  2. Single-Flight Coalescing: If 50 concurrent requests simultaneously need to resolve httpbun.com, only one real DNS query is dispatched to the network. The other 49 callers await the result and receive clones, preventing cache stampedes.
  3. Thread Safety: Internally synchronized via spinlocks. Network I/O is never performed while holding locks.
  4. Memory Bounds: Entries are bounded by maxEntries (default: 1024) to prevent memory exhaustion.

High-Level Usage via Client ​

zig
const std = @import("std");
const httpx = @import("httpx");

pub fn main() !void {
    var gpa: std.heap.DebugAllocator(.{}) = .init;
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    const io = std.Io.Threaded.global_single_threaded.io();

    // Initialize client with custom cache settings
    var client = httpx.Client.init(allocator, io, .{
        .dnsCache = .{
            .enable = true,
            .ttlMs = 120_000,         // 2 minutes positive TTL
            .negativeTtlMs = 3_000,  // 3 seconds negative TTL
            .maxEntries = 2048,
        },
    });
    defer client.deinit();

    // First lookup: queries DNS and populates cache
    var addrs1 = try client.resolve("httpbun.com", .{ .port = 443 });
    defer addrs1.deinit();

    // Second lookup: served instantly from cache
    var addrs2 = try client.resolve("httpbun.com", .{ .port = 443 });
    defer addrs2.deinit();

    // Force fresh lookup bypassing cache
    var fresh = try client.resolve("httpbun.com", .{ .port = 443, .useCache = false });
    defer fresh.deinit();
}

Advanced Direct Cache API ​

For specialized network applications, the standalone cache in src/net/dns/cache.zig can be used directly:

zig
const cache_mod = @import("httpx").dnsCache; // or @import("src/net/dns/cache.zig")

The cache exposes atomic observability counters:

  • hits: Number of lookups satisfied from cache.
  • misses: Number of lookups that required network I/O.
  • lookups_started: Number of network lookups initiated.
  • lookups_coalesced: Number of concurrent requests joined to an in-flight lookup.

Released under the MIT License.