Skip to content

Commit 1137789

Browse files
delete entries from the cache when the TTL expires
1 parent 1df3d26 commit 1137789

File tree

5 files changed

+65
-14
lines changed

5 files changed

+65
-14
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ See what needs to be done and submit a pull request :)
9494
* [x] Browse / Lookup / Register services
9595
* [x] Multiple IPv6 / IPv4 addresses support
9696
* [x] Send multiple probes (exp. back-off) if no service answers (*)
97-
* [ ] Timestamp entries for TTL checks
97+
* [x] Timestamp entries for TTL checks
9898
* [ ] Compare new multicasts with already received services
9999

100100
_Notes:_

client.go

+20-6
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,8 @@ func newClient(opts clientOpts) (*client, error) {
177177
}, nil
178178
}
179179

180+
var cleanupFreq = 10 * time.Second
181+
180182
// Start listeners and waits for the shutdown signal from exit channel
181183
func (c *client) mainloop(ctx context.Context, params *lookupParams) {
182184
// start listening for responses
@@ -189,16 +191,28 @@ func (c *client) mainloop(ctx context.Context, params *lookupParams) {
189191
}
190192

191193
// Iterate through channels from listeners goroutines
192-
var entries, sentEntries map[string]*ServiceEntry
193-
sentEntries = make(map[string]*ServiceEntry)
194+
var entries map[string]*ServiceEntry
195+
sentEntries := make(map[string]*ServiceEntry)
196+
197+
ticker := time.NewTicker(cleanupFreq)
198+
defer ticker.Stop()
194199
for {
200+
var now time.Time
195201
select {
196202
case <-ctx.Done():
197203
// Context expired. Notify subscriber that we are done here.
198204
params.done()
199205
c.shutdown()
200206
return
207+
case t := <-ticker.C:
208+
for k, e := range sentEntries {
209+
if t.After(e.Expiry) {
210+
delete(sentEntries, k)
211+
}
212+
}
213+
continue
201214
case msg := <-msgCh:
215+
now = time.Now()
202216
entries = make(map[string]*ServiceEntry)
203217
sections := append(msg.Answer, msg.Ns...)
204218
sections = append(sections, msg.Extra...)
@@ -218,7 +232,7 @@ func (c *client) mainloop(ctx context.Context, params *lookupParams) {
218232
params.Service,
219233
params.Domain)
220234
}
221-
entries[rr.Ptr].TTL = rr.Hdr.Ttl
235+
entries[rr.Ptr].Expiry = now.Add(time.Duration(rr.Hdr.Ttl) * time.Second)
222236
case *dns.SRV:
223237
if params.ServiceInstanceName() != "" && params.ServiceInstanceName() != rr.Hdr.Name {
224238
continue
@@ -233,7 +247,7 @@ func (c *client) mainloop(ctx context.Context, params *lookupParams) {
233247
}
234248
entries[rr.Hdr.Name].HostName = rr.Target
235249
entries[rr.Hdr.Name].Port = int(rr.Port)
236-
entries[rr.Hdr.Name].TTL = rr.Hdr.Ttl
250+
entries[rr.Hdr.Name].Expiry = now.Add(time.Duration(rr.Hdr.Ttl) * time.Second)
237251
case *dns.TXT:
238252
if params.ServiceInstanceName() != "" && params.ServiceInstanceName() != rr.Hdr.Name {
239253
continue
@@ -247,7 +261,7 @@ func (c *client) mainloop(ctx context.Context, params *lookupParams) {
247261
params.Domain)
248262
}
249263
entries[rr.Hdr.Name].Text = rr.Txt
250-
entries[rr.Hdr.Name].TTL = rr.Hdr.Ttl
264+
entries[rr.Hdr.Name].Expiry = now.Add(time.Duration(rr.Hdr.Ttl) * time.Second)
251265
}
252266
}
253267
// Associate IPs in a second round as other fields should be filled by now.
@@ -271,7 +285,7 @@ func (c *client) mainloop(ctx context.Context, params *lookupParams) {
271285

272286
if len(entries) > 0 {
273287
for k, e := range entries {
274-
if e.TTL == 0 {
288+
if !e.Expiry.After(now) {
275289
delete(entries, k)
276290
delete(sentEntries, k)
277291
continue

server.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ const (
2121
multicastRepetitions = 2
2222
)
2323

24+
var defaultTTL uint32 = 3200
25+
2426
// Register a service by given arguments. This call will take the system's hostname
2527
// and lookup IP by that hostname.
2628
func Register(instance, service, domain string, port int, text []string, ifaces []net.Interface) (*Server, error) {
@@ -173,7 +175,7 @@ func newServer(ifaces []net.Interface) (*Server, error) {
173175
ipv4conn: ipv4conn,
174176
ipv6conn: ipv6conn,
175177
ifaces: ifaces,
176-
ttl: 3200,
178+
ttl: defaultTTL,
177179
shouldShutdown: make(chan struct{}),
178180
}
179181

service.go

+7-6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"net"
66
"sync"
7+
"time"
78
)
89

910
// ServiceRecord contains the basic description of a service, which contains instance name, service type & domain
@@ -103,12 +104,12 @@ func (l *lookupParams) disableProbing() {
103104
// used to answer multicast queries.
104105
type ServiceEntry struct {
105106
ServiceRecord
106-
HostName string `json:"hostname"` // Host machine DNS name
107-
Port int `json:"port"` // Service Port
108-
Text []string `json:"text"` // Service info served as a TXT record
109-
TTL uint32 `json:"ttl"` // TTL of the service record
110-
AddrIPv4 []net.IP `json:"-"` // Host machine IPv4 address
111-
AddrIPv6 []net.IP `json:"-"` // Host machine IPv6 address
107+
HostName string `json:"hostname"` // Host machine DNS name
108+
Port int `json:"port"` // Service Port
109+
Text []string `json:"text"` // Service info served as a TXT record
110+
Expiry time.Time `json:"expiry"` // Expiry of the service entry, will be converted to a TTL value
111+
AddrIPv4 []net.IP `json:"-"` // Host machine IPv4 address
112+
AddrIPv6 []net.IP `json:"-"` // Host machine IPv6 address
112113
}
113114

114115
// NewServiceEntry constructs a ServiceEntry.

service_test.go

+34
Original file line numberDiff line numberDiff line change
@@ -163,4 +163,38 @@ func TestSubtype(t *testing.T) {
163163
t.Fatalf("Expected port is %d, but got %d", mdnsPort, result.Port)
164164
}
165165
})
166+
167+
t.Run("ttl", func(t *testing.T) {
168+
origTTL := defaultTTL
169+
origCleanupFreq := cleanupFreq
170+
defer func() {
171+
defaultTTL = origTTL
172+
cleanupFreq = origCleanupFreq
173+
}()
174+
defaultTTL = 2 // 2 seconds
175+
cleanupFreq = 100 * time.Millisecond
176+
177+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
178+
defer cancel()
179+
go startMDNS(ctx, mdnsPort, mdnsName, mdnsSubtype, mdnsDomain)
180+
181+
entries := make(chan *ServiceEntry, 100)
182+
resolver, err := NewResolver(nil)
183+
if err != nil {
184+
t.Fatalf("Expected create resolver success, but got %v", err)
185+
}
186+
if err := resolver.Browse(ctx, mdnsService, mdnsDomain, entries); err != nil {
187+
t.Fatalf("Expected browse success, but got %v", err)
188+
}
189+
190+
<-ctx.Done()
191+
if len(entries) != 2 {
192+
t.Fatalf("Expected to have received 2 entries, but got %d", len(entries))
193+
}
194+
res1 := <-entries
195+
res2 := <-entries
196+
if res1.ServiceInstanceName() != res2.ServiceInstanceName() {
197+
t.Fatalf("expected the two entries to be identical")
198+
}
199+
})
166200
}

0 commit comments

Comments
 (0)