Documentation
¶
Overview ¶
interfaces.go
Package ratelimit provides production-ready rate limiting for Go applications.
Gorly is designed for simplicity and performance, with a clean API that scales from prototype to production without changes. It supports multiple storage backends (in-memory for development, Redis for production), flexible identity extraction (IP-based, API key-based, user-based), and comprehensive HTTP middleware.
Quick Start ¶
Get started with a one-liner:
import "github.com/itsatony/gorly/middleware"
http.Handle("/api/", middleware.QuickLimit(100, time.Minute, yourHandler))
Or build a custom limiter in 3 lines:
store, _ := stores.NewMemoryStore(nil)
limiter, _ := ratelimit.NewSimple(store, 100, time.Hour)
result, _ := limiter.Allow(ctx, ratelimit.NewIPContext("192.168.1.1"))
Core Concepts ¶
Identity: Who/what is being rate limited (IP address, user ID, API key, etc.)
Scope: What operation is being rate limited (global, search, upload, etc.)
Tier: The user's subscription level (free, premium, enterprise)
Store: Where rate limit state is persisted (memory or Redis)
Production Features ¶
- Thread-safe with race detector testing - Proper error handling (distinguish errors from rate limits) - Redis-backed for distributed systems - Configurable algorithms (token bucket, sliding window) - Multi-tier support (different limits per subscription level) - Scope-based limits (different limits per operation type)
Architecture ¶
Gorly follows a clean architecture with clear separation:
RateLimiter (interface) -> Algorithm -> Store
↓
Identity (who is making the request)
↓
Result (allowed/denied + metadata)
See the README for detailed examples and recipes.
Package ratelimit version information using go-version
Index ¶
- Constants
- Variables
- func CheckMultiple(ctx context.Context, limiter RateLimiter, identities []string, ...) map[string]*Result
- func ContextsEqual(a, b Identity) bool
- func ConvertLogFields(fields []LogField) []interface{}
- func FormatRateString(limit int64, window time.Duration) string
- func FormatVersionString() string
- func GetAPIVersion(apiName string) (string, error)
- func GetComponentVersion(componentName string) (string, error)
- func GetGitCommit() string
- func GetProjectName() string
- func GetRetryAfter(result *Result) time.Duration
- func GetSchemaVersion(schemaName string) (string, error)
- func GetUsagePercent(result *Result) float64
- func GetVersion() string
- func GetVersionInfo() *version.Info
- func HealthHandler() http.Handler
- func InitializeVersion(opts ...version.Option) error
- func IsClosed(err error) bool
- func IsConfigError(err error) bool
- func IsConnectionError(err error) bool
- func IsContextError(err error) bool
- func IsKeyTooLong(err error) bool
- func IsNearLimit(result *Result, thresholdPercent float64) bool
- func IsRateLimitExceeded(err error) bool
- func IsRateLimited(result *Result) bool
- func IsScriptNotSupported(err error) bool
- func IsStorageError(err error) bool
- func IsStorageFailure(err error) bool
- func IsTimeoutError(err error) bool
- func MustGetVersionInfo() *version.Info
- func NewConnectionError(storeType, address string, err error, keyValues ...interface{}) error
- func NewLimitExceededError(identity, scope string, limit, used int64, keyValues ...interface{}) error
- func NewTimeoutError(operation string, duration interface{}, keyValues ...interface{}) error
- func ParseKey(key string) (tier, scope, identity string, err error)
- func ParseRateString(rateStr string) (int64, time.Duration, error)
- func QuickCheck(ctx context.Context, limiter RateLimiter, identity, scope, tier string) (bool, error)
- func QuickCheckN(ctx context.Context, limiter RateLimiter, identity, scope, tier string, ...) (bool, error)
- func QuickStats(ctx context.Context, limiter RateLimiter, identity, scope, tier string) (limit, used, remaining int64, err error)
- func ResetMultiple(ctx context.Context, limiter RateLimiter, identities []string, ...) error
- func ResetVersion()
- func ResultsEqual(a, b *Result) bool
- func ValidateBurstValue(burst int64) error
- func ValidateContext(ctx Identity) error
- func ValidateKeyLength(key string) error
- func ValidateLimitValue(limit int64) error
- func ValidateVersions(ctx context.Context, validators ...version.Validator) error
- func ValidateWindowSeconds(windowSec int64) error
- func VersionHandler() http.Handler
- func VersionHandlerFunc() http.HandlerFunc
- func VersionMiddleware(next http.Handler) http.Handler
- func WrapConfigError(err error, message string, keyValues ...interface{}) error
- func WrapContextError(err error, message string, keyValues ...interface{}) error
- func WrapResolverError(err error, message string, keyValues ...interface{}) error
- func WrapStorageError(err error, operation string, keyValues ...interface{}) error
- func WrapStrategyError(err error, strategyName string, keyValues ...interface{}) error
- type AggregatedResult
- type Algorithm
- type Builder
- func (b *Builder) Build() (RateLimiter, error)
- func (b *Builder) MustBuild() RateLimiter
- func (b *Builder) WithAlgorithm(algorithm Algorithm) *Builder
- func (b *Builder) WithBurst(burst int64) *Builder
- func (b *Builder) WithDefaultTiers() *Builder
- func (b *Builder) WithGenerousTiers() *Builder
- func (b *Builder) WithLimit(limit int64, window time.Duration) *Builder
- func (b *Builder) WithLimitString(rateStr string) *Builder
- func (b *Builder) WithLogger(logger Logger) *Builder
- func (b *Builder) WithMetrics(enable bool) *Builder
- func (b *Builder) WithResolver(resolver LimitResolver) *Builder
- func (b *Builder) WithSlidingWindow() *Builder
- func (b *Builder) WithStore(store Store) *Builder
- func (b *Builder) WithStrictTiers() *Builder
- func (b *Builder) WithTiers(resolverConfig *ResolverConfig) *Builder
- func (b *Builder) WithTokenBucket() *Builder
- type Config
- type ContextBuilder
- func (cb *ContextBuilder) AddMetadata(key string, value interface{}) *ContextBuilder
- func (cb *ContextBuilder) Build() (Identity, error)
- func (cb *ContextBuilder) MustBuild() Identity
- func (cb *ContextBuilder) WithIP(ip string) *ContextBuilder
- func (cb *ContextBuilder) WithIdentity(identity string) *ContextBuilder
- func (cb *ContextBuilder) WithMetadata(metadata map[string]interface{}) *ContextBuilder
- func (cb *ContextBuilder) WithMethod(method string) *ContextBuilder
- func (cb *ContextBuilder) WithPath(path string) *ContextBuilder
- func (cb *ContextBuilder) WithScope(scope string) *ContextBuilder
- func (cb *ContextBuilder) WithTier(tier string) *ContextBuilder
- func (cb *ContextBuilder) WithUserAgent(ua string) *ContextBuilder
- type HealthStatus
- type IdentifiedContext
- type Identity
- func CloneContext(ctx Identity) Identity
- func ContextFromKey(key string) (Identity, error)
- func NewAPIKeyContext(apiKey, tier string) Identity
- func NewIPContext(ip string) Identity
- func NewScopedContext(identity, scope, tier string, metadata map[string]interface{}) Identity
- func NewTenantContext(tenantID, tier string) Identity
- func NewUserContext(userID, tier string) Identity
- type LimitConfig
- type LimitResolver
- type LogField
- type Logger
- type RateLimiter
- func NewForAPI(store Store) (RateLimiter, error)
- func NewForMicroservice(store Store) (RateLimiter, error)
- func NewForPublicAPI(store Store) (RateLimiter, error)
- func NewForSaaS(store Store) (RateLimiter, error)
- func NewForWebApp(store Store) (RateLimiter, error)
- func NewRateLimiter(config *Config) (RateLimiter, error)
- func NewSimple(store Store, limit int64, window time.Duration) (RateLimiter, error)
- func NewWithConfig(config *Config) (RateLimiter, error)
- func NewWithTiers(store Store, resolverConfig *ResolverConfig) (RateLimiter, error)
- type ResolverConfig
- type Result
- func (r *Result) Clone() *Result
- func (r *Result) GetAllMetadata() map[string]interface{}
- func (r *Result) GetMetadata(key string) (interface{}, bool)
- func (r *Result) HasMetadata(key string) bool
- func (r *Result) IsNearLimit(threshold float64) bool
- func (r *Result) ResetAtUnix() int64
- func (r *Result) RetryAfterSeconds() int64
- func (r *Result) SetMetadata(key string, value interface{})
- func (r *Result) SetMetadataMap(metadata map[string]interface{})
- func (r *Result) String() string
- func (r *Result) TimeUntilReset() time.Duration
- func (r *Result) UsagePercentage() float64
- func (r *Result) WithContext(scope, entity, tier string) *Result
- func (r *Result) WithStrategy(strategy string) *Result
- type ResultBuilder
- func (rb *ResultBuilder) Allowed(allowed bool) *ResultBuilder
- func (rb *ResultBuilder) Build() *Result
- func (rb *ResultBuilder) Entity(entity string) *ResultBuilder
- func (rb *ResultBuilder) Limit(limit int64) *ResultBuilder
- func (rb *ResultBuilder) Metadata(key string, value interface{}) *ResultBuilder
- func (rb *ResultBuilder) Remaining(remaining int64) *ResultBuilder
- func (rb *ResultBuilder) ResetAt(resetAt time.Time) *ResultBuilder
- func (rb *ResultBuilder) RetryAfter(retryAfter time.Duration) *ResultBuilder
- func (rb *ResultBuilder) Scope(scope string) *ResultBuilder
- func (rb *ResultBuilder) Strategy(strategy string) *ResultBuilder
- func (rb *ResultBuilder) Tier(tier string) *ResultBuilder
- func (rb *ResultBuilder) Used(used int64) *ResultBuilder
- func (rb *ResultBuilder) Window(window time.Duration) *ResultBuilder
- type ScriptSupporter
- type SimpleContext
- type Store
- type TierConfig
Constants ¶
const ( // IDPrefixRateLimiter is the prefix for rate limiter instance IDs IDPrefixRateLimiter = "rl" // IDPrefixContext is the prefix for rate limit context IDs IDPrefixContext = "rlc" // IDPrefixResult is the prefix for result IDs (if needed) IDPrefixResult = "rlr" )
const ( // StrategyTokenBucket implements token bucket algorithm // Allows burst traffic, tokens regenerate over time StrategyTokenBucket = "token_bucket" // StrategySlidingWindow implements sliding window algorithm // More precise than token bucket, no burst allowance StrategySlidingWindow = "sliding_window" // StrategyFixedWindow implements fixed window algorithm // Simple window-based counting, fast performance StrategyFixedWindow = "fixed_window" // StrategyLeakyBucket implements leaky bucket algorithm // Smooth rate limiting, queue-based processing StrategyLeakyBucket = "leaky_bucket" )
const ( // StoreTypeMemory uses in-memory storage (default) // Good for: single instance, development, testing StoreTypeMemory = "memory" // StoreTypeRedis uses Redis for distributed storage // Good for: multi-instance, high availability, shared limits StoreTypeRedis = "redis" // StoreTypeMock uses mock store for testing StoreTypeMock = "mock" )
const ( // TierFree is the free service tier TierFree = "free" // TierPremium is the premium/paid service tier TierPremium = "premium" // TierEnterprise is the enterprise service tier TierEnterprise = "enterprise" // TierInternal is for internal/system requests TierInternal = "internal" // TierDefault is used when no tier is specified TierDefault = "default" )
const ( // ScopeGlobal applies to all operations ScopeGlobal = "global" // ScopeAPI applies to API requests ScopeAPI = "api" // ScopeSearch applies to search operations ScopeSearch = "search" // ScopeUpload applies to file upload operations ScopeUpload = "upload" // ScopeDownload applies to file download operations ScopeDownload = "download" // ScopeDatabase applies to database query operations ScopeDatabase = "database" // ScopeEvents applies to event processing ScopeEvents = "events" // ScopeAdmin applies to admin operations ScopeAdmin = "admin" // ScopeMetadata applies to metadata operations ScopeMetadata = "metadata" // ScopeAnalytics applies to analytics operations ScopeAnalytics = "analytics" )
const ( // MetadataKeyIP stores the client IP address MetadataKeyIP = "ip" // MetadataKeyUserAgent stores the client user agent MetadataKeyUserAgent = "user_agent" // MetadataKeyResource stores the resource being accessed MetadataKeyResource = "resource" // MetadataKeyMethod stores the HTTP method MetadataKeyMethod = "method" // MetadataKeyPath stores the URL path MetadataKeyPath = "path" // MetadataKeyTimestamp stores the request timestamp MetadataKeyTimestamp = "timestamp" // MetadataKeyRequestID stores the request ID MetadataKeyRequestID = "request_id" // MetadataKeyUserID stores the user ID MetadataKeyUserID = "user_id" // MetadataKeyTenant stores the tenant ID MetadataKeyTenant = "tenant" // MetadataKeyRegion stores the region MetadataKeyRegion = "region" )
const ( // ErrCodeInvalidConfig indicates invalid configuration ErrCodeInvalidConfig = "GORLY_INVALID_CONFIG" // ErrCodeInvalidContext indicates invalid rate limit context ErrCodeInvalidContext = "GORLY_INVALID_CONTEXT" // ErrCodeStorageFailure indicates storage backend failure ErrCodeStorageFailure = "GORLY_STORAGE_FAILURE" // ErrCodeStrategyFailure indicates strategy execution failure ErrCodeStrategyFailure = "GORLY_STRATEGY_FAILURE" // ErrCodeResolverFailure indicates config resolver failure ErrCodeResolverFailure = "GORLY_RESOLVER_FAILURE" // ErrCodeLimitExceeded indicates rate limit exceeded ErrCodeLimitExceeded = "GORLY_LIMIT_EXCEEDED" // ErrCodeInvalidLimit indicates invalid limit value ErrCodeInvalidLimit = "GORLY_INVALID_LIMIT" // ErrCodeInvalidWindow indicates invalid time window ErrCodeInvalidWindow = "GORLY_INVALID_WINDOW" // ErrCodeInvalidBurst indicates invalid burst value ErrCodeInvalidBurst = "GORLY_INVALID_BURST" // ErrCodeClosed indicates limiter is closed ErrCodeClosed = "GORLY_CLOSED" // ErrCodeKeyNotFound indicates key not found in store ErrCodeKeyNotFound = "GORLY_KEY_NOT_FOUND" // ErrCodeConnectionFailed indicates connection failure ErrCodeConnectionFailed = "GORLY_CONNECTION_FAILED" // ErrCodeTimeout indicates operation timeout ErrCodeTimeout = "GORLY_TIMEOUT" // ErrCodeScriptNotSupported indicates script execution is not supported ErrCodeScriptNotSupported = "GORLY_SCRIPT_NOT_SUPPORTED" // ErrCodeKeyTooLong indicates the key exceeds maximum allowed length ErrCodeKeyTooLong = "GORLY_KEY_TOO_LONG" )
const ( // ErrMsgInvalidConfig is returned when configuration is invalid ErrMsgInvalidConfig = "invalid rate limiter configuration" // ErrMsgInvalidContext is returned when context is invalid ErrMsgInvalidContext = "invalid rate limit context" // ErrMsgStorageFailure is returned when storage operation fails ErrMsgStorageFailure = "storage backend failure" // ErrMsgStrategyFailure is returned when strategy execution fails ErrMsgStrategyFailure = "rate limiting strategy failure" // ErrMsgResolverFailure is returned when config resolver fails ErrMsgResolverFailure = "rate limit resolver failure" // ErrMsgLimitExceeded is returned when rate limit is exceeded ErrMsgLimitExceeded = "rate limit exceeded" // ErrMsgInvalidLimit is returned when limit value is invalid ErrMsgInvalidLimit = "invalid rate limit value" // ErrMsgInvalidWindow is returned when window value is invalid ErrMsgInvalidWindow = "invalid time window" // ErrMsgInvalidBurst is returned when burst value is invalid ErrMsgInvalidBurst = "invalid burst value" // ErrMsgClosed is returned when limiter is closed ErrMsgClosed = "rate limiter is closed" // ErrMsgKeyNotFound is returned when key not found ErrMsgKeyNotFound = "key not found" // ErrMsgConnectionFailed is returned when connection fails ErrMsgConnectionFailed = "connection failed" // ErrMsgTimeout is returned when operation times out ErrMsgTimeout = "operation timeout" // ErrMsgScriptNotSupported is returned when store doesn't support script execution ErrMsgScriptNotSupported = "script execution not supported by this store" // ErrMsgKeyTooLong is returned when key exceeds maximum length ErrMsgKeyTooLong = "key length exceeds maximum allowed" )
const ( // DefaultLimit is the default rate limit (requests per window) DefaultLimit = int64(1000) // DefaultWindowSeconds is the default time window in seconds DefaultWindowSeconds = int64(3600) // 1 hour // DefaultWindow is the default time window duration DefaultWindow = time.Duration(DefaultWindowSeconds) * time.Second // DefaultBurst is the default burst size DefaultBurst = int64(100) // DefaultCleanupIntervalSeconds is the default cleanup interval for memory store DefaultCleanupIntervalSeconds = int64(300) // 5 minutes // DefaultCleanupInterval is the default cleanup interval duration DefaultCleanupInterval = time.Duration(DefaultCleanupIntervalSeconds) * time.Second // DefaultMaxKeys is the default maximum keys in memory store DefaultMaxKeys = int64(100000) // DefaultShardCount is the default number of shards for memory store DefaultShardCount = int(32) // DefaultRedisPoolSize is the default Redis connection pool size DefaultRedisPoolSize = int(10) // DefaultRedisTimeout is the default Redis operation timeout DefaultRedisTimeout = 5 * time.Second // DefaultRedisMaxRetries is the default number of Redis retries DefaultRedisMaxRetries = int(3) )
const ( // MinLimit is the minimum allowed rate limit MinLimit = int64(1) // MaxLimit is the maximum allowed rate limit MaxLimit = int64(1000000) // MinWindowSeconds is the minimum window duration in seconds MinWindowSeconds = int64(1) // MaxWindowSeconds is the maximum window duration in seconds (24 hours) MaxWindowSeconds = int64(86400) // MinBurst is the minimum burst size MinBurst = int64(0) // MaxBurst is the maximum burst size MaxBurst = int64(100000) // MinCleanupIntervalSeconds is the minimum cleanup interval MinCleanupIntervalSeconds = int64(10) // MaxCleanupIntervalSeconds is the maximum cleanup interval MaxCleanupIntervalSeconds = int64(3600) // MaxKeyLength is the maximum allowed length for store keys (in bytes) // This prevents DOS attacks via oversized keys and ensures compatibility // with Redis (512 MB max key size) while being reasonable for rate limiting MaxKeyLength = 256 )
const ( // HeaderRateLimitLimit is the header for rate limit total HeaderRateLimitLimit = "X-RateLimit-Limit" // HeaderRateLimitRemaining is the header for remaining requests HeaderRateLimitRemaining = "X-RateLimit-Remaining" // HeaderRateLimitReset is the header for reset timestamp HeaderRateLimitReset = "X-RateLimit-Reset" // HeaderRateLimitRetryAfter is the header for retry delay HeaderRateLimitRetryAfter = "Retry-After" // HeaderRateLimitUsed is the header for used requests HeaderRateLimitUsed = "X-RateLimit-Used" )
const ( // StorageKeyPrefixDefault is the default prefix for storage keys StorageKeyPrefixDefault = "gorly" // StorageKeySeparator is the separator for key components StorageKeySeparator = ":" )
const ( // LogMsgLimiterCreated is logged when limiter is created LogMsgLimiterCreated = "rate limiter created" // LogMsgLimiterClosed is logged when limiter is closed LogMsgLimiterClosed = "rate limiter closed" // LogMsgCheckAllowed is logged when request is allowed LogMsgCheckAllowed = "request allowed" // LogMsgCheckDenied is logged when request is denied LogMsgCheckDenied = "request denied" // LogMsgReset is logged when rate limit is reset LogMsgReset = "rate limit reset" // LogMsgStoreConnected is logged when store connects LogMsgStoreConnected = "store connected" // LogMsgStoreClosed is logged when store closes LogMsgStoreClosed = "store closed" // LogMsgCleanupStarted is logged when cleanup starts LogMsgCleanupStarted = "cleanup started" // LogMsgCleanupCompleted is logged when cleanup completes LogMsgCleanupCompleted = "cleanup completed" )
const ( // RateUnitSecond represents per-second rate RateUnitSecond = "second" // RateUnitMinute represents per-minute rate RateUnitMinute = "minute" // RateUnitHour represents per-hour rate RateUnitHour = "hour" // RateUnitDay represents per-day rate RateUnitDay = "day" // RateUnitShortSecond is the short form for second RateUnitShortSecond = "s" // RateUnitShortMinute is the short form for minute RateUnitShortMinute = "m" // RateUnitShortHour is the short form for hour RateUnitShortHour = "h" // RateUnitShortDay is the short form for day RateUnitShortDay = "d" )
Variables ¶
var ( // ErrInvalidConfig indicates the rate limiter configuration is invalid ErrInvalidConfig = errors.New(ErrMsgInvalidConfig) // ErrInvalidContext indicates the rate limit context is invalid ErrInvalidContext = errors.New(ErrMsgInvalidContext) // ErrStorageFailure indicates a storage backend failure ErrStorageFailure = errors.New(ErrMsgStorageFailure) // ErrStrategyFailure indicates a rate limiting strategy failure ErrStrategyFailure = errors.New(ErrMsgStrategyFailure) // ErrResolverFailure indicates a config resolver failure ErrResolverFailure = errors.New(ErrMsgResolverFailure) // ErrLimitExceeded indicates the rate limit has been exceeded ErrLimitExceeded = errors.New(ErrMsgLimitExceeded) // ErrInvalidLimit indicates an invalid rate limit value ErrInvalidLimit = errors.New(ErrMsgInvalidLimit) // ErrInvalidWindow indicates an invalid time window value ErrInvalidWindow = errors.New(ErrMsgInvalidWindow) // ErrInvalidBurst indicates an invalid burst value ErrInvalidBurst = errors.New(ErrMsgInvalidBurst) // ErrClosed indicates the rate limiter has been closed ErrClosed = errors.New(ErrMsgClosed) // ErrKeyNotFound indicates a key was not found in the store ErrKeyNotFound = errors.New(ErrMsgKeyNotFound) // ErrConnectionFailed indicates a connection failure ErrConnectionFailed = errors.New(ErrMsgConnectionFailed) // ErrTimeout indicates an operation timeout ErrTimeout = errors.New(ErrMsgTimeout) // ErrScriptNotSupported indicates the store doesn't support script execution ErrScriptNotSupported = errors.New(ErrMsgScriptNotSupported) // ErrKeyTooLong indicates the key exceeds maximum allowed length ErrKeyTooLong = errors.New(ErrMsgKeyTooLong) )
Core sentinel errors using standard errors package These are wrapped with cuserr when additional context is needed
Functions ¶
func CheckMultiple ¶ added in v1.1.0
func CheckMultiple(ctx context.Context, limiter RateLimiter, identities []string, scope, tier string) map[string]*Result
CheckMultiple checks rate limits for multiple identities at once Returns a map of identity -> result
func ContextsEqual ¶ added in v1.1.0
ContextsEqual checks if two contexts are equivalent for rate limiting
func ConvertLogFields ¶ added in v1.1.0
func ConvertLogFields(fields []LogField) []interface{}
ConvertLogFields converts LogField slice to interface{} slice for logging
func FormatRateString ¶ added in v1.1.0
FormatRateString formats limit and window into a rate string
func FormatVersionString ¶ added in v1.1.0
func FormatVersionString() string
FormatVersionString returns a formatted version string with additional info
func GetAPIVersion ¶ added in v1.1.0
GetAPIVersion returns the version of a specific API
func GetComponentVersion ¶ added in v1.1.0
GetComponentVersion returns the version of a specific component
func GetProjectName ¶ added in v1.1.0
func GetProjectName() string
GetProjectName returns the project name
func GetRetryAfter ¶
GetRetryAfter returns the retry-after duration from a result
func GetSchemaVersion ¶ added in v1.1.0
GetSchemaVersion returns the version of a specific schema
func GetUsagePercent ¶ added in v1.1.0
GetUsagePercent returns the usage as a percentage (0-100)
func GetVersionInfo ¶
GetVersionInfo returns comprehensive version information Initializes version info if not already done
func HealthHandler ¶ added in v1.1.0
HealthHandler returns an HTTP handler for health checks with version info
func InitializeVersion ¶ added in v1.1.0
InitializeVersion initializes the version information from the manifest This should be called once during application startup
func IsConfigError ¶
IsConfigError checks if error is a configuration error
func IsConnectionError ¶
IsConnectionError checks if error is a connection error
func IsContextError ¶ added in v1.1.0
IsContextError checks if error is a context error
func IsKeyTooLong ¶ added in v1.1.0
IsKeyTooLong checks if error indicates key length exceeded maximum
func IsNearLimit ¶ added in v1.1.0
IsNearLimit checks if usage is near the limit (>= threshold %)
func IsRateLimitExceeded ¶
IsRateLimitExceeded checks if error is a rate limit exceeded error
func IsRateLimited ¶ added in v1.1.0
IsRateLimited checks if a result indicates rate limiting
func IsScriptNotSupported ¶ added in v1.1.0
IsScriptNotSupported checks if error indicates scripts are not supported
func IsStorageError ¶ added in v1.1.0
IsStorageError checks if an error is a storage-related error
func IsStorageFailure ¶ added in v1.1.0
IsStorageFailure checks if error is a storage failure
func IsTimeoutError ¶ added in v1.1.0
IsTimeoutError checks if error is a timeout error
func MustGetVersionInfo ¶ added in v1.1.0
MustGetVersionInfo returns version information or panics if unavailable
func NewConnectionError ¶ added in v1.1.0
NewConnectionError creates a connection error with context
func NewLimitExceededError ¶ added in v1.1.0
func NewLimitExceededError(identity, scope string, limit, used int64, keyValues ...interface{}) error
NewLimitExceededError creates a rate limit exceeded error with context
func NewTimeoutError ¶ added in v1.1.0
NewTimeoutError creates a timeout error with context
func ParseKey ¶ added in v1.1.0
ParseKey parses a storage key back into components Expected format: "gorly:tier:scope:identity"
func ParseRateString ¶
ParseRateString parses a rate string like "1000/1h" into limit and window Supported formats:
- "1000/1h" - 1000 requests per hour
- "100/1m" - 100 requests per minute
- "10/1s" - 10 requests per second
- "5000/1d" - 5000 requests per day
SECURITY: This function includes comprehensive validation to prevent DOS attacks:
- Input length limited to 32 characters
- Only accepts positive non-zero values
- Overflow protection for all calculations
- Strict bounds checking on all parameters
func QuickCheck ¶ added in v1.1.0
func QuickCheck(ctx context.Context, limiter RateLimiter, identity, scope, tier string) (bool, error)
QuickCheck is a convenience function for simple rate limit checks Returns true if the request is allowed, false if rate limited, error if operation failed
BREAKING CHANGE: Now returns (bool, error) instead of just bool This allows callers to distinguish between rate limiting and errors
Example:
allowed, err := ratelimit.QuickCheck(ctx, limiter, "user123", "global", "free")
if err != nil {
// Handle error (e.g., store unavailable)
return err
}
if !allowed {
// Rate limit exceeded
return http.StatusTooManyRequests
}
func QuickCheckN ¶ added in v1.1.0
func QuickCheckN(ctx context.Context, limiter RateLimiter, identity, scope, tier string, n int64) (bool, error)
QuickCheckN is a convenience function for checking N tokens Returns true if the request is allowed, false if rate limited, error if operation failed
BREAKING CHANGE: Now returns (bool, error) instead of just bool
func QuickStats ¶ added in v1.1.0
func QuickStats(ctx context.Context, limiter RateLimiter, identity, scope, tier string) (limit, used, remaining int64, err error)
QuickStats returns basic usage statistics for an identity Returns limit, used, remaining, and error
BREAKING CHANGE: Now returns (int64, int64, int64, error) instead of just (int64, int64, int64) Zero values no longer indicate errors - check the error return instead
func ResetMultiple ¶ added in v1.1.0
func ResetMultiple(ctx context.Context, limiter RateLimiter, identities []string, scope, tier string) error
ResetMultiple resets rate limits for multiple identities
func ResetVersion ¶ added in v1.1.0
func ResetVersion()
ResetVersion resets the version information (useful for testing)
func ResultsEqual ¶ added in v1.1.0
ResultsEqual checks if two results are equivalent
func ValidateBurstValue ¶ added in v1.1.0
ValidateBurstValue validates a burst value
func ValidateContext ¶ added in v1.1.0
ValidateContext validates a rate limit context
func ValidateKeyLength ¶ added in v1.1.0
ValidateKeyLength validates a storage key length Returns ErrKeyTooLong if the key exceeds MaxKeyLength bytes
func ValidateLimitValue ¶ added in v1.1.0
ValidateLimitValue validates a rate limit value
func ValidateVersions ¶ added in v1.1.0
ValidateVersions validates that all components meet minimum version requirements This is useful for ensuring compatibility during initialization
func ValidateWindowSeconds ¶ added in v1.1.0
ValidateWindowSeconds validates a window duration in seconds
func VersionHandler ¶ added in v1.1.0
VersionHandler returns an HTTP handler that exposes version information This uses the go-version package's built-in handler
func VersionHandlerFunc ¶ added in v1.1.0
func VersionHandlerFunc() http.HandlerFunc
VersionHandlerFunc returns an HTTP handler function that exposes version information
func VersionMiddleware ¶ added in v1.1.0
VersionMiddleware returns middleware that adds version information to the context
func WrapConfigError ¶ added in v1.1.0
WrapConfigError wraps a configuration error with additional context
func WrapContextError ¶ added in v1.1.0
WrapContextError wraps a context error with additional context
func WrapResolverError ¶ added in v1.1.0
WrapResolverError wraps a resolver error with additional context
func WrapStorageError ¶ added in v1.1.0
WrapStorageError wraps a storage error with additional context
func WrapStrategyError ¶ added in v1.1.0
WrapStrategyError wraps a strategy error with additional context
Types ¶
type AggregatedResult ¶ added in v1.1.0
type AggregatedResult struct {
// Overall indicates if all checks passed
Overall bool
// Results contains individual results by scope
Results map[string]*Result
// LowestRemaining is the scope with the least remaining capacity
LowestRemaining string
// NextReset is the earliest reset time across all scopes
NextReset time.Time
}
AggregatedResult combines multiple results (e.g., from multi-scope checks)
func NewAggregatedResult ¶ added in v1.1.0
func NewAggregatedResult(results map[string]*Result) *AggregatedResult
NewAggregatedResult creates an aggregated result from multiple results
func (*AggregatedResult) String ¶ added in v1.1.0
func (ar *AggregatedResult) String() string
String returns a human-readable representation
type Algorithm ¶
type Algorithm interface {
// Name returns the algorithm name
Name() string
// Allow checks if a request is allowed and returns the result
Allow(ctx context.Context, store Store, key string, limit int64, window time.Duration, n int64) (*Result, error)
// Reset resets the rate limit for the given key
Reset(ctx context.Context, store Store, key string) error
}
Algorithm represents a rate limiting algorithm
func NewFixedWindowAlgorithm ¶ added in v1.1.0
func NewFixedWindowAlgorithm() Algorithm
NewFixedWindowAlgorithm creates a fixed window algorithm This is a placeholder - not yet implemented
func NewLeakyBucketAlgorithm ¶ added in v1.1.0
func NewLeakyBucketAlgorithm() Algorithm
NewLeakyBucketAlgorithm creates a leaky bucket algorithm This is a placeholder - not yet implemented
func NewSlidingWindowAlgorithm ¶ added in v1.1.0
func NewSlidingWindowAlgorithm() Algorithm
NewSlidingWindowAlgorithm creates a sliding window algorithm
func NewTokenBucketAlgorithm ¶ added in v1.1.0
func NewTokenBucketAlgorithm() Algorithm
NewTokenBucketAlgorithm creates a token bucket algorithm
type Builder ¶
type Builder struct {
// contains filtered or unexported fields
}
Builder provides a fluent API for constructing rate limiters
func NewBuilder ¶ added in v1.1.0
func NewBuilder() *Builder
NewBuilder creates a new rate limiter builder
func (*Builder) Build ¶
func (b *Builder) Build() (RateLimiter, error)
Build creates the rate limiter
func (*Builder) MustBuild ¶ added in v1.1.0
func (b *Builder) MustBuild() RateLimiter
MustBuild creates the rate limiter or panics on error Use only when you're certain the configuration is valid
func (*Builder) WithAlgorithm ¶ added in v1.1.0
WithAlgorithm sets a custom algorithm
func (*Builder) WithDefaultTiers ¶ added in v1.1.0
WithDefaultTiers sets up tier-based limiting with default configuration
func (*Builder) WithGenerousTiers ¶ added in v1.1.0
WithGenerousTiers sets up tier-based limiting with generous limits
func (*Builder) WithLimitString ¶ added in v1.1.0
WithLimitString sets the limit from a rate string like "1000/1h"
func (*Builder) WithLogger ¶ added in v1.1.0
WithLogger sets a custom logger
func (*Builder) WithMetrics ¶ added in v1.1.0
WithMetrics enables metrics collection
func (*Builder) WithResolver ¶ added in v1.1.0
func (b *Builder) WithResolver(resolver LimitResolver) *Builder
WithResolver sets a limit resolver for tier-based limits
func (*Builder) WithSlidingWindow ¶ added in v1.1.0
WithSlidingWindow sets the sliding window algorithm
func (*Builder) WithStrictTiers ¶ added in v1.1.0
WithStrictTiers sets up tier-based limiting with strict limits
func (*Builder) WithTiers ¶ added in v1.1.0
func (b *Builder) WithTiers(resolverConfig *ResolverConfig) *Builder
WithTiers sets up tier-based limiting with the given configuration
func (*Builder) WithTokenBucket ¶ added in v1.1.0
WithTokenBucket sets the token bucket algorithm
type Config ¶
type Config struct {
// Store is the backend storage for rate limit data
Store Store
// Algorithm is the rate limiting algorithm to use
Algorithm Algorithm
// DefaultLimit is the default rate limit (requests per window)
DefaultLimit int64
// DefaultWindow is the default time window
DefaultWindow time.Duration
// DefaultBurst is the default burst size for token bucket
DefaultBurst int64
// Resolver is the optional limit resolver for dynamic limits
// If set, it will override DefaultLimit/DefaultWindow/DefaultBurst
// based on tier and scope resolution
Resolver LimitResolver
// Logger for rate limiter operations
Logger Logger
// EnableMetrics enables metrics collection
EnableMetrics bool
}
Config holds rate limiter configuration
func DefaultConfig ¶
func DefaultConfig() *Config
DefaultConfig returns a configuration with sensible defaults
type ContextBuilder ¶ added in v1.1.0
type ContextBuilder struct {
// contains filtered or unexported fields
}
ContextBuilder provides a fluent API for building rate limit contexts
func NewContextBuilder ¶ added in v1.1.0
func NewContextBuilder() *ContextBuilder
NewContextBuilder creates a new context builder
func (*ContextBuilder) AddMetadata ¶ added in v1.1.0
func (cb *ContextBuilder) AddMetadata(key string, value interface{}) *ContextBuilder
AddMetadata adds a single metadata entry
func (*ContextBuilder) Build ¶ added in v1.1.0
func (cb *ContextBuilder) Build() (Identity, error)
Build creates the rate limit context
func (*ContextBuilder) MustBuild ¶ added in v1.1.0
func (cb *ContextBuilder) MustBuild() Identity
MustBuild creates the rate limit context or panics
func (*ContextBuilder) WithIP ¶ added in v1.1.0
func (cb *ContextBuilder) WithIP(ip string) *ContextBuilder
WithIP adds IP address to metadata
func (*ContextBuilder) WithIdentity ¶ added in v1.1.0
func (cb *ContextBuilder) WithIdentity(identity string) *ContextBuilder
WithIdentity sets the identity
func (*ContextBuilder) WithMetadata ¶ added in v1.1.0
func (cb *ContextBuilder) WithMetadata(metadata map[string]interface{}) *ContextBuilder
WithMetadata adds metadata (replaces existing)
func (*ContextBuilder) WithMethod ¶ added in v1.1.0
func (cb *ContextBuilder) WithMethod(method string) *ContextBuilder
WithMethod adds HTTP method to metadata
func (*ContextBuilder) WithPath ¶ added in v1.1.0
func (cb *ContextBuilder) WithPath(path string) *ContextBuilder
WithPath adds URL path to metadata
func (*ContextBuilder) WithScope ¶ added in v1.1.0
func (cb *ContextBuilder) WithScope(scope string) *ContextBuilder
WithScope sets the scope
func (*ContextBuilder) WithTier ¶ added in v1.1.0
func (cb *ContextBuilder) WithTier(tier string) *ContextBuilder
WithTier sets the tier
func (*ContextBuilder) WithUserAgent ¶ added in v1.1.0
func (cb *ContextBuilder) WithUserAgent(ua string) *ContextBuilder
WithUserAgent adds user agent to metadata
type HealthStatus ¶
type HealthStatus struct {
// Overall health
Healthy bool `json:"healthy"`
// Component health
StoreHealthy bool `json:"store_healthy"`
StrategyHealthy bool `json:"strategy_healthy"`
// Details
Message string `json:"message,omitempty"`
Timestamp time.Time `json:"timestamp"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
// Performance
ResponseTime time.Duration `json:"response_time,omitempty"`
}
HealthStatus represents the health status of the rate limiter
func NewHealthStatus ¶ added in v1.1.0
func NewHealthStatus(healthy bool, message string) *HealthStatus
NewHealthStatus creates a new health status
type IdentifiedContext ¶ added in v1.1.0
type IdentifiedContext struct {
// contains filtered or unexported fields
}
IdentifiedContext wraps a Identity with a unique ID
func NewIdentifiedContext ¶ added in v1.1.0
func NewIdentifiedContext(wrapped Identity) *IdentifiedContext
NewIdentifiedContext creates a context with a unique ID
func (*IdentifiedContext) ID ¶ added in v1.1.0
func (ic *IdentifiedContext) ID() string
ID returns the unique context ID
func (*IdentifiedContext) Identity ¶ added in v1.1.0
func (ic *IdentifiedContext) Identity() string
Identity returns the wrapped context's identity
func (*IdentifiedContext) Key ¶ added in v1.1.0
func (ic *IdentifiedContext) Key() string
Key returns the wrapped context's key
func (*IdentifiedContext) Metadata ¶ added in v1.1.0
func (ic *IdentifiedContext) Metadata() map[string]interface{}
Metadata returns the wrapped context's metadata
func (*IdentifiedContext) Scope ¶ added in v1.1.0
func (ic *IdentifiedContext) Scope() string
Scope returns the wrapped context's scope
func (*IdentifiedContext) Tier ¶ added in v1.1.0
func (ic *IdentifiedContext) Tier() string
Tier returns the wrapped context's tier
type Identity ¶ added in v1.1.0
type Identity interface {
// Identity returns the unique identifier for this rate limit subject
// Examples: user ID, API key, IP address, tenant ID, connection ID
Identity() string
// Scope returns the rate limit scope
// Examples: "api", "search", "upload", "db_query", "events"
Scope() string
// Tier returns the service tier
// Examples: "free", "premium", "enterprise", "internal"
Tier() string
// Metadata returns additional context for rate limiting decisions
// Can include: IP address, user agent, resource being accessed, etc.
Metadata() map[string]interface{}
// Key generates the storage key for this context
// Format: "gorly:tier:scope:identity"
Key() string
}
Identity represents the complete context for a rate limit decision It replaces the old AuthEntity concept with something more flexible and generic
func CloneContext ¶ added in v1.1.0
CloneContext creates a deep copy of a rate limit context
func ContextFromKey ¶ added in v1.1.0
ContextFromKey creates a simple context from a storage key
func NewAPIKeyContext ¶ added in v1.1.0
NewAPIKeyContext creates a rate limit context for API key-based limiting
func NewIPContext ¶ added in v1.1.0
NewIPContext creates a rate limit context for IP-based limiting
func NewScopedContext ¶ added in v1.1.0
NewScopedContext creates a context for a user with a specific scope This is a helper that wraps NewSimpleContext with clearer naming
func NewTenantContext ¶ added in v1.1.0
NewTenantContext creates a rate limit context for tenant-based limiting
func NewUserContext ¶ added in v1.1.0
NewUserContext creates a rate limit context for user-based limiting
type LimitConfig ¶ added in v1.1.0
type LimitConfig struct {
// Limit is the number of requests allowed
Limit int64
// Window is the time window for the limit
Window time.Duration
// Burst is the burst size (for token bucket)
Burst int64
// Strategy is the rate limiting strategy to use
Strategy string
}
LimitConfig represents a single rate limit configuration
func NewLimitConfig ¶ added in v1.1.0
func NewLimitConfig(limit int64, window time.Duration, burst int64) *LimitConfig
NewLimitConfig creates a new limit configuration
func NewLimitConfigFromRate ¶ added in v1.1.0
func NewLimitConfigFromRate(rateStr string, burst int64) (*LimitConfig, error)
NewLimitConfigFromRate creates a limit config from a rate string
func (*LimitConfig) Clone ¶ added in v1.1.0
func (lc *LimitConfig) Clone() *LimitConfig
Clone creates a copy of the limit configuration
func (*LimitConfig) Validate ¶ added in v1.1.0
func (lc *LimitConfig) Validate() error
Validate validates the limit configuration
type LimitResolver ¶ added in v1.1.0
type LimitResolver interface {
// ResolveLimit resolves the rate limit for the given context
// Returns the resolved limit configuration based on the hierarchy:
// 1. Entity-specific override (if enabled)
// 2. Tier-specific limit for scope (if enabled)
// 3. Tier default limit (if enabled)
// 4. Global default limit
ResolveLimit(rlCtx Identity) (*LimitConfig, error)
// SetEntityOverride sets a limit override for a specific entity
SetEntityOverride(entityID, scope string, limit *LimitConfig) error
// RemoveEntityOverride removes a limit override for an entity
RemoveEntityOverride(entityID, scope string) error
// GetTierConfig returns the configuration for a tier
GetTierConfig(tier string) (*TierConfig, error)
}
LimitResolver resolves rate limit configuration for a given context
func NewLimitResolver ¶ added in v1.1.0
func NewLimitResolver(config *ResolverConfig) (LimitResolver, error)
NewLimitResolver creates a new limit resolver with the given configuration
type LogField ¶ added in v1.1.0
type LogField struct {
Key string
Value interface{}
}
LogField is a helper type for structured logging fields
type Logger ¶
type Logger interface {
// Debug logs a debug message with optional fields
Debug(msg string, fields ...interface{})
// Info logs an info message with optional fields
Info(msg string, fields ...interface{})
// Warn logs a warning message with optional fields
Warn(msg string, fields ...interface{})
// Error logs an error message with optional fields
Error(msg string, fields ...interface{})
// With creates a child logger with the given fields
With(fields ...interface{}) Logger
// Named creates a named logger
Named(name string) Logger
}
Logger defines the logging interface for gorly This allows injection of any logging implementation
func NewCustomLogger ¶ added in v1.1.0
NewCustomLogger creates a logger with custom configuration
func NewDevelopmentLogger ¶ added in v1.1.0
func NewDevelopmentLogger() Logger
NewDevelopmentLogger creates a logger optimized for development
func NewLoggerFromZap ¶ added in v1.1.0
NewLoggerFromZap wraps an existing zap.Logger
func NewNopLogger ¶ added in v1.1.0
func NewNopLogger() Logger
NewNopLogger creates a logger that discards all logs Useful for testing or when logging is disabled
func NewProductionLogger ¶ added in v1.1.0
func NewProductionLogger() Logger
NewProductionLogger creates a logger optimized for production
type RateLimiter ¶
type RateLimiter interface {
// Check performs a rate limit check WITHOUT consuming tokens
// Useful for preflight checks or monitoring
Check(ctx context.Context, rlCtx Identity) (*Result, error)
// Allow performs a rate limit check and CONSUMES one token if allowed
// This is the main method for enforcing rate limits
Allow(ctx context.Context, rlCtx Identity) (*Result, error)
// AllowN performs a rate limit check and consumes N tokens if allowed
// Useful for batch operations or operations with different costs
AllowN(ctx context.Context, rlCtx Identity, n int64) (*Result, error)
// Reset clears the rate limit for the given context
// Useful for administrative overrides or testing
Reset(ctx context.Context, rlCtx Identity) error
// Stats returns usage statistics for the given context
// Provides visibility into current rate limit state
Stats(ctx context.Context, rlCtx Identity) (*Result, error)
// Health checks the health of the rate limiter
// Verifies store connectivity and system health
Health(ctx context.Context) error
// Close cleanly shuts down the rate limiter
// Releases resources, closes connections, stops goroutines
Close() error
}
RateLimiter is the core interface for rate limiting functionality This is the main entry point for all rate limiting operations
func NewForAPI ¶ added in v1.1.0
func NewForAPI(store Store) (RateLimiter, error)
NewForAPI creates a rate limiter optimized for API gateway use - Higher limits for throughput - Token bucket for burst handling Requires a store to be provided
func NewForMicroservice ¶ added in v1.1.0
func NewForMicroservice(store Store) (RateLimiter, error)
NewForMicroservice creates a rate limiter optimized for microservices - Lower limits to protect services - Token bucket for burst handling Requires a store to be provided
func NewForPublicAPI ¶ added in v1.1.0
func NewForPublicAPI(store Store) (RateLimiter, error)
NewForPublicAPI creates a rate limiter for public-facing APIs - Strict limits to prevent abuse - Sliding window for fairness - Multi-tier support Requires a store to be provided
func NewForSaaS ¶ added in v1.1.0
func NewForSaaS(store Store) (RateLimiter, error)
NewForSaaS creates a rate limiter for SaaS applications - Tier-based limits (free, premium, enterprise) - Token bucket for user experience Requires a store to be provided
func NewForWebApp ¶ added in v1.1.0
func NewForWebApp(store Store) (RateLimiter, error)
NewForWebApp creates a rate limiter optimized for web applications - Moderate limits - Sliding window for fairness Requires a store to be provided
func NewRateLimiter ¶
func NewRateLimiter(config *Config) (RateLimiter, error)
NewRateLimiter creates a new rate limiter with the given configuration
func NewSimple ¶ added in v1.1.0
NewSimple creates a rate limiter with the simplest possible configuration Requires a store to be provided (use stores.NewMemoryStore(nil) for in-memory storage)
Example:
store := stores.NewMemoryStore(nil)
limiter, err := ratelimit.NewSimple(store, 100, time.Hour)
if err != nil {
log.Fatal(err)
}
defer limiter.Close()
func NewWithConfig ¶ added in v1.1.0
func NewWithConfig(config *Config) (RateLimiter, error)
NewWithConfig creates a rate limiter with the given configuration Fills in sensible defaults for any missing values
Example:
store := stores.NewMemoryStore(nil)
limiter, err := ratelimit.NewWithConfig(&ratelimit.Config{
Store: store,
DefaultLimit: 1000,
DefaultWindow: time.Hour,
DefaultBurst: 100,
})
func NewWithTiers ¶ added in v1.1.0
func NewWithTiers(store Store, resolverConfig *ResolverConfig) (RateLimiter, error)
NewWithTiers creates a rate limiter with multi-tier support Uses the provided resolver configuration for tier-based limits Requires a store to be provided
Example:
store := stores.NewMemoryStore(nil) resolverConfig := ratelimit.NewDefaultResolverConfig() limiter, err := ratelimit.NewWithTiers(store, resolverConfig)
type ResolverConfig ¶ added in v1.1.0
type ResolverConfig struct {
// TierConfigs maps tier names to their configurations
TierConfigs map[string]*TierConfig
// EntityOverrides maps entity IDs to their specific limit configurations
// This takes highest precedence in resolution
EntityOverrides map[string]map[string]*LimitConfig // entity_id -> scope -> limit
// DefaultTierConfig is used when no tier-specific config is found
DefaultTierConfig *TierConfig
// EnableEntityOverrides enables entity-specific overrides
EnableEntityOverrides bool
// EnableTierLimits enables tier-based limits
EnableTierLimits bool
// EnableScopeLimits enables scope-based limits
EnableScopeLimits bool
}
ResolverConfig represents configuration for the limit resolver
func NewDefaultResolverConfig ¶ added in v1.1.0
func NewDefaultResolverConfig() *ResolverConfig
NewDefaultResolverConfig creates a resolver config with sensible defaults
func NewGenerousResolverConfig ¶ added in v1.1.0
func NewGenerousResolverConfig() *ResolverConfig
NewGenerousResolverConfig creates a resolver config with generous limits
func NewResolverConfig ¶ added in v1.1.0
func NewResolverConfig() *ResolverConfig
NewResolverConfig creates a new resolver configuration
func NewStrictResolverConfig ¶ added in v1.1.0
func NewStrictResolverConfig() *ResolverConfig
NewStrictResolverConfig creates a resolver config with strict limits
func (*ResolverConfig) SetEntityOverride ¶ added in v1.1.0
func (rc *ResolverConfig) SetEntityOverride(entityID, scope string, limit *LimitConfig)
SetEntityOverride sets a limit override for a specific entity and scope
func (*ResolverConfig) SetTierConfig ¶ added in v1.1.0
func (rc *ResolverConfig) SetTierConfig(tier string, config *TierConfig)
SetTierConfig sets the configuration for a tier
func (*ResolverConfig) Validate ¶ added in v1.1.0
func (rc *ResolverConfig) Validate() error
Validate validates the resolver configuration
type Result ¶
type Result struct {
// Allowed indicates if the request is permitted
Allowed bool
// Limit is the maximum requests allowed in the window
Limit int64
// Remaining is how many requests are left in current window
Remaining int64
// Used is how many requests have been consumed
Used int64
// RetryAfter indicates when the client can retry (if not allowed)
// This is the duration to wait before the next request
RetryAfter time.Duration
// ResetAt indicates when the rate limit window resets
ResetAt time.Time
// Window is the rate limit time window duration
Window time.Duration
// Scope is the scope that was evaluated
Scope string
// Entity is the entity that was evaluated
Entity string
// Tier is the tier that was evaluated
Tier string
// Strategy is the name of the strategy that was used
Strategy string
// contains filtered or unexported fields
}
Result represents the outcome of a rate limit check All fields are concrete types - no interface{}
============================================================================ P0-4 THREAD SAFETY DOCUMENTATION ============================================================================
SAFE OPERATIONS (can be called concurrently):
- GetMetadata(), SetMetadata(), GetAllMetadata(), HasMetadata() - all metadata operations
- Clone() - creates a thread-safe snapshot (FIXED: now properly synchronized)
- String(), UsagePercentage() - read-only helper methods
CONDITIONALLY SAFE (safe ONLY if WithContext/WithStrategy not called concurrently):
- Reading fields: Allowed, Limit, Remaining, Used, RetryAfter, ResetAt, Window
- Reading context fields: Scope, Entity, Tier, Strategy
UNSAFE OPERATIONS (NOT thread-safe, must NOT be called concurrently):
- WithContext() - modifies Scope, Entity, Tier without synchronization
- WithStrategy() - modifies Strategy without synchronization
- Direct field writes after construction
- Reading Scope/Entity/Tier/Strategy while WithContext/WithStrategy are running
RECOMMENDED USAGE PATTERN:
- Create Result with constructor (NewAllowedResult, NewDeniedResult)
- Call WithContext() and WithStrategy() to configure (BEFORE sharing)
- Return/share Result (now treat as read-only except for metadata)
- Use only GetMetadata/SetMetadata for concurrent metadata access
- If multiple goroutines need different fields, use Clone()
ANTI-PATTERNS (will cause race conditions):
❌ Sharing Result and calling WithContext/WithStrategy concurrently ❌ Modifying non-metadata fields after sharing across goroutines ❌ Direct writes to fields after construction: result.Scope = "new"
Example - SAFE:
result := NewAllowedResult(100, 90, 10, resetTime, window)
result.WithContext("api", "user123", "premium") // OK - before sharing
result.WithStrategy("token_bucket") // OK - before sharing
go processResult(result) // OK - Result now read-only
Example - UNSAFE (RACE CONDITION):
result := NewAllowedResult(...)
go func() { result.WithContext(...) }() // ❌ RACE
go func() { fmt.Println(result.Scope) }() // ❌ RACE
IMPORTANT: While individual Result instances are thread-safe for metadata operations, Result objects should generally not be shared across goroutines. Each rate limit check returns a new Result that should be consumed by a single goroutine or properly synchronized if sharing is necessary.
If you need to safely share a Result across goroutines:
- Option 1: Use Clone() to create independent copies
- Option 2: Only read immutable fields and use Get/SetMetadata()
- Option 3: Add external synchronization (sync.Mutex)
func NewAllowedResult ¶ added in v1.1.0
func NewAllowedResult(limit, remaining, used int64, resetAt time.Time, window time.Duration) *Result
NewAllowedResult creates a result indicating the request is allowed
func NewDeniedResult ¶ added in v1.1.0
NewDeniedResult creates a result indicating the request is denied
func NewEmptyResult ¶ added in v1.1.0
NewEmptyResult creates an empty result (for stats queries when no data exists)
func (*Result) Clone ¶ added in v1.1.0
Clone creates a deep copy of the result (thread-safe)
THREAD SAFETY: This method is fully thread-safe and can be called concurrently with all other methods, including WithContext() and WithStrategy(). It acquires a read lock for the entire duration of the clone operation to ensure a consistent snapshot of all fields.
func (*Result) GetAllMetadata ¶ added in v1.1.0
GetAllMetadata returns a copy of all metadata (thread-safe) Returns a new map so modifications won't affect the original
func (*Result) GetMetadata ¶ added in v1.1.0
GetMetadata retrieves a metadata value by key (thread-safe) Returns the value and a boolean indicating if the key exists
func (*Result) HasMetadata ¶ added in v1.1.0
HasMetadata checks if a metadata key exists (thread-safe)
func (*Result) IsNearLimit ¶ added in v1.1.0
IsNearLimit checks if usage is near the limit (>= threshold percentage) threshold should be between 0 and 100
func (*Result) ResetAtUnix ¶ added in v1.1.0
ResetAtUnix returns the ResetAt timestamp as Unix seconds Useful for HTTP headers
func (*Result) RetryAfterSeconds ¶ added in v1.1.0
RetryAfterSeconds returns the RetryAfter duration as seconds Useful for HTTP headers
func (*Result) SetMetadata ¶ added in v1.1.0
SetMetadata sets a metadata key-value pair (thread-safe)
func (*Result) SetMetadataMap ¶ added in v1.1.0
SetMetadataMap sets multiple metadata entries (thread-safe)
func (*Result) String ¶ added in v1.1.0
String returns a human-readable representation of the result
func (*Result) TimeUntilReset ¶ added in v1.1.0
TimeUntilReset returns the duration until the window resets
func (*Result) UsagePercentage ¶ added in v1.1.0
UsagePercentage returns the percentage of limit used (0-100)
func (*Result) WithContext ¶ added in v1.1.0
WithContext adds context information to the result
P0-4 THREAD SAFETY WARNING: This method modifies the Result and is NOT thread-safe. It should only be called during Result construction before the Result is shared across goroutines. Do NOT call this method on a Result that may be accessed concurrently by multiple goroutines.
Safe pattern:
result := NewAllowedResult(...) result.WithContext(scope, entity, tier) // OK - before sharing return result
Unsafe pattern:
go func() { result.WithContext(...) }() // RACE CONDITION
go func() { fmt.Println(result.Scope) }() // RACE CONDITION
func (*Result) WithStrategy ¶ added in v1.1.0
WithStrategy adds strategy information to the result
P0-4 THREAD SAFETY WARNING: This method modifies the Result and is NOT thread-safe. It should only be called during Result construction before the Result is shared across goroutines. Do NOT call this method on a Result that may be accessed concurrently by multiple goroutines.
Safe pattern:
result := NewAllowedResult(...)
result.WithStrategy("token_bucket") // OK - before sharing
return result
Unsafe pattern:
go func() { result.WithStrategy(...) }() // RACE CONDITION
go func() { fmt.Println(result.Strategy) }() // RACE CONDITION
type ResultBuilder ¶ added in v1.1.0
type ResultBuilder struct {
// contains filtered or unexported fields
}
ResultBuilder provides a fluent API for building results
func NewResultBuilder ¶ added in v1.1.0
func NewResultBuilder() *ResultBuilder
NewResultBuilder creates a new result builder
func (*ResultBuilder) Allowed ¶ added in v1.1.0
func (rb *ResultBuilder) Allowed(allowed bool) *ResultBuilder
Allowed sets whether the request is allowed
func (*ResultBuilder) Build ¶ added in v1.1.0
func (rb *ResultBuilder) Build() *Result
Build returns the constructed result
func (*ResultBuilder) Entity ¶ added in v1.1.0
func (rb *ResultBuilder) Entity(entity string) *ResultBuilder
Entity sets the entity
func (*ResultBuilder) Limit ¶ added in v1.1.0
func (rb *ResultBuilder) Limit(limit int64) *ResultBuilder
Limit sets the limit
func (*ResultBuilder) Metadata ¶ added in v1.1.0
func (rb *ResultBuilder) Metadata(key string, value interface{}) *ResultBuilder
Metadata adds metadata (thread-safe)
func (*ResultBuilder) Remaining ¶ added in v1.1.0
func (rb *ResultBuilder) Remaining(remaining int64) *ResultBuilder
Remaining sets the remaining count
func (*ResultBuilder) ResetAt ¶ added in v1.1.0
func (rb *ResultBuilder) ResetAt(resetAt time.Time) *ResultBuilder
ResetAt sets the reset time
func (*ResultBuilder) RetryAfter ¶ added in v1.1.0
func (rb *ResultBuilder) RetryAfter(retryAfter time.Duration) *ResultBuilder
RetryAfter sets the retry after duration
func (*ResultBuilder) Scope ¶ added in v1.1.0
func (rb *ResultBuilder) Scope(scope string) *ResultBuilder
Scope sets the scope
func (*ResultBuilder) Strategy ¶ added in v1.1.0
func (rb *ResultBuilder) Strategy(strategy string) *ResultBuilder
Strategy sets the strategy
func (*ResultBuilder) Tier ¶ added in v1.1.0
func (rb *ResultBuilder) Tier(tier string) *ResultBuilder
Tier sets the tier
func (*ResultBuilder) Used ¶ added in v1.1.0
func (rb *ResultBuilder) Used(used int64) *ResultBuilder
Used sets the used count
func (*ResultBuilder) Window ¶ added in v1.1.0
func (rb *ResultBuilder) Window(window time.Duration) *ResultBuilder
Window sets the window duration
type ScriptSupporter ¶ added in v1.1.0
type ScriptSupporter interface {
Store
// LoadScript pre-loads a script and returns its SHA1 hash for later execution
// This enables using EVALSHA instead of EVAL for better performance
LoadScript(ctx context.Context, script string) (string, error)
// ExecuteScriptSHA executes a pre-loaded script by its SHA1 hash
ExecuteScriptSHA(ctx context.Context, sha string, keys []string, args ...interface{}) (interface{}, error)
}
ScriptSupporter is an optional interface for stores that support Lua scripts Stores can implement this to provide script caching and optimization
type SimpleContext ¶ added in v1.1.0
type SimpleContext struct {
// contains filtered or unexported fields
}
SimpleContext is a basic implementation of Identity
func NewSimpleContext ¶ added in v1.1.0
func NewSimpleContext(identity, scope, tier string, metadata map[string]interface{}) *SimpleContext
NewSimpleContext creates a new simple rate limit context
func (*SimpleContext) Identity ¶ added in v1.1.0
func (sc *SimpleContext) Identity() string
Identity returns the identity
func (*SimpleContext) Key ¶ added in v1.1.0
func (sc *SimpleContext) Key() string
Key generates the storage key
func (*SimpleContext) Metadata ¶ added in v1.1.0
func (sc *SimpleContext) Metadata() map[string]interface{}
Metadata returns the metadata
func (*SimpleContext) Scope ¶ added in v1.1.0
func (sc *SimpleContext) Scope() string
Scope returns the scope
func (*SimpleContext) Tier ¶ added in v1.1.0
func (sc *SimpleContext) Tier() string
Tier returns the tier
type Store ¶
type Store interface {
// Get retrieves a value from the store
Get(ctx context.Context, key string) ([]byte, error)
// Set stores a value in the store with an optional expiration
Set(ctx context.Context, key string, value []byte, expiration time.Duration) error
// Increment atomically increments a counter and returns the new value
Increment(ctx context.Context, key string, expiration time.Duration) (int64, error)
// IncrementBy atomically increments a counter by the given amount
IncrementBy(ctx context.Context, key string, amount int64, expiration time.Duration) (int64, error)
// Delete removes a key from the store
Delete(ctx context.Context, key string) error
// Exists checks if a key exists in the store
Exists(ctx context.Context, key string) (bool, error)
// ExecuteScript executes a Lua script atomically in the store
// This enables complex atomic operations for race-free rate limiting
// Returns the script result as interface{} which must be type-asserted by caller
// Returns ErrScriptNotSupported if the store doesn't support scripting
ExecuteScript(ctx context.Context, script string, keys []string, args ...interface{}) (interface{}, error)
// Health checks the health of the store connection
Health(ctx context.Context) error
// Close closes the store connection
Close() error
}
Store represents a storage backend for rate limiting data
type TierConfig ¶
type TierConfig struct {
// TierName is the name of the tier
TierName string
// ScopeLimits maps scope names to their specific limits
// Example: "global" -> 1000/1h, "search" -> 100/1m
ScopeLimits map[string]*LimitConfig
// DefaultLimit is the default limit for scopes not explicitly configured
DefaultLimit *LimitConfig
// Strategy is the default strategy for this tier
Strategy string
// Description is a human-readable description of this tier
Description string
}
TierConfig represents rate limit configuration for a service tier
func NewTierConfig ¶ added in v1.1.0
func NewTierConfig(tierName string, defaultLimit *LimitConfig) *TierConfig
NewTierConfig creates a new tier configuration
func (*TierConfig) Clone ¶ added in v1.1.0
func (tc *TierConfig) Clone() *TierConfig
Clone creates a copy of the tier configuration
func (*TierConfig) GetScopeLimit ¶ added in v1.1.0
func (tc *TierConfig) GetScopeLimit(scope string) *LimitConfig
GetScopeLimit gets the limit for a specific scope, or default if not found
func (*TierConfig) SetScopeLimit ¶ added in v1.1.0
func (tc *TierConfig) SetScopeLimit(scope string, limit *LimitConfig)
SetScopeLimit sets the limit for a specific scope
func (*TierConfig) Validate ¶ added in v1.1.0
func (tc *TierConfig) Validate() error
Validate validates the tier configuration
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
algorithms/sliding_window.go
|
algorithms/sliding_window.go |
|
examples
|
|
|
basic
command
Package main demonstrates the most basic usage of gorly
|
Package main demonstrates the most basic usage of gorly |
|
builder
command
Package main demonstrates the builder pattern for configuring rate limiters
|
Package main demonstrates the builder pattern for configuring rate limiters |
|
middleware
command
Package main demonstrates HTTP middleware integration
|
Package main demonstrates HTTP middleware integration |
|
pattern-routing
command
Package main demonstrates pattern-based rate limiting with gorly.
|
Package main demonstrates pattern-based rate limiting with gorly. |
|
tiers
command
Package main demonstrates multi-tier rate limiting with scopes
|
Package main demonstrates multi-tier rate limiting with scopes |
|
version-info
command
Package main demonstrates comprehensive version information features using go-version
|
Package main demonstrates comprehensive version information features using go-version |
|
middleware/http.go
|
middleware/http.go |
|
Package routing provides debugging and inspection tools for pattern-based routing.
|
Package routing provides debugging and inspection tools for pattern-based routing. |