From a57c6457c9326bc7fcbc2df74d8fb94a4b4465e6 Mon Sep 17 00:00:00 2001 From: Michael Ben-Ami Date: Tue, 24 Mar 2026 15:11:11 -0400 Subject: [PATCH] ipn/ipnlocal: debounce extra enqueues in ExtensionHost.AuthReconfigAsync Fixes tailscale/corp#39065 Signed-off-by: Michael Ben-Ami --- ipn/ipnlocal/extension_host.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ipn/ipnlocal/extension_host.go b/ipn/ipnlocal/extension_host.go index 1cacb5353..0c4b1d933 100644 --- a/ipn/ipnlocal/extension_host.go +++ b/ipn/ipnlocal/extension_host.go @@ -90,6 +90,11 @@ type ExtensionHost struct { extByType sync.Map // reflect.Type -> ipnext.Extension + // hasPendingAuthReconfig tracks whether an AuthReconfig call + // has been enqueued but not yet executed. It avoids redundant + // reconfigs and prevents them from piling up in the workQueue. + hasPendingAuthReconfig atomic.Bool + // mu protects the following fields. // It must not be held when calling [LocalBackend] methods // or when invoking callbacks registered by extensions. @@ -544,11 +549,17 @@ func (h *ExtensionHost) Shutdown() { } // AuthReconfigAsync implements [ipnext.Host.AuthReconfigAsync]. +// Since execution uses the most recent state at execution time, +// multiple enqueued calls are redundant and are not enqueued. func (h *ExtensionHost) AuthReconfigAsync() { if h == nil { return } + if h.hasPendingAuthReconfig.Swap(true) { + return + } h.enqueueBackendOperation(func(b Backend) { + h.hasPendingAuthReconfig.Store(false) b.authReconfig() }) }