tailscale/util/fastuuid/fastuuid.go
James Tucker d0f3fa7d7e util/fastuuid: add a more efficient uuid generator
This still generates github.com/google/uuid UUID objects, but does so
using a ChaCha8 CSPRNG from the stdlib rand/v2 package. The public API
is backed by a sync.Pool to provide good performance in highly
concurrent operation.

Under high load the read API produces a lot of extra garbage and
overhead by way of temporaries and syscalls. This implementation reduces
both to minimal levels, and avoids any long held global lock by
utilizing sync.Pool.

Updates tailscale/corp#18266
Updates tailscale/corp#19054

Signed-off-by: James Tucker <james@tailscale.com>
2024-04-09 14:05:20 -07:00

57 lines
1.1 KiB
Go

// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
// Package fastuuid implements a UUID construction using an in process CSPRNG.
package fastuuid
import (
crand "crypto/rand"
"encoding/binary"
"io"
"math/rand/v2"
"sync"
"github.com/google/uuid"
)
// NewUUID returns a new UUID using a pool of generators, good for highly
// concurrent use.
func NewUUID() uuid.UUID {
g := pool.Get().(*generator)
defer pool.Put(g)
return g.newUUID()
}
var pool = sync.Pool{
New: func() any {
return newGenerator()
},
}
type generator struct {
rng rand.ChaCha8
}
func seed() [32]byte {
var r [32]byte
if _, err := io.ReadFull(crand.Reader, r[:]); err != nil {
panic(err)
}
return r
}
func newGenerator() *generator {
return &generator{
rng: *rand.NewChaCha8(seed()),
}
}
func (g *generator) newUUID() uuid.UUID {
var u uuid.UUID
binary.NativeEndian.PutUint64(u[:8], g.rng.Uint64())
binary.NativeEndian.PutUint64(u[8:], g.rng.Uint64())
u[6] = (u[6] & 0x0f) | 0x40 // Version 4
u[8] = (u[8] & 0x3f) | 0x80 // Variant 10
return u
}