ulid: simplify, add function without a timestamp

This commit is contained in:
2025-06-06 20:02:23 -07:00
parent ce203a4618
commit 785abdec8d
3 changed files with 148 additions and 79 deletions

View File

@@ -1,64 +1,44 @@
// Package ulid provides thread-safe ULID (Universally Unique Lexicographically Sortable Identifier) generation.
//
// ULIDs are 128-bit identifiers that are lexicographically sortable and contain
// a timestamp component. This package uses a pool-based approach with
// cryptographically secure random seeding and monotonic ordering for optimal
// performance in concurrent environments.
// a timestamp component. This package uses cryptographically secure random
// generation optimized for simplicity and performance in concurrent environments.
package ulid
import (
cryptorand "crypto/rand"
"encoding/binary"
"io"
mathrand "math/rand"
"os"
"sync"
"time"
oklid "github.com/oklog/ulid/v2"
"go.ntppool.org/common/logger"
)
// monotonicPool is a pool of monotonic ULID readers for performance optimization.
// Each reader is initialized with a cryptographically secure random seed
// and random increment value to ensure uniqueness across concurrent usage.
var monotonicPool = sync.Pool{
New: func() any {
log := logger.Setup()
var seed int64
err := binary.Read(cryptorand.Reader, binary.BigEndian, &seed)
if err != nil {
log.Error("crypto/rand error", "err", err)
os.Exit(10)
}
rand := mathrand.New(mathrand.NewSource(seed))
inc := uint64(mathrand.Int63())
// log.Printf("seed: %d", seed)
// log.Printf("inc: %d", inc)
// inc = inc & ^uint64(1<<63) // only want 63 bits
mono := oklid.Monotonic(rand, inc)
return mono
},
}
// MakeULID generates a new ULID with the specified timestamp using a pooled monotonic reader.
// MakeULID generates a new ULID with the specified timestamp using cryptographically secure randomness.
// The function is thread-safe and optimized for high-concurrency environments.
//
// Each call retrieves a monotonic reader from the pool, generates a ULID with the
// given timestamp, and returns it. The reader is not returned to the pool as it
// maintains internal state for monotonic ordering.
// This implementation prioritizes simplicity and performance over strict monotonicity within
// the same millisecond. Each ULID is guaranteed to be unique and lexicographically sortable
// across different timestamps.
//
// Returns a pointer to the generated ULID or an error if generation fails.
// Generation should only fail under extreme circumstances (entropy exhaustion).
func MakeULID(t time.Time) (*oklid.ULID, error) {
mono := monotonicPool.Get().(io.Reader)
id, err := oklid.New(oklid.Timestamp(t), mono)
id, err := oklid.New(oklid.Timestamp(t), cryptorand.Reader)
if err != nil {
return nil, err
}
return &id, nil
}
// Make generates a new ULID with the current timestamp using cryptographically secure randomness.
// This is a convenience function equivalent to MakeULID(time.Now()).
//
// The function is thread-safe and optimized for high-concurrency environments.
//
// Returns a pointer to the generated ULID or an error if generation fails.
// Generation should only fail under extreme circumstances (entropy exhaustion).
func Make() (*oklid.ULID, error) {
id, err := oklid.New(oklid.Now(), cryptorand.Reader)
if err != nil {
return nil, err
}