Enhancement: deduplicate prefetch goroutines in cache plugin
Hello coredns team,
What would you like to be added:
Add deduplication to the doPrefetch goroutine path in the cache plugin. Currently, when serve_stale is enabled and a cache entry goes stale, every concurrent request for the same domain spawns a separate go c.doPrefetch() goroutine (handler.go:58-59), even though they all do the same upstream query.
Why is this needed:
When the upstream is slow or temporarily unreachable, these duplicate prefetch goroutines pile up since each one blocks until the forward timeout (~2s). In a quick test, 1,000 concurrent queries for a single stale domain created 1,001 goroutines all doing the same work. With deduplication, only one goroutine would be spawned per unique query.
The same applies to the shouldPrefetch path (handler.go:63-64).
Example using a sync.Map to skip spawning a goroutine when one is already in-flight for the same query:
// In Cache struct, add:
prefetching sync.Map
// In the serve_stale path:
if !c.verifyStale {
k := hash(state.Name(), state.QType(), state.Do(), state.Req.CheckingDisabled)
if _, loaded := c.prefetching.LoadOrStore(k, true); !loaded {
go func() {
defer c.prefetching.Delete(k)
cw := newPrefetchResponseWriter(server, state, c)
c.doPrefetch(ctx, state, cw, i, now)
}()
}
}Happy to submit a PR if this sounds reasonable.
Thanks!
Source: coredns/coredns