-
Notifications
You must be signed in to change notification settings - Fork 322
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
feat: support for adaptive rate limiting [PIPE-481] #4160
Conversation
Codecov ReportAttention:
Additional details and impacted files@@ Coverage Diff @@
## master #4160 +/- ##
==========================================
+ Coverage 72.61% 72.62% +0.01%
==========================================
Files 387 397 +10
Lines 56479 56395 -84
==========================================
- Hits 41012 40959 -53
+ Misses 13098 13073 -25
+ Partials 2369 2363 -6 ☔ View full report in Codecov by Sentry. |
44e1790
to
1204bb0
Compare
1c303d6
to
d8302b1
Compare
1422c5e
to
5afbf5e
Compare
This comment was marked as resolved.
This comment was marked as resolved.
type Factory interface { | ||
Get(destName, destID string) Throttler | ||
Shutdown() | ||
} | ||
|
||
// NewFactory constructs a new Throttler Factory | ||
func NewFactory(config *config.Config, stats stats.Stats) (Factory, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we return a Factory interface
instead of a concrete implementation Factory struct
?
we can create a factory interface in router package if needed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am returning the interface here to limit the scope of the consumer.
cc @atzoum
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@atzoum can you elaborate, it seems like an anti-pattern to me
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are asking me to comment on a debatable topic :)
My view is that there are valid reasons for choosing to return interfaces instead of concrete implementations, e.g.:
- Hiding implementation details from callers (no need to worry about misuse of erroneously exported fields in structs)
- Augmenting/decorating behaviour at runtime
- Succinct documentation of the provided API (methods) to callers
The above doesn't mean that the callers need to re-use the interface that is being returned as is, they can declare their own (scoped) interfaces if needed.
@@ -0,0 +1,37 @@ | |||
package throttler |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@atzoum Is it required to hot reload between the two implemantioans?
When we the switch happens, the previous throttles will keep running in the background:
a. the use of memory and CPU resources
b. they stop getting updates on what is going on, which might lead to unknown/unpredictable behaviour if we switch back to them.
From my perspective, the probability of switching between those implementations will be low and restarting the processer is not a big issue. Given the additional implementation and risk for switching between implementations.
I would suggest removing this package, and resorting to restarts if needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see that much of a risk & the ability to switch without a restart is a blessing, especially if we need to do it in bulk.
a. CPU and memory overhead shouldn't be a consideration, GCRA requirements are minimal, let alone when it's not being used.
b. Both can be updated during ResponseCodeReceived to keep them up-to-date.
03c59ad
to
a30df71
Compare
@@ -0,0 +1,37 @@ | |||
package throttler |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see that much of a risk & the ability to switch without a restart is a blessing, especially if we need to do it in bulk.
a. CPU and memory overhead shouldn't be a consideration, GCRA requirements are minimal, let alone when it's not being used.
b. Both can be updated during ResponseCodeReceived to keep them up-to-date.
type Factory interface { | ||
Get(destName, destID string) Throttler | ||
Shutdown() | ||
} | ||
|
||
// NewFactory constructs a new Throttler Factory | ||
func NewFactory(config *config.Config, stats stats.Stats) (Factory, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are asking me to comment on a debatable topic :)
My view is that there are valid reasons for choosing to return interfaces instead of concrete implementations, e.g.:
- Hiding implementation details from callers (no need to worry about misuse of erroneously exported fields in structs)
- Augmenting/decorating behaviour at runtime
- Succinct documentation of the provided API (methods) to callers
The above doesn't mean that the callers need to re-use the interface that is being returned as is, they can declare their own (scoped) interfaces if needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some last comments
router/throttler/adaptive.go
Outdated
|
||
func (t *adaptiveThrottler) getLimit() int64 { | ||
limitFactor := t.algorithm.LimitFactor() | ||
defer t.limitFactorMeasurement.Gauge(limitFactor) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
defer t.limitFactorMeasurement.Gauge(limitFactor) | |
t.limitFactorMeasurement.Gauge(limitFactor) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
router/throttler/factory.go
Outdated
"destination_id": destID, | ||
"destination_name": destName, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"destination_id": destID, | |
"destination_name": destName, | |
"destinationId": destID, | |
"destType": destName, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
router/throttler/factory.go
Outdated
"destination_id": destID, | ||
"destination_name": destName, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe these are the labels we are using
"destination_id": destID, | |
"destination_name": destName, | |
"destinationId": destID, | |
"destType": destName, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
router/throttler/factory.go
Outdated
tags := stats.Tags{ | ||
"destination_id": destID, | ||
"destination_name": destName, | ||
"adaptive_enabled": fmt.Sprintf("%t", f.throttlers[destID].(*switchingThrottler).adaptiveEnabled.Load()), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"adaptive_enabled": fmt.Sprintf("%t", f.throttlers[destID].(*switchingThrottler).adaptiveEnabled.Load()), | |
"adaptive": fmt.Sprintf("%t", f.throttlers[destID].(*switchingThrottler).adaptiveEnabled.Load()), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed it
Description
Support for adaptive rate limiting. More info: https://www.notion.so/rudderstacks/Adaptive-Rate-Limiting-dfc7c99abb7e4a4c9a89bf8cef6141bd
In this PR, I have implemented the Approach 1
Security
Summary by CodeRabbit
New Features
Improvements
Testing
Documentation
Refactor
Bug Fixes