diff --git a/docs/source/connecting/compression.md b/docs/source/connecting/compression.md index 018c4cffc2..85b8a2c646 100644 --- a/docs/source/connecting/compression.md +++ b/docs/source/connecting/compression.md @@ -13,7 +13,7 @@ An example enabling `Snappy` compression algorithm: # extern crate scylla; # extern crate tokio; use scylla::{Session, SessionBuilder}; -use scylla::transport::Compression; +use scylla::connection::Compression; use std::error::Error; #[tokio::main] diff --git a/docs/source/data-types/counter.md b/docs/source/data-types/counter.md index 0f31b6cba7..654155ad4e 100644 --- a/docs/source/data-types/counter.md +++ b/docs/source/data-types/counter.md @@ -8,7 +8,7 @@ # use std::error::Error; # async fn check_only_compiles(session: &Session) -> Result<(), Box> { use scylla::IntoTypedRows; -use scylla::frame::value::Counter; +use scylla::cql::value::Counter; // Read counter from the table if let Some(rows) = session.query("SELECT c FROM keyspace.table", &[]).await?.rows { diff --git a/docs/source/data-types/date.md b/docs/source/data-types/date.md index 6d3384c6af..23ec76be5c 100644 --- a/docs/source/data-types/date.md +++ b/docs/source/data-types/date.md @@ -1,6 +1,6 @@ # Date -For most use cases `Date` can be represented as +For most use cases `Date` can be represented as [`chrono::NaiveDate`](https://docs.rs/chrono/0.4.19/chrono/naive/struct.NaiveDate.html).\ `NaiveDate` supports dates from -262145-1-1 to 262143-12-31. @@ -40,11 +40,10 @@ Internally `Date` is represented as number of days since -5877641-06-23 i.e. 2^3 # use scylla::Session; # use std::error::Error; # async fn check_only_compiles(session: &Session) -> Result<(), Box> { -use scylla::frame::value::Date; -use scylla::frame::response::result::CqlValue; +use scylla::cql::value::{CqlValue, Date}; // Insert date using raw u32 representation -let to_insert: Date = Date(2_u32.pow(31)); // 1970-01-01 +let to_insert: Date = Date(2_u32.pow(31)); // 1970-01-01 session .query("INSERT INTO keyspace.table (a) VALUES(?)", (to_insert,)) .await?; diff --git a/docs/source/data-types/duration.md b/docs/source/data-types/duration.md index 7526a478b3..9e473db60c 100644 --- a/docs/source/data-types/duration.md +++ b/docs/source/data-types/duration.md @@ -1,5 +1,5 @@ # Duration -`Duration` is represented as [`CqlDuration`](https://docs.rs/scylla/latest/scylla/frame/value/struct.CqlDuration.html)\ +`Duration` is represented as [`CqlDuration`](https://docs.rs/scylla/latest/scylla/cql/value/struct.CqlDuration.html)\ ```rust # extern crate scylla; @@ -7,7 +7,7 @@ # use std::error::Error; # async fn check_only_compiles(session: &Session) -> Result<(), Box> { use scylla::IntoTypedRows; -use scylla::frame::value::CqlDuration; +use scylla::cql::value::CqlDuration; // Insert some ip address into the table let to_insert: CqlDuration = CqlDuration { months: 1, days: 2, nanoseconds: 3 }; diff --git a/docs/source/data-types/time.md b/docs/source/data-types/time.md index 6f46f9dae1..f7a73f4f6a 100644 --- a/docs/source/data-types/time.md +++ b/docs/source/data-types/time.md @@ -1,7 +1,7 @@ # Time `Time` is represented as [`chrono::Duration`](https://docs.rs/chrono/0.4.19/chrono/struct.Duration.html) -Internally `Time` is represented as number of nanoseconds since midnight. +Internally `Time` is represented as number of nanoseconds since midnight. It can't be negative or exceed `86399999999999` (24 hours). When sending in a query it needs to be wrapped in `value::Time` to differentiate from [`Timestamp`](timestamp.md) @@ -13,7 +13,7 @@ When sending in a query it needs to be wrapped in `value::Time` to differentiate # use std::error::Error; # async fn check_only_compiles(session: &Session) -> Result<(), Box> { use scylla::IntoTypedRows; -use scylla::frame::value::Time; +use scylla::cql::value::Time; use chrono::Duration; // Insert some time into the table diff --git a/docs/source/data-types/timestamp.md b/docs/source/data-types/timestamp.md index d61aec2aec..5361aaec63 100644 --- a/docs/source/data-types/timestamp.md +++ b/docs/source/data-types/timestamp.md @@ -13,7 +13,7 @@ When sending in a query it needs to be wrapped in `value::Timestamp` to differen # use std::error::Error; # async fn check_only_compiles(session: &Session) -> Result<(), Box> { use scylla::IntoTypedRows; -use scylla::frame::value::Timestamp; +use scylla::cql::value::Timestamp; use chrono::Duration; // Insert some timestamp into the table diff --git a/docs/source/execution-profiles/create-and-use.md b/docs/source/execution-profiles/create-and-use.md index 01bfa52d99..01dcd6c3c8 100644 --- a/docs/source/execution-profiles/create-and-use.md +++ b/docs/source/execution-profiles/create-and-use.md @@ -8,7 +8,7 @@ To create an `ExecutionProfile` and attach it as default for `Session`: # async fn check_only_compiles() -> Result<(), Box> { use scylla::{Session, SessionBuilder}; use scylla::statement::Consistency; -use scylla::transport::ExecutionProfile; +use scylla::execution::ExecutionProfile; let profile = ExecutionProfile::builder() .consistency(Consistency::LocalOne) @@ -34,7 +34,7 @@ To create an `ExecutionProfile` and attach it to a `Query`: # async fn check_only_compiles() -> Result<(), Box> { use scylla::query::Query; use scylla::statement::Consistency; -use scylla::transport::ExecutionProfile; +use scylla::execution::ExecutionProfile; use std::time::Duration; let profile = ExecutionProfile::builder() @@ -60,7 +60,7 @@ To create an `ExecutionProfile` based on another profile: # use std::error::Error; # async fn check_only_compiles() -> Result<(), Box> { use scylla::statement::Consistency; -use scylla::transport::ExecutionProfile; +use scylla::execution::ExecutionProfile; use std::time::Duration; let base_profile = ExecutionProfile::builder() diff --git a/docs/source/execution-profiles/maximal-example.md b/docs/source/execution-profiles/maximal-example.md index 8209b926a1..0c5a50c930 100644 --- a/docs/source/execution-profiles/maximal-example.md +++ b/docs/source/execution-profiles/maximal-example.md @@ -9,9 +9,9 @@ use scylla::query::Query; use scylla::speculative_execution::SimpleSpeculativeExecutionPolicy; use scylla::statement::{Consistency, SerialConsistency}; -use scylla::transport::ExecutionProfile; -use scylla::transport::load_balancing::DefaultPolicy; -use scylla::transport::retry_policy::FallthroughRetryPolicy; +use scylla::execution::ExecutionProfile; +use scylla::execution::load_balancing::DefaultPolicy; +use scylla::execution::retries::FallthroughRetryPolicy; use std::{sync::Arc, time::Duration}; let profile = ExecutionProfile::builder() diff --git a/docs/source/execution-profiles/priority.md b/docs/source/execution-profiles/priority.md index 92a46d50c4..25bd0f7656 100644 --- a/docs/source/execution-profiles/priority.md +++ b/docs/source/execution-profiles/priority.md @@ -16,7 +16,7 @@ Priorities of execution profiles and directly set options: use scylla::{Session, SessionBuilder}; use scylla::query::Query; use scylla::statement::Consistency; -use scylla::transport::ExecutionProfile; +use scylla::execution::ExecutionProfile; let session_profile = ExecutionProfile::builder() .consistency(Consistency::One) diff --git a/docs/source/execution-profiles/remap.md b/docs/source/execution-profiles/remap.md index a64aee3916..308cb2b483 100644 --- a/docs/source/execution-profiles/remap.md +++ b/docs/source/execution-profiles/remap.md @@ -26,7 +26,7 @@ Below, the remaps described above are followed in code. use scylla::{Session, SessionBuilder}; use scylla::query::Query; use scylla::statement::Consistency; -use scylla::transport::ExecutionProfile; +use scylla::execution::ExecutionProfile; let profile1 = ExecutionProfile::builder() .consistency(Consistency::One) diff --git a/docs/source/load-balancing/load-balancing.md b/docs/source/load-balancing/load-balancing.md index cf5c699baf..db34a37439 100644 --- a/docs/source/load-balancing/load-balancing.md +++ b/docs/source/load-balancing/load-balancing.md @@ -9,7 +9,7 @@ balancing plan based on the query information and the state of the cluster. Load balancing policies do not influence to which nodes connections are being opened. For a node connection blacklist configuration refer to -`scylla::transport::host_filter::HostFilter`, which can be set session-wide +`scylla::cluster::host_filter::HostFilter`, which can be set session-wide using `SessionBuilder::host_filter` method. ## Plan @@ -49,8 +49,8 @@ The newly created execution profile is then converted to a handle using # async fn check_only_compiles(uri: &str) -> Result<(), Box> { use scylla::SessionBuilder; use scylla::load_balancing::DefaultPolicy; -use scylla::transport::ExecutionProfile; -use scylla::transport::session::Session; +use scylla::execution::ExecutionProfile; +use scylla::session::Session; use std::sync::Arc; let policy = Arc::new(DefaultPolicy::default()); diff --git a/docs/source/queries/result.md b/docs/source/queries/result.md index 6350eab9ad..80a215d8bc 100644 --- a/docs/source/queries/result.md +++ b/docs/source/queries/result.md @@ -89,7 +89,7 @@ session.query("INSERT INTO ks.tab (a) VALUES (0)", &[]).await?.result_not_rows() For more see [`QueryResult`](https://docs.rs/scylla/latest/scylla/transport/query_result/struct.QueryResult.html) ### `NULL` values -`NULL` values will return an error when parsed as a Rust type. +`NULL` values will return an error when parsed as a Rust type. To properly handle `NULL` values parse column as an `Option<>`: ```rust # extern crate scylla; @@ -123,7 +123,7 @@ Field names don't need to match column names. # async fn check_only_compiles(session: &Session) -> Result<(), Box> { use scylla::IntoTypedRows; use scylla::macros::FromRow; -use scylla::frame::response::cql_to_rust::FromRow; +use scylla::cql::cql_to_rust::FromRow; #[derive(FromRow)] struct MyRow { diff --git a/docs/source/queries/timeouts.md b/docs/source/queries/timeouts.md index 92c11dfd6a..bfbe87d8a6 100644 --- a/docs/source/queries/timeouts.md +++ b/docs/source/queries/timeouts.md @@ -18,7 +18,7 @@ However, setting per-statement timeout to `None` results in falling back to per- # use std::error::Error; # async fn timeouts() -> Result<(), Box> { use scylla::{Session, SessionBuilder, query::Query}; -use scylla::transport::ExecutionProfile; +use scylla::execution::ExecutionProfile; use std::time::Duration; let uri = std::env::var("SCYLLA_URI") diff --git a/docs/source/queries/values.md b/docs/source/queries/values.md index 09b369b689..7f5206ca12 100644 --- a/docs/source/queries/values.md +++ b/docs/source/queries/values.md @@ -1,7 +1,7 @@ # Query values Query text is constant, but the values might change. You can pass changing values to a query by specifying a list of variables as bound values.\ -Each `?` in query text will be filled with the matching value. +Each `?` in query text will be filled with the matching value. > **Never** pass values by adding strings, this could lead to [SQL Injection](https://en.wikipedia.org/wiki/SQL_injection) @@ -12,7 +12,7 @@ or a custom struct which derives from `ValueList`. A few examples: ```rust # extern crate scylla; -# use scylla::{Session, ValueList, frame::response::result::CqlValue}; +# use scylla::{Session, ValueList, cql::value::CqlValue}; # use std::error::Error; # use std::collections::HashMap; # async fn check_only_compiles(session: &Session) -> Result<(), Box> { @@ -94,7 +94,7 @@ Using `Unset` results in better performance: # use scylla::Session; # use std::error::Error; # async fn check_only_compiles(session: &Session) -> Result<(), Box> { -use scylla::frame::value::{MaybeUnset, Unset}; +use scylla::cql::value::{MaybeUnset, Unset}; // Inserting a null results in suboptimal performance let null_i32: Option = None; diff --git a/docs/source/retry-policy/default.md b/docs/source/retry-policy/default.md index e1f8514ed4..b4b2835c01 100644 --- a/docs/source/retry-policy/default.md +++ b/docs/source/retry-policy/default.md @@ -11,8 +11,8 @@ To use in `Session`: # use std::error::Error; # async fn check_only_compiles() -> Result<(), Box> { use scylla::{Session, SessionBuilder}; -use scylla::transport::ExecutionProfile; -use scylla::transport::retry_policy::DefaultRetryPolicy; +use scylla::execution::ExecutionProfile; +use scylla::execution::retries::DefaultRetryPolicy; let handle = ExecutionProfile::builder() .retry_policy(Box::new(DefaultRetryPolicy::new())) @@ -36,8 +36,8 @@ To use in a [simple query](../queries/simple.md): # use std::sync::Arc; # async fn check_only_compiles(session: &Session) -> Result<(), Box> { use scylla::query::Query; -use scylla::transport::ExecutionProfile; -use scylla::transport::retry_policy::DefaultRetryPolicy; +use scylla::execution::ExecutionProfile; +use scylla::execution::retries::DefaultRetryPolicy; // Create a Query manually and set the retry policy let mut my_query: Query = Query::new("INSERT INTO ks.tab (a) VALUES(?)"); @@ -65,8 +65,8 @@ To use in a [prepared query](../queries/prepared.md): # use std::sync::Arc; # async fn check_only_compiles(session: &Session) -> Result<(), Box> { use scylla::prepared_statement::PreparedStatement; -use scylla::transport::ExecutionProfile; -use scylla::transport::retry_policy::DefaultRetryPolicy; +use scylla::execution::ExecutionProfile; +use scylla::execution::retries::DefaultRetryPolicy; // Create PreparedStatement manually and set the retry policy let mut prepared: PreparedStatement = session diff --git a/docs/source/retry-policy/downgrading-consistency.md b/docs/source/retry-policy/downgrading-consistency.md index 711329ec80..cd2c1edbce 100644 --- a/docs/source/retry-policy/downgrading-consistency.md +++ b/docs/source/retry-policy/downgrading-consistency.md @@ -52,8 +52,8 @@ To use in `Session`: # use std::error::Error; # async fn check_only_compiles() -> Result<(), Box> { use scylla::{Session, SessionBuilder}; -use scylla::transport::ExecutionProfile; -use scylla::transport::downgrading_consistency_retry_policy::DowngradingConsistencyRetryPolicy; +use scylla::execution::ExecutionProfile; +use scylla::execution::retries::DowngradingConsistencyRetryPolicy; let handle = ExecutionProfile::builder() .retry_policy(Box::new(DowngradingConsistencyRetryPolicy::new())) @@ -76,8 +76,8 @@ To use in a [simple query](../queries/simple.md): # use std::error::Error; # async fn check_only_compiles(session: &Session) -> Result<(), Box> { use scylla::query::Query; -use scylla::transport::ExecutionProfile; -use scylla::transport::downgrading_consistency_retry_policy::DowngradingConsistencyRetryPolicy; +use scylla::execution::ExecutionProfile; +use scylla::execution::retries::DowngradingConsistencyRetryPolicy; let handle = ExecutionProfile::builder() .retry_policy(Box::new(DowngradingConsistencyRetryPolicy::new())) @@ -102,8 +102,8 @@ To use in a [prepared query](../queries/prepared.md): # use std::error::Error; # async fn check_only_compiles(session: &Session) -> Result<(), Box> { use scylla::prepared_statement::PreparedStatement; -use scylla::transport::ExecutionProfile; -use scylla::transport::downgrading_consistency_retry_policy::DowngradingConsistencyRetryPolicy; +use scylla::execution::ExecutionProfile; +use scylla::execution::retries::DowngradingConsistencyRetryPolicy; let handle = ExecutionProfile::builder() .retry_policy(Box::new(DowngradingConsistencyRetryPolicy::new())) diff --git a/docs/source/retry-policy/fallthrough.md b/docs/source/retry-policy/fallthrough.md index 0f6ab59388..8e7e2b4379 100644 --- a/docs/source/retry-policy/fallthrough.md +++ b/docs/source/retry-policy/fallthrough.md @@ -10,8 +10,8 @@ To use in `Session`: # use std::error::Error; # async fn check_only_compiles() -> Result<(), Box> { use scylla::{Session, SessionBuilder}; -use scylla::transport::ExecutionProfile; -use scylla::transport::retry_policy::FallthroughRetryPolicy; +use scylla::execution::ExecutionProfile; +use scylla::execution::retries::FallthroughRetryPolicy; let handle = ExecutionProfile::builder() .retry_policy(Box::new(FallthroughRetryPolicy::new())) @@ -34,8 +34,8 @@ To use in a [simple query](../queries/simple.md): # use std::error::Error; # async fn check_only_compiles(session: &Session) -> Result<(), Box> { use scylla::query::Query; -use scylla::transport::ExecutionProfile; -use scylla::transport::retry_policy::FallthroughRetryPolicy; +use scylla::execution::ExecutionProfile; +use scylla::execution::retries::FallthroughRetryPolicy; let handle = ExecutionProfile::builder() .retry_policy(Box::new(FallthroughRetryPolicy::new())) @@ -60,8 +60,8 @@ To use in a [prepared query](../queries/prepared.md): # use std::error::Error; # async fn check_only_compiles(session: &Session) -> Result<(), Box> { use scylla::prepared_statement::PreparedStatement; -use scylla::transport::ExecutionProfile; -use scylla::transport::retry_policy::FallthroughRetryPolicy; +use scylla::execution::ExecutionProfile; +use scylla::execution::retries::FallthroughRetryPolicy; let handle = ExecutionProfile::builder() .retry_policy(Box::new(FallthroughRetryPolicy::new())) diff --git a/docs/source/speculative-execution/percentile.md b/docs/source/speculative-execution/percentile.md index 243429d97f..9082b50f11 100644 --- a/docs/source/speculative-execution/percentile.md +++ b/docs/source/speculative-execution/percentile.md @@ -17,7 +17,7 @@ use scylla::{ Session, SessionBuilder, speculative_execution::PercentileSpeculativeExecutionPolicy, - transport::execution_profile::ExecutionProfile, + execution::ExecutionProfile, }; let policy = PercentileSpeculativeExecutionPolicy { diff --git a/docs/source/speculative-execution/simple.md b/docs/source/speculative-execution/simple.md index ad7720e664..ce2bec7b9b 100644 --- a/docs/source/speculative-execution/simple.md +++ b/docs/source/speculative-execution/simple.md @@ -17,7 +17,7 @@ use scylla::{ Session, SessionBuilder, speculative_execution::SimpleSpeculativeExecutionPolicy, - transport::execution_profile::ExecutionProfile, + execution::ExecutionProfile, }; let policy = SimpleSpeculativeExecutionPolicy { diff --git a/docs/source/tracing/basic.md b/docs/source/tracing/basic.md index 4ee5bc5737..c131fa5601 100644 --- a/docs/source/tracing/basic.md +++ b/docs/source/tracing/basic.md @@ -12,7 +12,7 @@ return a `QueryResult` which contains a `tracing_id` if tracing was enabled. # async fn check_only_compiles(session: &Session) -> Result<(), Box> { use scylla::query::Query; use scylla::QueryResult; -use scylla::tracing::TracingInfo; +use scylla::execution::tracing::TracingInfo; use uuid::Uuid; // Create a Query manually and enable tracing @@ -40,7 +40,7 @@ if let Some(id) = tracing_id { # async fn check_only_compiles(session: &Session) -> Result<(), Box> { use scylla::prepared_statement::PreparedStatement; use scylla::QueryResult; -use scylla::tracing::TracingInfo; +use scylla::execution::tracing::TracingInfo; use uuid::Uuid; // Prepare the query @@ -72,7 +72,7 @@ if let Some(id) = tracing_id { # async fn check_only_compiles(session: &Session) -> Result<(), Box> { use scylla::batch::Batch; use scylla::QueryResult; -use scylla::tracing::TracingInfo; +use scylla::execution::tracing::TracingInfo; use uuid::Uuid; // Create a batch statement diff --git a/docs/source/tracing/paged.md b/docs/source/tracing/paged.md index e69d4f3361..aec6b024b3 100644 --- a/docs/source/tracing/paged.md +++ b/docs/source/tracing/paged.md @@ -13,8 +13,8 @@ If tracing is enabled the row iterator will contain a list of tracing ids for al # use std::error::Error; # async fn check_only_compiles(session: &Session) -> Result<(), Box> { use scylla::query::Query; -use scylla::transport::iterator::RowIterator; -use scylla::tracing::TracingInfo; +use scylla::execution::iterator::RowIterator; +use scylla::execution::tracing::TracingInfo; use futures::StreamExt; use uuid::Uuid; @@ -49,8 +49,8 @@ for id in tracing_ids { # use std::error::Error; # async fn check_only_compiles(session: &Session) -> Result<(), Box> { use scylla::prepared_statement::PreparedStatement; -use scylla::transport::iterator::RowIterator; -use scylla::tracing::TracingInfo; +use scylla::execution::iterator::RowIterator; +use scylla::execution::tracing::TracingInfo; use futures::StreamExt; use uuid::Uuid; diff --git a/docs/source/tracing/prepare.md b/docs/source/tracing/prepare.md index 2f3850cbde..cef6a48d1c 100644 --- a/docs/source/tracing/prepare.md +++ b/docs/source/tracing/prepare.md @@ -10,7 +10,7 @@ # async fn check_only_compiles(session: &Session) -> Result<(), Box> { use scylla::query::Query; use scylla::prepared_statement::PreparedStatement; -use scylla::tracing::TracingInfo; +use scylla::execution::tracing::TracingInfo; use uuid::Uuid; // Prepare the query with tracing enabled diff --git a/docs/source/tracing/query-history.md b/docs/source/tracing/query-history.md index 1c0779259e..fd4cd086ff 100644 --- a/docs/source/tracing/query-history.md +++ b/docs/source/tracing/query-history.md @@ -12,7 +12,7 @@ This history includes all requests sent, decisions to retry and speculative exec # use std::error::Error; # async fn check_only_compiles(session: &Session) -> Result<(), Box> { use scylla::query::Query; -use scylla::history::{HistoryCollector, StructuredHistory}; +use scylla::execution::history::{HistoryCollector, StructuredHistory}; use std::sync::Arc; // Create a query for which we would like to trace the history of its execution diff --git a/examples/basic.rs b/examples/basic.rs index 01fb4848b0..848153fdfa 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -1,7 +1,7 @@ use anyhow::Result; use scylla::macros::FromRow; -use scylla::transport::session::{IntoTypedRows, Session}; use scylla::SessionBuilder; +use scylla::{session::Session, IntoTypedRows}; use std::env; #[tokio::main] diff --git a/examples/compare-tokens.rs b/examples/compare-tokens.rs index 5c56aa5f4d..a42572e560 100644 --- a/examples/compare-tokens.rs +++ b/examples/compare-tokens.rs @@ -1,7 +1,7 @@ use anyhow::Result; -use scylla::frame::value::ValueList; +use scylla::cluster::NodeAddr; +use scylla::cql::ValueList; use scylla::routing::Token; -use scylla::transport::NodeAddr; use scylla::{Session, SessionBuilder}; use std::env; diff --git a/examples/cql-time-types.rs b/examples/cql-time-types.rs index ac4da4831e..db5bbadc28 100644 --- a/examples/cql-time-types.rs +++ b/examples/cql-time-types.rs @@ -3,10 +3,9 @@ use anyhow::Result; use chrono::{Duration, NaiveDate}; -use scylla::frame::response::result::CqlValue; -use scylla::frame::value::{Date, Time, Timestamp}; -use scylla::transport::session::{IntoTypedRows, Session}; +use scylla::cql::value::{CqlValue, Date, Time, Timestamp}; use scylla::SessionBuilder; +use scylla::{IntoTypedRows, Session}; use std::env; #[tokio::main] diff --git a/examples/cqlsh-rs.rs b/examples/cqlsh-rs.rs index 877b4af596..fc7ceaebbf 100644 --- a/examples/cqlsh-rs.rs +++ b/examples/cqlsh-rs.rs @@ -3,7 +3,7 @@ use rustyline::completion::{Completer, Pair}; use rustyline::error::ReadlineError; use rustyline::{CompletionType, Config, Context, Editor}; use rustyline_derive::{Helper, Highlighter, Hinter, Validator}; -use scylla::transport::Compression; +use scylla::connection::Compression; use scylla::{QueryResult, Session, SessionBuilder}; use std::env; diff --git a/examples/custom_deserialization.rs b/examples/custom_deserialization.rs index 122c842bb8..5aa6ac66ed 100644 --- a/examples/custom_deserialization.rs +++ b/examples/custom_deserialization.rs @@ -1,6 +1,6 @@ use anyhow::Result; +use scylla::cql::value::CqlValue; use scylla::cql_to_rust::{FromCqlVal, FromCqlValError}; -use scylla::frame::response::result::CqlValue; use scylla::macros::impl_from_cql_value_from_method; use scylla::{Session, SessionBuilder}; use std::env; diff --git a/examples/custom_load_balancing_policy.rs b/examples/custom_load_balancing_policy.rs index c4b2d32002..2e33cd106d 100644 --- a/examples/custom_load_balancing_policy.rs +++ b/examples/custom_load_balancing_policy.rs @@ -1,7 +1,8 @@ use anyhow::Result; use scylla::{ + cluster::ClusterData, + execution::ExecutionProfile, load_balancing::{LoadBalancingPolicy, RoutingInfo}, - transport::{ClusterData, ExecutionProfile}, Session, SessionBuilder, }; use std::{env, sync::Arc}; @@ -17,7 +18,7 @@ impl LoadBalancingPolicy for CustomLoadBalancingPolicy { &'a self, _info: &'a RoutingInfo, cluster: &'a ClusterData, - ) -> Option> { + ) -> Option> { self.fallback(_info, cluster).next() } diff --git a/examples/execution_profile.rs b/examples/execution_profile.rs index d8d1b1eed9..3473357f5e 100644 --- a/examples/execution_profile.rs +++ b/examples/execution_profile.rs @@ -1,11 +1,11 @@ use anyhow::Result; +use scylla::execution::retries::{DefaultRetryPolicy, FallthroughRetryPolicy}; +use scylla::execution::ExecutionProfile; use scylla::load_balancing; use scylla::query::Query; -use scylla::retry_policy::{DefaultRetryPolicy, FallthroughRetryPolicy}; +use scylla::session::Session; use scylla::speculative_execution::PercentileSpeculativeExecutionPolicy; use scylla::statement::{Consistency, SerialConsistency}; -use scylla::transport::session::Session; -use scylla::transport::ExecutionProfile; use scylla::{SessionBuilder, SessionConfig}; use std::env; use std::sync::Arc; diff --git a/examples/get_by_name.rs b/examples/get_by_name.rs index 0b94b1e898..fdc47f2f9b 100644 --- a/examples/get_by_name.rs +++ b/examples/get_by_name.rs @@ -1,5 +1,5 @@ use anyhow::{anyhow, Result}; -use scylla::transport::session::Session; +use scylla::session::Session; use scylla::SessionBuilder; use std::env; diff --git a/examples/logging.rs b/examples/logging.rs index 0ee57340c7..6e0c2bb4a0 100644 --- a/examples/logging.rs +++ b/examples/logging.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use scylla::transport::session::Session; +use scylla::session::Session; use scylla::SessionBuilder; use std::env; use tracing::info; diff --git a/examples/query_history.rs b/examples/query_history.rs index f95001c2e3..3df429ba1f 100644 --- a/examples/query_history.rs +++ b/examples/query_history.rs @@ -2,9 +2,9 @@ use anyhow::Result; use futures::StreamExt; -use scylla::history::{HistoryCollector, StructuredHistory}; +use scylla::execution::history::{HistoryCollector, StructuredHistory}; use scylla::query::Query; -use scylla::transport::session::Session; +use scylla::session::Session; use scylla::SessionBuilder; use std::env; use std::sync::Arc; diff --git a/examples/schema_agreement.rs b/examples/schema_agreement.rs index 08e5a59384..cfa17f451e 100644 --- a/examples/schema_agreement.rs +++ b/examples/schema_agreement.rs @@ -1,7 +1,7 @@ use anyhow::{bail, Result}; -use scylla::transport::errors::QueryError; -use scylla::transport::session::{IntoTypedRows, Session}; +use scylla::execution::errors::QueryError; use scylla::SessionBuilder; +use scylla::{IntoTypedRows, Session}; use std::env; use std::time::Duration; diff --git a/examples/speculative-execution.rs b/examples/speculative-execution.rs index 4f0b3445f9..c7a35743fa 100644 --- a/examples/speculative-execution.rs +++ b/examples/speculative-execution.rs @@ -1,6 +1,6 @@ use scylla::{ - speculative_execution::PercentileSpeculativeExecutionPolicy, - transport::execution_profile::ExecutionProfile, Session, SessionBuilder, + execution::ExecutionProfile, speculative_execution::PercentileSpeculativeExecutionPolicy, + Session, SessionBuilder, }; use anyhow::Result; diff --git a/examples/tls.rs b/examples/tls.rs index 49006c1602..b7e0c1b7b7 100644 --- a/examples/tls.rs +++ b/examples/tls.rs @@ -1,6 +1,6 @@ use anyhow::Result; -use scylla::transport::session::{IntoTypedRows, Session}; use scylla::SessionBuilder; +use scylla::{IntoTypedRows, Session}; use std::env; use std::fs; use std::path::PathBuf; diff --git a/examples/tower.rs b/examples/tower.rs index 1c3bb2112a..f93a70655c 100644 --- a/examples/tower.rs +++ b/examples/tower.rs @@ -13,7 +13,7 @@ struct SessionService { // A trivial service implementation for sending parameterless simple string requests to Scylla. impl Service for SessionService { type Response = scylla::QueryResult; - type Error = scylla::transport::errors::QueryError; + type Error = scylla::execution::errors::QueryError; type Future = Pin>>>; fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { @@ -44,11 +44,9 @@ async fn main() -> anyhow::Result<()> { .call("SELECT keyspace_name, table_name FROM system_schema.tables;".into()) .await?; - let print_text = |t: &Option| { + let print_text = |t: &Option| { t.as_ref() - .unwrap_or(&scylla::frame::response::result::CqlValue::Text( - "".to_string(), - )) + .unwrap_or(&scylla::cql::value::CqlValue::Text("".to_string())) .as_text() .unwrap_or(&"".to_string()) .clone() diff --git a/examples/tracing.rs b/examples/tracing.rs index bff0bca94a..192fa6453d 100644 --- a/examples/tracing.rs +++ b/examples/tracing.rs @@ -4,11 +4,11 @@ use anyhow::{anyhow, Result}; use futures::StreamExt; use scylla::batch::Batch; +use scylla::execution::iterator::RowIterator; +use scylla::execution::tracing::TracingInfo; use scylla::statement::{ prepared_statement::PreparedStatement, query::Query, Consistency, SerialConsistency, }; -use scylla::tracing::TracingInfo; -use scylla::transport::iterator::RowIterator; use scylla::QueryResult; use scylla::{Session, SessionBuilder}; use std::env; diff --git a/examples/value_list.rs b/examples/value_list.rs index 44b388dcbc..9fbef876ac 100644 --- a/examples/value_list.rs +++ b/examples/value_list.rs @@ -37,7 +37,7 @@ async fn main() { // You can also use type generics: #[derive(scylla::ValueList)] - struct MyTypeWithGenerics { + struct MyTypeWithGenerics { k: i32, my: Option, } diff --git a/scylla-cql/benches/benchmark.rs b/scylla-cql/benches/benchmark.rs index 0aa6c89102..8789cd7a5c 100644 --- a/scylla-cql/benches/benchmark.rs +++ b/scylla-cql/benches/benchmark.rs @@ -1,8 +1,10 @@ use std::borrow::Cow; +use bytes::BytesMut; use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; use scylla_cql::frame::request::SerializableRequest; +use scylla_cql::frame::types; use scylla_cql::frame::value::SerializedValues; use scylla_cql::frame::value::ValueList; use scylla_cql::frame::{request::query, Compression, SerializedRequest}; @@ -51,5 +53,37 @@ fn serialized_request_make_bench(c: &mut Criterion) { } } -criterion_group!(benches, serialized_request_make_bench); +fn types_benchmark(c: &mut Criterion) { + let mut buf = BytesMut::with_capacity(64); + c.bench_function("short", |b| { + b.iter(|| { + buf.clear(); + types::write_short(-1, &mut buf); + types::read_short(&mut &buf[..]).unwrap(); + }) + }); + c.bench_function("int", |b| { + b.iter(|| { + buf.clear(); + types::write_int(-1, &mut buf); + types::read_int(&mut &buf[..]).unwrap(); + }) + }); + c.bench_function("long", |b| { + b.iter(|| { + buf.clear(); + types::write_long(-1, &mut buf); + types::read_long(&mut &buf[..]).unwrap(); + }) + }); + c.bench_function("string", |b| { + b.iter(|| { + buf.clear(); + types::write_string("hello, world", &mut buf).unwrap(); + types::read_string(&mut &buf[..]).unwrap(); + }) + }); +} + +criterion_group!(benches, serialized_request_make_bench, types_benchmark); criterion_main!(benches); diff --git a/scylla-cql/src/errors.rs b/scylla-cql/src/errors.rs index 40587cfef6..f9452f9f6d 100644 --- a/scylla-cql/src/errors.rs +++ b/scylla-cql/src/errors.rs @@ -1,56 +1,11 @@ -//! This module contains various errors which can be returned by `scylla::Session` +//! This module contains various errors which can be returned +//! while working with CQL frames. -use crate::frame::frame_errors::{FrameError, ParseError}; use crate::frame::protocol_features::ProtocolFeatures; -use crate::frame::value::SerializeValuesError; use crate::Consistency; use bytes::Bytes; -use std::io::ErrorKind; -use std::sync::Arc; use thiserror::Error; -/// Error that occurred during query execution -#[derive(Error, Debug, Clone)] -pub enum QueryError { - /// Database sent a response containing some error with a message - #[error("Database returned an error: {0}, Error message: {1}")] - DbError(DbError, String), - - /// Caller passed an invalid query - #[error(transparent)] - BadQuery(#[from] BadQuery), - - /// Input/Output error has occurred, connection broken etc. - #[error("IO Error: {0}")] - IoError(Arc), - - /// Unexpected message received - #[error("Protocol Error: {0}")] - ProtocolError(&'static str), - - /// Invalid message received - #[error("Invalid message: {0}")] - InvalidMessage(String), - - /// Timeout error has occurred, function didn't complete in time. - #[error("Timeout Error")] - TimeoutError, - - #[error("Too many orphaned stream ids: {0}")] - TooManyOrphanedStreamIds(u16), - - #[error("Unable to allocate stream id")] - UnableToAllocStreamId, - - /// Client timeout occurred before any response arrived - #[error("Request timeout: {0}")] - RequestTimeout(String), - - /// Address translation failed - #[error("Address translation failed: {0}")] - TranslationError(#[from] TranslationError), -} - /// An error sent from the database in response to a query /// as described in the [specification](https://github.com/apache/cassandra/blob/5ed5e84613ef0e9664a774493db7d2604e3596e0/doc/native_protocol_v4.spec#L1029)\ #[derive(Error, Debug, Clone, PartialEq, Eq)] @@ -291,15 +246,6 @@ impl DbError { } } -/// Error caused by failed address translation done before establishing connection -#[derive(Debug, Copy, Clone, Error)] -pub enum TranslationError { - #[error("No rule for address")] - NoRuleForAddress, - #[error("Invalid address in rule")] - InvalidAddressInRule, -} - /// Type of the operation rejected by rate limiting #[derive(Debug, Clone, PartialEq, Eq)] pub enum OperationType { @@ -332,179 +278,12 @@ pub enum WriteType { Other(String), } -/// Error caused by caller creating an invalid query -#[derive(Error, Debug, Clone)] -#[error("Invalid query passed to Session")] -pub enum BadQuery { - /// Failed to serialize values passed to a query - values too big - #[error("Serializing values failed: {0} ")] - SerializeValuesError(#[from] SerializeValuesError), - - /// Serialized values are too long to compute partition key - #[error("Serialized values are too long to compute partition key! Length: {0}, Max allowed length: {1}")] - ValuesTooLongForKey(usize, usize), - - /// Passed invalid keyspace name to use - #[error("Passed invalid keyspace name to use: {0}")] - BadKeyspaceName(#[from] BadKeyspaceName), - - /// Other reasons of bad query - #[error("{0}")] - Other(String), -} - -/// Error that occurred during session creation -#[derive(Error, Debug, Clone)] -pub enum NewSessionError { - /// Failed to resolve hostname passed in Session creation - #[error("Couldn't resolve any hostname: {0:?}")] - FailedToResolveAnyHostname(Vec), - - /// List of known nodes passed to Session constructor is empty - /// There needs to be at least one node to connect to - #[error("Empty known nodes list")] - EmptyKnownNodesList, - - /// Database sent a response containing some error with a message - #[error("Database returned an error: {0}, Error message: {1}")] - DbError(DbError, String), - - /// Caller passed an invalid query - #[error(transparent)] - BadQuery(#[from] BadQuery), - - /// Input/Output error has occurred, connection broken etc. - #[error("IO Error: {0}")] - IoError(Arc), - - /// Unexpected message received - #[error("Protocol Error: {0}")] - ProtocolError(&'static str), - - /// Invalid message received - #[error("Invalid message: {0}")] - InvalidMessage(String), - - /// Timeout error has occurred, couldn't connect to node in time. - #[error("Timeout Error")] - TimeoutError, - - #[error("Too many orphaned stream ids: {0}")] - TooManyOrphanedStreamIds(u16), - - #[error("Unable to allocate stream id")] - UnableToAllocStreamId, - - /// Client timeout occurred before a response arrived for some query - /// during `Session` creation. - #[error("Client timeout: {0}")] - RequestTimeout(String), - - /// Address translation failed - #[error("Address translation failed: {0}")] - TranslationError(#[from] TranslationError), -} - -/// Invalid keyspace name given to `Session::use_keyspace()` -#[derive(Debug, Error, Clone)] -pub enum BadKeyspaceName { - /// Keyspace name is empty - #[error("Keyspace name is empty")] - Empty, - - /// Keyspace name too long, must be up to 48 characters - #[error("Keyspace name too long, must be up to 48 characters, found {1} characters. Bad keyspace name: '{0}'")] - TooLong(String, usize), - - /// Illegal character - only alphanumeric and underscores allowed. - #[error("Illegal character found: '{1}', only alphanumeric and underscores allowed. Bad keyspace name: '{0}'")] - IllegalCharacter(String, char), -} - impl std::fmt::Display for WriteType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:?}", self) } } -impl From for QueryError { - fn from(io_error: std::io::Error) -> QueryError { - QueryError::IoError(Arc::new(io_error)) - } -} - -impl From for QueryError { - fn from(serialized_err: SerializeValuesError) -> QueryError { - QueryError::BadQuery(BadQuery::SerializeValuesError(serialized_err)) - } -} - -impl From for QueryError { - fn from(parse_error: ParseError) -> QueryError { - QueryError::InvalidMessage(format!("Error parsing message: {}", parse_error)) - } -} - -impl From for QueryError { - fn from(frame_error: FrameError) -> QueryError { - QueryError::InvalidMessage(format!("Frame error: {}", frame_error)) - } -} - -impl From for QueryError { - fn from(timer_error: tokio::time::error::Elapsed) -> QueryError { - QueryError::RequestTimeout(format!("{}", timer_error)) - } -} - -impl From for NewSessionError { - fn from(io_error: std::io::Error) -> NewSessionError { - NewSessionError::IoError(Arc::new(io_error)) - } -} - -impl From for NewSessionError { - fn from(query_error: QueryError) -> NewSessionError { - match query_error { - QueryError::DbError(e, msg) => NewSessionError::DbError(e, msg), - QueryError::BadQuery(e) => NewSessionError::BadQuery(e), - QueryError::IoError(e) => NewSessionError::IoError(e), - QueryError::ProtocolError(m) => NewSessionError::ProtocolError(m), - QueryError::InvalidMessage(m) => NewSessionError::InvalidMessage(m), - QueryError::TimeoutError => NewSessionError::TimeoutError, - QueryError::TooManyOrphanedStreamIds(ids) => { - NewSessionError::TooManyOrphanedStreamIds(ids) - } - QueryError::UnableToAllocStreamId => NewSessionError::UnableToAllocStreamId, - QueryError::RequestTimeout(msg) => NewSessionError::RequestTimeout(msg), - QueryError::TranslationError(e) => NewSessionError::TranslationError(e), - } - } -} - -impl From for QueryError { - fn from(keyspace_err: BadKeyspaceName) -> QueryError { - QueryError::BadQuery(BadQuery::BadKeyspaceName(keyspace_err)) - } -} - -impl QueryError { - /// Checks if this error indicates that a chosen source port/address cannot be bound. - /// This is caused by one of the following: - /// - The source address is already used by another socket, - /// - The source address is reserved and the process does not have sufficient privileges to use it. - pub fn is_address_unavailable_for_use(&self) -> bool { - if let QueryError::IoError(io_error) = self { - match io_error.kind() { - ErrorKind::AddrInUse | ErrorKind::PermissionDenied => return true, - _ => {} - } - } - - false - } -} - impl From for OperationType { fn from(operation_type: u8) -> OperationType { match operation_type { @@ -549,8 +328,7 @@ impl WriteType { #[cfg(test)] mod tests { - use super::{DbError, QueryError, WriteType}; - use crate::frame::types::Consistency; + use super::WriteType; #[test] fn write_type_from_str() { @@ -571,38 +349,4 @@ mod tests { assert_eq!(write_type, *expected_write_type); } } - - // A test to check that displaying DbError and QueryError::DbError works as expected - // - displays error description - // - displays error parameters - // - displays error message - // - indented multiline strings don't cause whitespace gaps - #[test] - fn dberror_full_info() { - // Test that DbError::Unavailable is displayed correctly - let db_error = DbError::Unavailable { - consistency: Consistency::Three, - required: 3, - alive: 2, - }; - - let db_error_displayed: String = format!("{}", db_error); - - let mut expected_dberr_msg = - "Not enough nodes are alive to satisfy required consistency level ".to_string(); - expected_dberr_msg += "(consistency: Three, required: 3, alive: 2)"; - - assert_eq!(db_error_displayed, expected_dberr_msg); - - // Test that QueryError::DbError::(DbError::Unavailable) is displayed correctly - let query_error = - QueryError::DbError(db_error, "a message about unavailable error".to_string()); - let query_error_displayed: String = format!("{}", query_error); - - let mut expected_querr_msg = "Database returned an error: ".to_string(); - expected_querr_msg += &expected_dberr_msg; - expected_querr_msg += ", Error message: a message about unavailable error"; - - assert_eq!(query_error_displayed, expected_querr_msg); - } } diff --git a/scylla-cql/src/frame/response/error.rs b/scylla-cql/src/frame/response/error.rs index e99d5686d5..95b1f875d0 100644 --- a/scylla-cql/src/frame/response/error.rs +++ b/scylla-cql/src/frame/response/error.rs @@ -1,4 +1,4 @@ -use crate::errors::{DbError, OperationType, QueryError, WriteType}; +use crate::errors::{DbError, OperationType, WriteType}; use crate::frame::frame_errors::ParseError; use crate::frame::protocol_features::ProtocolFeatures; use crate::frame::types; @@ -81,12 +81,6 @@ impl Error { } } -impl From for QueryError { - fn from(error: Error) -> QueryError { - QueryError::DbError(error.error, error.reason) - } -} - #[cfg(test)] mod tests { use super::Error; diff --git a/scylla-cql/src/frame/response/mod.rs b/scylla-cql/src/frame/response/mod.rs index c8c4ec104d..923707d67e 100644 --- a/scylla-cql/src/frame/response/mod.rs +++ b/scylla-cql/src/frame/response/mod.rs @@ -5,7 +5,7 @@ pub mod event; pub mod result; pub mod supported; -use crate::{errors::QueryError, frame::frame_errors::ParseError}; +use crate::frame::frame_errors::ParseError; use num_enum::TryFromPrimitive; use crate::frame::protocol_features::ProtocolFeatures; @@ -63,9 +63,9 @@ impl Response { Ok(response) } - pub fn into_non_error_response(self) -> Result { + pub fn into_non_error_response(self) -> Result { Ok(match self { - Response::Error(err) => return Err(QueryError::from(err)), + Response::Error(err) => return Err(err), Response::Ready => NonErrorResponse::Ready, Response::Result(res) => NonErrorResponse::Result(res), Response::Authenticate(auth) => NonErrorResponse::Authenticate(auth), diff --git a/scylla/benches/benchmark.rs b/scylla/benches/benchmark.rs index c6058923b6..2f61726d90 100644 --- a/scylla/benches/benchmark.rs +++ b/scylla/benches/benchmark.rs @@ -1,44 +1,10 @@ use criterion::{criterion_group, criterion_main, Criterion}; -use bytes::BytesMut; use scylla::{ - frame::types, - frame::value::ValueList, - transport::partitioner::{calculate_token_for_partition_key, Murmur3Partitioner}, + cql::ValueList, + routing::partitioner::{calculate_token_for_partition_key, Murmur3Partitioner}, }; -fn types_benchmark(c: &mut Criterion) { - let mut buf = BytesMut::with_capacity(64); - c.bench_function("short", |b| { - b.iter(|| { - buf.clear(); - types::write_short(-1, &mut buf); - types::read_short(&mut &buf[..]).unwrap(); - }) - }); - c.bench_function("int", |b| { - b.iter(|| { - buf.clear(); - types::write_int(-1, &mut buf); - types::read_int(&mut &buf[..]).unwrap(); - }) - }); - c.bench_function("long", |b| { - b.iter(|| { - buf.clear(); - types::write_long(-1, &mut buf); - types::read_long(&mut &buf[..]).unwrap(); - }) - }); - c.bench_function("string", |b| { - b.iter(|| { - buf.clear(); - types::write_string("hello, world", &mut buf).unwrap(); - types::read_string(&mut &buf[..]).unwrap(); - }) - }); -} - fn calculate_token_bench(c: &mut Criterion) { let simple_pk = ("I'm prepared!!!",); let serialized_simple_pk = simple_pk.serialized().unwrap().into_owned(); @@ -91,5 +57,5 @@ fn calculate_token_bench(c: &mut Criterion) { ); } -criterion_group!(benches, types_benchmark, calculate_token_bench); +criterion_group!(benches, calculate_token_bench); criterion_main!(benches); diff --git a/scylla/src/cloud/mod.rs b/scylla/src/cloud/mod.rs index 2b9bf23aae..11943bae9f 100644 --- a/scylla/src/cloud/mod.rs +++ b/scylla/src/cloud/mod.rs @@ -11,7 +11,7 @@ use openssl::{ use tracing::warn; use uuid::Uuid; -use crate::transport::connection::{ConnectionConfig, SslConfig}; +use crate::connection::{ConnectionConfig, SslConfig}; pub(crate) fn set_ssl_config_for_scylla_cloud_host( host_id: Option, diff --git a/scylla/src/transport/cluster.rs b/scylla/src/cluster/cluster.rs similarity index 97% rename from scylla/src/transport/cluster.rs rename to scylla/src/cluster/cluster.rs index 503d14519d..90411660e9 100644 --- a/scylla/src/transport/cluster.rs +++ b/scylla/src/cluster/cluster.rs @@ -1,23 +1,18 @@ +use crate::cluster::host_filter::HostFilter; +use crate::cluster::Node; +use crate::connection::{Connection, PoolConfig, VerifiedKeyspaceName}; +use crate::execution::errors::QueryError; /// Cluster manages up to date information and connections to database nodes use crate::frame::response::event::{Event, StatusChangeEvent}; use crate::frame::value::ValueList; use crate::prepared_statement::TokenCalculationError; -use crate::routing::Token; -use crate::transport::host_filter::HostFilter; -use crate::transport::{ - connection::{Connection, VerifiedKeyspaceName}, - connection_pool::PoolConfig, - errors::QueryError, - node::Node, - partitioner::PartitionerName, - topology::{Keyspace, Metadata, MetadataReader}, -}; +use crate::routing::{partitioner::PartitionerName, Token}; +use crate::execution::errors::{BadQuery, NewSessionError}; use arc_swap::ArcSwap; use futures::future::join_all; use futures::{future::RemoteHandle, FutureExt}; use itertools::Itertools; -use scylla_cql::errors::{BadQuery, NewSessionError}; use std::collections::HashMap; use std::net::SocketAddr; use std::sync::Arc; @@ -26,11 +21,11 @@ use tracing::instrument::WithSubscriber; use tracing::{debug, warn}; use uuid::Uuid; -use super::node::{KnownNode, NodeAddr}; +use crate::cluster::locator::ReplicaLocator; +use crate::cluster::metadata::{Keyspace, Metadata, MetadataReader, Strategy}; -use super::locator::ReplicaLocator; -use super::partitioner::calculate_token_for_partition_key; -use super::topology::Strategy; +use crate::cluster::{KnownNode, NodeAddr}; +use crate::routing::partitioner::calculate_token_for_partition_key; /// Cluster manages up to date information and connections to database nodes. /// All data can be accessed by cloning Arc in the `data` field diff --git a/scylla/src/transport/host_filter.rs b/scylla/src/cluster/host_filter.rs similarity index 86% rename from scylla/src/transport/host_filter.rs rename to scylla/src/cluster/host_filter.rs index d59f90a49a..b224924726 100644 --- a/scylla/src/transport/host_filter.rs +++ b/scylla/src/cluster/host_filter.rs @@ -1,15 +1,16 @@ //! Host filters. //! //! Host filters are essentially just a predicate over -//! [`Peer`](crate::transport::topology::Peer)s. Currently, they are used -//! by the [`Session`](crate::transport::session::Session) to determine whether +//! [`Peer`](crate::cluster::metadata::Peer)s. Currently, they are used +//! by the [`Session`](crate::session::Session) to determine whether //! connections should be opened to a given node or not. use std::collections::HashSet; use std::io::Error; use std::net::{SocketAddr, ToSocketAddrs}; -use crate::transport::topology::Peer; +use crate::cluster::metadata::Peer; +use crate::cluster::NodeAddr; /// The `HostFilter` trait. pub trait HostFilter: Send + Sync { @@ -55,11 +56,11 @@ impl AllowListHostFilter { impl HostFilter for AllowListHostFilter { fn accept(&self, peer: &Peer) -> bool { match peer.address { - super::NodeAddr::Translatable(addr) => self.allowed.contains(&addr), + NodeAddr::Translatable(addr) => self.allowed.contains(&addr), // If the address is Untranslatable, then the node either was originally // an ContactPoint, or a Translatable node for which the host filter // returned true. - super::NodeAddr::Untranslatable(_) => true, + NodeAddr::Untranslatable(_) => true, } } } diff --git a/scylla/src/transport/locator/mod.rs b/scylla/src/cluster/locator/mod.rs similarity index 99% rename from scylla/src/transport/locator/mod.rs rename to scylla/src/cluster/locator/mod.rs index db55b9fe69..996af07110 100644 --- a/scylla/src/transport/locator/mod.rs +++ b/scylla/src/cluster/locator/mod.rs @@ -8,7 +8,8 @@ mod token_ring; use rand::{seq::IteratorRandom, Rng}; pub use token_ring::TokenRing; -use super::{topology::Strategy, Node, NodeRef}; +use crate::cluster::metadata::Strategy; +use crate::cluster::{Node, NodeRef}; use crate::routing::Token; use itertools::Itertools; use precomputed_replicas::PrecomputedReplicas; @@ -740,7 +741,7 @@ impl<'a> IntoIterator for ReplicasOrdered<'a> { #[cfg(test)] mod tests { - use crate::{routing::Token, transport::locator::test::*}; + use crate::{cluster::locator::test::*, routing::Token}; #[tokio::test] async fn test_replicas_ordered() { diff --git a/scylla/src/transport/locator/precomputed_replicas.rs b/scylla/src/cluster/locator/precomputed_replicas.rs similarity index 98% rename from scylla/src/transport/locator/precomputed_replicas.rs rename to scylla/src/cluster/locator/precomputed_replicas.rs index de6d5e1a63..9493496219 100644 --- a/scylla/src/transport/locator/precomputed_replicas.rs +++ b/scylla/src/cluster/locator/precomputed_replicas.rs @@ -14,9 +14,9 @@ use super::replication_info::ReplicationInfo; use super::TokenRing; +use crate::cluster::metadata::Strategy; +use crate::cluster::Node; use crate::routing::Token; -use crate::transport::node::Node; -use crate::transport::topology::Strategy; use std::cmp; use std::collections::BTreeSet; @@ -215,11 +215,11 @@ mod tests { use std::collections::HashMap; use crate::{ - routing::Token, - transport::{ - locator::test::{create_ring, mock_metadata_for_token_aware_tests, A, C, D, E, F, G}, - topology::{Keyspace, Strategy}, + cluster::locator::test::{ + create_ring, mock_metadata_for_token_aware_tests, A, C, D, E, F, G, }, + cluster::metadata::{Keyspace, Strategy}, + routing::Token, }; use super::{PrecomputedReplicas, ReplicationInfo}; diff --git a/scylla/src/transport/locator/replicas.rs b/scylla/src/cluster/locator/replicas.rs similarity index 97% rename from scylla/src/transport/locator/replicas.rs rename to scylla/src/cluster/locator/replicas.rs index 30df602695..9cae8496a9 100644 --- a/scylla/src/transport/locator/replicas.rs +++ b/scylla/src/cluster/locator/replicas.rs @@ -1,6 +1,6 @@ use itertools::Either; -use crate::transport::{Node, NodeRef}; +use crate::cluster::{Node, NodeRef}; use std::{ops::Index, sync::Arc}; /// Container holding replicas, enabling unified use of both borrowed and owned `Node` sequences. diff --git a/scylla/src/transport/locator/replication_info.rs b/scylla/src/cluster/locator/replication_info.rs similarity index 99% rename from scylla/src/transport/locator/replication_info.rs rename to scylla/src/cluster/locator/replication_info.rs index d17ed361ba..52cddc2799 100644 --- a/scylla/src/transport/locator/replication_info.rs +++ b/scylla/src/cluster/locator/replication_info.rs @@ -1,8 +1,8 @@ use itertools::Itertools; use super::TokenRing; +use crate::cluster::Node; use crate::routing::Token; -use crate::transport::node::Node; use std::cmp; use std::collections::{BTreeSet, HashMap}; @@ -204,10 +204,10 @@ where #[cfg(test)] mod tests { use crate::{ - routing::Token, - transport::locator::test::{ + cluster::locator::test::{ create_ring, mock_metadata_for_token_aware_tests, A, B, C, D, E, F, G, }, + routing::Token, }; use super::ReplicationInfo; diff --git a/scylla/src/transport/locator/test.rs b/scylla/src/cluster/locator/test.rs similarity index 99% rename from scylla/src/transport/locator/test.rs rename to scylla/src/cluster/locator/test.rs index d09a22d7c1..f56acb7a3c 100644 --- a/scylla/src/transport/locator/test.rs +++ b/scylla/src/cluster/locator/test.rs @@ -3,13 +3,10 @@ use rand_chacha::ChaCha8Rng; use uuid::Uuid; use super::{ReplicaLocator, ReplicaSet}; +use crate::cluster::metadata::{Keyspace, Metadata, Peer, Strategy}; +use crate::cluster::{Node, NodeAddr, NodeRef}; +use crate::connection::PoolConfig; use crate::routing::Token; -use crate::transport::{ - connection_pool::PoolConfig, - topology::{Keyspace, Metadata, Peer, Strategy}, - Node, -}; -use crate::transport::{NodeAddr, NodeRef}; use std::collections::HashSet; use std::sync::Arc; diff --git a/scylla/src/transport/locator/token_ring.rs b/scylla/src/cluster/locator/token_ring.rs similarity index 100% rename from scylla/src/transport/locator/token_ring.rs rename to scylla/src/cluster/locator/token_ring.rs diff --git a/scylla/src/transport/topology.rs b/scylla/src/cluster/metadata.rs similarity index 99% rename from scylla/src/transport/topology.rs rename to scylla/src/cluster/metadata.rs index 63ee14f5b2..177c8c0372 100644 --- a/scylla/src/transport/topology.rs +++ b/scylla/src/cluster/metadata.rs @@ -1,19 +1,18 @@ +use crate::cluster::host_filter::HostFilter; +use crate::cluster::resolve_contact_points; +use crate::connection::{Connection, ConnectionConfig, NodeConnectionPool, PoolConfig, PoolSize}; +use crate::execution::errors::{DbError, QueryError}; use crate::frame::response::event::Event; use crate::routing::Token; use crate::statement::query::Query; -use crate::transport::connection::{Connection, ConnectionConfig}; -use crate::transport::connection_pool::{NodeConnectionPool, PoolConfig, PoolSize}; -use crate::transport::errors::{DbError, QueryError}; -use crate::transport::host_filter::HostFilter; -use crate::transport::node::resolve_contact_points; use crate::utils::parse::{ParseErrorCause, ParseResult, ParserState}; +use crate::execution::errors::NewSessionError; use futures::future::{self, FutureExt}; use futures::stream::{self, StreamExt, TryStreamExt}; use futures::Stream; use rand::seq::SliceRandom; use rand::{thread_rng, Rng}; -use scylla_cql::errors::NewSessionError; use scylla_cql::frame::response::result::Row; use scylla_cql::frame::value::ValueList; use scylla_macros::FromRow; @@ -32,7 +31,7 @@ use tokio::sync::{broadcast, mpsc}; use tracing::{debug, error, trace, warn}; use uuid::Uuid; -use super::node::{KnownNode, NodeAddr, ResolvedContactPoint}; +use crate::cluster::{KnownNode, NodeAddr, ResolvedContactPoint}; /// Allows to read current metadata from the cluster pub(crate) struct MetadataReader { diff --git a/scylla/src/cluster/mod.rs b/scylla/src/cluster/mod.rs new file mode 100644 index 0000000000..112ad2e022 --- /dev/null +++ b/scylla/src/cluster/mod.rs @@ -0,0 +1,15 @@ +#[allow(clippy::module_inception)] +mod cluster; +pub mod host_filter; +pub mod locator; +pub mod metadata; +mod node; + +pub(crate) use cluster::{Cluster, ClusterNeatDebug}; +pub use cluster::{ClusterData, Datacenter}; + +pub(crate) use node::{resolve_contact_points, resolve_hostname, ResolvedContactPoint}; +pub use node::{KnownNode, Node, NodeAddr, NodeRef}; + +#[cfg(feature = "cloud")] +pub(crate) use node::CloudEndpoint; diff --git a/scylla/src/transport/node.rs b/scylla/src/cluster/node.rs similarity index 97% rename from scylla/src/transport/node.rs rename to scylla/src/cluster/node.rs index 97b2679461..3e3000e982 100644 --- a/scylla/src/transport/node.rs +++ b/scylla/src/cluster/node.rs @@ -2,12 +2,12 @@ use tokio::net::lookup_host; use tracing::warn; use uuid::Uuid; +use crate::connection::Connection; +use crate::connection::VerifiedKeyspaceName; +use crate::connection::{NodeConnectionPool, PoolConfig}; +use crate::execution::errors::QueryError; /// Node represents a cluster node along with it's data and connections -use crate::routing::{Sharder, Token}; -use crate::transport::connection::Connection; -use crate::transport::connection::VerifiedKeyspaceName; -use crate::transport::connection_pool::{NodeConnectionPool, PoolConfig}; -use crate::transport::errors::QueryError; +use crate::routing::{sharding::Sharder, Token}; use std::fmt::Display; use std::io; @@ -21,7 +21,7 @@ use std::{ }, }; -use super::topology::{PeerEndpoint, UntranslatedEndpoint}; +use crate::cluster::metadata::{PeerEndpoint, UntranslatedEndpoint}; /// This enum is introduced to support address translation only upon opening a connection, /// as well as to cope with a bug present in older Cassandra and Scylla releases. diff --git a/scylla/src/transport/connection.rs b/scylla/src/connection/connection.rs similarity index 92% rename from scylla/src/transport/connection.rs rename to scylla/src/connection/connection.rs index bd12ed0614..597d6491b2 100644 --- a/scylla/src/transport/connection.rs +++ b/scylla/src/connection/connection.rs @@ -1,6 +1,8 @@ +use crate::execution::errors::TranslationError; +use crate::response::QueryResponse; +use async_trait::async_trait; use bytes::Bytes; use futures::{future::RemoteHandle, FutureExt}; -use scylla_cql::errors::TranslationError; use scylla_cql::frame::request::options::Options; use scylla_cql::frame::response::Error; use scylla_cql::frame::types::SerialConsistency; @@ -16,6 +18,7 @@ use uuid::Uuid; use std::borrow::Cow; #[cfg(feature = "ssl")] use std::pin::Pin; +use std::str::FromStr; use std::sync::atomic::AtomicU64; use std::time::Duration; #[cfg(feature = "ssl")] @@ -37,32 +40,32 @@ use std::{ net::{Ipv4Addr, Ipv6Addr}, }; -use super::errors::{BadKeyspaceName, DbError, QueryError}; -use super::iterator::RowIterator; -use super::session::AddressTranslator; -use super::topology::{PeerEndpoint, UntranslatedEndpoint, UntranslatedPeer}; -use super::NodeAddr; #[cfg(feature = "cloud")] use crate::cloud::CloudConfig; +use crate::cluster::metadata::{PeerEndpoint, UntranslatedEndpoint, UntranslatedPeer}; +use crate::cluster::NodeAddr; +use crate::execution::errors::{BadKeyspaceName, DbError, QueryError}; +use crate::execution::iterator::RowIterator; use crate::batch::{Batch, BatchStatement}; use crate::frame::protocol_features::ProtocolFeatures; use crate::frame::{ self, request::{self, batch, execute, query, register, SerializableRequest}, - response::{event::Event, result, NonErrorResponse, Response, ResponseOpcode}, + response::{event::Event, result, Response, ResponseOpcode}, server_event_type::EventType, value::{BatchValues, ValueList}, FrameParams, SerializedRequest, }; use crate::query::Query; -use crate::routing::ShardInfo; +use crate::response::IntoTypedRows; +use crate::routing::sharding::{Shard, ShardInfo, Sharder}; use crate::statement::prepared_statement::PreparedStatement; use crate::statement::Consistency; -use crate::transport::session::IntoTypedRows; -use crate::transport::Compression; use crate::QueryResult; +use super::Compression; + // Queries for schema agreement const LOCAL_VERSION: &str = "SELECT schema_version FROM system.local WHERE key='local'"; @@ -210,74 +213,6 @@ struct TaskResponse { body: Bytes, } -pub(crate) struct QueryResponse { - pub(crate) response: Response, - pub(crate) tracing_id: Option, - pub(crate) warnings: Vec, -} - -// A QueryResponse in which response can not be Response::Error -pub(crate) struct NonErrorQueryResponse { - pub(crate) response: NonErrorResponse, - pub(crate) tracing_id: Option, - pub(crate) warnings: Vec, -} - -impl QueryResponse { - pub(crate) fn into_non_error_query_response(self) -> Result { - Ok(NonErrorQueryResponse { - response: self.response.into_non_error_response()?, - tracing_id: self.tracing_id, - warnings: self.warnings, - }) - } - - pub(crate) fn into_query_result(self) -> Result { - self.into_non_error_query_response()?.into_query_result() - } -} - -impl NonErrorQueryResponse { - pub(crate) fn as_set_keyspace(&self) -> Option<&result::SetKeyspace> { - match &self.response { - NonErrorResponse::Result(result::Result::SetKeyspace(sk)) => Some(sk), - _ => None, - } - } - - pub(crate) fn as_schema_change(&self) -> Option<&result::SchemaChange> { - match &self.response { - NonErrorResponse::Result(result::Result::SchemaChange(sc)) => Some(sc), - _ => None, - } - } - - pub(crate) fn into_query_result(self) -> Result { - let (rows, paging_state, col_specs, serialized_size) = match self.response { - NonErrorResponse::Result(result::Result::Rows(rs)) => ( - Some(rs.rows), - rs.metadata.paging_state, - rs.metadata.col_specs, - rs.serialized_size, - ), - NonErrorResponse::Result(_) => (None, None, vec![], 0), - _ => { - return Err(QueryError::ProtocolError( - "Unexpected server response, expected Result or Error", - )) - } - }; - - Ok(QueryResult { - rows, - warnings: self.warnings, - tracing_id: self.tracing_id, - paging_state, - col_specs, - serialized_size, - }) - } -} #[cfg(feature = "ssl")] mod ssl_config { use openssl::{ @@ -407,7 +342,7 @@ impl ConnectionConfig { } // Used to listen for fatal error in connection -pub(crate) type ErrorReceiver = tokio::sync::oneshot::Receiver; +pub(super) type ErrorReceiver = tokio::sync::oneshot::Receiver; impl Connection { // Returns new connection and ErrorReceiver which can be used to wait for a fatal error @@ -1303,6 +1238,63 @@ impl Connection { } } +/// Translates IP addresses received from ScyllaDB nodes into locally reachable addresses. +/// +/// The driver auto-detects new ScyllaDB nodes added to the cluster through server side pushed +/// notifications and through checking the system tables. For each node, the address the driver +/// receives corresponds to the address set as `rpc_address` in the node yaml file. In most +/// cases, this is the correct address to use by the driver and that is what is used by default. +/// However, sometimes the addresses received through this mechanism will either not be reachable +/// directly by the driver or should not be the preferred address to use to reach the node (for +/// instance, the `rpc_address` set on ScyllaDB nodes might be a private IP, but some clients +/// may have to use a public IP, or pass by a router, e.g. through NAT, to reach that node). +/// This interface allows to deal with such cases, by allowing to translate an address as sent +/// by a ScyllaDB node to another address to be used by the driver for connection. +/// +/// Please note that the "known nodes" addresses provided while creating the [`crate::session::Session`] +/// instance are not translated, only IP address retrieved from or sent by Cassandra nodes +/// to the driver are. +#[async_trait] +pub trait AddressTranslator: Send + Sync { + async fn translate_address( + &self, + untranslated_peer: &UntranslatedPeer, + ) -> Result; +} + +#[async_trait] +impl AddressTranslator for HashMap { + async fn translate_address( + &self, + untranslated_peer: &UntranslatedPeer, + ) -> Result { + match self.get(&untranslated_peer.untranslated_address) { + Some(&translated_addr) => Ok(translated_addr), + None => Err(TranslationError::NoRuleForAddress), + } + } +} + +#[async_trait] +// Notice: this is unefficient, but what else can we do with such poor representation as str? +// After all, the cluster size is small enough to make this irrelevant. +impl AddressTranslator for HashMap<&'static str, &'static str> { + async fn translate_address( + &self, + untranslated_peer: &UntranslatedPeer, + ) -> Result { + for (&rule_addr_str, &translated_addr_str) in self.iter() { + if let Ok(rule_addr) = SocketAddr::from_str(rule_addr_str) { + if rule_addr == untranslated_peer.untranslated_address { + return SocketAddr::from_str(translated_addr_str) + .map_err(|_| TranslationError::InvalidAddressInRule); + } + } + } + Err(TranslationError::NoRuleForAddress) + } +} + async fn maybe_translated_addr( endpoint: UntranslatedEndpoint, address_translator: Option<&dyn AddressTranslator>, @@ -1452,6 +1444,32 @@ pub(crate) async fn open_named_connection( Ok((connection, error_receiver)) } +pub(crate) async fn open_connection_to_shard_aware_port( + endpoint: UntranslatedEndpoint, + shard: Shard, + sharder: Sharder, + connection_config: &ConnectionConfig, +) -> Result<(Connection, ErrorReceiver), QueryError> { + // Create iterator over all possible source ports for this shard + let source_port_iter = sharder.iter_source_ports_for_shard(shard); + + for port in source_port_iter { + let connect_result = + open_connection(endpoint.clone(), Some(port), connection_config.clone()).await; + + match connect_result { + Err(err) if err.is_address_unavailable_for_use() => continue, // If we can't use this port, try the next one + result => return result, + } + } + + // Tried all source ports for that shard, give up + Err(QueryError::IoError(Arc::new(std::io::Error::new( + std::io::ErrorKind::AddrInUse, + "Could not find free source port for shard", + )))) +} + async fn perform_authenticate( connection: &mut Connection, authenticate: &Authenticate, @@ -1748,8 +1766,8 @@ impl VerifiedKeyspaceName { #[cfg(test)] mod tests { + use crate::execution::errors::QueryError; use assert_matches::assert_matches; - use scylla_cql::errors::QueryError; use scylla_cql::frame::protocol_features::{ LWT_OPTIMIZATION_META_BIT_MASK_KEY, SCYLLA_LWT_ADD_METADATA_MARK_EXTENSION, }; @@ -1762,11 +1780,11 @@ mod tests { use tokio::select; use tokio::sync::mpsc; + use super::open_connection; use super::ConnectionConfig; + use crate::cluster::metadata::UntranslatedEndpoint; + use crate::cluster::ResolvedContactPoint; use crate::query::Query; - use crate::transport::connection::open_connection; - use crate::transport::node::ResolvedContactPoint; - use crate::transport::topology::UntranslatedEndpoint; use crate::utils::test_utils::unique_keyspace_name; use crate::{IntoTypedRows, SessionBuilder}; use futures::{StreamExt, TryStreamExt}; @@ -2151,4 +2169,60 @@ mod tests { let _ = proxy.finish().await; } + + use crate::routing::sharding::{ShardCount, Sharder}; + use std::net::ToSocketAddrs; + + // Open many connections to a node + // Port collision should occur + // If they are not handled this test will most likely fail + #[tokio::test] + #[cfg(not(scylla_cloud_tests))] + async fn many_connections() { + use super::open_connection_to_shard_aware_port; + + let connections_number = 512; + + let connect_address: SocketAddr = std::env::var("SCYLLA_URI") + .unwrap_or_else(|_| "127.0.0.1:9042".to_string()) + .to_socket_addrs() + .unwrap() + .next() + .unwrap(); + + let connection_config = ConnectionConfig { + compression: None, + tcp_nodelay: true, + #[cfg(feature = "ssl")] + ssl_config: None, + ..Default::default() + }; + + // This does not have to be the real sharder, + // the test is only about port collisions, not connecting + // to the right shard + let sharder = Sharder::new(ShardCount::new(3).unwrap(), 12); + + // Open the connections + let mut conns = Vec::new(); + + for _ in 0..connections_number { + conns.push(open_connection_to_shard_aware_port( + UntranslatedEndpoint::ContactPoint(ResolvedContactPoint { + address: connect_address, + datacenter: None, + }), + 0, + sharder.clone(), + &connection_config, + )); + } + + let joined = futures::future::join_all(conns).await; + + // Check that each connection managed to connect successfully + for res in joined { + res.unwrap(); + } + } } diff --git a/scylla/src/transport/connection_pool.rs b/scylla/src/connection/connection_pool.rs similarity index 93% rename from scylla/src/transport/connection_pool.rs rename to scylla/src/connection/connection_pool.rs index f26ea36ac2..0500c17e38 100644 --- a/scylla/src/transport/connection_pool.rs +++ b/scylla/src/connection/connection_pool.rs @@ -1,20 +1,20 @@ #[cfg(feature = "cloud")] use crate::cloud::set_ssl_config_for_scylla_cloud_host; -use crate::routing::{Shard, ShardCount, Sharder, Token}; -use crate::transport::errors::QueryError; -use crate::transport::{ - connection, - connection::{Connection, ConnectionConfig, ErrorReceiver, VerifiedKeyspaceName}, +use crate::connection::{Connection, ConnectionConfig, ErrorReceiver, VerifiedKeyspaceName}; +use crate::execution::errors::QueryError; +use crate::routing::{ + sharding::{Shard, ShardCount, Sharder}, + Token, }; #[cfg(feature = "cloud")] -use super::node::resolve_hostname; +use crate::cluster::resolve_hostname; +use crate::cluster::metadata::{PeerEndpoint, UntranslatedEndpoint}; +use crate::cluster::NodeAddr; #[cfg(feature = "cloud")] -use super::node::ResolvedContactPoint; -use super::topology::{PeerEndpoint, UntranslatedEndpoint}; -use super::NodeAddr; +use crate::cluster::ResolvedContactPoint; use arc_swap::ArcSwap; use futures::{future::RemoteHandle, stream::FuturesUnordered, Future, FutureExt, StreamExt}; @@ -30,6 +30,8 @@ use tokio::sync::{broadcast, mpsc, Notify}; use tracing::instrument::WithSubscriber; use tracing::{debug, trace, warn}; +use super::connection::{open_connection, open_connection_to_shard_aware_port}; + /// The target size of a per-node connection pool. #[derive(Debug, Clone, Copy)] pub enum PoolSize { @@ -947,7 +949,7 @@ impl PoolRefiller { .boxed(), _ => async move { let non_shard_aware_endpoint = endpoint_fut.await; - let result = connection::open_connection(non_shard_aware_endpoint, None, cfg).await; + let result = open_connection(non_shard_aware_endpoint, None, cfg).await; OpenedConnectionEvent { result, requested_shard: None, @@ -1227,91 +1229,3 @@ struct OpenedConnectionEvent { requested_shard: Option, keyspace_name: Option, } - -async fn open_connection_to_shard_aware_port( - endpoint: UntranslatedEndpoint, - shard: Shard, - sharder: Sharder, - connection_config: &ConnectionConfig, -) -> Result<(Connection, ErrorReceiver), QueryError> { - // Create iterator over all possible source ports for this shard - let source_port_iter = sharder.iter_source_ports_for_shard(shard); - - for port in source_port_iter { - let connect_result = - connection::open_connection(endpoint.clone(), Some(port), connection_config.clone()) - .await; - - match connect_result { - Err(err) if err.is_address_unavailable_for_use() => continue, // If we can't use this port, try the next one - result => return result, - } - } - - // Tried all source ports for that shard, give up - Err(QueryError::IoError(Arc::new(std::io::Error::new( - std::io::ErrorKind::AddrInUse, - "Could not find free source port for shard", - )))) -} - -#[cfg(test)] -mod tests { - use super::open_connection_to_shard_aware_port; - use crate::routing::{ShardCount, Sharder}; - use crate::transport::connection::ConnectionConfig; - use crate::transport::node::ResolvedContactPoint; - use crate::transport::topology::UntranslatedEndpoint; - use std::net::{SocketAddr, ToSocketAddrs}; - - // Open many connections to a node - // Port collision should occur - // If they are not handled this test will most likely fail - #[tokio::test] - #[cfg(not(scylla_cloud_tests))] - async fn many_connections() { - let connections_number = 512; - - let connect_address: SocketAddr = std::env::var("SCYLLA_URI") - .unwrap_or_else(|_| "127.0.0.1:9042".to_string()) - .to_socket_addrs() - .unwrap() - .next() - .unwrap(); - - let connection_config = ConnectionConfig { - compression: None, - tcp_nodelay: true, - #[cfg(feature = "ssl")] - ssl_config: None, - ..Default::default() - }; - - // This does not have to be the real sharder, - // the test is only about port collisions, not connecting - // to the right shard - let sharder = Sharder::new(ShardCount::new(3).unwrap(), 12); - - // Open the connections - let mut conns = Vec::new(); - - for _ in 0..connections_number { - conns.push(open_connection_to_shard_aware_port( - UntranslatedEndpoint::ContactPoint(ResolvedContactPoint { - address: connect_address, - datacenter: None, - }), - 0, - sharder.clone(), - &connection_config, - )); - } - - let joined = futures::future::join_all(conns).await; - - // Check that each connection managed to connect successfully - for res in joined { - res.unwrap(); - } - } -} diff --git a/scylla/src/connection/mod.rs b/scylla/src/connection/mod.rs new file mode 100644 index 0000000000..2f4814f1eb --- /dev/null +++ b/scylla/src/connection/mod.rs @@ -0,0 +1,16 @@ +#[allow(clippy::module_inception)] +mod connection; +mod connection_pool; + +use connection::ErrorReceiver; + +pub use scylla_cql::frame::Compression; + +pub use crate::execution::errors::TranslationError; +#[cfg(feature = "ssl")] +pub(crate) use connection::SslConfig; +pub use connection::{AddressTranslator, ConnectionConfig}; +pub(crate) use connection::{Connection, VerifiedKeyspaceName}; + +pub use connection_pool::PoolSize; +pub(crate) use connection_pool::{NodeConnectionPool, PoolConfig}; diff --git a/scylla/src/cql/mod.rs b/scylla/src/cql/mod.rs new file mode 100644 index 0000000000..6a35123af2 --- /dev/null +++ b/scylla/src/cql/mod.rs @@ -0,0 +1,28 @@ +//! This module re-exports entities from scylla-cql crate in a structured, +//! controlled way, so that only appropriate items are exposed for users. + +pub mod value { + pub use scylla_cql::frame::response::result::CqlValue; + pub use scylla_cql::frame::value::{ + Counter, CqlDuration, Date, MaybeUnset, SerializeValuesError, SerializedValues, Time, + Timestamp, Unset, Value, ValueList, + }; +} + +pub use value::{Value, ValueList}; + +pub mod frame { + pub mod response { + pub mod result { + pub use scylla_cql::frame::response::result::{ + ColumnSpec, ColumnType, PartitionKeyIndex, PreparedMetadata, Row, TableSpec, + }; + } + } +} + +pub mod cql_to_rust { + pub use scylla_cql::cql_to_rust::{ + CqlTypeError, FromCqlVal, FromCqlValError, FromRow, FromRowError, + }; +} diff --git a/scylla/src/execution/driver_tracing.rs b/scylla/src/execution/driver_tracing.rs new file mode 100644 index 0000000000..4144b2a05c --- /dev/null +++ b/scylla/src/execution/driver_tracing.rs @@ -0,0 +1,182 @@ +use std::{ + borrow::Borrow, + fmt::Display, + sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, + }, +}; + +use itertools::Either; +use scylla_cql::frame::response::result::{deser_cql_value, ColumnSpec, Rows}; +use tracing::trace_span; + +use crate::{ + cluster::Node, + connection::Connection, + routing::Token, + utils::pretty::{CommaSeparatedDisplayer, CqlValueDisplayer}, + QueryResult, +}; + +pub(crate) struct RequestSpan { + span: tracing::Span, + speculative_executions: AtomicUsize, +} + +impl RequestSpan { + pub(crate) fn new_query(contents: &str, request_size: usize) -> Self { + use tracing::field::Empty; + + let span = trace_span!( + "Request", + kind = "unprepared", + contents = contents, + // + request_size = request_size, + result_size = Empty, + result_rows = Empty, + replicas = Empty, + shard = Empty, + speculative_executions = Empty, + ); + + Self { + span, + speculative_executions: 0.into(), + } + } + + pub(crate) fn new_prepared<'ps>( + partition_key: Option + Clone>, + token: Option, + request_size: usize, + ) -> Self { + use tracing::field::Empty; + + let span = trace_span!( + "Request", + kind = "prepared", + partition_key = Empty, + token = Empty, + // + request_size = request_size, + result_size = Empty, + result_rows = Empty, + replicas = Empty, + shard = Empty, + speculative_executions = Empty, + ); + + if let Some(partition_key) = partition_key { + span.record( + "partition_key", + tracing::field::display( + format_args!("{}", partition_key_displayer(partition_key),), + ), + ); + } + if let Some(token) = token { + span.record("token", token.value); + } + + Self { + span, + speculative_executions: 0.into(), + } + } + + pub(crate) fn new_batch() -> Self { + use tracing::field::Empty; + + let span = trace_span!( + "Request", + kind = "batch", + // + request_size = Empty, + result_size = Empty, + result_rows = Empty, + replicas = Empty, + shard = Empty, + speculative_executions = Empty, + ); + + Self { + span, + speculative_executions: 0.into(), + } + } + + pub(crate) fn record_shard_id(&self, conn: &Connection) { + if let Some(info) = conn.get_shard_info() { + self.span.record("shard", info.shard); + } + } + + pub(crate) fn record_result_fields(&self, result: &QueryResult) { + self.span.record("result_size", result.serialized_size); + if let Some(rows) = result.rows.as_ref() { + self.span.record("result_rows", rows.len()); + } + } + + pub(crate) fn record_rows_fields(&self, rows: &Rows) { + self.span.record("result_size", rows.serialized_size); + self.span.record("result_rows", rows.rows.len()); + } + + pub(crate) fn record_replicas<'a>(&'a self, replicas: &'a [impl Borrow>]) { + struct ReplicaIps<'a, N>(&'a [N]); + impl<'a, N> Display for ReplicaIps<'a, N> + where + N: Borrow>, + { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut nodes = self.0.iter(); + if let Some(node) = nodes.next() { + write!(f, "{}", node.borrow().address.ip())?; + + for node in nodes { + write!(f, ",{}", node.borrow().address.ip())?; + } + } + Ok(()) + } + } + self.span + .record("replicas", tracing::field::display(&ReplicaIps(replicas))); + } + + pub(crate) fn inc_speculative_executions(&self) { + self.speculative_executions.fetch_add(1, Ordering::Relaxed); + } + + pub(crate) fn span(&self) -> &tracing::Span { + &self.span + } +} + +impl Drop for RequestSpan { + fn drop(&mut self) { + self.span.record( + "speculative_executions", + self.speculative_executions.load(Ordering::Relaxed), + ); + } +} + +fn partition_key_displayer<'ps, 'res>( + mut pk_values_iter: impl Iterator + 'res + Clone, +) -> impl Display + 'res { + CommaSeparatedDisplayer( + std::iter::from_fn(move || { + pk_values_iter + .next() + .map(|(mut cell, spec)| deser_cql_value(&spec.typ, &mut cell)) + }) + .map(|c| match c { + Ok(c) => Either::Left(CqlValueDisplayer(c)), + Err(_) => Either::Right(""), + }), + ) +} diff --git a/scylla/src/execution/errors.rs b/scylla/src/execution/errors.rs new file mode 100644 index 0000000000..196f74a8d5 --- /dev/null +++ b/scylla/src/execution/errors.rs @@ -0,0 +1,277 @@ +//! This module contains various errors which can be returned by `scylla::Session` + +use std::io::ErrorKind; +use std::sync::Arc; + +pub use scylla_cql::errors::{DbError, OperationType, WriteType}; + +use scylla_cql::frame::frame_errors::{FrameError, ParseError}; +use scylla_cql::frame::response::Error as CqlError; +use scylla_cql::frame::value::SerializeValuesError; +use thiserror::Error; + +/// Error that occurred during query execution +#[derive(Error, Debug, Clone)] +pub enum QueryError { + /// Database sent a response containing some error with a message + #[error("Database returned an error: {0}, Error message: {1}")] + DbError(DbError, String), + + /// Caller passed an invalid query + #[error(transparent)] + BadQuery(#[from] BadQuery), + + /// Input/Output error has occurred, connection broken etc. + #[error("IO Error: {0}")] + IoError(Arc), + + /// Unexpected message received + #[error("Protocol Error: {0}")] + ProtocolError(&'static str), + + /// Invalid message received + #[error("Invalid message: {0}")] + InvalidMessage(String), + + /// Timeout error has occurred, function didn't complete in time. + #[error("Timeout Error")] + TimeoutError, + + #[error("Too many orphaned stream ids: {0}")] + TooManyOrphanedStreamIds(u16), + + #[error("Unable to allocate stream id")] + UnableToAllocStreamId, + + /// Client timeout occurred before any response arrived + #[error("Request timeout: {0}")] + RequestTimeout(String), + + /// Address translation failed + #[error("Address translation failed: {0}")] + TranslationError(#[from] TranslationError), +} + +/// Error that occurred during session creation +#[derive(Error, Debug, Clone)] +pub enum NewSessionError { + /// Failed to resolve hostname passed in Session creation + #[error("Couldn't resolve any hostname: {0:?}")] + FailedToResolveAnyHostname(Vec), + + /// List of known nodes passed to Session constructor is empty + /// There needs to be at least one node to connect to + #[error("Empty known nodes list")] + EmptyKnownNodesList, + + /// Database sent a response containing some error with a message + #[error("Database returned an error: {0}, Error message: {1}")] + DbError(DbError, String), + + /// Caller passed an invalid query + #[error(transparent)] + BadQuery(#[from] BadQuery), + + /// Input/Output error has occurred, connection broken etc. + #[error("IO Error: {0}")] + IoError(Arc), + + /// Unexpected message received + #[error("Protocol Error: {0}")] + ProtocolError(&'static str), + + /// Invalid message received + #[error("Invalid message: {0}")] + InvalidMessage(String), + + /// Timeout error has occurred, couldn't connect to node in time. + #[error("Timeout Error")] + TimeoutError, + + #[error("Too many orphaned stream ids: {0}")] + TooManyOrphanedStreamIds(u16), + + #[error("Unable to allocate stream id")] + UnableToAllocStreamId, + + /// Client timeout occurred before a response arrived for some query + /// during `Session` creation. + #[error("Client timeout: {0}")] + RequestTimeout(String), + + /// Address translation failed + #[error("Address translation failed: {0}")] + TranslationError(#[from] TranslationError), +} + +/// Error caused by failed address translation done before establishing connection +#[derive(Debug, Copy, Clone, Error)] +pub enum TranslationError { + #[error("No rule for address")] + NoRuleForAddress, + #[error("Invalid address in rule")] + InvalidAddressInRule, +} + +/// Error caused by caller creating an invalid query +#[derive(Error, Debug, Clone)] +#[error("Invalid query passed to Session")] +pub enum BadQuery { + /// Failed to serialize values passed to a query - values too big + #[error("Serializing values failed: {0} ")] + SerializeValuesError(#[from] SerializeValuesError), + + /// Serialized values are too long to compute partition key + #[error("Serialized values are too long to compute partition key! Length: {0}, Max allowed length: {1}")] + ValuesTooLongForKey(usize, usize), + + /// Passed invalid keyspace name to use + #[error("Passed invalid keyspace name to use: {0}")] + BadKeyspaceName(#[from] BadKeyspaceName), + + /// Other reasons of bad query + #[error("{0}")] + Other(String), +} + +/// Invalid keyspace name given to `Session::use_keyspace()` +#[derive(Debug, Error, Clone)] +pub enum BadKeyspaceName { + /// Keyspace name is empty + #[error("Keyspace name is empty")] + Empty, + + /// Keyspace name too long, must be up to 48 characters + #[error("Keyspace name too long, must be up to 48 characters, found {1} characters. Bad keyspace name: '{0}'")] + TooLong(String, usize), + + /// Illegal character - only alphanumeric and underscores allowed. + #[error("Illegal character found: '{1}', only alphanumeric and underscores allowed. Bad keyspace name: '{0}'")] + IllegalCharacter(String, char), +} + +impl From for QueryError { + fn from(io_error: std::io::Error) -> QueryError { + QueryError::IoError(Arc::new(io_error)) + } +} + +impl From for QueryError { + fn from(serialized_err: SerializeValuesError) -> QueryError { + QueryError::BadQuery(BadQuery::SerializeValuesError(serialized_err)) + } +} + +impl From for QueryError { + fn from(parse_error: ParseError) -> QueryError { + QueryError::InvalidMessage(format!("Error parsing message: {}", parse_error)) + } +} + +impl From for QueryError { + fn from(frame_error: FrameError) -> QueryError { + QueryError::InvalidMessage(format!("Frame error: {}", frame_error)) + } +} + +impl From for QueryError { + fn from(timer_error: tokio::time::error::Elapsed) -> QueryError { + QueryError::RequestTimeout(format!("{}", timer_error)) + } +} + +impl From for NewSessionError { + fn from(io_error: std::io::Error) -> NewSessionError { + NewSessionError::IoError(Arc::new(io_error)) + } +} + +impl From for NewSessionError { + fn from(query_error: QueryError) -> NewSessionError { + match query_error { + QueryError::DbError(e, msg) => NewSessionError::DbError(e, msg), + QueryError::BadQuery(e) => NewSessionError::BadQuery(e), + QueryError::IoError(e) => NewSessionError::IoError(e), + QueryError::ProtocolError(m) => NewSessionError::ProtocolError(m), + QueryError::InvalidMessage(m) => NewSessionError::InvalidMessage(m), + QueryError::TimeoutError => NewSessionError::TimeoutError, + QueryError::TooManyOrphanedStreamIds(ids) => { + NewSessionError::TooManyOrphanedStreamIds(ids) + } + QueryError::UnableToAllocStreamId => NewSessionError::UnableToAllocStreamId, + QueryError::RequestTimeout(msg) => NewSessionError::RequestTimeout(msg), + QueryError::TranslationError(e) => NewSessionError::TranslationError(e), + } + } +} + +impl From for QueryError { + fn from(keyspace_err: BadKeyspaceName) -> QueryError { + QueryError::BadQuery(BadQuery::BadKeyspaceName(keyspace_err)) + } +} + +impl QueryError { + /// Checks if this error indicates that a chosen source port/address cannot be bound. + /// This is caused by one of the following: + /// - The source address is already used by another socket, + /// - The source address is reserved and the process does not have sufficient privileges to use it. + pub fn is_address_unavailable_for_use(&self) -> bool { + if let QueryError::IoError(io_error) = self { + match io_error.kind() { + ErrorKind::AddrInUse | ErrorKind::PermissionDenied => return true, + _ => {} + } + } + + false + } +} + +impl From for QueryError { + fn from(error: CqlError) -> QueryError { + QueryError::DbError(error.error, error.reason) + } +} + +#[cfg(test)] +mod tests { + use super::DbError; + use crate::statement::Consistency; + + use super::QueryError; + + // A test to check that displaying DbError and QueryError::DbError works as expected + // - displays error description + // - displays error parameters + // - displays error message + // - indented multiline strings don't cause whitespace gaps + #[test] + fn dberror_full_info() { + // Test that DbError::Unavailable is displayed correctly + let db_error = DbError::Unavailable { + consistency: Consistency::Three, + required: 3, + alive: 2, + }; + + let db_error_displayed: String = format!("{}", db_error); + + let mut expected_dberr_msg = + "Not enough nodes are alive to satisfy required consistency level ".to_string(); + expected_dberr_msg += "(consistency: Three, required: 3, alive: 2)"; + + assert_eq!(db_error_displayed, expected_dberr_msg); + + // Test that QueryError::DbError::(DbError::Unavailable) is displayed correctly + let query_error = + QueryError::DbError(db_error, "a message about unavailable error".to_string()); + let query_error_displayed: String = format!("{}", query_error); + + let mut expected_querr_msg = "Database returned an error: ".to_string(); + expected_querr_msg += &expected_dberr_msg; + expected_querr_msg += ", Error message: a message about unavailable error"; + + assert_eq!(query_error_displayed, expected_querr_msg); + } +} diff --git a/scylla/src/transport/execution_profile.rs b/scylla/src/execution/execution_profile.rs similarity index 92% rename from scylla/src/transport/execution_profile.rs rename to scylla/src/execution/execution_profile.rs index 245beffab9..ebe531d7fc 100644 --- a/scylla/src/transport/execution_profile.rs +++ b/scylla/src/execution/execution_profile.rs @@ -18,7 +18,7 @@ //! # async fn check_only_compiles() -> Result<(), Box> { //! use scylla::{Session, SessionBuilder}; //! use scylla::statement::Consistency; -//! use scylla::transport::ExecutionProfile; +//! use scylla::execution::ExecutionProfile; //! //! let profile = ExecutionProfile::builder() //! .consistency(Consistency::LocalOne) @@ -44,7 +44,7 @@ //! # async fn check_only_compiles() -> Result<(), Box> { //! use scylla::query::Query; //! use scylla::statement::Consistency; -//! use scylla::transport::ExecutionProfile; +//! use scylla::execution::ExecutionProfile; //! use std::time::Duration; //! //! let profile = ExecutionProfile::builder() @@ -71,7 +71,7 @@ //! # use std::error::Error; //! # async fn check_only_compiles() -> Result<(), Box> { //! use scylla::statement::Consistency; -//! use scylla::transport::ExecutionProfile; +//! use scylla::execution::ExecutionProfile; //! use std::time::Duration; //! //! let base_profile = ExecutionProfile::builder() @@ -112,7 +112,7 @@ //! use scylla::{Session, SessionBuilder}; //! use scylla::query::Query; //! use scylla::statement::Consistency; -//! use scylla::transport::ExecutionProfile; +//! use scylla::execution::ExecutionProfile; //! //! let profile1 = ExecutionProfile::builder() //! .consistency(Consistency::One) @@ -166,35 +166,35 @@ use arc_swap::ArcSwap; use scylla_cql::{frame::types::SerialConsistency, Consistency}; use crate::{ - load_balancing::LoadBalancingPolicy, retry_policy::RetryPolicy, + execution::retries::RetryPolicy, load_balancing::LoadBalancingPolicy, speculative_execution::SpeculativeExecutionPolicy, }; pub(crate) mod defaults { + use crate::execution::retries::{DefaultRetryPolicy, RetryPolicy}; + use crate::execution::ExecutionProfileInner; use crate::load_balancing::{self, LoadBalancingPolicy}; - use crate::retry_policy::{DefaultRetryPolicy, RetryPolicy}; use crate::speculative_execution::SpeculativeExecutionPolicy; - use crate::transport::execution_profile::ExecutionProfileInner; use scylla_cql::frame::types::SerialConsistency; use scylla_cql::Consistency; use std::sync::Arc; use std::time::Duration; - pub fn consistency() -> Consistency { + pub(crate) fn consistency() -> Consistency { Consistency::LocalQuorum } - pub fn serial_consistency() -> Option { + pub(crate) fn serial_consistency() -> Option { Some(SerialConsistency::LocalSerial) } - pub fn request_timeout() -> Option { + pub(crate) fn request_timeout() -> Option { Some(Duration::from_secs(30)) } - pub fn load_balancing_policy() -> Arc { + pub(crate) fn load_balancing_policy() -> Arc { Arc::new(load_balancing::DefaultPolicy::default()) } - pub fn retry_policy() -> Box { + pub(crate) fn retry_policy() -> Box { Box::new(DefaultRetryPolicy::new()) } - pub fn speculative_execution_policy() -> Option> { + pub(crate) fn speculative_execution_policy() -> Option> { None } @@ -216,7 +216,7 @@ pub(crate) mod defaults { /// # Example /// /// ``` -/// # use scylla::transport::{ExecutionProfile, retry_policy::FallthroughRetryPolicy}; +/// # use scylla::execution::{ExecutionProfile, retries::FallthroughRetryPolicy}; /// # use scylla::statement::Consistency; /// # fn example() -> Result<(), Box> { /// let profile: ExecutionProfile = ExecutionProfile::builder() @@ -242,7 +242,7 @@ impl ExecutionProfileBuilder { /// /// # Example /// ``` - /// # use scylla::transport::ExecutionProfile; + /// # use scylla::execution::ExecutionProfile; /// # use std::time::Duration; /// # fn example() -> Result<(), Box> { /// let profile: ExecutionProfile = ExecutionProfile::builder() @@ -276,8 +276,8 @@ impl ExecutionProfileBuilder { /// /// # Example /// ``` - /// # use scylla::transport::ExecutionProfile; - /// # use scylla::transport::load_balancing::DefaultPolicy; + /// # use scylla::execution::ExecutionProfile; + /// # use scylla::execution::load_balancing::DefaultPolicy; /// # use std::sync::Arc; /// # fn example() -> Result<(), Box> { /// let profile: ExecutionProfile = ExecutionProfile::builder() @@ -295,13 +295,13 @@ impl ExecutionProfileBuilder { } /// Sets the [`RetryPolicy`] to use by default on queries. - /// The default is [DefaultRetryPolicy](crate::transport::retry_policy::DefaultRetryPolicy). + /// The default is [DefaultRetryPolicy](crate::execution::retries::DefaultRetryPolicy). /// It is possible to implement a custom retry policy by implementing the trait [`RetryPolicy`]. /// /// # Example /// ``` - /// use scylla::transport::retry_policy::DefaultRetryPolicy; - /// # use scylla::transport::ExecutionProfile; + /// use scylla::execution::retries::DefaultRetryPolicy; + /// # use scylla::execution::ExecutionProfile; /// # fn example() -> Result<(), Box> { /// let profile: ExecutionProfile = ExecutionProfile::builder() /// .retry_policy(Box::new(DefaultRetryPolicy::new())) @@ -323,8 +323,8 @@ impl ExecutionProfileBuilder { /// # fn check_only_compiles() -> Result<(), Box> { /// use std::{sync::Arc, time::Duration}; /// use scylla::{ - /// transport::ExecutionProfile, - /// transport::speculative_execution::SimpleSpeculativeExecutionPolicy, + /// execution::ExecutionProfile, + /// execution::speculative_execution::SimpleSpeculativeExecutionPolicy, /// }; /// /// let policy = SimpleSpeculativeExecutionPolicy { @@ -350,8 +350,8 @@ impl ExecutionProfileBuilder { /// /// # Example /// ``` - /// use scylla::transport::retry_policy::DefaultRetryPolicy; - /// # use scylla::transport::ExecutionProfile; + /// use scylla::execution::retries::DefaultRetryPolicy; + /// # use scylla::execution::ExecutionProfile; /// # fn example() -> Result<(), Box> { /// let profile: ExecutionProfile = ExecutionProfile::builder() /// .retry_policy(Box::new(DefaultRetryPolicy::new())) diff --git a/scylla/src/history.rs b/scylla/src/execution/history.rs similarity index 99% rename from scylla/src/history.rs rename to scylla/src/execution/history.rs index 6b1bd1a98f..955f4f23cf 100644 --- a/scylla/src/history.rs +++ b/scylla/src/execution/history.rs @@ -7,10 +7,10 @@ use std::{ time::SystemTime, }; -use crate::retry_policy::RetryDecision; +use crate::execution::retries::RetryDecision; use chrono::{DateTime, Utc}; -use scylla_cql::errors::QueryError; +use crate::execution::errors::QueryError; use tracing::warn; /// Id of a single query, i.e. a single call to Session::query/execute/etc. @@ -456,7 +456,7 @@ mod tests { }; use crate::{ - query::Query, retry_policy::RetryDecision, utils::test_utils::unique_keyspace_name, + execution::retries::RetryDecision, query::Query, utils::test_utils::unique_keyspace_name, }; use super::{ @@ -464,12 +464,12 @@ mod tests { SpeculativeId, StructuredHistory, TimePoint, }; use crate::test_utils::create_new_session_builder; + use crate::{ + execution::errors::{DbError, QueryError}, + statement::Consistency, + }; use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, Utc}; use futures::StreamExt; - use scylla_cql::{ - errors::{DbError, QueryError}, - Consistency, - }; // Set a single time for all timestamps within StructuredHistory. // HistoryCollector sets the timestamp to current time which changes with each test. diff --git a/scylla/src/transport/iterator.rs b/scylla/src/execution/iterator.rs similarity index 98% rename from scylla/src/transport/iterator.rs rename to scylla/src/execution/iterator.rs index a9f5a7ec5b..8a26b7411d 100644 --- a/scylla/src/transport/iterator.rs +++ b/scylla/src/execution/iterator.rs @@ -17,11 +17,18 @@ use thiserror::Error; use tokio::sync::mpsc; use tracing::instrument::WithSubscriber; -use super::errors::QueryError; -use super::execution_profile::ExecutionProfileInner; -use super::session::RequestSpan; use crate::cql_to_rust::{FromRow, FromRowError}; - +use crate::execution::driver_tracing::RequestSpan; +use crate::execution::errors::QueryError; +use crate::execution::ExecutionProfileInner; + +use crate::cluster::ClusterData; +use crate::cluster::{Node, NodeRef}; +use crate::connection::Connection; +use crate::execution::history::{self, HistoryListener}; +use crate::execution::load_balancing::{self, RoutingInfo}; +use crate::execution::retries::{QueryInfo, RetryDecision, RetrySession}; +use crate::execution::Metrics; use crate::frame::{ response::{ result, @@ -29,15 +36,9 @@ use crate::frame::{ }, value::SerializedValues, }; -use crate::history::{self, HistoryListener}; +use crate::response::{NonErrorQueryResponse, QueryResponse}; use crate::statement::Consistency; use crate::statement::{prepared_statement::PreparedStatement, query::Query}; -use crate::transport::cluster::ClusterData; -use crate::transport::connection::{Connection, NonErrorQueryResponse, QueryResponse}; -use crate::transport::load_balancing::{self, RoutingInfo}; -use crate::transport::metrics::Metrics; -use crate::transport::retry_policy::{QueryInfo, RetryDecision, RetrySession}; -use crate::transport::{Node, NodeRef}; use crate::utils::unzip_option; use tracing::{trace, trace_span, warn, Instrument}; use uuid::Uuid; @@ -409,7 +410,8 @@ impl RowIterator { // A separate module is used here so that the parent module cannot construct // SendAttemptedProof directly. mod checked_channel_sender { - use scylla_cql::{errors::QueryError, frame::response::result::Rows}; + use crate::execution::errors::QueryError; + use scylla_cql::frame::response::result::Rows; use std::marker::PhantomData; use tokio::sync::mpsc; use uuid::Uuid; diff --git a/scylla/src/transport/load_balancing/default.rs b/scylla/src/execution/load_balancing/default.rs similarity index 99% rename from scylla/src/transport/load_balancing/default.rs rename to scylla/src/execution/load_balancing/default.rs index 3c2097c9dc..cadd1b4321 100644 --- a/scylla/src/transport/load_balancing/default.rs +++ b/scylla/src/execution/load_balancing/default.rs @@ -2,14 +2,15 @@ use self::latency_awareness::LatencyAwareness; pub use self::latency_awareness::LatencyAwarenessBuilder; use super::{FallbackPlan, LoadBalancingPolicy, NodeRef, RoutingInfo}; +use crate::cluster::ClusterData; +use crate::execution::errors::QueryError; +use crate::statement::{Consistency, SerialConsistency}; use crate::{ - routing::Token, - transport::{cluster::ClusterData, locator::ReplicaSet, node::Node, topology::Strategy}, + cluster::locator::ReplicaSet, cluster::metadata::Strategy, cluster::Node, routing::Token, }; use itertools::{Either, Itertools}; use rand::{prelude::SliceRandom, thread_rng, Rng}; use rand_pcg::Pcg32; -use scylla_cql::{errors::QueryError, frame::types::SerialConsistency, Consistency}; use std::{fmt, sync::Arc, time::Duration}; use tracing::warn; @@ -874,14 +875,12 @@ mod tests { ExpectedGroups, ExpectedGroupsBuilder, }; use crate::{ + cluster::locator::test::{KEYSPACE_NTS_RF_2, KEYSPACE_NTS_RF_3, KEYSPACE_SS_RF_2}, + cluster::ClusterData, load_balancing::{ default::tests::framework::mock_cluster_data_for_token_aware_tests, RoutingInfo, }, routing::Token, - transport::{ - locator::test::{KEYSPACE_NTS_RF_2, KEYSPACE_NTS_RF_3, KEYSPACE_SS_RF_2}, - ClusterData, - }, }; use super::{DefaultPolicy, NodeLocationPreference}; @@ -892,13 +891,13 @@ mod tests { use uuid::Uuid; use crate::{ - load_balancing::{LoadBalancingPolicy, Plan, RoutingInfo}, - routing::Token, - transport::{ - locator::test::{id_to_invalid_addr, mock_metadata_for_token_aware_tests}, - topology::{Metadata, Peer}, + cluster::locator::test::{id_to_invalid_addr, mock_metadata_for_token_aware_tests}, + cluster::{ + metadata::{Metadata, Peer}, ClusterData, }, + load_balancing::{LoadBalancingPolicy, Plan, RoutingInfo}, + routing::Token, }; enum ExpectedGroup { @@ -1199,7 +1198,7 @@ mod tests { #[tokio::test] async fn test_default_policy_with_token_aware_statements() { - use crate::transport::locator::test::{A, B, C, D, E, F, G}; + use crate::cluster::locator::test::{A, B, C, D, E, F, G}; let cluster = mock_cluster_data_for_token_aware_tests().await; struct Test<'a> { @@ -1669,7 +1668,7 @@ mod tests { #[tokio::test] async fn test_default_policy_with_lwt_statements() { - use crate::transport::locator::test::{A, B, C, D, E, F, G}; + use crate::cluster::locator::test::{A, B, C, D, E, F, G}; let cluster = mock_cluster_data_for_token_aware_tests().await; struct Test<'a> { @@ -2129,13 +2128,13 @@ mod tests { } mod latency_awareness { + use crate::execution::errors::{DbError, QueryError}; use futures::{future::RemoteHandle, FutureExt}; use itertools::Either; - use scylla_cql::errors::{DbError, QueryError}; use tracing::{instrument::WithSubscriber, trace}; use uuid::Uuid; - use crate::{load_balancing::NodeRef, transport::node::Node}; + use crate::{cluster::Node, load_balancing::NodeRef}; use std::{ collections::HashMap, ops::Deref, @@ -2717,22 +2716,22 @@ mod latency_awareness { }; use crate::{ - load_balancing::default::NodeLocationPreference, test_utils::create_new_session_builder, - }; - use crate::{ - load_balancing::{ - default::tests::test_default_policy_with_given_cluster_and_routing_info, - RoutingInfo, - }, - routing::Token, - transport::{ + cluster::{ locator::test::{ id_to_invalid_addr, A, B, C, D, E, F, G, KEYSPACE_NTS_RF_2, KEYSPACE_NTS_RF_3, }, ClusterData, NodeAddr, }, + load_balancing::{ + default::tests::test_default_policy_with_given_cluster_and_routing_info, + RoutingInfo, + }, + routing::Token, ExecutionProfile, }; + use crate::{ + load_balancing::default::NodeLocationPreference, test_utils::create_new_session_builder, + }; use std::time::Instant; trait DefaultPolicyTestExt { diff --git a/scylla/src/transport/load_balancing/mod.rs b/scylla/src/execution/load_balancing/mod.rs similarity index 97% rename from scylla/src/transport/load_balancing/mod.rs rename to scylla/src/execution/load_balancing/mod.rs index d4095743c3..a21d4f8588 100644 --- a/scylla/src/transport/load_balancing/mod.rs +++ b/scylla/src/execution/load_balancing/mod.rs @@ -2,9 +2,10 @@ //! `Session` can use any load balancing policy which implements the `LoadBalancingPolicy` trait\ //! See [the book](https://rust-driver.docs.scylladb.com/stable/load-balancing/load-balancing.html) for more information -use super::{cluster::ClusterData, NodeRef}; +use crate::cluster::{ClusterData, NodeRef}; +use crate::execution::errors::QueryError; use crate::routing::Token; -use scylla_cql::{errors::QueryError, frame::types}; +use scylla_cql::frame::types; use std::time::Duration; diff --git a/scylla/src/transport/load_balancing/plan.rs b/scylla/src/execution/load_balancing/plan.rs similarity index 96% rename from scylla/src/transport/load_balancing/plan.rs rename to scylla/src/execution/load_balancing/plan.rs index e49d4cb012..83c797178f 100644 --- a/scylla/src/transport/load_balancing/plan.rs +++ b/scylla/src/execution/load_balancing/plan.rs @@ -1,7 +1,7 @@ use tracing::error; use super::{FallbackPlan, LoadBalancingPolicy, NodeRef, RoutingInfo}; -use crate::transport::ClusterData; +use crate::cluster::ClusterData; enum PlanState<'a> { Created, @@ -105,10 +105,8 @@ impl<'a> Iterator for Plan<'a> { mod tests { use std::{net::SocketAddr, str::FromStr, sync::Arc}; - use crate::transport::{ - locator::test::{create_locator, mock_metadata_for_token_aware_tests}, - Node, NodeAddr, - }; + use crate::cluster::locator::test::{create_locator, mock_metadata_for_token_aware_tests}; + use crate::cluster::{Node, NodeAddr}; use super::*; diff --git a/scylla/src/transport/metrics.rs b/scylla/src/execution/metrics.rs similarity index 100% rename from scylla/src/transport/metrics.rs rename to scylla/src/execution/metrics.rs diff --git a/scylla/src/execution/mod.rs b/scylla/src/execution/mod.rs new file mode 100644 index 0000000000..5435b959fe --- /dev/null +++ b/scylla/src/execution/mod.rs @@ -0,0 +1,18 @@ +pub(crate) mod driver_tracing; +pub mod errors; +mod execution_profile; +pub mod history; +pub mod iterator; +pub mod load_balancing; +pub(crate) mod metrics; +pub mod retries; +pub mod speculative_execution; +pub mod tracing; + +pub(crate) use execution_profile::ExecutionProfileInner; +pub use execution_profile::{ExecutionProfile, ExecutionProfileBuilder, ExecutionProfileHandle}; + +#[cfg(test)] +pub(crate) use execution_profile::defaults; + +pub use metrics::Metrics; diff --git a/scylla/src/transport/retry_policy.rs b/scylla/src/execution/retries/default_rp.rs similarity index 85% rename from scylla/src/transport/retry_policy.rs rename to scylla/src/execution/retries/default_rp.rs index ee0c50aac2..05b6a2084d 100644 --- a/scylla/src/transport/retry_policy.rs +++ b/scylla/src/execution/retries/default_rp.rs @@ -1,89 +1,6 @@ -//! Query retries configurations\ -//! To decide when to retry a query the `Session` can use any object which implements -//! the `RetryPolicy` trait - -use crate::frame::types::Consistency; -use crate::transport::errors::{DbError, QueryError, WriteType}; - -/// Information about a failed query -pub struct QueryInfo<'a> { - /// The error with which the query failed - pub error: &'a QueryError, - /// A query is idempotent if it can be applied multiple times without changing the result of the initial application\ - /// If set to `true` we can be sure that it is idempotent\ - /// If set to `false` it is unknown whether it is idempotent - pub is_idempotent: bool, - /// Consistency with which the query failed - pub consistency: Consistency, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum RetryDecision { - RetrySameNode(Option), // None means that the same consistency should be used as before - RetryNextNode(Option), // ditto - DontRetry, - IgnoreWriteError, -} - -/// Specifies a policy used to decide when to retry a query -pub trait RetryPolicy: std::fmt::Debug + Send + Sync { - /// Called for each new query, starts a session of deciding about retries - fn new_session(&self) -> Box; - - /// Used to clone this RetryPolicy - fn clone_boxed(&self) -> Box; -} - -impl Clone for Box { - fn clone(&self) -> Box { - self.clone_boxed() - } -} - -/// Used throughout a single query to decide when to retry it -/// After this query is finished it is destroyed or reset -pub trait RetrySession: Send + Sync { - /// Called after the query failed - decide what to do next - fn decide_should_retry(&mut self, query_info: QueryInfo) -> RetryDecision; - - /// Reset before using for a new query - fn reset(&mut self); -} - -/// Forwards all errors directly to the user, never retries -#[derive(Debug)] -pub struct FallthroughRetryPolicy; -pub struct FallthroughRetrySession; - -impl FallthroughRetryPolicy { - pub fn new() -> FallthroughRetryPolicy { - FallthroughRetryPolicy - } -} - -impl Default for FallthroughRetryPolicy { - fn default() -> FallthroughRetryPolicy { - FallthroughRetryPolicy - } -} - -impl RetryPolicy for FallthroughRetryPolicy { - fn new_session(&self) -> Box { - Box::new(FallthroughRetrySession) - } +use crate::execution::errors::{DbError, QueryError, WriteType}; - fn clone_boxed(&self) -> Box { - Box::new(FallthroughRetryPolicy) - } -} - -impl RetrySession for FallthroughRetrySession { - fn decide_should_retry(&mut self, _query_info: QueryInfo) -> RetryDecision { - RetryDecision::DontRetry - } - - fn reset(&mut self) {} -} +use super::{QueryInfo, RetryDecision, RetryPolicy, RetrySession}; /// Default retry policy - retries when there is a high chance that a retry might help.\ /// Behaviour based on [DataStax Java Driver](https://docs.datastax.com/en/developer/java-driver/4.10/manual/core/retries/) @@ -219,8 +136,8 @@ impl RetrySession for DefaultRetrySession { #[cfg(test)] mod tests { use super::{DefaultRetryPolicy, QueryInfo, RetryDecision, RetryPolicy}; + use crate::execution::errors::{BadQuery, DbError, QueryError, WriteType}; use crate::statement::Consistency; - use crate::transport::errors::{BadQuery, DbError, QueryError, WriteType}; use bytes::Bytes; use std::io::ErrorKind; use std::sync::Arc; diff --git a/scylla/src/transport/downgrading_consistency_retry_policy.rs b/scylla/src/execution/retries/downgrading_consistency_rp.rs similarity index 98% rename from scylla/src/transport/downgrading_consistency_retry_policy.rs rename to scylla/src/execution/retries/downgrading_consistency_rp.rs index f55c25bcbf..a94343a21a 100644 --- a/scylla/src/transport/downgrading_consistency_retry_policy.rs +++ b/scylla/src/execution/retries/downgrading_consistency_rp.rs @@ -1,13 +1,13 @@ -use scylla_cql::{ - errors::{DbError, QueryError, WriteType}, - Consistency, +use crate::{ + execution::errors::{DbError, QueryError, WriteType}, + statement::Consistency, }; use tracing::debug; -use crate::retry_policy::{QueryInfo, RetryDecision, RetryPolicy, RetrySession}; +use super::{QueryInfo, RetryDecision, RetryPolicy, RetrySession}; /// Downgrading consistency retry policy - retries with lower consistency level if it knows\ -/// that the initial CL is unreachable. Also, it behaves as [DefaultRetryPolicy](crate::retry_policy::DefaultRetryPolicy) +/// that the initial CL is unreachable. Also, it behaves as [DefaultRetryPolicy](crate::execution::retries::DefaultRetryPolicy) /// when it believes that the initial CL is reachable. /// Behaviour based on [DataStax Java Driver]\ ///() @@ -183,8 +183,8 @@ impl RetrySession for DowngradingConsistencyRetrySession { mod tests { use std::{io::ErrorKind, sync::Arc}; + use crate::execution::errors::BadQuery; use bytes::Bytes; - use scylla_cql::errors::BadQuery; use super::*; diff --git a/scylla/src/execution/retries/fallthrough_rp.rs b/scylla/src/execution/retries/fallthrough_rp.rs new file mode 100644 index 0000000000..63d7fca23f --- /dev/null +++ b/scylla/src/execution/retries/fallthrough_rp.rs @@ -0,0 +1,36 @@ +use super::{QueryInfo, RetryDecision, RetryPolicy, RetrySession}; + +/// Forwards all errors directly to the user, never retries +#[derive(Debug)] +pub struct FallthroughRetryPolicy; +pub struct FallthroughRetrySession; + +impl FallthroughRetryPolicy { + pub fn new() -> FallthroughRetryPolicy { + FallthroughRetryPolicy + } +} + +impl Default for FallthroughRetryPolicy { + fn default() -> FallthroughRetryPolicy { + FallthroughRetryPolicy + } +} + +impl RetryPolicy for FallthroughRetryPolicy { + fn new_session(&self) -> Box { + Box::new(FallthroughRetrySession) + } + + fn clone_boxed(&self) -> Box { + Box::new(FallthroughRetryPolicy) + } +} + +impl RetrySession for FallthroughRetrySession { + fn decide_should_retry(&mut self, _query_info: QueryInfo) -> RetryDecision { + RetryDecision::DontRetry + } + + fn reset(&mut self) {} +} diff --git a/scylla/src/execution/retries/mod.rs b/scylla/src/execution/retries/mod.rs new file mode 100644 index 0000000000..d4a95a5645 --- /dev/null +++ b/scylla/src/execution/retries/mod.rs @@ -0,0 +1,11 @@ +mod default_rp; +mod downgrading_consistency_rp; +mod fallthrough_rp; +mod retry_policy; + +pub use default_rp::{DefaultRetryPolicy, DefaultRetrySession}; +pub use downgrading_consistency_rp::{ + DowngradingConsistencyRetryPolicy, DowngradingConsistencyRetrySession, +}; +pub use fallthrough_rp::{FallthroughRetryPolicy, FallthroughRetrySession}; +pub use retry_policy::{QueryInfo, RetryDecision, RetryPolicy, RetrySession}; diff --git a/scylla/src/execution/retries/retry_policy.rs b/scylla/src/execution/retries/retry_policy.rs new file mode 100644 index 0000000000..7562666882 --- /dev/null +++ b/scylla/src/execution/retries/retry_policy.rs @@ -0,0 +1,51 @@ +//! Query retries configurations\ +//! To decide when to retry a query the `Session` can use any object which implements +//! the `RetryPolicy` trait + +use crate::execution::errors::QueryError; +use crate::frame::types::Consistency; + +/// Information about a failed query +pub struct QueryInfo<'a> { + /// The error with which the query failed + pub error: &'a QueryError, + /// A query is idempotent if it can be applied multiple times without changing the result of the initial application\ + /// If set to `true` we can be sure that it is idempotent\ + /// If set to `false` it is unknown whether it is idempotent + pub is_idempotent: bool, + /// Consistency with which the query failed + pub consistency: Consistency, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum RetryDecision { + RetrySameNode(Option), // None means that the same consistency should be used as before + RetryNextNode(Option), // ditto + DontRetry, + IgnoreWriteError, +} + +/// Specifies a policy used to decide when to retry a query +pub trait RetryPolicy: std::fmt::Debug + Send + Sync { + /// Called for each new query, starts a session of deciding about retries + fn new_session(&self) -> Box; + + /// Used to clone this RetryPolicy + fn clone_boxed(&self) -> Box; +} + +impl Clone for Box { + fn clone(&self) -> Box { + self.clone_boxed() + } +} + +/// Used throughout a single query to decide when to retry it +/// After this query is finished it is destroyed or reset +pub trait RetrySession: Send + Sync { + /// Called after the query failed - decide what to do next + fn decide_should_retry(&mut self, query_info: QueryInfo) -> RetryDecision; + + /// Reset before using for a new query + fn reset(&mut self); +} diff --git a/scylla/src/transport/speculative_execution.rs b/scylla/src/execution/speculative_execution.rs similarity index 98% rename from scylla/src/transport/speculative_execution.rs rename to scylla/src/execution/speculative_execution.rs index 516ba8cd01..da10f25eb3 100644 --- a/scylla/src/transport/speculative_execution.rs +++ b/scylla/src/execution/speculative_execution.rs @@ -5,7 +5,8 @@ use futures::{ use std::{future::Future, sync::Arc, time::Duration}; use tracing::{trace_span, warn, Instrument}; -use super::{errors::QueryError, metrics::Metrics}; +use crate::execution::errors::QueryError; +use crate::execution::Metrics; /// Context is passed as an argument to `SpeculativeExecutionPolicy` methods pub struct Context { diff --git a/scylla/src/tracing.rs b/scylla/src/execution/tracing.rs similarity index 100% rename from scylla/src/tracing.rs rename to scylla/src/execution/tracing.rs diff --git a/scylla/src/lib.rs b/scylla/src/lib.rs index b1c8770eb0..9cefba3bd0 100644 --- a/scylla/src/lib.rs +++ b/scylla/src/lib.rs @@ -63,7 +63,7 @@ //! # Ok(()) //! # } //! ``` -//! But the driver will accept anything implementing the trait [ValueList](crate::frame::value::ValueList) +//! But the driver will accept anything implementing the trait [ValueList](crate::cql::ValueList) //! //! ### Receiving results //! The easiest way to read rows returned by a query is to cast each row to a tuple of values: @@ -98,18 +98,24 @@ pub mod _macro_internal { pub use scylla_cql::_macro_internal::*; } -pub use scylla_cql::frame; +pub(crate) use scylla_cql::frame; pub use scylla_cql::macros::{self, *}; pub mod authentication; #[cfg(feature = "cloud")] pub mod cloud; -pub mod history; +pub mod cluster; +pub mod connection; +pub mod cql; +pub mod execution; +pub mod response; pub mod routing; +pub mod session; pub mod statement; -pub mod tracing; -pub mod transport; + +#[cfg(test)] +mod tests; pub(crate) mod utils; @@ -122,22 +128,17 @@ pub use statement::batch; pub use statement::prepared_statement; pub use statement::query; -pub use frame::response::cql_to_rust; -pub use frame::response::cql_to_rust::FromRow; +pub use cql::cql_to_rust; +pub use cql::cql_to_rust::FromRow; -pub use transport::caching_session::CachingSession; -pub use transport::execution_profile::ExecutionProfile; -pub use transport::query_result::QueryResult; -pub use transport::session::{IntoTypedRows, Session, SessionConfig}; -pub use transport::session_builder::SessionBuilder; +pub use execution::ExecutionProfile; +pub use response::{IntoTypedRows, QueryResult}; +pub use session::CachingSession; +pub use session::SessionBuilder; +pub use session::{Session, SessionConfig}; #[cfg(feature = "cloud")] -pub use transport::session_builder::CloudSessionBuilder; - -pub use transport::execution_profile; -pub use transport::host_filter; -pub use transport::load_balancing; -pub use transport::retry_policy; -pub use transport::speculative_execution; +pub use session::CloudSessionBuilder; -pub use transport::metrics::Metrics; +pub use cluster::host_filter; +pub use execution::{load_balancing, speculative_execution}; diff --git a/scylla/src/response/mod.rs b/scylla/src/response/mod.rs new file mode 100644 index 0000000000..409109e030 --- /dev/null +++ b/scylla/src/response/mod.rs @@ -0,0 +1,8 @@ +mod query_response; +mod query_result; + +pub(crate) use query_response::{NonErrorQueryResponse, QueryResponse}; +pub use query_result::{ + FirstRowError, FirstRowTypedError, IntoTypedRows, MaybeFirstRowTypedError, QueryResult, + RowsExpectedError, RowsNotExpectedError, SingleRowError, SingleRowTypedError, TypedRowIter, +}; diff --git a/scylla/src/response/query_response.rs b/scylla/src/response/query_response.rs new file mode 100644 index 0000000000..fa0866eafa --- /dev/null +++ b/scylla/src/response/query_response.rs @@ -0,0 +1,73 @@ +use scylla_cql::frame::response::{result, NonErrorResponse, Response}; +use uuid::Uuid; + +use crate::{execution::errors::QueryError, QueryResult}; + +pub(crate) struct QueryResponse { + pub(crate) response: Response, + pub(crate) tracing_id: Option, + pub(crate) warnings: Vec, +} + +// A QueryResponse in which response can not be Response::Error +pub(crate) struct NonErrorQueryResponse { + pub(crate) response: NonErrorResponse, + pub(crate) tracing_id: Option, + pub(crate) warnings: Vec, +} + +impl QueryResponse { + pub(crate) fn into_non_error_query_response(self) -> Result { + Ok(NonErrorQueryResponse { + response: self.response.into_non_error_response()?, + tracing_id: self.tracing_id, + warnings: self.warnings, + }) + } + + pub(crate) fn into_query_result(self) -> Result { + self.into_non_error_query_response()?.into_query_result() + } +} + +impl NonErrorQueryResponse { + pub(crate) fn as_set_keyspace(&self) -> Option<&result::SetKeyspace> { + match &self.response { + NonErrorResponse::Result(result::Result::SetKeyspace(sk)) => Some(sk), + _ => None, + } + } + + pub(crate) fn as_schema_change(&self) -> Option<&result::SchemaChange> { + match &self.response { + NonErrorResponse::Result(result::Result::SchemaChange(sc)) => Some(sc), + _ => None, + } + } + + pub(crate) fn into_query_result(self) -> Result { + let (rows, paging_state, col_specs, serialized_size) = match self.response { + NonErrorResponse::Result(result::Result::Rows(rs)) => ( + Some(rs.rows), + rs.metadata.paging_state, + rs.metadata.col_specs, + rs.serialized_size, + ), + NonErrorResponse::Result(_) => (None, None, vec![], 0), + _ => { + return Err(QueryError::ProtocolError( + "Unexpected server response, expected Result or Error", + )) + } + }; + + Ok(QueryResult { + rows, + warnings: self.warnings, + tracing_id: self.tracing_id, + paging_state, + col_specs, + serialized_size, + }) + } +} diff --git a/scylla/src/transport/query_result.rs b/scylla/src/response/query_result.rs similarity index 94% rename from scylla/src/transport/query_result.rs rename to scylla/src/response/query_result.rs index 98a623f01c..9de0689914 100644 --- a/scylla/src/transport/query_result.rs +++ b/scylla/src/response/query_result.rs @@ -1,8 +1,9 @@ -use crate::frame::response::cql_to_rust::{FromRow, FromRowError}; +use crate::cql::cql_to_rust::{FromRow, FromRowError}; use crate::frame::response::result::ColumnSpec; use crate::frame::response::result::Row; -use crate::transport::session::{IntoTypedRows, TypedRowIter}; + use bytes::Bytes; +use scylla_cql::frame::response::result; use thiserror::Error; use uuid::Uuid; @@ -258,6 +259,38 @@ impl From for SingleRowTypedError { } } +/// Trait used to implement `Vec::into_typed` +// This is the only way to add custom method to Vec +pub trait IntoTypedRows { + fn into_typed(self) -> TypedRowIter; +} + +// Adds method Vec::into_typed(self) +// It transforms the Vec into iterator mapping to custom row type +impl IntoTypedRows for Vec { + fn into_typed(self) -> TypedRowIter { + TypedRowIter { + row_iter: self.into_iter(), + phantom_data: Default::default(), + } + } +} + +/// Iterator over rows parsed as the given type\ +/// Returned by `rows.into_typed::<(...)>()` +pub struct TypedRowIter { + row_iter: std::vec::IntoIter, + phantom_data: std::marker::PhantomData, +} + +impl Iterator for TypedRowIter { + type Item = Result; + + fn next(&mut self) -> Option { + self.row_iter.next().map(RowT::from_row) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/scylla/src/routing/mod.rs b/scylla/src/routing/mod.rs new file mode 100644 index 0000000000..e48d6afaa2 --- /dev/null +++ b/scylla/src/routing/mod.rs @@ -0,0 +1,7 @@ +pub mod partitioner; +pub mod sharding; + +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)] +pub struct Token { + pub value: i64, +} diff --git a/scylla/src/transport/partitioner.rs b/scylla/src/routing/partitioner.rs similarity index 99% rename from scylla/src/transport/partitioner.rs rename to scylla/src/routing/partitioner.rs index 9c8a542325..b2d2debbb8 100644 --- a/scylla/src/transport/partitioner.rs +++ b/scylla/src/routing/partitioner.rs @@ -366,9 +366,7 @@ mod tests { use rand::Rng; use rand_pcg::Pcg32; - use crate::transport::partitioner::PartitionerHasher; - - use super::{CDCPartitioner, Murmur3Partitioner, Partitioner}; + use super::{CDCPartitioner, Murmur3Partitioner, Partitioner, PartitionerHasher}; fn assert_correct_murmur3_hash(pk: &'static str, expected_hash: i64) { let hash = Murmur3Partitioner.hash_one(pk.as_bytes()).value; diff --git a/scylla/src/routing.rs b/scylla/src/routing/sharding.rs similarity index 98% rename from scylla/src/routing.rs rename to scylla/src/routing/sharding.rs index d6fff76466..8486466fcf 100644 --- a/scylla/src/routing.rs +++ b/scylla/src/routing/sharding.rs @@ -4,10 +4,7 @@ use std::convert::TryFrom; use std::num::NonZeroU16; use thiserror::Error; -#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)] -pub struct Token { - pub value: i64, -} +use super::Token; pub type Shard = u32; pub type ShardCount = NonZeroU16; diff --git a/scylla/src/transport/caching_session.rs b/scylla/src/session/caching_session.rs similarity index 99% rename from scylla/src/transport/caching_session.rs rename to scylla/src/session/caching_session.rs index 82e12b1ab2..1134b14f5c 100644 --- a/scylla/src/transport/caching_session.rs +++ b/scylla/src/session/caching_session.rs @@ -1,10 +1,10 @@ use crate::batch::{Batch, BatchStatement}; +use crate::execution::errors::QueryError; +use crate::execution::iterator::RowIterator; use crate::frame::value::{BatchValues, ValueList}; use crate::prepared_statement::PreparedStatement; use crate::query::Query; -use crate::transport::errors::QueryError; -use crate::transport::iterator::RowIterator; -use crate::transport::partitioner::PartitionerName; +use crate::routing::partitioner::PartitionerName; use crate::{QueryResult, Session}; use bytes::Bytes; use dashmap::DashMap; @@ -217,8 +217,8 @@ where #[cfg(test)] mod tests { use crate::query::Query; + use crate::routing::partitioner::PartitionerName; use crate::test_utils::create_new_session_builder; - use crate::transport::partitioner::PartitionerName; use crate::utils::test_utils::unique_keyspace_name; use crate::{ batch::{Batch, BatchStatement}, diff --git a/scylla/src/session/mod.rs b/scylla/src/session/mod.rs new file mode 100644 index 0000000000..ae7338f017 --- /dev/null +++ b/scylla/src/session/mod.rs @@ -0,0 +1,10 @@ +mod caching_session; +#[allow(clippy::module_inception)] +mod session; +pub mod session_builder; + +pub use caching_session::CachingSession; +pub use session::*; +#[cfg(feature = "cloud")] +pub use session_builder::CloudSessionBuilder; +pub use session_builder::SessionBuilder; diff --git a/scylla/src/transport/session.rs b/scylla/src/session/session.rs similarity index 86% rename from scylla/src/transport/session.rs rename to scylla/src/session/session.rs index 790f840400..fd5d64e1a3 100644 --- a/scylla/src/transport/session.rs +++ b/scylla/src/session/session.rs @@ -3,138 +3,70 @@ #[cfg(feature = "cloud")] use crate::cloud::CloudConfig; +use crate::IntoTypedRows; -use crate::history; -use crate::history::HistoryListener; -use crate::utils::pretty::{CommaSeparatedDisplayer, CqlValueDisplayer}; +use crate::execution::driver_tracing::RequestSpan; +use crate::execution::history::{self, HistoryListener}; use crate::utils::unzip_option; use arc_swap::ArcSwapOption; -use async_trait::async_trait; use bytes::Bytes; use futures::future::join_all; use futures::future::try_join_all; -use itertools::{Either, Itertools}; -pub use scylla_cql::errors::TranslationError; -use scylla_cql::frame::response::result::{deser_cql_value, ColumnSpec, Rows}; +use itertools::Itertools; use scylla_cql::frame::response::NonErrorResponse; use std::borrow::Borrow; -use std::collections::HashMap; -use std::fmt::Display; use std::future::Future; use std::net::SocketAddr; use std::num::NonZeroU32; -use std::str::FromStr; -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering; use std::sync::Arc; use std::time::Duration; use tokio::time::timeout; use tracing::{debug, trace, trace_span, Instrument}; use uuid::Uuid; -use super::connection::NonErrorQueryResponse; -use super::connection::QueryResponse; -#[cfg(feature = "ssl")] -use super::connection::SslConfig; -use super::errors::{NewSessionError, QueryError}; -use super::execution_profile::{ExecutionProfile, ExecutionProfileHandle, ExecutionProfileInner}; +use crate::cluster::host_filter::HostFilter; #[cfg(feature = "cloud")] -use super::node::CloudEndpoint; -use super::node::KnownNode; -use super::partitioner::PartitionerName; -use super::topology::UntranslatedPeer; -use super::NodeRef; -use crate::cql_to_rust::FromRow; -use crate::frame::response::cql_to_rust::FromRowError; +use crate::cluster::CloudEndpoint; +use crate::cluster::{Cluster, ClusterData, ClusterNeatDebug}; +use crate::cluster::{KnownNode, Node, NodeRef}; +use crate::connection::PoolConfig; +#[cfg(feature = "ssl")] +use crate::connection::SslConfig; +use crate::connection::{AddressTranslator, Connection, ConnectionConfig, VerifiedKeyspaceName}; +use crate::response::{NonErrorQueryResponse, QueryResponse}; + +use crate::connection::Compression; +use crate::execution::errors::{NewSessionError, QueryError}; +use crate::execution::iterator::{PreparedIteratorConfig, RowIterator}; +use crate::execution::load_balancing::{self, RoutingInfo}; +use crate::execution::retries::{QueryInfo, RetryDecision, RetrySession}; +use crate::execution::tracing::{ + TracingEvent, TracingInfo, TRACES_EVENTS_QUERY_STR, TRACES_SESSION_QUERY_STR, +}; +use crate::execution::Metrics; +use crate::execution::{ + speculative_execution, ExecutionProfile, ExecutionProfileHandle, ExecutionProfileInner, +}; use crate::frame::response::result; use crate::frame::value::{ BatchValues, BatchValuesFirstSerialized, BatchValuesIterator, ValueList, }; use crate::prepared_statement::PreparedStatement; use crate::query::Query; -use crate::routing::Token; +use crate::response::QueryResult; +use crate::routing::partitioner::PartitionerName; use crate::statement::Consistency; -use crate::tracing::{TracingEvent, TracingInfo}; -use crate::transport::cluster::{Cluster, ClusterData, ClusterNeatDebug}; -use crate::transport::connection::{Connection, ConnectionConfig, VerifiedKeyspaceName}; -use crate::transport::connection_pool::PoolConfig; -use crate::transport::host_filter::HostFilter; -use crate::transport::iterator::{PreparedIteratorConfig, RowIterator}; -use crate::transport::load_balancing::{self, RoutingInfo}; -use crate::transport::metrics::Metrics; -use crate::transport::node::Node; -use crate::transport::query_result::QueryResult; -use crate::transport::retry_policy::{QueryInfo, RetryDecision, RetrySession}; -use crate::transport::speculative_execution; -use crate::transport::Compression; use crate::{ batch::{Batch, BatchStatement}, statement::StatementConfig, }; -pub use crate::transport::connection_pool::PoolSize; +use crate::connection::PoolSize; use crate::authentication::AuthenticatorProvider; #[cfg(feature = "ssl")] use openssl::ssl::SslContext; -/// Translates IP addresses received from ScyllaDB nodes into locally reachable addresses. -/// -/// The driver auto-detects new ScyllaDB nodes added to the cluster through server side pushed -/// notifications and through checking the system tables. For each node, the address the driver -/// receives corresponds to the address set as `rpc_address` in the node yaml file. In most -/// cases, this is the correct address to use by the driver and that is what is used by default. -/// However, sometimes the addresses received through this mechanism will either not be reachable -/// directly by the driver or should not be the preferred address to use to reach the node (for -/// instance, the `rpc_address` set on ScyllaDB nodes might be a private IP, but some clients -/// may have to use a public IP, or pass by a router, e.g. through NAT, to reach that node). -/// This interface allows to deal with such cases, by allowing to translate an address as sent -/// by a ScyllaDB node to another address to be used by the driver for connection. -/// -/// Please note that the "known nodes" addresses provided while creating the [`Session`] -/// instance are not translated, only IP address retrieved from or sent by Cassandra nodes -/// to the driver are. -#[async_trait] -pub trait AddressTranslator: Send + Sync { - async fn translate_address( - &self, - untranslated_peer: &UntranslatedPeer, - ) -> Result; -} - -#[async_trait] -impl AddressTranslator for HashMap { - async fn translate_address( - &self, - untranslated_peer: &UntranslatedPeer, - ) -> Result { - match self.get(&untranslated_peer.untranslated_address) { - Some(&translated_addr) => Ok(translated_addr), - None => Err(TranslationError::NoRuleForAddress), - } - } -} - -#[async_trait] -// Notice: this is unefficient, but what else can we do with such poor representation as str? -// After all, the cluster size is small enough to make this irrelevant. -impl AddressTranslator for HashMap<&'static str, &'static str> { - async fn translate_address( - &self, - untranslated_peer: &UntranslatedPeer, - ) -> Result { - for (&rule_addr_str, &translated_addr_str) in self.iter() { - if let Ok(rule_addr) = SocketAddr::from_str(rule_addr_str) { - if rule_addr == untranslated_peer.untranslated_address { - return SocketAddr::from_str(translated_addr_str) - .map_err(|_| TranslationError::InvalidAddressInRule); - } - } - } - Err(TranslationError::NoRuleForAddress) - } -} - /// `Session` manages connections to the cluster and allows to perform queries pub struct Session { cluster: Cluster, @@ -404,38 +336,6 @@ impl Default for SessionConfig { } } -/// Trait used to implement `Vec::into_typed` -// This is the only way to add custom method to Vec -pub trait IntoTypedRows { - fn into_typed(self) -> TypedRowIter; -} - -// Adds method Vec::into_typed(self) -// It transforms the Vec into iterator mapping to custom row type -impl IntoTypedRows for Vec { - fn into_typed(self) -> TypedRowIter { - TypedRowIter { - row_iter: self.into_iter(), - phantom_data: Default::default(), - } - } -} - -/// Iterator over rows parsed as the given type\ -/// Returned by `rows.into_typed::<(...)>()` -pub struct TypedRowIter { - row_iter: std::vec::IntoIter, - phantom_data: std::marker::PhantomData, -} - -impl Iterator for TypedRowIter { - type Item = Result; - - fn next(&mut self) -> Option { - self.row_iter.next().map(RowT::from_row) - } -} - pub(crate) enum RunQueryResult { IgnoredWriteError, Completed(ResT), @@ -446,7 +346,7 @@ pub(crate) enum RunQueryResult { impl Session { /// Estabilishes a CQL session with the database /// - /// Usually it's easier to use [SessionBuilder](crate::transport::session_builder::SessionBuilder) + /// Usually it's easier to use [SessionBuilder](crate::session::SessionBuilder) /// instead of calling `Session::connect` directly, because it's more convenient. /// # Arguments /// * `config` - Connection configuration - known nodes, Compression, etc. @@ -457,7 +357,7 @@ impl Session { /// # use std::error::Error; /// # async fn check_only_compiles() -> Result<(), Box> { /// use scylla::{Session, SessionConfig}; - /// use scylla::transport::KnownNode; + /// use scylla::cluster::KnownNode; /// /// let mut config = SessionConfig::new(); /// config.known_nodes.push(KnownNode::Hostname("127.0.0.1:9042".to_string())); @@ -924,7 +824,7 @@ impl Session { /// Executes a previously prepared statement with previously received paging state /// # Arguments /// - /// * `prepared` - a statement prepared with [prepare](crate::transport::session::Session::prepare) + /// * `prepared` - a statement prepared with [prepare](crate::session::Session::prepare) /// * `values` - values bound to the query /// * `paging_state` - paging state from the previous query or None pub async fn execute_paged( @@ -1298,7 +1198,7 @@ impl Session { /// # Example /// ```rust /// # use scylla::{Session, SessionBuilder}; - /// # use scylla::transport::Compression; + /// # use scylla::connection::Compression; /// # async fn example() -> Result<(), Box> { /// # let session = SessionBuilder::new().known_node("127.0.0.1:9042").build().await?; /// session @@ -1405,12 +1305,12 @@ impl Session { consistency: Option, ) -> Result, QueryError> { // Query system_traces.sessions for TracingInfo - let mut traces_session_query = Query::new(crate::tracing::TRACES_SESSION_QUERY_STR); + let mut traces_session_query = Query::new(TRACES_SESSION_QUERY_STR); traces_session_query.config.consistency = consistency; traces_session_query.set_page_size(1024); // Query system_traces.events for TracingEvents - let mut traces_events_query = Query::new(crate::tracing::TRACES_EVENTS_QUERY_STR); + let mut traces_events_query = Query::new(TRACES_EVENTS_QUERY_STR); traces_events_query.config.consistency = consistency; traces_events_query.set_page_size(1024); @@ -1876,165 +1776,3 @@ impl<'a> ExecuteQueryContext<'a> { .log_attempt_error(*attempt_id, error, retry_decision); } } - -pub(crate) struct RequestSpan { - span: tracing::Span, - speculative_executions: AtomicUsize, -} - -impl RequestSpan { - pub(crate) fn new_query(contents: &str, request_size: usize) -> Self { - use tracing::field::Empty; - - let span = trace_span!( - "Request", - kind = "unprepared", - contents = contents, - // - request_size = request_size, - result_size = Empty, - result_rows = Empty, - replicas = Empty, - shard = Empty, - speculative_executions = Empty, - ); - - Self { - span, - speculative_executions: 0.into(), - } - } - - pub(crate) fn new_prepared<'ps>( - partition_key: Option + Clone>, - token: Option, - request_size: usize, - ) -> Self { - use tracing::field::Empty; - - let span = trace_span!( - "Request", - kind = "prepared", - partition_key = Empty, - token = Empty, - // - request_size = request_size, - result_size = Empty, - result_rows = Empty, - replicas = Empty, - shard = Empty, - speculative_executions = Empty, - ); - - if let Some(partition_key) = partition_key { - span.record( - "partition_key", - tracing::field::display( - format_args!("{}", partition_key_displayer(partition_key),), - ), - ); - } - if let Some(token) = token { - span.record("token", token.value); - } - - Self { - span, - speculative_executions: 0.into(), - } - } - - pub(crate) fn new_batch() -> Self { - use tracing::field::Empty; - - let span = trace_span!( - "Request", - kind = "batch", - // - request_size = Empty, - result_size = Empty, - result_rows = Empty, - replicas = Empty, - shard = Empty, - speculative_executions = Empty, - ); - - Self { - span, - speculative_executions: 0.into(), - } - } - - pub(crate) fn record_shard_id(&self, conn: &Connection) { - if let Some(info) = conn.get_shard_info() { - self.span.record("shard", info.shard); - } - } - - pub(crate) fn record_result_fields(&self, result: &QueryResult) { - self.span.record("result_size", result.serialized_size); - if let Some(rows) = result.rows.as_ref() { - self.span.record("result_rows", rows.len()); - } - } - - pub(crate) fn record_rows_fields(&self, rows: &Rows) { - self.span.record("result_size", rows.serialized_size); - self.span.record("result_rows", rows.rows.len()); - } - - pub(crate) fn record_replicas<'a>(&'a self, replicas: &'a [impl Borrow>]) { - struct ReplicaIps<'a, N>(&'a [N]); - impl<'a, N> Display for ReplicaIps<'a, N> - where - N: Borrow>, - { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let mut nodes = self.0.iter(); - if let Some(node) = nodes.next() { - write!(f, "{}", node.borrow().address.ip())?; - - for node in nodes { - write!(f, ",{}", node.borrow().address.ip())?; - } - } - Ok(()) - } - } - self.span - .record("replicas", tracing::field::display(&ReplicaIps(replicas))); - } - - pub(crate) fn inc_speculative_executions(&self) { - self.speculative_executions.fetch_add(1, Ordering::Relaxed); - } - - pub(crate) fn span(&self) -> &tracing::Span { - &self.span - } -} - -impl Drop for RequestSpan { - fn drop(&mut self) { - self.span.record( - "speculative_executions", - self.speculative_executions.load(Ordering::Relaxed), - ); - } -} - -fn partition_key_displayer<'ps, 'res>( - mut pk_values_iter: impl Iterator + 'res + Clone, -) -> impl Display + 'res { - CommaSeparatedDisplayer( - std::iter::from_fn(move || { - pk_values_iter - .next() - .map(|(mut cell, spec)| deser_cql_value(&spec.typ, &mut cell)) - }) - .map(|c| match c { - Ok(c) => Either::Left(CqlValueDisplayer(c)), - Err(_) => Either::Right(""), - }), - ) -} diff --git a/scylla/src/transport/session_builder.rs b/scylla/src/session/session_builder.rs similarity index 97% rename from scylla/src/transport/session_builder.rs rename to scylla/src/session/session_builder.rs index 394a348ca2..71f9dbd72f 100644 --- a/scylla/src/transport/session_builder.rs +++ b/scylla/src/session/session_builder.rs @@ -1,18 +1,18 @@ //! SessionBuilder provides an easy way to create new Sessions -use super::errors::NewSessionError; -use super::execution_profile::ExecutionProfileHandle; -use super::session::{AddressTranslator, Session, SessionConfig}; -use super::Compression; +use crate::connection::Compression; +use crate::connection::{AddressTranslator, PoolSize}; +use crate::execution::errors::NewSessionError; +use crate::execution::ExecutionProfileHandle; +use crate::session::{Session, SessionConfig}; #[cfg(feature = "cloud")] use crate::cloud::{CloudConfig, CloudConfigError}; #[cfg(feature = "cloud")] use crate::ExecutionProfile; +use crate::cluster::host_filter::HostFilter; use crate::statement::Consistency; -use crate::transport::connection_pool::PoolSize; -use crate::transport::host_filter::HostFilter; use std::borrow::Borrow; use std::marker::PhantomData; use std::net::SocketAddr; @@ -55,7 +55,7 @@ pub type CloudSessionBuilder = GenericSessionBuilder; /// /// ``` /// # use scylla::{Session, SessionBuilder}; -/// # use scylla::transport::Compression; +/// # use scylla::connection::Compression; /// # async fn example() -> Result<(), Box> { /// let session: Session = SessionBuilder::new() /// .known_node("127.0.0.1:9042") @@ -173,7 +173,7 @@ impl GenericSessionBuilder { /// # Example /// ``` /// # use scylla::{Session, SessionBuilder}; - /// # use scylla::transport::Compression; + /// # use scylla::connection::Compression; /// # async fn example() -> Result<(), Box> { /// let session: Session = SessionBuilder::new() /// .known_node("127.0.0.1:9042") @@ -201,7 +201,7 @@ impl GenericSessionBuilder { /// use scylla::{Session, SessionBuilder}; /// use async_trait::async_trait; /// use scylla::authentication::{AuthenticatorProvider, AuthenticatorSession, AuthError}; - /// # use scylla::transport::Compression; + /// # use scylla::connection::Compression; /// /// struct CustomAuthenticator; /// @@ -253,8 +253,8 @@ impl GenericSessionBuilder { /// # use std::net::SocketAddr; /// # use std::sync::Arc; /// # use scylla::{Session, SessionBuilder}; - /// # use scylla::transport::session::{AddressTranslator, TranslationError}; - /// # use scylla::transport::topology::UntranslatedPeer; + /// # use scylla::connection::{AddressTranslator, TranslationError}; + /// # use scylla::cluster::metadata::UntranslatedPeer; /// struct IdentityTranslator; /// /// #[async_trait] @@ -283,7 +283,7 @@ impl GenericSessionBuilder { /// # use std::collections::HashMap; /// # use std::str::FromStr; /// # use scylla::{Session, SessionBuilder}; - /// # use scylla::transport::session::{AddressTranslator, TranslationError}; + /// # use scylla::connection::{AddressTranslator, TranslationError}; /// # /// # async fn example() -> Result<(), Box> { /// let mut translation_rules = HashMap::new(); @@ -373,7 +373,7 @@ impl GenericSessionBuilder { /// # Example /// ``` /// # use scylla::{Session, SessionBuilder}; - /// # use scylla::transport::Compression; + /// # use scylla::connection::Compression; /// # async fn example() -> Result<(), Box> { /// let session: Session = SessionBuilder::new() /// .known_node("127.0.0.1:9042") @@ -493,7 +493,7 @@ impl GenericSessionBuilder { /// # Example /// ``` /// # use scylla::{Session, SessionBuilder}; - /// # use scylla::transport::Compression; + /// # use scylla::connection::Compression; /// # async fn example() -> Result<(), Box> { /// let session: Session = SessionBuilder::new() /// .known_node("127.0.0.1:9042") @@ -514,7 +514,7 @@ impl GenericSessionBuilder { /// # Example /// ``` /// # use scylla::{Session, SessionBuilder}; - /// # use scylla::transport::Compression; + /// # use scylla::connection::Compression; /// # async fn example() -> Result<(), Box> { /// let session: Session = SessionBuilder::new() /// .known_node("127.0.0.1:9042") @@ -558,7 +558,7 @@ impl GenericSessionBuilder { /// # use scylla::{Session, SessionBuilder}; /// # async fn example() -> Result<(), Box> { /// use std::num::NonZeroUsize; - /// use scylla::transport::session::PoolSize; + /// use scylla::connection::PoolSize; /// /// // This session will establish 4 connections to each node. /// // For Scylla clusters, this number will be divided across shards @@ -761,7 +761,7 @@ impl GenericSessionBuilder { /// should be opened to the node or not. The driver will also avoid /// those nodes when re-establishing the control connection. /// - /// See the [host filter](crate::transport::host_filter) module for a list + /// See the [host filter](crate::cluster::host_filter) module for a list /// of pre-defined filters. It is also possible to provide a custom filter /// by implementing the HostFilter trait. /// @@ -771,8 +771,8 @@ impl GenericSessionBuilder { /// # use std::net::SocketAddr; /// # use std::sync::Arc; /// # use scylla::{Session, SessionBuilder}; - /// # use scylla::transport::session::{AddressTranslator, TranslationError}; - /// # use scylla::transport::host_filter::DcHostFilter; + /// # use scylla::connection::{AddressTranslator, TranslationError}; + /// # use scylla::cluster::host_filter::DcHostFilter; /// /// # async fn example() -> Result<(), Box> { /// // The session will only connect to nodes from "my-local-dc" @@ -809,7 +809,7 @@ impl GenericSessionBuilder { self } - /// Set the number of attempts to fetch [TracingInfo](crate::tracing::TracingInfo) + /// Set the number of attempts to fetch [TracingInfo](crate::execution::tracing::TracingInfo) /// in [`Session::get_tracing_info`]. /// The default is 5 attempts. /// @@ -834,7 +834,7 @@ impl GenericSessionBuilder { self } - /// Set the delay between attempts to fetch [TracingInfo](crate::tracing::TracingInfo) + /// Set the delay between attempts to fetch [TracingInfo](crate::execution::tracing::TracingInfo) /// in [`Session::get_tracing_info`]. /// The default is 3 milliseconds. /// @@ -859,7 +859,7 @@ impl GenericSessionBuilder { self } - /// Set the consistency level of fetching [TracingInfo](crate::tracing::TracingInfo) + /// Set the consistency level of fetching [TracingInfo](crate::execution::tracing::TracingInfo) /// in [`Session::get_tracing_info`]. /// The default is [`Consistency::One`]. /// @@ -896,7 +896,7 @@ impl GenericSessionBuilder { /// # Example /// ``` /// # use scylla::{Session, SessionBuilder}; - /// # use scylla::transport::Compression; + /// # use scylla::connection::Compression; /// # async fn example() -> Result<(), Box> { /// let session: Session = SessionBuilder::new() /// .known_node("127.0.0.1:9042") @@ -949,9 +949,9 @@ mod tests { use scylla_cql::Consistency; use super::SessionBuilder; - use crate::transport::execution_profile::{defaults, ExecutionProfile}; - use crate::transport::node::KnownNode; - use crate::transport::Compression; + use crate::cluster::KnownNode; + use crate::connection::Compression; + use crate::execution::{defaults, ExecutionProfile}; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::time::Duration; diff --git a/scylla/src/statement/batch.rs b/scylla/src/statement/batch.rs index 58d8ce411b..04de6a5da8 100644 --- a/scylla/src/statement/batch.rs +++ b/scylla/src/statement/batch.rs @@ -1,10 +1,10 @@ use std::borrow::Cow; use std::sync::Arc; -use crate::history::HistoryListener; -use crate::retry_policy::RetryPolicy; +use crate::execution::history::HistoryListener; +use crate::execution::retries::RetryPolicy; +use crate::execution::ExecutionProfileHandle; use crate::statement::{prepared_statement::PreparedStatement, query::Query}; -use crate::transport::execution_profile::ExecutionProfileHandle; use super::StatementConfig; use super::{Consistency, SerialConsistency}; @@ -76,7 +76,7 @@ impl Batch { /// A query is idempotent if it can be applied multiple times without changing the result of the initial application /// If set to `true` we can be sure that it is idempotent /// If set to `false` it is unknown whether it is idempotent - /// This is used in [`RetryPolicy`](crate::retry_policy::RetryPolicy) to decide if retrying a query is safe + /// This is used in [`RetryPolicy`](crate::execution::retries::RetryPolicy) to decide if retrying a query is safe pub fn set_is_idempotent(&mut self, is_idempotent: bool) { self.config.is_idempotent = is_idempotent; } diff --git a/scylla/src/statement/mod.rs b/scylla/src/statement/mod.rs index a8d034615d..e8c06e84d1 100644 --- a/scylla/src/statement/mod.rs +++ b/scylla/src/statement/mod.rs @@ -1,7 +1,6 @@ use std::{sync::Arc, time::Duration}; -use crate::transport::execution_profile::ExecutionProfileHandle; -use crate::{history::HistoryListener, retry_policy::RetryPolicy}; +use crate::execution::{history::HistoryListener, retries::RetryPolicy, ExecutionProfileHandle}; pub mod batch; pub mod prepared_statement; diff --git a/scylla/src/statement/prepared_statement.rs b/scylla/src/statement/prepared_statement.rs index b57d5d4b23..1d828d7537 100644 --- a/scylla/src/statement/prepared_statement.rs +++ b/scylla/src/statement/prepared_statement.rs @@ -1,5 +1,5 @@ +use crate::execution::errors::{BadQuery, QueryError}; use bytes::{Bytes, BytesMut}; -use scylla_cql::errors::{BadQuery, QueryError}; use smallvec::{smallvec, SmallVec}; use std::convert::TryInto; use std::sync::Arc; @@ -10,14 +10,14 @@ use uuid::Uuid; use scylla_cql::frame::response::result::ColumnSpec; use super::StatementConfig; +use crate::execution::history::HistoryListener; +use crate::execution::retries::RetryPolicy; +use crate::execution::ExecutionProfileHandle; use crate::frame::response::result::PreparedMetadata; use crate::frame::types::{Consistency, SerialConsistency}; use crate::frame::value::SerializedValues; -use crate::history::HistoryListener; -use crate::retry_policy::RetryPolicy; +use crate::routing::partitioner::{Partitioner, PartitionerHasher, PartitionerName}; use crate::routing::Token; -use crate::transport::execution_profile::ExecutionProfileHandle; -use crate::transport::partitioner::{Partitioner, PartitionerHasher, PartitionerName}; /// Represents a statement prepared on the server. #[derive(Debug)] @@ -239,7 +239,7 @@ impl PreparedStatement { /// A query is idempotent if it can be applied multiple times without changing the result of the initial application /// If set to `true` we can be sure that it is idempotent /// If set to `false` it is unknown whether it is idempotent - /// This is used in [`RetryPolicy`](crate::retry_policy::RetryPolicy) to decide if retrying a query is safe + /// This is used in [`RetryPolicy`](crate::execution::retries::RetryPolicy) to decide if retrying a query is safe pub fn set_is_idempotent(&mut self, is_idempotent: bool) { self.config.is_idempotent = is_idempotent; } diff --git a/scylla/src/statement/query.rs b/scylla/src/statement/query.rs index 956b645833..cb79a95b35 100644 --- a/scylla/src/statement/query.rs +++ b/scylla/src/statement/query.rs @@ -1,8 +1,8 @@ use super::StatementConfig; +use crate::execution::history::HistoryListener; +use crate::execution::retries::RetryPolicy; +use crate::execution::ExecutionProfileHandle; use crate::frame::types::{Consistency, SerialConsistency}; -use crate::history::HistoryListener; -use crate::retry_policy::RetryPolicy; -use crate::transport::execution_profile::ExecutionProfileHandle; use std::sync::Arc; use std::time::Duration; @@ -76,7 +76,7 @@ impl Query { /// A query is idempotent if it can be applied multiple times without changing the result of the initial application /// If set to `true` we can be sure that it is idempotent /// If set to `false` it is unknown whether it is idempotent - /// This is used in [`RetryPolicy`](crate::retry_policy::RetryPolicy) to decide if retrying a query is safe + /// This is used in [`RetryPolicy`](crate::execution::retries::RetryPolicy) to decide if retrying a query is safe pub fn set_is_idempotent(&mut self, is_idempotent: bool) { self.config.is_idempotent = is_idempotent; } diff --git a/scylla/src/transport/cql_collections_test.rs b/scylla/src/tests/cql_collections_test.rs similarity index 98% rename from scylla/src/transport/cql_collections_test.rs rename to scylla/src/tests/cql_collections_test.rs index cd89443271..a70da29d38 100644 --- a/scylla/src/transport/cql_collections_test.rs +++ b/scylla/src/tests/cql_collections_test.rs @@ -2,7 +2,8 @@ use crate::cql_to_rust::FromCqlVal; use crate::frame::value::Value; use crate::test_utils::create_new_session_builder; use crate::utils::test_utils::unique_keyspace_name; -use crate::{frame::response::result::CqlValue, IntoTypedRows, Session}; +use crate::IntoTypedRows; +use crate::{frame::response::result::CqlValue, Session}; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; async fn connect() -> Session { diff --git a/scylla/src/transport/cql_types_test.rs b/scylla/src/tests/cql_types_test.rs similarity index 99% rename from scylla/src/transport/cql_types_test.rs rename to scylla/src/tests/cql_types_test.rs index dbc4cf2537..54b7ae30c9 100644 --- a/scylla/src/transport/cql_types_test.rs +++ b/scylla/src/tests/cql_types_test.rs @@ -5,9 +5,9 @@ use crate::frame::value::Counter; use crate::frame::value::Value; use crate::frame::value::{Date, Time, Timestamp}; use crate::macros::{FromUserType, IntoUserType}; +use crate::response::IntoTypedRows; +use crate::session::Session; use crate::test_utils::create_new_session_builder; -use crate::transport::session::IntoTypedRows; -use crate::transport::session::Session; use crate::utils::test_utils::unique_keyspace_name; use bigdecimal::BigDecimal; use chrono::{Duration, NaiveDate}; diff --git a/scylla/src/transport/cql_value_test.rs b/scylla/src/tests/cql_value_test.rs similarity index 100% rename from scylla/src/transport/cql_value_test.rs rename to scylla/src/tests/cql_value_test.rs diff --git a/scylla/src/tests/mod.rs b/scylla/src/tests/mod.rs new file mode 100644 index 0000000000..671402d945 --- /dev/null +++ b/scylla/src/tests/mod.rs @@ -0,0 +1,4 @@ +mod cql_collections_test; +mod cql_types_test; +mod cql_value_test; +mod session_test; diff --git a/scylla/src/transport/session_test.rs b/scylla/src/tests/session_test.rs similarity index 99% rename from scylla/src/transport/session_test.rs rename to scylla/src/tests/session_test.rs index 6bc9f0e4f1..f33365d25c 100644 --- a/scylla/src/transport/session_test.rs +++ b/scylla/src/tests/session_test.rs @@ -1,29 +1,28 @@ use crate as scylla; use crate::batch::{Batch, BatchStatement}; +use crate::cluster::metadata::Strategy::NetworkTopologyStrategy; +use crate::cluster::metadata::{CollectionType, ColumnKind, CqlType, NativeType, UserDefinedType}; +use crate::cluster::Datacenter; +use crate::execution::errors::{BadKeyspaceName, BadQuery, DbError, QueryError}; +use crate::execution::retries::{QueryInfo, RetryDecision, RetryPolicy, RetrySession}; +use crate::execution::tracing::TracingInfo; use crate::frame::response::result::Row; use crate::frame::value::ValueList; use crate::prepared_statement::PreparedStatement; use crate::query::Query; -use crate::retry_policy::{QueryInfo, RetryDecision, RetryPolicy, RetrySession}; -use crate::routing::Token; -use crate::statement::Consistency; -use crate::tracing::TracingInfo; -use crate::transport::cluster::Datacenter; -use crate::transport::errors::{BadKeyspaceName, BadQuery, DbError, QueryError}; -use crate::transport::partitioner::{ +use crate::routing::partitioner::{ calculate_token_for_partition_key, Murmur3Partitioner, Partitioner, PartitionerName, }; -use crate::transport::topology::Strategy::NetworkTopologyStrategy; -use crate::transport::topology::{ - CollectionType, ColumnKind, CqlType, NativeType, UserDefinedType, -}; +use crate::routing::Token; +use crate::statement::Consistency; use crate::utils::test_utils::{ create_new_session_builder, supports_feature, unique_keyspace_name, }; use crate::CachingSession; use crate::ExecutionProfile; +use crate::IntoTypedRows; use crate::QueryResult; -use crate::{IntoTypedRows, Session, SessionBuilder}; +use crate::{Session, SessionBuilder}; use assert_matches::assert_matches; use bytes::Bytes; use futures::{FutureExt, StreamExt, TryStreamExt}; @@ -2400,7 +2399,7 @@ async fn test_rate_limit_exceeded_exception() { } } - use scylla_cql::errors::OperationType; + use crate::execution::errors::OperationType; match maybe_err.expect("Rate limit error didn't occur") { QueryError::DbError(DbError::RateLimitReached { op_type, .. }, _) => { diff --git a/scylla/src/transport/mod.rs b/scylla/src/transport/mod.rs deleted file mode 100644 index 6025bf639f..0000000000 --- a/scylla/src/transport/mod.rs +++ /dev/null @@ -1,38 +0,0 @@ -pub(crate) mod caching_session; -mod cluster; -pub(crate) mod connection; -mod connection_pool; -pub mod downgrading_consistency_retry_policy; -pub mod execution_profile; -pub mod host_filter; -pub mod iterator; -pub mod load_balancing; -pub mod locator; -pub(crate) mod metrics; -mod node; -pub mod partitioner; -pub mod query_result; -pub mod retry_policy; -pub mod session; -pub mod session_builder; -pub mod speculative_execution; -pub mod topology; - -pub use crate::frame::{Authenticator, Compression}; -pub use execution_profile::ExecutionProfile; -pub use scylla_cql::errors; - -#[cfg(test)] -mod authenticate_test; -#[cfg(test)] -mod cql_collections_test; -#[cfg(test)] -mod session_test; - -#[cfg(test)] -mod cql_types_test; -#[cfg(test)] -mod cql_value_test; - -pub use cluster::ClusterData; -pub use node::{KnownNode, Node, NodeAddr, NodeRef}; diff --git a/scylla/src/utils/test_utils.rs b/scylla/src/utils/test_utils.rs index 7d3f079c5d..f672d1fd5b 100644 --- a/scylla/src/utils/test_utils.rs +++ b/scylla/src/utils/test_utils.rs @@ -1,5 +1,5 @@ #[cfg(test)] -use crate::transport::session_builder::{GenericSessionBuilder, SessionBuilderKind}; +use crate::session::session_builder::{GenericSessionBuilder, SessionBuilderKind}; #[cfg(test)] use crate::Session; #[cfg(test)] @@ -73,7 +73,7 @@ pub fn create_new_session_builder() -> GenericSessionBuilder GenericSessionBuilder( &'a self, query: &'a RoutingInfo, - cluster: &'a scylla::transport::ClusterData, - ) -> Option> { + cluster: &'a scylla::cluster::ClusterData, + ) -> Option> { self.routing_info_tx .send(OwnedRoutingInfo::from(query.clone())) .unwrap(); @@ -384,7 +384,7 @@ impl LoadBalancingPolicy for RoutingInfoReportingWrapper { fn fallback<'a>( &'a self, query: &'a RoutingInfo, - cluster: &'a scylla::transport::ClusterData, + cluster: &'a scylla::cluster::ClusterData, ) -> scylla::load_balancing::FallbackPlan<'a> { self.routing_info_tx .send(OwnedRoutingInfo::from(query.clone())) diff --git a/scylla/tests/integration/execution_profiles.rs b/scylla/tests/integration/execution_profiles.rs index 119487a609..3fe66b8fea 100644 --- a/scylla/tests/integration/execution_profiles.rs +++ b/scylla/tests/integration/execution_profiles.rs @@ -5,15 +5,15 @@ use crate::utils::test_with_3_node_cluster; use assert_matches::assert_matches; use scylla::batch::BatchStatement; use scylla::batch::{Batch, BatchType}; +use scylla::cluster::NodeRef; use scylla::query::Query; use scylla::statement::SerialConsistency; -use scylla::transport::NodeRef; use scylla::{ + cluster::ClusterData, + execution::retries::{RetryPolicy, RetrySession}, load_balancing::{LoadBalancingPolicy, RoutingInfo}, - retry_policy::{RetryPolicy, RetrySession}, speculative_execution::SpeculativeExecutionPolicy, test_utils::unique_keyspace_name, - transport::ClusterData, ExecutionProfile, SessionBuilder, }; use scylla_cql::Consistency; @@ -72,7 +72,7 @@ impl LoadBalancingPolicy for BoundToPredefinedNodePolicy { _query: &RoutingInfo, _latency: std::time::Duration, _node: NodeRef<'_>, - _error: &scylla_cql::errors::QueryError, + _error: &scylla::execution::errors::QueryError, ) { } @@ -82,7 +82,7 @@ impl LoadBalancingPolicy for BoundToPredefinedNodePolicy { } impl RetryPolicy for BoundToPredefinedNodePolicy { - fn new_session(&self) -> Box { + fn new_session(&self) -> Box { self.report_node(Report::RetryPolicy); Box::new(self.clone()) } @@ -95,10 +95,10 @@ impl RetryPolicy for BoundToPredefinedNodePolicy { impl RetrySession for BoundToPredefinedNodePolicy { fn decide_should_retry( &mut self, - query_info: scylla::retry_policy::QueryInfo, - ) -> scylla::retry_policy::RetryDecision { + query_info: scylla::execution::retries::QueryInfo, + ) -> scylla::execution::retries::RetryDecision { self.report_consistency(query_info.consistency); - scylla::retry_policy::RetryDecision::DontRetry + scylla::execution::retries::RetryDecision::DontRetry } fn reset(&mut self) {} diff --git a/scylla/tests/integration/hygiene.rs b/scylla/tests/integration/hygiene.rs index 6195bb0256..c0f0c449f7 100644 --- a/scylla/tests/integration/hygiene.rs +++ b/scylla/tests/integration/hygiene.rs @@ -19,9 +19,8 @@ macro_rules! test_crate { } #[test] fn test_rename() { + use _scylla::_macro_internal::{CqlValue, Value, ValueList}; use _scylla::cql_to_rust::{FromCqlVal, FromRow}; - use _scylla::frame::response::result::CqlValue; - use _scylla::frame::value::{Value, ValueList}; pub fn derived() where diff --git a/scylla/tests/integration/lwt_optimisation.rs b/scylla/tests/integration/lwt_optimisation.rs index 19ed45bec3..ceefad6eac 100644 --- a/scylla/tests/integration/lwt_optimisation.rs +++ b/scylla/tests/integration/lwt_optimisation.rs @@ -1,8 +1,7 @@ use crate::utils::test_with_3_node_cluster; -use scylla::frame::types; -use scylla::retry_policy::FallthroughRetryPolicy; -use scylla::transport::session::Session; -use scylla::{frame::protocol_features::ProtocolFeatures, test_utils::unique_keyspace_name}; +use scylla::execution::retries::FallthroughRetryPolicy; +use scylla::session::Session; +use scylla::test_utils::{types, unique_keyspace_name, ProtocolFeatures}; use scylla::{ExecutionProfile, SessionBuilder}; use std::sync::Arc; use tokio::sync::mpsc; diff --git a/scylla/tests/integration/new_session.rs b/scylla/tests/integration/new_session.rs index fc6490bb07..269ffb4200 100644 --- a/scylla/tests/integration/new_session.rs +++ b/scylla/tests/integration/new_session.rs @@ -1,6 +1,6 @@ use assert_matches::assert_matches; +use scylla::execution::errors::NewSessionError; use scylla::SessionBuilder; -use scylla_cql::errors::NewSessionError; #[cfg(not(scylla_cloud_tests))] #[tokio::test] diff --git a/scylla/tests/integration/retries.rs b/scylla/tests/integration/retries.rs index 04724ab2c7..974c71473d 100644 --- a/scylla/tests/integration/retries.rs +++ b/scylla/tests/integration/retries.rs @@ -1,7 +1,7 @@ use crate::utils::test_with_3_node_cluster; -use scylla::retry_policy::FallthroughRetryPolicy; +use scylla::execution::retries::FallthroughRetryPolicy; +use scylla::session::Session; use scylla::speculative_execution::SimpleSpeculativeExecutionPolicy; -use scylla::transport::session::Session; use scylla::ExecutionProfile; use scylla::SessionBuilder; use scylla::{query::Query, test_utils::unique_keyspace_name}; diff --git a/scylla/tests/integration/utils.rs b/scylla/tests/integration/utils.rs index f32ffa2764..43bb9677e9 100644 --- a/scylla/tests/integration/utils.rs +++ b/scylla/tests/integration/utils.rs @@ -22,8 +22,8 @@ impl LoadBalancingPolicy for FixedOrderLoadBalancer { fn pick<'a>( &'a self, _info: &'a scylla::load_balancing::RoutingInfo, - cluster: &'a scylla::transport::ClusterData, - ) -> Option> { + cluster: &'a scylla::cluster::ClusterData, + ) -> Option> { cluster .get_nodes_info() .iter() @@ -34,7 +34,7 @@ impl LoadBalancingPolicy for FixedOrderLoadBalancer { fn fallback<'a>( &'a self, _info: &'a scylla::load_balancing::RoutingInfo, - cluster: &'a scylla::transport::ClusterData, + cluster: &'a scylla::cluster::ClusterData, ) -> scylla::load_balancing::FallbackPlan<'a> { Box::new( cluster @@ -48,7 +48,7 @@ impl LoadBalancingPolicy for FixedOrderLoadBalancer { &self, _: &scylla::load_balancing::RoutingInfo, _: std::time::Duration, - _: scylla::transport::NodeRef<'_>, + _: scylla::cluster::NodeRef<'_>, ) { } @@ -56,8 +56,8 @@ impl LoadBalancingPolicy for FixedOrderLoadBalancer { &self, _: &scylla::load_balancing::RoutingInfo, _: std::time::Duration, - _: scylla::transport::NodeRef<'_>, - _: &scylla_cql::errors::QueryError, + _: scylla::cluster::NodeRef<'_>, + _: &scylla::execution::errors::QueryError, ) { }