Files
coredns/plugin/file/zone.go
wencyu deae7ec345 Performance tuning for plugin/file (#7658)
* plugin/file: improve performance of function tree.less(..)

PrevLabel always begins its iteration from the tail of domain name.
less(..) loop can improve its performance by calling PrevLabel starting
from the last processed label.

As the benchmark results showed, the performance is improved by about 15%.
$ go test -bench=Less -run=^$
goos: linux
goarch: amd64
pkg: github.com/coredns/coredns/plugin/file/tree
cpu: Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz
BenchmarkLess/base-16              99003             12105 ns/op
BenchmarkLess/optimized-16        114522             10590 ns/op
PASS
ok      github.com/coredns/coredns/plugin/file/tree     2.416s

Signed-off-by: yuwenchao <ywc689@163.com>

* plugin/file: performance enhancement for nameFromRight(..)

Similar to tree.less(..), performance of function nameFromRight
can boost by utilizing dns.PrevLabel more efficiently.

As benchmark tests shown, performance of this function with the
optimization is gained by double or triple.

* Benchmark test result for the original implementation:
BenchmarkNameFromRight/i0_origin-16     430719652                2.794 ns/op
BenchmarkNameFromRight/eq_origin_i1_shot-16             30933135                37.52 ns/op
BenchmarkNameFromRight/two_labels_i1-16                 29375857                40.71 ns/op
BenchmarkNameFromRight/two_labels_i2-16                 18556830                63.97 ns/op
BenchmarkNameFromRight/two_labels_i3_shot-16            14678812                84.73 ns/op
BenchmarkNameFromRight/ten_labels_i5-16                  8522132               133.0 ns/op
BenchmarkNameFromRight/ten_labels_i11_shot-16            3154410               378.2 ns/op
BenchmarkNameFromRight/not_subdomain_shot-16            35297224                33.59 ns/op
BenchmarkNameFromRightRandomized-16                     10638702               113.4 ns/op             0 B/op          0 allocs/op

* Benchmark test result with this optimization:
BenchmarkNameFromRight/i0_origin-16     425864671                2.808 ns/op
BenchmarkNameFromRight/eq_origin_i1_shot-16             60903428                19.53 ns/op
BenchmarkNameFromRight/two_labels_i1-16                 50209297                24.21 ns/op
BenchmarkNameFromRight/two_labels_i2-16                 42483711                27.88 ns/op
BenchmarkNameFromRight/two_labels_i3_shot-16            40898925                29.24 ns/op
BenchmarkNameFromRight/ten_labels_i5-16                 27916532                44.54 ns/op
BenchmarkNameFromRight/ten_labels_i11_shot-16           17540040                67.59 ns/op
BenchmarkNameFromRight/not_subdomain_shot-16            67180514                17.46 ns/op
BenchmarkNameFromRightRandomized-16                     32692081                38.21 ns/op            0 B/op          0 allocs/op

Signed-off-by: yuwenchao <yuwenchao@bytedance.com>

---------

Signed-off-by: yuwenchao <ywc689@163.com>
Signed-off-by: yuwenchao <yuwenchao@bytedance.com>
Co-authored-by: yuwenchao <yuwenchao@bytedance.com>
2025-11-06 13:12:49 -08:00

185 lines
3.9 KiB
Go

package file
import (
"fmt"
"path/filepath"
"strings"
"sync"
"time"
"github.com/coredns/coredns/plugin/file/tree"
"github.com/coredns/coredns/plugin/pkg/upstream"
"github.com/miekg/dns"
)
// Zone is a structure that contains all data related to a DNS zone.
type Zone struct {
origin string
origLen int
file string
*tree.Tree
Apex
Expired bool
sync.RWMutex
StartupOnce sync.Once
TransferFrom []string
ReloadInterval time.Duration
reloadShutdown chan bool
Upstream *upstream.Upstream // Upstream for looking up external names during the resolution process.
}
// Apex contains the apex records of a zone: SOA, NS and their potential signatures.
type Apex struct {
SOA *dns.SOA
NS []dns.RR
SIGSOA []dns.RR
SIGNS []dns.RR
}
// NewZone returns a new zone.
func NewZone(name, file string) *Zone {
return &Zone{
origin: dns.Fqdn(name),
origLen: dns.CountLabel(dns.Fqdn(name)),
file: filepath.Clean(file),
Tree: &tree.Tree{},
reloadShutdown: make(chan bool),
}
}
// Copy copies a zone.
func (z *Zone) Copy() *Zone {
z1 := NewZone(z.origin, z.file)
z1.TransferFrom = z.TransferFrom
z1.Expired = z.Expired
z1.Apex = z.Apex
return z1
}
// CopyWithoutApex copies zone z without the Apex records.
func (z *Zone) CopyWithoutApex() *Zone {
z1 := NewZone(z.origin, z.file)
z1.TransferFrom = z.TransferFrom
z1.Expired = z.Expired
return z1
}
// Insert inserts r into z.
func (z *Zone) Insert(r dns.RR) error {
// r.Header().Name = strings.ToLower(r.Header().Name)
if r.Header().Rrtype != dns.TypeSRV {
r.Header().Name = strings.ToLower(r.Header().Name)
}
switch h := r.Header().Rrtype; h {
case dns.TypeNS:
r.(*dns.NS).Ns = strings.ToLower(r.(*dns.NS).Ns)
if r.Header().Name == z.origin {
z.NS = append(z.NS, r)
return nil
}
case dns.TypeSOA:
r.(*dns.SOA).Ns = strings.ToLower(r.(*dns.SOA).Ns)
r.(*dns.SOA).Mbox = strings.ToLower(r.(*dns.SOA).Mbox)
z.SOA = r.(*dns.SOA)
return nil
case dns.TypeNSEC3, dns.TypeNSEC3PARAM:
return fmt.Errorf("NSEC3 zone is not supported, dropping RR: %s for zone: %s", r.Header().Name, z.origin)
case dns.TypeRRSIG:
x := r.(*dns.RRSIG)
switch x.TypeCovered {
case dns.TypeSOA:
z.SIGSOA = append(z.SIGSOA, x)
return nil
case dns.TypeNS:
if r.Header().Name == z.origin {
z.SIGNS = append(z.SIGNS, x)
return nil
}
}
case dns.TypeCNAME:
r.(*dns.CNAME).Target = strings.ToLower(r.(*dns.CNAME).Target)
case dns.TypeMX:
r.(*dns.MX).Mx = strings.ToLower(r.(*dns.MX).Mx)
case dns.TypeSRV:
// r.(*dns.SRV).Target = strings.ToLower(r.(*dns.SRV).Target)
}
z.Tree.Insert(r)
return nil
}
// File retrieves the file path in a safe way.
func (z *Zone) File() string {
z.RLock()
defer z.RUnlock()
return z.file
}
// SetFile updates the file path in a safe way.
func (z *Zone) SetFile(path string) {
z.Lock()
z.file = path
z.Unlock()
}
// ApexIfDefined returns the apex nodes from z. The SOA record is the first record, if it does not exist, an error is returned.
func (z *Zone) ApexIfDefined() ([]dns.RR, error) {
z.RLock()
defer z.RUnlock()
if z.SOA == nil {
return nil, fmt.Errorf("no SOA")
}
rrs := []dns.RR{z.SOA}
if len(z.SIGSOA) > 0 {
rrs = append(rrs, z.SIGSOA...)
}
if len(z.NS) > 0 {
rrs = append(rrs, z.NS...)
}
if len(z.SIGNS) > 0 {
rrs = append(rrs, z.SIGNS...)
}
return rrs, nil
}
// NameFromRight returns the labels from the right, staring with the
// origin and then i labels extra. When we are overshooting the name
// the returned boolean is set to true.
func (z *Zone) nameFromRight(qname string, i int) (string, bool) {
if i <= 0 {
return z.origin, false
}
n := len(qname)
for j := 1; j <= z.origLen; j++ {
if m, shot := dns.PrevLabel(qname[:n], 1); shot {
return qname, shot
} else {
n = m
}
}
for j := 1; j <= i; j++ {
m, shot := dns.PrevLabel(qname[:n], 1)
if shot {
return qname, shot
} else {
n = m
}
}
return qname[n:], false
}