Skip to content
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

Merged
merged 18 commits into from
Dec 6, 2023

Conversation

BonapartePC
Copy link
Contributor

@BonapartePC BonapartePC commented Nov 20, 2023

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

  • The code changed/added as part of this pull request won't create any security issues with how the software is being used.

Summary by CodeRabbit

  • New Features

    • Introduced adaptive throttling with real-time adjustments based on system feedback.
    • Added a new throttler factory system for more flexible throttler initialization and management.
  • Improvements

    • Enhanced throttling configuration with support for dynamic limit adjustments.
    • Improved system resilience by adding shutdown procedures for throttler components.
  • Testing

    • Implemented comprehensive tests for adaptive throttling behavior and configuration.
  • Documentation

    • Updated comments and documentation to reflect changes in throttling mechanisms and configurations.
  • Refactor

    • Refactored throttler initialization to use factories instead of direct instance creation.
    • Changed several throttler-related structures to improve performance and maintainability.
  • Bug Fixes

    • Fixed issues with throttler limit calculations and response code handling.

@BonapartePC BonapartePC marked this pull request as draft November 20, 2023 09:01
@BonapartePC BonapartePC marked this pull request as ready for review November 20, 2023 09:11
@BonapartePC BonapartePC marked this pull request as draft November 20, 2023 09:12
Copy link

codecov bot commented Nov 20, 2023

Codecov Report

Attention: 54 lines in your changes are missing coverage. Please review.

Comparison is base (7ff4103) 72.61% compared to head (3b1d077) 72.62%.
Report is 5 commits behind head on master.

Files Patch % Lines
router/throttler/factory.go 75.21% 24 Missing and 5 partials ⚠️
router/throttler/adaptive.go 57.50% 16 Missing and 1 partial ⚠️
...er/throttler/adaptivethrottlercounter/algorithm.go 94.54% 2 Missing and 1 partial ⚠️
router/throttler/static.go 88.88% 2 Missing and 1 partial ⚠️
app/apphandlers/processorAppHandler.go 0.00% 1 Missing ⚠️
router/handle.go 87.50% 0 Missing and 1 partial ⚠️
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.
📢 Have feedback on the report? Share it here.

@BonapartePC BonapartePC marked this pull request as ready for review November 20, 2023 13:32
router/throttler/adaptiveRateLimit.go Outdated Show resolved Hide resolved
router/throttler/adaptiveRateLimit.go Outdated Show resolved Hide resolved
router/throttler/adaptiveRateLimit.go Outdated Show resolved Hide resolved
router/throttler/adaptiveRateLimit.go Outdated Show resolved Hide resolved
router/throttler/throttler.go Outdated Show resolved Hide resolved
router/throttler/adaptiveRateLimit.go Outdated Show resolved Hide resolved
router/throttler/adaptiveRateLimit.go Outdated Show resolved Hide resolved
@BonapartePC BonapartePC force-pushed the feat.adaptiveRateLimiting branch from 44e1790 to 1204bb0 Compare November 21, 2023 14:28
@BonapartePC BonapartePC force-pushed the feat.adaptiveRateLimiting branch from 1c303d6 to d8302b1 Compare November 23, 2023 15:46
@BonapartePC BonapartePC force-pushed the feat.adaptiveRateLimiting branch from 1422c5e to 5afbf5e Compare November 23, 2023 16:19
@BonapartePC BonapartePC requested a review from lvrach November 23, 2023 16:35
router/throttler/adaptiveAlgorithmCounter/algorithm.go Outdated Show resolved Hide resolved
router/throttler/adaptiveAlgorithmCounter/algorithm.go Outdated Show resolved Hide resolved
router/throttler/adaptive.go Outdated Show resolved Hide resolved
router/throttler/adaptiveAlgorithmCounter/algorithm.go Outdated Show resolved Hide resolved
router/throttler/config.go Outdated Show resolved Hide resolved
router/throttler/config.go Outdated Show resolved Hide resolved

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as spam.

