Skip to content

Commit 212efbd

Browse files
analogrelayCopilotCopilot
authored
Cosmos: Options Alignment Step 1 of 2 - Align Driver Options with spec (#4055)
This PR aligns the driver's options with the spec and does a bit of consolidation to prepare for replatforming the SDK. First, operation-specific options are broken into two categories: 1. Relates to operation execution, but account (driver) or application (runtime) defaults make sense. Think `read_consistency_strategy` 2. Relates to the execution of a **specific** operation. Think `session_token`. We ensure that everything in category 1 lives on `OperationOptions` (formerly `RuntimeOptions`) and everything in category 2 lives on `CosmosOperation` itself (the type describing the specific operation to be run). When executing an operation, the driver combines the `OperationOptions` value from the environment variables, runtime, driver, and the provided options into a single `OperationOptionsView` which handles layering. This PR _also_ removes a number of options that don't do anything right now. When these options types are exposed to the user through the SDK, I'm concerned that the presence of these options will confuse users (since they don't _work_). If people are concerned about that, I'm open to restoring them, but I don't think it's hard at all to recreate them _when_ we build support for the relative feature (like dedicated gateway options, etc.). I'd rather we track all that in an issue than with dead options code. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 5128111 commit 212efbd

18 files changed

Lines changed: 474 additions & 819 deletions

sdk/cosmos/azure_data_cosmos_driver/src/driver/cosmos_driver.rs

