fix: handle funky clocks when removing expired addresses

When setting the TTL to 0, we expect `UpdateAddrs` to atomically delete
the addresses. However, due to funky clock stuff, "now" isn't always
consistent and seems to make us think these addresses are _about_ to
expire.

Funky clock stuff -> `time.Now().Add(0).Unix() > time.Now().Unix()` in
some cases...
This commit is contained in:
Steven Allen 2021-07-16 20:31:43 -07:00
parent 1907cca69f
commit 810b492220

View File

@ -99,14 +99,16 @@ func (r *addrsRecord) clean() (chgd bool) {
return true return true
} }
if r.dirty && addrsLen > 1 { // First remove expired.
r.Addrs = removeExpired(r.Addrs, now)
// Then sort if there's anything to do.
if r.dirty && len(r.Addrs) > 0 {
sort.Slice(r.Addrs, func(i, j int) bool { sort.Slice(r.Addrs, func(i, j int) bool {
return r.Addrs[i].Expiry < r.Addrs[j].Expiry return r.Addrs[i].Expiry < r.Addrs[j].Expiry
}) })
} }
r.Addrs = removeExpired(r.Addrs, now)
return r.dirty || len(r.Addrs) != addrsLen return r.dirty || len(r.Addrs) != addrsLen
} }
@ -118,17 +120,22 @@ func (r *addrsRecord) hasExpiredAddrs(now int64) bool {
} }
func removeExpired(entries []*pb.AddrBookRecord_AddrEntry, now int64) []*pb.AddrBookRecord_AddrEntry { func removeExpired(entries []*pb.AddrBookRecord_AddrEntry, now int64) []*pb.AddrBookRecord_AddrEntry {
// since addresses are sorted by expiration, we find the first // Filter out expired addresses in-place.
// survivor and split the slice on its index. newEntries := entries[:0]
pivot := -1 for _, addr := range entries {
for i, addr := range entries { // Sometimes clocks do funy things so we check the TTL as well.
if addr.Expiry > now { if addr.Ttl == 0 || addr.Expiry <= now {
break continue
} }
pivot = i newEntries = append(newEntries, addr)
} }
return entries[pivot+1:] // Let Go's GC free the remaining addresses.
for i := len(newEntries); i < len(entries); i++ {
entries[i] = nil
}
return newEntries
} }
// dsAddrBook is an address book backed by a Datastore with a GC procedure to purge expired entries. It uses an // dsAddrBook is an address book backed by a Datastore with a GC procedure to purge expired entries. It uses an