mirror of
https://github.com/tailscale/tailscale.git
synced 2025-02-18 02:48:40 +00:00
ipn/ipnlocal: do not process old status messages received out of order
When `setWgengineStatus` is invoked concurrently from multiple goroutines, it is possible that the call invoked with a newer status is processed before a call with an older status. e.g. a status that has endpoints might be followed by a status without endpoints. This causes unnecessary work in the engine and can result in packet loss. This patch adds an `AsOf time.Time` field to the status to specifiy when the status was calculated, which later allows `setWgengineStatus` to ignore any status messages it receives that are older than the one it has already processed. Updates tailscale/corp#2579 Signed-off-by: Maisem Ali <maisem@tailscale.com>
This commit is contained in:
parent
753f1bfad4
commit
6fecc16c3b
@ -140,6 +140,7 @@ type LocalBackend struct {
|
||||
peerAPIListeners []*peerAPIListener
|
||||
loginFlags controlclient.LoginFlags
|
||||
incomingFiles map[*incomingFile]bool
|
||||
lastStatusTime time.Time // status.AsOf value of the last processed status update
|
||||
// directFileRoot, if non-empty, means to write received files
|
||||
// directly to this directory, without staging them in an
|
||||
// intermediate buffered directory for "pick-up" later. If
|
||||
@ -706,6 +707,13 @@ func (b *LocalBackend) setWgengineStatus(s *wgengine.Status, err error) {
|
||||
}
|
||||
|
||||
b.mu.Lock()
|
||||
if s.AsOf.Before(b.lastStatusTime) {
|
||||
// Don't process a status update that is older than the one we have
|
||||
// already processed. (corp#2579)
|
||||
b.mu.Unlock()
|
||||
return
|
||||
}
|
||||
b.lastStatusTime = s.AsOf
|
||||
es := b.parseWgStatusLocked(s)
|
||||
cc := b.cc
|
||||
b.engineStatus = es
|
||||
|
@ -62,6 +62,7 @@ func (nt *notifyThrottler) put(n ipn.Notify) {
|
||||
// drain pulls the notifications out of the queue, asserting that there are
|
||||
// exactly count notifications that have been put so far.
|
||||
func (nt *notifyThrottler) drain(count int) []ipn.Notify {
|
||||
nt.t.Helper()
|
||||
nt.mu.Lock()
|
||||
ch := nt.ch
|
||||
nt.mu.Unlock()
|
||||
@ -923,7 +924,7 @@ func TestStateMachine(t *testing.T) {
|
||||
}
|
||||
notifies.expect(1)
|
||||
// Fake a DERP connection.
|
||||
b.setWgengineStatus(&wgengine.Status{DERPs: 1}, nil)
|
||||
b.setWgengineStatus(&wgengine.Status{DERPs: 1, AsOf: time.Now()}, nil)
|
||||
{
|
||||
nn := notifies.drain(1)
|
||||
cc.assertCalls("unpause")
|
||||
@ -1016,7 +1017,7 @@ func TestWGEngineStatusRace(t *testing.T) {
|
||||
if i == 0 {
|
||||
n = 1
|
||||
}
|
||||
b.setWgengineStatus(&wgengine.Status{DERPs: n}, nil)
|
||||
b.setWgengineStatus(&wgengine.Status{AsOf: time.Now(), DERPs: n}, nil)
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
|
@ -1113,6 +1113,7 @@ func (e *userspaceEngine) getStatus() (*Status, error) {
|
||||
}
|
||||
|
||||
return &Status{
|
||||
AsOf: time.Now(),
|
||||
LocalAddrs: append([]tailcfg.Endpoint(nil), e.endpoints...),
|
||||
Peers: peers,
|
||||
DERPs: derpConns,
|
||||
|
@ -6,6 +6,7 @@ package wgengine
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"inet.af/netaddr"
|
||||
"tailscale.com/ipn/ipnstate"
|
||||
@ -23,6 +24,7 @@ import (
|
||||
//
|
||||
// TODO(bradfitz): remove this, subset of ipnstate? Need to migrate users.
|
||||
type Status struct {
|
||||
AsOf time.Time // the time at which the status was calculated
|
||||
Peers []ipnstate.PeerStatusLite
|
||||
LocalAddrs []tailcfg.Endpoint // the set of possible endpoints for the magic conn
|
||||
DERPs int // number of active DERP connections
|
||||
|
Loading…
x
Reference in New Issue
Block a user