-
Couldn't load subscription status.
- Fork 1.7k
En/rate limiter #2263
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Umang01-hash
wants to merge
26
commits into
development
Choose a base branch
from
en/rate_limiter
base: development
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
En/rate limiter #2263
Changes from 10 commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
c8a566c
initial implementation for distributed and local storage rate limiters
Umang01-hash 2048c54
fix linters and other errors in the initial implementation
Umang01-hash 1d28995
fix bugs in the implementation after testing
Umang01-hash 4e6ecd7
add documentation
Umang01-hash a651ea7
add test for local rate limiter implementation
Umang01-hash 2b78265
Merge remote-tracking branch 'origin' into en/rate_limiter
Umang01-hash e826318
fix test
Umang01-hash 51616b5
fix rate limiter concurrency test
Umang01-hash 71d4f72
fix linters
Umang01-hash aae16f3
Merge remote-tracking branch 'origin' into en/rate_limiter
Umang01-hash a104474
make time window generic
Umang01-hash 8c8b29b
update documentation
Umang01-hash 1217084
Merge remote-tracking branch 'origin' into en/rate_limiter
Umang01-hash 12bb728
resolve review comments
Umang01-hash 51d3388
replace concrete rate limiter stores with interface
Umang01-hash 29b15e2
Merge remote-tracking branch 'origin' into en/rate_limiter
Umang01-hash a90d0e2
add more tests
Umang01-hash fd7fe70
refactor implementation to unify the structs and remove duplicate codes
Umang01-hash 815dec7
re-write tests
Umang01-hash 82623af
Merge remote-tracking branch 'origin' into en/rate_limiter
Umang01-hash c54c78e
revert unwanted changes
Umang01-hash 8e6f6ea
remove changes in interface of logger and metrics
Umang01-hash 8568065
fix linters
Umang01-hash bb387ea
refactoring implementation
Umang01-hash f9f7b04
build(deps): update github.com/grpc-ecosystem/go-grpc-middleware to v2
Juneezee 8bfd3fb
refactor(docs): replace {serviceName} placeholders with <SERVICE_NAME…
NishantRajZop File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,7 @@ import ( | |
|
|
||
| type Logger interface { | ||
| Log(args ...any) | ||
| Debug(args ...any) | ||
| } | ||
|
|
||
| type Log struct { | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| package service | ||
|
|
||
| import ( | ||
| "errors" | ||
| "fmt" | ||
| "net/http" | ||
| "time" | ||
|
|
||
| gofrRedis "gofr.dev/pkg/gofr/datasource/redis" | ||
| ) | ||
|
|
||
| var ( | ||
| errInvalidRequestRate = errors.New("requestsPerSecond must be greater than 0") | ||
Umang01-hash marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| errInvalidBurstSize = errors.New("burst must be greater than 0") | ||
Umang01-hash marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| errInvalidRedisResultType = errors.New("unexpected Redis result type") | ||
| ) | ||
|
|
||
| // RateLimiterConfig with custom keying support. | ||
| type RateLimiterConfig struct { | ||
| RequestsPerSecond float64 // Token refill rate (must be > 0) | ||
| Burst int // Maximum burst capacity (must be > 0) | ||
| KeyFunc func(*http.Request) string // Optional custom key extraction | ||
| RedisClient *gofrRedis.Redis `json:"-"` // Optional Redis for distributed limiting | ||
| } | ||
|
|
||
| // defaultKeyFunc extracts a normalized service key from an HTTP request. | ||
| func defaultKeyFunc(req *http.Request) string { | ||
| if req == nil || req.URL == nil { | ||
| return "unknown" | ||
| } | ||
|
|
||
| scheme := req.URL.Scheme | ||
| host := req.URL.Host | ||
|
|
||
| if scheme == "" { | ||
| if req.TLS != nil { | ||
| scheme = methodHTTPS | ||
| } else { | ||
| scheme = methodHTTP | ||
| } | ||
| } | ||
|
|
||
| if host == "" { | ||
| host = req.Host | ||
| } | ||
|
|
||
| if host == "" { | ||
| host = unknownServiceKey | ||
| } | ||
|
|
||
| return scheme + "://" + host | ||
| } | ||
|
|
||
| // Validate checks if the configuration is valid. | ||
| func (config *RateLimiterConfig) Validate() error { | ||
| if config.RequestsPerSecond <= 0 { | ||
| return fmt.Errorf("%w: %f", errInvalidRequestRate, config.RequestsPerSecond) | ||
| } | ||
|
|
||
| if config.Burst <= 0 { | ||
| return fmt.Errorf("%w: %d", errInvalidBurstSize, config.Burst) | ||
| } | ||
|
|
||
| // Set default key function if not provided. | ||
| if config.KeyFunc == nil { | ||
| config.KeyFunc = defaultKeyFunc | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| // AddOption implements the Options interface. | ||
| func (config *RateLimiterConfig) AddOption(h HTTP) HTTP { | ||
| if err := config.Validate(); err != nil { | ||
| if httpSvc, ok := h.(*httpService); ok { | ||
| httpSvc.Logger.Log("Invalid rate limiter config, disabling rate limiting", "error", err) | ||
| } | ||
|
|
||
| return h | ||
| } | ||
|
|
||
| // Choose implementation based on Redis client availability. | ||
| if config.RedisClient != nil { | ||
| return NewDistributedRateLimiter(*config, h) | ||
| } | ||
|
|
||
| // Log warning for local rate limiting. | ||
| if httpSvc, ok := h.(*httpService); ok { | ||
| httpSvc.Logger.Log("Using local rate limiting - not suitable for multi-instance deployments") | ||
| } | ||
|
|
||
| return NewLocalRateLimiter(*config, h) | ||
| } | ||
|
|
||
| // RateLimitError represents a rate limiting error. | ||
| type RateLimitError struct { | ||
| ServiceKey string | ||
| RetryAfter time.Duration | ||
| } | ||
|
|
||
| func (e *RateLimitError) Error() string { | ||
| return fmt.Sprintf("rate limit exceeded for service: %s, retry after: %v", e.ServiceKey, e.RetryAfter) | ||
| } | ||
|
|
||
| // StatusCode Implement StatusCodeResponder so Responder picks correct HTTP code. | ||
| func (*RateLimitError) StatusCode() int { | ||
| return http.StatusTooManyRequests // 429 | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.