mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-04-28 11:30:15 +00:00
Related to #608 Implements DNS caching using rs/dnscache to dramatically reduce DNS query volume for frequently accessed Proxmox hosts. Users were reporting 260,000+ DNS queries in 37 hours for the same hostnames. Changes: - Added rs/dnscache dependency for DNS resolution caching - Created pkg/tlsutil/dnscache.go with DNS cache wrapper - Updated HTTP client creation to use cached DNS resolver - Added DNSCacheTimeout configuration option (default: 5 minutes) - Made DNS cache timeout configurable via: - system.json: dnsCacheTimeout field (seconds) - Environment variable: DNS_CACHE_TIMEOUT (duration string) - DNS cache periodically refreshes to prevent stale entries Benefits: - Reduces DNS query load on local DNS servers by ~99% - Reduces network traffic and DNS query log volume - Maintains fresh DNS entries through periodic refresh - Configurable timeout for different network environments Default behavior: 5-minute cache timeout with automatic refresh
100 lines
2.2 KiB
Go
100 lines
2.2 KiB
Go
package tlsutil
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/rs/dnscache"
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
var (
|
|
// Global DNS resolver with caching
|
|
globalResolver *dnscache.Resolver
|
|
globalResolverOnce sync.Once
|
|
resolverMutex sync.RWMutex
|
|
resolverRefreshTTL time.Duration = 5 * time.Minute // Default TTL
|
|
)
|
|
|
|
// GetDNSResolver returns the global DNS resolver instance with caching
|
|
func GetDNSResolver() *dnscache.Resolver {
|
|
globalResolverOnce.Do(func() {
|
|
initDNSResolver(resolverRefreshTTL)
|
|
})
|
|
return globalResolver
|
|
}
|
|
|
|
// initDNSResolver initializes the DNS resolver with the specified TTL
|
|
func initDNSResolver(ttl time.Duration) {
|
|
log.Info().
|
|
Dur("ttl", ttl).
|
|
Msg("Initializing DNS resolver cache to reduce DNS query load")
|
|
|
|
globalResolver = &dnscache.Resolver{}
|
|
|
|
// Start a goroutine to periodically refresh the DNS cache
|
|
// This prevents stale DNS entries while still providing caching benefits
|
|
go func() {
|
|
ticker := time.NewTicker(ttl)
|
|
defer ticker.Stop()
|
|
|
|
for range ticker.C {
|
|
globalResolver.Refresh(true)
|
|
log.Debug().
|
|
Dur("ttl", ttl).
|
|
Msg("DNS cache refreshed")
|
|
}
|
|
}()
|
|
}
|
|
|
|
// SetDNSCacheTTL updates the DNS cache TTL
|
|
// This function should be called before any HTTP clients are created
|
|
func SetDNSCacheTTL(ttl time.Duration) {
|
|
resolverMutex.Lock()
|
|
defer resolverMutex.Unlock()
|
|
|
|
if ttl <= 0 {
|
|
ttl = 5 * time.Minute // Default
|
|
}
|
|
|
|
resolverRefreshTTL = ttl
|
|
|
|
log.Info().
|
|
Dur("ttl", ttl).
|
|
Msg("DNS cache TTL configured")
|
|
}
|
|
|
|
// DialContextWithCache is a DialContext function that uses the DNS cache
|
|
func DialContextWithCache(ctx context.Context, network, address string) (net.Conn, error) {
|
|
resolver := GetDNSResolver()
|
|
|
|
host, port, err := net.SplitHostPort(address)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Look up the IP address using the cached resolver
|
|
ips, err := resolver.LookupHost(ctx, host)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Use the first IP address
|
|
if len(ips) == 0 {
|
|
return nil, &net.DNSError{
|
|
Err: "no IP addresses found",
|
|
Name: host,
|
|
}
|
|
}
|
|
|
|
// Create a dialer with the resolved IP
|
|
dialer := &net.Dialer{
|
|
Timeout: 10 * time.Second,
|
|
KeepAlive: 30 * time.Second,
|
|
}
|
|
|
|
// Dial with the resolved IP address
|
|
return dialer.DialContext(ctx, network, net.JoinHostPort(ips[0], port))
|
|
}
|