[performance] faster request id generation (#4405)

This uses a much faster method of generating request IDs using an atomically updated counter instead of a mutex lock and read of /dev/random.

Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4405
Co-authored-by: kim <grufwub@gmail.com>
Co-committed-by: kim <grufwub@gmail.com>
This commit is contained in:
kim 2025-09-04 14:43:36 +02:00 committed by kim
commit 5a54e7156b
2 changed files with 27 additions and 30 deletions

View file

@ -53,7 +53,10 @@ func HeaderFilter(state *state.State) gin.HandlerFunc {
} }
func headerFilterAllowMode(state *state.State) func(c *gin.Context) { func headerFilterAllowMode(state *state.State) func(c *gin.Context) {
_ = *state //nolint if state == nil {
panic(gtserror.New("nil check elimination"))
}
// Allowlist mode: explicit block takes // Allowlist mode: explicit block takes
// precedence over explicit allow. // precedence over explicit allow.
// //
@ -93,7 +96,10 @@ func headerFilterAllowMode(state *state.State) func(c *gin.Context) {
} }
func headerFilterBlockMode(state *state.State) func(c *gin.Context) { func headerFilterBlockMode(state *state.State) func(c *gin.Context) {
_ = *state //nolint if state == nil {
panic(gtserror.New("nil check elimination"))
}
// Blocklist/default mode: explicit allow // Blocklist/default mode: explicit allow
// takes precedence over explicit block. // takes precedence over explicit block.
// //

View file

@ -18,12 +18,9 @@
package middleware package middleware
import ( import (
"bufio"
"crypto/rand"
"encoding/base32" "encoding/base32"
"encoding/binary" "encoding/binary"
"io" "sync/atomic"
"sync"
"time" "time"
"code.superseriousbusiness.org/gotosocial/internal/gtscontext" "code.superseriousbusiness.org/gotosocial/internal/gtscontext"
@ -31,43 +28,37 @@ import (
) )
var ( var (
// crand provides buffered reads of random input. // request counter.
crand = bufio.NewReader(rand.Reader) count atomic.Uint32
mrand sync.Mutex
// base32enc is a base 32 encoding based on a human-readable character set (no padding). // server start time in milliseconds.
base32enc = base32.NewEncoding("0123456789abcdefghjkmnpqrstvwxyz").WithPadding(-1) start = uint64(time.Now().UnixMilli())
// shorthand to binary.
be = binary.BigEndian
// b32 is a base 32 encoding based on a human-readable character set (no padding).
b32 = base32.NewEncoding("0123456789abcdefghjkmnpqrstvwxyz").WithPadding(-1)
) )
// NewRequestID generates a new request ID string. // NewRequestID generates a new request ID string.
func NewRequestID() string { func NewRequestID() string {
// 0:8 = timestamp var buf [12]byte
// 8:12 = entropy
//
// inspired by ULID.
b := make([]byte, 12)
// Get current time in milliseconds. // Generate unique request
ms := uint64(time.Now().UnixMilli()) // #nosec G115 -- Pre-1970 clock? // ID from request count and
// time of server initialization.
// Store binary time data in byte buffer. be.PutUint64(buf[0:], start)
binary.LittleEndian.PutUint64(b[0:8], ms) be.PutUint32(buf[8:], count.Add(1))
return b32.EncodeToString(buf[:])
mrand.Lock()
// Read random bits into buffer end.
_, _ = io.ReadFull(crand, b[8:12])
mrand.Unlock()
// Encode the binary time+entropy ID.
return base32enc.EncodeToString(b)
} }
// AddRequestID returns a gin middleware which adds a unique ID to each request (both response header and context). // AddRequestID returns a gin middleware which adds a unique ID to each request (both response header and context).
func AddRequestID(header string) gin.HandlerFunc { func AddRequestID(header string) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
id := c.GetHeader(header) id := c.GetHeader(header)
// Have we found anything?
if id == "" { if id == "" {
// Generate new ID. // Generate new ID.
id = NewRequestID() id = NewRequestID()