diff --git a/cmd/stdiscosrv/apisrv.go b/cmd/stdiscosrv/apisrv.go index 5ac4622a1..a848ebc93 100644 --- a/cmd/stdiscosrv/apisrv.go +++ b/cmd/stdiscosrv/apisrv.go @@ -18,6 +18,7 @@ import ( "net" "net/http" "net/url" + "sort" "strconv" "strings" "sync" @@ -279,6 +280,10 @@ func (s *apiSrv) handleAnnounce(remote net.IP, deviceID protocol.DeviceID, addre dbAddrs[i].Expires = expire } + // The address slice must always be sorted for database merges to work + // properly. + sort.Sort(databaseAddressOrder(dbAddrs)) + seen := now.UnixNano() if s.repl != nil { s.repl.send(key, dbAddrs, seen) diff --git a/cmd/stdiscosrv/database.go b/cmd/stdiscosrv/database.go index d4d02536f..84a28b7ae 100644 --- a/cmd/stdiscosrv/database.go +++ b/cmd/stdiscosrv/database.go @@ -10,6 +10,7 @@ package main import ( + "log" "sort" "time" @@ -263,12 +264,15 @@ func (s *levelDBStore) Stop() { // chosen for any duplicates. func merge(a, b DatabaseRecord) DatabaseRecord { // Both lists must be sorted for this to work. - sort.Slice(a.Addresses, func(i, j int) bool { - return a.Addresses[i].Address < a.Addresses[j].Address - }) - sort.Slice(b.Addresses, func(i, j int) bool { - return b.Addresses[i].Address < b.Addresses[j].Address - }) + if !sort.IsSorted(databaseAddressOrder(a.Addresses)) { + log.Println("Warning: bug: addresses not correctly sorted in merge") + a.Addresses = sortedAddressCopy(a.Addresses) + } + if !sort.IsSorted(databaseAddressOrder(b.Addresses)) { + // no warning because this is the side we read from disk and it may + // legitimately predate correct sorting. + b.Addresses = sortedAddressCopy(b.Addresses) + } res := DatabaseRecord{ Addresses: make([]DatabaseAddress, 0, len(a.Addresses)+len(b.Addresses)), @@ -352,3 +356,24 @@ func expire(addrs []DatabaseAddress, now int64) []DatabaseAddress { } return addrs } + +func sortedAddressCopy(addrs []DatabaseAddress) []DatabaseAddress { + sorted := make([]DatabaseAddress, len(addrs)) + copy(sorted, addrs) + sort.Sort(databaseAddressOrder(sorted)) + return sorted +} + +type databaseAddressOrder []DatabaseAddress + +func (s databaseAddressOrder) Less(a, b int) bool { + return s[a].Address < s[b].Address +} + +func (s databaseAddressOrder) Swap(a, b int) { + s[a], s[b] = s[b], s[a] +} + +func (s databaseAddressOrder) Len() int { + return len(s) +}