feat: always prefetch upstream IPs to avoid stalling user queries

Otherwise, a request to blocky could end up waiting for 2 DNS requests:
  1. lookup the DNS server IP
  2. forward the user request to the server looked-up in 1
This commit is contained in:
ThinkChaos 2022-12-02 21:19:06 -05:00
parent 7c76836373
commit 63f65002e8
3 changed files with 41 additions and 7 deletions

View File

@ -616,6 +616,18 @@ type CachingConfig struct {
PrefetchMaxItemsCount int `yaml:"prefetchMaxItemsCount"`
}
func (c *CachingConfig) EnablePrefetch() {
const day = 24 * time.Hour
if c.MaxCachingTime == 0 {
// make sure resolver gets enabled
c.MaxCachingTime = Duration(day)
}
c.Prefetching = true
c.PrefetchThreshold = 0
}
// QueryLogConfig configuration for the query logging
type QueryLogConfig struct {
Target string `yaml:"target"`

View File

@ -63,11 +63,23 @@ func NewBootstrap(cfg *config.Config) (b *Bootstrap, err error) {
return nil, fmt.Errorf("could not create bootstrap ParallelBestResolver: %w", err)
}
// Always enable prefetching to avoid stalling user requests
// Otherwise, a request to blocky could end up waiting for 2 DNS requests:
// 1. lookup the DNS server IP
// 2. forward the user request to the server looked-up in 1
cachingCfg := cfg.Caching
cachingCfg.EnablePrefetch()
if cachingCfg.MinCachingTime == 0 {
// Set a min time in case the user didn't to avoid prefetching too often
cachingCfg.MinCachingTime = config.Duration(time.Hour)
}
b.bootstraped = bootstraped
b.resolver = Chain(
NewFilteringResolver(cfg.Filtering),
NewCachingResolver(cfg.Caching, nil),
NewCachingResolver(cachingCfg, nil),
parallelResolver,
)

View File

@ -70,10 +70,16 @@ func configureCaches(c *CachingResolver, cfg *config.CachingConfig) {
c.prefetchThreshold = cfg.PrefetchThreshold
c.prefetchingNameCache = expirationcache.NewCache(expirationcache.WithCleanUpInterval(time.Minute),
expirationcache.WithMaxSize(uint(cfg.PrefetchMaxItemsCount)))
c.resultCache = expirationcache.NewCache(cleanupOption, maxSizeOption,
expirationcache.WithOnExpiredFn(c.onExpired))
c.prefetchingNameCache = expirationcache.NewCache(
expirationcache.WithCleanUpInterval(time.Minute),
expirationcache.WithMaxSize(uint(cfg.PrefetchMaxItemsCount)),
)
c.resultCache = expirationcache.NewCache(
cleanupOption,
maxSizeOption,
expirationcache.WithOnExpiredFn(c.onExpired),
)
} else {
c.resultCache = expirationcache.NewCache(cleanupOption, maxSizeOption)
}
@ -93,7 +99,11 @@ func setupRedisCacheSubscriber(c *CachingResolver) {
}
// check if domain was queried > threshold in the time window
func (r *CachingResolver) isPrefetchingDomain(cacheKey string) bool {
func (r *CachingResolver) shouldPrefetch(cacheKey string) bool {
if r.prefetchThreshold == 0 {
return true
}
cnt, _ := r.prefetchingNameCache.Get(cacheKey)
return cnt != nil && cnt.(int) > r.prefetchThreshold
@ -104,7 +114,7 @@ func (r *CachingResolver) onExpired(cacheKey string) (val interface{}, ttl time.
logger := log.PrefixedLog("caching_resolver")
if r.isPrefetchingDomain(cacheKey) {
if r.shouldPrefetch(cacheKey) {
logger.Debugf("prefetching '%s' (%s)", util.Obfuscate(domainName), qType.String())
req := newRequest(fmt.Sprintf("%s.", domainName), qType, logger)