Repository: scylladb/scylla-rust-driver
Pull request #792 (2023-08-21 10:42:21)

codewide: Allow setting Serial,LocalSerial for Consistency
# Motivation
See #736.

# What's done
- `Consistency` is extended with `Serial` and `LocalSerial` to support reading using Paxos (SELECT statements),
- `LegacyConsistency` is removed altogether,
- a test is added that performs such a Paxos read.

Fixes: #736

## Pre-review checklist

- [x] I have split my patch into logically separate commits.
- [x] All commit messages clearly explain what they change and why.
- [x] I added relevant tests for new features and bug fixes.
- [x] All commits compile, pass static checks and pass test.
- [x] PR description sums up the changes and reasons why they should be introduced.
- ~~[ ] I have provided docstrings for the public items that I want to introduce.~~
- ~~[ ] I have adjusted the documentation in `./docs/source/`.~~
- [x] I added appropriate `Fixes:` annotations to PR description.



Patch:
diff --git a/scylla-cql/src/errors.rs b/scylla-cql/src/errors.rs
index 6456f126..40587cfe 100644
--- a/scylla-cql/src/errors.rs
+++ b/scylla-cql/src/errors.rs
@@ -2,8 +2,8 @@
 
 use crate::frame::frame_errors::{FrameError, ParseError};
 use crate::frame::protocol_features::ProtocolFeatures;
-use crate::frame::types::LegacyConsistency;
 use crate::frame::value::SerializeValuesError;
+use crate::Consistency;
 use bytes::Bytes;
 use std::io::ErrorKind;
 use std::sync::Arc;
