control/controlclient: skip SetControlClientStatus when queue has newer results later

Updates tailscale/corp#26058

Change-Id: I3033d235ca49f9739fdf3deaf603eea4ec3e407e
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick 2025-01-24 13:09:21 -08:00
parent 69bc164c62
commit 0b4d9065e6

View File

@ -6,12 +6,14 @@ package controlclient
import (
"context"
"errors"
"expvar"
"fmt"
"net/http"
"sync"
"sync/atomic"
"time"
"tailscale.com/envknob"
"tailscale.com/logtail/backoff"
"tailscale.com/net/sockstats"
"tailscale.com/tailcfg"
@ -131,6 +133,8 @@ type Auto struct {
// the server.
lastUpdateGen updateGen
lastStatus atomic.Pointer[Status]
paused bool // whether we should stop making HTTP requests
unpauseWaiters []chan bool // chans that gets sent true (once) on wake, or false on Shutdown
loggedIn bool // true if currently logged in
@ -596,21 +600,61 @@ func (c *Auto) sendStatus(who string, err error, url string, nm *netmap.NetworkM
// not logged in.
nm = nil
}
new := Status{
newSt := &Status{
URL: url,
Persist: p,
NetMap: nm,
Err: err,
state: state,
}
c.lastStatus.Store(newSt)
// Launch a new goroutine to avoid blocking the caller while the observer
// does its thing, which may result in a call back into the client.
c.observerQueue.Add(func() {
c.observer.SetControlClientStatus(c, new)
if c.canSkipStatus(newSt) {
metricSkippable.Add(1)
if debugSkipQueue() {
metricSkipped.Add(1)
return
}
}
c.observer.SetControlClientStatus(c, *newSt)
c.lastStatus.CompareAndSwap(newSt, nil)
})
}
var debugSkipQueue = envknob.RegisterBool("TS_DEBUG_SKIP_STATUS_QUEUE")
var (
metricSkippable = expvar.NewInt("controlclient_auto_status_queue_skippable")
metricSkipped = expvar.NewInt("controlclient_auto_status_queue_skipped")
)
func (c *Auto) canSkipStatus(s1 *Status) bool {
s2 := c.lastStatus.Load()
if s2 == nil {
return false
}
if s1 == s2 {
return false
}
if s1.Err != nil || s1.URL != "" {
return false
}
if !s1.Persist.Equals(s2.Persist) || s1.state != s2.state {
return false
}
if s1.NetMap != nil && s2.NetMap != nil {
// If s1 doesn't have an error and doesn't have a URL
// and is otherwise identical to the latest one
// other than the a different NetMap, then this is
// skippable.
return true
}
return false
}
func (c *Auto) Login(flags LoginFlags) {
c.logf("client.Login(%v)", flags)