From 14322713a5847176dcc29b8970d71a9ef4361713 Mon Sep 17 00:00:00 2001 From: "M. J. Fromberger" Date: Tue, 3 Feb 2026 07:55:41 -0800 Subject: [PATCH] ipn/ipnlocal/netmapcache: ensure cache updates preserve unchanged data (#18590) Found by @cmol. When rewriting the same value into the cache, we were dropping the unchanged keys, resulting in the cache being pruned incorrectly. Also update the tests to catch this. Updates #12639 Change-Id: Iab67e444eb7ddc22ccc680baa2f6a741a00eb325 Signed-off-by: M. J. Fromberger --- ipn/ipnlocal/netmapcache/netmapcache.go | 1 + ipn/ipnlocal/netmapcache/netmapcache_test.go | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/ipn/ipnlocal/netmapcache/netmapcache.go b/ipn/ipnlocal/netmapcache/netmapcache.go index d5706f9b7..b12443b99 100644 --- a/ipn/ipnlocal/netmapcache/netmapcache.go +++ b/ipn/ipnlocal/netmapcache/netmapcache.go @@ -86,6 +86,7 @@ func (c *Cache) writeJSON(ctx context.Context, key string, v any) error { // this at all? last, ok := c.lastWrote[key] if ok && cacheDigest(j) == last.digest { + c.wantKeys.Add(key) return nil } diff --git a/ipn/ipnlocal/netmapcache/netmapcache_test.go b/ipn/ipnlocal/netmapcache/netmapcache_test.go index 437015ccc..b31db2d5e 100644 --- a/ipn/ipnlocal/netmapcache/netmapcache_test.go +++ b/ipn/ipnlocal/netmapcache/netmapcache_test.go @@ -10,6 +10,7 @@ import ( "flag" "fmt" "iter" + "maps" "os" "reflect" "slices" @@ -181,6 +182,24 @@ func TestRoundTrip(t *testing.T) { }) } + + t.Run("Twice", func(t *testing.T) { + // Verify that storing the same network map twice results in no change. + + s := make(testStore) + c := netmapcache.NewCache(s) + if err := c.Store(t.Context(), testMap); err != nil { + t.Fatalf("Store 1 netmap failed: %v", err) + } + scp := maps.Clone(s) // for comparison, see below + + if err := c.Store(t.Context(), testMap); err != nil { + t.Fatalf("Store 2 netmap failed; %v", err) + } + if diff := cmp.Diff(s, scp); diff != "" { + t.Errorf("Updated store (-got, +want):\n%s", diff) + } + }) } func TestInvalidCache(t *testing.T) {