-
Notifications
You must be signed in to change notification settings - Fork 2.3k
[QueryThrottler] Implement Query Throttler #18449
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
Conversation
…ry throttler This commit introduces the core interfaces and configuration structures for the incoming query throttler system as outlined in RFC vitessio#18412. Key additions: - ThrottlingStrategyHandler interface for implementing throttling strategies - Config struct with support for multiple throttling strategies: - TabletThrottler (Vitess native tablet throttler) - Cinnamon (Uber's load-shedding system) - ConfigLoader interface for dynamic configuration loading This establishes the architectural foundation for query-level throttling that will be built upon in subsequent commits. Ref: vitessio#18412 Signed-off-by: siddharth16396 <[email protected]>
Review ChecklistHello reviewers! 👋 Please follow this checklist when reviewing this Pull Request. General
Tests
Documentation
New flags
If a workflow is added or modified:
Backward compatibility
|
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #18449 +/- ##
==========================================
+ Coverage 67.51% 67.54% +0.03%
==========================================
Files 1607 1613 +6
Lines 263357 263742 +385
==========================================
+ Hits 177815 178155 +340
- Misses 85542 85587 +45 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Add file-based configuration loader for the incoming query throttler
system.
This implementation reads throttler configuration from a JSON file at
`/config/throttler-config.json` and supports the Config struct with
enabled flag and throttling strategy selection.
Key features:
- Implements ConfigLoader interface for file-based config loading
- Reads from fixed path /config/throttler-config.json
- Supports JSON unmarshaling of Config struct with enabled/strategy
fields
- Comprehensive error handling for file read and JSON parsing failures
- Extensive test coverage with stubbed dependencies using gostub
library
The FileBasedConfigLoader provides a foundation for loading
throttler configuration from external files, supporting the
broader incoming query throttling system architecture.
Related to: vitessio#18412
Signed-off-by: siddharth16396 <[email protected]>
Add file-based configuration loader for the incoming query throttler system. This implementation reads throttler configuration from a JSON file at `/config/throttler-config.json` and supports the Config struct with enabled flag and throttling strategy selection. Key features: - Implements ConfigLoader interface for file-based config loading - Reads from fixed path /config/throttler-config.json - Supports JSON unmarshaling of Config struct with enabled/strategy fields - Comprehensive error handling for file read and JSON parsin failures - Extensive test coverage with stubbed dependencies using gostub library The FileBasedConfigLoader provides a foundation for loading throttler configuration from external files, supporting the broader incoming query throttling system architecture. Related to: vitessio#18412 Signed-off-by: siddharth16396 <[email protected]>
Signed-off-by: siddharth16396 <[email protected]>
This commit introduces a new incoming query throttling system for Vitess tablets with the following key components: Core Implementation: * IncomingQueryThrottler: Main throttler with configurable strategies and automatic config refresh * Strategy pattern with ThrottlingStrategyHandler interface for pluggable throttling algorithms * NoOpStrategy: Default no-operation strategy * CinnamonStrategy: Placeholder for future Cinnamon throttling implementation Key Features: * Automatic configuration refresh every minute via background goroutine * Thread-safe strategy switching with proper lifecycle management (start/stop) * Integration with existing Vitess throttle infrastructure via throttle.Client * Graceful shutdown with resource cleanup Signed-off-by: siddharth16396 <[email protected]>
Signed-off-by: siddharth16396 <[email protected]>
| type FileBasedConfigLoader struct{} | ||
|
|
||
| // NewFileBasedConfigLoader creates a new instance of FileBasedConfigLoader with the given file path. | ||
| func NewFileBasedConfigLoader() *FileBasedConfigLoader { |
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.
We have a TopoServerConfig loader as well, but that will be a seperate PR by another team mate
| case ThrottlingStrategyCinnamon: | ||
| return &CinnamonStrategy{} | ||
| case ThrottlingStrategyTabletThrottler: | ||
| fallthrough |
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.
This will be implemented as a seperate PR by me.
|
|
||
| var ( | ||
| _ ConfigLoader = (*FileBasedConfigLoader)(nil) | ||
| _osReadFile = os.ReadFile |
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.
We should not update global state like this. Similar to below, we should pass in configuration like this and also then we don't need to mock / change _osReadFile.
That we add a dependency in go.mod here is a smell I think and also points at these problems.
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've incorporated the changes and used dependency injection for this
Signed-off-by: siddharth16396 <[email protected]>
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 have this? There is no implementation.
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.
Removed it, the implementation was uber specific code that couldn't be open sourced.
| type ThrottlingStrategy string | ||
|
|
||
| // Predefined throttling strategies for the IncomingQueryThrottler. | ||
| const ( | ||
| // ThrottlingStrategyTabletThrottler uses Vitess Tablet Throttler to shed load | ||
| // from incoming queries when the tablet is under pressure. | ||
| // Reference: https://vitess.io/docs/21.0/reference/features/tablet-throttler/ | ||
| ThrottlingStrategyTabletThrottler ThrottlingStrategy = "TabletThrottler" | ||
|
|
||
| // ThrottlingStrategyCinnamon uses Uber's Cinnamon load-shedding system | ||
| // to regulate incoming queries under high load conditions. | ||
| // Reference: https://www.uber.com/en-IN/blog/cinnamon-using-century-old-tech-to-build-a-mean-load-shedder/ | ||
| ThrottlingStrategyCinnamon ThrottlingStrategy = "Cinnamon" | ||
|
|
||
| // ThrottlingStrategyUnknown is used when the strategy is not known. | ||
| ThrottlingStrategyUnknown ThrottlingStrategy = "Unknown" | ||
| ) | ||
|
|
||
| // Config defines the runtime configuration for the IncomingQueryThrottler. | ||
| // It specifies whether throttling is enabled and which strategy to use. | ||
| type Config struct { | ||
| // Enabled indicates whether the throttler should actively apply throttling logic. | ||
| Enabled bool `json:"enabled"` | ||
|
|
||
| // Strategy selects which throttling strategy should be used. | ||
| Strategy ThrottlingStrategy `json:"strategy"` | ||
| } |
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.
instead of this we can do factory based. where each implementation registers to the factory and then based on the config will load that strategy
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.
This is done, this strategy is just a enum now.
go/vt/vttablet/tabletserver/incomingquerythrottler/file_based_config_loader.go
Show resolved
Hide resolved
| var ( | ||
| _configRefreshTicket = time.NewTicker(1 * time.Minute) | ||
| ) | ||
|
|
||
| type IncomingQueryThrottler struct { | ||
| ctx context.Context | ||
| throttleClient *throttle.Client | ||
| mu sync.RWMutex | ||
| // cfg holds the current configuration for the 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.
We should not use the global ticker here and pass the timer in constructor.
This way we will also remove the usage of gostub in the test.
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.
This is taken care of now.
| newCfg, err := i.cfgLoader.Load(i.ctx) | ||
| if err != nil { | ||
| log.Errorf("Error loading config: %v", err) | ||
| } |
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.
If there is an error loading the config, we cannot continue with this. It will panic with nil
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.
Ack, i will be taking care of this
I think it make sense that we perform query throttling in |
@harshit-gangal : Just to be clear, are you saying we move the call to Or can we keep it in |
Moving this to query_executor.go makes sense, and checking to throttle there is fine. Regarding your Evaluate code: that logic should be done outside the hot path. The hot path call should return from a cached value instead of evaluating on the fly. |
Signed-off-by: siddharth16396 <[email protected]>
|
I checked the Plan struct This says So should we be using
|
|
Also, to move the |
Signed-off-by: siddharth16396 <[email protected]>
query_executor.go Signed-off-by: siddharth16396 <[email protected]>
Signed-off-by: siddharth16396 <[email protected]>
|
Incorporated all the requested changes @harshit-gangal | @shlomi-noach |
| return nil, err | ||
| } | ||
|
|
||
| if reqThrottledErr := qre.tsv.queryThrottler.Throttle(qre.ctx, qre.targetTabletType, qre.plan.FullQuery, qre.logStats.TransactionID, qre.options); reqThrottledErr != nil { |
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.
use connID
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.
connID and TransactionID is same
Signed-off-by: siddharth16396 <[email protected]>
shlomi-noach
left a comment
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.
Minor last styling comment, otherwise good to go!
| tCfg := qt.cfg | ||
| tStrategy := qt.strategy |
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.
Is there an intentional reason why these two values are copied?
qt.cfgis not a pointer, the entire config is duplicated. But I don't see the need.qt.strategyis aninterfaceso there's no additional cost, but then why not justdecision := qt.strategy.Evaluate(...)?
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.
There was a reason initially because we used to have read locks on this, but that caused performance degradations so we removed the locks and so we can remove the variable assignments. let me incorp that.
Thanks for pointing out.
removing unnecessary variable assignments Co-authored-by: Shlomi Noach <[email protected]> Signed-off-by: siddharth16396 <[email protected]>
…r-first Signed-off-by: siddharth16396 <[email protected]>
…first Signed-off-by: siddharth16396 <[email protected]>
Signed-off-by: siddharth16396 <[email protected]>
Head branch was pushed to by a user without write access
|
@shlomi-noach : bd07b5a added the same comment twice, so had to remove it 😅 |
Description
This PR introduces a comprehensive incoming query throttling system for Vitess tablets outlined in RFC #18412 with the following key components:
IncomingQueryThrottlerwith strategy pattern for pluggable throttling algorithms/config/throttler-config.jsonwith enabled/disabled flag and strategy selectionThe system supports multiple throttling strategies (TabletThrottler, Cinnamon) and can be dynamically reconfigured without restart. The implementation follows Vitess patterns and integrates with the existing throttle client infrastructure.
Tests
Ref: #18412
Checklist
Deployment Notes