Final feedback implementation and fixes
This commit is contained in:
@@ -132,7 +132,6 @@ func Resolve(ctx context.Context, q *Query) (rrCache *RRCache, err error) {
|
||||
if !q.NoCaching {
|
||||
rrCache = checkCache(ctx, q)
|
||||
if rrCache != nil && !rrCache.Expired() {
|
||||
rrCache.MixAnswers()
|
||||
return rrCache, nil
|
||||
}
|
||||
|
||||
@@ -142,7 +141,6 @@ func Resolve(ctx context.Context, q *Query) (rrCache *RRCache, err error) {
|
||||
// we waited for another request, recheck the cache!
|
||||
rrCache = checkCache(ctx, q)
|
||||
if rrCache != nil && !rrCache.Expired() {
|
||||
rrCache.MixAnswers()
|
||||
return rrCache, nil
|
||||
}
|
||||
log.Tracer(ctx).Debugf("resolver: waited for another %s%s query, but cache missed!", q.FQDN, q.QType)
|
||||
@@ -221,7 +219,7 @@ func checkCache(ctx context.Context, q *Query) *RRCache {
|
||||
log.Tracer(ctx).Tracef(
|
||||
"resolver: cache for %s will expire in %s, refreshing async now",
|
||||
q.ID(),
|
||||
time.Until(time.Unix(rrCache.TTL, 0)),
|
||||
time.Until(time.Unix(rrCache.TTL, 0)).Round(time.Second),
|
||||
)
|
||||
|
||||
// resolve async
|
||||
@@ -231,6 +229,8 @@ func checkCache(ctx context.Context, q *Query) *RRCache {
|
||||
_, err := resolveAndCache(ctx, q, nil)
|
||||
if err != nil {
|
||||
tracer.Warningf("resolver: async query for %s failed: %s", q.ID(), err)
|
||||
} else {
|
||||
tracer.Debugf("resolver: async query for %s succeeded", q.ID())
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@@ -240,7 +240,7 @@ func checkCache(ctx context.Context, q *Query) *RRCache {
|
||||
|
||||
log.Tracer(ctx).Tracef(
|
||||
"resolver: using cached RR (expires in %s)",
|
||||
time.Until(time.Unix(rrCache.TTL, 0)),
|
||||
time.Until(time.Unix(rrCache.TTL, 0)).Round(time.Second),
|
||||
)
|
||||
return rrCache
|
||||
}
|
||||
@@ -392,13 +392,24 @@ resolveLoop:
|
||||
}
|
||||
|
||||
// Check if we want to use an older cache instead.
|
||||
switch {
|
||||
case err != nil:
|
||||
// There was an error during resolving, return the old cache entry instead.
|
||||
return oldCache, nil
|
||||
case rrCache.IsNXDomain():
|
||||
// The new result is NXDomain, return the old cache entry instead.
|
||||
return oldCache, nil
|
||||
if oldCache != nil {
|
||||
oldCache.isBackup = true
|
||||
|
||||
switch {
|
||||
case err != nil:
|
||||
// There was an error during resolving, return the old cache entry instead.
|
||||
log.Tracer(ctx).Debugf("resolver: serving backup cache of %s because query failed: %s", q.ID(), err)
|
||||
return oldCache, nil
|
||||
case rrCache.IsNXDomain():
|
||||
// The new result is NXDomain, return the old cache entry instead.
|
||||
log.Tracer(ctx).Debugf("resolver: serving backup cache of %s because fresh response is NXDomain", q.ID())
|
||||
return oldCache, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Return error, if there is one.
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Save the new entry if cache is enabled.
|
||||
@@ -406,7 +417,7 @@ resolveLoop:
|
||||
rrCache.Clean(minTTL)
|
||||
err = rrCache.Save()
|
||||
if err != nil {
|
||||
log.Warningf("resolver: failed to cache RR for %s%s: %s", q.FQDN, q.QType.String(), err)
|
||||
log.Tracer(ctx).Warningf("resolver: failed to cache RR for %s: %s", q.ID(), err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ type RRCache struct {
|
||||
Domain string // constant
|
||||
Question dns.Type // constant
|
||||
|
||||
Answer []dns.RR // might be mixed
|
||||
Answer []dns.RR // constant
|
||||
Ns []dns.RR // constant
|
||||
Extra []dns.RR // constant
|
||||
TTL int64 // constant
|
||||
@@ -34,6 +34,7 @@ type RRCache struct {
|
||||
|
||||
servedFromCache bool // mutable
|
||||
requestingNew bool // mutable
|
||||
isBackup bool // mutable
|
||||
Filtered bool // mutable
|
||||
FilteredEntries []string // mutable
|
||||
|
||||
@@ -55,19 +56,11 @@ func (rrCache *RRCache) ExpiresSoon() bool {
|
||||
return rrCache.TTL <= time.Now().Unix()+refreshTTL
|
||||
}
|
||||
|
||||
// MixAnswers randomizes the answer records to allow dumb clients (who only look at the first record) to reliably connect.
|
||||
func (rrCache *RRCache) MixAnswers() {
|
||||
// Clean sets all TTLs to 17 and sets cache expiry with specified minimum.
|
||||
func (rrCache *RRCache) Clean(minExpires uint32) {
|
||||
rrCache.Lock()
|
||||
defer rrCache.Unlock()
|
||||
|
||||
for i := range rrCache.Answer {
|
||||
j := rand.Intn(i + 1)
|
||||
rrCache.Answer[i], rrCache.Answer[j] = rrCache.Answer[j], rrCache.Answer[i]
|
||||
}
|
||||
}
|
||||
|
||||
// Clean sets all TTLs to 17 and sets cache expiry with specified minimum.
|
||||
func (rrCache *RRCache) Clean(minExpires uint32) {
|
||||
var lowestTTL uint32 = 0xFFFFFFFF
|
||||
var header *dns.RR_Header
|
||||
|
||||
@@ -221,6 +214,9 @@ func (rrCache *RRCache) Flags() string {
|
||||
if rrCache.requestingNew {
|
||||
s += "R"
|
||||
}
|
||||
if rrCache.isBackup {
|
||||
s += "B"
|
||||
}
|
||||
if rrCache.Filtered {
|
||||
s += "F"
|
||||
}
|
||||
@@ -253,6 +249,7 @@ func (rrCache *RRCache) ShallowCopy() *RRCache {
|
||||
updated: rrCache.updated,
|
||||
servedFromCache: rrCache.servedFromCache,
|
||||
requestingNew: rrCache.requestingNew,
|
||||
isBackup: rrCache.isBackup,
|
||||
Filtered: rrCache.Filtered,
|
||||
FilteredEntries: rrCache.FilteredEntries,
|
||||
}
|
||||
@@ -263,13 +260,23 @@ func (rrCache *RRCache) ReplyWithDNS(ctx context.Context, request *dns.Msg) *dns
|
||||
// reply to query
|
||||
reply := new(dns.Msg)
|
||||
reply.SetRcode(request, dns.RcodeSuccess)
|
||||
reply.Answer = rrCache.Answer
|
||||
reply.Ns = rrCache.Ns
|
||||
reply.Extra = rrCache.Extra
|
||||
|
||||
// Set NXDomain return code.
|
||||
if rrCache.IsNXDomain() {
|
||||
// Set NXDomain return code if not the reply has no answers.
|
||||
reply.Rcode = dns.RcodeNameError
|
||||
} else {
|
||||
// Copy answers, as we randomize their order a little.
|
||||
reply.Answer = make([]dns.RR, len(rrCache.Answer))
|
||||
copy(reply.Answer, rrCache.Answer)
|
||||
|
||||
// Randomize the order of the answer records a little to allow dumb clients
|
||||
// (who only look at the first record) to reliably connect.
|
||||
for i := range reply.Answer {
|
||||
j := rand.Intn(i + 1)
|
||||
reply.Answer[i], reply.Answer[j] = reply.Answer[j], reply.Answer[i]
|
||||
}
|
||||
}
|
||||
|
||||
return reply
|
||||
@@ -286,13 +293,16 @@ func (rrCache *RRCache) GetExtraRRs(ctx context.Context, query *dns.Msg) (extra
|
||||
|
||||
// Add expiry and cache information.
|
||||
if rrCache.Expired() {
|
||||
extra = addExtra(ctx, extra, fmt.Sprintf("record expired since %s, requesting new", time.Since(time.Unix(rrCache.TTL, 0))))
|
||||
extra = addExtra(ctx, extra, fmt.Sprintf("record expired since %s", time.Since(time.Unix(rrCache.TTL, 0)).Round(time.Second)))
|
||||
} else {
|
||||
extra = addExtra(ctx, extra, fmt.Sprintf("record valid for %s", time.Until(time.Unix(rrCache.TTL, 0))))
|
||||
extra = addExtra(ctx, extra, fmt.Sprintf("record valid for %s", time.Until(time.Unix(rrCache.TTL, 0)).Round(time.Second)))
|
||||
}
|
||||
if rrCache.requestingNew {
|
||||
extra = addExtra(ctx, extra, "async request to refresh the cache has been started")
|
||||
}
|
||||
if rrCache.isBackup {
|
||||
extra = addExtra(ctx, extra, "this record is served because a fresh request failed")
|
||||
}
|
||||
|
||||
// Add information about filtered entries.
|
||||
if rrCache.Filtered {
|
||||
|
||||
Reference in New Issue
Block a user