RFC: Behavior Versions
Status: RFC
Applies to: client
For a summarized list of proposed changes, see the Changes Checklist section.
This RFC describes "Behavior Versions," a mechanism to allow SDKs to ship breaking behavioral changes like a new retry strategy, while allowing customers who rely on extremely consistent behavior to evolve at their own pace.
By adding behavior major versions (BMV) to the Rust SDK, we will make it possible to ship new secure/recommended defaults to new customers without impacting legacy customers.
The fundamental issue stems around our inability to communicate and decouple releases of service updates and behavior within a single major version.
Both legacy and new SDKs have the need to alter their SDKs default. Historically, this caused new customers on legacy SDKs to be subject to legacy defaults, even when a better alternative existed.
For new SDKs, a GA cutline presents difficult choices around timeline and features that can’t be added later without altering behavior.
Both of these use cases are addressed by Behavior Versions.
The user experience if this RFC is implemented
In the current version of the SDK, users can construct clients without indicating any sort of behavior major version. Once this RFC is implemented, there will be two ways to set a behavior major version:
- In code via
aws_config::defaults(BehaviorVersion::latest())
and<service>::Config::builder().behavior_version(...)
. This will also work forconfig_override
. - By enabling
behavior-version-latest
in eitheraws-config
(which brings backfrom_env
) OR a specific generated SDK crate
# Cargo.toml
[dependencies]
aws-config = { version = "1", features = ["behavior-version-latest"] }
# OR
aws-sdk-s3 = { version = "1", features = ["behavior-version-latest"] }
If no BehaviorVersion
is set, the client will panic during construction.
BehaviorVersion
is an opaque struct with initializers like ::latest()
, ::v2023_11_09()
. Downstream code can check the version by calling methods like ::supports_v1()
When new BMV are added, the previous version constructor will be marked as deprecated
. This serves as a mechanism to alert customers that a new BMV exists to allow them to upgrade.
How to actually implement this RFC
In order to implement this feature, we need to create a BehaviorVersion
struct, add config options to SdkConfig
and aws-config
, and wire it throughout the stack.
#![allow(unused)] fn main() { /// Behavior major-version of the client /// /// Over time, new best-practice behaviors are introduced. However, these behaviors might not be backwards /// compatible. For example, a change which introduces new default timeouts or a new retry-mode for /// all operations might be the ideal behavior but could break existing applications. #[derive(Debug, Clone)] pub struct BehaviorVersion { // currently there is only 1 MV so we don't actually need anything in here. _private: (), } }
To help customers migrate, we are including from_env
hooks that set behavior-version-latest
that are deprecated. This allows customers to see that they are missing the required cargo feature and add it to remove the deprecation warning.
Internally, BehaviorVersion
will become an additional field on <client>::Config
. It is not ever stored in the ConfigBag
or in RuntimePlugins
.
When constructing the set of "default runtime plugins," the default runtime plugin parameters will be passed the BehaviorVersion
. This will select the correct runtime plugin. Logging will clearly indicate which plugin was selected.
Design Alternatives Considered
An original design was also considered that made BMV optional and relied on documentation to steer customers in the right direction. This was deemed too weak of a mechanism to ensure that customers aren't broken by unexpected changes.
Changes checklist
-
Create
BehaviorVersion
and the BMV runtime plugin - Add BMV as a required runtime component
- Wire up setters throughout the stack
- Add tests of BMV (set via aws-config, cargo features & code params)
-
RemoveWe decided to persist these deprecationsaws_config::from_env
deprecation stand-ins - Update generated usage examples