mirror of
https://github.com/coredns/coredns.git
synced 2026-10-09 03:55:21 -04:00
* plugin/dynupdate: add authenticated RFC 2136 updates Signed-off-by: houyuwushang <liuluoqianqiu@outlook.com> * plugin/dynupdate: fix README test fixtures Signed-off-by: houyuwushang <liuluoqianqiu@outlook.com> * test: format README fixture map Signed-off-by: houyuwushang <liuluoqianqiu@outlook.com> * plugin/dynupdate: persist updates and bound writable zones Signed-off-by: houyuwushang <liuluoqianqiu@outlook.com> * plugin/dynupdate: preserve middleware and fix interoperability fixtures Signed-off-by: houyuwushang <liuluoqianqiu@outlook.com> * test(dynupdate): validate Kea lifecycle and bounded zone costs Signed-off-by: houyuwushang <liuluoqianqiu@outlook.com> * plugin/dynupdate: reject duplicate directives and harden client fixtures Signed-off-by: houyuwushang <liuluoqianqiu@outlook.com> * plugin/dynupdate: fix update routing and startup validation Signed-off-by: houyuwushang <liuluoqianqiu@outlook.com> --------- Signed-off-by: houyuwushang <liuluoqianqiu@outlook.com>
142 lines
4.0 KiB
Go
142 lines
4.0 KiB
Go
package dynupdate
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/coredns/coredns/plugin/pkg/dnstest"
|
|
coretest "github.com/coredns/coredns/plugin/test"
|
|
|
|
"github.com/miekg/dns"
|
|
)
|
|
|
|
// BenchmarkUpdate measures a bounded, changing zone rather than duplicate
|
|
// no-op updates. Durable mode uses bbolt's normal synchronous commit path.
|
|
func BenchmarkUpdate(b *testing.B) {
|
|
for _, count := range []int{100, 1000, defaultMaxRecords} {
|
|
for _, durable := range []bool{false, true} {
|
|
for _, readers := range []int{0, 4} {
|
|
b.Run(fmt.Sprintf("records=%d/durable=%t/readers=%d", count, durable, readers), func(b *testing.B) {
|
|
d := benchmarkZone(b, count, durable)
|
|
updates := make([][]dns.RR, 2)
|
|
for i := range updates {
|
|
rr, err := dns.NewRR(fmt.Sprintf("change.example.org. 60 IN A 192.0.2.%d", i+1))
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
m := new(dns.Msg).SetUpdate(testZone)
|
|
m.RemoveRRset([]dns.RR{rr})
|
|
m.Insert([]dns.RR{rr})
|
|
updates[i] = m.Ns
|
|
}
|
|
var queries, queryNanos, maxQueryNanos atomic.Int64
|
|
var wg sync.WaitGroup
|
|
startQueries := make(chan struct{})
|
|
stop := make(chan struct{})
|
|
errors := make(chan error, readers)
|
|
for range readers {
|
|
wg.Go(func() {
|
|
query := new(dns.Msg).SetQuestion("change.example.org.", dns.TypeA)
|
|
<-startQueries
|
|
for {
|
|
select {
|
|
case <-stop:
|
|
return
|
|
default:
|
|
}
|
|
start := time.Now()
|
|
w := dnstest.NewRecorder(&coretest.ResponseWriter{})
|
|
code, err := d.ServeDNS(context.Background(), w, query)
|
|
if err != nil || code != dns.RcodeSuccess || w.Msg == nil || len(w.Msg.Answer) != 1 {
|
|
errors <- fmt.Errorf("concurrent query: code=%d err=%v reply=%v", code, err, w.Msg)
|
|
return
|
|
}
|
|
latency := time.Since(start).Nanoseconds()
|
|
queries.Add(1)
|
|
queryNanos.Add(latency)
|
|
for old := maxQueryNanos.Load(); latency > old; old = maxQueryNanos.Load() {
|
|
if maxQueryNanos.CompareAndSwap(old, latency) {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
start := time.Now()
|
|
close(startQueries)
|
|
for i := range b.N {
|
|
code, err := d.applyUpdate(testKey, nil, updates[i%2])
|
|
if code != dns.RcodeSuccess || err != nil {
|
|
b.Errorf("update: code=%d err=%v", code, err)
|
|
break
|
|
}
|
|
}
|
|
b.StopTimer()
|
|
close(stop)
|
|
wg.Wait()
|
|
elapsed := time.Since(start)
|
|
close(errors)
|
|
for err := range errors {
|
|
b.Error(err)
|
|
}
|
|
if n := queries.Load(); n > 0 {
|
|
b.ReportMetric(float64(queryNanos.Load())/float64(n), "ns/query")
|
|
b.ReportMetric(float64(maxQueryNanos.Load()), "max-ns/query")
|
|
b.ReportMetric(float64(n)/elapsed.Seconds(), "queries/s")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func benchmarkZone(b *testing.B, count int, durable bool) *DynUpdate {
|
|
b.Helper()
|
|
texts := []string{
|
|
"example.org. 60 IN SOA ns.example.org. hostmaster.example.org. 10 60 60 60 60",
|
|
"example.org. 60 IN NS ns.example.org.",
|
|
"change.example.org. 60 IN A 192.0.2.254",
|
|
}
|
|
for i := len(texts); i < count; i++ {
|
|
texts = append(texts, fmt.Sprintf("host-%d.example.org. 60 IN A 192.0.2.100", i))
|
|
}
|
|
d := &DynUpdate{Zone: testZone, permissions: []permission{{key: testKey, name: allNames, allTypes: true}}}
|
|
for _, text := range texts {
|
|
rr, err := dns.NewRR(text)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
d.records = append(d.records, rr)
|
|
}
|
|
var err error
|
|
d.view, err = d.build(d.records)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
if durable {
|
|
dir := b.TempDir()
|
|
d.seed, d.database = filepath.Join(dir, "seed.zone"), filepath.Join(dir, "zone.db")
|
|
if err := os.WriteFile(d.seed, []byte(strings.Join(texts, "\n")+"\n"), 0600); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
if err := d.ensureStore(); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
b.Cleanup(func() {
|
|
if err := d.close(); err != nil {
|
|
b.Error(err)
|
|
}
|
|
})
|
|
return d
|
|
}
|