router/throttler/algorithm.go Outdated Show resolved Hide resolved
router/throttler/default.go Outdated Show resolved Hide resolved
Comment on lines +14 to +20
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) {
Copy link
Member

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

Copy link
Contributor Author

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

Copy link
Member

@lvrach lvrach Dec 5, 2023

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

Copy link
Contributor

@atzoum atzoum Dec 6, 2023

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.

router/throttler/algorithm.go Outdated Show resolved Hide resolved
router/throttler/config.go Outdated Show resolved Hide resolved
router/throttler/factory.go Show resolved Hide resolved
router/throttler/factory.go Outdated Show resolved Hide resolved
router/throttler/factory_test.go Outdated Show resolved Hide resolved
router/handle.go Outdated Show resolved Hide resolved
router/handle.go Outdated Show resolved Hide resolved
router/handle_lifecycle.go Outdated Show resolved Hide resolved
router/throttler/adaptiveAlgorithmCounter/algorithm.go Outdated Show resolved Hide resolved
router/throttler/adaptiveAlgorithmCounter/algorithm.go Outdated Show resolved Hide resolved
@achettyiitr achettyiitr self-requested a review December 4, 2023 15:17
@rudderlabs rudderlabs deleted a comment from coderabbitai bot Dec 4, 2023
@rudderlabs rudderlabs deleted a comment from coderabbitai bot Dec 4, 2023
@rudderlabs rudderlabs deleted a comment from coderabbitai bot Dec 4, 2023
@rudderlabs rudderlabs deleted a comment from coderabbitai bot Dec 4, 2023
@rudderlabs rudderlabs deleted a comment from coderabbitai bot Dec 4, 2023
@rudderlabs rudderlabs deleted a comment from coderabbitai bot Dec 4, 2023
@@ -0,0 +1,37 @@
package throttler
Copy link
Member

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.

Copy link
Contributor

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.

@BonapartePC BonapartePC force-pushed the feat.adaptiveRateLimiting branch from 03c59ad to a30df71 Compare December 5, 2023 16:59
@rudderlabs rudderlabs deleted a comment from coderabbitai bot Dec 6, 2023
router/throttler/adaptivethrottlercounter/algorithm.go Outdated Show resolved Hide resolved
router/throttler/switcher.go Outdated Show resolved Hide resolved
@@ -0,0 +1,37 @@
package throttler
Copy link
Contributor

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.

router/throttler/adaptive.go Show resolved Hide resolved
Comment on lines +14 to +20
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) {
Copy link
Contributor

@atzoum atzoum Dec 6, 2023

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.

@BonapartePC BonapartePC requested review from lvrach and atzoum December 6, 2023 13:54
Copy link
Contributor

@atzoum atzoum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some last comments


func (t *adaptiveThrottler) getLimit() int64 {
limitFactor := t.algorithm.LimitFactor()
defer t.limitFactorMeasurement.Gauge(limitFactor)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
defer t.limitFactorMeasurement.Gauge(limitFactor)
t.limitFactorMeasurement.Gauge(limitFactor)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines 73 to 74
"destination_id": destID,
"destination_name": destName,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"destination_id": destID,
"destination_name": destName,
"destinationId": destID,
"destType": destName,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines 48 to 49
"destination_id": destID,
"destination_name": destName,
Copy link
Contributor

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

Suggested change
"destination_id": destID,
"destination_name": destName,
"destinationId": destID,
"destType": destName,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

tags := stats.Tags{
"destination_id": destID,
"destination_name": destName,
"adaptive_enabled": fmt.Sprintf("%t", f.throttlers[destID].(*switchingThrottler).adaptiveEnabled.Load()),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"adaptive_enabled": fmt.Sprintf("%t", f.throttlers[destID].(*switchingThrottler).adaptiveEnabled.Load()),
"adaptive": fmt.Sprintf("%t", f.throttlers[destID].(*switchingThrottler).adaptiveEnabled.Load()),

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed it

@BonapartePC BonapartePC merged commit d711148 into master Dec 6, 2023
41 checks passed
@rudderlabs rudderlabs deleted a comment from coderabbitai bot Dec 7, 2023
@achettyiitr achettyiitr deleted the feat.adaptiveRateLimiting branch January 12, 2024 05:59
This was referenced Jan 15, 2024
This was referenced Feb 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants