plugin/file: load secondary zones lazily on startup (#2944)

This fixes a long standing bug:
fixes: #1609

Load secondary zones in a go-routine; this required another mutex to
protect some fields; I think those were needded anyway because a
transfer can also happen when we're running; we just didn't have a test
for that situation.

The test had to be changed to wait for the transfer to happen at this is
async now.

Signed-off-by: Miek Gieben <miek@miek.nl>
This commit is contained in:
Miek Gieben 2019-06-29 22:22:34 +01:00 committed by Yong Tang
parent 2c1f5009be
commit 3a0c7c6153
7 changed files with 21 additions and 17 deletions

View file

@ -19,7 +19,7 @@ type (
// File is the plugin that reads zone data from disk.
File struct {
Next plugin.Handler
Zones Zones
Zones
}
// Zones maps zone names to a *Zone.

View file

@ -44,7 +44,9 @@ func (z *Zone) Lookup(ctx context.Context, state request.Request, qname string)
// If z is a secondary zone we might not have transferred it, meaning we have
// all zone context setup, except the actual record. This means (for one thing) the apex
// is empty and we don't have a SOA record.
z.apexMu.RLock()
soa := z.Apex.SOA
z.apexMu.RUnlock()
if soa == nil {
return nil, nil, nil, ServerFailure
}

View file

@ -51,9 +51,11 @@ Transfer:
return Err
}
z.apexMu.Lock()
z.Tree = z1.Tree
z.Apex = z1.Apex
*z.Expired = false
z.apexMu.Unlock()
log.Infof("Transferred: %s from %s", z.origin, tr)
return nil
}

View file

@ -80,7 +80,7 @@ func TestShouldTransfer(t *testing.T) {
}
defer s.Shutdown()
z := new(Zone)
z := NewZone("testzone", "test")
z.origin = testZone
z.TransferFrom = []string{addrstr}

View file

@ -21,7 +21,8 @@ type Zone struct {
origLen int
file string
*tree.Tree
Apex Apex
Apex
apexMu sync.RWMutex
TransferTo []string
StartupOnce sync.Once
@ -32,7 +33,7 @@ type Zone struct {
LastReloaded time.Time
reloadMu sync.RWMutex
reloadShutdown chan bool
Upstream *upstream.Upstream // Upstream for looking up external names during the resolution process
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.
@ -55,7 +56,6 @@ func NewZone(name, file string) *Zone {
LastReloaded: time.Now(),
}
*z.Expired = false
return z
}
@ -186,11 +186,6 @@ func (z *Zone) All() []dns.RR {
return append([]dns.RR{z.Apex.SOA}, records...)
}
// Print prints the zone's tree to stdout.
func (z *Zone) Print() {
z.Tree.Print()
}
// 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.

View file

@ -29,8 +29,8 @@ func setup(c *caddy.Controller) error {
if len(z.TransferFrom) > 0 {
c.OnStartup(func() error {
z.StartupOnce.Do(func() {
z.TransferIn()
go func() {
z.TransferIn()
z.Update()
}()
})

View file

@ -2,6 +2,7 @@ package test
import (
"testing"
"time"
"github.com/coredns/coredns/plugin/test"
@ -69,12 +70,16 @@ func TestSecondaryZoneTransfer(t *testing.T) {
m := new(dns.Msg)
m.SetQuestion("example.org.", dns.TypeSOA)
r, err := dns.Exchange(m, udp)
if err != nil {
t.Fatalf("Expected to receive reply, but didn't: %s", err)
}
var r *dns.Msg
// This is now async; we we need to wait for it to be transfered.
for i := 0; i < 10; i++ {
r, _ = dns.Exchange(m, udp)
if len(r.Answer) == 0 {
break
}
time.Sleep(100 * time.Microsecond)
}
if len(r.Answer) != 0 {
t.Fatalf("Expected answer section")
}
}