@@ -108,7 +108,7 @@ pub enum DbError {
     )]
     Unavailable {
         /// Consistency level of the query
-        consistency: LegacyConsistency,
+        consistency: Consistency,
         /// Number of nodes required to be alive to satisfy required consistency level
         required: i32,
         /// Found number of active nodes
@@ -132,7 +132,7 @@ pub enum DbError {
             (consistency: {consistency}, received: {received}, required: {required}, data_present: {data_present})")]
     ReadTimeout {
         /// Consistency level of the query
-        consistency: LegacyConsistency,
+        consistency: Consistency,
         /// Number of nodes that responded to the read request
         received: i32,
         /// Number of nodes required to respond to satisfy required consistency level
@@ -146,7 +146,7 @@ pub enum DbError {
             (consistency: {consistency}, received: {received}, required: {required}, write_type: {write_type})")]
     WriteTimeout {
         /// Consistency level of the query
-        consistency: LegacyConsistency,
+        consistency: Consistency,
         /// Number of nodes that responded to the write request
         received: i32,
         /// Number of nodes required to respond to satisfy required consistency level
@@ -163,7 +163,7 @@ pub enum DbError {
     )]
     ReadFailure {
         /// Consistency level of the query
-        consistency: LegacyConsistency,
+        consistency: Consistency,
         /// Number of nodes that responded to the read request
         received: i32,
         /// Number of nodes required to respond to satisfy required consistency level
@@ -182,7 +182,7 @@ pub enum DbError {
     )]
     WriteFailure {
         /// Consistency level of the query
-        consistency: LegacyConsistency,
+        consistency: Consistency,
         /// Number of nodes that responded to the read request
         received: i32,
         /// Number of nodes required to respond to satisfy required consistency level
@@ -550,7 +550,7 @@ impl WriteType {
 #[cfg(test)]
 mod tests {
     use super::{DbError, QueryError, WriteType};
-    use crate::frame::types::{Consistency, LegacyConsistency};
+    use crate::frame::types::Consistency;
 
     #[test]
     fn write_type_from_str() {
@@ -581,7 +581,7 @@ mod tests {
     fn dberror_full_info() {
         // Test that DbError::Unavailable is displayed correctly
         let db_error = DbError::Unavailable {
-            consistency: LegacyConsistency::Regular(Consistency::Three),
+            consistency: Consistency::Three,
             required: 3,
             alive: 2,
         };
diff --git a/scylla-cql/src/frame/request/batch.rs b/scylla-cql/src/frame/request/batch.rs
index 91326a51..3c0bad39 100644
--- a/scylla-cql/src/frame/request/batch.rs
+++ b/scylla-cql/src/frame/request/batch.rs
@@ -4,7 +4,7 @@ use std::{borrow::Cow, convert::TryInto};
 use crate::frame::{
     frame_errors::ParseError,
     request::{RequestOpcode, SerializableRequest},
-    types,
+    types::{self, SerialConsistency},
     value::{BatchValues, BatchValuesIterator, SerializedValues},
 };
 
@@ -202,13 +202,7 @@ impl<'b> DeserializableRequest for Batch<'b, BatchStatement<'b>, Vec<SerializedV
             })
             .collect::<Result<Vec<_>, ParseError>>()?;
 
-        let consistency = match types::read_consistency(buf)? {
-            types::LegacyConsistency::Regular(reg) => Ok(reg),
-            types::LegacyConsistency::Serial(ser) => Err(ParseError::BadIncomingData(format!(
-                "Expected regular Consistency, got SerialConsistency {}",
-                ser
-            ))),
-        }?;
+        let consistency = types::read_consistency(buf)?;
 
         let flags = buf.get_u8();
         let unknown_flags = flags & (!ALL_FLAGS);
@@ -224,15 +218,15 @@ impl<'b> DeserializableRequest for Batch<'b, BatchStatement<'b>, Vec<SerializedV
         let serial_consistency = serial_consistency_flag
             .then(|| types::read_consistency(buf))
             .transpose()?
-            .map(|legacy_consistency| match legacy_consistency {
-                types::LegacyConsistency::Regular(reg) => {
-                    Err(ParseError::BadIncomingData(format!(
+            .map(
+                |consistency| match SerialConsistency::try_from(consistency) {
+                    Ok(serial_consistency) => Ok(serial_consistency),
+                    Err(_) => Err(ParseError::BadIncomingData(format!(
                         "Expected SerialConsistency, got regular Consistency {}",
-                        reg
-                    )))
-                }
-                types::LegacyConsistency::Serial(ser) => Ok(ser),
-            })
+                        consistency
+                    ))),
+                },
+            )
             .transpose()?;
 
         let timestamp = default_timestamp_flag
diff --git a/scylla-cql/src/frame/request/mod.rs b/scylla-cql/src/frame/request/mod.rs
index 8202ccde..cd41d6bc 100644
--- a/scylla-cql/src/frame/request/mod.rs
+++ b/scylla-cql/src/frame/request/mod.rs
@@ -112,7 +112,7 @@ mod tests {
                 query::{Query, QueryParameters},
                 DeserializableRequest, SerializableRequest,
             },
-            types::{self, LegacyConsistency, SerialConsistency},
+            types::{self, SerialConsistency},
             value::SerializedValues,
         },
         Consistency,
@@ -236,10 +236,7 @@ mod tests {
 
             // Now buf_ptr points at consistency.
             let consistency = types::read_consistency(&mut buf_ptr).unwrap();
-            assert_eq!(
-                consistency,
-                LegacyConsistency::Regular(Consistency::default())
-            );
+            assert_eq!(consistency, Consistency::default());
 
             // Now buf_ptr points at flags, but it is immutable. Get mutable reference into the buffer.
             let flags_idx = buf.len() - buf_ptr.len();
diff --git a/scylla-cql/src/frame/request/query.rs b/scylla-cql/src/frame/request/query.rs
index 1b5a52ed..ff0b0cc8 100644
--- a/scylla-cql/src/frame/request/query.rs
+++ b/scylla-cql/src/frame/request/query.rs
@@ -1,6 +1,6 @@
 use std::borrow::Cow;
 
-use crate::frame::frame_errors::ParseError;
+use crate::frame::{frame_errors::ParseError, types::SerialConsistency};
 use bytes::{Buf, BufMut, Bytes};
 
 use crate::{
@@ -134,13 +134,7 @@ impl QueryParameters<'_> {
 
 impl<'q> QueryParameters<'q> {
     pub fn deserialize(buf: &mut &[u8]) -> Result<Self, ParseError> {
-        let consistency = match types::read_consistency(buf)? {
-            types::LegacyConsistency::Regular(reg) => Ok(reg),
-            types::LegacyConsistency::Serial(ser) => Err(ParseError::BadIncomingData(format!(
-                "Expected regular Consistency, got SerialConsistency {}",
-                ser
-            ))),
-        }?;
+        let consistency = types::read_consistency(buf)?;
 
         let flags = buf.get_u8();
         let unknown_flags = flags & (!ALL_FLAGS);
@@ -169,19 +163,19 @@ impl<'q> QueryParameters<'q> {
         } else {
             None
         };
-        let serial_consistency = if serial_consistency_flag {
-            match types::read_consistency(buf)? {
-                types::LegacyConsistency::Regular(reg) => {
-                    return Err(ParseError::BadIncomingData(format!(
+        let serial_consistency = serial_consistency_flag
+            .then(|| types::read_consistency(buf))
+            .transpose()?
+            .map(
+                |consistency| match SerialConsistency::try_from(consistency) {
+                    Ok(serial_consistency) => Ok(serial_consistency),
+                    Err(_) => Err(ParseError::BadIncomingData(format!(
                         "Expected SerialConsistency, got regular Consistency {}",
-                        reg
-                    )))
-                }
-                types::LegacyConsistency::Serial(ser) => Some(ser),
-            }
-        } else {
-            None
-        };
+                        consistency
+                    ))),
+                },
+            )
+            .transpose()?;
         let timestamp = if default_timestamp_flag {
             Some(types::read_long(buf)?)
         } else {
diff --git a/scylla-cql/src/frame/response/error.rs b/scylla-cql/src/frame/response/error.rs
index 8f438243..e99d5686 100644
--- a/scylla-cql/src/frame/response/error.rs
+++ b/scylla-cql/src/frame/response/error.rs
@@ -92,7 +92,6 @@ mod tests {
     use super::Error;
     use crate::errors::{DbError, OperationType, WriteType};
     use crate::frame::protocol_features::ProtocolFeatures;
-    use crate::frame::types::LegacyConsistency;
     use crate::Consistency;
     use bytes::Bytes;
     use std::convert::TryInto;
@@ -151,7 +150,7 @@ mod tests {
         assert_eq!(
             error.error,
             DbError::Unavailable {
-                consistency: LegacyConsistency::Regular(Consistency::One),
+                consistency: Consistency::One,
                 required: 2,
                 alive: 3,
             }
@@ -178,7 +177,7 @@ mod tests {
         assert_eq!(
             error.error,
             DbError::WriteTimeout {
-                consistency: LegacyConsistency::Regular(Consistency::Quorum),
+                consistency: Consistency::Quorum,
                 received: -5, // Allow negative values when they don't make sense, it's better than crashing with ProtocolError
                 required: 100,
                 write_type: WriteType::Simple,
@@ -202,7 +201,7 @@ mod tests {
         assert_eq!(
             error.error,
             DbError::ReadTimeout {
-                consistency: LegacyConsistency::Regular(Consistency::Two),
+                consistency: Consistency::Two,
                 received: 8,
                 required: 32,
                 data_present: false,
@@ -227,7 +226,7 @@ mod tests {
         assert_eq!(
             error.error,
             DbError::ReadFailure {
-                consistency: LegacyConsistency::Regular(Consistency::Three),
+                consistency: Consistency::Three,
                 received: 4,
                 required: 5,
                 numfailures: 6,
@@ -299,7 +298,7 @@ mod tests {
         assert_eq!(
             error.error,
             DbError::WriteFailure {
-                consistency: LegacyConsistency::Regular(Consistency::Any),
+                consistency: Consistency::Any,
                 received: 2,
                 required: 4,
                 numfailures: 8,
diff --git a/scylla-cql/src/frame/types.rs b/scylla-cql/src/frame/types.rs
index 858db35f..71b0f782 100644
--- a/scylla-cql/src/frame/types.rs
+++ b/scylla-cql/src/frame/types.rs
@@ -10,6 +10,7 @@ use std::convert::TryInto;
 use std::net::IpAddr;
 use std::net::SocketAddr;
 use std::str;
+use thiserror::Error;
 use uuid::Uuid;
 
 #[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, TryFromPrimitive)]
@@ -27,6 +28,11 @@ pub enum Consistency {
     LocalQuorum = 0x0006,
     EachQuorum = 0x0007,
     LocalOne = 0x000A,
+
+    // Apparently, Consistency can be set to Serial or LocalSerial in SELECT statements
+    // to make them use Paxos.
+    Serial = 0x0008,
+    LocalSerial = 0x0009,
 }
 
 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, TryFromPrimitive)]
@@ -38,13 +44,34 @@ pub enum SerialConsistency {
     LocalSerial = 0x0009,
 }
 
-// LegacyConsistency exists, because Scylla may return a SerialConsistency value
-// as Consistency when returning certain error types - the distinction between
-// Consistency and SerialConsistency is not really a thing in CQL.
-#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
-pub enum LegacyConsistency {
-    Regular(Consistency),
-    Serial(SerialConsistency),
+impl Consistency {
+    pub fn is_serial(&self) -> bool {
+        matches!(self, Consistency::Serial | Consistency::LocalSerial)
+    }
+}
+
+#[derive(Debug, Error)]
+#[error("Expected Consistency Serial or LocalSerial, got: {0}")]
+pub struct NonSerialConsistencyError(Consistency);
+
+impl TryFrom<Consistency> for SerialConsistency {
+    type Error = NonSerialConsistencyError;
+
+    fn try_from(c: Consistency) -> Result<Self, Self::Error> {
+        match c {
+            Consistency::Any
+            | Consistency::One
+            | Consistency::Two
+            | Consistency::Three
+            | Consistency::Quorum
+            | Consistency::All
+            | Consistency::LocalQuorum
+            | Consistency::EachQuorum
+            | Consistency::LocalOne => Err(NonSerialConsistencyError(c)),
+            Consistency::Serial => Ok(SerialConsistency::Serial),
+            Consistency::LocalSerial => Ok(SerialConsistency::LocalSerial),
+        }
+    }
 }
 
 impl std::fmt::Display for Consistency {
@@ -59,15 +86,6 @@ impl std::fmt::Display for SerialConsistency {
     }
 }
 
-impl std::fmt::Display for LegacyConsistency {
-    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
-        match self {
-            Self::Regular(c) => c.fmt(f),
-            Self::Serial(c) => c.fmt(f),
-        }
-    }
-}
-
 impl From<std::num::TryFromIntError> for ParseError {
     fn from(_err: std::num::TryFromIntError) -> Self {
         ParseError::BadIncomingData("Integer conversion out of range".to_string())
@@ -439,18 +457,10 @@ fn type_uuid() {
     assert_eq!(u, u2);
 }
 
-pub fn read_consistency(buf: &mut &[u8]) -> Result<LegacyConsistency, ParseError> {
+pub fn read_consistency(buf: &mut &[u8]) -> Result<Consistency, ParseError> {
     let raw = read_short(buf)?;
-    let parsed = match Consistency::try_from(raw) {
-        Ok(c) => LegacyConsistency::Regular(c),
-        Err(_) => {
-            let parsed_serial = SerialConsistency::try_from(raw).map_err(|_| {
-                ParseError::BadIncomingData(format!("unknown consistency: {}", raw))
-            })?;
-            LegacyConsistency::Serial(parsed_serial)
-        }
-    };
-    Ok(parsed)
+    Consistency::try_from(raw)
+        .map_err(|_| ParseError::BadIncomingData(format!("unknown consistency: {}", raw)))
 }
 
 pub fn write_consistency(c: Consistency, buf: &mut impl BufMut) {
@@ -467,7 +477,7 @@ fn type_consistency() {
     let mut buf = Vec::new();
     write_consistency(c, &mut buf);
     let c2 = read_consistency(&mut &*buf).unwrap();
-    assert_eq!(LegacyConsistency::Regular(c), c2);
+    assert_eq!(c, c2);
 
     let c: i16 = 0x1234;
     buf.clear();
diff --git a/scylla-proxy/src/actions.rs b/scylla-proxy/src/actions.rs
index 4e617bfb..ef0945ba 100644
--- a/scylla-proxy/src/actions.rs
+++ b/scylla-proxy/src/actions.rs
@@ -9,7 +9,6 @@ use crate::frame::{
 };
 use scylla_cql::{
     errors::{DbError, WriteType},
-    frame::types::LegacyConsistency,
     Consistency,
 };
 
@@ -427,7 +426,7 @@ impl ExampleDbErrors {
     }
     pub fn unavailable() -> DbError {
         DbError::Unavailable {
-            consistency: LegacyConsistency::Regular(Consistency::One),
+            consistency: Consistency::One,
             required: 2,
             alive: 1,
         }
@@ -443,7 +442,7 @@ impl ExampleDbErrors {
     }
     pub fn read_timeout() -> DbError {
         DbError::ReadTimeout {
-            consistency: LegacyConsistency::Regular(Consistency::One),
+            consistency: Consistency::One,
             received: 2,
             required: 3,
             data_present: true,
@@ -451,7 +450,7 @@ impl ExampleDbErrors {
     }
     pub fn write_timeout() -> DbError {
         DbError::WriteTimeout {
-            consistency: LegacyConsistency::Regular(Consistency::One),
+            consistency: Consistency::One,
             received: 2,
             required: 3,
             write_type: WriteType::UnloggedBatch,
@@ -459,7 +458,7 @@ impl ExampleDbErrors {
     }
     pub fn read_failure() -> DbError {
         DbError::ReadFailure {
-            consistency: LegacyConsistency::Regular(Consistency::One),
+            consistency: Consistency::One,
             received: 2,
             required: 3,
             data_present: true,
@@ -468,7 +467,7 @@ impl ExampleDbErrors {
     }
     pub fn write_failure() -> DbError {
         DbError::WriteFailure {
-            consistency: LegacyConsistency::Regular(Consistency::One),
+            consistency: Consistency::One,
             received: 2,
             required: 3,
             write_type: WriteType::UnloggedBatch,
diff --git a/scylla-proxy/src/frame.rs b/scylla-proxy/src/frame.rs
index cf61a6b0..5aab0340 100644
--- a/scylla-proxy/src/frame.rs
+++ b/scylla-proxy/src/frame.rs
@@ -6,8 +6,6 @@ use scylla_cql::frame::protocol_features::ProtocolFeatures;
 use scylla_cql::frame::request::Request;
 pub use scylla_cql::frame::request::RequestOpcode;
 pub use scylla_cql::frame::response::ResponseOpcode;
-use scylla_cql::frame::types::LegacyConsistency;
-use scylla_cql::Consistency;
 use scylla_cql::{errors::DbError, frame::types};
 use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
 
@@ -147,19 +145,13 @@ impl ResponseFrame {
 }
 
 fn serialize_error_specific_fields(buf: &mut BytesMut, error: DbError) -> Result<(), ParseError> {
-    fn unwrap_cl(c: LegacyConsistency) -> Consistency {
-        match c {
-            types::LegacyConsistency::Regular(c) => c,
-            types::LegacyConsistency::Serial(_) => unreachable!(),
-        }
-    }
     match error {
         DbError::Unavailable {
             consistency,
             required,
             alive,
         } => {
-            types::write_consistency(unwrap_cl(consistency), buf);
+            types::write_consistency(consistency, buf);
             types::write_int(required, buf);
             types::write_int(alive, buf);
         }
@@ -169,7 +161,7 @@ fn serialize_error_specific_fields(buf: &mut BytesMut, error: DbError) -> Result
             required,
             write_type,
         } => {
-            types::write_consistency(unwrap_cl(consistency), buf);
+            types::write_consistency(consistency, buf);
             types::write_int(received, buf);
             types::write_int(required, buf);
             types::write_string(write_type.as_str(), buf)?;
@@ -180,7 +172,7 @@ fn serialize_error_specific_fields(buf: &mut BytesMut, error: DbError) -> Result
             required,
             data_present,
         } => {
-            types::write_consistency(unwrap_cl(consistency), buf);
+            types::write_consistency(consistency, buf);
             types::write_int(received, buf);
             types::write_int(required, buf);
             buf.put_u8(u8::from(data_present));
@@ -192,7 +184,7 @@ fn serialize_error_specific_fields(buf: &mut BytesMut, error: DbError) -> Result
             numfailures,
             data_present,
         } => {
-            types::write_consistency(unwrap_cl(consistency), buf);
+            types::write_consistency(consistency, buf);
             types::write_int(received, buf);
             types::write_int(required, buf);
             types::write_int(numfailures, buf);
@@ -205,7 +197,7 @@ fn serialize_error_specific_fields(buf: &mut BytesMut, error: DbError) -> Result
             numfailures,
             write_type,
         } => {
-            types::write_consistency(unwrap_cl(consistency), buf);
+            types::write_consistency(consistency, buf);
             types::write_int(received, buf);
             types::write_int(required, buf);
             types::write_int(numfailures, buf);
diff --git a/scylla/src/history.rs b/scylla/src/history.rs
index 4767690e..6b1bd1a9 100644
--- a/scylla/src/history.rs
+++ b/scylla/src/history.rs
@@ -468,7 +468,6 @@ mod tests {
     use futures::StreamExt;
     use scylla_cql::{
         errors::{DbError, QueryError},
-        frame::types::LegacyConsistency,
         Consistency,
     };
 
@@ -576,7 +575,7 @@ mod tests {
     fn unavailable_error() -> QueryError {
         QueryError::DbError(
             DbError::Unavailable {
-                consistency: LegacyConsistency::Regular(Consistency::Quorum),
+                consistency: Consistency::Quorum,
                 required: 2,
                 alive: 1,
             },
diff --git a/scylla/src/transport/downgrading_consistency_retry_policy.rs b/scylla/src/transport/downgrading_consistency_retry_policy.rs
index 879e6b10..f55c25bc 100644
--- a/scylla/src/transport/downgrading_consistency_retry_policy.rs
+++ b/scylla/src/transport/downgrading_consistency_retry_policy.rs
@@ -1,6 +1,5 @@
 use scylla_cql::{
     errors::{DbError, QueryError, WriteType},
-    frame::types::LegacyConsistency,
     Consistency,
 };
 use tracing::debug;
@@ -56,7 +55,7 @@ impl Default for DowngradingConsistencyRetrySession {
 impl RetrySession for DowngradingConsistencyRetrySession {
     fn decide_should_retry(&mut self, query_info: QueryInfo) -> RetryDecision {
         let cl = match query_info.consistency {
-            LegacyConsistency::Serial(_) => {
+            Consistency::Serial | Consistency::LocalSerial => {
                 return match query_info.error {
                     QueryError::DbError(DbError::Unavailable { .. }, _) => {
                         // JAVA-764: if the requested consistency level is serial, it means that the operation failed at
@@ -67,7 +66,7 @@ impl RetrySession for DowngradingConsistencyRetrySession {
                     _ => RetryDecision::DontRetry,
                 };
             }
-            LegacyConsistency::Regular(cl) => cl,
+            cl => cl,
         };
 
         fn max_likely_to_work_cl(known_ok: i32, previous_cl: Consistency) -> RetryDecision {
@@ -209,7 +208,7 @@ mod tests {
         QueryInfo {
             error,
             is_idempotent,
-            consistency: LegacyConsistency::Regular(cl),
+            consistency: cl,
         }
     }
 
@@ -246,14 +245,14 @@ mod tests {
             DbError::Unauthorized,
             DbError::ConfigError,
             DbError::ReadFailure {
-                consistency: LegacyConsistency::Regular(Consistency::Two),
+                consistency: Consistency::Two,
                 received: 1,
                 required: 2,
                 numfailures: 1,
                 data_present: false,
             },
             DbError::WriteFailure {
-                consistency: LegacyConsistency::Regular(Consistency::Two),
+                consistency: Consistency::Two,
                 received: 1,
                 required: 2,
                 numfailures: 1,
@@ -361,7 +360,7 @@ mod tests {
         let alive = 1;
         let error = QueryError::DbError(
             DbError::Unavailable {
-                consistency: LegacyConsistency::Regular(Consistency::Two),
+                consistency: Consistency::Two,
                 required: 2,
                 alive,
             },
@@ -399,7 +398,7 @@ mod tests {
         // Enough responses and data_present == false - coordinator received only checksums
         let enough_responses_no_data = QueryError::DbError(
             DbError::ReadTimeout {
-                consistency: LegacyConsistency::Regular(Consistency::Two),
+                consistency: Consistency::Two,
                 received: 2,
                 required: 2,
                 data_present: false,
@@ -450,7 +449,7 @@ mod tests {
         // waiting for read-repair acknowledgement.
         let enough_responses_with_data = QueryError::DbError(
             DbError::ReadTimeout {
-                consistency: LegacyConsistency::Regular(Consistency::Two),
+                consistency: Consistency::Two,
                 received: 2,
                 required: 2,
                 data_present: true,
@@ -486,7 +485,7 @@ mod tests {
         let received = 1;
         let not_enough_responses_with_data = QueryError::DbError(
             DbError::ReadTimeout {
-                consistency: LegacyConsistency::Regular(Consistency::Two),
+                consistency: Consistency::Two,
                 received,
                 required: 2,
                 data_present: true,
@@ -547,7 +546,7 @@ mod tests {
             // WriteType == BatchLog
             let write_type_batchlog = QueryError::DbError(
                 DbError::WriteTimeout {
-                    consistency: LegacyConsistency::Regular(Consistency::Two),
+                    consistency: Consistency::Two,
                     received,
                     required,
                     write_type: WriteType::BatchLog,
@@ -590,7 +589,7 @@ mod tests {
             // WriteType == UnloggedBatch
             let write_type_unlogged_batch = QueryError::DbError(
                 DbError::WriteTimeout {
-                    consistency: LegacyConsistency::Regular(Consistency::Two),
+                    consistency: Consistency::Two,
                     received,
                     required,
                     write_type: WriteType::UnloggedBatch,
@@ -633,7 +632,7 @@ mod tests {
             // WriteType == other
             let write_type_other = QueryError::DbError(
                 DbError::WriteTimeout {
-                    consistency: LegacyConsistency::Regular(Consistency::Two),
+                    consistency: Consistency::Two,
                     received,
                     required,
                     write_type: WriteType::Simple,
diff --git a/scylla/src/transport/iterator.rs b/scylla/src/transport/iterator.rs
index 8c2e554a..a9f5a7ec 100644
--- a/scylla/src/transport/iterator.rs
+++ b/scylla/src/transport/iterator.rs
@@ -22,7 +22,6 @@ use super::execution_profile::ExecutionProfileInner;
 use super::session::RequestSpan;
 use crate::cql_to_rust::{FromRow, FromRowError};
 
-use crate::frame::types::LegacyConsistency;
 use crate::frame::{
     response::{
         result,
@@ -572,7 +571,7 @@ where
                 let query_info = QueryInfo {
                     error: &last_error,
                     is_idempotent: self.query_is_idempotent,
-                    consistency: LegacyConsistency::Regular(self.query_consistency),
+                    consistency: self.query_consistency,
                 };
 
                 let retry_decision = self.retry_session.decide_should_retry(query_info);
diff --git a/scylla/src/transport/retry_policy.rs b/scylla/src/transport/retry_policy.rs
index 1fb0279e..ee0c50aa 100644
--- a/scylla/src/transport/retry_policy.rs
+++ b/scylla/src/transport/retry_policy.rs
@@ -2,7 +2,7 @@
 //! To decide when to retry a query the `Session` can use any object which implements
 //! the `RetryPolicy` trait
 
-use crate::frame::types::{Consistency, LegacyConsistency};
+use crate::frame::types::Consistency;
 use crate::transport::errors::{DbError, QueryError, WriteType};
 
 /// Information about a failed query
@@ -14,7 +14,7 @@ pub struct QueryInfo<'a> {
     /// If set to `false` it is unknown whether it is idempotent
     pub is_idempotent: bool,
     /// Consistency with which the query failed
-    pub consistency: LegacyConsistency,
+    pub consistency: Consistency,
 }
 
 #[derive(Clone, Debug, PartialEq, Eq)]
@@ -136,7 +136,7 @@ impl Default for DefaultRetrySession {
 
 impl RetrySession for DefaultRetrySession {
     fn decide_should_retry(&mut self, query_info: QueryInfo) -> RetryDecision {
-        if let LegacyConsistency::Serial(_) = query_info.consistency {
+        if query_info.consistency.is_serial() {
             return RetryDecision::DontRetry;
         };
         match query_info.error {
@@ -219,7 +219,6 @@ impl RetrySession for DefaultRetrySession {
 #[cfg(test)]
 mod tests {
     use super::{DefaultRetryPolicy, QueryInfo, RetryDecision, RetryPolicy};
-    use crate::frame::types::LegacyConsistency;
     use crate::statement::Consistency;
     use crate::transport::errors::{BadQuery, DbError, QueryError, WriteType};
     use bytes::Bytes;
@@ -230,7 +229,7 @@ mod tests {
         QueryInfo {
             error,
             is_idempotent,
-            consistency: LegacyConsistency::Regular(Consistency::One),
+            consistency: Consistency::One,
         }
     }
 
@@ -267,14 +266,14 @@ mod tests {
             DbError::Unauthorized,
             DbError::ConfigError,
             DbError::ReadFailure {
-                consistency: LegacyConsistency::Regular(Consistency::Two),
+                consistency: Consistency::Two,
                 received: 2,
                 required: 1,
                 numfailures: 1,
                 data_present: false,
             },
             DbError::WriteFailure {
-                consistency: LegacyConsistency::Regular(Consistency::Two),
+                consistency: Consistency::Two,
                 received: 1,
                 required: 2,
                 numfailures: 1,
@@ -351,7 +350,7 @@ mod tests {
     fn default_unavailable() {
         let error = QueryError::DbError(
             DbError::Unavailable {
-                consistency: LegacyConsistency::Regular(Consistency::Two),
+                consistency: Consistency::Two,
                 required: 2,
                 alive: 1,
             },
@@ -385,7 +384,7 @@ mod tests {
         // Enough responses and data_present == false - coordinator received only checksums
         let enough_responses_no_data = QueryError::DbError(
             DbError::ReadTimeout {
-                consistency: LegacyConsistency::Regular(Consistency::Two),
+                consistency: Consistency::Two,
                 received: 2,
                 required: 2,
                 data_present: false,
@@ -419,7 +418,7 @@ mod tests {
         // waiting for read-repair acknowledgement.
         let enough_responses_with_data = QueryError::DbError(
             DbError::ReadTimeout {
-                consistency: LegacyConsistency::Regular(Consistency::Two),
+                consistency: Consistency::Two,
                 received: 2,
                 required: 2,
                 data_present: true,
@@ -444,7 +443,7 @@ mod tests {
         // Not enough responses, data_present == true
         let not_enough_responses_with_data = QueryError::DbError(
             DbError::ReadTimeout {
-                consistency: LegacyConsistency::Regular(Consistency::Two),
+                consistency: Consistency::Two,
                 received: 1,
                 required: 2,
                 data_present: true,
@@ -473,7 +472,7 @@ mod tests {
         // WriteType == BatchLog
         let good_write_type = QueryError::DbError(
             DbError::WriteTimeout {
-                consistency: LegacyConsistency::Regular(Consistency::Two),
+                consistency: Consistency::Two,
                 received: 1,
                 required: 2,
                 write_type: WriteType::BatchLog,
@@ -502,7 +501,7 @@ mod tests {
         // WriteType != BatchLog
         let bad_write_type = QueryError::DbError(
             DbError::WriteTimeout {
-                consistency: LegacyConsistency::Regular(Consistency::Two),
+                consistency: Consistency::Two,
                 received: 4,
                 required: 2,
                 write_type: WriteType::Simple,
diff --git a/scylla/src/transport/session.rs b/scylla/src/transport/session.rs
index 3b48bbba..f1193f32 100644
--- a/scylla/src/transport/session.rs
+++ b/scylla/src/transport/session.rs
@@ -4,7 +4,6 @@
 #[cfg(feature = "cloud")]
 use crate::cloud::CloudConfig;
 
-use crate::frame::types::LegacyConsistency;
 use crate::history;
 use crate::history::HistoryListener;
 use crate::utils::pretty::{CommaSeparatedDisplayer, CqlValueDisplayer};
@@ -1767,11 +1766,9 @@ impl Session {
                 let query_info = QueryInfo {
                     error: the_error,
                     is_idempotent: context.is_idempotent,
-                    consistency: LegacyConsistency::Regular(
-                        context
-                            .consistency_set_on_statement
-                            .unwrap_or(execution_profile.consistency),
-                    ),
+                    consistency: context
+                        .consistency_set_on_statement
+                        .unwrap_or(execution_profile.consistency),
                 };
 
                 let retry_decision = context.retry_session.decide_should_retry(query_info);


Test patch:
diff --git a/scylla/tests/integration/consistency.rs b/scylla/tests/integration/consistency.rs
index 41c32c73..9eaabf03 100644
--- a/scylla/tests/integration/consistency.rs
+++ b/scylla/tests/integration/consistency.rs
@@ -452,3 +452,24 @@ async fn consistency_is_correctly_set_in_routing_info() {
     )
     .await;
 }
+
+// Performs a read using Paxos, by setting Consistency to Serial.
+// This not really checks that functionality works properly, but stays here
+// to ensure that it is even expressible to issue such query.
+// Before, Consistency did not contain serial variants, so it used to be impossible.
+#[tokio::test]
+#[ntest::timeout(60000)]
+#[cfg(not(scylla_cloud_tests))]
+async fn consistency_allows_for_paxos_selects() {
+    let uri = std::env::var("SCYLLA_URI").unwrap_or_else(|_| "127.0.0.1:9042".to_string());
+
+    let session = SessionBuilder::new()
+        .known_node(uri.as_str())
+        .build()
+        .await
+        .unwrap();
+
+    let mut query = Query::from("SELECT host_id FROM system.peers WHERE peer = '127.0.0.1'");
+    query.set_consistency(Consistency::Serial);
+    session.query(query, ()).await.unwrap();
+}
diff --git a/scylla/tests/integration/execution_profiles.rs b/scylla/tests/integration/execution_profiles.rs
index 93334106..119487a6 100644
--- a/scylla/tests/integration/execution_profiles.rs
+++ b/scylla/tests/integration/execution_profiles.rs
@@ -9,7 +9,6 @@ use scylla::query::Query;
 use scylla::statement::SerialConsistency;
 use scylla::transport::NodeRef;
 use scylla::{
-    frame::types::LegacyConsistency,
     load_balancing::{LoadBalancingPolicy, RoutingInfo},
     retry_policy::{RetryPolicy, RetrySession},
     speculative_execution::SpeculativeExecutionPolicy,
@@ -34,14 +33,14 @@ enum Report {
 #[derive(Debug, Clone)]
 struct BoundToPredefinedNodePolicy<const NODE: u8> {
     profile_reporter: mpsc::UnboundedSender<(Report, u8)>,
-    consistency_reporter: mpsc::UnboundedSender<LegacyConsistency>,
+    consistency_reporter: mpsc::UnboundedSender<Consistency>,
 }
 
 impl<const NODE: u8> BoundToPredefinedNodePolicy<NODE> {
     fn report_node(&self, report: Report) {
         self.profile_reporter.send((report, NODE)).unwrap();
     }
-    fn report_consistency(&self, c: LegacyConsistency) {
+    fn report_consistency(&self, c: Consistency) {
         self.consistency_reporter.send(c).unwrap();
     }
 }
@@ -260,55 +259,55 @@ async fn test_execution_profiles() {
         // Run non-LWT on default per-session execution profile
         session.query(query.clone(), &[]).await.unwrap_err();
         let report_consistency = consistency_rx.recv().await.unwrap();
-        assert_matches!(report_consistency, LegacyConsistency::Regular(Consistency::One));
+        assert_matches!(report_consistency, Consistency::One);
         consistency_rx.try_recv().unwrap_err();
 
         session.execute(&prepared, &[]).await.unwrap_err();
         let report_consistency = consistency_rx.recv().await.unwrap();
-        assert_matches!(report_consistency, LegacyConsistency::Regular(Consistency::One));
+        assert_matches!(report_consistency, Consistency::One);
         consistency_rx.try_recv().unwrap_err();
 
         session.batch(&batch, ((),)).await.unwrap_err();
         let report_consistency = consistency_rx.recv().await.unwrap();
-        assert_matches!(report_consistency, LegacyConsistency::Regular(Consistency::One));
+        assert_matches!(report_consistency, Consistency::One);
         consistency_rx.try_recv().unwrap_err();
 
         // Run on statement-specific execution profile
         query.set_execution_profile_handle(Some(profile2.clone().into_handle()));
         session.query(query.clone(), &[]).await.unwrap_err();
         let report_consistency = consistency_rx.recv().await.unwrap();
-        assert_matches!(report_consistency, LegacyConsistency::Regular(Consistency::Two));
+        assert_matches!(report_consistency, Consistency::Two);
         consistency_rx.try_recv().unwrap_err();
 
         prepared.set_execution_profile_handle(Some(profile2.clone().into_handle()));
         session.execute(&prepared, &[]).await.unwrap_err();
         let report_consistency = consistency_rx.recv().await.unwrap();
-        assert_matches!(report_consistency, LegacyConsistency::Regular(Consistency::Two));
+        assert_matches!(report_consistency, Consistency::Two);
         consistency_rx.try_recv().unwrap_err();
 
         batch.set_execution_profile_handle(Some(profile2.clone().into_handle()));
         session.batch(&batch, ((),)).await.unwrap_err();
         let report_consistency = consistency_rx.recv().await.unwrap();
-        assert_matches!(report_consistency, LegacyConsistency::Regular(Consistency::Two));
+        assert_matches!(report_consistency, Consistency::Two);
         consistency_rx.try_recv().unwrap_err();
 
         // Run with statement-set specific options
         query.set_consistency(Consistency::Three);
         session.query(query.clone(), &[]).await.unwrap_err();
         let report_consistency = consistency_rx.recv().await.unwrap();
-        assert_matches!(report_consistency, LegacyConsistency::Regular(Consistency::Three));
+        assert_matches!(report_consistency, Consistency::Three);
         consistency_rx.try_recv().unwrap_err();
 
         prepared.set_consistency(Consistency::Three);
         session.execute(&prepared, &[]).await.unwrap_err();
         let report_consistency = consistency_rx.recv().await.unwrap();
-        assert_matches!(report_consistency, LegacyConsistency::Regular(Consistency::Three));
+        assert_matches!(report_consistency, Consistency::Three);
         consistency_rx.try_recv().unwrap_err();
 
         batch.set_consistency(Consistency::Three);
         session.batch(&batch, ((),)).await.unwrap_err();
         let report_consistency = consistency_rx.recv().await.unwrap();
-        assert_matches!(report_consistency, LegacyConsistency::Regular(Consistency::Three));
+        assert_matches!(report_consistency, Consistency::Three);
         consistency_rx.try_recv().unwrap_err();
 
         for i in 0..=2 {