package resolver import "time" type ResolverRuntimeTuningInput struct { TTL int Workers int Now int PrecheckStatePath string PrecheckEnvForced bool PrecheckFileForced bool } type ResolverRuntimeTuning struct { TTL int Workers int DNSTimeoutMS int DNSTimeout time.Duration PrecheckEverySec int PrecheckMaxDomains int TimeoutRecheckMax int LiveBatchMin int LiveBatchMax int LiveBatchTarget int LiveBatchNXHeavyMin int LiveBatchNXHeavyMax int LiveBatchNXHeavyPct int PrecheckDue bool StaleKeepSec int NegTTLNX int NegTTLTimeout int NegTTLTemporary int NegTTLOther int } type ResolverRuntimeTuningDeps struct { EnvInt func(string, int) int LoadResolverPrecheckLastRun func(path string) int LoadResolverLiveBatchTarget func(path string, fallback, minV, maxV int) int LoadResolverLiveBatchNXHeavyPct func(path string, fallback, minV, maxV int) int } func BuildResolverRuntimeTuning(in ResolverRuntimeTuningInput, deps ResolverRuntimeTuningDeps) ResolverRuntimeTuning { envInt := deps.EnvInt if envInt == nil { envInt = func(_ string, def int) int { return def } } ttl := in.TTL if ttl <= 0 { ttl = 24 * 3600 } if ttl < 60 { ttl = 60 } if ttl > 24*3600 { ttl = 24 * 3600 } workers := in.Workers if workers <= 0 { workers = 80 } if workers < 1 { workers = 1 } if workers > 500 { workers = 500 } dnsTimeoutMs := envInt("RESOLVE_DNS_TIMEOUT_MS", 1800) if dnsTimeoutMs < 300 { dnsTimeoutMs = 300 } if dnsTimeoutMs > 5000 { dnsTimeoutMs = 5000 } precheckEverySec := envInt("RESOLVE_PRECHECK_EVERY_SEC", 24*3600) if precheckEverySec < 0 { precheckEverySec = 0 } precheckMaxDomains := envInt("RESOLVE_PRECHECK_MAX_DOMAINS", 3000) if precheckMaxDomains < 0 { precheckMaxDomains = 0 } if precheckMaxDomains > 50000 { precheckMaxDomains = 50000 } timeoutRecheckMax := envInt("RESOLVE_TIMEOUT_RECHECK_MAX", precheckMaxDomains) if timeoutRecheckMax < 0 { timeoutRecheckMax = 0 } if timeoutRecheckMax > 50000 { timeoutRecheckMax = 50000 } liveBatchMin := envInt("RESOLVE_LIVE_BATCH_MIN", 800) liveBatchMax := envInt("RESOLVE_LIVE_BATCH_MAX", 3000) liveBatchDefault := envInt("RESOLVE_LIVE_BATCH_DEFAULT", 1800) if liveBatchMin < 200 { liveBatchMin = 200 } if liveBatchMin > 50000 { liveBatchMin = 50000 } if liveBatchMax < liveBatchMin { liveBatchMax = liveBatchMin } if liveBatchMax > 50000 { liveBatchMax = 50000 } if liveBatchDefault < liveBatchMin { liveBatchDefault = liveBatchMin } if liveBatchDefault > liveBatchMax { liveBatchDefault = liveBatchMax } liveBatchTarget := liveBatchDefault if deps.LoadResolverLiveBatchTarget != nil { liveBatchTarget = deps.LoadResolverLiveBatchTarget(in.PrecheckStatePath, liveBatchDefault, liveBatchMin, liveBatchMax) } liveBatchNXHeavyMin := envInt("RESOLVE_LIVE_BATCH_NX_HEAVY_MIN_PCT", 5) liveBatchNXHeavyMax := envInt("RESOLVE_LIVE_BATCH_NX_HEAVY_MAX_PCT", 35) liveBatchNXHeavyDefault := envInt("RESOLVE_LIVE_BATCH_NX_HEAVY_PCT", 10) if liveBatchNXHeavyMin < 0 { liveBatchNXHeavyMin = 0 } if liveBatchNXHeavyMin > 100 { liveBatchNXHeavyMin = 100 } if liveBatchNXHeavyMax < liveBatchNXHeavyMin { liveBatchNXHeavyMax = liveBatchNXHeavyMin } if liveBatchNXHeavyMax > 100 { liveBatchNXHeavyMax = 100 } if liveBatchNXHeavyDefault < liveBatchNXHeavyMin { liveBatchNXHeavyDefault = liveBatchNXHeavyMin } if liveBatchNXHeavyDefault > liveBatchNXHeavyMax { liveBatchNXHeavyDefault = liveBatchNXHeavyMax } liveBatchNXHeavyPct := liveBatchNXHeavyDefault if deps.LoadResolverLiveBatchNXHeavyPct != nil { liveBatchNXHeavyPct = deps.LoadResolverLiveBatchNXHeavyPct(in.PrecheckStatePath, liveBatchNXHeavyDefault, liveBatchNXHeavyMin, liveBatchNXHeavyMax) } precheckLastRun := 0 if deps.LoadResolverPrecheckLastRun != nil { precheckLastRun = deps.LoadResolverPrecheckLastRun(in.PrecheckStatePath) } precheckDue := in.PrecheckEnvForced || in.PrecheckFileForced || (precheckEverySec > 0 && (precheckLastRun <= 0 || in.Now-precheckLastRun >= precheckEverySec)) staleKeepSec := envInt("RESOLVE_STALE_KEEP_SEC", 48*3600) if staleKeepSec < 0 { staleKeepSec = 0 } if staleKeepSec > 7*24*3600 { staleKeepSec = 7 * 24 * 3600 } negTTLNX := envInt("RESOLVE_NEGATIVE_TTL_NX", 6*3600) negTTLTimeout := envInt("RESOLVE_NEGATIVE_TTL_TIMEOUT", 15*60) negTTLTemporary := envInt("RESOLVE_NEGATIVE_TTL_TEMPORARY", 10*60) negTTLOther := envInt("RESOLVE_NEGATIVE_TTL_OTHER", 10*60) clampTTL := func(v int) int { if v < 0 { return 0 } if v > 24*3600 { return 24 * 3600 } return v } negTTLNX = clampTTL(negTTLNX) negTTLTimeout = clampTTL(negTTLTimeout) negTTLTemporary = clampTTL(negTTLTemporary) negTTLOther = clampTTL(negTTLOther) return ResolverRuntimeTuning{ TTL: ttl, Workers: workers, DNSTimeoutMS: dnsTimeoutMs, DNSTimeout: time.Duration(dnsTimeoutMs) * time.Millisecond, PrecheckEverySec: precheckEverySec, PrecheckMaxDomains: precheckMaxDomains, TimeoutRecheckMax: timeoutRecheckMax, LiveBatchMin: liveBatchMin, LiveBatchMax: liveBatchMax, LiveBatchTarget: liveBatchTarget, LiveBatchNXHeavyMin: liveBatchNXHeavyMin, LiveBatchNXHeavyMax: liveBatchNXHeavyMax, LiveBatchNXHeavyPct: liveBatchNXHeavyPct, PrecheckDue: precheckDue, StaleKeepSec: staleKeepSec, NegTTLNX: negTTLNX, NegTTLTimeout: negTTLTimeout, NegTTLTemporary: negTTLTemporary, NegTTLOther: negTTLOther, } }