router: add singleflight deduplication for concurrent narinfo races

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: Ib682889f34ad4ad4fb331ee2924dc9916a6a6964
This commit is contained in:
raf 2026-03-06 19:44:24 +03:00
commit 41b18dd1f8
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF
8 changed files with 90 additions and 18 deletions

View file

@ -11,6 +11,7 @@ import (
"sync"
"time"
"golang.org/x/sync/singleflight"
"notashelf.dev/ncro/internal/cache"
"notashelf.dev/ncro/internal/metrics"
"notashelf.dev/ncro/internal/narinfo"
@ -37,18 +38,21 @@ type Router struct {
prober *prober.Prober
routeTTL time.Duration
raceTimeout time.Duration
negativeTTL time.Duration
client *http.Client
mu sync.RWMutex
upstreamKeys map[string]string // upstream URL → Nix public key string
group singleflight.Group
}
// Creates a Router.
func New(db *cache.DB, p *prober.Prober, routeTTL, raceTimeout time.Duration) *Router {
func New(db *cache.DB, p *prober.Prober, routeTTL, raceTimeout, negativeTTL time.Duration) *Router {
return &Router{
db: db,
prober: p,
routeTTL: routeTTL,
raceTimeout: raceTimeout,
negativeTTL: negativeTTL,
client: &http.Client{Timeout: raceTimeout},
upstreamKeys: make(map[string]string),
}
@ -82,7 +86,14 @@ func (r *Router) Resolve(storeHash string, candidates []string) (*Result, error)
}
}
metrics.NarinfoCacheMisses.Inc()
return r.race(storeHash, candidates)
v, raceErr, _ := r.group.Do(storeHash, func() (interface{}, error) {
return r.race(storeHash, candidates)
})
if raceErr != nil {
return nil, raceErr
}
return v.(*Result), nil
}
type raceResult struct {