ipn/ipnlocal: don't run portlist code unless service collection is on

We were selectively uploading it, but we were still gathering it,
which can be a waste of CPU.

Also remove a bunch of complexity that I don't think matters anymore.

And add an envknob to force service collection off on a single node,
even if the tailnet policy permits it.

Fixes #13463

Change-Id: Ib6abe9e29d92df4ffa955225289f045eeeb279cf
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick 2024-10-02 16:55:01 -07:00 committed by Brad Fitzpatrick
parent d837e0252f
commit 383120c534

View File

@ -191,7 +191,6 @@ type LocalBackend struct {
unregisterHealthWatch func() unregisterHealthWatch func()
portpoll *portlist.Poller // may be nil portpoll *portlist.Poller // may be nil
portpollOnce sync.Once // guards starting readPoller portpollOnce sync.Once // guards starting readPoller
gotPortPollRes chan struct{} // closed upon first readPoller result
varRoot string // or empty if SetVarRoot never called varRoot string // or empty if SetVarRoot never called
logFlushFunc func() // or nil if SetLogFlusher wasn't called logFlushFunc func() // or nil if SetLogFlusher wasn't called
em *expiryManager // non-nil em *expiryManager // non-nil
@ -473,7 +472,6 @@ func NewLocalBackend(logf logger.Logf, logID logid.PublicID, sys *tsd.System, lo
state: ipn.NoState, state: ipn.NoState,
portpoll: new(portlist.Poller), portpoll: new(portlist.Poller),
em: newExpiryManager(logf), em: newExpiryManager(logf),
gotPortPollRes: make(chan struct{}),
loginFlags: loginFlags, loginFlags: loginFlags,
clock: clock, clock: clock,
selfUpdateProgress: make([]ipnstate.UpdateProgress, 0), selfUpdateProgress: make([]ipnstate.UpdateProgress, 0),
@ -2058,20 +2056,6 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
if b.portpoll != nil { if b.portpoll != nil {
b.portpollOnce.Do(func() { b.portpollOnce.Do(func() {
go b.readPoller() go b.readPoller()
// Give the poller a second to get results to
// prevent it from restarting our map poll
// HTTP request (via doSetHostinfoFilterServices >
// cli.SetHostinfo). In practice this is very quick.
t0 := b.clock.Now()
timer, timerChannel := b.clock.NewTimer(time.Second)
select {
case <-b.gotPortPollRes:
b.logf("[v1] got initial portlist info in %v", b.clock.Since(t0).Round(time.Millisecond))
timer.Stop()
case <-timerChannel:
b.logf("timeout waiting for initial portlist")
}
}) })
} }
@ -2582,21 +2566,21 @@ func shrinkDefaultRoute(route netip.Prefix, localInterfaceRoutes *netipx.IPSet,
// readPoller is a goroutine that receives service lists from // readPoller is a goroutine that receives service lists from
// b.portpoll and propagates them into the controlclient's HostInfo. // b.portpoll and propagates them into the controlclient's HostInfo.
func (b *LocalBackend) readPoller() { func (b *LocalBackend) readPoller() {
isFirst := true if !envknob.BoolDefaultTrue("TS_PORTLIST") {
return
}
ticker, tickerChannel := b.clock.NewTicker(portlist.PollInterval()) ticker, tickerChannel := b.clock.NewTicker(portlist.PollInterval())
defer ticker.Stop() defer ticker.Stop()
initChan := make(chan struct{})
close(initChan)
for { for {
select { select {
case <-tickerChannel: case <-tickerChannel:
case <-b.ctx.Done(): case <-b.ctx.Done():
return return
case <-initChan: }
// Preserving old behavior: readPoller should
// immediately poll the first time, then wait if !b.shouldUploadServices() {
// for a tick after. continue
initChan = nil
} }
ports, changed, err := b.portpoll.Poll() ports, changed, err := b.portpoll.Poll()
@ -2627,11 +2611,6 @@ func (b *LocalBackend) readPoller() {
b.mu.Unlock() b.mu.Unlock()
b.doSetHostinfoFilterServices() b.doSetHostinfoFilterServices()
if isFirst {
isFirst = false
close(b.gotPortPollRes)
}
} }
} }