From 178b561c365163ea8a3166c373b486629e2b5a45 Mon Sep 17 00:00:00 2001 From: Mateusz Date: Thu, 21 May 2026 12:57:14 +0100 Subject: [PATCH] RSCBC-275: Allow for more control over dependencies --- .github/workflows/feature-check.yml | 38 ++++++++++ sdk/couchbase-connstr/Cargo.toml | 7 +- sdk/couchbase-connstr/src/error.rs | 9 ++- sdk/couchbase-connstr/src/lib.rs | 73 +++++++++++++++++-- sdk/couchbase-core/Cargo.toml | 1 - sdk/couchbase-core/src/helpers/durations.rs | 29 ++++---- sdk/couchbase-core/src/httpx/decoder.rs | 22 +++--- sdk/couchbase-core/src/lib.rs | 2 - sdk/couchbase-core/src/mgmtx/mgmt.rs | 30 ++++---- sdk/couchbase-core/src/retry.rs | 26 +++---- .../tests/common/test_config.rs | 7 +- sdk/couchbase/Cargo.toml | 12 +-- sdk/couchbase/src/clients/cluster_client.rs | 17 +++-- sdk/couchbase/src/lib.rs | 7 +- sdk/couchbase/src/options/cluster_options.rs | 6 ++ sdk/couchbase/tests/common/test_config.rs | 5 +- 16 files changed, 193 insertions(+), 98 deletions(-) create mode 100644 .github/workflows/feature-check.yml diff --git a/.github/workflows/feature-check.yml b/.github/workflows/feature-check.yml new file mode 100644 index 00000000..7a91af3b --- /dev/null +++ b/.github/workflows/feature-check.yml @@ -0,0 +1,38 @@ +on: + pull_request: + push: + branches: + - main +name: Feature combinations check + +jobs: + cargo_hack: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup Rust toolchain and cache + uses: actions-rust-lang/setup-rust-toolchain@v1 + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack + - name: Check all feature combinations (couchbase-connstr) + run: cargo hack check --feature-powerset --no-dev-deps -p couchbase-connstr + - name: Check all feature combinations (couchbase-core) + run: | + cargo hack check --feature-powerset --no-dev-deps \ + --exclude-features dhat-heap \ + --group-features rustls-tls,native-tls \ + --at-least-one-of rustls-tls,native-tls \ + -p couchbase-core + - name: Check all feature combinations (couchbase) + run: | + cargo hack check --feature-powerset --no-dev-deps \ + --exclude-features internal \ + --group-features rustls-tls,native-tls \ + --at-least-one-of rustls-tls,native-tls \ + -p couchbase + - name: Check minimal build (couchbase-connstr, no default features) + run: cargo check --no-default-features -p couchbase-connstr + - name: Check minimal build (couchbase-core, rustls-tls only) + run: cargo check --no-default-features --features rustls-tls -p couchbase-core + - name: Check minimal build (couchbase, rustls-tls only) + run: cargo check --no-default-features --features rustls-tls -p couchbase diff --git a/sdk/couchbase-connstr/Cargo.toml b/sdk/couchbase-connstr/Cargo.toml index b792013a..330802dc 100644 --- a/sdk/couchbase-connstr/Cargo.toml +++ b/sdk/couchbase-connstr/Cargo.toml @@ -14,10 +14,15 @@ authors = [ [dependencies] form_urlencoded = "1.2" -hickory-resolver = { version = "0.25.2", features = ["tokio", "system-config"], default-features = false } +hickory-resolver = { version = "0.25.2", features = ["tokio", "system-config"], default-features = false, optional = true } regex = "1.12" tracing = { version = "0.1.44", features = ["log"] } url = "2.5" [dev-dependencies] tokio = { version = "1.50", features = ["rt", "net", "macros"] } + +[features] +default = ["dns-srv"] +dns-srv = ["dep:hickory-resolver"] + diff --git a/sdk/couchbase-connstr/src/error.rs b/sdk/couchbase-connstr/src/error.rs index ae0a5dc0..a5059298 100644 --- a/sdk/couchbase-connstr/src/error.rs +++ b/sdk/couchbase-connstr/src/error.rs @@ -36,8 +36,12 @@ impl Error { #[non_exhaustive] pub enum ErrorKind { Parse(String), - InvalidArgument { msg: String, arg: String }, + InvalidArgument { + msg: String, + arg: String, + }, Io(io::Error), + #[cfg(feature = "dns-srv")] Resolve(hickory_resolver::ResolveError), } @@ -50,6 +54,7 @@ impl Clone for ErrorKind { arg: arg.clone(), }, ErrorKind::Io(e) => Self::Io(io::Error::from(e.kind())), + #[cfg(feature = "dns-srv")] ErrorKind::Resolve(e) => Self::Resolve(e.clone()), } } @@ -69,6 +74,7 @@ impl Display for ErrorKind { write!(f, "Invalid argument: {msg} ({arg})") } ErrorKind::Io(e) => write!(f, "IO error: {e}"), + #[cfg(feature = "dns-srv")] ErrorKind::Resolve(e) => write!(f, "Resolve error: {e}"), } } @@ -82,6 +88,7 @@ impl From for Error { } } +#[cfg(feature = "dns-srv")] impl From for Error { fn from(e: hickory_resolver::ResolveError) -> Self { Self { diff --git a/sdk/couchbase-connstr/src/lib.rs b/sdk/couchbase-connstr/src/lib.rs index 05593d7e..f6c75677 100644 --- a/sdk/couchbase-connstr/src/lib.rs +++ b/sdk/couchbase-connstr/src/lib.rs @@ -19,17 +19,25 @@ pub mod error; use error::ErrorKind; +#[cfg(feature = "dns-srv")] use hickory_resolver::config::*; +#[cfg(feature = "dns-srv")] use hickory_resolver::name_server::TokioConnectionProvider; +#[cfg(feature = "dns-srv")] use hickory_resolver::proto::xfer::Protocol; +#[cfg(feature = "dns-srv")] use hickory_resolver::system_conf::read_system_conf; +#[cfg(feature = "dns-srv")] use hickory_resolver::TokioResolver; use regex::Regex; use std::collections::HashMap; use std::fmt; use std::fmt::{Display, Formatter}; +#[cfg(feature = "dns-srv")] use std::net::SocketAddr; +#[cfg(feature = "dns-srv")] use std::time::Duration; +#[cfg(feature = "dns-srv")] use tracing::debug; use url::form_urlencoded; @@ -52,6 +60,7 @@ pub struct Address { pub port: u16, } +#[cfg(feature = "dns-srv")] #[derive(Debug, Clone, PartialEq)] pub struct DnsConfig { pub namespace: SocketAddr, @@ -81,6 +90,7 @@ pub struct SrvRecord { pub host: String, } +#[cfg(feature = "dns-srv")] impl ConnSpec { fn srv_record(&self) -> Option { if let Some(scheme_type) = &self.scheme { @@ -222,6 +232,7 @@ pub struct ResolvedConnSpec { pub options: HashMap>, } +#[cfg(feature = "dns-srv")] pub async fn resolve( conn_spec: ConnSpec, dns_config: impl Into>, @@ -275,6 +286,40 @@ pub async fn resolve( }; }; + resolve_without_srv(conn_spec, default_port, has_explicit_scheme, use_ssl) +} + +#[cfg(not(feature = "dns-srv"))] +pub async fn resolve(conn_spec: ConnSpec) -> error::Result { + let (default_port, has_explicit_scheme, use_ssl) = if let Some(scheme) = &conn_spec.scheme { + match scheme.as_str() { + "couchbase" => (DEFAULT_MEMD_PORT, true, false), + "couchbases" => (DEFAULT_SSL_MEMD_PORT, true, true), + "couchbase2" => { + return handle_couchbase2_scheme(conn_spec); + } + "" => (DEFAULT_MEMD_PORT, false, false), + _ => { + return Err(ErrorKind::InvalidArgument { + msg: "unrecognized scheme".to_string(), + arg: "scheme".to_string(), + } + .into()); + } + } + } else { + (DEFAULT_MEMD_PORT, false, false) + }; + + resolve_without_srv(conn_spec, default_port, has_explicit_scheme, use_ssl) +} + +fn resolve_without_srv( + conn_spec: ConnSpec, + default_port: u16, + has_explicit_scheme: bool, + use_ssl: bool, +) -> error::Result { if conn_spec.hosts.is_empty() { let (memd_port, http_port) = if use_ssl { (DEFAULT_SSL_MEMD_PORT, DEFAULT_LEGACY_HTTPS_PORT) @@ -386,6 +431,7 @@ fn handle_couchbase2_scheme(conn_spec: ConnSpec) -> error::Result bool { host.starts_with('[') || host.parse::().is_ok() } @@ -448,9 +495,22 @@ mod test { } async fn resolve_or_die(conn_spec: ConnSpec) -> ResolvedConnSpec { - resolve(conn_spec.clone(), None) + #[cfg(feature = "dns-srv")] + let result = resolve(conn_spec.clone(), None) .await - .unwrap_or_else(|e| panic!("Failed to resolve {conn_spec:?}: {e:?}")) + .unwrap_or_else(|e| panic!("Failed to resolve {conn_spec:?}: {e:?}")); + #[cfg(not(feature = "dns-srv"))] + let result = resolve(conn_spec.clone()) + .await + .unwrap_or_else(|e| panic!("Failed to resolve {conn_spec:?}: {e:?}")); + result + } + + async fn resolve_is_err(conn_spec: ConnSpec) -> bool { + #[cfg(feature = "dns-srv")] + return resolve(conn_spec, None).await.is_err(); + #[cfg(not(feature = "dns-srv"))] + return resolve(conn_spec).await.is_err(); } fn check_address_parsing( @@ -667,14 +727,11 @@ mod test { .await; let cs = parse_or_die("1.2.3.4:8091"); - assert!( - resolve(cs, None).await.is_err(), - "Expected error with http port" - ); + assert!(resolve_is_err(cs).await, "Expected error with http port"); let cs = parse_or_die("1.2.3.4:999"); assert!( - resolve(cs, None).await.is_err(), + resolve_is_err(cs).await, "Expected error with non-default port without scheme" ); } @@ -755,7 +812,7 @@ mod test { let cs = parse_or_die("couchbase://foo.com:8091"); assert!( - resolve(cs, None).await.is_err(), + resolve_is_err(cs).await, "Expected error for couchbase://XXX:8091" ); diff --git a/sdk/couchbase-core/Cargo.toml b/sdk/couchbase-core/Cargo.toml index 1cd679ff..eebce72f 100644 --- a/sdk/couchbase-core/Cargo.toml +++ b/sdk/couchbase-core/Cargo.toml @@ -24,7 +24,6 @@ crc32fast = "1.5" futures = "0.3.32" hmac = "0.12.1" http = "1.4" -lazy_static = "1.5" rand = "0.10.0" regex = "1.12" reqwest = { version = "0.13.2", features = ["json", "stream"], default-features = false } diff --git a/sdk/couchbase-core/src/helpers/durations.rs b/sdk/couchbase-core/src/helpers/durations.rs index d8e774c2..5820df60 100644 --- a/sdk/couchbase-core/src/helpers/durations.rs +++ b/sdk/couchbase-core/src/helpers/durations.rs @@ -18,24 +18,21 @@ use std::collections::HashMap; use std::ops::Add; +use std::sync::LazyLock; use std::time::Duration; -use lazy_static::lazy_static; - -lazy_static! { - static ref UNIT_MAP: HashMap<&'static str, u64> = { - let mut m = HashMap::new(); - m.insert("ns", 1); - m.insert("us", 1_000); - m.insert("µs", 1_000); - m.insert("μs", 1_000); - m.insert("ms", 1_000_000); - m.insert("s", 1_000_000_000); - m.insert("m", 60_000_000_000); - m.insert("h", 3_600_000_000_000); - m - }; -} +static UNIT_MAP: LazyLock> = LazyLock::new(|| { + let mut m = HashMap::new(); + m.insert("ns", 1); + m.insert("us", 1_000); + m.insert("µs", 1_000); + m.insert("μs", 1_000); + m.insert("ms", 1_000_000); + m.insert("s", 1_000_000_000); + m.insert("m", 60_000_000_000); + m.insert("h", 3_600_000_000_000); + m +}); // Note that this will not parse negative durations, rust Durations do not support negative values. pub fn parse_duration_from_golang_string(s: &str) -> Result { diff --git a/sdk/couchbase-core/src/httpx/decoder.rs b/sdk/couchbase-core/src/httpx/decoder.rs index 91e86faa..05d4cdc1 100644 --- a/sdk/couchbase-core/src/httpx/decoder.rs +++ b/sdk/couchbase-core/src/httpx/decoder.rs @@ -92,11 +92,11 @@ impl Decoder { self.scan.incr_bytes(-1); return Ok(scanp - self.scanp); } - ScanState::EndObject | ScanState::EndArray => { - if self.scan.step(b' ') == ScanState::End { - scanp += 1; - return Ok(scanp - self.scanp); - } + ScanState::EndObject | ScanState::EndArray + if self.scan.step(b' ') == ScanState::End => + { + scanp += 1; + return Ok(scanp - self.scanp); } ScanState::Error => { let scan_err = self.scan.err().expect("scan state error but no error set"); @@ -246,10 +246,6 @@ impl Decoder { } } - fn input_offset(&self) -> usize { - self.scanned + self.scanp - } - pub async fn token(&mut self) -> HttpxResult { loop { let c = match self.peek().await { @@ -354,10 +350,10 @@ impl Decoder { fn token_error(&self, c: u8) -> HttpxResult { let context = match self.token_state { - TokenState::TopValue => " looking for beginning of value", - TokenState::ArrayStart | TokenState::ArrayValue | TokenState::ObjectValue => { - " looking for beginning of value" - } + TokenState::TopValue + | TokenState::ArrayStart + | TokenState::ArrayValue + | TokenState::ObjectValue => " looking for beginning of value", TokenState::ArrayComma => " after array element", TokenState::ObjectKey => " looking for beginning of object key string", TokenState::ObjectColon => " after object key", diff --git a/sdk/couchbase-core/src/lib.rs b/sdk/couchbase-core/src/lib.rs index 39073a4e..63d4c61b 100644 --- a/sdk/couchbase-core/src/lib.rs +++ b/sdk/couchbase-core/src/lib.rs @@ -17,8 +17,6 @@ */ extern crate core; -#[macro_use] -extern crate lazy_static; pub mod address; pub mod agent; diff --git a/sdk/couchbase-core/src/mgmtx/mgmt.rs b/sdk/couchbase-core/src/mgmtx/mgmt.rs index b485d212..746733b0 100644 --- a/sdk/couchbase-core/src/mgmtx/mgmt.rs +++ b/sdk/couchbase-core/src/mgmtx/mgmt.rs @@ -34,24 +34,22 @@ use serde::de::DeserializeOwned; use serde::{Deserialize, Deserializer}; use serde_json::value::RawValue; use std::collections::HashMap; -use std::sync::Arc; +use std::sync::{Arc, LazyLock}; use std::time::Duration; -lazy_static! { - static ref FIELD_NAME_MAP: HashMap = { - HashMap::from([ - ( - "durability_min_level".to_string(), - "DurabilityMinLevel".to_string(), - ), - ("ramquota".to_string(), "RamQuotaMB".to_string()), - ("replicanumber".to_string(), "ReplicaNumber".to_string()), - ("maxttl".to_string(), "MaxTTL".to_string()), - ("history".to_string(), "HistoryEnabled".to_string()), - ("numvbuckets".to_string(), "numVBuckets".to_string()), - ]) - }; -} +static FIELD_NAME_MAP: LazyLock> = LazyLock::new(|| { + HashMap::from([ + ( + "durability_min_level".to_string(), + "DurabilityMinLevel".to_string(), + ), + ("ramquota".to_string(), "RamQuotaMB".to_string()), + ("replicanumber".to_string(), "ReplicaNumber".to_string()), + ("maxttl".to_string(), "MaxTTL".to_string()), + ("history".to_string(), "HistoryEnabled".to_string()), + ("numvbuckets".to_string(), "numVBuckets".to_string()), + ]) +}); #[derive(Debug)] pub struct Management { diff --git a/sdk/couchbase-core/src/retry.rs b/sdk/couchbase-core/src/retry.rs index f02435f2..f60cc137 100644 --- a/sdk/couchbase-core/src/retry.rs +++ b/sdk/couchbase-core/src/retry.rs @@ -19,7 +19,7 @@ use std::collections::HashSet; use std::fmt::{Debug, Display}; use std::future::Future; -use std::sync::Arc; +use std::sync::{Arc, LazyLock}; use std::time::Duration; use crate::errmapcomponent::ErrMapComponent; @@ -33,10 +33,8 @@ use async_trait::async_trait; use tokio::time::sleep; use tracing::{debug, info}; -lazy_static! { - pub(crate) static ref DEFAULT_RETRY_STRATEGY: Arc = - Arc::new(FailFastRetryStrategy::default()); -} +pub(crate) static DEFAULT_RETRY_STRATEGY: LazyLock> = + LazyLock::new(|| Arc::new(FailFastRetryStrategy::default())); /// The reason an operation is being retried. /// @@ -356,10 +354,8 @@ pub(crate) fn error_to_retry_reason( match err.kind() { Server(e) => return server_error_to_retry_reason(rs, e), Resource(e) => return server_error_to_retry_reason(rs, e.cause()), - Cancelled(e) => { - if e == &CancellationErrorKind::ClosedInFlight { - return Some(RetryReason::SocketClosedWhileInFlight); - } + Cancelled(e) if e == &CancellationErrorKind::ClosedInFlight => { + return Some(RetryReason::SocketClosedWhileInFlight); } Dispatch { .. } => return Some(RetryReason::SocketNotAvailable), _ => {} @@ -397,10 +393,8 @@ pub(crate) fn error_to_retry_reason( _ => {} }, ErrorKind::Search(e) => match e.kind() { - searchx::error::ErrorKind::Server(e) => { - if e.status_code() == 429 { - return Some(RetryReason::SearchTooManyRequests); - } + searchx::error::ErrorKind::Server(e) if e.status_code() == 429 => { + return Some(RetryReason::SearchTooManyRequests); } searchx::error::ErrorKind::Http { error, .. } => match error.kind() { httpx::error::ErrorKind::SendRequest(_) => { @@ -471,10 +465,8 @@ fn server_error_to_retry_reason(rs: &Arc, e: &ServerError) -> Opti ServerErrorKind::SyncWriteRecommitInProgress => { return Some(RetryReason::KvSyncWriteRecommitInProgress); } - ServerErrorKind::UnknownStatus { status } => { - if rs.err_map_component.should_retry(status) { - return Some(RetryReason::KvErrorMapRetryIndicated); - } + ServerErrorKind::UnknownStatus { status } if rs.err_map_component.should_retry(status) => { + return Some(RetryReason::KvErrorMapRetryIndicated); } _ => {} } diff --git a/sdk/couchbase-core/tests/common/test_config.rs b/sdk/couchbase-core/tests/common/test_config.rs index af9e1ef1..e0905522 100644 --- a/sdk/couchbase-core/tests/common/test_config.rs +++ b/sdk/couchbase-core/tests/common/test_config.rs @@ -24,7 +24,6 @@ use couchbase_core::address::Address; use couchbase_core::agent::Agent; use couchbase_core::options::waituntilready::WaitUntilReadyOptions; use envconfig::Envconfig; -use lazy_static::lazy_static; use log::LevelFilter; use std::env; use std::future::Future; @@ -38,10 +37,8 @@ use tokio::runtime::Runtime; use tokio::sync::RwLock; use tokio::time::{timeout_at, Instant}; -lazy_static! { - pub static ref TEST_AGENT: RwLock> = RwLock::new(None); - pub static ref LOGGER_INITIATED: AtomicBool = AtomicBool::new(false); -} +pub static TEST_AGENT: LazyLock>> = LazyLock::new(|| RwLock::new(None)); +pub static LOGGER_INITIATED: LazyLock = LazyLock::new(|| AtomicBool::new(false)); #[derive(Debug, Clone, Envconfig)] pub struct EnvTestConfig { diff --git a/sdk/couchbase/Cargo.toml b/sdk/couchbase/Cargo.toml index 4a59717e..dbb2a4d3 100644 --- a/sdk/couchbase/Cargo.toml +++ b/sdk/couchbase/Cargo.toml @@ -17,23 +17,22 @@ authors = [ bytes = "1.11" chrono = "0.4.44" futures = "0.3.32" -hdrhistogram = "7.5" +hdrhistogram = { version = "7.5", optional = true } http = "1.4" -lazy_static = "1.5" rustls-pki-types = { version = "1.14", optional = true } serde = "1.0" serde_json = { version = "1.0", features = ["preserve_order"] } uuid = { version = "1.22", features = ["v4"] } webpki-roots = "1.0" -couchbase-connstr = { path = "../couchbase-connstr", version = "1.0.1" } +couchbase-connstr = { path = "../couchbase-connstr", version = "1.0.1", default-features = false } couchbase-core = { path = "../couchbase-core", default-features = false, version = "1.0.1" } tokio = { version = "1.50" } tokio-native-tls = { version = "0.3.1", optional = true } tokio-rustls = { version = "0.26.4", optional = true } tracing = { version = "0.1.44", features = ["log"] } -tracing-subscriber = "0.3.22" +tracing-subscriber = { version = "0.3.22", optional = true } [dev-dependencies] criterion = { version = "0.8.2", features = ["html_reports", "async_tokio", "async"] } @@ -46,10 +45,13 @@ tokio = "1.50" tracing-subscriber = { version = "0.3.22", features = ["env-filter"] } [features] -default = ["default-tls"] +default = ["default-tls", "logging-meter", "threshold-logging-tracer", "dns-srv"] default-tls = ["rustls-tls"] rustls-tls = ["dep:tokio-rustls", "couchbase-core/rustls-tls", "dep:rustls-pki-types"] native-tls = ["dep:tokio-native-tls", "couchbase-core/native-tls"] +logging-meter = ["dep:hdrhistogram", "dep:tracing-subscriber"] +threshold-logging-tracer = ["dep:tracing-subscriber"] +dns-srv = ["couchbase-connstr/dns-srv"] # Note that we do not use feature flags to indicate API stability level. # Instead, unstable features are marked with comments indicating uncommitted or volatile stability levels. diff --git a/sdk/couchbase/src/clients/cluster_client.rs b/sdk/couchbase/src/clients/cluster_client.rs index e2956cba..348b482b 100644 --- a/sdk/couchbase/src/clients/cluster_client.rs +++ b/sdk/couchbase/src/clients/cluster_client.rs @@ -65,13 +65,16 @@ impl ClusterClient { ) -> error::Result { let conn_spec = parse(conn_str)?; - // This isn't ideal but dns options have to be a part of ClusterOptions, and we need to pull - // the dns options out for resolve. - // We could create a new type to pass into the backend connect functions but it just - // seems unnecessary. - let dns_options = take(&mut opts.dns_options).map(couchbase_connstr::DnsConfig::from); - - let resolved_conn_spec = resolve(conn_spec, dns_options).await?; + // When the dns-srv feature is enabled, DnsOptions lives on ClusterOptions and must be + // extracted here before conn_spec is consumed by resolve. The field and setter are both + // gated behind dns-srv, so this block is compiled out when the feature is disabled. + #[cfg(feature = "dns-srv")] + let resolved_conn_spec = { + let dns_options = take(&mut opts.dns_options).map(couchbase_connstr::DnsConfig::from); + resolve(conn_spec, dns_options).await? + }; + #[cfg(not(feature = "dns-srv"))] + let resolved_conn_spec = resolve(conn_spec).await?; let backend = if let Some(host) = resolved_conn_spec.couchbase2_host { ClusterClientBackend::Couchbase2ClusterBackend( diff --git a/sdk/couchbase/src/lib.rs b/sdk/couchbase/src/lib.rs index 1b34a21a..2b1491b9 100644 --- a/sdk/couchbase/src/lib.rs +++ b/sdk/couchbase/src/lib.rs @@ -239,8 +239,9 @@ //! | `default-tls` | ✅ | Alias for `rustls-tls` | //! | `rustls-tls` | ✅ | Use `rustls` for TLS (recommended) | //! | `native-tls` | ❌ | Use the platform's native TLS stack instead of `rustls` | -//! | `unstable-dns-options` | ❌ | Enable DNS-SRV bootstrap configuration (volatile) | -//! | `unstable-error-construction` | ❌ | Allow explicit `Error` construction (e.g. for mocking) | +//! | `logging-meter` | ✅ | Enable [`LoggingMeter`](logging_meter::LoggingMeter) (pulls in `hdrhistogram` and `tracing-subscriber`) | +//! | `threshold-logging-tracer` | ✅ | Enable [`ThresholdLoggingTracer`](threshold_logging_tracer::ThresholdLoggingTracer) (pulls in `tracing-subscriber`) | +//! | `dns-srv` | ✅ | Enable DNS-SRV bootstrap via `hickory-resolver` | //! //! Note that the SDK does not typically use feature flags for API stability levels. //! Instead, unstable features are commented with **uncommitted** or **volatile**. @@ -259,6 +260,7 @@ pub mod diagnostics; pub mod durability_level; pub mod error; mod error_context; +#[cfg(feature = "logging-meter")] pub mod logging_meter; pub mod management; pub mod mutation_state; @@ -269,6 +271,7 @@ pub mod scope; pub mod search; pub mod service_type; pub mod subdoc; +#[cfg(feature = "threshold-logging-tracer")] pub mod threshold_logging_tracer; mod tracing; pub mod transcoding; diff --git a/sdk/couchbase/src/options/cluster_options.rs b/sdk/couchbase/src/options/cluster_options.rs index f6e7a9c8..17793500 100644 --- a/sdk/couchbase/src/options/cluster_options.rs +++ b/sdk/couchbase/src/options/cluster_options.rs @@ -80,6 +80,7 @@ pub struct ClusterOptions { /// Configuration for the key-value (memcached) connections. pub kv_options: KvOptions, /// DNS configuration. **Volatile: This feature is subject to change at any time**. + #[cfg(feature = "dns-srv")] pub dns_options: Option, /// Configuration for the orphan response reporter. pub orphan_reporter_options: OrphanReporterOptions, @@ -114,6 +115,7 @@ impl ClusterOptions { poller_options: PollerOptions::new(), http_options: HttpOptions::new(), kv_options: KvOptions::new(), + #[cfg(feature = "dns-srv")] dns_options: None, orphan_reporter_options: OrphanReporterOptions::new(), default_retry_strategy: None, @@ -157,6 +159,7 @@ impl ClusterOptions { } /// Sets the DNS configuration. **Volatile: This feature is subject to change at any time**. + #[cfg(feature = "dns-srv")] pub fn dns_options(mut self, dns_options: DnsOptions) -> Self { self.dns_options = Some(dns_options); self @@ -679,6 +682,7 @@ impl Display for TlsOptions { } /// Custom DNS resolver configuration. **Volatile: This feature is subject to change at any time**. +#[cfg(feature = "dns-srv")] #[derive(Clone, Debug, PartialEq)] #[non_exhaustive] pub struct DnsOptions { @@ -688,6 +692,7 @@ pub struct DnsOptions { pub timeout: Option, } +#[cfg(feature = "dns-srv")] impl DnsOptions { /// Creates a new `DnsOptions` with the given DNS server address. pub fn new(namespace: SocketAddr) -> Self { @@ -703,6 +708,7 @@ impl DnsOptions { self } } +#[cfg(feature = "dns-srv")] impl From for couchbase_connstr::DnsConfig { fn from(opts: DnsOptions) -> Self { couchbase_connstr::DnsConfig { diff --git a/sdk/couchbase/tests/common/test_config.rs b/sdk/couchbase/tests/common/test_config.rs index 26ea5373..b33701e8 100644 --- a/sdk/couchbase/tests/common/test_config.rs +++ b/sdk/couchbase/tests/common/test_config.rs @@ -29,13 +29,10 @@ use couchbase::options::diagnostic_options::WaitUntilReadyOptions; use couchbase::service_type::ServiceType; use couchbase_connstr::ResolvedConnSpec; use envconfig::Envconfig; -use lazy_static::lazy_static; use tokio::runtime::Runtime; use tokio::sync::RwLock; -lazy_static! { - pub static ref TEST_CONFIG: RwLock> = RwLock::new(None); -} +pub static TEST_CONFIG: LazyLock>> = LazyLock::new(|| RwLock::new(None)); #[derive(Debug, Clone, Envconfig)] pub struct EnvTestConfig {