From a08b0f4262eacba4212296d9497c8baf52629144 Mon Sep 17 00:00:00 2001 From: Julien Voisin Date: Sat, 30 May 2026 07:05:01 +0200 Subject: [PATCH] perf: enable uuid randomness pool and minor cleanups (#1652) cmd/anubis: call uuid.EnableRandPool() at the top of main. The pool batches crypto/rand reads internally, dramatically reducing per-call syscall overhead for UUID generation. UUIDs are produced on every issued challenge (NewV7, 3.7 times faster, down to zero allocation) and on every challenge page render (NewString, 1.6 times faster, 1 fewer allocation). The pool is non-cryptographic-key material, PoW challenge bytes and signing keys still go directly to crypto/rand. lib/anubis.go: three trivial optimizations in issueChallenge and maybeReverseProxy, reducing the amount of allocations by 2%, which isn't much but since the changes are trivial: - fmt.Sprintf("%x", randomData) -> hex.EncodeToString(randomData) - cache uuid.UUID.String() once instead of calling it three times - fmt.Sprintf("ogtags:allow:%s%s", ...) -> string concat Signed-off-by: jvoisin Signed-off-by: Xe Iaso Co-authored-by: Xe Iaso --- cmd/anubis/main.go | 4 ++++ docs/docs/CHANGELOG.md | 1 + lib/anubis.go | 12 +++++++----- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/cmd/anubis/main.go b/cmd/anubis/main.go index b1a906ab..eeb7a626 100644 --- a/cmd/anubis/main.go +++ b/cmd/anubis/main.go @@ -36,6 +36,7 @@ import ( "github.com/TecharoHQ/anubis/lib/thoth" "github.com/TecharoHQ/anubis/web" "github.com/facebookgo/flagenv" + "github.com/google/uuid" _ "github.com/joho/godotenv/autoload" healthv1 "google.golang.org/grpc/health/grpc_health_v1" ) @@ -193,6 +194,9 @@ func main() { flagenv.Parse() flag.Parse() + // Must be set before any concurrent UUID call. + uuid.EnableRandPool() + if *versionFlag { fmt.Println("Anubis", anubis.Version) return diff --git a/docs/docs/CHANGELOG.md b/docs/docs/CHANGELOG.md index 645369fd..fa7912b5 100644 --- a/docs/docs/CHANGELOG.md +++ b/docs/docs/CHANGELOG.md @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix a race in the bbolt store where the asynchronous cleanup scheduled by an expired read could delete a value that had just been refreshed; the delete now only fires when the key still carries the same expired generation it observed. - Marginally increase the performances of requests processing - Marginally improve the performances of PoW validation +- Marginally improve the performances of challenges generation/display - Significantly improve the performances of the gzip middleware ## v1.25.0: Necron diff --git a/lib/anubis.go b/lib/anubis.go index ac3481ab..369cd25b 100644 --- a/lib/anubis.go +++ b/lib/anubis.go @@ -4,6 +4,7 @@ import ( "context" "crypto/ed25519" "crypto/rand" + "encoding/hex" "encoding/json" "errors" "fmt" @@ -162,6 +163,7 @@ func (s *Server) issueChallenge(ctx context.Context, r *http.Request, lg *slog.L if err != nil { return nil, err } + idStr := id.String() var randomData = make([]byte, 64) if _, err := rand.Read(randomData); err != nil { @@ -169,9 +171,9 @@ func (s *Server) issueChallenge(ctx context.Context, r *http.Request, lg *slog.L } chall := challenge.Challenge{ - ID: id.String(), + ID: idStr, Method: rule.Challenge.Algorithm, - RandomData: fmt.Sprintf("%x", randomData), + RandomData: hex.EncodeToString(randomData), IssuedAt: time.Now(), Difficulty: rule.Challenge.Difficulty, PolicyRuleHash: rule.Hash(), @@ -182,11 +184,11 @@ func (s *Server) issueChallenge(ctx context.Context, r *http.Request, lg *slog.L } j := store.JSON[challenge.Challenge]{Underlying: s.store} - if err := j.Set(ctx, "challenge:"+id.String(), chall, 30*time.Minute); err != nil { + if err := j.Set(ctx, "challenge:"+idStr, chall, 30*time.Minute); err != nil { return nil, err } - lg.Info("new challenge issued", "challenge", id.String(), "weight", cr.Weight) + lg.Info("new challenge issued", "challenge", idStr, "weight", cr.Weight) return &chall, err } @@ -240,7 +242,7 @@ func (s *Server) maybeReverseProxyOrPage(w http.ResponseWriter, r *http.Request) func (s *Server) maybeReverseProxy(w http.ResponseWriter, r *http.Request, httpStatusOnly bool) { lg, r := s.getRequestLogger(r) - if val, _ := s.store.Get(r.Context(), fmt.Sprintf("ogtags:allow:%s%s", r.Host, r.URL.String())); val != nil { + if val, _ := s.store.Get(r.Context(), "ogtags:allow:"+r.Host+r.URL.String()); val != nil { lg.Debug("serving opengraph tag asset") s.ServeHTTPNext(w, r) return