Skip to content

Commit 50c9bf5

Browse files
authored
Merge pull request #244 from Notifiarr/dn2_faster
Fewer hot path allocations
2 parents 6a2fb2d + 2f9b0b2 commit 50c9bf5

3 files changed

Lines changed: 46 additions & 8 deletions

File tree

pkg/webserver/functions.go

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,27 @@ const (
1818
keyPosition = 5 // example: /api/v1/route/method/apikey
1919
)
2020

21+
// pathSegment returns the idx-th element of strings.Split(pathStr, "/") using strings.SplitSeq
22+
// so the full split slice is not allocated.
23+
func pathSegment(pathStr string, idx int) string {
24+
segIdx := 0
25+
26+
for seg := range strings.SplitSeq(pathStr, "/") {
27+
if segIdx == idx {
28+
before, _, found := strings.Cut(seg, "?")
29+
if found {
30+
return before
31+
}
32+
33+
return seg
34+
}
35+
36+
segIdx++
37+
}
38+
39+
return ""
40+
}
41+
2142
type responseWrapper struct {
2243
http.ResponseWriter
2344

@@ -68,12 +89,23 @@ func (s *server) parseAPIKey(next http.Handler) http.Handler {
6889
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
6990
key := req.Header.Get("X-Api-Key")
7091
if len(key) != keyLength {
71-
if uri := strings.Split(req.Header.Get("X-Original-Uri"), "/"); len(uri) > keyPosition {
72-
key = strings.Split(uri[keyPosition], "?")[0]
73-
}
92+
key = pathSegment(req.Header.Get("X-Original-Uri"), keyPosition)
93+
}
94+
95+
pooled := s.apiKeyVarsPool.Get()
96+
97+
urlVars, ok := pooled.(map[string]string)
98+
if !ok {
99+
urlVars = make(map[string]string, 1)
74100
}
75101

76-
req = mux.SetURLVars(req, map[string]string{apiKey: key})
102+
urlVars[apiKey] = key
103+
req = mux.SetURLVars(req, urlVars)
104+
105+
defer func() {
106+
delete(urlVars, apiKey)
107+
s.apiKeyVarsPool.Put(urlVars)
108+
}()
77109

78110
if len(key) != keyLength {
79111
s.metrics.HTTPRequests.WithLabelValues(exp.HTTPEventInvalidKey).Inc()

pkg/webserver/handlers_nginx.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,22 @@ type keyReq struct {
2323
}
2424

2525
func (s *server) handleServer(resp http.ResponseWriter, req *http.Request) {
26+
key := req.Header.Get("X-Server")
2627
s.handleGetAny(resp, req, &keyReq{
2728
label: "servers",
28-
key: req.Header.Get("X-Server"),
29-
cache: s.servers.Get(req.Header.Get("X-Server")),
29+
key: key,
30+
cache: s.servers.Get(key),
3031
get: s.ui.GetServer,
3132
save: s.servers.Save,
3233
})
3334
}
3435

3536
func (s *server) handleGetKey(resp http.ResponseWriter, req *http.Request) {
37+
key := mux.Vars(req)[apiKey]
3638
s.handleGetAny(resp, req, &keyReq{
3739
label: "users",
38-
key: mux.Vars(req)[apiKey],
39-
cache: s.users.Get(mux.Vars(req)[apiKey]),
40+
key: key,
41+
cache: s.users.Get(key),
4042
get: s.ui.GetInfo,
4143
save: s.users.Save,
4244
})

pkg/webserver/start.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ type server struct {
5959
// noAuthMu protects NoAuthPaths on the embedded Config (RequiresAPIKey, reload, showConfig).
6060
noAuthMu sync.RWMutex
6161
metrics *exp.Metrics
62+
// apiKeyVarsPool backs mux.SetURLVars in parseAPIKey (avoids a map alloc per request).
63+
apiKeyVarsPool sync.Pool
6264
}
6365

6466
// ErrNoSQLConfig is returned if no mysql config is present.
@@ -123,6 +125,8 @@ func Start(config *Config) error {
123125
}
124126

125127
func (s *server) start() error {
128+
s.apiKeyVarsPool = sync.Pool{New: func() any { return make(map[string]string, 1) }}
129+
126130
s.users = cache.New(cache.Config{PruneInterval: pruneInterval, RequestAccuracy: time.Second})
127131
defer s.users.Stop(false)
128132

0 commit comments

Comments
 (0)