-
Notifications
You must be signed in to change notification settings - Fork 1.1k
feat(mysql): add AWS locality-aware backend selection #6061
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
Merged
renecannao
merged 19 commits into
feature/aws-iam-database-auth
from
feature/aws-locality-awareness-design
Aug 16, 2026
Merged
Changes from all commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
64ffa63
docs: design AWS locality-aware backend selection
renecannao 211d2ba
docs: define AWS locality stats lifecycle
renecannao 7b4666e
docs: plan AWS locality awareness implementation
renecannao 7d1220a
feat(mysql): parse AWS locality policies
renecannao 567419b
feat(mysql): manage asynchronous AWS locality metadata
renecannao 6713cab
feat(mysql): load AWS locality configuration
renecannao e1ee397
feat(mysql): apply AWS locality during backend selection
renecannao 2d9a77c
feat(aws): provide asynchronous locality metadata
renecannao 39dab69
feat(stats): expose AWS locality decisions
renecannao 8b1a7cf
docs: document AWS locality awareness
renecannao f8a2f56
test: sort AWS locality group entries
renecannao ed8f092
test: publish locality manager in config fixture
renecannao a7d0954
fix(mysql): harden AWS locality lifecycle and selection
renecannao e8c5423
fix(aws): constrain IMDS curl transport
renecannao 7b15362
fix(aws): keep IMDS transport HTTP-only
renecannao d0c2476
fix(aws): resolve locality Sonar findings
renecannao 5791c85
refactor(aws): move locality provider out of tree
renecannao b72e1a5
Merge updated AWS IAM base
renecannao 48be8fa
Merge commit '5a7a08c87' into agent/aws-locality-public-boundary
renecannao 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,114 @@ | ||
| # AWS locality-aware MySQL backend selection | ||
|
|
||
| ProxySQL 4.0 can apply temporary locality multipliers while selecting eligible | ||
| Amazon RDS and Aurora MySQL backends. The MySQL module owns the configuration, | ||
| policy validation, immutable selection snapshot, and effective-weight | ||
| calculation. Locality never changes `mysql_servers.weight`, | ||
| `runtime_mysql_servers.weight`, saved configuration, or ProxySQL Cluster | ||
| checksums. | ||
|
|
||
| Locality metadata is supplied asynchronously by an optional compatible | ||
| external provider. Without a provider, or when metadata is unavailable or too | ||
| old, selection stays neutral and uses the configured server weights. | ||
|
|
||
| ## Hostgroup policy | ||
|
|
||
| Add `aws.locality_awareness` to the existing | ||
| `mysql_hostgroup_attributes.hostgroup_settings` JSON. Both multipliers are | ||
| required: | ||
|
|
||
| ```sql | ||
| INSERT INTO mysql_hostgroup_attributes(hostgroup_id, hostgroup_settings) | ||
| VALUES ( | ||
| 10, | ||
| '{ | ||
| "aws": { | ||
| "locality_awareness": { | ||
| "same_region_multiplier": 2.0, | ||
| "same_az_multiplier": 4.0, | ||
| "refresh_interval_seconds": 300, | ||
| "stale_ttl_seconds": 1800 | ||
| } | ||
| } | ||
| }' | ||
| ); | ||
|
|
||
| LOAD MYSQL SERVERS TO RUNTIME; | ||
| SAVE MYSQL SERVERS TO DISK; | ||
| ``` | ||
|
|
||
| The accepted values are: | ||
|
|
||
| ```text | ||
| 1.0 <= same_region_multiplier <= same_az_multiplier <= 10.0 | ||
|
|
||
| refresh_interval_seconds default: 300 | ||
| stale_ttl_seconds default: 1800 | ||
|
|
||
| 30 <= refresh_interval_seconds <= 86400 | ||
| refresh_interval_seconds <= stale_ttl_seconds <= 604800 | ||
| ``` | ||
|
|
||
| Multipliers are JSON numbers. An invalid `aws.locality_awareness` object | ||
| disables locality bias for that hostgroup when servers are loaded. Diagnostics | ||
| identify the rejected field and hostgroup without logging the supplied value. | ||
|
|
||
| ## Master switch | ||
|
|
||
| The process-wide MySQL variable defaults to `false`: | ||
|
|
||
| ```sql | ||
| SET mysql-aws_locality_awareness = true; | ||
| LOAD MYSQL VARIABLES TO RUNTIME; | ||
| SAVE MYSQL VARIABLES TO DISK; | ||
| ``` | ||
|
|
||
| Disabling the variable immediately restores configured-weight selection, | ||
| cancels or supersedes outstanding locality requests, and stops new refresh | ||
| scheduling. It does not change existing backend connections or server rows. | ||
|
|
||
| ## Selection contract | ||
|
|
||
| For each selection attempt, after the normal health, capacity, lag, GTID, | ||
| backoff, and session-compatibility checks, ProxySQL calculates: | ||
|
|
||
| ```text | ||
| remote or unknown configured_weight | ||
| same Region, different AZ int(configured_weight * same_region_multiplier) | ||
| same AZ int(configured_weight * same_az_multiplier) | ||
| ``` | ||
|
|
||
| Conversion to an integer truncates toward zero. The tiers are not cumulative, | ||
| and configured weight zero stays zero. Global hostgroup selection and | ||
| thread-local idle-connection reuse use the same immutable snapshot and never | ||
| perform provider or network work on the selection path. | ||
|
|
||
| ProxySQL recognizes eligible RDS and Aurora endpoint shapes to form neutral | ||
| metadata requests. A compatible provider is responsible for authoritative | ||
| endpoint discovery and normalized Region, Availability Zone, and account | ||
| metadata. Custom CNAMEs, arbitrary MySQL hosts, proxy endpoints, malformed | ||
| names, and endpoints the provider cannot confirm remain neutral. | ||
|
|
||
| ## Provider absence and failures | ||
|
|
||
| The public provider interface uses retained leases so shutdown rejects new | ||
| work, drains active callbacks, joins the locality manager worker, and only then | ||
| permits provider destruction and module unload. Provider absence is exposed as | ||
| the fixed `provider_unavailable` category. | ||
|
|
||
| Metadata states are `pending`, `fresh`, `stale`, `expired`, `error`, and | ||
| `disabled`. Only `fresh` and unexpired `stale` values activate a multiplier. | ||
| All other states use multiplier `1.0`, so effective weights equal configured | ||
| weights. A failed refresh can retain the last successful value through the | ||
| bounded stale TTL; it becomes neutral after expiry. | ||
|
|
||
| An external provider may register a read-only runtime diagnostics table using | ||
| the plugin table/view services and the MySQL-owned projection callback. Public | ||
| core does not register `stats_mysql_aws_locality`; without a provider that | ||
| registers it, the table does not exist. | ||
|
|
||
| ## Rollback | ||
|
|
||
| Set `mysql-aws_locality_awareness` to `false` and load MySQL variables to | ||
| runtime. To remove a policy, delete the `aws.locality_awareness` object from | ||
| that hostgroup's `hostgroup_settings`, then load MySQL servers to runtime. | ||
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,141 @@ | ||
| #ifndef __CLASS_AWS_LOCALITY_MANAGER_H | ||
| #define __CLASS_AWS_LOCALITY_MANAGER_H | ||
|
|
||
| #include "Aws_Locality_Types.h" | ||
| #include "json_fwd.hpp" | ||
|
|
||
| #include <chrono> | ||
| #include <cstddef> | ||
| #include <cstdint> | ||
| #include <functional> | ||
| #include <memory> | ||
| #include <string> | ||
| #include <string_view> | ||
| #include <unordered_map> | ||
| #include <unordered_set> | ||
| #include <vector> | ||
|
|
||
| AwsLocalityPolicy parse_aws_locality_policy( | ||
| const nlohmann::json& policy_json, | ||
| uint32_t hostgroup_id, | ||
| AwsLocalityPolicyError& error); | ||
|
|
||
| AwsEndpointCandidate recognize_rds_endpoint( | ||
| uint32_t hostgroup_id, | ||
| std::string_view hostname, | ||
| uint16_t port); | ||
|
|
||
| AwsLocalityClass classify_aws_locality( | ||
| const AwsLocalLocation& local, | ||
| const AwsBackendLocation& backend); | ||
|
|
||
| uint64_t aws_locality_effective_weight( | ||
| int64_t configured_weight, | ||
| double multiplier); | ||
|
|
||
| uint64_t aws_locality_saturating_add(uint64_t lhs, uint64_t rhs); | ||
| size_t aws_locality_weighted_index( | ||
| const uint64_t* weights, | ||
| size_t count, | ||
| uint64_t random_value); | ||
|
|
||
| using AwsMetadataProviderDestroyFn = void (*)(AwsMetadataProvider*); | ||
|
|
||
| class AwsMetadataProviderLease { | ||
| public: | ||
| AwsMetadataProviderLease() = default; | ||
| ~AwsMetadataProviderLease(); | ||
| AwsMetadataProviderLease(AwsMetadataProviderLease&& other) noexcept; | ||
| AwsMetadataProviderLease& operator=(AwsMetadataProviderLease&& other) noexcept; | ||
|
|
||
| AwsMetadataProvider* get() const { return provider_; } | ||
| AwsMetadataProvider* operator->() const { return provider_; } | ||
| explicit operator bool() const { return provider_ != nullptr; } | ||
|
|
||
| AwsMetadataProviderLease(const AwsMetadataProviderLease&) = delete; | ||
| AwsMetadataProviderLease& operator=(const AwsMetadataProviderLease&) = delete; | ||
|
|
||
| private: | ||
| explicit AwsMetadataProviderLease(AwsMetadataProvider* provider) | ||
| : provider_(provider) {} | ||
| void release(); | ||
| AwsMetadataProvider* provider_ { nullptr }; | ||
|
|
||
| friend AwsMetadataProviderLease acquire_global_aws_metadata_provider(); | ||
| }; | ||
|
|
||
| bool install_global_aws_metadata_provider( | ||
| AwsMetadataProvider* provider, | ||
| AwsMetadataProviderDestroyFn destroy, | ||
| void* module_handle); | ||
| AwsMetadataProviderLease acquire_global_aws_metadata_provider(); | ||
| void shutdown_global_aws_metadata_provider(); | ||
|
|
||
| struct AwsLocalitySnapshotEntry { | ||
| uint32_t hostgroup_id { 0 }; | ||
| std::string hostname; | ||
| uint16_t port { 0 }; | ||
| AwsEndpointType endpoint_type { AwsEndpointType::unknown }; | ||
| int64_t configured_weight { 0 }; | ||
| AwsLocalLocation local; | ||
| AwsBackendLocation backend; | ||
| AwsLocalityClass locality { AwsLocalityClass::unknown }; | ||
| double multiplier { 1.0 }; | ||
| AwsLocalityMetadataStatus status { AwsLocalityMetadataStatus::disabled }; | ||
| int64_t last_success_timestamp { 0 }; | ||
| int64_t last_attempt_timestamp { 0 }; | ||
| std::string failure_category; | ||
| }; | ||
|
|
||
| struct AwsLocalitySnapshot { | ||
| uint64_t generation { 0 }; | ||
| bool enabled { false }; | ||
| std::unordered_multimap<uint64_t, AwsLocalitySnapshotEntry> entries; | ||
| std::unordered_set<uint32_t> hostgroups; | ||
|
|
||
| const AwsLocalitySnapshotEntry* find( | ||
| uint32_t hostgroup_id, | ||
| std::string_view hostname, | ||
| uint16_t port) const; | ||
| uint64_t effective_weight( | ||
| uint32_t hostgroup_id, | ||
| std::string_view hostname, | ||
| uint16_t port, | ||
| int64_t configured_weight) const; | ||
|
renecannao marked this conversation as resolved.
|
||
| bool has_hostgroup(uint32_t hostgroup_id) const { | ||
| return hostgroups.find(hostgroup_id) != hostgroups.end(); | ||
| } | ||
| }; | ||
|
|
||
| struct AwsLocalityManagerConfig { | ||
| using SteadyClock = std::function<std::chrono::steady_clock::time_point()>; | ||
| using WallClock = std::function<std::chrono::system_clock::time_point()>; | ||
|
|
||
| AwsLocalityManagerConfig(); | ||
| SteadyClock steady_clock; | ||
| WallClock wall_clock; | ||
| std::chrono::milliseconds request_timeout { std::chrono::seconds(5) }; | ||
| std::chrono::milliseconds disable_wait_timeout { std::chrono::milliseconds(250) }; | ||
| std::function<void()> before_completion; | ||
| }; | ||
|
|
||
| class MySQLAwsLocalityManager { | ||
| public: | ||
| explicit MySQLAwsLocalityManager(AwsLocalityManagerConfig config = {}); | ||
| ~MySQLAwsLocalityManager(); | ||
| MySQLAwsLocalityManager(const MySQLAwsLocalityManager&) = delete; | ||
| MySQLAwsLocalityManager& operator=(const MySQLAwsLocalityManager&) = delete; | ||
|
|
||
| void configure(std::vector<AwsLocalityHostgroupConfig> hostgroups); | ||
| void set_enabled(bool enabled); | ||
| void request_refresh(); | ||
| std::shared_ptr<const AwsLocalitySnapshot> snapshot() const; | ||
| std::vector<AwsLocalitySnapshotEntry> diagnostic_rows() const; | ||
| void shutdown(); | ||
|
|
||
| private: | ||
| class Impl; | ||
| std::unique_ptr<Impl> impl_; | ||
| }; | ||
|
|
||
| #endif // __CLASS_AWS_LOCALITY_MANAGER_H | ||
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.