Skip to content
This repository was archived by the owner on Aug 12, 2021. It is now read-only.

Commit 6724681

Browse files
delete entries from the cache when the TTL expires
1 parent 5bca855 commit 6724681

File tree

5 files changed

+65
-14
lines changed

5 files changed

+65
-14
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ See what needs to be done and submit a pull request :)
8989
* [x] Browse / Lookup / Register services
9090
* [x] Multiple IPv6 / IPv4 addresses support
9191
* [x] Send multiple probes (exp. back-off) if no service answers (*)
92-
* [ ] Timestamp entries for TTL checks
92+
* [x] Timestamp entries for TTL checks
9393
* [ ] Compare new multicasts with already received services
9494

9595
_Notes:_

client.go

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ func newClient(opts clientOpts) (*client, error) {
155155
}, nil
156156
}
157157

158+
var cleanupFreq = 10 * time.Second
159+
158160
// Start listeners and waits for the shutdown signal from exit channel
159161
func (c *client) mainloop(ctx context.Context, params *lookupParams) {
160162
// start listening for responses
@@ -167,16 +169,28 @@ func (c *client) mainloop(ctx context.Context, params *lookupParams) {
167169
}
168170

169171
// Iterate through channels from listeners goroutines
170-
var entries, sentEntries map[string]*ServiceEntry
171-
sentEntries = make(map[string]*ServiceEntry)
172+
var entries map[string]*ServiceEntry
173+
sentEntries := make(map[string]*ServiceEntry)
174+
175+
ticker := time.NewTicker(cleanupFreq)
176+
defer ticker.Stop()
172177
for {
178+
var now time.Time
173179
select {
174180
case <-ctx.Done():
175181
// Context expired. Notify subscriber that we are done here.
176182
params.done()
177183
c.shutdown()
178184
return
185+
case t := <-ticker.C:
186+
for k, e := range sentEntries {
187+
if t.After(e.Expiry) {
188+
delete(sentEntries, k)
189+
}
190+
}
191+
continue
179192
case msg := <-msgCh:
193+
now = time.Now()
180194
entries = make(map[string]*ServiceEntry)
181195
sections := append(msg.Answer, msg.Ns...)
182196
sections = append(sections, msg.Extra...)
@@ -196,7 +210,7 @@ func (c *client) mainloop(ctx context.Context, params *lookupParams) {
196210
params.Service,
197211
params.Domain)
198212
}
199-
entries[rr.Ptr].TTL = rr.Hdr.Ttl
213+
entries[rr.Ptr].Expiry = now.Add(time.Duration(rr.Hdr.Ttl) * time.Second)
200214
case *dns.SRV:
201215
if params.ServiceInstanceName() != "" && params.ServiceInstanceName() != rr.Hdr.Name {
202216
continue
@@ -211,7 +225,7 @@ func (c *client) mainloop(ctx context.Context, params *lookupParams) {
211225
}
212226
entries[rr.Hdr.Name].HostName = rr.Target
213227
entries[rr.Hdr.Name].Port = int(rr.Port)
214-
entries[rr.Hdr.Name].TTL = rr.Hdr.Ttl
228+
entries[rr.Hdr.Name].Expiry = now.Add(time.Duration(rr.Hdr.Ttl) * time.Second)
215229
case *dns.TXT:
216230
if params.ServiceInstanceName() != "" && params.ServiceInstanceName() != rr.Hdr.Name {
217231
continue
@@ -225,7 +239,7 @@ func (c *client) mainloop(ctx context.Context, params *lookupParams) {
225239
params.Domain)
226240
}
227241
entries[rr.Hdr.Name].Text = rr.Txt
228-
entries[rr.Hdr.Name].TTL = rr.Hdr.Ttl
242+
entries[rr.Hdr.Name].Expiry = now.Add(time.Duration(rr.Hdr.Ttl) * time.Second)
229243
}
230244
}
231245
// Associate IPs in a second round as other fields should be filled by now.
@@ -249,7 +263,7 @@ func (c *client) mainloop(ctx context.Context, params *lookupParams) {
249263

250264
if len(entries) > 0 {
251265
for k, e := range entries {
252-
if e.TTL == 0 {
266+
if !e.Expiry.After(now) {
253267
delete(entries, k)
254268
delete(sentEntries, k)
255269
continue

server.go

Lines changed: 3 additions & 1 deletion
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

Lines changed: 7 additions & 6 deletions
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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,4 +146,38 @@ func TestSubtype(t *testing.T) {
146146
t.Fatalf("Expected port is %d, but got %d", mdnsPort, result.Port)
147147
}
148148
})
149+
150+
t.Run("ttl", func(t *testing.T) {
151+
origTTL := defaultTTL
152+
origCleanupFreq := cleanupFreq
153+
defer func() {
154+
defaultTTL = origTTL
155+
cleanupFreq = origCleanupFreq
156+
}()
157+
defaultTTL = 2 // 2 seconds
158+
cleanupFreq = 100 * time.Millisecond
159+
160+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
161+
defer cancel()
162+
go startMDNS(ctx, mdnsPort, mdnsName, mdnsSubtype, mdnsDomain)
163+
164+
entries := make(chan *ServiceEntry, 100)
165+
resolver, err := NewResolver(nil)
166+
if err != nil {
167+
t.Fatalf("Expected create resolver success, but got %v", err)
168+
}
169+
if err := resolver.Browse(ctx, mdnsService, mdnsDomain, entries); err != nil {
170+
t.Fatalf("Expected browse success, but got %v", err)
171+
}
172+
173+
<-ctx.Done()
174+
if len(entries) != 2 {
175+
t.Fatalf("Expected to have received 2 entries, but got %d", len(entries))
176+
}
177+
res1 := <-entries
178+
res2 := <-entries
179+
if res1.ServiceInstanceName() != res2.ServiceInstanceName() {
180+
t.Fatalf("expected the two entries to be identical")
181+
}
182+
})
149183
}

0 commit comments

Comments
 (0)