Skip to content

Commit 047d9a6

Browse files
delete entries from the cache when the TTL expires
1 parent 42c42d2 commit 047d9a6

File tree

5 files changed

+61
-14
lines changed

5 files changed

+61
-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
@@ -159,6 +159,8 @@ func newClient(opts clientOpts) (*client, error) {
159159
}, nil
160160
}
161161

162+
var cleanupFreq = 10 * time.Second
163+
162164
// Start listeners and waits for the shutdown signal from exit channel
163165
func (c *client) mainloop(ctx context.Context, params *lookupParams) {
164166
// start listening for responses
@@ -171,16 +173,28 @@ func (c *client) mainloop(ctx context.Context, params *lookupParams) {
171173
}
172174

173175
// Iterate through channels from listeners goroutines
174-
var entries, sentEntries map[string]*ServiceEntry
175-
sentEntries = make(map[string]*ServiceEntry)
176+
var entries map[string]*ServiceEntry
177+
sentEntries := make(map[string]*ServiceEntry)
178+
179+
ticker := time.NewTicker(cleanupFreq)
180+
defer ticker.Stop()
176181
for {
182+
var now time.Time
177183
select {
178184
case <-ctx.Done():
179185
// Context expired. Notify subscriber that we are done here.
180186
params.done()
181187
c.shutdown()
182188
return
189+
case t := <-ticker.C:
190+
for k, e := range sentEntries {
191+
if t.After(e.Expiry) {
192+
delete(sentEntries, k)
193+
}
194+
}
195+
continue
183196
case msg := <-msgCh:
197+
now = time.Now()
184198
entries = make(map[string]*ServiceEntry)
185199
sections := append(msg.Answer, msg.Ns...)
186200
sections = append(sections, msg.Extra...)
@@ -200,7 +214,7 @@ func (c *client) mainloop(ctx context.Context, params *lookupParams) {
200214
params.Service,
201215
params.Domain)
202216
}
203-
entries[rr.Ptr].TTL = rr.Hdr.Ttl
217+
entries[rr.Ptr].Expiry = now.Add(time.Duration(rr.Hdr.Ttl) * time.Second)
204218
case *dns.SRV:
205219
if params.ServiceInstanceName() != "" && params.ServiceInstanceName() != rr.Hdr.Name {
206220
continue
@@ -215,7 +229,7 @@ func (c *client) mainloop(ctx context.Context, params *lookupParams) {
215229
}
216230
entries[rr.Hdr.Name].HostName = rr.Target
217231
entries[rr.Hdr.Name].Port = int(rr.Port)
218-
entries[rr.Hdr.Name].TTL = rr.Hdr.Ttl
232+
entries[rr.Hdr.Name].Expiry = now.Add(time.Duration(rr.Hdr.Ttl) * time.Second)
219233
case *dns.TXT:
220234
if params.ServiceInstanceName() != "" && params.ServiceInstanceName() != rr.Hdr.Name {
221235
continue
@@ -229,7 +243,7 @@ func (c *client) mainloop(ctx context.Context, params *lookupParams) {
229243
params.Domain)
230244
}
231245
entries[rr.Hdr.Name].Text = rr.Txt
232-
entries[rr.Hdr.Name].TTL = rr.Hdr.Ttl
246+
entries[rr.Hdr.Name].Expiry = now.Add(time.Duration(rr.Hdr.Ttl) * time.Second)
233247
}
234248
}
235249
// Associate IPs in a second round as other fields should be filled by now.
@@ -253,7 +267,7 @@ func (c *client) mainloop(ctx context.Context, params *lookupParams) {
253267

254268
if len(entries) > 0 {
255269
for k, e := range entries {
256-
if e.TTL == 0 {
270+
if !e.Expiry.After(now) {
257271
delete(entries, k)
258272
delete(sentEntries, k)
259273
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: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,4 +146,34 @@ 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+
if err := Browse(ctx, mdnsService, mdnsDomain, entries); err != nil {
166+
t.Fatalf("Expected browse success, but got %v", err)
167+
}
168+
169+
<-ctx.Done()
170+
if len(entries) != 2 {
171+
t.Fatalf("Expected to have received 2 entries, but got %d", len(entries))
172+
}
173+
res1 := <-entries
174+
res2 := <-entries
175+
if res1.ServiceInstanceName() != res2.ServiceInstanceName() {
176+
t.Fatalf("expected the two entries to be identical")
177+
}
178+
})
149179
}

0 commit comments

Comments
 (0)