v2.0.0 - Pluggable Store Interface
Fortify v2.0.0 - Pluggable Store Interface
This release introduces a major architectural refactor of the rate limiter to use a pluggable Store interface, enabling distributed rate limiting across multiple application instances.
⚠️ Breaking Changes
- Removed
backends/redismodule - Users now implement theStoreinterface for custom backends - Removed
examples/backends/redis- See migration guide for implementation patterns
✨ New Features
Pluggable Store Interface
type Store interface {
AtomicUpdate(ctx context.Context, key string, updateFn func(*BucketState) *BucketState) (*BucketState, error)
Get(ctx context.Context, key string) (*BucketState, error)
Delete(ctx context.Context, key string) error
Close() error
}New Rate Limiter Methods
Execute(ctx, key, operation)- Combines rate limiting check with operation executionExecuteN(ctx, key, tokens, operation)- Multi-token variant for batch operationsReset(ctx)- Clear all rate limiting state (requiresResetterinterface)BucketCount()- Monitor active buckets (requiresBucketCounterinterface)
Optional Interfaces for Extensibility
HealthChecker- Health check support for distributed storesResetter- Reset/clear all bucketsBucketCounter- Report active bucket count
Configuration Enhancements
FailOpen- Allow requests when storage fails (availability over consistency)MaxTokensPerRequest- Prevent DoS via excessive token requests
📊 Performance (Apple M1, Go 1.23)
| Operation | Latency | Memory | Allocations |
|---|---|---|---|
| Allow() | ~200ns | 74B | 3 |
| Take() | ~197ns | 65B | 3 |
| BucketCount() | ~3ns | 0B | 0 |
| Concurrent | ~395ns | 82B | 3 |
📈 Quality Metrics
- Test Coverage: 92.3%
- Race Detection: Clean
- Security Grade: A+
- Examples: 16 runnable examples
🔄 Migration Guide
See docs/MIGRATION_REDIS.md for:
- Custom Redis Store implementation patterns
- DynamoDB and PostgreSQL examples
- Migration steps from v1.x
📦 Installation
go get github.com/felixgeelhaar/fortify@v2.0.0💡 Quick Start
// In-memory (default) - no changes required
rl := ratelimit.New(ratelimit.Config{
Rate: 100,
Burst: 200,
Interval: time.Second,
})
// Custom store for distributed rate limiting
rl := ratelimit.New(ratelimit.Config{
Rate: 100,
Burst: 200,
Interval: time.Second,
Store: myRedisStore, // Implement Store interface
FailOpen: true, // Allow on storage failure
})
// New Execute method
err := rl.Execute(ctx, "user-123", func() error {
return processRequest()
})🙏 Contributors
Thank you to everyone who contributed to this release!