Lines changed: 67 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::{
1414
},
1515
options::{
1616
ConnectionPoolOptions, DiagnosticsOptions, DriverOptions, OperationOptions,
17-
RuntimeOptionsView, ThroughputControlGroupSnapshot,
17+
OperationOptionsView, ThroughputControlGroupSnapshot,
1818
},
1919
};
2020
use arc_swap::ArcSwap;
@@ -433,7 +433,7 @@ impl CosmosDriver {
433433
container_name: &str,
434434
) -> azure_core::Result<ContainerReference> {
435435
let db_ref = DatabaseReference::from_name(self.account().clone(), db_name.to_owned());
436-
let options = OperationOptions::new();
436+
let options = OperationOptions::default();
437437

438438
let db_result = self
439439
.execute_operation(
@@ -485,7 +485,7 @@ impl CosmosDriver {
485485
container_rid: &str,
486486
) -> azure_core::Result<ContainerReference> {
487487
let db_ref = DatabaseReference::from_rid(self.account().clone(), db_rid.to_owned());
488-
let options = OperationOptions::new();
488+
let options = OperationOptions::default();
489489

490490
let db_result = self
491491
.execute_operation(
@@ -555,12 +555,11 @@ impl CosmosDriver {
555555
});
556556

557557
// Resolve endpoint_unavailability_ttl from driver → runtime layers, then
558-
// fall back to env var. The env_options layer is skipped because this field
559-
// has no #[option(env)] annotation on RuntimeOptions.
558+
// fall back to env var.
560559
let endpoint_unavailability_ttl = options
561-
.runtime_options()
560+
.operation_options()
562561
.endpoint_unavailability_ttl
563-
.or(runtime.runtime_options().endpoint_unavailability_ttl)
562+
.or(runtime.operation_options().endpoint_unavailability_ttl)
564563
.unwrap_or_else(|| {
565564
std::env::var("AZURE_COSMOS_ENDPOINT_UNAVAILABLE_TTL_MS")
566565
.ok()
@@ -671,35 +670,35 @@ impl CosmosDriver {
671670
Ok(())
672671
}
673672

674-
/// Constructs a [`RuntimeOptionsView`] for resolving options across all layers.
673+
/// Constructs an [`OperationOptionsView`] for resolving options across all layers.
675674
///
676675
/// The view resolves options in priority order (highest first):
677676
/// 1. `OperationOptions` - operation-specific overrides
678677
/// 2. `DriverOptions` - driver-level defaults
679-
/// 3. `CosmosDriverRuntime` - global defaults
678+
/// 3. `CosmosDriverRuntime` - global runtime defaults
680679
/// 4. Environment - env vars read at startup
681-
pub fn runtime_options_view<'a>(
680+
pub fn operation_options_view<'a>(
682681
&self,
683682
operation_options: &'a OperationOptions,
684-
) -> RuntimeOptionsView<'a> {
685-
RuntimeOptionsView::new(
686-
Some(Arc::clone(self.runtime.env_options())),
687-
Some(self.runtime.runtime_options()),
688-
Some(self.options.runtime_options().clone()),
689-
Some(operation_options.runtime()),
683+
) -> OperationOptionsView<'a> {
684+
OperationOptionsView::new(
685+
Some(Arc::clone(self.runtime.env_operation_options())),
686+
Some(self.runtime.operation_options()),
687+
Some(self.options.operation_options().clone()),
688+
Some(operation_options),
690689
)
691690
}
692691

693692
/// Computes the effective throughput control group for an operation.
694693
///
695694
/// Resolution order (first match wins):
696-
/// 1. Explicit group name from the resolved runtime options + operation's container
695+
/// 1. Explicit group name from the resolved options + operation's container
697696
/// 2. Default group for the operation's container
698697
///
699698
/// Returns `None` if no applicable control group is found.
700699
pub(crate) fn effective_throughput_control_group(
701700
&self,
702-
effective_options: &RuntimeOptionsView<'_>,
701+
effective_options: &OperationOptionsView<'_>,
703702
container: &ContainerReference,
704703
) -> Option<ThroughputControlGroupSnapshot> {
705704
// First, check if an explicit group name is specified in options
@@ -743,7 +742,7 @@ impl CosmosDriver {
743742
///
744743
/// ```no_run
745744
/// use azure_data_cosmos_driver::driver::CosmosDriverRuntime;
746-
/// use azure_data_cosmos_driver::options::{OperationOptions, ContentResponseOnWrite};
745+
/// use azure_data_cosmos_driver::options::{OperationOptions, OperationOptionsBuilder, ContentResponseOnWrite};
747746
/// use azure_data_cosmos_driver::models::AccountReference;
748747
/// use url::Url;
749748
///
@@ -758,8 +757,9 @@ impl CosmosDriver {
758757
/// let driver = runtime.get_or_create_driver(account, None).await?;
759758
///
760759
/// // Execute operations with operation-specific options that override defaults
761-
/// let options = OperationOptions::new()
762-
/// .with_content_response_on_write(ContentResponseOnWrite::Disabled);
760+
/// let options = OperationOptionsBuilder::new()
761+
/// .with_content_response_on_write(ContentResponseOnWrite::Disabled)
762+
/// .build();
763763
///
764764
/// // let result = driver.execute_operation(operation, options).await?;
765765
/// # Ok(())
@@ -787,8 +787,8 @@ impl CosmosDriver {
787787
}
788788
tracing::debug!("operation started");
789789

790-
// Step 1: Build the runtime options view for layered resolution.
791-
let effective_options = self.runtime_options_view(&options);
790+
// Step 1: Build the single OperationOptionsView for layered resolution.
791+
let effective_options = self.operation_options_view(&options);
792792

793793
// Step 2: Resolve effective throughput control group (if any).
794794
// Step 1 transport pipeline does not consume this yet.
@@ -863,7 +863,6 @@ impl CosmosDriver {
863863
// Step 7: Execute via the new operation pipeline
864864
super::pipeline::operation_pipeline::execute_operation_pipeline(
865865
&operation,
866-
&options,
867866
&effective_options,
868867
self.location_state_store.as_ref(),
869868
&transport,
@@ -917,7 +916,7 @@ impl CosmosDriver {
917916
/// // Use the resolved container for item operations
918917
/// let item = ItemReference::from_name(&container, PartitionKey::from("pk1"), "doc1");
919918
/// let result = driver
920-
/// .execute_operation(CosmosOperation::read_item(item), OperationOptions::new())
919+
/// .execute_operation(CosmosOperation::read_item(item), OperationOptions::default())
921920
/// .await?;
922921
/// # Ok(())
923922
/// # }
@@ -998,7 +997,7 @@ mod tests {
998997
driver::CosmosDriverRuntimeBuilder,
999998
models::AccountReference,
1000999
options::{
1001-
ContentResponseOnWrite, CorrelationId, RuntimeOptionsBuilder, UserAgentSuffix,
1000+
ContentResponseOnWrite, CorrelationId, OperationOptionsBuilder, UserAgentSuffix,
10021001
WorkloadId,
10031002
},
10041003
};
@@ -1118,15 +1117,15 @@ mod tests {
11181117
}
11191118

11201119
#[tokio::test]
1121-
async fn default_runtime_options() {
1120+
async fn default_operation_options() {
11221121
let runtime = CosmosDriverRuntimeBuilder::new().build().await.unwrap();
11231122
assert!(runtime
1124-
.runtime_options()
1123+
.operation_options()
11251124
.throughput_control_group_name
11261125
.is_none());
11271126
assert!(runtime
1128-
.runtime_options()
1129-
.content_response_on_write
1127+
.operation_options()
1128+
.max_failover_retry_count
11301129
.is_none());
11311130
// user_agent is always available with base prefix
11321131
assert!(runtime
@@ -1140,20 +1139,20 @@ mod tests {
11401139
}
11411140

11421141
#[tokio::test]
1143-
async fn builder_sets_runtime_options() {
1144-
let opts = RuntimeOptionsBuilder::new()
1145-
.with_content_response_on_write(ContentResponseOnWrite::Disabled)
1142+
async fn builder_sets_operation_options() {
1143+
let opts = OperationOptionsBuilder::new()
1144+
.with_max_failover_retry_count(7)
11461145
.build();
11471146

11481147
let runtime = CosmosDriverRuntimeBuilder::new()
1149-
.with_runtime_options(opts)
1148+
.with_operation_options(opts)
11501149
.build()
11511150
.await
11521151
.unwrap();
11531152

11541153
assert_eq!(
1155-
runtime.runtime_options().content_response_on_write,
1156-
Some(ContentResponseOnWrite::Disabled)
1154+
runtime.operation_options().max_failover_retry_count,
1155+
Some(7)
11571156
);
11581157
}
11591158

@@ -1292,59 +1291,48 @@ mod tests {
12921291

12931292
// Initially none
12941293
assert!(runtime
1295-
.runtime_options()
1296-
.content_response_on_write
1294+
.operation_options()
1295+
.max_failover_retry_count
12971296
.is_none());
12981297

12991298
// Replace runtime options atomically
1300-
let new_opts = RuntimeOptionsBuilder::new()
1301-
.with_content_response_on_write(ContentResponseOnWrite::Enabled)
1299+
let new_opts = OperationOptionsBuilder::new()
1300+
.with_max_failover_retry_count(5)
13021301
.build();
1303-
runtime.set_runtime_options(new_opts);
1302+
runtime.set_operation_options(new_opts);
13041303

13051304
// Now set
13061305
assert_eq!(
1307-
runtime.runtime_options().content_response_on_write,
1308-
Some(ContentResponseOnWrite::Enabled)
1306+
runtime.operation_options().max_failover_retry_count,
1307+
Some(5)
13091308
);
13101309
}
13111310

13121311
#[tokio::test]
13131312
async fn effective_options_merge_priority() {
1314-
// Runtime has ENABLED
1315-
let cosmos_runtime = CosmosDriverRuntimeBuilder::new()
1316-
.with_runtime_options(
1317-
RuntimeOptionsBuilder::new()
1318-
.with_content_response_on_write(ContentResponseOnWrite::Enabled)
1319-
.build(),
1320-
)
1321-
.build()
1322-
.await
1323-
.unwrap();
1313+
// Build runtime (no operation options at runtime level yet)
1314+
let cosmos_runtime = CosmosDriverRuntimeBuilder::new().build().await.unwrap();
13241315

1325-
// Driver has DISABLED
1326-
let driver_options = DriverOptions::builder(test_account())
1327-
.with_runtime_options(
1328-
RuntimeOptionsBuilder::new()
1329-
.with_content_response_on_write(ContentResponseOnWrite::Disabled)
1330-
.build(),
1331-
)
1332-
.build();
1316+
// Driver has no operation options override either
1317+
let driver_options = DriverOptions::builder(test_account()).build();
13331318

13341319
let driver = CosmosDriver::new(cosmos_runtime, driver_options);
13351320

1336-
// Operation has no override - should get driver's DISABLED
1337-
let op_options = OperationOptions::new();
1338-
let view = driver.runtime_options_view(&op_options);
1321+
// Operation has DISABLED - should get DISABLED from operation options view
1322+
let op_options = OperationOptionsBuilder::new()
1323+
.with_content_response_on_write(ContentResponseOnWrite::Disabled)
1324+
.build();
1325+
let view = driver.operation_options_view(&op_options);
13391326
assert_eq!(
13401327
view.content_response_on_write(),
13411328
Some(&ContentResponseOnWrite::Disabled)
13421329
);
13431330

13441331
// Operation overrides to ENABLED - should get ENABLED
1345-
let op_options =
1346-
OperationOptions::new().with_content_response_on_write(ContentResponseOnWrite::Enabled);
1347-
let view = driver.runtime_options_view(&op_options);
1332+
let op_options = OperationOptionsBuilder::new()
1333+
.with_content_response_on_write(ContentResponseOnWrite::Enabled)
1334+
.build();
1335+
let view = driver.operation_options_view(&op_options);
13481336
assert_eq!(
13491337
view.content_response_on_write(),
13501338
Some(&ContentResponseOnWrite::Enabled)
@@ -1353,29 +1341,28 @@ mod tests {
13531341

13541342
#[tokio::test]
13551343
async fn effective_options_falls_back_to_runtime() {
1356-
// Runtime has ENABLED
1357-
let cosmos_runtime = CosmosDriverRuntimeBuilder::new()
1358-
.with_runtime_options(
1359-
RuntimeOptionsBuilder::new()
1360-
.with_content_response_on_write(ContentResponseOnWrite::Enabled)
1361-
.build(),
1362-
)
1363-
.build()
1364-
.await
1365-
.unwrap();
1344+
// Build runtime (env-level operation options are auto-loaded)
1345+
let cosmos_runtime = CosmosDriverRuntimeBuilder::new().build().await.unwrap();
13661346

13671347
// Driver has no override
13681348
let driver_options = DriverOptions::builder(test_account()).build();
13691349

13701350
let driver = CosmosDriver::new(cosmos_runtime, driver_options);
13711351

1372-
// Operation has no override - should fall back to runtime's ENABLED
1373-
let op_options = OperationOptions::new();
1374-
let view = driver.runtime_options_view(&op_options);
1352+
// Operation sets ENABLED - should get ENABLED from operation options view
1353+
let op_options = OperationOptionsBuilder::new()
1354+
.with_content_response_on_write(ContentResponseOnWrite::Enabled)
1355+
.build();
1356+
let view = driver.operation_options_view(&op_options);
13751357
assert_eq!(
13761358
view.content_response_on_write(),
13771359
Some(&ContentResponseOnWrite::Enabled)
13781360
);
1361+
1362+
// Operation has no override - env has no override - should be None
1363+
let op_options = OperationOptions::default();
1364+
let view = driver.operation_options_view(&op_options);
1365+
assert!(view.content_response_on_write().is_none());
13791366
}
13801367

13811368
#[test]

0 commit comments

Comments
 (0)