ipn/ipnlocal/netmapcache: add UpdateSelfOnly method (#19818)

Some netmap updates are guaranteed to affect only the "static" parts of the
netmap, and so should not require us to walk through all the peers and user
profiles when updating the cache. To support this, the new UpdateSelfOnly
method updates only the Self node and other tailnet settings that are not
dependent on the peers and profiles.

Use this when updating the cache on DERP home changes.

Updates #12542

Change-Id: Ifed522b29d579fb76e010b4ff738cc4e0a72d27f
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
This commit is contained in:
M. J. Fromberger
2026-05-20 16:29:04 -07:00
committed by GitHub
parent 93dbd33ef7
commit c09407002f
5 changed files with 133 additions and 37 deletions
@@ -258,6 +258,55 @@ func TestInvalidCache(t *testing.T) {
})
}
func TestUpdateSelfOnly(t *testing.T) {
s := make(testStore)
c := netmapcache.NewCache(s)
// Initialize the cache with the test map so we get a baseline.
if err := c.Store(t.Context(), testMap); err != nil {
t.Fatalf("Store initial netmap: %v", err)
}
// Modify a shallow copy of the map so we can perform an update and verify
// that it round-trips through a Load after calling UpdateSelfOnly.
newSelf := &tailcfg.Node{
ID: 23456,
StableID: "n23456FAKE",
User: 8675309,
Name: "alt.example.com.",
Key: testNodeKey,
HomeDERP: 6174,
Capabilities: []tailcfg.NodeCapability{"cap1", "cap3"},
}
updated := *testMap // shallow copy
updated.SelfNode = newSelf.View()
updated.AllCaps = set.Of[tailcfg.NodeCapability]("cap1", "cap3")
updated.DNS = tailcfg.DNSConfig{Domains: []string{"example3.org", "example4.horse"}}
// Empty the peers and profiles so that we can verify the update does not
// attempt to use them or GC based on their absence.
updated.Peers = nil
updated.UserProfiles = nil
if err := c.UpdateSelfOnly(t.Context(), &updated); err != nil {
t.Fatalf("UpdateSelfOnly failed: %v", err)
}
// Verify we got the same results back. Importantly, we expect the same
// peers and profiles as before, to enforce that the self-only update did
// not prune
updated.Peers = testMap.Peers
updated.UserProfiles = testMap.UserProfiles
got, err := c.Load(t.Context())
if err != nil {
t.Fatalf("Load netmap failed: %v", err)
}
if diff := diffNetMaps(got, &updated); diff != "" {
t.Fatalf("Updated map differs (-got, +want):\n%s", diff)
}
}
// skippedMapFields are the names of fields that should not be considered by
// network map caching, and thus skipped when comparing test results.
var skippedMapFields = []string{