mirror of
https://github.com/tailscale/tailscale.git
synced 2025-12-15 12:02:31 +00:00
control/controlclient: improve handling of concurrent lite map requests
This reverts commit6eca47b16cand fixes forward. Previously the first ever streaming MapRequest that a client sent would also set ReadOnly to true as it didn't have any endpoints and expected/relied on the map poll to restart as soon as it got endpoints. However with48f6c1eba4, we would no longer restart MapRequests as frequently as we used to, so control would only ever get the first streaming MapRequest which had ReadOnly=true. Control would treat this as an uninteresting request and would not send it any further netmaps, while the client would happily stay in the map poll forever while litemap updates happened in parallel. This makes it so that we never set `ReadOnly=true` when we are doing a streaming MapRequest. This is no longer necessary either as most endpoint discovery happens over disco anyway. Co-authored-by: Andrew Dunham <andrew@du.nham.ca> Signed-off-by: Maisem Ali <maisem@tailscale.com>
This commit is contained in:
@@ -12,7 +12,6 @@ import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
@@ -89,16 +88,15 @@ type Direct struct {
|
||||
sfGroup singleflight.Group[struct{}, *NoiseClient] // protects noiseClient creation.
|
||||
noiseClient *NoiseClient
|
||||
|
||||
persist persist.PersistView
|
||||
authKey string
|
||||
tryingNewKey key.NodePrivate
|
||||
expiry *time.Time
|
||||
hostinfo *tailcfg.Hostinfo // always non-nil
|
||||
netinfo *tailcfg.NetInfo
|
||||
endpoints []tailcfg.Endpoint
|
||||
tkaHead string
|
||||
everEndpoints bool // whether we've ever had non-empty endpoints
|
||||
lastPingURL string // last PingRequest.URL received, for dup suppression
|
||||
persist persist.PersistView
|
||||
authKey string
|
||||
tryingNewKey key.NodePrivate
|
||||
expiry *time.Time
|
||||
hostinfo *tailcfg.Hostinfo // always non-nil
|
||||
netinfo *tailcfg.NetInfo
|
||||
endpoints []tailcfg.Endpoint
|
||||
tkaHead string
|
||||
lastPingURL string // last PingRequest.URL received, for dup suppression
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
@@ -753,9 +751,6 @@ func (c *Direct) newEndpoints(endpoints []tailcfg.Endpoint) (changed bool) {
|
||||
}
|
||||
c.logf("[v2] client.newEndpoints(%v)", epStrs)
|
||||
c.endpoints = append(c.endpoints[:0], endpoints...)
|
||||
if len(endpoints) > 0 {
|
||||
c.everEndpoints = true
|
||||
}
|
||||
return true // changed
|
||||
}
|
||||
|
||||
@@ -768,8 +763,6 @@ func (c *Direct) SetEndpoints(endpoints []tailcfg.Endpoint) (changed bool) {
|
||||
return c.newEndpoints(endpoints)
|
||||
}
|
||||
|
||||
func inTest() bool { return flag.Lookup("test.v") != nil }
|
||||
|
||||
// PollNetMap makes a /map request to download the network map, calling cb with
|
||||
// each new netmap.
|
||||
func (c *Direct) PollNetMap(ctx context.Context, cb func(*netmap.NetworkMap)) error {
|
||||
@@ -824,7 +817,6 @@ func (c *Direct) sendMapRequest(ctx context.Context, maxPolls int, readOnly bool
|
||||
epStrs = append(epStrs, ep.Addr.String())
|
||||
epTypes = append(epTypes, ep.Type)
|
||||
}
|
||||
everEndpoints := c.everEndpoints
|
||||
c.mu.Unlock()
|
||||
|
||||
machinePrivKey, err := c.getMachinePrivKey()
|
||||
@@ -865,15 +857,17 @@ func (c *Direct) sendMapRequest(ctx context.Context, maxPolls int, readOnly bool
|
||||
OmitPeers: cb == nil,
|
||||
TKAHead: c.tkaHead,
|
||||
|
||||
// On initial startup before we know our endpoints, set the ReadOnly flag
|
||||
// to tell the control server not to distribute out our (empty) endpoints to peers.
|
||||
// Presumably we'll learn our endpoints in a half second and do another post
|
||||
// with useful results. The first POST just gets us the DERP map which we
|
||||
// need to do the STUN queries to discover our endpoints.
|
||||
// TODO(bradfitz): we skip this optimization in tests, though,
|
||||
// because the e2e tests are currently hyper-specific about the
|
||||
// ordering of things. The e2e tests need love.
|
||||
ReadOnly: readOnly || (len(epStrs) == 0 && !everEndpoints && !inTest()),
|
||||
// Previously we'd set ReadOnly to true if we didn't have any endpoints
|
||||
// yet as we expected to learn them in a half second and restart the full
|
||||
// streaming map poll, however as we are trying to reduce the number of
|
||||
// times we restart the full streaming map poll we now just set ReadOnly
|
||||
// false when we're doing a full streaming map poll.
|
||||
//
|
||||
// TODO(maisem/bradfitz): really ReadOnly should be set to true if for
|
||||
// all streams and we should only do writes via lite map updates.
|
||||
// However that requires an audit and a bunch of testing to make sure we
|
||||
// don't break anything.
|
||||
ReadOnly: readOnly && !allowStream,
|
||||
}
|
||||
var extraDebugFlags []string
|
||||
if hi != nil && c.linkMon != nil && !c.skipIPForwardingCheck &&
|
||||
|
||||
Reference in New Issue
Block a user