package resolver type ResolveBatchInput struct { ToResolve []string Workers int Now int StaleKeepSec int } type ResolveBatchResult struct { DNSStats DNSMetrics ResolvedNowDNS int ResolvedNowStale int UnresolvedAfterAttempts int StaleHitsDelta int } func ExecuteResolveBatch( in ResolveBatchInput, resolved map[string][]string, domainCache *DomainCacheState, cacheSourceForHost func(string) DomainCacheSource, resolveHost func(string) ([]string, DNSMetrics), logf func(string, ...any), ) ResolveBatchResult { out := ResolveBatchResult{} if len(in.ToResolve) == 0 || resolveHost == nil || domainCache == nil { return out } workers := in.Workers if workers < 1 { workers = 1 } if workers > 500 { workers = 500 } resolveSource := cacheSourceForHost if resolveSource == nil { resolveSource = func(string) DomainCacheSource { return DomainCacheSourceDirect } } type result struct { host string ips []string stats DNSMetrics } jobs := make(chan string, len(in.ToResolve)) results := make(chan result, len(in.ToResolve)) for i := 0; i < workers; i++ { go func() { for host := range jobs { ips, stats := resolveHost(host) results <- result{host: host, ips: ips, stats: stats} } }() } for _, host := range in.ToResolve { jobs <- host } close(jobs) for i := 0; i < len(in.ToResolve); i++ { r := <-results out.DNSStats.Merge(r.stats) hostErrors := r.stats.TotalErrors() if hostErrors > 0 && logf != nil { logf("resolve errors for %s: total=%d nxdomain=%d timeout=%d temporary=%d other=%d", r.host, hostErrors, r.stats.NXDomain, r.stats.Timeout, r.stats.Temporary, r.stats.Other) } if len(r.ips) > 0 { if resolved != nil { resolved[r.host] = r.ips } out.ResolvedNowDNS++ source := resolveSource(r.host) domainCache.Set(r.host, source, r.ips, in.Now) if logf != nil { logf("%s -> %v", r.host, r.ips) } continue } staleApplied := false if hostErrors > 0 { source := resolveSource(r.host) domainCache.SetErrorWithStats(r.host, source, r.stats, in.Now) if in.StaleKeepSec > 0 && ShouldUseStaleOnError(r.stats) { if staleIPs, staleAge, ok := domainCache.GetStale(r.host, source, in.Now, in.StaleKeepSec); ok { out.StaleHitsDelta++ out.ResolvedNowStale++ staleApplied = true if resolved != nil { resolved[r.host] = staleIPs } if logf != nil { logf("cache stale-keep (error)[age=%ds]: %s -> %v", staleAge, r.host, staleIPs) } } } } if !staleApplied { out.UnresolvedAfterAttempts++ } if logf != nil && resolved != nil { if _, ok := resolved[r.host]; !ok { logf("%s: no IPs", r.host) } } } return out }